001 /*
002 * Copyright 2010-2013 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.jet.cli.common.modules;
018
019 import com.intellij.openapi.util.io.StreamUtil;
020 import com.intellij.util.SmartList;
021 import kotlin.modules.Module;
022 import kotlin.modules.ModuleBuilder;
023 import org.jetbrains.annotations.NotNull;
024 import org.jetbrains.jet.cli.common.messages.MessageCollector;
025 import org.jetbrains.jet.cli.common.messages.MessageCollectorUtil;
026 import org.jetbrains.jet.cli.common.messages.OutputMessageUtil;
027 import org.xml.sax.Attributes;
028 import org.xml.sax.SAXException;
029 import org.xml.sax.helpers.DefaultHandler;
030
031 import javax.xml.parsers.ParserConfigurationException;
032 import javax.xml.parsers.SAXParser;
033 import javax.xml.parsers.SAXParserFactory;
034 import java.io.*;
035 import java.util.List;
036
037 import static org.jetbrains.jet.cli.common.messages.CompilerMessageLocation.NO_LOCATION;
038 import static org.jetbrains.jet.cli.common.messages.CompilerMessageSeverity.ERROR;
039
040 public class ModuleXmlParser {
041
042 public static final String MODULES = "modules";
043 public static final String MODULE = "module";
044 public static final String NAME = "name";
045 public static final String OUTPUT_DIR = "outputDir";
046 public static final String SOURCES = "sources";
047 public static final String PATH = "path";
048 public static final String CLASSPATH = "classpath";
049 public static final String EXTERNAL_ANNOTATIONS = "externalAnnotations";
050
051 @NotNull
052 public static ModuleScriptData parseModuleScript(
053 @NotNull String xmlFile,
054 @NotNull MessageCollector messageCollector
055 ) {
056 FileInputStream stream = null;
057 try {
058 stream = new FileInputStream(xmlFile);
059 //noinspection IOResourceOpenedButNotSafelyClosed
060 return new ModuleXmlParser(messageCollector).parse(new BufferedInputStream(stream));
061 }
062 catch (FileNotFoundException e) {
063 MessageCollectorUtil.reportException(messageCollector, e);
064 return ModuleScriptData.EMPTY;
065 }
066 finally {
067 StreamUtil.closeStream(stream);
068 }
069 }
070
071 private final MessageCollector messageCollector;
072 private final List<Module> modules = new SmartList<Module>();
073 private DefaultHandler currentState;
074
075 private ModuleXmlParser(@NotNull MessageCollector messageCollector) {
076 this.messageCollector = messageCollector;
077 }
078
079 private void setCurrentState(@NotNull DefaultHandler currentState) {
080 this.currentState = currentState;
081 }
082
083 private ModuleScriptData parse(@NotNull InputStream xml) {
084 try {
085 setCurrentState(initial);
086 SAXParser saxParser = SAXParserFactory.newInstance().newSAXParser();
087 saxParser.parse(xml, new DelegatedSaxHandler() {
088 @NotNull
089 @Override
090 protected DefaultHandler getDelegate() {
091 return currentState;
092 }
093 });
094 return new ModuleScriptData(modules);
095 }
096 catch (ParserConfigurationException e) {
097 MessageCollectorUtil.reportException(messageCollector, e);
098 }
099 catch (SAXException e) {
100 messageCollector.report(ERROR, OutputMessageUtil.renderException(e), NO_LOCATION);
101 }
102 catch (IOException e) {
103 MessageCollectorUtil.reportException(messageCollector, e);
104 }
105 return ModuleScriptData.EMPTY;
106 }
107
108 private final DefaultHandler initial = new DefaultHandler() {
109 @Override
110 public void startElement(String uri, String localName, String qName, Attributes attributes) 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(String uri, String localName, String qName, Attributes attributes) throws SAXException {
122 if (!MODULE.equalsIgnoreCase(qName)) {
123 throw createError(qName);
124 }
125
126 setCurrentState(new InsideModule(
127 getAttribute(attributes, NAME, qName),
128 getAttribute(attributes, OUTPUT_DIR, qName)
129 ));
130 }
131
132 @Override
133 public void endElement(String uri, String localName, String qName) throws SAXException {
134 if (MODULE.equalsIgnoreCase(qName) || MODULES.equalsIgnoreCase(qName)) {
135 setCurrentState(insideModules);
136 }
137 }
138 };
139
140 private class InsideModule extends DefaultHandler {
141
142 private final ModuleBuilder moduleBuilder;
143 private InsideModule(String name, String outputDir) {
144 this.moduleBuilder = new ModuleBuilder(name, outputDir);
145 modules.add(moduleBuilder);
146 }
147
148 @Override
149 public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
150 if (SOURCES.equalsIgnoreCase(qName)) {
151 String path = getAttribute(attributes, PATH, qName);
152 moduleBuilder.addSourceFiles(path);
153 }
154 else if (CLASSPATH.equalsIgnoreCase(qName)) {
155 String path = getAttribute(attributes, PATH, qName);
156 moduleBuilder.addClasspathEntry(path);
157 }
158 else if (EXTERNAL_ANNOTATIONS.equalsIgnoreCase(qName)) {
159 String path = getAttribute(attributes, PATH, qName);
160 moduleBuilder.addAnnotationsPathEntry(path);
161 }
162 else {
163 throw createError(qName);
164 }
165 }
166
167 @Override
168 public void endElement(String uri, String localName, String qName) throws SAXException {
169 if (MODULE.equalsIgnoreCase(qName)) {
170 setCurrentState(insideModules);
171 }
172 }
173 }
174
175 @NotNull
176 private static String getAttribute(Attributes attributes, String qName, String tag) throws SAXException {
177 String name = attributes.getValue(qName);
178 if (name == null) {
179 throw new SAXException("No '" + qName + "' attribute for " + tag);
180 }
181 return name;
182 }
183
184 private static SAXException createError(String qName) throws SAXException {
185 return new SAXException("Unexpected tag: " + qName);
186 }
187 }