This is an automated email from the ASF dual-hosted git repository. sunlan pushed a commit to branch GROOVY_3_0_X in repository https://gitbox.apache.org/repos/asf/groovy.git
commit d9f55d50f82131e340cc291f05eed6725f83d372 Author: Eric Milles <eric.mil...@thomsonreuters.com> AuthorDate: Fri May 29 11:28:04 2020 -0500 GROOVY-9577: maintain insertion order of imports and retain duplicates - lazily create name to node map (cherry picked from commit dbd54539291d49baf355e6b6e1afac0cc9c6b70e) --- .../java/org/codehaus/groovy/ast/ModuleNode.java | 13 +++--- .../groovy/control/io/AbstractReaderSource.java | 12 ++---- .../org/codehaus/groovy/ast/ModuleNodeTest.java | 46 +++++++++++++++------- .../groovy/syntax/parser/TestParserSupport.java | 38 ------------------ 4 files changed, 44 insertions(+), 65 deletions(-) diff --git a/src/main/java/org/codehaus/groovy/ast/ModuleNode.java b/src/main/java/org/codehaus/groovy/ast/ModuleNode.java index 7fcdfc6..4cc6870 100644 --- a/src/main/java/org/codehaus/groovy/ast/ModuleNode.java +++ b/src/main/java/org/codehaus/groovy/ast/ModuleNode.java @@ -32,12 +32,12 @@ import java.net.URI; import java.net.URISyntaxException; import java.util.ArrayList; import java.util.Collections; -import java.util.HashMap; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; +import java.util.stream.Collectors; import static org.codehaus.groovy.ast.tools.GeneralUtils.args; import static org.codehaus.groovy.ast.tools.GeneralUtils.callX; @@ -57,7 +57,7 @@ public class ModuleNode extends ASTNode implements Opcodes { private List<ClassNode> classes = new LinkedList<>(); private final List<MethodNode> methods = new ArrayList<>(); - private final Map<String, ImportNode> imports = new HashMap<>(); + private final List<ImportNode> imports = new ArrayList<>(); private final List<ImportNode> starImports = new ArrayList<>(); private final Map<String, ImportNode> staticImports = new LinkedHashMap<>(); private final Map<String, ImportNode> staticStarImports = new LinkedHashMap<>(); @@ -96,7 +96,7 @@ public class ModuleNode extends ASTNode implements Opcodes { } public List<ImportNode> getImports() { - return new ArrayList<>(imports.values()); + return Collections.unmodifiableList(imports); } public List<ImportNode> getStarImports() { @@ -125,7 +125,9 @@ public class ModuleNode extends ASTNode implements Opcodes { * @return the import node for the given alias or null if none is available */ public ImportNode getImport(final String alias) { - return imports.get(alias); + Map<String, ImportNode> aliases = getNodeMetaData("import.aliases", x -> + imports.stream().collect(Collectors.toMap(ImportNode::getAlias, n -> n, (n, m) -> m))); + return aliases.get(alias); } public void addImport(final String alias, final ClassNode type) { @@ -135,8 +137,9 @@ public class ModuleNode extends ASTNode implements Opcodes { public void addImport(final String alias, final ClassNode type, final List<AnnotationNode> annotations) { ImportNode importNode = new ImportNode(type, alias); importNode.addAnnotations(annotations); - imports.put(alias, importNode); + imports.add(importNode); + removeNodeMetaData("import.aliases"); storeLastAddedImportNode(importNode); } diff --git a/src/main/java/org/codehaus/groovy/control/io/AbstractReaderSource.java b/src/main/java/org/codehaus/groovy/control/io/AbstractReaderSource.java index c3a9d59..d31dd7b 100644 --- a/src/main/java/org/codehaus/groovy/control/io/AbstractReaderSource.java +++ b/src/main/java/org/codehaus/groovy/control/io/AbstractReaderSource.java @@ -29,15 +29,11 @@ import java.io.IOException; * provides common functionality. */ public abstract class AbstractReaderSource implements ReaderSource { - protected CompilerConfiguration configuration; // Configuration data - public AbstractReaderSource(CompilerConfiguration configuration) { - if (configuration == null) { - throw new IllegalArgumentException("Compiler configuration must not be null!"); - // ... or more relaxed? - // configuration = CompilerConfiguration.DEFAULT; - } - this.configuration = configuration; + protected CompilerConfiguration configuration; + + public AbstractReaderSource(final CompilerConfiguration configuration) { + this.configuration = configuration != null ? configuration : CompilerConfiguration.DEFAULT; } /** diff --git a/src/test/org/codehaus/groovy/ast/ModuleNodeTest.java b/src/test/org/codehaus/groovy/ast/ModuleNodeTest.java index e38a0c6..67d155f 100644 --- a/src/test/org/codehaus/groovy/ast/ModuleNodeTest.java +++ b/src/test/org/codehaus/groovy/ast/ModuleNodeTest.java @@ -18,31 +18,49 @@ */ package org.codehaus.groovy.ast; -import org.codehaus.groovy.syntax.parser.TestParserSupport; +import org.codehaus.groovy.runtime.DefaultGroovyMethods; +import org.junit.Test; -import java.util.List; +import static org.codehaus.groovy.control.ParserPlugin.buildAST; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; /** - * Tests the ClassNode + * Tests for {@link ModuleNode}. */ -public class ModuleNodeTest extends TestParserSupport { +public final class ModuleNodeTest { + @Test public void testStatementClass() { - ModuleNode module = parse("x = [1, 2, 3]; println(x)", "Cheese.groovy"); - assertFalse("Should have statements", module.getStatementBlock().isEmpty()); + ModuleNode mn = buildAST("x = [1, 2, 3]; println(x)", null, null, null); - List<ClassNode> classes = module.getClasses(); - assertEquals("Number of classes", 1, classes.size()); - - ClassNode classNode = (ClassNode) classes.get(0); - assertEquals("Class name", "Cheese", classNode.getName()); + assertEquals(1, mn.getClasses().size()); + assertTrue(mn.getClasses().get(0).getName().startsWith("Script")); + assertFalse("Should have statements", mn.getStatementBlock().isEmpty()); } - // GROOVY-9194 + @Test // GROOVY-9194 public void testScriptStartingWithHash() { ModuleNode mn = new ModuleNode((CompileUnit) null); mn.setDescription("#script.groovy"); - ClassNode cn = mn.getScriptClassDummy(); - assertEquals("Dummy class name should not be empty", "#script", cn.getName()); + + assertEquals("Dummy class name should not be empty", "#script", mn.getScriptClassDummy().getName()); + } + + @Test // GROOVY-9577 + public void testDuplicateImports() { + //@formatter:off + String source = + "import java.lang.Object\n" + + "import java.lang.Object\n" + + "import java.lang.Object as X\n"; + //@formatter:on + ModuleNode mn = buildAST(source, null, null, null); + + assertEquals(3, mn.getImports().size()); + assertEquals(3, mn.getImport("X").getLineNumber()); + assertEquals(2, mn.getImport("Object").getLineNumber()); + assertEquals("X", DefaultGroovyMethods.last(mn.getImports()).getAlias()); } } diff --git a/src/test/org/codehaus/groovy/syntax/parser/TestParserSupport.java b/src/test/org/codehaus/groovy/syntax/parser/TestParserSupport.java deleted file mode 100644 index c1fa1f7..0000000 --- a/src/test/org/codehaus/groovy/syntax/parser/TestParserSupport.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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.codehaus.groovy.syntax.parser; - -import groovy.test.GroovyTestCase; -import org.codehaus.groovy.ast.ModuleNode; -import org.codehaus.groovy.control.CompilationUnit; -import org.codehaus.groovy.control.Phases; -import org.codehaus.groovy.control.SourceUnit; - -/** - * An abstract base class useful for AST parser related test cases - */ -public abstract class TestParserSupport extends GroovyTestCase { - public ModuleNode parse(String text, String description) { - SourceUnit unit = SourceUnit.create(description, text); - CompilationUnit compUnit = new CompilationUnit(); - compUnit.addSource(unit); - compUnit.compile(Phases.CONVERSION); - return unit.getAST(); - } -}