092e2f1fc8fc75c798b34524cf74023a2c374318
[idea/community.git] / jps / model-serialization / src / org / jetbrains / jps / model / serialization / library / JpsSdkTableSerializer.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.diagnostic.Logger;
19 import com.intellij.openapi.util.JDOMUtil;
20 import org.jdom.Element;
21 import org.jetbrains.annotations.NotNull;
22 import org.jetbrains.annotations.Nullable;
23 import org.jetbrains.jps.model.JpsDummyElement;
24 import org.jetbrains.jps.model.JpsElement;
25 import org.jetbrains.jps.model.JpsElementFactory;
26 import org.jetbrains.jps.model.java.JpsJavaExtensionService;
27 import org.jetbrains.jps.model.java.JpsJavaSdkType;
28 import org.jetbrains.jps.model.java.JpsJavaSdkTypeWrapper;
29 import org.jetbrains.jps.model.library.JpsLibrary;
30 import org.jetbrains.jps.model.library.JpsLibraryCollection;
31 import org.jetbrains.jps.model.library.JpsLibraryRoot;
32 import org.jetbrains.jps.model.library.JpsOrderRootType;
33 import org.jetbrains.jps.model.library.sdk.JpsSdk;
34 import org.jetbrains.jps.model.library.sdk.JpsSdkReference;
35 import org.jetbrains.jps.model.library.sdk.JpsSdkType;
36 import org.jetbrains.jps.model.module.JpsSdkReferencesTable;
37 import org.jetbrains.jps.model.serialization.JpsModelSerializerExtension;
38
39 import java.io.File;
40 import java.util.ArrayList;
41 import java.util.Arrays;
42 import java.util.Collections;
43 import java.util.List;
44
45 public class JpsSdkTableSerializer {
46   private static final Logger LOG = Logger.getInstance(JpsSdkTableSerializer.class);
47
48   private static final JpsLibraryRootTypeSerializer[] PREDEFINED_ROOT_TYPE_SERIALIZERS = {
49     new JpsLibraryRootTypeSerializer("classPath", JpsOrderRootType.COMPILED, true),
50     new JpsLibraryRootTypeSerializer("sourcePath", JpsOrderRootType.SOURCES, true)
51   };
52   private static final JpsSdkPropertiesSerializer<JpsDummyElement> JPS_JAVA_SDK_PROPERTIES_LOADER =
53     new JpsSdkPropertiesSerializer<JpsDummyElement>("JavaSDK", JpsJavaSdkType.INSTANCE) {
54       @NotNull
55       @Override
56       public JpsDummyElement loadProperties(Element propertiesElement) {
57         return JpsElementFactory.getInstance().createDummyElement();
58       }
59
60       @Override
61       public void saveProperties(@NotNull JpsDummyElement properties, @NotNull Element element) {
62       }
63     };
64   private static final String JDK_TAG = "jdk";
65   private static final String NAME_TAG = "name";
66   private static final String TYPE_TAG = "type";
67   private static final String TYPE_ATTRIBUTE = "type";
68   private static final String ROOTS_TAG = "roots";
69   private static final String ROOT_TAG = "root";
70   private static final String VERSION_TAG = "version";
71   private static final String HOME_PATH_TAG = "homePath";
72   private static final String VALUE_ATTRIBUTE = "value";
73   private static final String COMPOSITE_TYPE = "composite";
74   private static final String SIMPLE_TYPE = "simple";
75   private static final String URL_ATTRIBUTE = "url";
76   private static final String ADDITIONAL_TAG = "additional";
77
78   public static void loadSdks(@Nullable Element sdkListElement, JpsLibraryCollection result) {
79     for (Element sdkElement : JDOMUtil.getChildren(sdkListElement, JDK_TAG)) {
80       result.addLibrary(loadSdk(sdkElement));
81     }
82   }
83
84   public static void saveSdks(JpsLibraryCollection libraryCollection, Element sdkListElement) {
85     for (JpsLibrary library : libraryCollection.getLibraries()) {
86       JpsElement properties = library.getProperties();
87       if (properties instanceof JpsSdk<?>) {
88         Element sdkTag = new Element(JDK_TAG);
89         saveSdk((JpsSdk<?>)properties, sdkTag);
90         sdkListElement.addContent(sdkTag);
91       }
92     }
93   }
94
95   private static JpsLibrary loadSdk(Element sdkElement) {
96     String name = getAttributeValue(sdkElement, NAME_TAG);
97     String typeId = getAttributeValue(sdkElement, TYPE_TAG);
98     LOG.debug("Loading " + typeId + " SDK '" + name + "'");
99     JpsSdkPropertiesSerializer<?> serializer = getSdkPropertiesSerializer(typeId);
100     final JpsLibrary library = createSdk(name, serializer, sdkElement);
101     final Element roots = sdkElement.getChild(ROOTS_TAG);
102     for (Element rootTypeElement : JDOMUtil.getChildren(roots)) {
103       JpsLibraryRootTypeSerializer rootTypeSerializer = getRootTypeSerializer(rootTypeElement.getName());
104       if (rootTypeSerializer != null) {
105         for (Element rootElement : rootTypeElement.getChildren()) {
106           loadRoots(rootElement, library, rootTypeSerializer.getType());
107         }
108       }
109       else {
110         LOG.info("root type serializer not found for " + rootTypeElement.getName());
111       }
112     }
113     if (LOG.isDebugEnabled()) {
114       List<File> files = library.getFiles(JpsOrderRootType.COMPILED);
115       LOG.debug(name + " SDK classpath (" + files.size() + " roots):");
116       for (File file : files) {
117         LOG.debug(" " + file.getAbsolutePath());
118       }
119     }
120     return library;
121   }
122
123   private static <P extends JpsElement> void saveSdk(final JpsSdk<P> sdk, Element sdkTag) {
124     JpsLibrary library = sdk.getParent();
125     sdkTag.setAttribute("version", "2");
126     setAttributeValue(sdkTag, NAME_TAG, library.getName());
127     JpsSdkPropertiesSerializer<P> serializer = getSdkPropertiesSerializer(sdk.getSdkType());
128     setAttributeValue(sdkTag, TYPE_TAG, serializer.getTypeId());
129     String versionString = sdk.getVersionString();
130     if (versionString != null) {
131       setAttributeValue(sdkTag, VERSION_TAG, versionString);
132     }
133     setAttributeValue(sdkTag, HOME_PATH_TAG, sdk.getHomePath());
134
135     Element rootsTag = new Element(ROOTS_TAG);
136     for (JpsLibraryRootTypeSerializer rootTypeSerializer : getRootTypeSerializers()) {
137       Element rootTypeTag = new Element(rootTypeSerializer.getTypeId());
138       Element compositeTag = new Element(ROOT_TAG);
139       compositeTag.setAttribute(TYPE_ATTRIBUTE, COMPOSITE_TYPE);
140       List<JpsLibraryRoot> roots = library.getRoots(rootTypeSerializer.getType());
141       for (JpsLibraryRoot root : roots) {
142         compositeTag.addContent(new Element(ROOT_TAG).setAttribute(TYPE_ATTRIBUTE, SIMPLE_TYPE).setAttribute(URL_ATTRIBUTE, root.getUrl()));
143       }
144       rootTypeTag.addContent(compositeTag);
145       rootsTag.addContent(rootTypeTag);
146     }
147     sdkTag.addContent(rootsTag);
148
149     Element additionalTag = new Element(ADDITIONAL_TAG);
150     serializer.saveProperties(sdk.getSdkProperties(), additionalTag);
151     sdkTag.addContent(additionalTag);
152   }
153
154   private static void setAttributeValue(Element tag, final String tagName, final String value) {
155     tag.addContent(new Element(tagName).setAttribute(VALUE_ATTRIBUTE, value));
156   }
157
158   private static void loadRoots(Element rootElement, JpsLibrary library, JpsOrderRootType rootType) {
159     final String type = rootElement.getAttributeValue(TYPE_ATTRIBUTE);
160     if (type.equals(COMPOSITE_TYPE)) {
161       for (Element element : rootElement.getChildren()) {
162         loadRoots(element, library, rootType);
163       }
164     }
165     else if (type.equals(SIMPLE_TYPE)) {
166       library.addRoot(rootElement.getAttributeValue(URL_ATTRIBUTE), rootType);
167     }
168   }
169
170   @Nullable
171   private static JpsLibraryRootTypeSerializer getRootTypeSerializer(String typeId) {
172     for (JpsLibraryRootTypeSerializer serializer : PREDEFINED_ROOT_TYPE_SERIALIZERS) {
173       if (serializer.getTypeId().equals(typeId)) {
174         return serializer;
175       }
176     }
177     for (JpsModelSerializerExtension extension : JpsModelSerializerExtension.getExtensions()) {
178       for (JpsLibraryRootTypeSerializer serializer : extension.getSdkRootTypeSerializers()) {
179         if (serializer.getTypeId().equals(typeId)) {
180           return serializer;
181         }
182       }
183     }
184     return null;
185   }
186
187   private static List<JpsLibraryRootTypeSerializer> getRootTypeSerializers() {
188     List<JpsLibraryRootTypeSerializer> serializers = new ArrayList<>(Arrays.asList(PREDEFINED_ROOT_TYPE_SERIALIZERS));
189     for (JpsModelSerializerExtension extension : JpsModelSerializerExtension.getExtensions()) {
190       serializers.addAll(extension.getSdkRootTypeSerializers());
191     }
192     Collections.sort(serializers);
193     return serializers;
194   }
195
196   private static <P extends JpsElement> JpsLibrary createSdk(String name, JpsSdkPropertiesSerializer<P> loader, Element sdkElement) {
197     String versionString = getAttributeValue(sdkElement, VERSION_TAG);
198     String homePath = getAttributeValue(sdkElement, HOME_PATH_TAG);
199     Element propertiesTag = sdkElement.getChild(ADDITIONAL_TAG);
200     P properties = loader.loadProperties(propertiesTag);
201     return JpsElementFactory.getInstance().createSdk(name, homePath, versionString, loader.getType(), properties);
202   }
203
204   public static JpsSdkPropertiesSerializer<?> getSdkPropertiesSerializer(@Nullable String typeId) {
205     for (JpsModelSerializerExtension extension : JpsModelSerializerExtension.getExtensions()) {
206       for (JpsSdkPropertiesSerializer<?> loader : extension.getSdkPropertiesSerializers()) {
207         if (loader.getTypeId().equals(typeId)) {
208           return loader;
209         }
210       }
211     }
212     return JPS_JAVA_SDK_PROPERTIES_LOADER;
213   }
214
215   public static <P extends JpsElement> JpsSdkPropertiesSerializer<P> getSdkPropertiesSerializer(JpsSdkType<P> type) {
216     for (JpsModelSerializerExtension extension : JpsModelSerializerExtension.getExtensions()) {
217       for (JpsSdkPropertiesSerializer<?> loader : extension.getSdkPropertiesSerializers()) {
218         if (loader.getType().equals(type)) {
219           //noinspection unchecked
220           return (JpsSdkPropertiesSerializer<P>)loader;
221         }
222       }
223     }
224     //noinspection unchecked
225     return (JpsSdkPropertiesSerializer<P>)JPS_JAVA_SDK_PROPERTIES_LOADER;
226   }
227
228   @Nullable
229   private static String getAttributeValue(Element element, String childName) {
230     final Element child = element.getChild(childName);
231     return child != null ? child.getAttributeValue(VALUE_ATTRIBUTE) : null;
232   }
233
234   public static JpsSdkType<?> getSdkType(@Nullable String typeId) {
235     return getSdkPropertiesSerializer(typeId).getType();
236   }
237
238   public static JpsSdkPropertiesSerializer<?> getLoader(JpsSdkType<?> type) {
239     for (JpsModelSerializerExtension extension : JpsModelSerializerExtension.getExtensions()) {
240       for (JpsSdkPropertiesSerializer<?> loader : extension.getSdkPropertiesSerializers()) {
241         if (loader.getType().equals(type)) {
242           return loader;
243         }
244       }
245     }
246     return JPS_JAVA_SDK_PROPERTIES_LOADER;
247   }
248
249   public static <P extends JpsElement> void setSdkReference(final JpsSdkReferencesTable table, String sdkName, JpsSdkType<P> sdkType) {
250     JpsSdkReference<P> reference = JpsElementFactory.getInstance().createSdkReference(sdkName, sdkType);
251     table.setSdkReference(sdkType, reference);
252     if (sdkType instanceof JpsJavaSdkTypeWrapper) {
253       JpsSdkReference<P> wrapperRef = JpsElementFactory.getInstance().createSdkReference(sdkName, sdkType);
254       table.setSdkReference(JpsJavaSdkType.INSTANCE, JpsJavaExtensionService.getInstance().createWrappedJavaSdkReference((JpsJavaSdkTypeWrapper)sdkType,
255                                                                                                                          wrapperRef));
256     }
257   }
258 }