This Bugzilla instance is a read-only archive of historic NetBeans bug reports. To report a bug in NetBeans please follow the project's instructions for reporting issues.

View | Details | Raw Unified | Return to bug 89258
Collapse All | Expand All

(-)a/java.hints/src/org/netbeans/modules/java/hints/Bundle.properties (-1 / +6 lines)
Lines 76-82 Link Here
76
LBL_Imports_EXCLUDED=Import from Excluded
76
LBL_Imports_EXCLUDED=Import from Excluded
77
LBL_Imports_STAR=Star import
77
LBL_Imports_STAR=Star import
78
78
79
DSC_Imports_DELAGATE=Delegate - non GUI
79
DSC_Imports_DELEGATE=Delegate - non GUI
80
DSC_Imports_UNUSED=Unused Import
80
DSC_Imports_UNUSED=Unused Import
81
DSC_Imports_DUPLICATE=Multiple Import
81
DSC_Imports_DUPLICATE=Multiple Import
82
DSC_Imports_SAME_PACKAGE=Import From The Same Package
82
DSC_Imports_SAME_PACKAGE=Import From The Same Package
Lines 262-267 Link Here
262
ERR_SynchronizationOnNonFinalField=Synchronization on non-final field
262
ERR_SynchronizationOnNonFinalField=Synchronization on non-final field
263
DN_SynchronizationOnNonFinalField=Synchronization on non-final field
263
DN_SynchronizationOnNonFinalField=Synchronization on non-final field
264
264
265
HINT_StaticImport=Convert method to static import
266
DSC_StaticImport=Convert method to static import
267
ERR_StaticImport=Convert method to static import
268
DN_StaticImport=Convert method to static import
269
265
HINT_SuspiciousCall=Suspicious call to {0}:\nExpected type {2}, actual type {1}
270
HINT_SuspiciousCall=Suspicious call to {0}:\nExpected type {2}, actual type {1}
266
HINT_SuspiciousCallIncompatibleTypes=Suspicious call to {0}:\nGiven object cannot contain instances of {1} (expected {2})
271
HINT_SuspiciousCallIncompatibleTypes=Suspicious call to {0}:\nGiven object cannot contain instances of {1} (expected {2})
267
DN_CollectionRemove=Suspicous method call
272
DN_CollectionRemove=Suspicous method call
(-)3489508b5c9c (+272 lines)
Added Link Here
1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
4
 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
5
 *
6
 * The contents of this file are subject to the terms of either the GNU
7
 * General Public License Version 2 only ("GPL") or the Common
8
 * Development and Distribution License("CDDL") (collectively, the
9
 * "License"). You may not use this file except in compliance with the
10
 * License. You can obtain a copy of the License at
11
 * http://www.netbeans.org/cddl-gplv2.html
12
 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
13
 * specific language governing permissions and limitations under the
14
 * License.  When distributing the software, include this License Header
15
 * Notice in each file and include the License file at
16
 * nbbuild/licenses/CDDL-GPL-2-CP.  Sun designates this
17
 * particular file as subject to the "Classpath" exception as provided
18
 * by Sun in the GPL Version 2 section of the License file that
19
 * accompanied this code. If applicable, add the following below the
20
 * License Header, with the fields enclosed by brackets [] replaced by
21
 * your own identifying information:
22
 * "Portions Copyrighted [year] [name of copyright owner]"
23
 *
24
 * Contributor(s):
25
 *
26
 * Portions Copyrighted 2009 Sun Microsystems, Inc.
27
 */
28
package org.netbeans.modules.java.hints;
29
30
import com.sun.source.tree.CompilationUnitTree;
31
import com.sun.source.tree.ExpressionTree;
32
import com.sun.source.tree.ImportTree;
33
import com.sun.source.tree.MethodInvocationTree;
34
import com.sun.source.tree.Tree.Kind;
35
import com.sun.source.util.TreePath;
36
import java.util.ArrayList;
37
import java.util.Collections;
38
import java.util.EnumSet;
39
import java.util.List;
40
import java.util.Set;
41
import java.util.concurrent.atomic.AtomicBoolean;
42
import javax.lang.model.element.Element;
43
import javax.lang.model.element.ElementKind;
44
import javax.lang.model.element.Modifier;
45
import javax.lang.model.element.TypeElement;
46
import javax.lang.model.type.TypeMirror;
47
import org.netbeans.api.java.source.CompilationInfo;
48
import org.netbeans.api.java.source.ElementUtilities;
49
import org.netbeans.api.java.source.JavaSource;
50
import org.netbeans.api.java.source.JavaSource.Phase;
51
import org.netbeans.api.java.source.Task;
52
import org.netbeans.api.java.source.TreeMaker;
53
import org.netbeans.api.java.source.TreePathHandle;
54
import org.netbeans.api.java.source.WorkingCopy;
55
import org.netbeans.modules.java.hints.spi.AbstractHint;
56
import org.netbeans.spi.editor.hints.ChangeInfo;
57
import org.netbeans.spi.editor.hints.ErrorDescription;
58
import org.netbeans.spi.editor.hints.ErrorDescriptionFactory;
59
import org.netbeans.spi.editor.hints.Fix;
60
import org.openide.util.NbBundle;
61
import static org.netbeans.modules.editor.java.Utilities.getElementName;
62
63
/**
64
 * Hint offering to convert a qualified static method into a static import. e.g.
65
 * <code>Math.abs(-1)</code> -> <code>abs(-1)</code>.
66
 *
67
 * @author Sam Halliday
68
 * @see <a href="http://www.netbeans.org/issues/show_bug.cgi?id=89258">RFE 89258</a>
69
 */
70
public class StaticImport extends AbstractHint {
71
72
    private final AtomicBoolean cancel = new AtomicBoolean();
73
74
    public StaticImport() {
75
        super(true, false, HintSeverity.CURRENT_LINE_WARNING);
76
    }
77
78
    @Override
79
    public String getDescription() {
80
        return NbBundle.getMessage(StaticImport.class, "DSC_StaticImport");
81
    }
82
83
    public Set<Kind> getTreeKinds() {
84
        return EnumSet.of(Kind.METHOD_INVOCATION);
85
    }
86
87
    public List<ErrorDescription> run(CompilationInfo info, TreePath treePath) {
88
        if (treePath.getLeaf().getKind() != Kind.METHOD_INVOCATION) {
89
            return null;
90
        }
91
        cancel.set(false);
92
        MethodInvocationTree tree = (MethodInvocationTree) treePath.getLeaf();
93
        ExpressionTree identifier = tree.getMethodSelect();
94
        Element e = info.getTrees().getElement(new TreePath(treePath, identifier));
95
        if (e == null || !e.getModifiers().contains(Modifier.STATIC) || identifier.getKind() != Kind.MEMBER_SELECT) {
96
            return null;
97
        }
98
        // TODO ignore case where containing class not imported yet (that's for errors to handle)
99
100
        // TODO ignore case where source code is less than Java 1.5
101
102
        String sn = e.getSimpleName().toString();
103
        if (sn.length() == 0){
104
            return null;
105
        }
106
        String fqn;
107
        TreePath klassPath = getContainingClass(treePath);
108
        Element klassEl = info.getTrees().getElement(klassPath);
109
        if (info.getTypes().isSubtype(klassEl.asType(), e.getEnclosingElement().asType())) {
110
            //if (e.getEnclosingElement().equals(klassEl)) {
111
            // a local or inherited static method, no edits to the import tree necessary
112
            fqn = null;
113
        } else {
114
            Element klassElement = info.getTrees().getElement(klassPath);
115
            if (hasElementWithSimpleName(info, klassElement, sn, ElementKind.METHOD) || isStaticImportNameClash(info, e)) {
116
                return null;
117
            }
118
            fqn = getMethodFqn(e);
119
        }
120
121
        List<Fix> fixes = Collections.<Fix>singletonList(new FixImpl(TreePathHandle.create(treePath, info), fqn, sn));
122
123
        int start = (int) info.getTrees().getSourcePositions().getStartPosition(info.getCompilationUnit(), identifier);
124
        int end = (int) info.getTrees().getSourcePositions().getEndPosition(info.getCompilationUnit(), identifier);
125
        String desc = NbBundle.getMessage(AddOverrideAnnotation.class, "HINT_StaticImport");
126
        ErrorDescription ed = ErrorDescriptionFactory.createErrorDescription(getSeverity().toEditorSeverity(), desc, fixes, info.getFileObject(), start, end);
127
        if (cancel.get()) {
128
            return null;
129
        }
130
        return Collections.singletonList(ed);
131
    }
132
133
    public String getId() {
134
        return StaticImport.class.getName();
135
    }
136
137
    public String getDisplayName() {
138
        return NbBundle.getMessage(StaticImport.class, "DSC_StaticImport");
139
    }
140
141
    public void cancel() {
142
        cancel.set(true);
143
    }
144
145
    private static final class FixImpl implements Fix, Task<WorkingCopy> {
146
147
        private final TreePathHandle handle;
148
        private final String fqn;
149
        private final String sn;
150
151
        private FixImpl(TreePathHandle handle, String fqn, String sn) {
152
            this.handle = handle;
153
            this.fqn = fqn;
154
            this.sn = sn;
155
        }
156
157
        public String getText() {
158
            return NbBundle.getMessage(StaticImport.class, "HINT_StaticImport");
159
        }
160
161
        public ChangeInfo implement() throws Exception {
162
            JavaSource js = JavaSource.forFileObject(handle.getFileObject());
163
            js.runModificationTask(this).commit();
164
            return null;
165
        }
166
167
        public void run(WorkingCopy copy) throws Exception {
168
            if (copy.toPhase(Phase.RESOLVED).compareTo(Phase.RESOLVED) < 0) {
169
                return;
170
            }
171
            TreePath path = handle.resolve(copy);
172
            if (path == null || path.getLeaf().getKind() != Kind.METHOD_INVOCATION) {
173
                return;
174
            }
175
            MethodInvocationTree tree = (MethodInvocationTree) path.getLeaf();
176
            TreeMaker make = copy.getTreeMaker();
177
            copy.rewrite(tree.getMethodSelect(), make.Identifier(sn));
178
            if (fqn == null) {
179
                return;
180
            }
181
            CompilationUnitTree cut = copy.getCompilationUnit();
182
183
            // XXX can't use JavaFixAllImports.addImports because no support for static imports
184
            List<ImportTree> imports = new ArrayList<ImportTree>(cut.getImports());
185
            ImportTree imp = make.Import(make.Identifier(fqn), true);
186
            for (ImportTree i : imports) {
187
                if (!i.isStatic()) {
188
                    continue;
189
                }
190
                String iS = i.getQualifiedIdentifier().toString();
191
                if (fqn.equals(iS) || (iS.endsWith(".*") && fqn.startsWith(iS.substring(0, iS.length() - 2)))) { // NOI18N
192
                    return;
193
                }
194
            }
195
            imports.add(imp); // XXX smart insertion
196
            CompilationUnitTree nue = make.CompilationUnit(cut.getPackageName(), imports, cut.getTypeDecls(), cut.getSourceFile());
197
            copy.rewrite(cut, nue);
198
        }
199
    }
200
201
    public static String getMethodFqn(Element e) {
202
        return getElementName(e.getEnclosingElement(), true) + "." + e.getSimpleName();
203
    }
204
205
    // returns the class
206
    public static TreePath getContainingClass(TreePath tp) {
207
        while (tp != null && tp.getLeaf().getKind() != Kind.CLASS) {
208
            tp = tp.getParentPath();
209
        }
210
        return tp;
211
    }
212
213
    // returns true if an element of kind is enclosed in el with simple name sn
214
    public static boolean hasElementWithSimpleName(CompilationInfo info, Element el, final String sn, final ElementKind kind) {
215
        Iterable<? extends Element> members =
216
                info.getElementUtilities().getMembers(el.asType(), new ElementUtilities.ElementAcceptor() {
217
218
            public boolean accept(Element e, TypeMirror type) {
219
                if (e.getKind() == kind && e.getSimpleName().toString().equals(sn)) {
220
                    return true;
221
                }
222
                return false;
223
            }
224
        });
225
        return members.iterator().hasNext();
226
    }
227
228
    // return true if a static import exists with a clashing simple name, but not fqn
229
    // caveat: clashes with supertype methods from other packages will return false
230
    public static boolean isStaticImportNameClash(CompilationInfo info, Element e) {
231
        String fqn = getMethodFqn(e);
232
        String sn = e.getSimpleName().toString();
233
        String pkg = info.getElements().getPackageOf(e).getQualifiedName().toString();
234
        for (ImportTree i : info.getCompilationUnit().getImports()) {
235
            if (!i.isStatic()) {
236
                continue;
237
            }
238
            String q = i.getQualifiedIdentifier().toString();
239
            if (q.endsWith(".*")) { //NOI18N
240
                TypeElement ie = info.getElements().getTypeElement(q.substring(0, q.length() - 2));
241
                if (ie == null) {
242
                    continue;
243
                }
244
                for (Element enclosed : ie.getEnclosedElements()) {
245
                    Set<Modifier> modifiers = enclosed.getModifiers();
246
                    if (enclosed.getKind() != ElementKind.METHOD || !modifiers.contains(Modifier.STATIC) || modifiers.contains(Modifier.PRIVATE)) {
247
                        continue;
248
                    }
249
                    String pkg1 = info.getElements().getPackageOf(enclosed).getQualifiedName().toString();
250
                    if (!pkg.endsWith(pkg1) && !modifiers.contains(Modifier.PUBLIC)) {
251
                        continue;
252
                    }
253
                    String sn1 = enclosed.getSimpleName().toString();
254
                    String fqn1 = getMethodFqn(enclosed);
255
                    if (sn.equals(sn1) && !fqn.equals(fqn1)) {
256
                        return true;
257
                    }
258
                }
259
            } else {
260
                int endIndex = q.lastIndexOf("."); //NOI18N
261
                if (endIndex == -1 || endIndex == q.length()) {
262
                    continue;
263
                }
264
                String fqn1 = q.substring(endIndex + 1);
265
                if (q.substring(endIndex).equals(sn) && !fqn.equals(fqn1)) {
266
                    return true;
267
                }
268
            }
269
        }
270
        return false;
271
    }
272
}
(-)a/java.hints/src/org/netbeans/modules/java/hints/resources/layer.xml (+1 lines)
Lines 137-142 Link Here
137
                        <attr name="instanceCreate" methodvalue="org.netbeans.modules.java.hints.EqualsHint.getDelegate"/>
137
                        <attr name="instanceCreate" methodvalue="org.netbeans.modules.java.hints.EqualsHint.getDelegate"/>
138
                    </file>
138
                    </file>
139
                    <file name="org-netbeans-modules-java-hints-EqualsMethodHint.instance"/>
139
                    <file name="org-netbeans-modules-java-hints-EqualsMethodHint.instance"/>
140
                    <file name="org-netbeans-modules-java-hints-StaticImport.instance"/>
140
                    <file name="org-netbeans-modules-java-hints-SyncOnNonFinal.instance"/>
141
                    <file name="org-netbeans-modules-java-hints-SyncOnNonFinal.instance"/>
141
                    <file name="org-netbeans-modules-java-hints-CollectionRemove.instance"/>
142
                    <file name="org-netbeans-modules-java-hints-CollectionRemove.instance"/>
142
                </folder>
143
                </folder>

Return to bug 89258