org.jetbrains.kotlin.cli.common.modules.ModuleXmlParser Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of kotlin-compiler-client-embeddable Show documentation
Show all versions of kotlin-compiler-client-embeddable Show documentation
Kotlin compiler client embeddable
/*
* Copyright 2010-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.cli.common.modules;
import com.intellij.util.SmartList;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.cli.common.messages.MessageCollector;
import org.jetbrains.kotlin.cli.common.messages.MessageCollectorUtil;
import org.jetbrains.kotlin.modules.JavaRootPath;
import org.jetbrains.kotlin.modules.Module;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import static org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity.ERROR;
public class ModuleXmlParser {
public static final String MODULES = "modules";
public static final String MODULE = "module";
public static final String NAME = "name";
public static final String TYPE = "type";
public static final String TYPE_PRODUCTION = "java-production";
public static final String TYPE_TEST = "java-test";
public static final String OUTPUT_DIR = "outputDir";
public static final String FRIEND_DIR = "friendDir";
public static final String SOURCES = "sources";
public static final String COMMON_SOURCES = "commonSources";
public static final String JAVA_SOURCE_ROOTS = "javaSourceRoots";
public static final String JAVA_SOURCE_PACKAGE_PREFIX = "packagePrefix";
public static final String PATH = "path";
public static final String CLASSPATH = "classpath";
public static final String MODULAR_JDK_ROOT = "modularJdkRoot";
@NotNull
public static ModuleChunk parseModuleScript(
@NotNull String xmlFile,
@NotNull MessageCollector messageCollector
) {
try (FileInputStream stream = new FileInputStream(xmlFile)) {
return new ModuleXmlParser(messageCollector).parse(new BufferedInputStream(stream));
}
catch (IOException e) {
MessageCollectorUtil.reportException(messageCollector, e);
return ModuleChunk.EMPTY;
}
}
private final MessageCollector messageCollector;
private final List modules = new SmartList<>();
private DefaultHandler currentState;
private ModuleXmlParser(@NotNull MessageCollector messageCollector) {
this.messageCollector = messageCollector;
}
private void setCurrentState(@NotNull DefaultHandler currentState) {
this.currentState = currentState;
}
private ModuleChunk parse(@NotNull InputStream xml) {
try {
setCurrentState(initial);
SAXParserFactory factory = SAXParserFactory.newInstance();
factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
factory.setFeature("http://xml.org/sax/features/external-general-entities", false);
factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
factory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
SAXParser saxParser = factory.newSAXParser();
saxParser.parse(xml, new DelegatedSaxHandler() {
@NotNull
@Override
protected DefaultHandler getDelegate() {
return currentState;
}
});
return new ModuleChunk(modules);
}
catch (ParserConfigurationException | IOException e) {
MessageCollectorUtil.reportException(messageCollector, e);
}
catch (SAXException e) {
messageCollector.report(ERROR, "Build file does not have a valid XML: " + e, null);
}
return ModuleChunk.EMPTY;
}
private final DefaultHandler initial = new DefaultHandler() {
@Override
public void startElement(@NotNull String uri, @NotNull String localName, @NotNull String qName, @NotNull Attributes attributes)
throws SAXException {
if (!MODULES.equalsIgnoreCase(qName)) {
throw createError(qName);
}
setCurrentState(insideModules);
}
};
private final DefaultHandler insideModules = new DefaultHandler() {
@Override
public void startElement(@NotNull String uri, @NotNull String localName, @NotNull String qName, @NotNull Attributes attributes)
throws SAXException {
if (!MODULE.equalsIgnoreCase(qName)) {
throw createError(qName);
}
String moduleType = getAttribute(attributes, TYPE, qName);
assert(TYPE_PRODUCTION.equals(moduleType) || TYPE_TEST.equals(moduleType)): "Unknown module type: " + moduleType;
setCurrentState(new InsideModule(
getAttribute(attributes, NAME, qName),
getAttribute(attributes, OUTPUT_DIR, qName),
moduleType
));
}
@Override
public void endElement(String uri, @NotNull String localName, @NotNull String qName) throws SAXException {
if (MODULE.equalsIgnoreCase(qName) || MODULES.equalsIgnoreCase(qName)) {
setCurrentState(insideModules);
}
}
};
private class InsideModule extends DefaultHandler {
private final ModuleBuilder moduleBuilder;
private InsideModule(String name, String outputDir, @NotNull String type) {
this.moduleBuilder = new ModuleBuilder(name, outputDir, type);
modules.add(moduleBuilder);
}
@Override
public void startElement(@NotNull String uri, @NotNull String localName, @NotNull String qName, @NotNull Attributes attributes)
throws SAXException {
if (SOURCES.equalsIgnoreCase(qName)) {
String path = getAttribute(attributes, PATH, qName);
moduleBuilder.addSourceFiles(path);
}
else if (COMMON_SOURCES.equalsIgnoreCase(qName)) {
String path = getAttribute(attributes, PATH, qName);
moduleBuilder.addCommonSourceFiles(path);
}
else if (FRIEND_DIR.equalsIgnoreCase(qName)) {
String path = getAttribute(attributes, PATH, qName);
moduleBuilder.addFriendDir(path);
}
else if (CLASSPATH.equalsIgnoreCase(qName)) {
String path = getAttribute(attributes, PATH, qName);
moduleBuilder.addClasspathEntry(path);
}
else if (JAVA_SOURCE_ROOTS.equalsIgnoreCase(qName)) {
String path = getAttribute(attributes, PATH, qName);
String packagePrefix = getNullableAttribute(attributes, JAVA_SOURCE_PACKAGE_PREFIX);
moduleBuilder.addJavaSourceRoot(new JavaRootPath(path, packagePrefix));
}
else if (MODULAR_JDK_ROOT.equalsIgnoreCase(qName)) {
String path = getAttribute(attributes, PATH, qName);
moduleBuilder.setModularJdkRoot(path);
}
else {
throw createError(qName);
}
}
@Override
public void endElement(String uri, @NotNull String localName, @NotNull String qName) throws SAXException {
if (MODULE.equalsIgnoreCase(qName)) {
setCurrentState(insideModules);
}
}
}
@NotNull
private static String getAttribute(Attributes attributes, String qName, String tag) throws SAXException {
String name = attributes.getValue(qName);
if (name == null) {
throw new SAXException("No '" + qName + "' attribute for " + tag);
}
return name;
}
@Nullable
private static String getNullableAttribute(Attributes attributes, String qName) throws SAXException {
return attributes.getValue(qName);
}
private static SAXException createError(String qName) throws SAXException {
return new SAXException("Unexpected tag: " + qName);
}
}