Commits

Jan Lahoda  committed e910309

Add parentheses as needed - ported from NB.

  • Participants
  • Parent commits 651c5cb

Comments (0)

Files changed (2)

File api/src/org/netbeans/modules/jackpot30/spi/JavaFix.java

 /*
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
  *
- * Copyright 2008-2010 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved.
  *
  * The contents of this file are subject to the terms of either the GNU
  * General Public License Version 2 only ("GPL") or the Common
 
 import com.sun.javadoc.Doc;
 import com.sun.javadoc.Tag;
+import com.sun.source.tree.AssignmentTree;
 import com.sun.source.tree.BinaryTree;
 import com.sun.source.tree.BlockTree;
 import com.sun.source.tree.ClassTree;
+import com.sun.source.tree.CompoundAssignmentTree;
 import com.sun.source.tree.ExpressionStatementTree;
 import com.sun.source.tree.ExpressionTree;
 import com.sun.source.tree.LiteralTree;
 import com.sun.source.util.TreePathScanner;
 import java.util.Arrays;
 import java.util.Collections;
+import java.util.EnumMap;
 import java.util.EnumSet;
 import java.util.HashMap;
 import java.util.LinkedList;
         return handle.getFileObject();
     }
 
+    public static Fix rewriteFix(HintContext ctx, String displayName, TreePath what, final String to, String... imports) {
+        return rewriteFix(ctx, displayName, what, to, Collections.<String, TypeMirror>emptyMap(), imports);
+    }
+    
+    public static Fix rewriteFix(HintContext ctx, String displayName, TreePath what, final String to, Map<String, TypeMirror> constraints, String... imports) {
+        return rewriteFix(ctx.getInfo(), displayName, what, to, ctx.getVariables(), ctx.getMultiVariables(), ctx.getVariableNames(), constraints, imports);
+    }
+
     public static Fix rewriteFix(CompilationInfo info, String displayName, TreePath what, final String to, Map<String, TreePath> parameters, Map<String, Collection<? extends TreePath>> parametersMulti, final Map<String, String> parameterNames, Map<String, TypeMirror> constraints, String... imports) {
         return rewriteFix(info, displayName, what, to, parameters, parametersMulti, parameterNames, constraints, Collections.<String, String>emptyMap(), imports);
     }
                 }
             }
 
-            new ReplaceParameters(wc, canShowUI, parameters, parametersMulti, parameterNames).scan(new TreePath(new TreePath(tp.getCompilationUnit()), parsed), null);
+            new ReplaceParameters(wc, canShowUI, parameters, parametersMulti, parameterNames).scan(new TreePath(tp.getParentPath(), parsed), null);
         }
     }
 
                     if (NUMBER_LITERAL_KINDS.contains(tp.getLeaf().getKind())) {
                         return (Number) ((LiteralTree) tp.getLeaf()).getValue();
                     }
+                    Tree target = tp.getLeaf();
+                    //TODO: might also remove parenthesis, but needs to ensure the diff will still be minimal
+//                    while (target.getKind() == Kind.PARENTHESIZED
+//                           && !requiresParenthesis(((ParenthesizedTree) target).getExpression(), getCurrentPath().getParentPath().getLeaf())) {
+//                        target = ((ParenthesizedTree) target).getExpression();
+//                    }
+                    if (requiresParenthesis(target, node, getCurrentPath().getParentPath().getLeaf())) {
+                        target = wc.getTreeMaker().Parenthesized((ExpressionTree) target);
+                    }
+                    wc.rewrite(node, target);
                     return null;
                 }
             }
         }
     }
 
+    private static final Map<Kind, Integer> OPERATOR_PRIORITIES;
+
+    static {
+        OPERATOR_PRIORITIES = new EnumMap<Kind, Integer>(Kind.class);
+
+        OPERATOR_PRIORITIES.put(Kind.IDENTIFIER, 0);
+
+        for (Kind k : Kind.values()) {
+            if (k.asInterface() == LiteralTree.class) {
+                OPERATOR_PRIORITIES.put(k, 0);
+            }
+        }
+
+        OPERATOR_PRIORITIES.put(Kind.ARRAY_ACCESS, 1);
+        OPERATOR_PRIORITIES.put(Kind.METHOD_INVOCATION, 1);
+        OPERATOR_PRIORITIES.put(Kind.MEMBER_SELECT, 1);
+        OPERATOR_PRIORITIES.put(Kind.POSTFIX_DECREMENT, 1);
+        OPERATOR_PRIORITIES.put(Kind.POSTFIX_INCREMENT, 1);
+
+        OPERATOR_PRIORITIES.put(Kind.BITWISE_COMPLEMENT, 2);
+        OPERATOR_PRIORITIES.put(Kind.LOGICAL_COMPLEMENT, 2);
+        OPERATOR_PRIORITIES.put(Kind.PREFIX_DECREMENT, 2);
+        OPERATOR_PRIORITIES.put(Kind.PREFIX_INCREMENT, 2);
+        OPERATOR_PRIORITIES.put(Kind.UNARY_MINUS, 2);
+        OPERATOR_PRIORITIES.put(Kind.UNARY_PLUS, 2);
+
+        OPERATOR_PRIORITIES.put(Kind.NEW_ARRAY, 3);
+        OPERATOR_PRIORITIES.put(Kind.NEW_CLASS, 3);
+        OPERATOR_PRIORITIES.put(Kind.TYPE_CAST, 3);
+
+        OPERATOR_PRIORITIES.put(Kind.DIVIDE, 4);
+        OPERATOR_PRIORITIES.put(Kind.MULTIPLY, 4);
+        OPERATOR_PRIORITIES.put(Kind.REMAINDER, 4);
+
+        OPERATOR_PRIORITIES.put(Kind.MINUS, 5);
+        OPERATOR_PRIORITIES.put(Kind.PLUS, 5);
+
+        OPERATOR_PRIORITIES.put(Kind.LEFT_SHIFT, 6);
+        OPERATOR_PRIORITIES.put(Kind.RIGHT_SHIFT, 6);
+        OPERATOR_PRIORITIES.put(Kind.UNSIGNED_RIGHT_SHIFT, 6);
+
+        OPERATOR_PRIORITIES.put(Kind.INSTANCE_OF, 7);
+        OPERATOR_PRIORITIES.put(Kind.GREATER_THAN, 7);
+        OPERATOR_PRIORITIES.put(Kind.GREATER_THAN_EQUAL, 7);
+        OPERATOR_PRIORITIES.put(Kind.LESS_THAN, 7);
+        OPERATOR_PRIORITIES.put(Kind.LESS_THAN_EQUAL, 7);
+
+        OPERATOR_PRIORITIES.put(Kind.EQUAL_TO, 8);
+        OPERATOR_PRIORITIES.put(Kind.NOT_EQUAL_TO, 8);
+
+        OPERATOR_PRIORITIES.put(Kind.AND, 9);
+        OPERATOR_PRIORITIES.put(Kind.OR, 11);
+        OPERATOR_PRIORITIES.put(Kind.XOR, 10);
+
+        OPERATOR_PRIORITIES.put(Kind.CONDITIONAL_AND, 12);
+        OPERATOR_PRIORITIES.put(Kind.CONDITIONAL_OR, 13);
+
+        OPERATOR_PRIORITIES.put(Kind.CONDITIONAL_EXPRESSION, 14);
+
+        OPERATOR_PRIORITIES.put(Kind.AND_ASSIGNMENT, 15);
+        OPERATOR_PRIORITIES.put(Kind.ASSIGNMENT, 15);
+        OPERATOR_PRIORITIES.put(Kind.DIVIDE_ASSIGNMENT, 15);
+        OPERATOR_PRIORITIES.put(Kind.LEFT_SHIFT_ASSIGNMENT, 15);
+        OPERATOR_PRIORITIES.put(Kind.MINUS_ASSIGNMENT, 15);
+        OPERATOR_PRIORITIES.put(Kind.MULTIPLY_ASSIGNMENT, 15);
+        OPERATOR_PRIORITIES.put(Kind.OR_ASSIGNMENT, 15);
+        OPERATOR_PRIORITIES.put(Kind.PLUS_ASSIGNMENT, 15);
+        OPERATOR_PRIORITIES.put(Kind.REMAINDER_ASSIGNMENT, 15);
+        OPERATOR_PRIORITIES.put(Kind.RIGHT_SHIFT_ASSIGNMENT, 15);
+        OPERATOR_PRIORITIES.put(Kind.UNSIGNED_RIGHT_SHIFT_ASSIGNMENT, 15);
+        OPERATOR_PRIORITIES.put(Kind.XOR_ASSIGNMENT, 15);
+    }
+
+    private static boolean requiresParenthesis(Tree inner, Tree original, Tree outter) {
+        if (!ExpressionTree.class.isAssignableFrom(inner.getKind().asInterface())) return false;
+        if (!ExpressionTree.class.isAssignableFrom(outter.getKind().asInterface())) return false;
+
+        if (outter.getKind() == Kind.PARENTHESIZED || inner.getKind() == Kind.PARENTHESIZED) return false;
+
+        Integer innerPriority = OPERATOR_PRIORITIES.get(inner.getKind());
+        Integer outterPriority = OPERATOR_PRIORITIES.get(outter.getKind());
+
+        if (innerPriority == null || outterPriority == null) {
+            Logger.getLogger(JavaFix.class.getName()).log(Level.WARNING, "Unknown tree kind(s): {0}/{1}", new Object[] {inner.getKind(), outter.getKind()});
+            return true;
+        }
+
+        if (innerPriority > outterPriority) {
+            return true;
+        }
+
+        if (innerPriority < outterPriority) {
+            return false;
+        }
+
+        //associativity
+        if (BinaryTree.class.isAssignableFrom(outter.getKind().asInterface())) {
+            BinaryTree ot = (BinaryTree) outter;
+
+            //TODO: for + it might be possible to skip the parenthesis:
+            return ot.getRightOperand() == original;
+        }
+
+        if (CompoundAssignmentTree.class.isAssignableFrom(outter.getKind().asInterface())) {
+            CompoundAssignmentTree ot = (CompoundAssignmentTree) outter;
+
+            return ot.getVariable() == original;
+        }
+
+        if (AssignmentTree.class.isAssignableFrom(outter.getKind().asInterface())) {
+            AssignmentTree ot = (AssignmentTree) outter;
+
+            return ot.getVariable() == original;
+        }
+
+        return false;
+    }
+
     static {
         JavaFixImpl.Accessor.INSTANCE = new JavaFixImpl.Accessor() {
             @Override

File api/test/unit/src/org/netbeans/modules/jackpot30/spi/JavaFixTest.java

 
 import com.sun.source.tree.ClassTree;
 import com.sun.source.tree.ExpressionTree;
+import com.sun.source.tree.Tree.Kind;
 import com.sun.source.tree.VariableTree;
 import com.sun.source.util.TreePath;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicBoolean;
 import javax.lang.model.element.ExecutableElement;
 import javax.lang.model.element.TypeElement;
 import javax.lang.model.type.TypeMirror;
 import javax.lang.model.util.ElementFilter;
+import org.netbeans.modules.jackpot30.impl.RulesManager;
 import org.netbeans.modules.jackpot30.impl.TestBase;
+import org.netbeans.modules.jackpot30.impl.hints.HintsInvoker;
+import org.netbeans.modules.jackpot30.spi.HintDescription.PatternDescription;
+import org.netbeans.modules.jackpot30.spi.support.ErrorDescriptionFactory;
+import org.netbeans.spi.editor.hints.ErrorDescription;
 import org.netbeans.spi.editor.hints.Fix;
 import org.openide.LifecycleManager;
 import org.openide.modules.SpecificationVersion;
 
         return f.format(ARITHMETIC);
     }
-}
+
+    public void testRewriteWithParenthesis1() throws Exception {
+        performRewriteTest("package test;\n" +
+                           "public class Test {\n" +
+                           "    int i = new String(\"a\" + \"b\").length();\n" +
+                           "}\n",
+                           "new String($1)=>$1",
+                           "package test;\n" +
+                           "public class Test {\n" +
+		           "    int i = (\"a\" + \"b\").length();\n" +
+		           "}\n");
+    }
+
+    public void testRewriteWithParenthesis2() throws Exception {
+        performRewriteTest("package test;\n" +
+                           "public class Test {\n" +
+                           "    int i = Integer.valueOf(1 + 2) * 3;\n" +
+                           "}\n",
+                           "Integer.valueOf($1)=>$1",
+                           "package test;\n" +
+                           "public class Test {\n" +
+		           "    int i = (1 + 2) * 3;\n" +
+		           "}\n");
+    }
+
+    public void testRewriteWithoutParenthesis1() throws Exception {
+        performRewriteTest("package test;\n" +
+                           "public class Test {\n" +
+                           "    int i = new String(\"a\" + \"b\").length();\n" +
+                           "}\n",
+                           "new String($1)=>java.lang.String.format(\"%s%s\", $1, \"\")",
+                           "package test;\n" +
+                           "public class Test {\n" +
+		           "    int i = String.format(\"%s%s\", \"a\" + \"b\", \"\").length();\n" +
+		           "}\n");
+    }
+
+    public void testRewriteWithoutParenthesis2() throws Exception {
+        performRewriteTest("package test;\n" +
+                           "public class Test {\n" +
+                           "    String s = (\"a\" + \"b\").intern();\n" +
+                           "}\n",
+                           "($1).intern()=>$1",
+                           "package test;\n" +
+                           "public class Test {\n" +
+		           "    String s = \"a\" + \"b\";\n" +
+		           "}\n");
+    }
+
+    public void testRewriteWithoutParenthesis3() throws Exception {
+        performRewriteTest("package test;\n" +
+                           "public class Test {\n" +
+                           "    int i = Integer.valueOf(1 + 2) + 3;\n" +
+                           "}\n",
+                           "Integer.valueOf($1)=>$1",
+                           "package test;\n" +
+                           "public class Test {\n" +
+		           "    int i = 1 + 2 + 3;\n" +
+		           "}\n");
+    }
+
+    public void testRewriteWithoutParenthesis4() throws Exception {
+        performRewriteTest("package test;\n" +
+                           "public class Test {\n" +
+                           "    int i = Integer.valueOf(1 * 2) + 3;\n" +
+                           "}\n",
+                           "Integer.valueOf($1)=>$1",
+                           "package test;\n" +
+                           "public class Test {\n" +
+		           "    int i = 1 * 2 + 3;\n" +
+		           "}\n");
+    }
+
+    public void performRewriteTest(String code, String rule, String golden) throws Exception {
+	prepareTest("test/Test.java", code);
+
+        final String[] split = rule.split("=>");
+        assertEquals(2, split.length);
+        HintDescription hd = HintDescriptionFactory.create()
+                                                   .setTriggerPattern(PatternDescription.create(split[0], Collections.<String, String>emptyMap()))
+                                                   .setWorker(new HintDescription.Worker() {
+            @Override public Collection<? extends ErrorDescription> createErrors(HintContext ctx) {
+                return Collections.singletonList(ErrorDescriptionFactory.forName(ctx, ctx.getPath(), "", JavaFix.rewriteFix(ctx, "", ctx.getPath(), split[1])));
+            }
+        }).produce();
+
+        Map<PatternDescription, List<HintDescription>> patternHints = new HashMap<PatternDescription, List<HintDescription>>();
+        HashMap<Kind, List<HintDescription>> kindHints = new HashMap<Kind, List<HintDescription>>();
+
+        RulesManager.sortOut(Collections.singleton(hd), kindHints, patternHints);
+        List<ErrorDescription> computeHints = new HintsInvoker(info, new AtomicBoolean()).computeHints(info, kindHints, patternHints);
+
+        assertEquals(computeHints.toString(), 1, computeHints.size());
+
+        Fix fix = computeHints.get(0).getFixes().getFixes().get(0);
+
+	fix.implement();
+
+        assertEquals(golden, doc.getText(0, doc.getLength()));
+    }
+}