diff -r 24d72d2643e1 java.hints/src/org/netbeans/modules/java/hints/Bundle.properties --- a/java.hints/src/org/netbeans/modules/java/hints/Bundle.properties Sat May 23 03:06:07 2009 +0200 +++ b/java.hints/src/org/netbeans/modules/java/hints/Bundle.properties Sun May 24 00:15:37 2009 -0400 @@ -58,7 +58,7 @@ LBL_Braces_Fix=Add Braces -# Assgnment to itself +# Assignment to itself LBL_ATI=Assignment To Itself DSC_ATI=Assignment To Itself @@ -108,7 +108,7 @@ # {0} name of the annotation LBL_AnnotationAsSuperInterface=Don't use Annotation as super interface HNT_AnnotationAsSuperInterface=Don''t use Annotation {0} as super interface -DSC_AnnotationAsSuperInterface=Despite of the compiler permits such constructs\ +DSC_AnnotationAsSuperInterface=Despite the compiler permitting such constructs,\ Annotations should not be used as superinterfaces. # Utility class @@ -131,14 +131,14 @@ LBL_Javac_DIVISION_BY_ZERO=Division By Zero LBL_Javac_RAWTYPES=Raw Types -DSC_Javac_DEPRECATED=Deprecated -DSC_Javac_UNCHECKED=Unchecked -DSC_Javac_FALLTHROUGH=Fallthrough -DSC_Javac_SERIALIZATION=Serialization -DSC_Javac_FINALLY=Finally -DSC_Javac_UNNECESSARY_CAST=Unnecessary Cast +DSC_Javac_DEPRECATED=Warn when code uses deprecated API. +DSC_Javac_UNCHECKED=Warn when unchecked conversions may cause ClassCastExceptions at runtime. +DSC_Javac_FALLTHROUGH=Warn when a case can fall through to the next case. +DSC_Javac_SERIALIZATION=Warn when a class which implements java.io.Serializable does not declare a serialVersionUID. +DSC_Javac_FINALLY=Warn when a finally block interrupts flow of a try/catch block. +DSC_Javac_UNNECESSARY_CAST=Warn when an object is cast unnecessarily to the same type or a supertype. DSC_Javac_EMPTY_STATEMENT_AFTER_IF=Empty Statement After If -DSC_Javac_OVERRIDES=Overrides +DSC_Javac_OVERRIDES=Warn when an overriding method is not annotated with @Overrides DSC_Javac_DIVISION_BY_ZERO=Division By Zero DSC_Javac_RAWTYPES=Raw Types @@ -157,6 +157,13 @@ DSC_WrongStringComparison=Checks for usages of == or != operator for comparing Strings.
\ String comparisons should generally be done using the equals() method. +FIX_WrongStringComparison_NullCheck=Use equals() with null check +FIX_WrongStringComparison_TernaryNullCheck=Use equals() with null check (ternary) +FIX_WrongStringComparison_NoNullCheck=Use equals() without null check + +WrongStringComparisonCustomizer.ternaryNullCheck.text=Check for null using ternary conditional operator +ACSD_Ternary_Null_Check=Controls whether the fix uses a ternary conditional null check. + #Empty statements LBL_Empty_FOR_LOOP=Empty statement after 'for' LBL_Empty_ENHANCED_FOR_LOOP=Empty statement after 'for' diff -r 24d72d2643e1 java.hints/src/org/netbeans/modules/java/hints/WrongStringComparison.java --- a/java.hints/src/org/netbeans/modules/java/hints/WrongStringComparison.java Sat May 23 03:06:07 2009 +0200 +++ b/java.hints/src/org/netbeans/modules/java/hints/WrongStringComparison.java Sun May 24 00:15:37 2009 -0400 @@ -35,18 +35,28 @@ import com.sun.source.util.SourcePositions; import com.sun.source.util.TreePath; import com.sun.source.util.Trees; +import java.util.ArrayList; import java.util.Collections; import java.util.EnumSet; import java.util.List; import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.prefs.Preferences; import javax.lang.model.type.TypeMirror; +import javax.swing.JComponent; import org.netbeans.api.java.source.CompilationInfo; +import org.netbeans.api.java.source.JavaSource; +import org.netbeans.api.java.source.Task; +import org.netbeans.api.java.source.TreeMaker; +import org.netbeans.api.java.source.TreePathHandle; +import org.netbeans.api.java.source.WorkingCopy; import org.netbeans.modules.java.hints.introduce.CopyFinder; import org.netbeans.modules.java.hints.spi.AbstractHint; +import org.netbeans.spi.editor.hints.ChangeInfo; import org.netbeans.spi.editor.hints.ErrorDescription; import org.netbeans.spi.editor.hints.ErrorDescriptionFactory; import org.netbeans.spi.editor.hints.Fix; +import org.openide.filesystems.FileObject; import org.openide.util.NbBundle; /** @@ -55,20 +65,17 @@ */ public class WrongStringComparison extends AbstractHint { - private static String STRING_TYPE = "java.lang.String"; // NOI18N - - private static final List NO_FIXES = Collections.emptyList(); - + private static final String TERNARY_NULL_CHECK = "ternary-null-check"; + private static final String STRING_TYPE = "java.lang.String"; // NOI18N private static final Set TREE_KINDS = EnumSet.of( Tree.Kind.EQUAL_TO, Tree.Kind.NOT_EQUAL_TO ); private AtomicBoolean cancel = new AtomicBoolean(); public WrongStringComparison() { - super( true, true, AbstractHint.HintSeverity.WARNING); + super(true, true, AbstractHint.HintSeverity.WARNING); } - public Set getTreeKinds() { return TREE_KINDS; } @@ -78,8 +85,7 @@ Tree t = treePath.getLeaf(); - if ( t.getKind() != Tree.Kind.EQUAL_TO && - t.getKind() != Tree.Kind.NOT_EQUAL_TO ) { + if (!getTreeKinds().contains(t.getKind())) { return null; } @@ -100,12 +106,16 @@ return null; } + FileObject file = info.getFileObject(); + TreePathHandle tph = TreePathHandle.create(treePath, info); + ArrayList fixes = new ArrayList(); + fixes.add(new WrongStringComparisonFix(file, tph, getTernaryNullCheck())); + fixes.add(new WrongStringComparisonFix(file, tph, null)); //no null check return Collections.singletonList( ErrorDescriptionFactory.createErrorDescription( getSeverity().toEditorSeverity(), getDisplayName(), - // Collections.singletonList(new EmptyStatementFix( info.getFileObject(), TreePathHandle.create(tp, info) ) ), - NO_FIXES, + Collections.unmodifiableList(fixes), info.getFileObject(), (int)info.getTrees().getSourcePositions().getStartPosition(info.getCompilationUnit(), t), (int)info.getTrees().getSourcePositions().getEndPosition(info.getCompilationUnit(), t)) ); @@ -131,6 +141,15 @@ return NbBundle.getMessage(WrongStringComparison.class, "DSC_WrongStringComparison"); } + public boolean getTernaryNullCheck() { + return getTernaryNullCheck(getPreferences(null)); + } + + @Override + public JComponent getCustomizer(Preferences node) { + return new WrongStringComparisonCustomizer(node); + } + private boolean checkInsideGeneratedEquals(CompilationInfo info, TreePath treePath, Tree left, Tree right) { TreePath sourcePathParent = treePath.getParentPath(); @@ -160,4 +179,82 @@ return CopyFinder.isDuplicate(info, originalPath, correctPath, cancel); } + static boolean getTernaryNullCheck(Preferences p) { + return p.getBoolean(TERNARY_NULL_CHECK, false); + } + + static void setTernaryNullCheck(Preferences p, boolean selected) { + p.putBoolean(TERNARY_NULL_CHECK, selected); + } + + static class WrongStringComparisonFix implements Fix, Task { + + protected FileObject file; + protected TreePathHandle tph; + protected Boolean nullCheck; + + public WrongStringComparisonFix(FileObject file, TreePathHandle tph, Boolean nullCheck) { + this.file = file; + this.tph = tph; + this.nullCheck = nullCheck; + } + + public String getText() { + if (nullCheck == null) { + return NbBundle.getMessage(WrongStringComparison.class, "FIX_WrongStringComparison_NoNullCheck"); // NOI18N + } else { + if (nullCheck) { + return NbBundle.getMessage(WrongStringComparison.class, "FIX_WrongStringComparison_TernaryNullCheck"); // NOI18N + } else { + return NbBundle.getMessage(WrongStringComparison.class, "FIX_WrongStringComparison_NullCheck"); // NOI18N + } + } + } + + public ChangeInfo implement() throws Exception { + JavaSource js = JavaSource.forFileObject(file); + js.runModificationTask(this).commit(); + return null; + } + + public void run(WorkingCopy copy) throws Exception { + copy.toPhase(JavaSource.Phase.PARSED); + TreePath path = tph.resolve(copy); + if (path != null) { + TreeMaker make = copy.getTreeMaker(); + BinaryTree oldTree = (BinaryTree) path.getLeaf(); + ExpressionTree left = oldTree.getLeftOperand(); + ExpressionTree right = oldTree.getRightOperand(); + ExpressionTree leftEquals = make.MemberSelect(left, "equals"); // NOI18N + ExpressionTree leftEqualsRight = make.MethodInvocation(Collections.emptyList(), leftEquals, Collections.singletonList(right)); + if (oldTree.getKind() == Tree.Kind.NOT_EQUAL_TO) { + leftEqualsRight = make.Unary(Tree.Kind.LOGICAL_COMPLEMENT, leftEqualsRight); + } + ExpressionTree newTree; + if (nullCheck == null) { + // str1.equals(str2) + newTree = leftEqualsRight; + } else { + ExpressionTree leftEqNull = make.Binary(Tree.Kind.EQUAL_TO, left, make.Identifier("null")); // NOI18N + ExpressionTree rightEqNull = make.Binary(oldTree.getKind(), right, make.Identifier("null")); // NOI18N + if (nullCheck) { + // str1 == null ? str2 == null : str1.equals(str2) + newTree = make.ConditionalExpression(leftEqNull, rightEqNull, leftEqualsRight); + } else { + // (str1 == null && str2 == null) || (str1 != null && str1.equals(str2)) + ExpressionTree leftNeNull = make.Binary(Tree.Kind.NOT_EQUAL_TO, left, make.Identifier("null")); // NOI18N + ExpressionTree orLeft = make.Parenthesized(make.Binary(Tree.Kind.CONDITIONAL_AND, leftEqNull, rightEqNull)); + ExpressionTree orRight = make.Parenthesized(make.Binary(Tree.Kind.CONDITIONAL_AND, leftNeNull, leftEqualsRight)); + newTree = make.Binary(Tree.Kind.CONDITIONAL_OR, orLeft, orRight); + } + if (path.getParentPath().getLeaf().getKind() != Tree.Kind.PARENTHESIZED) { + newTree = make.Parenthesized(newTree); + } + } + copy.rewrite(oldTree, newTree); + } + } + + } + } diff -r 24d72d2643e1 java.hints/src/org/netbeans/modules/java/hints/WrongStringComparisonCustomizer.form --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/java.hints/src/org/netbeans/modules/java/hints/WrongStringComparisonCustomizer.form Sun May 24 00:15:37 2009 -0400 @@ -0,0 +1,45 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff -r 24d72d2643e1 java.hints/src/org/netbeans/modules/java/hints/WrongStringComparisonCustomizer.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/java.hints/src/org/netbeans/modules/java/hints/WrongStringComparisonCustomizer.java Sun May 24 00:15:37 2009 -0400 @@ -0,0 +1,101 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 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 + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2009 Sun Microsystems, Inc. + */ + +package org.netbeans.modules.java.hints; + +import java.util.prefs.Preferences; + + +/** + * Options Customiser panel for {@link WrongStringComparison} hint + * + * @author Mark Lewis + */ +public class WrongStringComparisonCustomizer extends javax.swing.JPanel { + + private Preferences p; + + /** Creates new customizer WrongStringComparisonCustomizer */ + public WrongStringComparisonCustomizer(Preferences p) { + initComponents(); + this.p = p; + ternaryNullCheck.setSelected(WrongStringComparison.getTernaryNullCheck(p)); + } + + /** This method is called from within the constructor to + * initialize the form. + * WARNING: Do NOT modify this code. The content of this method is + * always regenerated by the FormEditor. + */ + // //GEN-BEGIN:initComponents + private void initComponents() { + + ternaryNullCheck = new javax.swing.JCheckBox(); + + ternaryNullCheck.setText(org.openide.util.NbBundle.getMessage(WrongStringComparisonCustomizer.class, "WrongStringComparisonCustomizer.ternaryNullCheck.text")); // NOI18N + ternaryNullCheck.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + ternaryNullCheckActionPerformed(evt); + } + }); + + org.jdesktop.layout.GroupLayout layout = new org.jdesktop.layout.GroupLayout(this); + this.setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) + .add(ternaryNullCheck, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 265, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE) + ); + layout.setVerticalGroup( + layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) + .add(ternaryNullCheck) + ); + + ternaryNullCheck.getAccessibleContext().setAccessibleDescription(org.openide.util.NbBundle.getMessage(WrongStringComparisonCustomizer.class, "ACSD_Ternary_Null_Check")); // NOI18N + }// //GEN-END:initComponents + + private void ternaryNullCheckActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_ternaryNullCheckActionPerformed + WrongStringComparison.setTernaryNullCheck(p, ternaryNullCheck.isSelected()); + }//GEN-LAST:event_ternaryNullCheckActionPerformed + + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JCheckBox ternaryNullCheck; + // End of variables declaration//GEN-END:variables + +}