diff --git a/compiler/cli/cli-common/src/org/jetbrains/jet/cli/common/modules/DelegatedSaxHandler.java b/compiler/cli/cli-common/src/org/jetbrains/jet/cli/common/modules/DelegatedSaxHandler.java new file mode 100644 index 00000000000..ef3c32b5d87 --- /dev/null +++ b/compiler/cli/cli-common/src/org/jetbrains/jet/cli/common/modules/DelegatedSaxHandler.java @@ -0,0 +1,114 @@ +/* + * Copyright 2010-2013 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.jet.cli.common.modules; + +import org.jetbrains.annotations.NotNull; +import org.xml.sax.*; +import org.xml.sax.helpers.DefaultHandler; + +import java.io.IOException; + +public abstract class DelegatedSaxHandler extends DefaultHandler { + + @NotNull + protected abstract DefaultHandler getDelegate(); + + @Override + public InputSource resolveEntity(String publicId, String systemId) throws IOException, SAXException { + return getDelegate().resolveEntity(publicId, systemId); + } + + @Override + public void notationDecl(String name, String publicId, String systemId) throws SAXException { + getDelegate().notationDecl(name, publicId, systemId); + } + + @Override + public void unparsedEntityDecl(String name, String publicId, String systemId, String notationName) throws SAXException { + getDelegate().unparsedEntityDecl(name, publicId, systemId, notationName); + } + + @Override + public void setDocumentLocator(Locator locator) { + getDelegate().setDocumentLocator(locator); + } + + @Override + public void startDocument() throws SAXException { + getDelegate().startDocument(); + } + + @Override + public void endDocument() throws SAXException { + getDelegate().endDocument(); + } + + @Override + public void startPrefixMapping(String prefix, String uri) throws SAXException { + getDelegate().startPrefixMapping(prefix, uri); + } + + @Override + public void endPrefixMapping(String prefix) throws SAXException { + getDelegate().endPrefixMapping(prefix); + } + + @Override + public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { + getDelegate().startElement(uri, localName, qName, attributes); + } + + @Override + public void endElement(String uri, String localName, String qName) throws SAXException { + getDelegate().endElement(uri, localName, qName); + } + + @Override + public void characters(char[] ch, int start, int length) throws SAXException { + getDelegate().characters(ch, start, length); + } + + @Override + public void ignorableWhitespace(char[] ch, int start, int length) throws SAXException { + getDelegate().ignorableWhitespace(ch, start, length); + } + + @Override + public void processingInstruction(String target, String data) throws SAXException { + getDelegate().processingInstruction(target, data); + } + + @Override + public void skippedEntity(String name) throws SAXException { + getDelegate().skippedEntity(name); + } + + @Override + public void warning(SAXParseException e) throws SAXException { + getDelegate().warning(e); + } + + @Override + public void error(SAXParseException e) throws SAXException { + getDelegate().error(e); + } + + @Override + public void fatalError(SAXParseException e) throws SAXException { + getDelegate().fatalError(e); + } +} diff --git a/compiler/cli/cli-common/src/org/jetbrains/jet/cli/common/modules/ModuleDescription.java b/compiler/cli/cli-common/src/org/jetbrains/jet/cli/common/modules/ModuleDescription.java new file mode 100644 index 00000000000..f28bd6ac662 --- /dev/null +++ b/compiler/cli/cli-common/src/org/jetbrains/jet/cli/common/modules/ModuleDescription.java @@ -0,0 +1,93 @@ +/* + * Copyright 2010-2013 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.jet.cli.common.modules; + +import com.intellij.util.SmartList; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +/** + * This interface duplicates {@link jet.modules.Module}, because cli-common should not depend on kotlin-runtime.jar + */ +public interface ModuleDescription { + + @NotNull + String getModuleName(); + + @NotNull + List getSourceFiles(); + + @NotNull + List getClasspathRoots(); + + @NotNull + List getAnnotationsRoots(); + + class Impl implements ModuleDescription { + + private String name; + private final List sources = new SmartList(); + private final List classpath = new SmartList(); + private final List annotations = new SmartList(); + + public void setName(String name) { + this.name = name; + } + + public void addSourcePath(String path) { + sources.add(path); + } + + public void addClassPath(String path) { + classpath.add(path); + } + + public void addAnnotationPath(String path) { + annotations.add(path); + } + + @NotNull + @Override + public String getModuleName() { + return name; + } + + @NotNull + @Override + public List getSourceFiles() { + return sources; + } + + @NotNull + @Override + public List getClasspathRoots() { + return classpath; + } + + @NotNull + @Override + public List getAnnotationsRoots() { + return annotations; + } + + @Override + public String toString() { + return name + "\n\tsources=" + sources + "\n\tclasspath=" + classpath + "\n\tannotations=" + annotations; + } + } +} diff --git a/compiler/cli/cli-common/src/org/jetbrains/jet/cli/common/modules/ModuleXmlParser.java b/compiler/cli/cli-common/src/org/jetbrains/jet/cli/common/modules/ModuleXmlParser.java new file mode 100644 index 00000000000..0ce0e521768 --- /dev/null +++ b/compiler/cli/cli-common/src/org/jetbrains/jet/cli/common/modules/ModuleXmlParser.java @@ -0,0 +1,178 @@ +/* + * Copyright 2010-2013 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.jet.cli.common.modules; + +import com.intellij.openapi.util.io.StreamUtil; +import com.intellij.util.SmartList; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.cli.common.messages.*; +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.*; +import java.util.Collections; +import java.util.List; + +import static org.jetbrains.jet.cli.common.messages.CompilerMessageLocation.*; +import static org.jetbrains.jet.cli.common.messages.CompilerMessageSeverity.*; + +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 SOURCES = "sources"; + public static final String PATH = "path"; + public static final String CLASSPATH = "classpath"; + public static final String EXTERNAL_ANNOTATIONS = "externalAnnotations"; + + @NotNull + public static List parse(@NotNull String xmlFile, @NotNull MessageCollector messageCollector) { + FileInputStream stream = null; + try { + stream = new FileInputStream(xmlFile); + //noinspection IOResourceOpenedButNotSafelyClosed + return new ModuleXmlParser(messageCollector).parse(new BufferedInputStream(stream)); + } + catch (FileNotFoundException e) { + MessageCollectorUtil.reportException(messageCollector, e); + return Collections.emptyList(); + } + finally { + StreamUtil.closeStream(stream); + } + } + + private final MessageCollector messageCollector; + private final List result = new SmartList(); + private DefaultHandler currentState; + + private ModuleXmlParser(@NotNull MessageCollector messageCollector) { + this.messageCollector = messageCollector; + } + + private void setCurrentState(@NotNull DefaultHandler currentState) { + this.currentState = currentState; + } + + private List parse(@NotNull InputStream xml) { + try { + setCurrentState(initial); + SAXParser saxParser = SAXParserFactory.newInstance().newSAXParser(); + saxParser.parse(xml, new DelegatedSaxHandler() { + @NotNull + @Override + protected DefaultHandler getDelegate() { + return currentState; + } + }); + return result; + } + catch (ParserConfigurationException e) { + MessageCollectorUtil.reportException(messageCollector, e); + } + catch (SAXException e) { + messageCollector.report(ERROR, MessageRenderer.PLAIN.renderException(e), NO_LOCATION); + } + catch (IOException e) { + MessageCollectorUtil.reportException(messageCollector, e); + } + return Collections.emptyList(); + } + + private final DefaultHandler initial = new DefaultHandler() { + @Override + public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { + if (!MODULES.equalsIgnoreCase(qName)) { + throw createError(qName); + } + + setCurrentState(insideModules); + } + }; + + private final DefaultHandler insideModules = new DefaultHandler() { + @Override + public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { + if (!MODULE.equalsIgnoreCase(qName)) { + throw createError(qName); + } + + setCurrentState(new InsideModule(getAttribute(attributes, NAME, qName))); + } + + @Override + public void endElement(String uri, String localName, String qName) throws SAXException { + if (MODULE.equalsIgnoreCase(qName) || MODULES.equalsIgnoreCase(qName)) { + setCurrentState(insideModules); + } + } + }; + + private class InsideModule extends DefaultHandler { + + private final ModuleDescription.Impl moduleDescription; + private InsideModule(String name) { + this.moduleDescription = new ModuleDescription.Impl(); + this.moduleDescription.setName(name); + result.add(moduleDescription); + } + + @Override + public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { + if (SOURCES.equalsIgnoreCase(qName)) { + String path = getAttribute(attributes, PATH, qName); + moduleDescription.addSourcePath(path); + } + else if (CLASSPATH.equalsIgnoreCase(qName)) { + String path = getAttribute(attributes, PATH, qName); + moduleDescription.addClassPath(path); + } + else if (EXTERNAL_ANNOTATIONS.equalsIgnoreCase(qName)) { + String path = getAttribute(attributes, PATH, qName); + moduleDescription.addAnnotationPath(path); + } + else { + throw createError(qName); + } + } + + @Override + public void endElement(String uri, String localName, 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; + } + + private static SAXException createError(String qName) throws SAXException { + return new SAXException("Unexpected tag: " + qName); + } +} diff --git a/compiler/testData/modules.xml/allOnce.txt b/compiler/testData/modules.xml/allOnce.txt new file mode 100644 index 00000000000..eff45bdf1a6 --- /dev/null +++ b/compiler/testData/modules.xml/allOnce.txt @@ -0,0 +1,4 @@ +name + sources=[foo] + classpath=[bar] + annotations=[baz] diff --git a/compiler/testData/modules.xml/allOnce.xml b/compiler/testData/modules.xml/allOnce.xml new file mode 100644 index 00000000000..c6aa8435b6b --- /dev/null +++ b/compiler/testData/modules.xml/allOnce.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/compiler/testData/modules.xml/comments.txt b/compiler/testData/modules.xml/comments.txt new file mode 100644 index 00000000000..c787f635a11 --- /dev/null +++ b/compiler/testData/modules.xml/comments.txt @@ -0,0 +1,4 @@ +name + sources=[foo, foo1, foo2] + classpath=[bar1, bar2] + annotations=[baz, baz1, baz2] diff --git a/compiler/testData/modules.xml/comments.xml b/compiler/testData/modules.xml/comments.xml new file mode 100644 index 00000000000..3d40d4ebd58 --- /dev/null +++ b/compiler/testData/modules.xml/comments.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/compiler/testData/modules.xml/empty.txt b/compiler/testData/modules.xml/empty.txt new file mode 100644 index 00000000000..e69de29bb2d diff --git a/compiler/testData/modules.xml/empty.xml b/compiler/testData/modules.xml/empty.xml new file mode 100644 index 00000000000..1ef80092b42 --- /dev/null +++ b/compiler/testData/modules.xml/empty.xml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/compiler/testData/modules.xml/emptyModule.txt b/compiler/testData/modules.xml/emptyModule.txt new file mode 100644 index 00000000000..d8a25da9ed9 --- /dev/null +++ b/compiler/testData/modules.xml/emptyModule.txt @@ -0,0 +1,4 @@ +name + sources=[] + classpath=[] + annotations=[] diff --git a/compiler/testData/modules.xml/emptyModule.xml b/compiler/testData/modules.xml/emptyModule.xml new file mode 100644 index 00000000000..a6f8c3f8f61 --- /dev/null +++ b/compiler/testData/modules.xml/emptyModule.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/compiler/testData/modules.xml/manyTimes.txt b/compiler/testData/modules.xml/manyTimes.txt new file mode 100644 index 00000000000..095cbf36f71 --- /dev/null +++ b/compiler/testData/modules.xml/manyTimes.txt @@ -0,0 +1,4 @@ +name + sources=[foo, foo1, foo2] + classpath=[bar, bar1, bar2] + annotations=[baz, baz1, baz2] diff --git a/compiler/testData/modules.xml/manyTimes.xml b/compiler/testData/modules.xml/manyTimes.xml new file mode 100644 index 00000000000..551691dad0b --- /dev/null +++ b/compiler/testData/modules.xml/manyTimes.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/compiler/testData/modules.xml/onlySources.txt b/compiler/testData/modules.xml/onlySources.txt new file mode 100644 index 00000000000..14fe55ba9c6 --- /dev/null +++ b/compiler/testData/modules.xml/onlySources.txt @@ -0,0 +1,4 @@ +name + sources=[foo] + classpath=[] + annotations=[] diff --git a/compiler/testData/modules.xml/onlySources.xml b/compiler/testData/modules.xml/onlySources.xml new file mode 100644 index 00000000000..7b205f3bfa7 --- /dev/null +++ b/compiler/testData/modules.xml/onlySources.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/compiler/testData/modules.xml/twoModules.txt b/compiler/testData/modules.xml/twoModules.txt new file mode 100644 index 00000000000..01635bfa581 --- /dev/null +++ b/compiler/testData/modules.xml/twoModules.txt @@ -0,0 +1,8 @@ +name + sources=[foo, foo1, foo2] + classpath=[bar, bar1, bar2] + annotations=[baz, baz1, baz2] +name2 + sources=[2foo, 2foo1, 2foo2] + classpath=[2bar, 2bar1, 2bar2] + annotations=[2baz, 2baz1, 2baz2] diff --git a/compiler/testData/modules.xml/twoModules.xml b/compiler/testData/modules.xml/twoModules.xml new file mode 100644 index 00000000000..1e905696ccd --- /dev/null +++ b/compiler/testData/modules.xml/twoModules.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/compiler/tests/org/jetbrains/jet/modules/xml/AbstractModuleXmlParserTest.java b/compiler/tests/org/jetbrains/jet/modules/xml/AbstractModuleXmlParserTest.java new file mode 100644 index 00000000000..ecdf1b91db5 --- /dev/null +++ b/compiler/tests/org/jetbrains/jet/modules/xml/AbstractModuleXmlParserTest.java @@ -0,0 +1,63 @@ +/* + * Copyright 2010-2013 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.jet.modules.xml; + +import com.intellij.openapi.util.io.FileUtil; +import junit.framework.TestCase; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.cli.common.messages.CompilerMessageLocation; +import org.jetbrains.jet.cli.common.messages.CompilerMessageSeverity; +import org.jetbrains.jet.cli.common.messages.MessageCollector; +import org.jetbrains.jet.cli.common.messages.MessageRenderer; +import org.jetbrains.jet.cli.common.modules.ModuleDescription; +import org.jetbrains.jet.cli.common.modules.ModuleXmlParser; +import org.jetbrains.jet.utils.Printer; + +import java.io.File; +import java.io.IOException; +import java.util.List; + +public abstract class AbstractModuleXmlParserTest extends TestCase { + + protected void doTest(String xmlPath) throws IOException { + File txtFile = new File(FileUtil.getNameWithoutExtension(xmlPath) + ".txt"); + + List result = ModuleXmlParser.parse(xmlPath, new MessageCollector() { + @Override + public void report( + @NotNull CompilerMessageSeverity severity, @NotNull String message, @NotNull CompilerMessageLocation location + ) { + throw new AssertionError(MessageRenderer.PLAIN.render(severity, message, location)); + } + }); + + StringBuilder sb = new StringBuilder(); + Printer p = new Printer(sb); + for (ModuleDescription description : result) { + p.println(description); + } + + String actual = sb.toString(); + + if (!txtFile.exists()) { + FileUtil.writeToFile(txtFile, actual); + fail("Expected data file does not exist. A new file created: " + txtFile); + } + + assertEquals(FileUtil.loadFile(txtFile), actual); + } +} diff --git a/compiler/tests/org/jetbrains/jet/modules/xml/ModuleXmlParserTestGenerated.java b/compiler/tests/org/jetbrains/jet/modules/xml/ModuleXmlParserTestGenerated.java new file mode 100644 index 00000000000..57db5e5c42c --- /dev/null +++ b/compiler/tests/org/jetbrains/jet/modules/xml/ModuleXmlParserTestGenerated.java @@ -0,0 +1,68 @@ +/* + * Copyright 2010-2013 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.jet.modules.xml; + +import org.jetbrains.jet.JetTestUtils; +import org.jetbrains.jet.test.TestMetadata; + +import java.io.File; +import java.util.regex.Pattern; + +/** This class is generated by {@link org.jetbrains.jet.generators.tests.GenerateTests}. DO NOT MODIFY MANUALLY */ +@SuppressWarnings("all") +@TestMetadata("compiler/testData/modules.xml") +public class ModuleXmlParserTestGenerated extends AbstractModuleXmlParserTest { + public void testAllFilesPresentInModules_xml() throws Exception { + JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.GenerateTests", new File("compiler/testData/modules.xml"), Pattern.compile("^(.+)\\.xml$"), true); + } + + @TestMetadata("allOnce.xml") + public void testAllOnce() throws Exception { + doTest("compiler/testData/modules.xml/allOnce.xml"); + } + + @TestMetadata("comments.xml") + public void testComments() throws Exception { + doTest("compiler/testData/modules.xml/comments.xml"); + } + + @TestMetadata("empty.xml") + public void testEmpty() throws Exception { + doTest("compiler/testData/modules.xml/empty.xml"); + } + + @TestMetadata("emptyModule.xml") + public void testEmptyModule() throws Exception { + doTest("compiler/testData/modules.xml/emptyModule.xml"); + } + + @TestMetadata("manyTimes.xml") + public void testManyTimes() throws Exception { + doTest("compiler/testData/modules.xml/manyTimes.xml"); + } + + @TestMetadata("onlySources.xml") + public void testOnlySources() throws Exception { + doTest("compiler/testData/modules.xml/onlySources.xml"); + } + + @TestMetadata("twoModules.xml") + public void testTwoModules() throws Exception { + doTest("compiler/testData/modules.xml/twoModules.xml"); + } + +} diff --git a/generators/org/jetbrains/jet/generators/tests/GenerateTests.java b/generators/org/jetbrains/jet/generators/tests/GenerateTests.java index 883d1c5c524..ebfd4695151 100644 --- a/generators/org/jetbrains/jet/generators/tests/GenerateTests.java +++ b/generators/org/jetbrains/jet/generators/tests/GenerateTests.java @@ -35,6 +35,7 @@ import org.jetbrains.jet.jvm.compiler.*; import org.jetbrains.jet.lang.resolve.lazy.AbstractLazyResolveDescriptorRendererTest; import org.jetbrains.jet.lang.resolve.lazy.AbstractLazyResolveNamespaceComparingTest; import org.jetbrains.jet.lang.resolve.lazy.AbstractLazyResolveTest; +import org.jetbrains.jet.modules.xml.AbstractModuleXmlParserTest; import org.jetbrains.jet.plugin.codeInsight.codeTransformations.AbstractCodeTransformationTest; import org.jetbrains.jet.plugin.codeInsight.surroundWith.AbstractSurroundWithTest; import org.jetbrains.jet.plugin.folding.AbstractKotlinFoldingTest; @@ -203,6 +204,13 @@ public class GenerateTests { testModel("compiler/testData/lazyResolve/namespaceComparator", "doTestCheckingPrimaryConstructors") ); + generateTest( + "compiler/tests/", + "ModuleXmlParserTestGenerated", + AbstractModuleXmlParserTest.class, + testModel("compiler/testData/modules.xml", true, "xml", "doTest") + ); + generateTest( "idea/tests/", "JetPsiCheckerTestGenerated", diff --git a/ide-compiler-runner/src/org/jetbrains/jet/compiler/runner/KotlinModuleDescriptionGenerator.java b/ide-compiler-runner/src/org/jetbrains/jet/compiler/runner/KotlinModuleDescriptionGenerator.java index 0e6e515ae79..460002a3a10 100644 --- a/ide-compiler-runner/src/org/jetbrains/jet/compiler/runner/KotlinModuleDescriptionGenerator.java +++ b/ide-compiler-runner/src/org/jetbrains/jet/compiler/runner/KotlinModuleDescriptionGenerator.java @@ -32,11 +32,11 @@ public interface KotlinModuleDescriptionGenerator { Set directoriesToFilterOut ); - public interface DependencyProvider { + interface DependencyProvider { void processClassPath(@NotNull DependencyProcessor processor); } - public interface DependencyProcessor { + interface DependencyProcessor { void processClassPathSection(@NotNull String sectionDescription, @NotNull Collection files); void processAnnotationRoots(@NotNull List files); } diff --git a/ide-compiler-runner/src/org/jetbrains/jet/compiler/runner/KotlinModuleXmlGenerator.java b/ide-compiler-runner/src/org/jetbrains/jet/compiler/runner/KotlinModuleXmlGenerator.java new file mode 100644 index 00000000000..d59306ed51f --- /dev/null +++ b/ide-compiler-runner/src/org/jetbrains/jet/compiler/runner/KotlinModuleXmlGenerator.java @@ -0,0 +1,116 @@ +/* + * Copyright 2010-2013 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.jet.compiler.runner; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.utils.Printer; + +import java.io.File; +import java.util.Collection; +import java.util.List; +import java.util.Set; + +import static com.intellij.openapi.util.io.FileUtil.toSystemIndependentName; +import static com.intellij.openapi.util.text.StringUtil.escapeXml; +import static org.jetbrains.jet.cli.common.modules.ModuleXmlParser.*; + +public class KotlinModuleXmlGenerator implements KotlinModuleDescriptionGenerator { + + public static final KotlinModuleXmlGenerator INSTANCE = new KotlinModuleXmlGenerator(); + + private KotlinModuleXmlGenerator() {} + + @Override + public CharSequence generateModuleScript( + String moduleName, + DependencyProvider dependencyProvider, + List sourceFiles, + boolean tests, + final Set directoriesToFilterOut + ) { + StringBuilder xml = new StringBuilder(); + final Printer p = new Printer(xml); + + openTag(p, MODULES); + + if (tests) { + p.println(""); + } + else { + p.println(""); + } + + p.println("<", MODULE, " ", NAME, "=\"", escapeXml(moduleName), "\">"); + p.pushIndent(); + + for (File sourceFile : sourceFiles) { + p.println("<", SOURCES, " ", PATH, "=\"", getEscapedPath(sourceFile), "\"/>"); + } + + dependencyProvider.processClassPath(new DependencyProcessor() { + @Override + public void processClassPathSection(@NotNull String sectionDescription, @NotNull Collection files) { + p.println(""); + for (File file : files) { + boolean isOutput = directoriesToFilterOut.contains(file); + if (isOutput) { + // For IDEA's make (incremental compilation) purposes, output directories of the current module and its dependencies + // appear on the class path, so we are at risk of seeing the results of the previous build, i.e. if some class was + // removed in the sources, it may still be there in binaries. Thus, we delete these entries from the classpath. + p.println(""); + p.println(""); + } + } + } + + @Override + public void processAnnotationRoots(@NotNull List files) { + p.println(""); + for (File file : files) { + p.println("<", EXTERNAL_ANNOTATIONS, " ", PATH, "= \"", getEscapedPath(file), "\"/>"); + } + } + }); + + closeTag(p, MODULE); + + closeTag(p, MODULES); + return xml; + } + + private static void openTag(Printer p, String tag) { + p.println("<" + tag + ">"); + p.pushIndent(); + } + + private static void closeTag(Printer p, String tag) { + p.popIndent(); + p.println(""); + } + + private static String getEscapedPath(File sourceFile) { + return escapeXml(toSystemIndependentName(sourceFile.getPath())); + } +} diff --git a/idea/testData/modules.xml/basic.xml b/idea/testData/modules.xml/basic.xml new file mode 100644 index 00000000000..b44ef43d69f --- /dev/null +++ b/idea/testData/modules.xml/basic.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/idea/testData/modules.xml/filtered.xml b/idea/testData/modules.xml/filtered.xml new file mode 100644 index 00000000000..9c16c84f538 --- /dev/null +++ b/idea/testData/modules.xml/filtered.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + diff --git a/idea/tests/org/jetbrains/jet/modules/xml/KotlinModuleXmlGeneratorTest.java b/idea/tests/org/jetbrains/jet/modules/xml/KotlinModuleXmlGeneratorTest.java new file mode 100644 index 00000000000..aa07e5250f5 --- /dev/null +++ b/idea/tests/org/jetbrains/jet/modules/xml/KotlinModuleXmlGeneratorTest.java @@ -0,0 +1,65 @@ +/* + * Copyright 2010-2013 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.jet.modules.xml; + +import com.intellij.openapi.util.io.FileUtil; +import junit.framework.TestCase; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.compiler.runner.KotlinModuleDescriptionGenerator; +import org.jetbrains.jet.compiler.runner.KotlinModuleXmlGenerator; + +import java.io.File; +import java.util.Arrays; +import java.util.Collections; + +public class KotlinModuleXmlGeneratorTest extends TestCase { + public void testBasic() throws Exception { + String actual = KotlinModuleXmlGenerator.INSTANCE.generateModuleScript( + "name", + new KotlinModuleDescriptionGenerator.DependencyProvider() { + @Override + public void processClassPath(@NotNull KotlinModuleDescriptionGenerator.DependencyProcessor processor) { + processor.processAnnotationRoots(Arrays.asList(new File("a1/f1"), new File("a2"))); + processor.processClassPathSection("s1", Arrays.asList(new File("cp1"), new File("cp2"))); + } + }, + Arrays.asList(new File("s1"), new File("s2")), + false, + Collections.emptySet() + ).toString(); + String expected = FileUtil.loadFile(new File("idea/testData/modules.xml/basic.xml")); + assertEquals(expected, actual); + } + + public void testFiltered() throws Exception { + String actual = KotlinModuleXmlGenerator.INSTANCE.generateModuleScript( + "name", + new KotlinModuleDescriptionGenerator.DependencyProvider() { + @Override + public void processClassPath(@NotNull KotlinModuleDescriptionGenerator.DependencyProcessor processor) { + processor.processAnnotationRoots(Arrays.asList(new File("a1/f1"), new File("a2"))); + processor.processClassPathSection("s1", Arrays.asList(new File("cp1"), new File("cp2"))); + } + }, + Arrays.asList(new File("s1"), new File("s2")), + false, + Collections.singleton(new File("cp1")) + ).toString(); + String expected = FileUtil.loadFile(new File("idea/testData/modules.xml/filtered.xml")); + assertEquals(expected, actual); + } +}