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.MessageRenderer;
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 INCREMENTAL_CACHE = "incrementalCache";
047 public static final String SOURCES = "sources";
048 public static final String PATH = "path";
049 public static final String CLASSPATH = "classpath";
050 public static final String EXTERNAL_ANNOTATIONS = "externalAnnotations";
051
052 @NotNull
053 public static ModuleScriptData parseModuleScript(
054 @NotNull String xmlFile,
055 @NotNull MessageCollector messageCollector
056 ) {
057 FileInputStream stream = null;
058 try {
059 stream = new FileInputStream(xmlFile);
060 //noinspection IOResourceOpenedButNotSafelyClosed
061 return new ModuleXmlParser(messageCollector).parse(new BufferedInputStream(stream));
062 }
063 catch (FileNotFoundException e) {
064 MessageCollectorUtil.reportException(messageCollector, e);
065 return ModuleScriptData.EMPTY;
066 }
067 finally {
068 StreamUtil.closeStream(stream);
069 }
070 }
071
072 private final MessageCollector messageCollector;
073 private String incrementalCacheDir;
074 private final List<Module> modules = new SmartList<Module>();
075 private DefaultHandler currentState;
076
077 private ModuleXmlParser(@NotNull MessageCollector messageCollector) {
078 this.messageCollector = messageCollector;
079 }
080
081 private void setCurrentState(@NotNull DefaultHandler currentState) {
082 this.currentState = currentState;
083 }
084
085 private ModuleScriptData parse(@NotNull InputStream xml) {
086 try {
087 setCurrentState(initial);
088 SAXParser saxParser = SAXParserFactory.newInstance().newSAXParser();
089 saxParser.parse(xml, new DelegatedSaxHandler() {
090 @NotNull
091 @Override
092 protected DefaultHandler getDelegate() {
093 return currentState;
094 }
095 });
096 return new ModuleScriptData(modules, incrementalCacheDir);
097 }
098 catch (ParserConfigurationException e) {
099 MessageCollectorUtil.reportException(messageCollector, e);
100 }
101 catch (SAXException e) {
102 messageCollector.report(ERROR, MessageRenderer.PLAIN.renderException(e), NO_LOCATION);
103 }
104 catch (IOException e) {
105 MessageCollectorUtil.reportException(messageCollector, e);
106 }
107 return ModuleScriptData.EMPTY;
108 }
109
110 private final DefaultHandler initial = new DefaultHandler() {
111 @Override
112 public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
113 if (!MODULES.equalsIgnoreCase(qName)) {
114 throw createError(qName);
115 }
116
117 incrementalCacheDir = attributes.getValue(INCREMENTAL_CACHE);
118 setCurrentState(insideModules);
119 }
120 };
121
122 private final DefaultHandler insideModules = new DefaultHandler() {
123 @Override
124 public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
125 if (!MODULE.equalsIgnoreCase(qName)) {
126 throw createError(qName);
127 }
128
129 setCurrentState(new InsideModule(
130 getAttribute(attributes, NAME, qName),
131 getAttribute(attributes, OUTPUT_DIR, qName)
132 ));
133 }
134
135 @Override
136 public void endElement(String uri, String localName, String qName) throws SAXException {
137 if (MODULE.equalsIgnoreCase(qName) || MODULES.equalsIgnoreCase(qName)) {
138 setCurrentState(insideModules);
139 }
140 }
141 };
142
143 private class InsideModule extends DefaultHandler {
144
145 private final ModuleBuilder moduleBuilder;
146 private InsideModule(String name, String outputDir) {
147 this.moduleBuilder = new ModuleBuilder(name, outputDir);
148 modules.add(moduleBuilder);
149 }
150
151 @Override
152 public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
153 if (SOURCES.equalsIgnoreCase(qName)) {
154 String path = getAttribute(attributes, PATH, qName);
155 moduleBuilder.addSourceFiles(path);
156 }
157 else if (CLASSPATH.equalsIgnoreCase(qName)) {
158 String path = getAttribute(attributes, PATH, qName);
159 moduleBuilder.addClasspathEntry(path);
160 }
161 else if (EXTERNAL_ANNOTATIONS.equalsIgnoreCase(qName)) {
162 String path = getAttribute(attributes, PATH, qName);
163 moduleBuilder.addAnnotationsPathEntry(path);
164 }
165 else {
166 throw createError(qName);
167 }
168 }
169
170 @Override
171 public void endElement(String uri, String localName, String qName) throws SAXException {
172 if (MODULE.equalsIgnoreCase(qName)) {
173 setCurrentState(insideModules);
174 }
175 }
176 }
177
178 @NotNull
179 private static String getAttribute(Attributes attributes, String qName, String tag) throws SAXException {
180 String name = attributes.getValue(qName);
181 if (name == null) {
182 throw new SAXException("No '" + qName + "' attribute for " + tag);
183 }
184 return name;
185 }
186
187 private static SAXException createError(String qName) throws SAXException {
188 return new SAXException("Unexpected tag: " + qName);
189 }
190 }