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;
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;
23 import java.util.ArrayList;
24 import java.util.Collections;
25 import java.util.List;
27 import static com.intellij.openapi.util.JDOMUtil.getChildren;
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 = "#";
58 public static void loadRootModel(JpsModule module, @Nullable Element rootModelComponent, @Nullable JpsSdkType<?> projectSdkType) {
59 if (rootModelComponent == null) return;
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));
67 for (Element excludeElement : getChildren(contentElement, EXCLUDE_FOLDER_TAG)) {
68 module.getExcludeRootsList().addUrl(getRequiredAttribute(excludeElement, URL_ATTRIBUTE));
70 for (Element excludePatternElement : getChildren(contentElement, EXCLUDE_PATTERN_TAG)) {
71 module.addExcludePattern(url, getRequiredAttribute(excludePatternElement, EXCLUDE_PATTERN_ATTRIBUTE));
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;
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);
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);
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);
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);
115 name = GENERATED_LIBRARY_NAME_PREFIX;
117 String uniqueName = nameGenerator.generateUniqueName(name);
118 final JpsLibrary library = JpsLibraryTableSerializer.loadLibrary(moduleLibraryElement, uniqueName);
119 module.addModuleLibrary(library);
121 final JpsLibraryDependency dependency = dependenciesList.addLibraryDependency(library);
122 loadModuleDependencyProperties(dependency, orderEntry);
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);
131 if (!moduleSourceAdded) {
132 dependenciesList.addModuleSourceDependency();
135 for (JpsModelSerializerExtension extension : JpsModelSerializerExtension.getExtensions()) {
136 extension.loadRootModel(module, rootModelComponent);
141 private static String getRequiredAttribute(Element element, String attribute) {
142 final String url = element.getAttributeValue(attribute);
144 throw new JpsSerializationFormatException("'" + attribute + "' attribute is missing in '" + element.getName() + "' tag");
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);
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));
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;
169 for (JpsModelSerializerExtension extension : JpsModelSerializerExtension.getExtensions()) {
170 for (JpsModuleSourceRootPropertiesSerializer<?> serializer : extension.getModuleSourceRootPropertiesSerializers()) {
171 if (serializer.getTypeId().equals(typeAttribute)) {
176 LOG.warn("Unknown module source root type " + typeAttribute);
177 return UnknownSourceRootPropertiesSerializer.forType(UnknownSourceRootType.getInstance(typeAttribute));
180 public static void saveRootModel(JpsModule module, Element rootModelElement) {
181 for (JpsModelSerializerExtension extension : JpsModelSerializerExtension.getExtensions()) {
182 extension.saveRootModel(module, rootModelElement);
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());
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);
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()));
209 for (JpsDependencyElement dependency : module.getDependenciesList().getDependencies()) {
210 if (dependency instanceof JpsModuleSourceDependency) {
211 rootModelElement.addContent(createDependencyElement(SOURCE_FOLDER_TYPE).setAttribute("forTests", "false"));
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));
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);
227 else if (dependency instanceof JpsLibraryDependency) {
228 JpsLibraryReference reference = ((JpsLibraryDependency)dependency).getLibraryReference();
229 JpsElementReference<? extends JpsCompositeElement> parentReference = reference.getParentReference();
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);
241 element = createDependencyElement(LIBRARY_TYPE);
242 saveModuleDependencyProperties(dependency, element);
243 element.setAttribute(NAME_ATTRIBUTE, reference.getLibraryName());
244 element.setAttribute(LEVEL_ATTRIBUTE, JpsLibraryTableSerializer.getLevelId(parentReference));
246 rootModelElement.addContent(element);
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);
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);
268 serializer.saveProperties(root.getProperties(), sourceElement);
270 contentElement.addContent(sourceElement);
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);
278 for (JpsModelSerializerExtension extension : JpsModelSerializerExtension.getExtensions()) {
279 for (JpsModuleSourceRootPropertiesSerializer<?> serializer : extension.getModuleSourceRootPropertiesSerializers()) {
280 if (serializer.getType().equals(type)) {
281 return (JpsModuleSourceRootPropertiesSerializer<P>)serializer;
288 private static boolean isGeneratedName(String libraryName) {
289 return libraryName.startsWith(GENERATED_LIBRARY_NAME_PREFIX);
292 private static Element createDependencyElement(final String type) {
293 return new Element(ORDER_ENTRY_TAG).setAttribute(TYPE_ATTRIBUTE, type);
296 private static List<String> getSortedList(final List<String> list) {
297 List<String> strings = new ArrayList<>(list);
298 Collections.sort(strings);
302 private static void loadModuleDependencyProperties(JpsDependencyElement dependency, Element orderEntry) {
303 for (JpsModelSerializerExtension extension : JpsModelSerializerExtension.getExtensions()) {
304 extension.loadModuleDependencyProperties(dependency, orderEntry);
308 private static void saveModuleDependencyProperties(JpsDependencyElement dependency, Element orderEntry) {
309 for (JpsModelSerializerExtension extension : JpsModelSerializerExtension.getExtensions()) {
310 extension.saveModuleDependencyProperties(dependency, orderEntry);