cleanup (inspection "Java | Class structure | Utility class is not 'final'")
[idea/community.git] / jps / model-serialization / src / org / jetbrains / jps / model / serialization / module / JpsModuleRootModelSerializer.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.model.serialization.module;
3
4 import com.intellij.openapi.diagnostic.Logger;
5 import com.intellij.openapi.util.io.FileUtil;
6 import com.intellij.util.text.UniqueNameGenerator;
7 import org.jdom.Element;
8 import org.jetbrains.annotations.NotNull;
9 import org.jetbrains.annotations.Nullable;
10 import org.jetbrains.jps.model.*;
11 import org.jetbrains.jps.model.java.JpsJavaSdkType;
12 import org.jetbrains.jps.model.java.JpsJavaSdkTypeWrapper;
13 import org.jetbrains.jps.model.library.JpsLibrary;
14 import org.jetbrains.jps.model.library.JpsLibraryReference;
15 import org.jetbrains.jps.model.library.sdk.JpsSdkReference;
16 import org.jetbrains.jps.model.library.sdk.JpsSdkType;
17 import org.jetbrains.jps.model.module.*;
18 import org.jetbrains.jps.model.serialization.JpsModelSerializerExtension;
19 import org.jetbrains.jps.model.serialization.impl.JpsSerializationFormatException;
20 import org.jetbrains.jps.model.serialization.library.JpsLibraryTableSerializer;
21 import org.jetbrains.jps.model.serialization.library.JpsSdkTableSerializer;
22
23 import java.util.ArrayList;
24 import java.util.Collections;
25 import java.util.List;
26
27 import static com.intellij.openapi.util.JDOMUtil.getChildren;
28
29 public final class JpsModuleRootModelSerializer {
30   private static final Logger LOG = Logger.getInstance(JpsModuleRootModelSerializer.class);
31   public static final String URL_ATTRIBUTE = "url";
32   public static final String CONTENT_TAG = "content";
33   public static final String SOURCE_FOLDER_TAG = "sourceFolder";
34   public static final String PACKAGE_PREFIX_ATTRIBUTE = "packagePrefix";
35   public static final String IS_TEST_SOURCE_ATTRIBUTE = "isTestSource";
36   public static final String EXCLUDE_FOLDER_TAG = "excludeFolder";
37   public static final String EXCLUDE_PATTERN_TAG = "excludePattern";
38   public static final String EXCLUDE_PATTERN_ATTRIBUTE = "pattern";
39   public static final String ORDER_ENTRY_TAG = "orderEntry";
40   public static final String TYPE_ATTRIBUTE = "type";
41   public static final String SOURCE_FOLDER_TYPE = "sourceFolder";
42   public static final String JDK_TYPE = "jdk";
43   public static final String JDK_NAME_ATTRIBUTE = "jdkName";
44   public static final String JDK_TYPE_ATTRIBUTE = "jdkType";
45   public static final String INHERITED_JDK_TYPE = "inheritedJdk";
46   public static final String LIBRARY_TYPE = "library";
47   public static final String NAME_ATTRIBUTE = "name";
48   public static final String LEVEL_ATTRIBUTE = "level";
49   public static final String LIBRARY_TAG = "library";
50   public static final String MODULE_LIBRARY_TYPE = "module-library";
51   public static final String MODULE_TYPE = "module";
52   public static final String MODULE_NAME_ATTRIBUTE = "module-name";
53   public static final String SOURCE_ROOT_TYPE_ATTRIBUTE = "type";
54   public static final String JAVA_SOURCE_ROOT_TYPE_ID = "java-source";
55   public static final String JAVA_TEST_ROOT_TYPE_ID = "java-test";
56   private static final String GENERATED_LIBRARY_NAME_PREFIX = "#";
57
58   public static void loadRootModel(JpsModule module, @Nullable Element rootModelComponent, @Nullable JpsSdkType<?> projectSdkType) {
59     if (rootModelComponent == null) return;
60
61     for (Element contentElement : getChildren(rootModelComponent, CONTENT_TAG)) {
62       final String url = getRequiredAttribute(contentElement, URL_ATTRIBUTE);
63       module.getContentRootsList().addUrl(url);
64       for (Element sourceElement : getChildren(contentElement, SOURCE_FOLDER_TAG)) {
65         module.addSourceRoot(loadSourceRoot(sourceElement));
66       }
67       for (Element excludeElement : getChildren(contentElement, EXCLUDE_FOLDER_TAG)) {
68         module.getExcludeRootsList().addUrl(getRequiredAttribute(excludeElement, URL_ATTRIBUTE));
69       }
70       for (Element excludePatternElement : getChildren(contentElement, EXCLUDE_PATTERN_TAG)) {
71         module.addExcludePattern(url, getRequiredAttribute(excludePatternElement, EXCLUDE_PATTERN_ATTRIBUTE));
72       }
73     }
74
75     final JpsDependenciesList dependenciesList = module.getDependenciesList();
76     dependenciesList.clear();
77     final JpsElementFactory elementFactory = JpsElementFactory.getInstance();
78     UniqueNameGenerator nameGenerator = new UniqueNameGenerator();
79     boolean moduleSourceAdded = false;
80     for (Element orderEntry : getChildren(rootModelComponent, ORDER_ENTRY_TAG)) {
81       String type = orderEntry.getAttributeValue(TYPE_ATTRIBUTE);
82       if (SOURCE_FOLDER_TYPE.equals(type)) {
83         dependenciesList.addModuleSourceDependency();
84         moduleSourceAdded = true;
85       }
86       else if (JDK_TYPE.equals(type)) {
87         String sdkName = getRequiredAttribute(orderEntry, JDK_NAME_ATTRIBUTE);
88         String sdkTypeId = orderEntry.getAttributeValue(JDK_TYPE_ATTRIBUTE);
89         final JpsSdkType<?> sdkType = JpsSdkTableSerializer.getSdkType(sdkTypeId);
90         dependenciesList.addSdkDependency(sdkType);
91         JpsSdkTableSerializer.setSdkReference(module.getSdkReferencesTable(), sdkName, sdkType);
92         if (sdkType instanceof JpsJavaSdkTypeWrapper) {
93           dependenciesList.addSdkDependency(JpsJavaSdkType.INSTANCE);
94         }
95       }
96       else if (INHERITED_JDK_TYPE.equals(type)) {
97         final JpsSdkType<?> sdkType = projectSdkType != null? projectSdkType : JpsJavaSdkType.INSTANCE;
98         dependenciesList.addSdkDependency(sdkType);
99         if (sdkType instanceof JpsJavaSdkTypeWrapper) {
100           dependenciesList.addSdkDependency(JpsJavaSdkType.INSTANCE);
101         }
102       }
103       else if (LIBRARY_TYPE.equals(type)) {
104         String name = getRequiredAttribute(orderEntry, NAME_ATTRIBUTE);
105         String level = getRequiredAttribute(orderEntry, LEVEL_ATTRIBUTE);
106         JpsElementReference<? extends JpsCompositeElement> ref = JpsLibraryTableSerializer.createLibraryTableReference(level);
107         final JpsLibraryDependency dependency = dependenciesList.addLibraryDependency(elementFactory.createLibraryReference(name, ref));
108         loadModuleDependencyProperties(dependency, orderEntry);
109       }
110       else if (MODULE_LIBRARY_TYPE.equals(type)) {
111         final Element moduleLibraryElement = orderEntry.getChild(LIBRARY_TAG);
112         if (moduleLibraryElement != null) {
113           String name = moduleLibraryElement.getAttributeValue(NAME_ATTRIBUTE);
114           if (name == null) {
115             name = GENERATED_LIBRARY_NAME_PREFIX;
116           }
117           String uniqueName = nameGenerator.generateUniqueName(name);
118           final JpsLibrary library = JpsLibraryTableSerializer.loadLibrary(moduleLibraryElement, uniqueName);
119           module.addModuleLibrary(library);
120
121           final JpsLibraryDependency dependency = dependenciesList.addLibraryDependency(library);
122           loadModuleDependencyProperties(dependency, orderEntry);
123         }
124       }
125       else if (MODULE_TYPE.equals(type)) {
126         String name = getRequiredAttribute(orderEntry, MODULE_NAME_ATTRIBUTE);
127         final JpsModuleDependency dependency = dependenciesList.addModuleDependency(elementFactory.createModuleReference(name));
128         loadModuleDependencyProperties(dependency, orderEntry);
129       }
130     }
131     if (!moduleSourceAdded) {
132       dependenciesList.addModuleSourceDependency();
133     }
134
135     for (JpsModelSerializerExtension extension : JpsModelSerializerExtension.getExtensions()) {
136       extension.loadRootModel(module, rootModelComponent);
137     }
138   }
139
140   @NotNull
141   private static String getRequiredAttribute(Element element, String attribute) {
142     final String url = element.getAttributeValue(attribute);
143     if (url == null) {
144       throw new JpsSerializationFormatException("'" + attribute + "' attribute is missing in '" + element.getName() + "' tag");
145     }
146     return url;
147   }
148
149   @NotNull
150   public static JpsModuleSourceRoot loadSourceRoot(Element sourceElement) {
151     final String sourceUrl = getRequiredAttribute(sourceElement, URL_ATTRIBUTE);
152     JpsModuleSourceRootPropertiesSerializer<?> serializer = getSourceRootPropertiesSerializer(sourceElement);
153     return createSourceRoot(sourceUrl, serializer, sourceElement);
154   }
155
156   @NotNull
157   private static <P extends JpsElement> JpsModuleSourceRoot createSourceRoot(@NotNull String url,
158                                                                              @NotNull JpsModuleSourceRootPropertiesSerializer<P> serializer,
159                                                                              @NotNull Element sourceElement) {
160     return JpsElementFactory.getInstance().createModuleSourceRoot(url, serializer.getType(), serializer.loadProperties(sourceElement));
161   }
162
163   @NotNull
164   private static JpsModuleSourceRootPropertiesSerializer<?> getSourceRootPropertiesSerializer(@NotNull Element sourceElement) {
165     String typeAttribute = sourceElement.getAttributeValue(SOURCE_ROOT_TYPE_ATTRIBUTE);
166     if (typeAttribute == null) {
167       typeAttribute = Boolean.parseBoolean(sourceElement.getAttributeValue(IS_TEST_SOURCE_ATTRIBUTE))? JAVA_TEST_ROOT_TYPE_ID : JAVA_SOURCE_ROOT_TYPE_ID;
168     }
169     for (JpsModelSerializerExtension extension : JpsModelSerializerExtension.getExtensions()) {
170       for (JpsModuleSourceRootPropertiesSerializer<?> serializer : extension.getModuleSourceRootPropertiesSerializers()) {
171         if (serializer.getTypeId().equals(typeAttribute)) {
172           return serializer;
173         }
174       }
175     }
176     LOG.warn("Unknown module source root type " + typeAttribute);
177     return UnknownSourceRootPropertiesSerializer.forType(UnknownSourceRootType.getInstance(typeAttribute));
178   }
179
180   public static void saveRootModel(JpsModule module, Element rootModelElement) {
181     for (JpsModelSerializerExtension extension : JpsModelSerializerExtension.getExtensions()) {
182       extension.saveRootModel(module, rootModelElement);
183     }
184
185     List<JpsModuleSourceRoot> sourceRoots = module.getSourceRoots();
186     List<String> excludedUrls = getSortedList(module.getExcludeRootsList().getUrls());
187     for (String url : getSortedList(module.getContentRootsList().getUrls())) {
188       Element contentElement = new Element(CONTENT_TAG);
189       contentElement.setAttribute(URL_ATTRIBUTE, url);
190       rootModelElement.addContent(contentElement);
191       for (JpsModuleSourceRoot root : sourceRoots) {
192         if (FileUtil.startsWith(root.getUrl(), url)) {
193           saveSourceRoot(contentElement, root.asTyped().getUrl(), root.asTyped());
194         }
195       }
196       for (String excludedUrl : excludedUrls) {
197         if (FileUtil.startsWith(excludedUrl, url)) {
198           Element element = new Element(EXCLUDE_FOLDER_TAG).setAttribute(URL_ATTRIBUTE, excludedUrl);
199           contentElement.addContent(element);
200         }
201       }
202       for (JpsExcludePattern pattern : module.getExcludePatterns()) {
203         if (pattern.getBaseDirUrl().equals(url)) {
204           contentElement.addContent(new Element(EXCLUDE_PATTERN_TAG).setAttribute(EXCLUDE_PATTERN_ATTRIBUTE, pattern.getPattern()));
205         }
206       }
207     }
208
209     for (JpsDependencyElement dependency : module.getDependenciesList().getDependencies()) {
210       if (dependency instanceof JpsModuleSourceDependency) {
211         rootModelElement.addContent(createDependencyElement(SOURCE_FOLDER_TYPE).setAttribute("forTests", "false"));
212       }
213       else if (dependency instanceof JpsSdkDependency) {
214         JpsSdkType<?> sdkType = ((JpsSdkDependency)dependency).getSdkType();
215         JpsSdkReferencesTable table = module.getSdkReferencesTable();
216         JpsSdkReference<?> reference = table.getSdkReference(sdkType);
217         if (reference == null) {
218           rootModelElement.addContent(createDependencyElement(INHERITED_JDK_TYPE));
219         }
220         else {
221           Element element = createDependencyElement(JDK_TYPE);
222           element.setAttribute(JDK_NAME_ATTRIBUTE, reference.getSdkName());
223           element.setAttribute(JDK_TYPE_ATTRIBUTE, JpsSdkTableSerializer.getLoader(sdkType).getTypeId());
224           rootModelElement.addContent(element);
225         }
226       }
227       else if (dependency instanceof JpsLibraryDependency) {
228         JpsLibraryReference reference = ((JpsLibraryDependency)dependency).getLibraryReference();
229         JpsElementReference<? extends JpsCompositeElement> parentReference = reference.getParentReference();
230         Element element;
231         if (parentReference instanceof JpsModuleReference) {
232           element = createDependencyElement(MODULE_LIBRARY_TYPE);
233           saveModuleDependencyProperties(dependency, element);
234           Element libraryElement = new Element(LIBRARY_TAG);
235           JpsLibrary library = reference.resolve();
236           String libraryName = library.getName();
237           JpsLibraryTableSerializer.saveLibrary(library, libraryElement, isGeneratedName(libraryName) ? null : libraryName);
238           element.addContent(libraryElement);
239         }
240         else {
241           element = createDependencyElement(LIBRARY_TYPE);
242           saveModuleDependencyProperties(dependency, element);
243           element.setAttribute(NAME_ATTRIBUTE, reference.getLibraryName());
244           element.setAttribute(LEVEL_ATTRIBUTE, JpsLibraryTableSerializer.getLevelId(parentReference));
245         }
246         rootModelElement.addContent(element);
247       }
248       else if (dependency instanceof JpsModuleDependency) {
249         Element element = createDependencyElement(MODULE_TYPE);
250         element.setAttribute(MODULE_NAME_ATTRIBUTE, ((JpsModuleDependency)dependency).getModuleReference().getModuleName());
251         saveModuleDependencyProperties(dependency, element);
252         rootModelElement.addContent(element);
253       }
254     }
255   }
256
257   public static <P extends JpsElement> void saveSourceRoot(@NotNull Element contentElement,
258                                                            final @NotNull String rootUrl,
259                                                            @NotNull JpsTypedModuleSourceRoot<P> root) {
260     Element sourceElement = new Element(SOURCE_FOLDER_TAG);
261     sourceElement.setAttribute(URL_ATTRIBUTE, rootUrl);
262     JpsModuleSourceRootPropertiesSerializer<P> serializer = getSerializer(root.getRootType());
263     if (serializer != null) {
264       String typeId = serializer.getTypeId();
265       if (!typeId.equals(JAVA_SOURCE_ROOT_TYPE_ID) && !typeId.equals(JAVA_TEST_ROOT_TYPE_ID)) {
266         sourceElement.setAttribute(SOURCE_ROOT_TYPE_ATTRIBUTE, typeId);
267       }
268       serializer.saveProperties(root.getProperties(), sourceElement);
269     }
270     contentElement.addContent(sourceElement);
271   }
272
273   @Nullable
274   private static <P extends JpsElement> JpsModuleSourceRootPropertiesSerializer<P> getSerializer(JpsModuleSourceRootType<P> type) {
275     if (type instanceof UnknownSourceRootType) {
276       return (JpsModuleSourceRootPropertiesSerializer<P>)UnknownSourceRootPropertiesSerializer.forType((UnknownSourceRootType)type);
277     }
278     for (JpsModelSerializerExtension extension : JpsModelSerializerExtension.getExtensions()) {
279       for (JpsModuleSourceRootPropertiesSerializer<?> serializer : extension.getModuleSourceRootPropertiesSerializers()) {
280         if (serializer.getType().equals(type)) {
281           return (JpsModuleSourceRootPropertiesSerializer<P>)serializer;
282         }
283       }
284     }
285     return null;
286   }
287
288   private static boolean isGeneratedName(String libraryName) {
289     return libraryName.startsWith(GENERATED_LIBRARY_NAME_PREFIX);
290   }
291
292   private static Element createDependencyElement(final String type) {
293     return new Element(ORDER_ENTRY_TAG).setAttribute(TYPE_ATTRIBUTE, type);
294   }
295
296   private static List<String> getSortedList(final List<String> list) {
297     List<String> strings = new ArrayList<>(list);
298     Collections.sort(strings);
299     return strings;
300   }
301
302   private static void loadModuleDependencyProperties(JpsDependencyElement dependency, Element orderEntry) {
303     for (JpsModelSerializerExtension extension : JpsModelSerializerExtension.getExtensions()) {
304       extension.loadModuleDependencyProperties(dependency, orderEntry);
305     }
306   }
307
308   private static void saveModuleDependencyProperties(JpsDependencyElement dependency, Element orderEntry) {
309     for (JpsModelSerializerExtension extension : JpsModelSerializerExtension.getExtensions()) {
310       extension.saveModuleDependencyProperties(dependency, orderEntry);
311     }
312   }
313 }