cleanup (inspection "Java | Class structure | Utility class is not 'final'")
[idea/community.git] / jps / jps-builders / src / org / jetbrains / jps / incremental / artifacts / builders / LayoutElementBuildersRegistry.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.incremental.artifacts.builders;
3
4 import com.intellij.openapi.diagnostic.Logger;
5 import com.intellij.openapi.util.text.StringUtil;
6 import com.intellij.util.containers.ClassMap;
7 import com.intellij.util.containers.ContainerUtil;
8 import org.jetbrains.annotations.NotNull;
9 import org.jetbrains.annotations.Nullable;
10 import org.jetbrains.jps.builders.BuildTarget;
11 import org.jetbrains.jps.builders.TargetOutputIndex;
12 import org.jetbrains.jps.builders.java.JavaModuleBuildTargetType;
13 import org.jetbrains.jps.incremental.ModuleBuildTarget;
14 import org.jetbrains.jps.incremental.artifacts.instructions.ArtifactCompilerInstructionCreator;
15 import org.jetbrains.jps.incremental.artifacts.instructions.ArtifactInstructionsBuilderContext;
16 import org.jetbrains.jps.incremental.artifacts.instructions.CopyToDirectoryInstructionCreator;
17 import org.jetbrains.jps.model.artifact.JpsArtifact;
18 import org.jetbrains.jps.model.artifact.elements.*;
19 import org.jetbrains.jps.model.java.*;
20 import org.jetbrains.jps.model.module.JpsModule;
21 import org.jetbrains.jps.model.module.JpsModuleSourceRoot;
22 import org.jetbrains.jps.service.JpsServiceManager;
23 import org.jetbrains.jps.util.JpsPathUtil;
24
25 import java.io.File;
26 import java.util.Collection;
27 import java.util.Collections;
28 import java.util.List;
29 import java.util.Set;
30
31 public class LayoutElementBuildersRegistry {
32   private static final Logger LOG = Logger.getInstance(LayoutElementBuildersRegistry.class);
33
34   private static final class InstanceHolder {
35
36     static final LayoutElementBuildersRegistry ourInstance = new LayoutElementBuildersRegistry();
37   }
38   public static LayoutElementBuildersRegistry getInstance() {
39     return InstanceHolder.ourInstance;
40   }
41
42   private final ClassMap<LayoutElementBuilderService> myBuilders;
43
44   private LayoutElementBuildersRegistry() {
45     myBuilders = new ClassMap<>();
46     LayoutElementBuilderService<?>[] standardBuilders = {
47       new RootElementBuilder(), new DirectoryElementBuilder(), new ArchiveElementBuilder(), new DirectoryCopyElementBuilder(),
48       new FileCopyElementBuilder(), new ExtractedDirectoryElementBuilder(), new ModuleOutputElementBuilder(),
49       new ModuleSourceElementBuilder(),
50       new ModuleTestOutputElementBuilder(), new ComplexElementBuilder(), new ArtifactOutputElementBuilder()
51     };
52     for (LayoutElementBuilderService<?> builder : standardBuilders) {
53       myBuilders.put(builder.getElementClass(), builder);
54     }
55     for (LayoutElementBuilderService builder : JpsServiceManager.getInstance().getExtensions(LayoutElementBuilderService.class)) {
56       myBuilders.put(builder.getElementClass(), builder);
57     }
58   }
59
60   public void generateInstructions(JpsArtifact artifact, CopyToDirectoryInstructionCreator creator, ArtifactInstructionsBuilderContext context) {
61     context.enterArtifact(artifact);
62     generateInstructions(artifact.getRootElement(), creator, context);
63   }
64
65   public Collection<BuildTarget<?>> getDependencies(JpsPackagingElement element, TargetOutputIndex outputIndex) {
66     LayoutElementBuilderService builder = getElementBuilder(element);
67     if (builder != null) {
68       //noinspection unchecked
69       return builder.getDependencies(element, outputIndex);
70     }
71     return Collections.emptyList();
72   }
73
74   private void generateInstructions(JpsPackagingElement layoutElement, ArtifactCompilerInstructionCreator instructionCreator,
75                                    ArtifactInstructionsBuilderContext builderContext) {
76     final LayoutElementBuilderService builder = getElementBuilder(layoutElement);
77     if (builder != null) {
78       //noinspection unchecked
79       builder.generateInstructions(layoutElement, instructionCreator, builderContext);
80     }
81   }
82
83   private LayoutElementBuilderService<?> getElementBuilder(JpsPackagingElement layoutElement) {
84     final LayoutElementBuilderService<?> builder = myBuilders.get(layoutElement.getClass());
85     if (builder == null) {
86       LOG.error("Builder not found for artifact output layout element of class " + layoutElement.getClass());
87     }
88     return builder;
89   }
90
91   private void generateChildrenInstructions(JpsCompositePackagingElement element, ArtifactCompilerInstructionCreator instructionCreator,
92                                             ArtifactInstructionsBuilderContext builderContext) {
93     generateInstructions(element.getChildren(), instructionCreator, builderContext);
94   }
95
96   private void generateSubstitutionInstructions(JpsComplexPackagingElement element,
97                                                 ArtifactCompilerInstructionCreator instructionCreator,
98                                                 ArtifactInstructionsBuilderContext builderContext) {
99     final List<JpsPackagingElement> substitution = element.getSubstitution();
100     if (substitution != null) {
101       generateInstructions(substitution, instructionCreator, builderContext);
102     }
103   }
104
105   private void generateInstructions(final List<JpsPackagingElement> elements, ArtifactCompilerInstructionCreator instructionCreator,
106                                     ArtifactInstructionsBuilderContext builderContext) {
107     for (JpsPackagingElement child : elements) {
108       generateInstructions(child, instructionCreator, builderContext);
109     }
110   }
111
112   private static void generateModuleSourceInstructions(@NotNull List<JpsModuleSourceRoot> roots,
113                                                        @NotNull ArtifactCompilerInstructionCreator creator,
114                                                        @NotNull JpsPackagingElement contextElement) {
115     for (JpsModuleSourceRoot root : roots) {
116       File source = root.getFile();
117       ArtifactCompilerInstructionCreator target;
118       JavaSourceRootProperties javaProperties = root.getProperties(JavaModuleSourceRootTypes.SOURCES);
119       if (javaProperties != null) {
120         String prefix = javaProperties.getPackagePrefix().replace('.', '/');
121         target = creator.subFolderByRelativePath(prefix);
122       } else {
123         target = creator;
124       }
125
126       target.addDirectoryCopyInstructions(source, null, target.getInstructionsBuilder().createCopyingHandler(source, contextElement, target));
127     }
128   }
129
130   private static void generateModuleOutputInstructions(@Nullable String outputUrl,
131                                                        @NotNull ArtifactCompilerInstructionCreator creator,
132                                                        @NotNull JpsPackagingElement contextElement) {
133     if (outputUrl != null) {
134       File directory = JpsPathUtil.urlToFile(outputUrl);
135       creator.addDirectoryCopyInstructions(directory, null, creator.getInstructionsBuilder().createCopyingHandler(directory, contextElement, creator));
136     }
137   }
138
139   private class RootElementBuilder extends LayoutElementBuilderService<JpsArtifactRootElement> {
140     RootElementBuilder() {
141       super(JpsArtifactRootElement.class);
142     }
143
144     @Override
145     public void generateInstructions(JpsArtifactRootElement element, ArtifactCompilerInstructionCreator instructionCreator, ArtifactInstructionsBuilderContext builderContext) {
146       generateChildrenInstructions(element, instructionCreator, builderContext);
147     }
148   }
149
150   private class DirectoryElementBuilder extends LayoutElementBuilderService<JpsDirectoryPackagingElement> {
151     DirectoryElementBuilder() {
152       super(JpsDirectoryPackagingElement.class);
153     }
154
155     @Override
156     public void generateInstructions(JpsDirectoryPackagingElement element,
157                                      ArtifactCompilerInstructionCreator instructionCreator,
158                                      ArtifactInstructionsBuilderContext builderContext) {
159       generateChildrenInstructions(element, instructionCreator.subFolder(element.getDirectoryName()), builderContext);
160     }
161   }
162
163   private class ArchiveElementBuilder extends LayoutElementBuilderService<JpsArchivePackagingElement> {
164     ArchiveElementBuilder() {
165       super(JpsArchivePackagingElement.class);
166     }
167
168     @Override
169     public void generateInstructions(JpsArchivePackagingElement element, ArtifactCompilerInstructionCreator instructionCreator,
170                                      ArtifactInstructionsBuilderContext builderContext) {
171       generateChildrenInstructions(element, instructionCreator.archive(element.getArchiveName()), builderContext);
172     }
173   }
174
175   private static class DirectoryCopyElementBuilder extends LayoutElementBuilderService<JpsDirectoryCopyPackagingElement> {
176     DirectoryCopyElementBuilder() {
177       super(JpsDirectoryCopyPackagingElement.class);
178     }
179
180     @Override
181     public void generateInstructions(JpsDirectoryCopyPackagingElement element, ArtifactCompilerInstructionCreator instructionCreator,
182                                      ArtifactInstructionsBuilderContext builderContext) {
183       final String dirPath = element.getDirectoryPath();
184       if (dirPath != null) {
185         final File directory = new File(dirPath);
186         instructionCreator.addDirectoryCopyInstructions(directory, null, instructionCreator.getInstructionsBuilder().createCopyingHandler(directory, element, instructionCreator));
187       }
188     }
189
190     @Override
191     public Collection<? extends BuildTarget<?>> getDependencies(@NotNull JpsDirectoryCopyPackagingElement element,
192                                                                 TargetOutputIndex outputIndex) {
193       String dirPath = element.getDirectoryPath();
194       if (dirPath != null) {
195         return outputIndex.getTargetsByOutputFile(new File(dirPath));
196       }
197       return Collections.emptyList();
198     }
199   }
200
201   private static class FileCopyElementBuilder extends LayoutElementBuilderService<JpsFileCopyPackagingElement> {
202     FileCopyElementBuilder() {
203       super(JpsFileCopyPackagingElement.class);
204     }
205
206     @Override
207     public void generateInstructions(JpsFileCopyPackagingElement element, ArtifactCompilerInstructionCreator instructionCreator,
208                                      ArtifactInstructionsBuilderContext builderContext) {
209       final String filePath = element.getFilePath();
210       if (filePath != null) {
211         final File file = new File(filePath);
212         final String fileName = element.getRenamedOutputFileName();
213         String outputFileName = fileName != null ? fileName : file.getName();
214         instructionCreator.addFileCopyInstruction(file, outputFileName,
215                                                   instructionCreator.getInstructionsBuilder().createCopyingHandler(file, element, instructionCreator));
216       }
217     }
218
219     @Override
220     public Collection<? extends BuildTarget<?>> getDependencies(@NotNull JpsFileCopyPackagingElement element,
221                                                                 TargetOutputIndex outputIndex) {
222       String filePath = element.getFilePath();
223       if (filePath != null) {
224         return outputIndex.getTargetsByOutputFile(new File(filePath));
225       }
226       return Collections.emptyList();
227     }
228   }
229
230   private static class ExtractedDirectoryElementBuilder extends LayoutElementBuilderService<JpsExtractedDirectoryPackagingElement> {
231     ExtractedDirectoryElementBuilder() {
232       super(JpsExtractedDirectoryPackagingElement.class);
233     }
234
235     @Override
236     public void generateInstructions(JpsExtractedDirectoryPackagingElement element,
237                                      ArtifactCompilerInstructionCreator instructionCreator,
238                                      ArtifactInstructionsBuilderContext builderContext) {
239       final String jarPath = element.getFilePath();
240       final String pathInJar = element.getPathInJar();
241       instructionCreator.addExtractDirectoryInstruction(new File(jarPath), pathInJar);
242     }
243   }
244
245   private static class ModuleOutputElementBuilder extends LayoutElementBuilderService<JpsProductionModuleOutputPackagingElement> {
246     ModuleOutputElementBuilder() {
247       super(JpsProductionModuleOutputPackagingElement.class);
248     }
249
250     @Override
251     public void generateInstructions(JpsProductionModuleOutputPackagingElement element,
252                                      ArtifactCompilerInstructionCreator instructionCreator,
253                                      ArtifactInstructionsBuilderContext builderContext) {
254       generateModuleOutputInstructions(element.getOutputUrl(), instructionCreator, element);
255     }
256
257     @Override
258     public Collection<? extends BuildTarget<?>> getDependencies(@NotNull JpsProductionModuleOutputPackagingElement element,
259                                                                 TargetOutputIndex outputIndex) {
260       JpsModule module = element.getModuleReference().resolve();
261       if (module != null) {
262         return Collections.singletonList(new ModuleBuildTarget(module, JavaModuleBuildTargetType.PRODUCTION));
263       }
264       return Collections.emptyList();
265     }
266   }
267
268   private static class ModuleSourceElementBuilder extends LayoutElementBuilderService<JpsProductionModuleSourcePackagingElement> {
269     ModuleSourceElementBuilder() {
270       super(JpsProductionModuleSourcePackagingElement.class);
271     }
272
273     @Override
274     public void generateInstructions(JpsProductionModuleSourcePackagingElement element,
275                                      ArtifactCompilerInstructionCreator instructionCreator,
276                                      ArtifactInstructionsBuilderContext builderContext) {
277       JpsModule module = element.getModuleReference().resolve();
278       if (module != null) {
279         List<JpsModuleSourceRoot> productionSources = ContainerUtil.filter(module.getSourceRoots(), root -> JavaModuleSourceRootTypes.PRODUCTION.contains(root.getRootType()));
280         generateModuleSourceInstructions(productionSources, instructionCreator, element);
281       }
282     }
283
284     @Override
285     public Collection<? extends BuildTarget<?>> getDependencies(@NotNull JpsProductionModuleSourcePackagingElement element,
286                                                                 TargetOutputIndex outputIndex) {
287       return Collections.emptyList();
288     }
289   }
290
291   private static class ModuleTestOutputElementBuilder extends LayoutElementBuilderService<JpsTestModuleOutputPackagingElement> {
292     ModuleTestOutputElementBuilder() {
293       super(JpsTestModuleOutputPackagingElement.class);
294     }
295
296     @Override
297     public void generateInstructions(JpsTestModuleOutputPackagingElement element,
298                                      ArtifactCompilerInstructionCreator instructionCreator,
299                                      ArtifactInstructionsBuilderContext builderContext) {
300       generateModuleOutputInstructions(element.getOutputUrl(), instructionCreator, element);
301     }
302
303     @Override
304     public Collection<? extends BuildTarget<?>> getDependencies(@NotNull JpsTestModuleOutputPackagingElement element,
305                                                                 TargetOutputIndex outputIndex) {
306       JpsModule module = element.getModuleReference().resolve();
307       if (module != null) {
308         return Collections.singletonList(new ModuleBuildTarget(module, JavaModuleBuildTargetType.TEST));
309       }
310       return Collections.emptyList();
311     }
312   }
313
314   private class ComplexElementBuilder extends LayoutElementBuilderService<JpsComplexPackagingElement> {
315     ComplexElementBuilder() {
316       super(JpsComplexPackagingElement.class);
317     }
318
319     @Override
320     public void generateInstructions(JpsComplexPackagingElement element,
321                                      ArtifactCompilerInstructionCreator instructionCreator,
322                                      ArtifactInstructionsBuilderContext builderContext) {
323       generateSubstitutionInstructions(element, instructionCreator, builderContext);
324     }
325   }
326
327   private class ArtifactOutputElementBuilder extends LayoutElementBuilderService<JpsArtifactOutputPackagingElement> {
328     ArtifactOutputElementBuilder() {
329       super(JpsArtifactOutputPackagingElement.class);
330     }
331
332     @Override
333     public void generateInstructions(JpsArtifactOutputPackagingElement element,
334                                      ArtifactCompilerInstructionCreator instructionCreator,
335                                      ArtifactInstructionsBuilderContext builderContext) {
336       final JpsArtifact artifact = element.getArtifactReference().resolve();
337       if (artifact == null) return;
338
339       Set<JpsArtifact> parentArtifacts = builderContext.getParentArtifacts();
340       List<JpsPackagingElement> customLayout = getCustomArtifactLayout(artifact, parentArtifacts);
341
342       final String outputPath = artifact.getOutputPath();
343       if (StringUtil.isEmpty(outputPath) || customLayout != null) {
344         try {
345           if (builderContext.enterArtifact(artifact)) {
346             if (customLayout != null) {
347               LayoutElementBuildersRegistry.this.generateInstructions(customLayout, instructionCreator, builderContext);
348             }
349             else {
350               generateSubstitutionInstructions(element, instructionCreator, builderContext);
351             }
352           }
353         }
354         finally {
355           builderContext.leaveArtifact(artifact);
356         }
357         return;
358       }
359
360       final JpsPackagingElement rootElement = artifact.getRootElement();
361       final File outputDir = new File(outputPath);
362       if (rootElement instanceof JpsArchivePackagingElement) {
363         final String fileName = ((JpsArchivePackagingElement)rootElement).getArchiveName();
364         instructionCreator.addFileCopyInstruction(new File(outputDir, fileName), fileName);
365       }
366       else {
367         instructionCreator.addDirectoryCopyInstructions(outputDir);
368       }
369     }
370
371     @Nullable
372     private List<JpsPackagingElement> getCustomArtifactLayout(@NotNull JpsArtifact artifact, @NotNull Set<JpsArtifact> parentArtifacts) {
373       for (ArtifactLayoutCustomizationService service : JpsServiceManager.getInstance().getExtensions(ArtifactLayoutCustomizationService.class)) {
374         List<JpsPackagingElement> elements = service.getCustomizedLayout(artifact, parentArtifacts);
375         if (elements != null) {
376           return elements;
377         }
378       }
379       return null;
380     }
381   }
382 }