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