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
;
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
);
59 public static void copyChildren(CompositePackagingElement
<?
> oldParent
, CompositePackagingElement
<?
> newParent
, @NotNull Project project
) {
60 for (PackagingElement
<?
> child
: oldParent
.getChildren()) {
61 newParent
.addOrFindChild(copyWithChildren(child
, project
));
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
);
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());
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
>() {
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
)) {
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
)) {
134 if (type
== null || element
.getType().equals(type
)) {
135 if (!processor
.process(parents
, (E
)element
)) {
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
);
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
);
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());
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
>() {
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
)) {
227 else if (!processor
.process(FList
.<CompositePackagingElement
<?
>>emptyList(), (E
)element
)) {
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
);
261 if (tail
.length() == 0) {
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
));
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
)) {
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());
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
<?
>>() {
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
) {
326 final String relativePath
;
327 if (root
.equals(file
) && element
instanceof FileCopyPackagingElement
) {
328 relativePath
= ((FileCopyPackagingElement
)element
).getOutputFileName();
331 relativePath
= VfsUtil
.getRelativePath(file
, root
, '/');
333 path
= DeploymentUtil
.concatPaths(getPathFromRoot(parents
, "/"), relativePath
);
338 artifacts
.add(Pair
.create(artifact
, path
));
343 }, ArtifactManager
.getInstance(project
).getResolvingContext(), true);
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);
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
);
415 public static boolean processParents(@NotNull Artifact artifact
,
416 @NotNull PackagingElementResolvingContext context
,
417 @NotNull ParentElementProcessor processor
,
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
;
434 if (!processor
.process(rootElement
, pathToElement
, artifact
)) {
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
>() {
447 public boolean shouldProcessSubstitution(ComplexPackagingElement
<?
> element
) {
448 return !(element
instanceof ArtifactPackagingElement
);
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
)) {
460 currentPath
= currentPath
.prepend(new Pair
<Artifact
, CompositePackagingElement
<?
>>(anArtifact
, parent
));
461 if (currentPath
.size() > maxLevel
) {
466 if (!parents
.isEmpty()) {
467 CompositePackagingElement
<?
> lastParent
= parents
.get(parents
.size() - 1);
468 if (lastParent
instanceof ArtifactRootElement
<?
> && !processor
.process(lastParent
, currentPath
, anArtifact
)) {
472 return processParents(anArtifact
, context
, processor
, currentPath
, maxLevel
, processed
);
477 if (!processPackagingElements(anArtifact
, ArtifactElementType
.ARTIFACT_ELEMENT_TYPE
, elementProcessor
, context
, 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()) {
498 else if (condition
.value(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
<?
>;