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 }