Introducing TreeUtilities.translate. diff --git a/java.hints/src/org/netbeans/modules/java/hints/introduce/IntroduceHint.java b/java.hints/src/org/netbeans/modules/java/hints/introduce/IntroduceHint.java --- a/java.hints/src/org/netbeans/modules/java/hints/introduce/IntroduceHint.java +++ b/java.hints/src/org/netbeans/modules/java/hints/introduce/IntroduceHint.java @@ -904,23 +904,6 @@ return result; } - private static ExpressionTree expressionCopy(TreePath expression, WorkingCopy copy) throws IOException, BadLocationException { - //hack: creating a copy of the expression: - String text = getExpressionText(copy, expression); - if (expression.getLeaf().getKind() == Kind.NEW_ARRAY) { - return copy.getTreeUtilities().parseVariableInitializer(text, new SourcePositions[1]); - } - return copy.getTreeUtilities().parseExpression(text, new SourcePositions[1]); - } - - private static String getExpressionText(WorkingCopy copy, TreePath expression) throws BadLocationException, IOException { - Document doc = copy.getDocument(); - int start = (int) copy.getTrees().getSourcePositions().getStartPosition(copy.getCompilationUnit(), expression.getLeaf()); - int end = (int) copy.getTrees().getSourcePositions().getEndPosition(copy.getCompilationUnit(), expression.getLeaf()); - String text = doc.getText(start, end - start); - return text; - } - private static List realArguments(final TreeMaker make, List parameters) { List realArguments = new LinkedList(); @@ -1399,8 +1382,7 @@ tm = Utilities.convertIfAnonymous(Utilities.resolveCapturedType(parameter, tm)); - //hack: creating a copy of the expression: - ExpressionTree expressionCopy = expressionCopy(resolved, parameter); + ExpressionTree expression = (ExpressionTree) resolved.getLeaf(); ModifiersTree mods; final TreeMaker make = parameter.getTreeMaker(); @@ -1425,7 +1407,7 @@ mods = make.Modifiers(localAccess); - VariableTree constant = make.Variable(mods, name, make.Type(tm), expressionCopy); + VariableTree constant = make.Variable(mods, name, make.Type(tm), expression); ClassTree nueClass = GeneratorUtils.insertClassMember(parameter, pathToClass, constant); parameter.rewrite(pathToClass.getLeaf(), nueClass); @@ -1476,7 +1458,7 @@ List nueStatements = new LinkedList(statements.getStatements()); mods = make.Modifiers(declareFinal ? EnumSet.of(Modifier.FINAL) : EnumSet.noneOf(Modifier.class)); - nueStatements.add(index, make.Variable(mods, name, make.Type(tm), expressionCopy/*(ExpressionTree) resolved.getLeaf()*//*(ExpressionTree) resolved.getLeaf()*/)); + nueStatements.add(index, make.Variable(mods, name, make.Type(tm), expression)); if (expressionStatement) nueStatements.remove(resolved.getParentPath().getLeaf()); @@ -1487,8 +1469,12 @@ break; } - if (!expressionStatement) - parameter.rewrite(resolved.getLeaf(), make.Identifier(name)); + if (!expressionStatement) { + Tree origParent = resolved.getParentPath().getLeaf(); + Tree newParent = parameter.getTreeUtilities().translate(origParent, Collections.singletonMap(resolved.getLeaf(), make.Identifier(name))); + parameter.rewrite(origParent, newParent); + + } } }).commit(); return null; @@ -1563,8 +1549,7 @@ return ; //TODO... } - //hack: creating a copy of the expression: - ExpressionTree expressionCopy = expressionCopy(resolved, parameter); + ExpressionTree expression = (ExpressionTree) resolved.getLeaf(); Set mods = declareFinal ? EnumSet.of(Modifier.FINAL) : EnumSet.noneOf(Modifier.class); @@ -1593,10 +1578,12 @@ ModifiersTree modsTree = make.Modifiers(mods); - VariableTree field = make.Variable(modsTree, name, make.Type(tm), initializeIn == IntroduceFieldPanel.INIT_FIELD ? expressionCopy : null); + VariableTree field = make.Variable(modsTree, name, make.Type(tm), initializeIn == IntroduceFieldPanel.INIT_FIELD ? expression : null); ClassTree nueClass = GeneratorUtils.insertClassMember(parameter, pathToClass, field); - parameter.rewrite(resolved.getLeaf(), make.Identifier(name)); + Tree parentTree = resolved.getParentPath().getLeaf(); + Tree nueParent = parameter.getTreeUtilities().translate(parentTree, Collections.singletonMap(resolved.getLeaf(), make.Identifier(name))); + parameter.rewrite(parentTree, nueParent); TreePath method = findMethod(resolved); @@ -1626,12 +1613,12 @@ List nueStatements = new LinkedList(statements.getStatements()); - if (expressionCopy.getKind() == Kind.NEW_ARRAY) { - List initializers = ((NewArrayTree) expressionCopy).getInitializers(); - expressionCopy = make.NewArray(make.Type(((ArrayType)tm).getComponentType()), Collections.emptyList(), initializers); + if (expression.getKind() == Kind.NEW_ARRAY) { + List initializers = ((NewArrayTree) expression).getInitializers(); + expression = make.NewArray(make.Type(((ArrayType)tm).getComponentType()), Collections.emptyList(), initializers); } - nueStatements.add(index, make.ExpressionStatement(make.Assignment(make.Identifier(name), expressionCopy))); + nueStatements.add(index, make.ExpressionStatement(make.Assignment(make.Identifier(name), expression))); BlockTree nueBlock = make.Block(nueStatements, false); @@ -1647,7 +1634,7 @@ Element clazz = parameter.getTrees().getElement(pathToClass); ModifiersTree constrMods = clazz.getKind() != ElementKind.ENUM?make.Modifiers(EnumSet.of(Modifier.PUBLIC)):make.Modifiers(Collections.EMPTY_SET); - nueStatements.add(make.ExpressionStatement(make.Assignment(reference, expressionCopy))); + nueStatements.add(make.ExpressionStatement(make.Assignment(reference, expression))); BlockTree nueBlock = make.Block(nueStatements, false); MethodTree nueConstr = make.Method(constrMods, "", null, Collections.emptyList(), Collections.emptyList(), Collections.emptyList(), nueBlock, null); //NOI18N @@ -1677,7 +1664,7 @@ if (!parameter.getTreeUtilities().isSynthetic(TreePath.getPath(constructor, canBeSuper))) { nueStatements.add(canBeSuper); } - nueStatements.add(make.ExpressionStatement(make.Assignment(reference, expressionCopy))); + nueStatements.add(make.ExpressionStatement(make.Assignment(reference, expression))); nueStatements.addAll(origStatements.subList(1, origStatements.size())); BlockTree nueBlock = make.Block(nueStatements, false); @@ -2079,8 +2066,6 @@ } returnType = Utilities.convertIfAnonymous(Utilities.resolveCapturedType(copy, returnType)); - ExpressionTree expressionCopy = expressionCopy(expression,copy); - final TreeMaker make = copy.getTreeMaker(); Tree returnTypeTree = make.Type(returnType); @@ -2115,7 +2100,7 @@ List methodStatements = new LinkedList(); - methodStatements.add(make.Return(expressionCopy)); + methodStatements.add(make.Return((ExpressionTree) expression.getLeaf())); MethodTree method = make.Method(mods, name, returnTypeTree, Collections.emptyList(), formalArguments, thrown, make.Block(methodStatements, false), null); TreePath pathToClass = findClass(expression); @@ -2131,7 +2116,10 @@ } copy.rewrite(pathToClass.getLeaf(), nueClass); - copy.rewrite(expression.getLeaf(), invocation); + + Tree parentTree = expression.getParentPath().getLeaf(); + Tree nueParent = copy.getTreeUtilities().translate(parentTree, Collections.singletonMap(expression.getLeaf(), invocation)); + copy.rewrite(parentTree, nueParent); if (replaceOther) { //handle duplicates diff --git a/java.hints/src/org/netbeans/modules/java/hints/jackpot/impl/Utilities.java b/java.hints/src/org/netbeans/modules/java/hints/jackpot/impl/Utilities.java --- a/java.hints/src/org/netbeans/modules/java/hints/jackpot/impl/Utilities.java +++ b/java.hints/src/org/netbeans/modules/java/hints/jackpot/impl/Utilities.java @@ -432,7 +432,7 @@ ImportAnalysis2 ia = new ImportAnalysis2(c); ia.setImports(Collections.emptyList()); - fixTree.attach(c, ia, cut, null); + fixTree.attach(c, ia, null); return fixTree.translate(patternTree); } @@ -805,7 +805,7 @@ //but sometimes no CompilationUnitTree (e.g. during BatchApply): CompilationUnitTree cut = TreeFactory.instance(c).CompilationUnit(null, Collections.emptyList(), Collections.emptyList(), null); - itt.attach(c, new NoImports(c), cut, null); + itt.attach(c, new NoImports(c), null); return itt.translate(original.getLeaf()); } diff --git a/java.source/apichanges.xml b/java.source/apichanges.xml --- a/java.source/apichanges.xml +++ b/java.source/apichanges.xml @@ -108,6 +108,20 @@ + + + Added TreeUtilities.translate method. + + + + + + Added TreeUtilities.translate method, which allows + to construct new tree based on a given set of changes. + + + + Added TypeUtilities.getTypeName method. diff --git a/java.source/nbproject/project.properties b/java.source/nbproject/project.properties --- a/java.source/nbproject/project.properties +++ b/java.source/nbproject/project.properties @@ -46,7 +46,7 @@ javadoc.title=Java Source javadoc.arch=${basedir}/arch.xml javadoc.apichanges=${basedir}/apichanges.xml -spec.version.base=0.62.0 +spec.version.base=0.63.0 test.qa-functional.cp.extra=${refactoring.java.dir}/modules/ext/javac-api-nb-7.0-b07.jar test.unit.run.cp.extra=${o.n.core.dir}/core/core.jar:\ ${o.n.core.dir}/lib/boot.jar:\ diff --git a/java.source/src/org/netbeans/api/java/source/TreeUtilities.java b/java.source/src/org/netbeans/api/java/source/TreeUtilities.java --- a/java.source/src/org/netbeans/api/java/source/TreeUtilities.java +++ b/java.source/src/org/netbeans/api/java/source/TreeUtilities.java @@ -60,7 +60,7 @@ import com.sun.tools.javac.tree.JCTree; import com.sun.tools.javac.tree.JCTree.JCClassDecl; import com.sun.tools.javac.tree.JCTree.JCMethodDecl; -import java.io.IOException; +import com.sun.tools.javac.util.Context; import java.util.*; import java.util.logging.Level; import java.util.logging.Logger; @@ -72,12 +72,11 @@ import org.netbeans.api.annotations.common.NonNull; import org.netbeans.api.java.lexer.JavaTokenId; import org.netbeans.api.java.source.JavaSource.Phase; -import org.netbeans.api.lexer.Token; import org.netbeans.api.lexer.TokenSequence; import org.netbeans.modules.java.source.builder.CommentHandlerService; import org.netbeans.modules.java.source.builder.CommentSetImpl; -import org.netbeans.modules.java.source.parsing.SourceFileObject; -import org.openide.util.Exceptions; +import org.netbeans.modules.java.source.pretty.ImportAnalysis2; +import org.netbeans.modules.java.source.transform.ImmutableTreeTranslator; /** * @@ -837,6 +836,94 @@ ESCAPE_ENCODE = Collections.unmodifiableMap(encode); } + /**Returns new tree based on {@code original}, such that each visited subtree + * that occurs as a key in {@code original2Translated} is replaced by the corresponding + * value from {@code original2Translated}. The value is then translated using the same + * algorithm. Each key from {@code original2Translated} is used at most once. + * Unless the provided {@code original} tree is a key in {@code original2Translated}, + * the resulting tree has the same type as {@code original}. + * + * Principally, the method inner workings are: + *
+     * translate(original, original2Translated) {
+     *      if (original2Translated.containsKey(original))
+     *          original = original2Translated.remove(original);
+     *      newTree = copyOf(original);
+     *      for (Tree child : allChildrenOf(original)) {
+     *          newTree.replace(child, translate(child, original2Translated));
+     *      }
+     *      return newTree;
+     * }
+     * 
+ * + * @param original the tree that should be translated + * @param original2Translated map containing trees that should be translated + * @return translated tree. + * @since 0.63 + */ + public @NonNull Tree translate(final @NonNull Tree original, final @NonNull Map original2Translated) { + return translate(original, original2Translated, new NoImports(info), null); + } + + @NonNull Tree translate(final @NonNull Tree original, final @NonNull Map original2Translated, ImportAnalysis2 ia, Map tree2Tag) { + ImmutableTreeTranslator itt = new ImmutableTreeTranslator() { + private @NonNull Map map = new HashMap(original2Translated); + @Override + public Tree translate(Tree tree) { + Tree translated = map.remove(tree); + + if (translated != null) { + return super.translate(translated); + } else { + return super.translate(tree); + } + } + }; + + Context c = info.impl.getJavacTask().getContext(); + + itt.attach(c, ia, tree2Tag); + + return itt.translate(original); + } + + private static final class NoImports extends ImportAnalysis2 { + + public NoImports(CompilationInfo info) { + super(info); + } + + @Override + public void classEntered(ClassTree clazz) {} + + @Override + public void classLeft() {} + + @Override + public ExpressionTree resolveImport(MemberSelectTree orig, Element element) { + return orig; + } + + @Override + public void setCompilationUnit(CompilationUnitTree cut) {} + + private List imports; + + @Override + public void setImports(List importsToAdd) { + this.imports = importsToAdd; + } + + @Override + public List getImports() { + return this.imports; + } + + @Override + public void setPackage(ExpressionTree packageNameTree) {} + + } + private void copyInnerClassIndexes(Tree from, Tree to) { final int[] fromIdx = {-2}; TreeScanner scanner = new TreeScanner() { diff --git a/java.source/src/org/netbeans/api/java/source/WorkingCopy.java b/java.source/src/org/netbeans/api/java/source/WorkingCopy.java --- a/java.source/src/org/netbeans/api/java/source/WorkingCopy.java +++ b/java.source/src/org/netbeans/api/java/source/WorkingCopy.java @@ -412,7 +412,7 @@ } List diffs = new ArrayList(); - ImportAnalysis2 ia = new ImportAnalysis2(getContext()); + ImportAnalysis2 ia = new ImportAnalysis2(this); boolean importsFilled = false; @@ -440,9 +440,7 @@ ia.classEntered(ct); } - translator.attach(getContext(), ia, getCompilationUnit(), tree2Tag); - - Tree brandNew = translator.translate(path.getLeaf(), parent2Rewrites.get(path)); + Tree brandNew = getTreeUtilities().translate(path.getLeaf(), parent2Rewrites.get(path), ia, tree2Tag); //tagging debug //System.err.println("brandNew=" + brandNew); @@ -490,11 +488,7 @@ List result = new LinkedList(); for (CompilationUnitTree t : externalChanges.values()) { - Translator translator = new Translator(); - - translator.attach(getContext(), new ImportAnalysis2(getContext()), t, tree2Tag); - - CompilationUnitTree nue = (CompilationUnitTree) translator.translate(t, changes); + CompilationUnitTree nue = (CompilationUnitTree) getTreeUtilities().translate(t, changes, new ImportAnalysis2(this), tree2Tag); VeryPretty printer = new VeryPretty(this, VeryPretty.getCodeStyle(this)); printer.print((JCTree.JCCompilationUnit) nue); diff --git a/java.source/src/org/netbeans/modules/java/source/pretty/ImportAnalysis2.java b/java.source/src/org/netbeans/modules/java/source/pretty/ImportAnalysis2.java --- a/java.source/src/org/netbeans/modules/java/source/pretty/ImportAnalysis2.java +++ b/java.source/src/org/netbeans/modules/java/source/pretty/ImportAnalysis2.java @@ -70,10 +70,11 @@ import javax.lang.model.element.ElementKind; import javax.lang.model.element.Modifier; import javax.lang.model.element.PackageElement; -import javax.lang.model.element.PackageElement; import javax.lang.model.element.TypeElement; import javax.lang.model.util.ElementFilter; import javax.lang.model.util.Elements; +import org.netbeans.api.java.source.CompilationInfo; +import org.netbeans.modules.java.source.JavaSourceAccessor; import org.netbeans.modules.java.source.builder.ASTService; import org.netbeans.modules.java.source.builder.TreeFactory; @@ -97,6 +98,10 @@ private Set implicitlyImportedClassNames; private PackageElement javaLang; + public ImportAnalysis2(CompilationInfo info) { + this(JavaSourceAccessor.getINSTANCE().getJavacTask(info).getContext()); + } + public ImportAnalysis2(Context env) { elements = JavacElements.instance(env); make = TreeFactory.instance(env); diff --git a/java.source/src/org/netbeans/modules/java/source/transform/ImmutableTreeTranslator.java b/java.source/src/org/netbeans/modules/java/source/transform/ImmutableTreeTranslator.java --- a/java.source/src/org/netbeans/modules/java/source/transform/ImmutableTreeTranslator.java +++ b/java.source/src/org/netbeans/modules/java/source/transform/ImmutableTreeTranslator.java @@ -90,28 +90,21 @@ protected TreeFactory make; protected CommentHandler comments; protected ASTService model; - private CompilationUnitTree topLevel; private ImportAnalysis2 importAnalysis; private Map tree2Tag; - public void attach(Context context, ImportAnalysis2 importAnalysis, CompilationUnitTree topLevel, Map tree2Tag) { + public void attach(Context context, ImportAnalysis2 importAnalysis, Map tree2Tag) { make = TreeFactory.instance(context); comments = CommentHandlerService.instance(context); model = ASTService.instance(context); this.importAnalysis = importAnalysis; - this.topLevel = topLevel; this.tree2Tag = tree2Tag; } - public void attach(Context context) { - attach(context, new ImportAnalysis2(context), null, null); - } - public void release() { make = null; comments = null; model = null; - topLevel = null; importAnalysis = null; } @@ -319,9 +312,7 @@ ****************************************************************************/ public Tree visitCompilationUnit(CompilationUnitTree tree, Object p) { - topLevel = tree; - CompilationUnitTree result = rewriteChildren(topLevel); - topLevel = null; + CompilationUnitTree result = rewriteChildren(tree); return result; } public Tree visitImport(ImportTree tree, Object p) { @@ -614,10 +605,11 @@ * from its parent's. Instead, just mark the topLevel so * Commit knows to save it. */ - assert topLevel != null; -// if (topLevel == null) -// topLevel = model.getTopLevel(tree); - model.setPos(topLevel, NOPOS); + //TODO: this does not seem to be checked currently: +// assert topLevel != null; +//// if (topLevel == null) +//// topLevel = model.getTopLevel(tree); +// model.setPos(topLevel, NOPOS); } else copyPosTo(tree,n); tree = n;