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