Hint to convert an old-style Iterator to a for-loop.
1.1 --- a/javahints/nbproject/project.properties Mon Apr 16 13:14:53 2012 -0400
1.2 +++ b/javahints/nbproject/project.properties Mon Apr 16 15:13:19 2012 -0400
1.3 @@ -50,7 +50,7 @@
1.4 auxiliary.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.text-limit-width=80
1.5 javac.compilerargs=-Xlint:unchecked
1.6 javac.source=1.6
1.7 -spec.version.base=2.53.0
1.8 +spec.version.base=2.54.0
1.9
1.10 nbm.needs.restart=true
1.11 requires.nb.javac=true
2.1 --- a/javahints/nbproject/project.xml Mon Apr 16 13:14:53 2012 -0400
2.2 +++ b/javahints/nbproject/project.xml Mon Apr 16 15:13:19 2012 -0400
2.3 @@ -423,6 +423,10 @@
2.4 <test/>
2.5 </test-dependency>
2.6 <test-dependency>
2.7 + <code-name-base>org.netbeans.modules.java.hints.test</code-name-base>
2.8 + <compile-dependency/>
2.9 + </test-dependency>
2.10 + <test-dependency>
2.11 <code-name-base>org.netbeans.modules.java.lexer</code-name-base>
2.12 </test-dependency>
2.13 <test-dependency>
3.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
3.2 +++ b/javahints/src/org/netbeans/modules/javahints/jdk5/IteratorToFor.java Mon Apr 16 15:13:19 2012 -0400
3.3 @@ -0,0 +1,198 @@
3.4 +/*
3.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3.6 + *
3.7 + * Copyright 2012 Oracle and/or its affiliates. All rights reserved.
3.8 + *
3.9 + * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
3.10 + * Other names may be trademarks of their respective owners.
3.11 + *
3.12 + * The contents of this file are subject to the terms of either the GNU
3.13 + * General Public License Version 2 only ("GPL") or the Common
3.14 + * Development and Distribution License("CDDL") (collectively, the
3.15 + * "License"). You may not use this file except in compliance with the
3.16 + * License. You can obtain a copy of the License at
3.17 + * http://www.netbeans.org/cddl-gplv2.html
3.18 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
3.19 + * specific language governing permissions and limitations under the
3.20 + * License. When distributing the software, include this License Header
3.21 + * Notice in each file and include the License file at
3.22 + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
3.23 + * particular file as subject to the "Classpath" exception as provided
3.24 + * by Oracle in the GPL Version 2 section of the License file that
3.25 + * accompanied this code. If applicable, add the following below the
3.26 + * License Header, with the fields enclosed by brackets [] replaced by
3.27 + * your own identifying information:
3.28 + * "Portions Copyrighted [year] [name of copyright owner]"
3.29 + *
3.30 + * If you wish your version of this file to be governed by only the CDDL
3.31 + * or only the GPL Version 2, indicate your decision by adding
3.32 + * "[Contributor] elects to include this software in this distribution
3.33 + * under the [CDDL or GPL Version 2] license." If you do not indicate a
3.34 + * single choice of license, a recipient has the option to distribute
3.35 + * your version of this file under either the CDDL, the GPL Version 2 or
3.36 + * to extend the choice of license to its licensees as provided above.
3.37 + * However, if you add GPL Version 2 code and therefore, elected the GPL
3.38 + * Version 2 license, then the option applies only if the new code is
3.39 + * made subject to such option by the copyright holder.
3.40 + *
3.41 + * Contributor(s):
3.42 + *
3.43 + * Portions Copyrighted 2012 Sun Microsystems, Inc.
3.44 + */
3.45 +
3.46 +package org.netbeans.modules.javahints.jdk5;
3.47 +
3.48 +import com.sun.source.tree.BlockTree;
3.49 +import com.sun.source.tree.ExpressionTree;
3.50 +import com.sun.source.tree.StatementTree;
3.51 +import com.sun.source.tree.Tree;
3.52 +import com.sun.source.tree.VariableTree;
3.53 +import com.sun.source.util.TreePath;
3.54 +import com.sun.source.util.TreePathScanner;
3.55 +import java.util.ArrayList;
3.56 +import java.util.Collection;
3.57 +import java.util.Collections;
3.58 +import java.util.List;
3.59 +import java.util.Map;
3.60 +import javax.lang.model.element.Element;
3.61 +import javax.lang.model.element.Modifier;
3.62 +import org.netbeans.api.java.source.TreeMaker;
3.63 +import org.netbeans.api.java.source.WorkingCopy;
3.64 +import org.netbeans.spi.editor.hints.ErrorDescription;
3.65 +import org.netbeans.spi.java.hints.ErrorDescriptionFactory;
3.66 +import org.netbeans.spi.java.hints.Hint;
3.67 +import org.netbeans.spi.java.hints.HintContext;
3.68 +import org.netbeans.spi.java.hints.JavaFix;
3.69 +import org.netbeans.spi.java.hints.JavaFix.TransformationContext;
3.70 +import org.netbeans.spi.java.hints.TriggerPattern;
3.71 +import org.openide.util.NbBundle.Messages;
3.72 +
3.73 +@Hint(displayName="#DN_IteratorToFor", description="#DESC_IteratorToFor", category="rules15")
3.74 +@Messages({
3.75 + "DN_IteratorToFor=Use JDK 5 for-loop",
3.76 + "DESC_IteratorToFor=Replaces simple uses of Iterator with a corresponding for-loop.",
3.77 + "ERR_IteratorToFor=Use of Iterator for simple loop",
3.78 + "FIX_IteratorToFor=Convert to for-loop"
3.79 +})
3.80 +public class IteratorToFor {
3.81 +
3.82 + @TriggerPattern("java.util.Iterator $it = $coll.iterator(); while ($it.hasNext()) {$type $elem = ($type) $it.next(); $rest$;}")
3.83 + public static ErrorDescription whileIdiom(HintContext ctx) {
3.84 + if (uses(ctx, ctx.getMultiVariables().get("$rest$"), ctx.getVariables().get("$it"))) {
3.85 + return null;
3.86 + }
3.87 + return ErrorDescriptionFactory.forName(ctx, ctx.getPath(), Bundle.ERR_IteratorToFor(), new WhileFix(ctx).toEditorFix());
3.88 + }
3.89 +
3.90 + // like JavaFixUtilities.rewriteFix(..., "for ($type $elem : $coll) {$rest$;}"))
3.91 + // but does not mess up interior comments
3.92 + private static final class WhileFix extends JavaFix {
3.93 +
3.94 + private final HintContext hctx;
3.95 +
3.96 + WhileFix(HintContext hctx) {
3.97 + super(hctx.getInfo(), hctx.getPath());
3.98 + // XXX #211273 comment #3: should not be keeping hctx here, ought to rewrite
3.99 + this.hctx = hctx;
3.100 + }
3.101 +
3.102 + @Override protected String getText() {
3.103 + return Bundle.FIX_IteratorToFor();
3.104 + }
3.105 +
3.106 + @Override protected void performRewrite(TransformationContext ctx) {
3.107 + WorkingCopy wc = ctx.getWorkingCopy();
3.108 + TreeMaker tm = wc.getTreeMaker();
3.109 + Map<String,TreePath> vars = hctx.getVariables();
3.110 + Map<String,Collection<? extends TreePath>> multivars = hctx.getMultiVariables();
3.111 + BlockTree block = (BlockTree) vars.get("$_").getLeaf();
3.112 + List<StatementTree> stmts = new ArrayList<StatementTree>(block.getStatements());
3.113 + boolean deleted = stmts.remove((StatementTree) vars.get("$it").getLeaf());
3.114 + assert deleted;
3.115 + int idx = stmts.indexOf((StatementTree) vars.get("$elem").getParentPath().getParentPath().getLeaf());
3.116 + assert idx != -1;
3.117 + VariableTree decl = tm.Variable(tm.Modifiers(Collections.<Modifier>emptySet()), hctx.getVariableNames().get("$elem"), vars.get("$type").getLeaf(), null);
3.118 + ExpressionTree expr = (ExpressionTree) vars.get("$coll").getLeaf();
3.119 + List<StatementTree> rest = new ArrayList<StatementTree>();
3.120 + for (TreePath p : multivars.get("$rest$")) {
3.121 + rest.add((StatementTree) p.getLeaf());
3.122 + }
3.123 + StatementTree body = tm.Block(rest, false);
3.124 + stmts.set(idx, tm.EnhancedForLoop(decl, expr, body));
3.125 + wc.rewrite(block, tm.Block(stmts, false));
3.126 + }
3.127 +
3.128 + }
3.129 +
3.130 + @TriggerPattern("for (java.util.Iterator $it = $coll.iterator(); $it.hasNext(); ) {$type $elem = ($type) $it.next(); $rest$;}")
3.131 + public static ErrorDescription forIdiom(HintContext ctx) {
3.132 + if (uses(ctx, ctx.getMultiVariables().get("$rest$"), ctx.getVariables().get("$it"))) {
3.133 + return null;
3.134 + }
3.135 + return ErrorDescriptionFactory.forName(ctx, ctx.getPath(), Bundle.ERR_IteratorToFor(), new ForFix(ctx).toEditorFix());
3.136 + }
3.137 +
3.138 + private static final class ForFix extends JavaFix {
3.139 +
3.140 + private final HintContext hctx;
3.141 +
3.142 + ForFix(HintContext hctx) {
3.143 + super(hctx.getInfo(), hctx.getPath());
3.144 + this.hctx = hctx;
3.145 + }
3.146 +
3.147 + @Override protected String getText() {
3.148 + return Bundle.FIX_IteratorToFor();
3.149 + }
3.150 +
3.151 + @Override protected void performRewrite(TransformationContext ctx) {
3.152 + WorkingCopy wc = ctx.getWorkingCopy();
3.153 + TreeMaker tm = wc.getTreeMaker();
3.154 + Map<String,TreePath> vars = hctx.getVariables();
3.155 + Map<String,Collection<? extends TreePath>> multivars = hctx.getMultiVariables();
3.156 + VariableTree decl = tm.Variable(tm.Modifiers(Collections.<Modifier>emptySet()), hctx.getVariableNames().get("$elem"), vars.get("$type").getLeaf(), null);
3.157 + ExpressionTree expr = (ExpressionTree) vars.get("$coll").getLeaf();
3.158 + List<StatementTree> rest = new ArrayList<StatementTree>();
3.159 + for (TreePath p : multivars.get("$rest$")) {
3.160 + rest.add((StatementTree) p.getLeaf());
3.161 + }
3.162 + StatementTree body = tm.Block(rest, false);
3.163 + wc.rewrite(ctx.getPath().getLeaf(), tm.EnhancedForLoop(decl, expr, body));
3.164 + }
3.165 +
3.166 + }
3.167 +
3.168 + // adapted from org.netbeans.modules.java.hints.declarative.conditionapi.Matcher.referencedIn
3.169 + private static boolean uses(final HintContext ctx, Collection<? extends TreePath> statements, TreePath var) {
3.170 + final Element e = ctx.getInfo().getTrees().getElement(var);
3.171 + for (TreePath tp : statements) {
3.172 + boolean occurs = Boolean.TRUE.equals(new TreePathScanner<Boolean, Void>() {
3.173 + @Override public Boolean scan(Tree tree, Void p) {
3.174 + if (tree == null) {
3.175 + return false;
3.176 + }
3.177 + TreePath currentPath = new TreePath(getCurrentPath(), tree);
3.178 + Element currentElement = ctx.getInfo().getTrees().getElement(currentPath);
3.179 + if (e.equals(currentElement)) {
3.180 + return true;
3.181 + }
3.182 + return super.scan(tree, p);
3.183 + }
3.184 + @Override public Boolean reduce(Boolean r1, Boolean r2) {
3.185 + if (r1 == null) {
3.186 + return r2;
3.187 + }
3.188 + if (r2 == null) {
3.189 + return r1;
3.190 + }
3.191 + return r1 || r2;
3.192 + }
3.193 + }.scan(tp, null));
3.194 + if (occurs) {
3.195 + return true;
3.196 + }
3.197 + }
3.198 + return false;
3.199 + }
3.200 +
3.201 +}
4.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
4.2 +++ b/javahints/test/unit/src/org/netbeans/modules/javahints/jdk5/IteratorToForTest.java Mon Apr 16 15:13:19 2012 -0400
4.3 @@ -0,0 +1,183 @@
4.4 +/*
4.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
4.6 + *
4.7 + * Copyright 2012 Oracle and/or its affiliates. All rights reserved.
4.8 + *
4.9 + * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
4.10 + * Other names may be trademarks of their respective owners.
4.11 + *
4.12 + * The contents of this file are subject to the terms of either the GNU
4.13 + * General Public License Version 2 only ("GPL") or the Common
4.14 + * Development and Distribution License("CDDL") (collectively, the
4.15 + * "License"). You may not use this file except in compliance with the
4.16 + * License. You can obtain a copy of the License at
4.17 + * http://www.netbeans.org/cddl-gplv2.html
4.18 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
4.19 + * specific language governing permissions and limitations under the
4.20 + * License. When distributing the software, include this License Header
4.21 + * Notice in each file and include the License file at
4.22 + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
4.23 + * particular file as subject to the "Classpath" exception as provided
4.24 + * by Oracle in the GPL Version 2 section of the License file that
4.25 + * accompanied this code. If applicable, add the following below the
4.26 + * License Header, with the fields enclosed by brackets [] replaced by
4.27 + * your own identifying information:
4.28 + * "Portions Copyrighted [year] [name of copyright owner]"
4.29 + *
4.30 + * If you wish your version of this file to be governed by only the CDDL
4.31 + * or only the GPL Version 2, indicate your decision by adding
4.32 + * "[Contributor] elects to include this software in this distribution
4.33 + * under the [CDDL or GPL Version 2] license." If you do not indicate a
4.34 + * single choice of license, a recipient has the option to distribute
4.35 + * your version of this file under either the CDDL, the GPL Version 2 or
4.36 + * to extend the choice of license to its licensees as provided above.
4.37 + * However, if you add GPL Version 2 code and therefore, elected the GPL
4.38 + * Version 2 license, then the option applies only if the new code is
4.39 + * made subject to such option by the copyright holder.
4.40 + *
4.41 + * Contributor(s):
4.42 + *
4.43 + * Portions Copyrighted 2012 Sun Microsystems, Inc.
4.44 + */
4.45 +
4.46 +package org.netbeans.modules.javahints.jdk5;
4.47 +
4.48 +import org.junit.Test;
4.49 +import org.netbeans.modules.java.hints.test.api.HintTest;
4.50 +
4.51 +public class IteratorToForTest {
4.52 +
4.53 + @Test public void whileWarning() throws Exception {
4.54 + HintTest.create().input("package test;\n"
4.55 + + "import java.util.*;"
4.56 + + "public class Test {\n"
4.57 + + " void m(List<String> strings) {\n"
4.58 + + " Iterator it = strings.iterator();\n"
4.59 + + " while (it.hasNext()) {\n"
4.60 + + " String s = (String) it.next();\n"
4.61 + + " System.out.println(s);\n"
4.62 + + " System.err.println(s);\n"
4.63 + + " }\n"
4.64 + + " }\n"
4.65 + + "}\n").run(IteratorToFor.class).assertWarnings("2:33-9:5:verifier:" + Bundle.ERR_IteratorToFor());
4.66 + }
4.67 +
4.68 + @Test public void whileUsedSpecially() throws Exception {
4.69 + HintTest.create().input("package test;\n"
4.70 + + "import java.util.*;"
4.71 + + "public class Test {\n"
4.72 + + " void m(List<String> strings) {\n"
4.73 + + " Iterator it = strings.iterator();\n"
4.74 + + " while (it.hasNext()) {\n"
4.75 + + " String s = (String) it.next();\n"
4.76 + + " if (s.isEmpty()) {\n"
4.77 + + " it.remove();\n"
4.78 + + " } else {\n"
4.79 + + " System.out.println(s);\n"
4.80 + + " }\n"
4.81 + + " }\n"
4.82 + + " }\n"
4.83 + + "}\n").run(IteratorToFor.class).assertWarnings();
4.84 + }
4.85 +
4.86 + @Test public void whileFix() throws Exception {
4.87 + HintTest.create().input("package test;\n"
4.88 + + "import java.util.*;"
4.89 + + "public class Test {\n"
4.90 + + " void m(List<String> strings) {\n"
4.91 + + " Collections.reverse(strings);\n"
4.92 + + " Iterator it = strings.iterator();\n"
4.93 + + " while (it.hasNext()) {\n"
4.94 + + " String s = (String) it.next();\n"
4.95 + + " System.out.println(s);\n"
4.96 + + " // OK\n"
4.97 + + " System.err.println(s);\n"
4.98 + + " }\n"
4.99 + + " System.out.println();\n"
4.100 + + " }\n"
4.101 + + "}\n").run(IteratorToFor.class).findWarning("2:33-12:5:verifier:" + Bundle.ERR_IteratorToFor()).
4.102 + applyFix().
4.103 + assertCompilable()
4.104 + .assertOutput("package test;\n"
4.105 + + "import java.util.*;"
4.106 + + "public class Test {\n"
4.107 + + " void m(List<String> strings) {\n"
4.108 + + " Collections.reverse(strings);\n"
4.109 + + " for (String s : strings) {\n"
4.110 + + " System.out.println(s);\n"
4.111 + + " // OK\n"
4.112 + + " System.err.println(s);\n"
4.113 + + " }\n"
4.114 + + " System.out.println();\n"
4.115 + + " }\n"
4.116 + + "}\n");
4.117 + }
4.118 +
4.119 + @Test public void forWarning() throws Exception {
4.120 + HintTest.create().input("package test;\n"
4.121 + + "import java.util.*;"
4.122 + + "public class Test {\n"
4.123 + + " void m(List<String> strings) {\n"
4.124 + + " for (Iterator it = strings.iterator(); it.hasNext(); ) {\n"
4.125 + + " String s = (String) it.next();\n"
4.126 + + " System.out.println(s);\n"
4.127 + + " System.err.println(s);\n"
4.128 + + " }\n"
4.129 + + " }\n"
4.130 + + "}\n").run(IteratorToFor.class).assertWarnings("3:8-3:11:verifier:" + Bundle.ERR_IteratorToFor());
4.131 + }
4.132 +
4.133 + @Test public void forUsedSpecially() throws Exception {
4.134 + HintTest.create().input("package test;\n"
4.135 + + "import java.util.*;"
4.136 + + "public class Test {\n"
4.137 + + " void m(List<String> strings) {\n"
4.138 + + " for (Iterator it = strings.iterator(); it.hasNext(); ) {\n"
4.139 + + " String s = (String) it.next();\n"
4.140 + + " if (s.isEmpty()) {\n"
4.141 + + " it.remove();\n"
4.142 + + " } else {\n"
4.143 + + " System.out.println(s);\n"
4.144 + + " }\n"
4.145 + + " }\n"
4.146 + + " }\n"
4.147 + + "}\n").run(IteratorToFor.class).assertWarnings();
4.148 + }
4.149 +
4.150 + @Test public void forFix() throws Exception {
4.151 + HintTest.create().input("package test;\n"
4.152 + + "import java.util.*;"
4.153 + + "public class Test {\n"
4.154 + + " void m(List<String> strings) {\n"
4.155 + + " Collections.reverse(strings);\n"
4.156 + + " for (Iterator it = strings.iterator(); it.hasNext(); ) {\n"
4.157 + + " String s = (String) it.next();\n"
4.158 + + " System.out.println(s);\n"
4.159 + + " // OK\n"
4.160 + + " System.err.println(s);\n"
4.161 + + " }\n"
4.162 + + " System.out.println();\n"
4.163 + + " }\n"
4.164 + + "}\n").run(IteratorToFor.class).findWarning("4:8-4:11:verifier:" + Bundle.ERR_IteratorToFor()).
4.165 + applyFix().
4.166 + assertCompilable()
4.167 + .assertOutput("package test;\n"
4.168 + + "import java.util.*;"
4.169 + + "public class Test {\n"
4.170 + + " void m(List<String> strings) {\n"
4.171 + + " Collections.reverse(strings);\n"
4.172 + + " for (String s : strings) {\n"
4.173 + + " System.out.println(s);\n"
4.174 + + " // OK\n"
4.175 + + " System.err.println(s);\n"
4.176 + + " }\n"
4.177 + + " System.out.println();\n"
4.178 + + " }\n"
4.179 + + "}\n");
4.180 + }
4.181 +
4.182 + // XXX also ought to match: for (Iterator i = coll.iterator(); i.hasNext(); ) {use((Type) i.next());}
4.183 + // XXX match final modifiers on iterator and/or element vars
4.184 + // XXX remove import of java.util.Iterator if present
4.185 +
4.186 +}