cleanup (inspection "Java | Class structure | Utility class is not 'final'")
[idea/community.git] / jps / jps-builders / src / org / jetbrains / jps / ProjectPaths.java
1 // Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
2 package org.jetbrains.jps;
3
4 import com.intellij.openapi.util.Condition;
5 import com.intellij.openapi.util.text.StringUtil;
6 import com.intellij.util.Consumer;
7 import org.jetbrains.annotations.NotNull;
8 import org.jetbrains.annotations.Nullable;
9 import org.jetbrains.jps.model.JpsDummyElement;
10 import org.jetbrains.jps.model.java.*;
11 import org.jetbrains.jps.model.java.compiler.ProcessorConfigProfile;
12 import org.jetbrains.jps.model.library.JpsOrderRootType;
13 import org.jetbrains.jps.model.library.sdk.JpsSdk;
14 import org.jetbrains.jps.model.module.JpsDependencyElement;
15 import org.jetbrains.jps.model.module.JpsModule;
16 import org.jetbrains.jps.model.module.JpsModuleSourceRoot;
17 import org.jetbrains.jps.model.module.JpsSdkDependency;
18 import org.jetbrains.jps.util.JpsPathUtil;
19
20 import java.io.File;
21 import java.util.*;
22
23 /**
24  * @author Eugene Zhuravlev
25  */
26 public final class ProjectPaths {
27   private ProjectPaths() { }
28
29   @NotNull
30   public static Collection<File> getCompilationClasspathFiles(ModuleChunk chunk,
31                                                               boolean includeTests,
32                                                               boolean excludeMainModuleOutput,
33                                                               boolean exportedOnly) {
34     return getClasspathFiles(chunk, JpsJavaClasspathKind.compile(includeTests), excludeMainModuleOutput, ClasspathPart.WHOLE, exportedOnly);
35   }
36
37   @NotNull
38   public static Collection<File> getPlatformCompilationClasspath(ModuleChunk chunk, boolean excludeMainModuleOutput) {
39     return getClasspathFiles(chunk, JpsJavaClasspathKind.compile(chunk.containsTests()), excludeMainModuleOutput, ClasspathPart.BEFORE_PLUS_JDK, true);
40   }
41
42   @NotNull
43   public static Collection<File> getCompilationClasspath(ModuleChunk chunk, boolean excludeMainModuleOutput) {
44     return getClasspathFiles(chunk, JpsJavaClasspathKind.compile(chunk.containsTests()), excludeMainModuleOutput, ClasspathPart.AFTER_JDK, true);
45   }
46
47   @NotNull
48   public static Collection<File> getCompilationModulePath(ModuleChunk chunk, boolean excludeMainModuleOutput) {
49     return getClasspathFiles(chunk, JpsJavaClasspathKind.compile(chunk.containsTests()), excludeMainModuleOutput, ClasspathPart.AFTER_JDK, false);
50   }
51
52   @NotNull
53   private static Collection<File> getClasspathFiles(ModuleChunk chunk,
54                                                     JpsJavaClasspathKind kind,
55                                                     boolean excludeMainModuleOutput,
56                                                     ClasspathPart classpathPart,
57                                                     boolean exportedOnly) {
58     final Set<File> files = new LinkedHashSet<>();
59     for (JpsModule module : chunk.getModules()) {
60       JpsJavaDependenciesEnumerator enumerator = JpsJavaExtensionService.dependencies(module).includedIn(kind).recursively();
61       if (exportedOnly) {
62         enumerator = enumerator.exportedOnly();
63       }
64       if (classpathPart == ClasspathPart.BEFORE_JDK || classpathPart == ClasspathPart.BEFORE_PLUS_JDK) {
65         enumerator = enumerator.satisfying(new BeforeJavaSdkItemFilter(module));
66       }
67       else if (classpathPart == ClasspathPart.AFTER_JDK) {
68         enumerator = enumerator.satisfying(new AfterJavaSdkItemFilter(module));
69       }
70       JpsJavaDependenciesRootsEnumerator rootsEnumerator = enumerator.classes();
71       if (excludeMainModuleOutput) {
72         rootsEnumerator = rootsEnumerator.withoutSelfModuleOutput();
73       }
74       files.addAll(rootsEnumerator.getRoots());
75     }
76
77     if (classpathPart == ClasspathPart.BEFORE_PLUS_JDK) {
78       for (JpsModule module : chunk.getModules()) {
79         JpsSdk<JpsDummyElement> sdk = module.getSdk(JpsJavaSdkType.INSTANCE);
80         if (sdk != null) {
81           files.addAll(sdk.getParent().getFiles(JpsOrderRootType.COMPILED));
82         }
83       }
84     }
85     return files;
86   }
87
88   private static void addFile(Set<? super File> classpath, @Nullable String url) {
89     if (url != null) {
90       classpath.add(JpsPathUtil.urlToFile(url));
91     }
92   }
93
94   /**
95    * Returns a mapping "sourceRoot" -> "package prefix". A package prefix uses slashes instead of dots and ends with a trailing slash.
96    */
97   @NotNull
98   public static Map<File, String> getSourceRootsWithDependents(ModuleChunk chunk) {
99     final boolean includeTests = chunk.containsTests();
100     final Map<File, String> result = new LinkedHashMap<>();
101     processModulesRecursively(chunk, JpsJavaClasspathKind.compile(includeTests), module -> {
102       for (JpsModuleSourceRoot root : module.getSourceRoots()) {
103         if (root.getRootType().equals(JavaSourceRootType.SOURCE) ||
104             includeTests && root.getRootType().equals(JavaSourceRootType.TEST_SOURCE)) {
105           String prefix = ((JavaSourceRootProperties)root.getProperties()).getPackagePrefix();
106           if (!prefix.isEmpty()) {
107             prefix = prefix.replace('.', '/');
108             if (!prefix.endsWith("/")) {
109               prefix += "/";
110             }
111           }
112           else {
113             prefix = null;
114           }
115           result.put(JpsPathUtil.urlToFile(root.getUrl()), prefix);
116         }
117       }
118     });
119     return result;
120   }
121
122   public static Collection<File> getOutputPathsWithDependents(final ModuleChunk chunk) {
123     final boolean forTests = chunk.containsTests();
124     final Set<File> sourcePaths = new LinkedHashSet<>();
125     processModulesRecursively(chunk, JpsJavaClasspathKind.compile(forTests),
126                               module -> addFile(sourcePaths, JpsJavaExtensionService.getInstance().getOutputUrl(module, forTests)));
127     return sourcePaths;
128   }
129
130   private static void processModulesRecursively(ModuleChunk chunk, JpsJavaClasspathKind kind, Consumer<? super JpsModule> processor) {
131     JpsJavaExtensionService.getInstance().enumerateDependencies(chunk.getModules()).includedIn(kind).recursively().processModules(processor);
132   }
133
134   @Nullable
135   public static File getModuleOutputDir(JpsModule module, boolean forTests) {
136     return JpsJavaExtensionService.getInstance().getOutputDirectory(module, forTests);
137   }
138
139   @Nullable
140   public static File getAnnotationProcessorGeneratedSourcesOutputDir(JpsModule module, final boolean forTests, ProcessorConfigProfile profile) {
141     final String sourceDirName = profile.getGeneratedSourcesDirectoryName(forTests);
142     if (profile.isOutputRelativeToContentRoot()) {
143       List<String> roots = module.getContentRootsList().getUrls();
144       if (roots.isEmpty()) {
145         return null;
146       }
147       if (roots.size() > 1) {
148         roots = new ArrayList<>(roots); // sort roots to get deterministic result
149         roots.sort(Comparator.naturalOrder());
150       }
151       final File parent = JpsPathUtil.urlToFile(roots.get(0));
152       return StringUtil.isEmpty(sourceDirName)? parent : new File(parent, sourceDirName);
153     }
154
155     final File outputDir = getModuleOutputDir(module, forTests);
156     if (outputDir == null) {
157       return null;
158     }
159     return StringUtil.isEmpty(sourceDirName)? outputDir : new File(outputDir, sourceDirName);
160   }
161
162   private enum ClasspathPart {WHOLE, BEFORE_JDK, BEFORE_PLUS_JDK, AFTER_JDK}
163
164   private static class BeforeJavaSdkItemFilter implements Condition<JpsDependencyElement> {
165     private final JpsModule myModule;
166     private boolean mySdkFound;
167
168     private BeforeJavaSdkItemFilter(JpsModule module) {
169       myModule = module;
170     }
171
172     @Override
173     public boolean value(JpsDependencyElement dependency) {
174       boolean isJavaSdk = dependency instanceof JpsSdkDependency && ((JpsSdkDependency)dependency).getSdkType().equals(JpsJavaSdkType.INSTANCE);
175       if (myModule.equals(dependency.getContainingModule()) && isJavaSdk) {
176         mySdkFound = true;
177       }
178       return !mySdkFound && !isJavaSdk;
179     }
180   }
181
182   private static class AfterJavaSdkItemFilter implements Condition<JpsDependencyElement> {
183     private final JpsModule myModule;
184     private boolean mySdkFound;
185
186     private AfterJavaSdkItemFilter(JpsModule module) {
187       myModule = module;
188     }
189
190     @Override
191     public boolean value(JpsDependencyElement dependency) {
192       if (myModule.equals(dependency.getContainingModule())) {
193         if (dependency instanceof JpsSdkDependency && ((JpsSdkDependency)dependency).getSdkType().equals(JpsJavaSdkType.INSTANCE)) {
194           mySdkFound = true;
195           return false;
196         }
197       }
198       return mySdkFound;
199     }
200   }
201 }