--- a/java.hints/src/org/netbeans/modules/java/hints/Bundle.properties Sat Apr 18 17:08:23 2009 +0100
+++ a/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
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ 7962d72beb03 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;
+ }
+ }
+}
--- a/java.hints/src/org/netbeans/modules/java/hints/resources/layer.xml Sat Apr 18 17:08:23 2009 +0100
+++ a/java.hints/src/org/netbeans/modules/java/hints/resources/layer.xml Mon Apr 20 23:14:54 2009 +0100
@@ -137,6 +137,7 @@
+