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