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