IDEADEV-40824: Artifact: project elements move/rename does not update artifact definition
[fedora-idea.git] / java / compiler / impl / src / com / intellij / packaging / impl / artifacts / ArtifactUtil.java
blobcf2b82412988f7a660e081a88d55c1b87baa8ff0
1 /*
2 * Copyright 2000-2009 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package com.intellij.packaging.impl.artifacts;
18 import com.intellij.openapi.module.Module;
19 import com.intellij.openapi.project.Project;
20 import com.intellij.openapi.roots.CompilerProjectExtension;
21 import com.intellij.openapi.roots.ContentEntry;
22 import com.intellij.openapi.roots.SourceFolder;
23 import com.intellij.openapi.util.Condition;
24 import com.intellij.openapi.util.Pair;
25 import com.intellij.openapi.util.io.FileUtil;
26 import com.intellij.openapi.util.text.StringUtil;
27 import com.intellij.openapi.vfs.VfsUtil;
28 import com.intellij.openapi.vfs.VirtualFile;
29 import com.intellij.openapi.deployment.DeploymentUtil;
30 import com.intellij.packaging.artifacts.Artifact;
31 import com.intellij.packaging.artifacts.ArtifactManager;
32 import com.intellij.packaging.artifacts.ArtifactProperties;
33 import com.intellij.packaging.artifacts.ArtifactType;
34 import com.intellij.packaging.elements.*;
35 import com.intellij.packaging.impl.elements.*;
36 import com.intellij.util.Processor;
37 import com.intellij.util.SmartList;
38 import com.intellij.util.containers.ContainerUtil;
39 import com.intellij.util.containers.FList;
40 import org.jetbrains.annotations.NotNull;
41 import org.jetbrains.annotations.Nullable;
43 import java.util.*;
45 /**
46 * @author nik
48 public class ArtifactUtil {
49 private ArtifactUtil() {
52 public static CompositePackagingElement<?> copyFromRoot(@NotNull CompositePackagingElement<?> oldRoot, @NotNull Project project) {
53 final CompositePackagingElement<?> newRoot = (CompositePackagingElement<?>)copyElement(oldRoot, project);
54 copyChildren(oldRoot, newRoot, project);
55 return newRoot;
59 public static void copyChildren(CompositePackagingElement<?> oldParent, CompositePackagingElement<?> newParent, @NotNull Project project) {
60 for (PackagingElement<?> child : oldParent.getChildren()) {
61 newParent.addOrFindChild(copyWithChildren(child, project));
65 @NotNull
66 public static <S> PackagingElement<S> copyWithChildren(@NotNull PackagingElement<S> element, @NotNull Project project) {
67 final PackagingElement<S> copy = copyElement(element, project);
68 if (element instanceof CompositePackagingElement<?>) {
69 copyChildren((CompositePackagingElement<?>)element, (CompositePackagingElement<?>)copy, project);
71 return copy;
74 @NotNull
75 private static <S> PackagingElement<S> copyElement(@NotNull PackagingElement<S> element, @NotNull Project project) {
76 //noinspection unchecked
77 final PackagingElement<S> copy = (PackagingElement<S>)element.getType().createEmpty(project);
78 copy.loadState(element.getState());
79 return copy;
82 public static <E extends PackagingElement<?>> boolean processPackagingElements(@NotNull Artifact artifact, @Nullable PackagingElementType<E> type,
83 @NotNull final Processor<? super E> processor,
84 final @NotNull PackagingElementResolvingContext resolvingContext,
85 final boolean processSubstitutions) {
86 return processPackagingElements(artifact, type, new PackagingElementProcessor<E>() {
87 @Override
88 public boolean process(@NotNull List<CompositePackagingElement<?>> parents, @NotNull E e) {
89 return processor.process(e);
91 }, resolvingContext, processSubstitutions);
94 public static <E extends PackagingElement<?>> boolean processPackagingElements(@NotNull Artifact artifact, @Nullable PackagingElementType<E> type,
95 @NotNull PackagingElementProcessor<? super E> processor,
96 final @NotNull PackagingElementResolvingContext resolvingContext,
97 final boolean processSubstitutions) {
98 return processPackagingElements(artifact.getRootElement(), type, processor, resolvingContext, processSubstitutions, artifact.getArtifactType());
101 public static <E extends PackagingElement<?>> boolean processPackagingElements(final PackagingElement<?> rootElement, @Nullable PackagingElementType<E> type,
102 @NotNull PackagingElementProcessor<? super E> processor,
103 final @NotNull PackagingElementResolvingContext resolvingContext,
104 final boolean processSubstitutions,
105 final ArtifactType artifactType) {
106 return processElements(rootElement, type, processor, resolvingContext, processSubstitutions, artifactType,
107 FList.<CompositePackagingElement<?>>emptyList(), new HashSet<PackagingElement<?>>());
110 private static <E extends PackagingElement<?>> boolean processElements(final List<? extends PackagingElement<?>> elements,
111 @Nullable PackagingElementType<E> type,
112 @NotNull PackagingElementProcessor<? super E> processor,
113 final @NotNull PackagingElementResolvingContext resolvingContext,
114 final boolean processSubstitutions, ArtifactType artifactType,
115 FList<CompositePackagingElement<?>> parents,
116 Set<PackagingElement<?>> processed) {
117 for (PackagingElement<?> element : elements) {
118 if (!processElements(element, type, processor, resolvingContext, processSubstitutions, artifactType, parents, processed)) {
119 return false;
122 return true;
125 private static <E extends PackagingElement<?>> boolean processElements(@NotNull PackagingElement<?> element, @Nullable PackagingElementType<E> type,
126 @NotNull PackagingElementProcessor<? super E> processor,
127 @NotNull PackagingElementResolvingContext resolvingContext,
128 final boolean processSubstitutions,
129 ArtifactType artifactType,
130 FList<CompositePackagingElement<?>> parents, Set<PackagingElement<?>> processed) {
131 if (!processor.shouldProcess(element) || !processed.add(element)) {
132 return true;
134 if (type == null || element.getType().equals(type)) {
135 if (!processor.process(parents, (E)element)) {
136 return false;
139 if (element instanceof CompositePackagingElement<?>) {
140 final CompositePackagingElement<?> composite = (CompositePackagingElement<?>)element;
141 return processElements(composite.getChildren(), type, processor, resolvingContext, processSubstitutions, artifactType,
142 parents.prepend(composite), processed);
144 else if (element instanceof ComplexPackagingElement<?> && processSubstitutions) {
145 final ComplexPackagingElement<?> complexElement = (ComplexPackagingElement<?>)element;
146 if (processor.shouldProcessSubstitution(complexElement)) {
147 final List<? extends PackagingElement<?>> substitution = complexElement.getSubstitution(resolvingContext, artifactType);
148 if (substitution != null) {
149 return processElements(substitution, type, processor, resolvingContext, processSubstitutions, artifactType, parents, processed);
153 return true;
156 public static void removeDuplicates(@NotNull CompositePackagingElement<?> parent) {
157 List<PackagingElement<?>> prevChildren = new ArrayList<PackagingElement<?>>();
159 List<PackagingElement<?>> toRemove = new ArrayList<PackagingElement<?>>();
160 for (PackagingElement<?> child : parent.getChildren()) {
161 if (child instanceof CompositePackagingElement<?>) {
162 removeDuplicates((CompositePackagingElement<?>)child);
164 boolean merged = false;
165 for (PackagingElement<?> prevChild : prevChildren) {
166 if (child.isEqualTo(prevChild)) {
167 if (child instanceof CompositePackagingElement<?>) {
168 for (PackagingElement<?> childElement : ((CompositePackagingElement<?>)child).getChildren()) {
169 ((CompositePackagingElement<?>)prevChild).addOrFindChild(childElement);
172 merged = true;
173 break;
176 if (merged) {
177 toRemove.add(child);
179 else {
180 prevChildren.add(child);
184 for (PackagingElement<?> child : toRemove) {
185 parent.removeChild(child);
189 public static <S> void copyProperties(ArtifactProperties<?> from, ArtifactProperties<S> to) {
190 //noinspection unchecked
191 to.loadState((S)from.getState());
194 @Nullable
195 public static String getDefaultArtifactOutputPath(@NotNull String artifactName, final @NotNull Project project) {
196 final CompilerProjectExtension extension = CompilerProjectExtension.getInstance(project);
197 if (extension == null) return null;
198 final String outputUrl = extension.getCompilerOutputUrl();
199 if (outputUrl == null) return null;
200 return VfsUtil.urlToPath(outputUrl) + "/artifacts/" + FileUtil.sanitizeFileName(artifactName);
203 public static <E extends PackagingElement<?>> boolean processElements(@NotNull List<? extends PackagingElement<?>> elements,
204 @NotNull PackagingElementResolvingContext context,
205 @NotNull ArtifactType artifactType,
206 @NotNull final Processor<E> processor) {
207 return processElements(elements, context, artifactType, new PackagingElementProcessor<E>() {
208 @Override
209 public boolean process(@NotNull List<CompositePackagingElement<?>> parents, @NotNull E e) {
210 return processor.process(e);
215 public static <E extends PackagingElement<?>> boolean processElements(@NotNull List<? extends PackagingElement<?>> elements,
216 @NotNull PackagingElementResolvingContext context,
217 @NotNull ArtifactType artifactType,
218 @NotNull PackagingElementProcessor<E> processor) {
219 for (PackagingElement<?> element : elements) {
220 if (element instanceof ComplexPackagingElement<?> && processor.shouldProcessSubstitution((ComplexPackagingElement)element)) {
221 final List<? extends PackagingElement<?>> substitution =
222 ((ComplexPackagingElement<?>)element).getSubstitution(context, artifactType);
223 if (substitution != null && !processElements(substitution, context, artifactType, processor)) {
224 return false;
227 else if (!processor.process(FList.<CompositePackagingElement<?>>emptyList(), (E)element)) {
228 return false;
231 return true;
234 public static List<PackagingElement<?>> findByRelativePath(@NotNull CompositePackagingElement<?> parent, @NotNull String relativePath,
235 @NotNull PackagingElementResolvingContext context, @NotNull ArtifactType artifactType) {
236 relativePath = StringUtil.trimStart(relativePath, "/");
237 if (relativePath.length() == 0) {
238 return new SmartList<PackagingElement<?>>(parent);
241 int i = relativePath.indexOf('/');
242 final String firstName = i != -1 ? relativePath.substring(0, i) : relativePath;
243 String tail = i != -1 ? relativePath.substring(i+1) : "";
245 final List<PackagingElement<?>> children = new SmartList<PackagingElement<?>>();
246 processElements(parent.getChildren(), context, artifactType, new Processor<PackagingElement<?>>() {
247 public boolean process(PackagingElement<?> element) {
248 if (element instanceof CompositePackagingElement && firstName.equals(((CompositePackagingElement<?>)element).getName())) {
249 children.add(element);
251 else if (element instanceof FileCopyPackagingElement) {
252 final FileCopyPackagingElement fileCopy = (FileCopyPackagingElement)element;
253 if (firstName.equals(fileCopy.getOutputFileName())) {
254 children.add(element);
257 return true;
261 if (tail.length() == 0) {
262 return children;
265 List<PackagingElement<?>> result = new SmartList<PackagingElement<?>>();
266 for (PackagingElement<?> child : children) {
267 if (child instanceof CompositePackagingElement) {
268 result.addAll(findByRelativePath((CompositePackagingElement<?>)child, tail, context, artifactType));
271 return result;
274 public static boolean processDirectoryChildren(@NotNull CompositePackagingElement<?> root,
275 @NotNull String relativePath,
276 @NotNull PackagingElementResolvingContext context,
277 @NotNull ArtifactType artifactType,
278 @NotNull Processor<PackagingElement<?>> processor) {
279 final List<PackagingElement<?>> dirs = findByRelativePath(root, relativePath, context, artifactType);
280 for (PackagingElement<?> dir : dirs) {
281 if (dir instanceof DirectoryPackagingElement) {
282 final List<PackagingElement<?>> children = ((DirectoryPackagingElement)dir).getChildren();
283 if (!processElements(children, context, artifactType, processor)) {
284 return false;
288 return true;
291 public static Collection<? extends Artifact> findArtifactsByFile(@NotNull final VirtualFile file, @NotNull Project project) {
292 final Collection<Pair<Artifact, String>> pairs = findContainingArtifactsWithOutputPaths(file, project);
293 final List<Artifact> result = new ArrayList<Artifact>();
294 for (Pair<Artifact, String> pair : pairs) {
295 result.add(pair.getFirst());
297 return result;
300 public static void processFileOrDirectoryCopyElements(Artifact artifact,
301 PackagingElementProcessor<FileOrDirectoryCopyPackagingElement<?>> processor,
302 PackagingElementResolvingContext context,
303 boolean processSubstitutions) {
304 processPackagingElements(artifact, PackagingElementFactoryImpl.FILE_COPY_ELEMENT_TYPE, processor, context, processSubstitutions);
305 processPackagingElements(artifact, PackagingElementFactoryImpl.DIRECTORY_COPY_ELEMENT_TYPE, processor, context, processSubstitutions);
308 public static Collection<Pair<Artifact, String>> findContainingArtifactsWithOutputPaths(@NotNull final VirtualFile file, @NotNull Project project) {
309 final List<Pair<Artifact, String>> artifacts = new ArrayList<Pair<Artifact, String>>();
310 for (final Artifact artifact : ArtifactManager.getInstance(project).getArtifacts()) {
311 processFileOrDirectoryCopyElements(artifact, new PackagingElementProcessor<FileOrDirectoryCopyPackagingElement<?>>() {
312 @Override
313 public boolean process(@NotNull List<CompositePackagingElement<?>> parents,
314 @NotNull FileOrDirectoryCopyPackagingElement<?> element) {
315 final VirtualFile root = element.findFile();
316 if (root != null && VfsUtil.isAncestor(root, file, false)) {
317 boolean isInArchive = false;
318 for (CompositePackagingElement<?> parent : parents) {
319 if (parent instanceof ArchivePackagingElement) {
320 isInArchive = true;
321 break;
324 String path;
325 if (!isInArchive) {
326 final String relativePath;
327 if (root.equals(file) && element instanceof FileCopyPackagingElement) {
328 relativePath = ((FileCopyPackagingElement)element).getOutputFileName();
330 else {
331 relativePath = VfsUtil.getRelativePath(file, root, '/');
333 path = DeploymentUtil.concatPaths(getPathFromRoot(parents, "/"), relativePath);
335 else {
336 path = null;
338 artifacts.add(Pair.create(artifact, path));
339 return false;
341 return true;
343 }, ArtifactManager.getInstance(project).getResolvingContext(), true);
345 return artifacts;
348 @Nullable
349 public static VirtualFile findSourceFileByOutputPath(Artifact artifact, String outputPath, PackagingElementResolvingContext context) {
350 final List<VirtualFile> files = findSourceFilesByOutputPath(artifact.getRootElement(), outputPath, context, artifact.getArtifactType());
351 return files.isEmpty() ? null : files.get(0);
354 @Nullable
355 public static VirtualFile findSourceFileByOutputPath(CompositePackagingElement<?> parent, String outputPath,
356 PackagingElementResolvingContext context, ArtifactType artifactType) {
357 final List<VirtualFile> files = findSourceFilesByOutputPath(parent, outputPath, context, artifactType);
358 return files.isEmpty() ? null : files.get(0);
361 public static List<VirtualFile> findSourceFilesByOutputPath(CompositePackagingElement<?> parent, final String outputPath,
362 final PackagingElementResolvingContext context, final ArtifactType artifactType) {
363 final String path = StringUtil.trimStart(outputPath, "/");
364 if (path.length() == 0) {
365 return Collections.emptyList();
368 int i = path.indexOf('/');
369 final String firstName = i != -1 ? path.substring(0, i) : path;
370 final String tail = i != -1 ? path.substring(i+1) : "";
372 final List<VirtualFile> result = new SmartList<VirtualFile>();
373 processElements(parent.getChildren(), context, artifactType, new Processor<PackagingElement<?>>() {
374 public boolean process(PackagingElement<?> element) {
375 //todo[nik] replace by method findSourceFile() in PackagingElement
376 if (element instanceof CompositePackagingElement) {
377 final CompositePackagingElement<?> compositeElement = (CompositePackagingElement<?>)element;
378 if (firstName.equals(compositeElement.getName())) {
379 result.addAll(findSourceFilesByOutputPath(compositeElement, tail, context, artifactType));
382 else if (element instanceof FileCopyPackagingElement) {
383 final FileCopyPackagingElement fileCopyElement = (FileCopyPackagingElement)element;
384 if (firstName.equals(fileCopyElement.getOutputFileName()) && tail.length() == 0) {
385 ContainerUtil.addIfNotNull(fileCopyElement.findFile(), result);
388 else if (element instanceof DirectoryCopyPackagingElement) {
389 final VirtualFile sourceRoot = ((DirectoryCopyPackagingElement)element).findFile();
390 if (sourceRoot != null) {
391 ContainerUtil.addIfNotNull(sourceRoot.findFileByRelativePath(path), result);
394 else if (element instanceof ModuleOutputPackagingElement) {
395 final Module module = ((ModuleOutputPackagingElement)element).findModule(context);
396 if (module != null) {
397 final ContentEntry[] contentEntries = context.getModulesProvider().getRootModel(module).getContentEntries();
398 for (ContentEntry contentEntry : contentEntries) {
399 for (SourceFolder sourceFolder : contentEntry.getSourceFolders()) {
400 final VirtualFile sourceRoot = sourceFolder.getFile();
401 if (!sourceFolder.isTestSource() && sourceRoot != null) {
402 ContainerUtil.addIfNotNull(sourceRoot.findFileByRelativePath(path), result);
408 return true;
412 return result;
415 public static boolean processParents(@NotNull Artifact artifact,
416 @NotNull PackagingElementResolvingContext context,
417 @NotNull ParentElementProcessor processor,
418 int maxLevel) {
419 return processParents(artifact, context, processor, FList.<Pair<Artifact, CompositePackagingElement<?>>>emptyList(), maxLevel,
420 new HashSet<Artifact>());
423 private static boolean processParents(@NotNull final Artifact artifact, @NotNull final PackagingElementResolvingContext context,
424 @NotNull final ParentElementProcessor processor, FList<Pair<Artifact, CompositePackagingElement<?>>> pathToElement,
425 final int maxLevel, final Set<Artifact> processed) {
426 if (!processed.add(artifact)) return true;
428 final FList<Pair<Artifact, CompositePackagingElement<?>>> pathFromRoot;
429 final CompositePackagingElement<?> rootElement = artifact.getRootElement();
430 if (rootElement instanceof ArtifactRootElement<?>) {
431 pathFromRoot = pathToElement;
433 else {
434 if (!processor.process(rootElement, pathToElement, artifact)) {
435 return false;
437 pathFromRoot = pathToElement.prepend(new Pair<Artifact, CompositePackagingElement<?>>(artifact, rootElement));
439 if (pathFromRoot.size() > maxLevel) return true;
441 for (final Artifact anArtifact : context.getArtifactModel().getArtifacts()) {
442 if (processed.contains(anArtifact)) continue;
444 final PackagingElementProcessor<ArtifactPackagingElement> elementProcessor =
445 new PackagingElementProcessor<ArtifactPackagingElement>() {
446 @Override
447 public boolean shouldProcessSubstitution(ComplexPackagingElement<?> element) {
448 return !(element instanceof ArtifactPackagingElement);
451 @Override
452 public boolean process(@NotNull List<CompositePackagingElement<?>> parents, @NotNull ArtifactPackagingElement element) {
453 if (artifact.getName().equals(element.getArtifactName())) {
454 FList<Pair<Artifact, CompositePackagingElement<?>>> currentPath = pathFromRoot;
455 for (int i = 0, parentsSize = parents.size(); i < parentsSize - 1; i++) {
456 CompositePackagingElement<?> parent = parents.get(i);
457 if (!processor.process(parent, currentPath, anArtifact)) {
458 return false;
460 currentPath = currentPath.prepend(new Pair<Artifact, CompositePackagingElement<?>>(anArtifact, parent));
461 if (currentPath.size() > maxLevel) {
462 return true;
466 if (!parents.isEmpty()) {
467 CompositePackagingElement<?> lastParent = parents.get(parents.size() - 1);
468 if (lastParent instanceof ArtifactRootElement<?> && !processor.process(lastParent, currentPath, anArtifact)) {
469 return false;
472 return processParents(anArtifact, context, processor, currentPath, maxLevel, processed);
474 return true;
477 if (!processPackagingElements(anArtifact, ArtifactElementType.ARTIFACT_ELEMENT_TYPE, elementProcessor, context, true)) {
478 return false;
481 return true;
484 public static boolean isArchiveName(String name) {
485 return name.length() >= 4 && name.charAt(name.length() - 4) == '.' && StringUtil.endsWithIgnoreCase(name, "ar");
488 public static void removeChildrenRecursively(@NotNull CompositePackagingElement<?> element, @NotNull Condition<PackagingElement<?>> condition) {
489 List<PackagingElement<?>> toRemove = new ArrayList<PackagingElement<?>>();
490 for (PackagingElement<?> child : element.getChildren()) {
491 if (child instanceof CompositePackagingElement<?>) {
492 final CompositePackagingElement<?> compositeChild = (CompositePackagingElement<?>)child;
493 removeChildrenRecursively(compositeChild, condition);
494 if (compositeChild.getChildren().isEmpty()) {
495 toRemove.add(child);
498 else if (condition.value(child)) {
499 toRemove.add(child);
503 element.removeChildren(toRemove);
506 public static boolean shouldClearArtifactOutputBeforeRebuild(Artifact artifact) {
507 final String outputPath = artifact.getOutputPath();
508 return !StringUtil.isEmpty(outputPath) && artifact.getRootElement() instanceof ArtifactRootElement<?>;