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