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