001 /*
002 * Copyright 2010-2015 JetBrains s.r.o.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017 package org.jetbrains.kotlin.cli.common.modules;
018
019 import com.intellij.openapi.util.io.StreamUtil;
020 import com.intellij.util.SmartList;
021 import org.jetbrains.annotations.NotNull;
022 import org.jetbrains.kotlin.cli.common.messages.MessageCollector;
023 import org.jetbrains.kotlin.cli.common.messages.MessageCollectorUtil;
024 import org.jetbrains.kotlin.cli.common.messages.OutputMessageUtil;
025 import org.xml.sax.Attributes;
026 import org.xml.sax.SAXException;
027 import org.xml.sax.helpers.DefaultHandler;
028
029 import javax.xml.parsers.ParserConfigurationException;
030 import javax.xml.parsers.SAXParser;
031 import javax.xml.parsers.SAXParserFactory;
032 import java.io.*;
033 import java.util.List;
034
035 import static org.jetbrains.kotlin.cli.common.messages.CompilerMessageLocation.NO_LOCATION;
036 import static org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity.ERROR;
037
038 public class ModuleXmlParser {
039
040 public static final String MODULES = "modules";
041 public static final String MODULE = "module";
042 public static final String NAME = "name";
043 public static final String OUTPUT_DIR = "outputDir";
044 public static final String SOURCES = "sources";
045 public static final String JAVA_SOURCE_ROOTS = "javaSourceRoots";
046 public static final String PATH = "path";
047 public static final String CLASSPATH = "classpath";
048 public static final String EXTERNAL_ANNOTATIONS = "externalAnnotations";
049
050 @NotNull
051 public static ModuleScriptData parseModuleScript(
052 @NotNull String xmlFile,
053 @NotNull MessageCollector messageCollector
054 ) {
055 FileInputStream stream = null;
056 try {
057 stream = new FileInputStream(xmlFile);
058 //noinspection IOResourceOpenedButNotSafelyClosed
059 return new ModuleXmlParser(messageCollector).parse(new BufferedInputStream(stream));
060 }
061 catch (FileNotFoundException e) {
062 MessageCollectorUtil.reportException(messageCollector, e);
063 return ModuleScriptData.EMPTY;
064 }
065 finally {
066 StreamUtil.closeStream(stream);
067 }
068 }
069
070 private final MessageCollector messageCollector;
071 private final List<Module> modules = new SmartList<Module>();
072 private DefaultHandler currentState;
073
074 private ModuleXmlParser(@NotNull MessageCollector messageCollector) {
075 this.messageCollector = messageCollector;
076 }
077
078 private void setCurrentState(@NotNull DefaultHandler currentState) {
079 this.currentState = currentState;
080 }
081
082 private ModuleScriptData parse(@NotNull InputStream xml) {
083 try {
084 setCurrentState(initial);
085 SAXParser saxParser = SAXParserFactory.newInstance().newSAXParser();
086 saxParser.parse(xml, new DelegatedSaxHandler() {
087 @NotNull
088 @Override
089 protected DefaultHandler getDelegate() {
090 return currentState;
091 }
092 });
093 return new ModuleScriptData(modules);
094 }
095 catch (ParserConfigurationException e) {
096 MessageCollectorUtil.reportException(messageCollector, e);
097 }
098 catch (SAXException e) {
099 messageCollector.report(ERROR, OutputMessageUtil.renderException(e), NO_LOCATION);
100 }
101 catch (IOException e) {
102 MessageCollectorUtil.reportException(messageCollector, e);
103 }
104 return ModuleScriptData.EMPTY;
105 }
106
107 private final DefaultHandler initial = new DefaultHandler() {
108 @Override
109 public void startElement(@NotNull String uri, @NotNull String localName, @NotNull String qName, @NotNull Attributes attributes)
110 throws SAXException {
111 if (!MODULES.equalsIgnoreCase(qName)) {
112 throw createError(qName);
113 }
114
115 setCurrentState(insideModules);
116 }
117 };
118
119 private final DefaultHandler insideModules = new DefaultHandler() {
120 @Override
121 public void startElement(@NotNull String uri, @NotNull String localName, @NotNull String qName, @NotNull Attributes attributes)
122 throws SAXException {
123 if (!MODULE.equalsIgnoreCase(qName)) {
124 throw createError(qName);
125 }
126
127 setCurrentState(new InsideModule(
128 getAttribute(attributes, NAME, qName),
129 getAttribute(attributes, OUTPUT_DIR, qName)
130 ));
131 }
132
133 @Override
134 public void endElement(String uri, @NotNull String localName, @NotNull String qName) throws SAXException {
135 if (MODULE.equalsIgnoreCase(qName) || MODULES.equalsIgnoreCase(qName)) {
136 setCurrentState(insideModules);
137 }
138 }
139 };
140
141 private class InsideModule extends DefaultHandler {
142
143 private final ModuleBuilder moduleBuilder;
144 private InsideModule(String name, String outputDir) {
145 this.moduleBuilder = new ModuleBuilder(name, outputDir);
146 modules.add(moduleBuilder);
147 }
148
149 @Override
150 public void startElement(@NotNull String uri, @NotNull String localName, @NotNull String qName, @NotNull Attributes attributes)
151 throws SAXException {
152 if (SOURCES.equalsIgnoreCase(qName)) {
153 String path = getAttribute(attributes, PATH, qName);
154 moduleBuilder.addSourceFiles(path);
155 }
156 else if (CLASSPATH.equalsIgnoreCase(qName)) {
157 String path = getAttribute(attributes, PATH, qName);
158 moduleBuilder.addClasspathEntry(path);
159 }
160 else if (EXTERNAL_ANNOTATIONS.equalsIgnoreCase(qName)) {
161 String path = getAttribute(attributes, PATH, qName);
162 moduleBuilder.addAnnotationsPathEntry(path);
163 }
164 else if (JAVA_SOURCE_ROOTS.equalsIgnoreCase(qName)) {
165 String path = getAttribute(attributes, PATH, qName);
166 moduleBuilder.addJavaSourceRoot(path);
167 }
168 else {
169 throw createError(qName);
170 }
171 }
172
173 @Override
174 public void endElement(String uri, @NotNull String localName, @NotNull String qName) throws SAXException {
175 if (MODULE.equalsIgnoreCase(qName)) {
176 setCurrentState(insideModules);
177 }
178 }
179 }
180
181 @NotNull
182 private static String getAttribute(Attributes attributes, String qName, String tag) throws SAXException {
183 String name = attributes.getValue(qName);
184 if (name == null) {
185 throw new SAXException("No '" + qName + "' attribute for " + tag);
186 }
187 return name;
188 }
189
190 private static SAXException createError(String qName) throws SAXException {
191 return new SAXException("Unexpected tag: " + qName);
192 }
193 }