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 }