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
;
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
);
41 public static void copyChildren(CompositePackagingElement
<?
> oldParent
, CompositePackagingElement
<?
> newParent
, @NotNull Project project
) {
42 for (PackagingElement
<?
> child
: oldParent
.getChildren()) {
43 newParent
.addOrFindChild(copyWithChildren(child
, project
));
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
);
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());
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
>() {
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
)) {
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
)) {
116 if (type
== null || element
.getType().equals(type
)) {
117 if (!processor
.process(parents
, (E
)element
)) {
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
);
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
);
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());
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
>() {
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
)) {
209 else if (!processor
.process(FList
.<CompositePackagingElement
<?
>>emptyList(), (E
)element
)) {
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
);
243 if (tail
.length() == 0) {
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
));
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
)) {
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());
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
>() {
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
) {
300 final String relativePath
;
301 if (root
.equals(file
)) {
302 relativePath
= fileCopyPackagingElement
.getOutputFileName();
305 relativePath
= VfsUtil
.getRelativePath(file
, root
, '/');
307 path
= DeploymentUtil
.concatPaths(getPathFromRoot(parents
, "/"), relativePath
);
312 artifacts
.add(Pair
.create(artifact
, path
));
317 }, ArtifactManager
.getInstance(project
).getResolvingContext(), true);
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);
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
);
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
);
388 public static boolean processParents(@NotNull Artifact artifact
,
389 @NotNull PackagingElementResolvingContext context
,
390 @NotNull ParentElementProcessor processor
,
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
;
407 if (!processor
.process(rootElement
, pathToElement
, artifact
)) {
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
>() {
420 public boolean shouldProcessSubstitution(ComplexPackagingElement
<?
> element
) {
421 return !(element
instanceof ArtifactPackagingElement
);
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
)) {
433 currentPath
= currentPath
.prepend(new Pair
<Artifact
, CompositePackagingElement
<?
>>(anArtifact
, parent
));
434 if (currentPath
.size() > maxLevel
) {
439 if (!parents
.isEmpty()) {
440 CompositePackagingElement
<?
> lastParent
= parents
.get(parents
.size() - 1);
441 if (lastParent
instanceof ArtifactRootElement
<?
> && !processor
.process(lastParent
, currentPath
, anArtifact
)) {
445 return processParents(anArtifact
, context
, processor
, currentPath
, maxLevel
, processed
);
450 if (!processPackagingElements(anArtifact
, ArtifactElementType
.ARTIFACT_ELEMENT_TYPE
, elementProcessor
, context
, 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()) {
471 else if (condition
.value(child
)) {
476 element
.removeChildren(toRemove
);