diff -r 7962d72beb03 java.hints/src/org/netbeans/modules/java/hints/Bundle.properties --- a/java.hints/src/org/netbeans/modules/java/hints/Bundle.properties Sat Apr 18 17:08:23 2009 +0100 +++ b/java.hints/src/org/netbeans/modules/java/hints/Bundle.properties Mon Apr 20 23:14:54 2009 +0100 @@ -76,7 +76,7 @@ LBL_Imports_EXCLUDED=Import from Excluded LBL_Imports_STAR=Star import -DSC_Imports_DELAGATE=Delegate - non GUI +DSC_Imports_DELEGATE=Delegate - non GUI DSC_Imports_UNUSED=Unused Import DSC_Imports_DUPLICATE=Multiple Import DSC_Imports_SAME_PACKAGE=Import From The Same Package @@ -262,6 +262,11 @@ ERR_SynchronizationOnNonFinalField=Synchronization on non-final field DN_SynchronizationOnNonFinalField=Synchronization on non-final field +HINT_StaticImport=Convert method to static import +DSC_StaticImport=Convert method to static import +ERR_StaticImport=Convert method to static import +DN_StaticImport=Convert method to static import + HINT_SuspiciousCall=Suspicious call to {0}:\nExpected type {2}, actual type {1} HINT_SuspiciousCallIncompatibleTypes=Suspicious call to {0}:\nGiven object cannot contain instances of {1} (expected {2}) DN_CollectionRemove=Suspicous method call diff -r 7962d72beb03 java.hints/src/org/netbeans/modules/java/hints/StaticImport.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/java.hints/src/org/netbeans/modules/java/hints/StaticImport.java Mon Apr 20 23:14:54 2009 +0100 @@ -0,0 +1,194 @@ +/* + * 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]" + * + * Contributor(s): + * + * Portions Copyrighted 2009 Sun Microsystems, Inc. + */ +package org.netbeans.modules.java.hints; + +import com.sun.source.tree.CompilationUnitTree; +import com.sun.source.tree.ExpressionTree; +import com.sun.source.tree.ImportTree; +import com.sun.source.tree.MethodInvocationTree; +import com.sun.source.tree.Tree.Kind; +import com.sun.source.util.TreePath; +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 javax.lang.model.element.Element; +import javax.lang.model.element.Modifier; +import org.netbeans.api.java.source.CompilationInfo; +import org.netbeans.api.java.source.JavaSource; +import org.netbeans.api.java.source.JavaSource.Phase; +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.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.util.NbBundle; +import static org.netbeans.modules.editor.java.Utilities.getElementName; + +/** + * Hint offering to convert a qualified static method into a static import. e.g. + * Math.abs(-1) -> abs(-1). + * + * @author Samuel Halliday + * @see RFE 89258 + */ +public class StaticImport extends AbstractHint { + + private final AtomicBoolean cancel = new AtomicBoolean(); + + public StaticImport() { + super(true, false, HintSeverity.CURRENT_LINE_WARNING); + } + + @Override + public String getDescription() { + return NbBundle.getMessage(StaticImport.class, "DSC_StaticImport"); + } + + public Set getTreeKinds() { + return EnumSet.of(Kind.METHOD_INVOCATION); + } + + public List run(CompilationInfo info, TreePath treePath) { + if (treePath.getLeaf().getKind() != Kind.METHOD_INVOCATION) { + return null; // XXX is this sanity check needed? + } + MethodInvocationTree tree = (MethodInvocationTree) treePath.getLeaf(); + ExpressionTree identifier = tree.getMethodSelect(); + Element e = info.getTrees().getElement(new TreePath(treePath, identifier)); + if (e == null || !e.getModifiers().contains(Modifier.STATIC)) { + return null; + } + + int start = (int) info.getTrees().getSourcePositions().getStartPosition(info.getCompilationUnit(), identifier); + int end = (int) info.getTrees().getSourcePositions().getEndPosition(info.getCompilationUnit(), identifier); + + // XXX is there a better way to work out if the method is qualified + if (!identifier.toString().contains(".")) { // NOI18N + return null; + } + + // TODO ignore case where containing class not imported yet (that's for errors to handle) + + // TODO ignore case where source code is less than Java 1.5 + + List fixes = Collections.singletonList(new FixImpl(TreePathHandle.create(treePath, info))); + + String desc = NbBundle.getMessage(AddOverrideAnnotation.class, "HINT_StaticImport"); + ErrorDescription ed = ErrorDescriptionFactory.createErrorDescription(getSeverity().toEditorSeverity(), desc, fixes, info.getFileObject(), start, end); + if (cancel.get()) { + return null; + } + return Collections.singletonList(ed); + } + + public String getId() { + return StaticImport.class.getName(); + } + + public String getDisplayName() { + return NbBundle.getMessage(StaticImport.class, "DSC_StaticImport"); + } + + public void cancel() { + cancel.set(true); + } + + private static final class FixImpl implements Fix, Task { + + private final TreePathHandle handle; + + private FixImpl(TreePathHandle handle) { + this.handle = handle; + } + + public String getText() { + return NbBundle.getMessage(StaticImport.class, "HINT_StaticImport"); + } + + public ChangeInfo implement() throws Exception { + JavaSource js = JavaSource.forFileObject(handle.getFileObject()); + js.runModificationTask(this).commit(); + return null; + } + + public void run(WorkingCopy copy) throws Exception { + if (copy.toPhase(Phase.RESOLVED).compareTo(Phase.RESOLVED) < 0) { + return; + } + TreePath path = handle.resolve(copy); + if (path == null || path.getLeaf().getKind() != Kind.METHOD_INVOCATION) { + return; // XXX is this sanity check needed? + } + Element el = copy.getTrees().getElement(path); + CharSequence fqn = getElementName(el.getEnclosingElement(), true) + "." + el.getSimpleName(); + TreeMaker make = copy.getTreeMaker(); + + MethodInvocationTree tree = (MethodInvocationTree) path.getLeaf(); + copy.rewrite(tree.getMethodSelect(), make.Identifier(el)); + + CompilationUnitTree cut = copy.getCompilationUnit(); + TreePath klassPath = getContainingClass(path); + Element klassEl = copy.getTrees().getElement(klassPath); + if (el.getEnclosingElement().equals(klassEl)) { + return; + } + + // XXX can't use JavaFixAllImports.addImports because no support for static imports + List imports = new ArrayList(cut.getImports()); + ImportTree imp = make.Import(make.Identifier(fqn), true); + String impS = imp.getQualifiedIdentifier().toString(); + for (ImportTree i : imports) { + if (!i.isStatic()) { + continue; + } + String iS = i.getQualifiedIdentifier().toString(); + // XXX dependence on undocumented form of toString + if (impS.equals(iS) || (iS.endsWith(".*") && impS.startsWith(iS.substring(0, iS.length() - 2)))) { // NOI18N + return; + } + } + imports.add(imp); // XXX smart insertion + CompilationUnitTree nue = make.CompilationUnit(cut.getPackageName(), imports, cut.getTypeDecls(), cut.getSourceFile()); + copy.rewrite(cut, nue); + } + + private TreePath getContainingClass(TreePath tp) { + while (tp != null && tp.getLeaf().getKind() != Kind.CLASS) { + tp = tp.getParentPath(); + } + return tp; + } + } +} diff -r 7962d72beb03 java.hints/src/org/netbeans/modules/java/hints/resources/layer.xml --- a/java.hints/src/org/netbeans/modules/java/hints/resources/layer.xml Sat Apr 18 17:08:23 2009 +0100 +++ b/java.hints/src/org/netbeans/modules/java/hints/resources/layer.xml Mon Apr 20 23:14:54 2009 +0100 @@ -137,6 +137,7 @@ +