IDEADEV-41464: Package File does nothing
[fedora-idea.git] / java / compiler / impl / src / com / intellij / packaging / impl / artifacts / ArtifactUtil.java
blob8d9ea13d3a395c1ec723ffee7bd3fe71439e3568
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.compiler.CompilerConfiguration;
19 import com.intellij.openapi.module.Module;
20 import com.intellij.openapi.project.Project;
21 import com.intellij.openapi.roots.CompilerProjectExtension;
22 import com.intellij.openapi.roots.ContentEntry;
23 import com.intellij.openapi.roots.ModuleRootModel;
24 import com.intellij.openapi.roots.SourceFolder;
25 import com.intellij.openapi.util.Condition;
26 import com.intellij.openapi.util.Pair;
27 import com.intellij.openapi.util.Trinity;
28 import com.intellij.openapi.util.io.FileUtil;
29 import com.intellij.openapi.util.text.StringUtil;
30 import com.intellij.openapi.vfs.VfsUtil;
31 import com.intellij.openapi.vfs.VirtualFile;
32 import com.intellij.packaging.artifacts.Artifact;
33 import com.intellij.packaging.artifacts.ArtifactManager;
34 import com.intellij.packaging.artifacts.ArtifactProperties;
35 import com.intellij.packaging.artifacts.ArtifactType;
36 import com.intellij.packaging.elements.*;
37 import com.intellij.packaging.impl.elements.*;
38 import com.intellij.util.Processor;
39 import com.intellij.util.SmartList;
40 import com.intellij.util.containers.ContainerUtil;
41 import com.intellij.util.containers.FList;
42 import org.jetbrains.annotations.NotNull;
43 import org.jetbrains.annotations.Nullable;
45 import java.util.*;
47 /**
48 * @author nik
50 public class ArtifactUtil {
51 private ArtifactUtil() {
54 public static CompositePackagingElement<?> copyFromRoot(@NotNull CompositePackagingElement<?> oldRoot, @NotNull Project project) {
55 final CompositePackagingElement<?> newRoot = (CompositePackagingElement<?>)copyElement(oldRoot, project);
56 copyChildren(oldRoot, newRoot, project);
57 return newRoot;
61 public static void copyChildren(CompositePackagingElement<?> oldParent, CompositePackagingElement<?> newParent, @NotNull Project project) {
62 for (PackagingElement<?> child : oldParent.getChildren()) {
63 newParent.addOrFindChild(copyWithChildren(child, project));
67 @NotNull
68 public static <S> PackagingElement<S> copyWithChildren(@NotNull PackagingElement<S> element, @NotNull Project project) {
69 final PackagingElement<S> copy = copyElement(element, project);
70 if (element instanceof CompositePackagingElement<?>) {
71 copyChildren((CompositePackagingElement<?>)element, (CompositePackagingElement<?>)copy, project);
73 return copy;
76 @NotNull
77 private static <S> PackagingElement<S> copyElement(@NotNull PackagingElement<S> element, @NotNull Project project) {
78 //noinspection unchecked
79 final PackagingElement<S> copy = (PackagingElement<S>)element.getType().createEmpty(project);
80 copy.loadState(element.getState());
81 return copy;
84 public static <E extends PackagingElement<?>> boolean processPackagingElements(@NotNull Artifact artifact, @Nullable PackagingElementType<E> type,
85 @NotNull final Processor<? super E> processor,
86 final @NotNull PackagingElementResolvingContext resolvingContext,
87 final boolean processSubstitutions) {
88 return processPackagingElements(artifact, type, new PackagingElementProcessor<E>() {
89 @Override
90 public boolean process(@NotNull E e, @NotNull PackagingElementPath path) {
91 return processor.process(e);
93 }, resolvingContext, processSubstitutions);
96 public static <E extends PackagingElement<?>> boolean processPackagingElements(@NotNull Artifact artifact, @Nullable PackagingElementType<E> type,
97 @NotNull PackagingElementProcessor<? super E> processor,
98 final @NotNull PackagingElementResolvingContext resolvingContext,
99 final boolean processSubstitutions) {
100 return processPackagingElements(artifact.getRootElement(), type, processor, resolvingContext, processSubstitutions, artifact.getArtifactType());
103 public static <E extends PackagingElement<?>> boolean processPackagingElements(final PackagingElement<?> rootElement, @Nullable PackagingElementType<E> type,
104 @NotNull PackagingElementProcessor<? super E> processor,
105 final @NotNull PackagingElementResolvingContext resolvingContext,
106 final boolean processSubstitutions,
107 final ArtifactType artifactType) {
108 return processElement(rootElement, type, processor, resolvingContext, processSubstitutions, artifactType,
109 PackagingElementPath.EMPTY, new HashSet<PackagingElement<?>>());
112 private static <E extends PackagingElement<?>> boolean processElements(final List<? extends PackagingElement<?>> elements,
113 @Nullable PackagingElementType<E> type,
114 @NotNull PackagingElementProcessor<? super E> processor,
115 final @NotNull PackagingElementResolvingContext resolvingContext,
116 final boolean processSubstitutions, ArtifactType artifactType,
117 @NotNull PackagingElementPath path,
118 Set<PackagingElement<?>> processed) {
119 for (PackagingElement<?> element : elements) {
120 if (!processElement(element, type, processor, resolvingContext, processSubstitutions, artifactType, path, processed)) {
121 return false;
124 return true;
127 private static <E extends PackagingElement<?>> boolean processElement(@NotNull PackagingElement<?> element, @Nullable PackagingElementType<E> type,
128 @NotNull PackagingElementProcessor<? super E> processor,
129 @NotNull PackagingElementResolvingContext resolvingContext,
130 final boolean processSubstitutions,
131 ArtifactType artifactType,
132 @NotNull PackagingElementPath path, Set<PackagingElement<?>> processed) {
133 if (!processor.shouldProcess(element) || !processed.add(element)) {
134 return true;
136 if (type == null || element.getType().equals(type)) {
137 if (!processor.process((E)element, path)) {
138 return false;
141 if (element instanceof CompositePackagingElement<?>) {
142 final CompositePackagingElement<?> composite = (CompositePackagingElement<?>)element;
143 return processElements(composite.getChildren(), type, processor, resolvingContext, processSubstitutions, artifactType,
144 path.appendComposite(composite), processed);
146 else if (element instanceof ComplexPackagingElement<?> && processSubstitutions) {
147 final ComplexPackagingElement<?> complexElement = (ComplexPackagingElement<?>)element;
148 if (processor.shouldProcessSubstitution(complexElement)) {
149 final List<? extends PackagingElement<?>> substitution = complexElement.getSubstitution(resolvingContext, artifactType);
150 if (substitution != null) {
151 return processElements(substitution, type, processor, resolvingContext, processSubstitutions, artifactType,
152 path.appendComplex(complexElement), processed);
156 return true;
159 public static void removeDuplicates(@NotNull CompositePackagingElement<?> parent) {
160 List<PackagingElement<?>> prevChildren = new ArrayList<PackagingElement<?>>();
162 List<PackagingElement<?>> toRemove = new ArrayList<PackagingElement<?>>();
163 for (PackagingElement<?> child : parent.getChildren()) {
164 if (child instanceof CompositePackagingElement<?>) {
165 removeDuplicates((CompositePackagingElement<?>)child);
167 boolean merged = false;
168 for (PackagingElement<?> prevChild : prevChildren) {
169 if (child.isEqualTo(prevChild)) {
170 if (child instanceof CompositePackagingElement<?>) {
171 for (PackagingElement<?> childElement : ((CompositePackagingElement<?>)child).getChildren()) {
172 ((CompositePackagingElement<?>)prevChild).addOrFindChild(childElement);
175 merged = true;
176 break;
179 if (merged) {
180 toRemove.add(child);
182 else {
183 prevChildren.add(child);
187 for (PackagingElement<?> child : toRemove) {
188 parent.removeChild(child);
192 public static <S> void copyProperties(ArtifactProperties<?> from, ArtifactProperties<S> to) {
193 //noinspection unchecked
194 to.loadState((S)from.getState());
197 @Nullable
198 public static String getDefaultArtifactOutputPath(@NotNull String artifactName, final @NotNull Project project) {
199 final CompilerProjectExtension extension = CompilerProjectExtension.getInstance(project);
200 if (extension == null) return null;
201 final String outputUrl = extension.getCompilerOutputUrl();
202 if (outputUrl == null) return null;
203 return VfsUtil.urlToPath(outputUrl) + "/artifacts/" + FileUtil.sanitizeFileName(artifactName);
206 public static <E extends PackagingElement<?>> boolean processElements(@NotNull List<? extends PackagingElement<?>> elements,
207 @NotNull PackagingElementResolvingContext context,
208 @NotNull ArtifactType artifactType,
209 @NotNull PackagingElementPath parentPath,
210 @NotNull PackagingElementProcessor<E> processor) {
211 for (PackagingElement<?> element : elements) {
212 if (element instanceof ComplexPackagingElement<?> && processor.shouldProcessSubstitution((ComplexPackagingElement)element)) {
213 final ComplexPackagingElement<?> complexElement = (ComplexPackagingElement<?>)element;
214 final List<? extends PackagingElement<?>> substitution = complexElement.getSubstitution(context, artifactType);
215 if (substitution != null && !processElements(substitution, context, artifactType, parentPath.appendComplex(complexElement), processor)) {
216 return false;
219 else if (!processor.process((E)element, parentPath)) {
220 return false;
223 return true;
226 public static List<PackagingElement<?>> findByRelativePath(@NotNull CompositePackagingElement<?> parent, @NotNull String relativePath,
227 @NotNull PackagingElementResolvingContext context, @NotNull ArtifactType artifactType) {
228 final List<PackagingElement<?>> result = new ArrayList<PackagingElement<?>>();
229 processElementsByRelativePath(parent, relativePath, context, artifactType, PackagingElementPath.EMPTY, new PackagingElementProcessor<PackagingElement<?>>() {
230 @Override
231 public boolean process(@NotNull PackagingElement<?> packagingElement, @NotNull PackagingElementPath path) {
232 result.add(packagingElement);
233 return true;
236 return result;
239 public static boolean processElementsByRelativePath(@NotNull final CompositePackagingElement<?> parent, @NotNull String relativePath,
240 @NotNull final PackagingElementResolvingContext context, @NotNull final ArtifactType artifactType,
241 @NotNull PackagingElementPath parentPath,
242 @NotNull final PackagingElementProcessor<PackagingElement<?>> processor) {
243 relativePath = StringUtil.trimStart(relativePath, "/");
244 if (relativePath.length() == 0) {
245 return true;
248 int i = relativePath.indexOf('/');
249 final String firstName = i != -1 ? relativePath.substring(0, i) : relativePath;
250 final String tail = i != -1 ? relativePath.substring(i+1) : "";
252 return processElements(parent.getChildren(), context, artifactType, parentPath.appendComposite(parent), new PackagingElementProcessor<PackagingElement<?>>() {
253 @Override
254 public boolean process(@NotNull PackagingElement<?> element, @NotNull PackagingElementPath path) {
255 boolean process = false;
256 if (element instanceof CompositePackagingElement && firstName.equals(((CompositePackagingElement<?>)element).getName())) {
257 process = true;
259 else if (element instanceof FileCopyPackagingElement) {
260 final FileCopyPackagingElement fileCopy = (FileCopyPackagingElement)element;
261 if (firstName.equals(fileCopy.getOutputFileName())) {
262 process = true;
266 if (process) {
267 if (tail.length() == 0) {
268 if (!processor.process(element, path)) return false;
270 else if (element instanceof CompositePackagingElement<?>) {
271 return processElementsByRelativePath((CompositePackagingElement)element, tail, context, artifactType, path, processor);
274 return true;
279 public static boolean processDirectoryChildren(@NotNull CompositePackagingElement<?> parent,
280 @NotNull PackagingElementPath pathToParent,
281 @NotNull String relativePath,
282 @NotNull final PackagingElementResolvingContext context,
283 @NotNull final ArtifactType artifactType,
284 @NotNull final PackagingElementProcessor<PackagingElement<?>> processor) {
285 return processElementsByRelativePath(parent, relativePath, context, artifactType, pathToParent, new PackagingElementProcessor<PackagingElement<?>>() {
286 @Override
287 public boolean process(@NotNull PackagingElement<?> element, @NotNull PackagingElementPath path) {
288 if (element instanceof DirectoryPackagingElement) {
289 final List<PackagingElement<?>> children = ((DirectoryPackagingElement)element).getChildren();
290 if (!processElements(children, context, artifactType, path.appendComposite((DirectoryPackagingElement)element), processor)) {
291 return false;
294 return true;
299 public static void processFileOrDirectoryCopyElements(Artifact artifact,
300 PackagingElementProcessor<FileOrDirectoryCopyPackagingElement<?>> processor,
301 PackagingElementResolvingContext context,
302 boolean processSubstitutions) {
303 processPackagingElements(artifact, PackagingElementFactoryImpl.FILE_COPY_ELEMENT_TYPE, processor, context, processSubstitutions);
304 processPackagingElements(artifact, PackagingElementFactoryImpl.DIRECTORY_COPY_ELEMENT_TYPE, processor, context, processSubstitutions);
307 public static Collection<Trinity<Artifact, PackagingElementPath, String>> findContainingArtifactsWithOutputPaths(@NotNull final VirtualFile file, @NotNull Project project) {
308 final boolean isResourceFile = CompilerConfiguration.getInstance(project).isResourceFile(file);
309 final List<Trinity<Artifact, PackagingElementPath, String>> artifacts = new ArrayList<Trinity<Artifact, PackagingElementPath, String>>();
310 final PackagingElementResolvingContext context = ArtifactManager.getInstance(project).getResolvingContext();
311 for (final Artifact artifact : ArtifactManager.getInstance(project).getArtifacts()) {
312 processPackagingElements(artifact, null, new PackagingElementProcessor<PackagingElement<?>>() {
313 @Override
314 public boolean process(@NotNull PackagingElement<?> element, @NotNull PackagingElementPath path) {
315 if (element instanceof FileOrDirectoryCopyPackagingElement<?>) {
316 final VirtualFile root = ((FileOrDirectoryCopyPackagingElement)element).findFile();
317 if (root != null && VfsUtil.isAncestor(root, file, false)) {
318 final String relativePath;
319 if (root.equals(file) && element instanceof FileCopyPackagingElement) {
320 relativePath = ((FileCopyPackagingElement)element).getOutputFileName();
322 else {
323 relativePath = VfsUtil.getRelativePath(file, root, '/');
325 artifacts.add(Trinity.create(artifact, path, relativePath));
326 return false;
329 else if (isResourceFile && element instanceof ModuleOutputPackagingElement) {
330 final String relativePath = getRelativePathInSources(file, (ModuleOutputPackagingElement)element, context);
331 if (relativePath != null) {
332 artifacts.add(Trinity.create(artifact, path, relativePath));
333 return false;
336 return true;
338 }, context, true);
340 return artifacts;
343 @Nullable
344 private static String getRelativePathInSources(@NotNull VirtualFile file, final @NotNull ModuleOutputPackagingElement moduleElement,
345 @NotNull PackagingElementResolvingContext context) {
346 final Module module = moduleElement.findModule(context);
347 if (module != null) {
348 final ModuleRootModel rootModel = context.getModulesProvider().getRootModel(module);
349 for (ContentEntry entry : rootModel.getContentEntries()) {
350 for (SourceFolder folder : entry.getSourceFolders()) {
351 final VirtualFile sourceRoot = folder.getFile();
352 if (!folder.isTestSource() && sourceRoot != null && VfsUtil.isAncestor(sourceRoot, file, true)) {
353 return VfsUtil.getRelativePath(file, sourceRoot, '/');
358 return null;
361 @Nullable
362 public static VirtualFile findSourceFileByOutputPath(Artifact artifact, String outputPath, PackagingElementResolvingContext context) {
363 final List<VirtualFile> files = findSourceFilesByOutputPath(artifact.getRootElement(), outputPath, context, artifact.getArtifactType());
364 return files.isEmpty() ? null : files.get(0);
367 @Nullable
368 public static VirtualFile findSourceFileByOutputPath(CompositePackagingElement<?> parent, String outputPath,
369 PackagingElementResolvingContext context, ArtifactType artifactType) {
370 final List<VirtualFile> files = findSourceFilesByOutputPath(parent, outputPath, context, artifactType);
371 return files.isEmpty() ? null : files.get(0);
374 public static List<VirtualFile> findSourceFilesByOutputPath(CompositePackagingElement<?> parent, final String outputPath,
375 final PackagingElementResolvingContext context, final ArtifactType artifactType) {
376 final String path = StringUtil.trimStart(outputPath, "/");
377 if (path.length() == 0) {
378 return Collections.emptyList();
381 int i = path.indexOf('/');
382 final String firstName = i != -1 ? path.substring(0, i) : path;
383 final String tail = i != -1 ? path.substring(i+1) : "";
385 final List<VirtualFile> result = new SmartList<VirtualFile>();
386 processElements(parent.getChildren(), context, artifactType, PackagingElementPath.EMPTY, new PackagingElementProcessor<PackagingElement<?>>() {
387 @Override
388 public boolean process(@NotNull PackagingElement<?> element, @NotNull PackagingElementPath elementPath) {
389 //todo[nik] replace by method findSourceFile() in PackagingElement
390 if (element instanceof CompositePackagingElement) {
391 final CompositePackagingElement<?> compositeElement = (CompositePackagingElement<?>)element;
392 if (firstName.equals(compositeElement.getName())) {
393 result.addAll(findSourceFilesByOutputPath(compositeElement, tail, context, artifactType));
396 else if (element instanceof FileCopyPackagingElement) {
397 final FileCopyPackagingElement fileCopyElement = (FileCopyPackagingElement)element;
398 if (firstName.equals(fileCopyElement.getOutputFileName()) && tail.length() == 0) {
399 ContainerUtil.addIfNotNull(fileCopyElement.findFile(), result);
402 else if (element instanceof DirectoryCopyPackagingElement) {
403 final VirtualFile sourceRoot = ((DirectoryCopyPackagingElement)element).findFile();
404 if (sourceRoot != null) {
405 ContainerUtil.addIfNotNull(sourceRoot.findFileByRelativePath(path), result);
408 else if (element instanceof ModuleOutputPackagingElement) {
409 final Module module = ((ModuleOutputPackagingElement)element).findModule(context);
410 if (module != null) {
411 final ContentEntry[] contentEntries = context.getModulesProvider().getRootModel(module).getContentEntries();
412 for (ContentEntry contentEntry : contentEntries) {
413 for (SourceFolder sourceFolder : contentEntry.getSourceFolders()) {
414 final VirtualFile sourceRoot = sourceFolder.getFile();
415 if (!sourceFolder.isTestSource() && sourceRoot != null) {
416 ContainerUtil.addIfNotNull(sourceRoot.findFileByRelativePath(path), result);
422 return true;
426 return result;
429 public static boolean processParents(@NotNull Artifact artifact,
430 @NotNull PackagingElementResolvingContext context,
431 @NotNull ParentElementProcessor processor,
432 int maxLevel) {
433 return processParents(artifact, context, processor, FList.<Pair<Artifact, CompositePackagingElement<?>>>emptyList(), maxLevel,
434 new HashSet<Artifact>());
437 private static boolean processParents(@NotNull final Artifact artifact, @NotNull final PackagingElementResolvingContext context,
438 @NotNull final ParentElementProcessor processor, FList<Pair<Artifact, CompositePackagingElement<?>>> pathToElement,
439 final int maxLevel, final Set<Artifact> processed) {
440 if (!processed.add(artifact)) return true;
442 final FList<Pair<Artifact, CompositePackagingElement<?>>> pathFromRoot;
443 final CompositePackagingElement<?> rootElement = artifact.getRootElement();
444 if (rootElement instanceof ArtifactRootElement<?>) {
445 pathFromRoot = pathToElement;
447 else {
448 if (!processor.process(rootElement, pathToElement, artifact)) {
449 return false;
451 pathFromRoot = pathToElement.prepend(new Pair<Artifact, CompositePackagingElement<?>>(artifact, rootElement));
453 if (pathFromRoot.size() > maxLevel) return true;
455 for (final Artifact anArtifact : context.getArtifactModel().getArtifacts()) {
456 if (processed.contains(anArtifact)) continue;
458 final PackagingElementProcessor<ArtifactPackagingElement> elementProcessor =
459 new PackagingElementProcessor<ArtifactPackagingElement>() {
460 @Override
461 public boolean shouldProcessSubstitution(ComplexPackagingElement<?> element) {
462 return !(element instanceof ArtifactPackagingElement);
465 @Override
466 public boolean process(@NotNull ArtifactPackagingElement element, @NotNull PackagingElementPath path) {
467 if (artifact.getName().equals(element.getArtifactName())) {
468 FList<Pair<Artifact, CompositePackagingElement<?>>> currentPath = pathFromRoot;
469 final List<CompositePackagingElement<?>> parents = path.getParents();
470 for (int i = 0, parentsSize = parents.size(); i < parentsSize - 1; i++) {
471 CompositePackagingElement<?> parent = parents.get(i);
472 if (!processor.process(parent, currentPath, anArtifact)) {
473 return false;
475 currentPath = currentPath.prepend(new Pair<Artifact, CompositePackagingElement<?>>(anArtifact, parent));
476 if (currentPath.size() > maxLevel) {
477 return true;
481 if (!parents.isEmpty()) {
482 CompositePackagingElement<?> lastParent = parents.get(parents.size() - 1);
483 if (lastParent instanceof ArtifactRootElement<?> && !processor.process(lastParent, currentPath, anArtifact)) {
484 return false;
487 return processParents(anArtifact, context, processor, currentPath, maxLevel, processed);
489 return true;
492 if (!processPackagingElements(anArtifact, ArtifactElementType.ARTIFACT_ELEMENT_TYPE, elementProcessor, context, true)) {
493 return false;
496 return true;
499 public static boolean isArchiveName(String name) {
500 return name.length() >= 4 && name.charAt(name.length() - 4) == '.' && StringUtil.endsWithIgnoreCase(name, "ar");
503 public static void removeChildrenRecursively(@NotNull CompositePackagingElement<?> element, @NotNull Condition<PackagingElement<?>> condition) {
504 List<PackagingElement<?>> toRemove = new ArrayList<PackagingElement<?>>();
505 for (PackagingElement<?> child : element.getChildren()) {
506 if (child instanceof CompositePackagingElement<?>) {
507 final CompositePackagingElement<?> compositeChild = (CompositePackagingElement<?>)child;
508 removeChildrenRecursively(compositeChild, condition);
509 if (compositeChild.getChildren().isEmpty()) {
510 toRemove.add(child);
513 else if (condition.value(child)) {
514 toRemove.add(child);
518 element.removeChildren(toRemove);
521 public static boolean shouldClearArtifactOutputBeforeRebuild(Artifact artifact) {
522 final String outputPath = artifact.getOutputPath();
523 return !StringUtil.isEmpty(outputPath) && artifact.getRootElement() instanceof ArtifactRootElement<?>;