migrated to artifacts
[fedora-idea.git] / java / compiler / impl / src / com / intellij / packaging / impl / artifacts / ArtifactUtil.java
blobd12aa626943128b4b3d822418f71a53e97d6c0f7
1 package com.intellij.packaging.impl.artifacts;
3 import com.intellij.openapi.project.Project;
4 import com.intellij.openapi.roots.CompilerProjectExtension;
5 import com.intellij.openapi.util.Condition;
6 import com.intellij.openapi.util.Pair;
7 import com.intellij.openapi.util.io.FileUtil;
8 import com.intellij.openapi.util.text.StringUtil;
9 import com.intellij.openapi.vfs.VfsUtil;
10 import com.intellij.openapi.vfs.VirtualFile;
11 import com.intellij.openapi.deployment.DeploymentUtil;
12 import com.intellij.packaging.artifacts.Artifact;
13 import com.intellij.packaging.artifacts.ArtifactManager;
14 import com.intellij.packaging.artifacts.ArtifactProperties;
15 import com.intellij.packaging.artifacts.ArtifactType;
16 import com.intellij.packaging.elements.*;
17 import com.intellij.packaging.impl.elements.*;
18 import com.intellij.util.Processor;
19 import com.intellij.util.SmartList;
20 import com.intellij.util.containers.ContainerUtil;
21 import com.intellij.util.containers.FList;
22 import org.jetbrains.annotations.NotNull;
23 import org.jetbrains.annotations.Nullable;
25 import java.util.*;
27 /**
28 * @author nik
30 public class ArtifactUtil {
31 private ArtifactUtil() {
34 public static CompositePackagingElement<?> copyFromRoot(@NotNull CompositePackagingElement<?> oldRoot, @NotNull Project project) {
35 final CompositePackagingElement<?> newRoot = (CompositePackagingElement<?>)copyElement(oldRoot, project);
36 copyChildren(oldRoot, newRoot, project);
37 return newRoot;
41 public static void copyChildren(CompositePackagingElement<?> oldParent, CompositePackagingElement<?> newParent, @NotNull Project project) {
42 for (PackagingElement<?> child : oldParent.getChildren()) {
43 newParent.addOrFindChild(copyWithChildren(child, project));
47 @NotNull
48 public static <S> PackagingElement<S> copyWithChildren(@NotNull PackagingElement<S> element, @NotNull Project project) {
49 final PackagingElement<S> copy = copyElement(element, project);
50 if (element instanceof CompositePackagingElement<?>) {
51 copyChildren((CompositePackagingElement<?>)element, (CompositePackagingElement<?>)copy, project);
53 return copy;
56 @NotNull
57 private static <S> PackagingElement<S> copyElement(@NotNull PackagingElement<S> element, @NotNull Project project) {
58 //noinspection unchecked
59 final PackagingElement<S> copy = (PackagingElement<S>)element.getType().createEmpty(project);
60 copy.loadState(element.getState());
61 return copy;
64 public static <E extends PackagingElement<?>> boolean processPackagingElements(@NotNull Artifact artifact, @Nullable PackagingElementType<E> type,
65 @NotNull final Processor<E> processor,
66 final @NotNull PackagingElementResolvingContext resolvingContext,
67 final boolean processSubstitutions) {
68 return processPackagingElements(artifact, type, new PackagingElementProcessor<E>() {
69 @Override
70 public boolean process(@NotNull List<CompositePackagingElement<?>> parents, @NotNull E e) {
71 return processor.process(e);
73 }, resolvingContext, processSubstitutions);
76 public static <E extends PackagingElement<?>> boolean processPackagingElements(@NotNull Artifact artifact, @Nullable PackagingElementType<E> type,
77 @NotNull PackagingElementProcessor<E> processor,
78 final @NotNull PackagingElementResolvingContext resolvingContext,
79 final boolean processSubstitutions) {
80 return processPackagingElements(artifact.getRootElement(), type, processor, resolvingContext, processSubstitutions, artifact.getArtifactType());
83 public static <E extends PackagingElement<?>> boolean processPackagingElements(final PackagingElement<?> rootElement, @Nullable PackagingElementType<E> type,
84 @NotNull PackagingElementProcessor<E> processor,
85 final @NotNull PackagingElementResolvingContext resolvingContext,
86 final boolean processSubstituions,
87 final ArtifactType artifactType) {
88 return processElements(rootElement, type, processor, resolvingContext, processSubstituions, artifactType,
89 FList.<CompositePackagingElement<?>>emptyList(), new HashSet<PackagingElement<?>>());
92 private static <E extends PackagingElement<?>> boolean processElements(final List<? extends PackagingElement<?>> elements,
93 @Nullable PackagingElementType<E> type,
94 @NotNull PackagingElementProcessor<E> processor,
95 final @NotNull PackagingElementResolvingContext resolvingContext,
96 final boolean processSubstitutions, ArtifactType artifactType,
97 FList<CompositePackagingElement<?>> parents,
98 Set<PackagingElement<?>> processed) {
99 for (PackagingElement<?> element : elements) {
100 if (!processElements(element, type, processor, resolvingContext, processSubstitutions, artifactType, parents, processed)) {
101 return false;
104 return true;
107 private static <E extends PackagingElement<?>> boolean processElements(@NotNull PackagingElement<?> element, @Nullable PackagingElementType<E> type,
108 @NotNull PackagingElementProcessor<E> processor,
109 @NotNull PackagingElementResolvingContext resolvingContext,
110 final boolean processSubstitutions,
111 ArtifactType artifactType,
112 FList<CompositePackagingElement<?>> parents, Set<PackagingElement<?>> processed) {
113 if (!processor.shouldProcess(element) || !processed.add(element)) {
114 return true;
116 if (type == null || element.getType().equals(type)) {
117 if (!processor.process(parents, (E)element)) {
118 return false;
121 if (element instanceof CompositePackagingElement<?>) {
122 final CompositePackagingElement<?> composite = (CompositePackagingElement<?>)element;
123 return processElements(composite.getChildren(), type, processor, resolvingContext, processSubstitutions, artifactType,
124 parents.prepend(composite), processed);
126 else if (element instanceof ComplexPackagingElement<?> && processSubstitutions) {
127 final ComplexPackagingElement<?> complexElement = (ComplexPackagingElement<?>)element;
128 if (processor.shouldProcessSubstitution(complexElement)) {
129 final List<? extends PackagingElement<?>> substitution = complexElement.getSubstitution(resolvingContext, artifactType);
130 if (substitution != null) {
131 return processElements(substitution, type, processor, resolvingContext, processSubstitutions, artifactType, parents, processed);
135 return true;
138 public static void removeDuplicates(@NotNull CompositePackagingElement<?> parent) {
139 List<PackagingElement<?>> prevChildren = new ArrayList<PackagingElement<?>>();
141 List<PackagingElement<?>> toRemove = new ArrayList<PackagingElement<?>>();
142 for (PackagingElement<?> child : parent.getChildren()) {
143 if (child instanceof CompositePackagingElement<?>) {
144 removeDuplicates((CompositePackagingElement<?>)child);
146 boolean merged = false;
147 for (PackagingElement<?> prevChild : prevChildren) {
148 if (child.isEqualTo(prevChild)) {
149 if (child instanceof CompositePackagingElement<?>) {
150 for (PackagingElement<?> childElement : ((CompositePackagingElement<?>)child).getChildren()) {
151 ((CompositePackagingElement<?>)prevChild).addOrFindChild(childElement);
154 merged = true;
155 break;
158 if (merged) {
159 toRemove.add(child);
161 else {
162 prevChildren.add(child);
166 for (PackagingElement<?> child : toRemove) {
167 parent.removeChild(child);
171 public static <S> void copyProperties(ArtifactProperties<?> from, ArtifactProperties<S> to) {
172 //noinspection unchecked
173 to.loadState((S)from.getState());
176 @Nullable
177 public static String getDefaultArtifactOutputPath(@NotNull String artifactName, final @NotNull Project project) {
178 final CompilerProjectExtension extension = CompilerProjectExtension.getInstance(project);
179 if (extension == null) return null;
180 final String outputUrl = extension.getCompilerOutputUrl();
181 if (outputUrl == null) return null;
182 return VfsUtil.urlToPath(outputUrl) + "/artifacts/" + FileUtil.sanitizeFileName(artifactName);
185 public static <E extends PackagingElement<?>> boolean processElements(@NotNull List<? extends PackagingElement<?>> elements,
186 @NotNull PackagingElementResolvingContext context,
187 @NotNull ArtifactType artifactType,
188 @NotNull final Processor<E> processor) {
189 return processElements(elements, context, artifactType, new PackagingElementProcessor<E>() {
190 @Override
191 public boolean process(@NotNull List<CompositePackagingElement<?>> parents, @NotNull E e) {
192 return processor.process(e);
197 public static <E extends PackagingElement<?>> boolean processElements(@NotNull List<? extends PackagingElement<?>> elements,
198 @NotNull PackagingElementResolvingContext context,
199 @NotNull ArtifactType artifactType,
200 @NotNull PackagingElementProcessor<E> processor) {
201 for (PackagingElement<?> element : elements) {
202 if (element instanceof ComplexPackagingElement<?> && processor.shouldProcessSubstitution((ComplexPackagingElement)element)) {
203 final List<? extends PackagingElement<?>> substitution =
204 ((ComplexPackagingElement<?>)element).getSubstitution(context, artifactType);
205 if (substitution != null && !processElements(substitution, context, artifactType, processor)) {
206 return false;
209 else if (!processor.process(FList.<CompositePackagingElement<?>>emptyList(), (E)element)) {
210 return false;
213 return true;
216 public static List<PackagingElement<?>> findByRelativePath(@NotNull CompositePackagingElement<?> parent, @NotNull String relativePath,
217 @NotNull PackagingElementResolvingContext context, @NotNull ArtifactType artifactType) {
218 relativePath = StringUtil.trimStart(relativePath, "/");
219 if (relativePath.length() == 0) {
220 return new SmartList<PackagingElement<?>>(parent);
223 int i = relativePath.indexOf('/');
224 final String firstName = i != -1 ? relativePath.substring(0, i) : relativePath;
225 String tail = i != -1 ? relativePath.substring(i+1) : "";
227 final List<PackagingElement<?>> children = new SmartList<PackagingElement<?>>();
228 processElements(parent.getChildren(), context, artifactType, new Processor<PackagingElement<?>>() {
229 public boolean process(PackagingElement<?> element) {
230 if (element instanceof CompositePackagingElement && firstName.equals(((CompositePackagingElement<?>)element).getName())) {
231 children.add(element);
233 else if (element instanceof FileCopyPackagingElement) {
234 final FileCopyPackagingElement fileCopy = (FileCopyPackagingElement)element;
235 if (!fileCopy.isDirectory() && firstName.equals(fileCopy.getOutputFileName())) {
236 children.add(element);
239 return true;
243 if (tail.length() == 0) {
244 return children;
247 List<PackagingElement<?>> result = new SmartList<PackagingElement<?>>();
248 for (PackagingElement<?> child : children) {
249 if (child instanceof CompositePackagingElement) {
250 result.addAll(findByRelativePath((CompositePackagingElement<?>)child, tail, context, artifactType));
253 return result;
256 public static boolean processDirectoryChildren(@NotNull CompositePackagingElement<?> root,
257 @NotNull String relativePath,
258 @NotNull PackagingElementResolvingContext context,
259 @NotNull ArtifactType artifactType,
260 @NotNull Processor<PackagingElement<?>> processor) {
261 final List<PackagingElement<?>> dirs = findByRelativePath(root, relativePath, context, artifactType);
262 for (PackagingElement<?> dir : dirs) {
263 if (dir instanceof DirectoryPackagingElement) {
264 final List<PackagingElement<?>> children = ((DirectoryPackagingElement)dir).getChildren();
265 if (!processElements(children, context, artifactType, processor)) {
266 return false;
270 return true;
273 public static Collection<? extends Artifact> findArtifactsByFile(@NotNull final VirtualFile file, @NotNull Project project) {
274 final Collection<Pair<Artifact, String>> pairs = findContainingArtifactsWithOutputPaths(file, project);
275 final List<Artifact> result = new ArrayList<Artifact>();
276 for (Pair<Artifact, String> pair : pairs) {
277 result.add(pair.getFirst());
279 return result;
282 public static Collection<Pair<Artifact, String>> findContainingArtifactsWithOutputPaths(@NotNull final VirtualFile file, @NotNull Project project) {
283 final List<Pair<Artifact, String>> artifacts = new ArrayList<Pair<Artifact, String>>();
284 for (final Artifact artifact : ArtifactManager.getInstance(project).getArtifacts()) {
285 processPackagingElements(artifact, PackagingElementFactoryImpl.FILE_COPY_ELEMENT_TYPE, new PackagingElementProcessor<FileCopyPackagingElement>() {
286 @Override
287 public boolean process(@NotNull List<CompositePackagingElement<?>> parents,
288 @NotNull FileCopyPackagingElement fileCopyPackagingElement) {
289 final VirtualFile root = fileCopyPackagingElement.findFile();
290 if (root != null && VfsUtil.isAncestor(root, file, false)) {
291 boolean isInArchive = false;
292 for (CompositePackagingElement<?> parent : parents) {
293 if (parent instanceof ArchivePackagingElement) {
294 isInArchive = true;
295 break;
298 String path;
299 if (!isInArchive) {
300 final String relativePath;
301 if (root.equals(file)) {
302 relativePath = fileCopyPackagingElement.getOutputFileName();
304 else {
305 relativePath = VfsUtil.getRelativePath(file, root, '/');
307 path = DeploymentUtil.concatPaths(getPathFromRoot(parents, "/"), relativePath);
309 else {
310 path = null;
312 artifacts.add(Pair.create(artifact, path));
313 return false;
315 return true;
317 }, ArtifactManager.getInstance(project).getResolvingContext(), true);
319 return artifacts;
322 @Nullable
323 public static VirtualFile findSourceFileByOutputPath(Artifact artifact, String outputPath, PackagingElementResolvingContext context) {
324 final List<VirtualFile> files = findSourceFilesByOutputPath(artifact.getRootElement(), outputPath, context, artifact.getArtifactType());
325 return files.isEmpty() ? null : files.get(0);
328 @Nullable
329 public static VirtualFile findSourceFileByOutputPath(CompositePackagingElement<?> parent, String outputPath,
330 PackagingElementResolvingContext context, ArtifactType artifactType) {
331 final List<VirtualFile> files = findSourceFilesByOutputPath(parent, outputPath, context, artifactType);
332 return files.isEmpty() ? null : files.get(0);
335 public static List<VirtualFile> findSourceFilesByOutputPath(CompositePackagingElement<?> parent, String outputPath,
336 PackagingElementResolvingContext context, ArtifactType artifactType) {
337 outputPath = StringUtil.trimStart(outputPath, "/");
338 if (outputPath.length() == 0) {
339 return Collections.emptyList();
342 int i = outputPath.indexOf('/');
343 final String firstName = i != -1 ? outputPath.substring(0, i) : outputPath;
344 String tail = i != -1 ? outputPath.substring(i+1) : "";
346 final List<CompositePackagingElement<?>> compositeChildren = new SmartList<CompositePackagingElement<?>>();
347 final List<FileCopyPackagingElement> fileCopies = new SmartList<FileCopyPackagingElement>();
348 final List<FileCopyPackagingElement> dirCopies = new SmartList<FileCopyPackagingElement>();
349 processElements(parent.getChildren(), context, artifactType, new Processor<PackagingElement<?>>() {
350 public boolean process(PackagingElement<?> element) {
351 if (element instanceof CompositePackagingElement) {
352 final CompositePackagingElement<?> compositeElement = (CompositePackagingElement<?>)element;
353 if (firstName.equals(compositeElement.getName())) {
354 compositeChildren.add(compositeElement);
357 else if (element instanceof FileCopyPackagingElement) {
358 final FileCopyPackagingElement fileCopyElement = (FileCopyPackagingElement)element;
359 if (fileCopyElement.isDirectory()) {
360 dirCopies.add(fileCopyElement);
362 else if (firstName.equals(fileCopyElement.getOutputFileName())) {
363 fileCopies.add(fileCopyElement);
366 return true;
370 List<VirtualFile> result = new SmartList<VirtualFile>();
371 for (CompositePackagingElement<?> child : compositeChildren) {
372 result.addAll(findSourceFilesByOutputPath(child, tail, context, artifactType));
374 if (tail.length() == 0) {
375 for (FileCopyPackagingElement fileCopy : fileCopies) {
376 ContainerUtil.addIfNotNull(fileCopy.findFile(), result);
379 for (FileCopyPackagingElement dirCopy : dirCopies) {
380 final VirtualFile sourceRoot = dirCopy.findFile();
381 if (sourceRoot != null) {
382 ContainerUtil.addIfNotNull(sourceRoot.findFileByRelativePath(outputPath), result);
385 return result;
388 public static boolean processParents(@NotNull Artifact artifact,
389 @NotNull PackagingElementResolvingContext context,
390 @NotNull ParentElementProcessor processor,
391 int maxLevel) {
392 return processParents(artifact, context, processor, FList.<Pair<Artifact, CompositePackagingElement<?>>>emptyList(), maxLevel,
393 new HashSet<Artifact>());
396 private static boolean processParents(@NotNull final Artifact artifact, @NotNull final PackagingElementResolvingContext context,
397 @NotNull final ParentElementProcessor processor, FList<Pair<Artifact, CompositePackagingElement<?>>> pathToElement,
398 final int maxLevel, final Set<Artifact> processed) {
399 if (!processed.add(artifact)) return true;
401 final FList<Pair<Artifact, CompositePackagingElement<?>>> pathFromRoot;
402 final CompositePackagingElement<?> rootElement = artifact.getRootElement();
403 if (rootElement instanceof ArtifactRootElement<?>) {
404 pathFromRoot = pathToElement;
406 else {
407 if (!processor.process(rootElement, pathToElement, artifact)) {
408 return false;
410 pathFromRoot = pathToElement.prepend(new Pair<Artifact, CompositePackagingElement<?>>(artifact, rootElement));
412 if (pathFromRoot.size() > maxLevel) return true;
414 for (final Artifact anArtifact : context.getArtifactModel().getArtifacts()) {
415 if (processed.contains(anArtifact)) continue;
417 final PackagingElementProcessor<ArtifactPackagingElement> elementProcessor =
418 new PackagingElementProcessor<ArtifactPackagingElement>() {
419 @Override
420 public boolean shouldProcessSubstitution(ComplexPackagingElement<?> element) {
421 return !(element instanceof ArtifactPackagingElement);
424 @Override
425 public boolean process(@NotNull List<CompositePackagingElement<?>> parents, @NotNull ArtifactPackagingElement element) {
426 if (artifact.getName().equals(element.getArtifactName())) {
427 FList<Pair<Artifact, CompositePackagingElement<?>>> currentPath = pathFromRoot;
428 for (int i = 0, parentsSize = parents.size(); i < parentsSize - 1; i++) {
429 CompositePackagingElement<?> parent = parents.get(i);
430 if (!processor.process(parent, currentPath, anArtifact)) {
431 return false;
433 currentPath = currentPath.prepend(new Pair<Artifact, CompositePackagingElement<?>>(anArtifact, parent));
434 if (currentPath.size() > maxLevel) {
435 return true;
439 if (!parents.isEmpty()) {
440 CompositePackagingElement<?> lastParent = parents.get(parents.size() - 1);
441 if (lastParent instanceof ArtifactRootElement<?> && !processor.process(lastParent, currentPath, anArtifact)) {
442 return false;
445 return processParents(anArtifact, context, processor, currentPath, maxLevel, processed);
447 return true;
450 if (!processPackagingElements(anArtifact, ArtifactElementType.ARTIFACT_ELEMENT_TYPE, elementProcessor, context, true)) {
451 return false;
454 return true;
457 public static boolean isArchiveName(String name) {
458 return name.length() >= 4 && name.charAt(name.length() - 4) == '.' && StringUtil.endsWithIgnoreCase(name, "ar");
461 public static void removeChildrenRecursively(@NotNull CompositePackagingElement<?> element, @NotNull Condition<PackagingElement<?>> condition) {
462 List<PackagingElement<?>> toRemove = new ArrayList<PackagingElement<?>>();
463 for (PackagingElement<?> child : element.getChildren()) {
464 if (child instanceof CompositePackagingElement<?>) {
465 final CompositePackagingElement<?> compositeChild = (CompositePackagingElement<?>)child;
466 removeChildrenRecursively(compositeChild, condition);
467 if (compositeChild.getChildren().isEmpty()) {
468 toRemove.add(child);
471 else if (condition.value(child)) {
472 toRemove.add(child);
476 element.removeChildren(toRemove);