[duplicates] enable duplicates analysis in PyCharm/WebStorm/PhpStorm/RubyMine
[idea/community.git] / jps / model-serialization / src / org / jetbrains / jps / model / serialization / library / JpsLibraryTableSerializer.java
1 /*
2  * Copyright 2000-2016 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.library;
17
18 import com.intellij.openapi.util.JDOMUtil;
19 import com.intellij.util.containers.MultiMap;
20 import org.jdom.Element;
21 import org.jetbrains.annotations.NotNull;
22 import org.jetbrains.annotations.Nullable;
23 import org.jetbrains.jps.model.*;
24 import org.jetbrains.jps.model.java.JpsJavaLibraryType;
25 import org.jetbrains.jps.model.library.*;
26 import org.jetbrains.jps.model.library.sdk.JpsSdkType;
27 import org.jetbrains.jps.model.module.JpsModuleReference;
28 import org.jetbrains.jps.model.serialization.JpsModelSerializerExtension;
29
30 import java.util.*;
31
32 /**
33  * @author nik
34  */
35 public class JpsLibraryTableSerializer {
36   private static final JpsLibraryRootTypeSerializer[] PREDEFINED_ROOT_TYPES_SERIALIZERS = {
37     new JpsLibraryRootTypeSerializer("CLASSES", JpsOrderRootType.COMPILED, true),
38     new JpsLibraryRootTypeSerializer("SOURCES", JpsOrderRootType.SOURCES, true)
39   };
40   private static final String NAME_ATTRIBUTE = "name";
41   private static final String TYPE_ATTRIBUTE = "type";
42   private static final String PROPERTIES_TAG = "properties";
43   private static final String JAR_DIRECTORY_TAG = "jarDirectory";
44   private static final String URL_ATTRIBUTE = "url";
45   private static final String ROOT_TAG = "root";
46   private static final String RECURSIVE_ATTRIBUTE = "recursive";
47   private static final String LIBRARY_TAG = "library";
48   private static final JpsLibraryPropertiesSerializer<JpsDummyElement> JAVA_LIBRARY_PROPERTIES_SERIALIZER =
49     new JpsLibraryPropertiesSerializer<JpsDummyElement>(JpsJavaLibraryType.INSTANCE, null) {
50       @Override
51       public JpsDummyElement loadProperties(@Nullable Element propertiesElement) {
52         return JpsElementFactory.getInstance().createDummyElement();
53       }
54
55       @Override
56       public void saveProperties(JpsDummyElement properties, Element element) {
57       }
58     };
59   private static final String MODULE_LEVEL = "module";
60   private static final String PROJECT_LEVEL = "project";
61   private static final String APPLICATION_LEVEL = "application";
62
63   public static void loadLibraries(@Nullable Element libraryTableElement, JpsLibraryCollection result) {
64     for (Element libraryElement : JDOMUtil.getChildren(libraryTableElement, LIBRARY_TAG)) {
65       result.addLibrary(loadLibrary(libraryElement));
66     }
67   }
68
69   public static void saveLibraries(JpsLibraryCollection libraryCollection, Element libraryTableElement) {
70     List<JpsLibrary> libraries = new ArrayList<>();
71     for (JpsLibrary library : libraryCollection.getLibraries()) {
72       if (!(library.getType() instanceof JpsSdkType<?>)) {
73         libraries.add(library);
74       }
75     }
76
77     libraries.sort((o1, o2) -> o1.getName().compareToIgnoreCase(o2.getName()));
78
79     for (JpsLibrary library : libraries) {
80       Element libraryTag = new Element(LIBRARY_TAG);
81       saveLibrary(library, libraryTag, library.getName());
82       libraryTableElement.addContent(libraryTag);
83     }
84   }
85
86   public static JpsLibrary loadLibrary(Element libraryElement) {
87     return loadLibrary(libraryElement, libraryElement.getAttributeValue(NAME_ATTRIBUTE));
88   }
89
90   public static JpsLibrary loadLibrary(Element libraryElement, String name) {
91     String typeId = libraryElement.getAttributeValue(TYPE_ATTRIBUTE);
92     final JpsLibraryPropertiesSerializer<?> loader = getLibraryPropertiesSerializer(typeId);
93     JpsLibrary library = createLibrary(name, loader, libraryElement.getChild(PROPERTIES_TAG));
94
95     MultiMap<JpsOrderRootType, String> jarDirectories = new MultiMap<>();
96     MultiMap<JpsOrderRootType, String> recursiveJarDirectories = new MultiMap<>();
97     for (Element jarDirectory : JDOMUtil.getChildren(libraryElement, JAR_DIRECTORY_TAG)) {
98       String url = jarDirectory.getAttributeValue(URL_ATTRIBUTE);
99       String rootTypeId = jarDirectory.getAttributeValue(TYPE_ATTRIBUTE);
100       final JpsOrderRootType rootType = rootTypeId != null ? getRootType(rootTypeId) : JpsOrderRootType.COMPILED;
101       boolean recursive = Boolean.parseBoolean(jarDirectory.getAttributeValue(RECURSIVE_ATTRIBUTE));
102       jarDirectories.putValue(rootType, url);
103       if (recursive) {
104         recursiveJarDirectories.putValue(rootType, url);
105       }
106     }
107     for (Element rootsElement : libraryElement.getChildren()) {
108       final String rootTypeId = rootsElement.getName();
109       if (!rootTypeId.equals(JAR_DIRECTORY_TAG) && !rootTypeId.equals(PROPERTIES_TAG)) {
110         final JpsOrderRootType rootType = getRootType(rootTypeId);
111         for (Element rootElement : JDOMUtil.getChildren(rootsElement, ROOT_TAG)) {
112           String url = rootElement.getAttributeValue(URL_ATTRIBUTE);
113           JpsLibraryRoot.InclusionOptions options;
114           if (jarDirectories.get(rootType).contains(url)) {
115             final boolean recursive = recursiveJarDirectories.get(rootType).contains(url);
116             options = recursive ? JpsLibraryRoot.InclusionOptions.ARCHIVES_UNDER_ROOT_RECURSIVELY : JpsLibraryRoot.InclusionOptions.ARCHIVES_UNDER_ROOT;
117           }
118           else {
119             options = JpsLibraryRoot.InclusionOptions.ROOT_ITSELF;
120           }
121           library.addRoot(url, rootType, options);
122         }
123       }
124     }
125     return library;
126   }
127
128   public static void saveLibrary(JpsLibrary library, Element libraryElement, final String libraryName) {
129     if (libraryName != null) {
130       libraryElement.setAttribute(NAME_ATTRIBUTE, libraryName);
131     }
132     saveProperties((JpsTypedLibrary<?>)library, libraryElement);
133     List<Element> jarDirectoryElements = new ArrayList<>();
134     for (JpsLibraryRootTypeSerializer serializer : getSortedSerializers()) {
135       List<JpsLibraryRoot> roots = library.getRoots(serializer.getType());
136       if (roots.isEmpty() && !serializer.isWriteIfEmpty()) continue;
137
138       Element typeElement = new Element(serializer.getTypeId());
139       for (JpsLibraryRoot root : roots) {
140         typeElement.addContent(new Element(ROOT_TAG).setAttribute(URL_ATTRIBUTE, root.getUrl()));
141         if (root.getInclusionOptions() != JpsLibraryRoot.InclusionOptions.ROOT_ITSELF) {
142           Element jarDirectoryElement = new Element(JAR_DIRECTORY_TAG).setAttribute(URL_ATTRIBUTE, root.getUrl());
143           boolean recursive = root.getInclusionOptions() == JpsLibraryRoot.InclusionOptions.ARCHIVES_UNDER_ROOT_RECURSIVELY;
144           jarDirectoryElement.setAttribute(RECURSIVE_ATTRIBUTE, Boolean.toString(recursive));
145           if (!serializer.getType().equals(JpsOrderRootType.COMPILED)) {
146             jarDirectoryElement.setAttribute(TYPE_ATTRIBUTE, serializer.getTypeId());
147           }
148           jarDirectoryElements.add(jarDirectoryElement);
149         }
150       }
151       libraryElement.addContent(typeElement);
152     }
153     libraryElement.addContent(jarDirectoryElements);
154   }
155
156   private static <P extends JpsElement> void saveProperties(JpsTypedLibrary<P> library, Element libraryElement) {
157     JpsLibraryType<P> type = library.getType();
158     if (!type.equals(JpsJavaLibraryType.INSTANCE)) {
159       JpsLibraryPropertiesSerializer<P> serializer = getLibraryPropertiesSerializer(type);
160       libraryElement.setAttribute(TYPE_ATTRIBUTE, serializer.getTypeId());
161       Element element = new Element(PROPERTIES_TAG);
162       serializer.saveProperties(library.getProperties(), element);
163       if (!element.getContent().isEmpty() || !element.getAttributes().isEmpty()) {
164         libraryElement.addContent(element);
165       }
166     }
167   }
168
169   private static <P extends JpsElement> JpsLibrary createLibrary(String name, JpsLibraryPropertiesSerializer<P> loader,
170                                                                            final Element propertiesElement) {
171     return JpsElementFactory.getInstance().createLibrary(name, loader.getType(), loader.loadProperties(propertiesElement));
172   }
173
174   private static JpsOrderRootType getRootType(String rootTypeId) {
175     for (JpsLibraryRootTypeSerializer serializer : PREDEFINED_ROOT_TYPES_SERIALIZERS) {
176       if (serializer.getTypeId().equals(rootTypeId)) {
177         return serializer.getType();
178       }
179     }
180     for (JpsModelSerializerExtension extension : JpsModelSerializerExtension.getExtensions()) {
181       for (JpsLibraryRootTypeSerializer serializer : extension.getLibraryRootTypeSerializers()) {
182         if (serializer.getTypeId().equals(rootTypeId)) {
183           return serializer.getType();
184         }
185       }
186     }
187     return JpsOrderRootType.COMPILED;
188   }
189
190   private static Collection<JpsLibraryRootTypeSerializer> getSortedSerializers() {
191     List<JpsLibraryRootTypeSerializer> serializers = new ArrayList<>();
192     Collections.addAll(serializers, PREDEFINED_ROOT_TYPES_SERIALIZERS);
193     for (JpsModelSerializerExtension extension : JpsModelSerializerExtension.getExtensions()) {
194       serializers.addAll(extension.getLibraryRootTypeSerializers());
195     }
196     Collections.sort(serializers);
197     return serializers;
198   }
199
200   private static JpsLibraryPropertiesSerializer<?> getLibraryPropertiesSerializer(@Nullable String typeId) {
201     if (typeId != null) {
202       for (JpsModelSerializerExtension extension : JpsModelSerializerExtension.getExtensions()) {
203         for (JpsLibraryPropertiesSerializer<?> loader : extension.getLibraryPropertiesSerializers()) {
204           if (loader.getTypeId().equals(typeId)) {
205             return loader;
206           }
207         }
208       }
209     }
210     return JAVA_LIBRARY_PROPERTIES_SERIALIZER;
211   }
212
213   private static <P extends JpsElement> JpsLibraryPropertiesSerializer<P> getLibraryPropertiesSerializer(@NotNull JpsLibraryType<P> type) {
214     for (JpsModelSerializerExtension extension : JpsModelSerializerExtension.getExtensions()) {
215       for (JpsLibraryPropertiesSerializer<?> loader : extension.getLibraryPropertiesSerializers()) {
216         if (loader.getType().equals(type)) {
217           //noinspection unchecked
218           return (JpsLibraryPropertiesSerializer<P>)loader;
219         }
220       }
221     }
222     throw new IllegalArgumentException("unknown type library:" + type);
223   }
224
225   public static JpsElementReference<? extends JpsCompositeElement> createLibraryTableReference(String level) {
226     JpsElementFactory elementFactory = JpsElementFactory.getInstance();
227     if (level.equals(PROJECT_LEVEL)) {
228       return elementFactory.createProjectReference();
229     }
230     if (level.equals(APPLICATION_LEVEL)) {
231       return elementFactory.createGlobalReference();
232     }
233     for (JpsModelSerializerExtension extension : JpsModelSerializerExtension.getExtensions()) {
234       final JpsElementReference<? extends JpsCompositeElement> reference = extension.createLibraryTableReference(level);
235       if (reference != null) {
236         return reference;
237       }
238     }
239     throw new UnsupportedOperationException();
240   }
241
242   public static String getLevelId(JpsElementReference<? extends JpsCompositeElement> reference) {
243     if (reference instanceof JpsModuleReference) {
244       return MODULE_LEVEL;
245     }
246     JpsCompositeElement element = reference.resolve();
247     if (element instanceof JpsProject) {
248       return PROJECT_LEVEL;
249     }
250     else if (element instanceof JpsGlobal) {
251       return APPLICATION_LEVEL;
252     }
253     for (JpsModelSerializerExtension extension : JpsModelSerializerExtension.getExtensions()) {
254       String levelId = extension.getLibraryTableLevelId(reference);
255       if (levelId != null) {
256         return levelId;
257       }
258     }
259     return null;
260   }
261 }