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 (+274 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.Collection;
38
import java.util.Collections;
39
import java.util.EnumSet;
40
import java.util.List;
41
import java.util.Set;
42
import java.util.concurrent.atomic.AtomicBoolean;
43
import javax.lang.model.element.Element;
44
import javax.lang.model.element.ElementKind;
45
import javax.lang.model.element.Modifier;
46
import javax.lang.model.element.TypeElement;
47
import javax.lang.model.type.TypeMirror;
48
import javax.tools.Diagnostic;
49
import org.netbeans.api.java.source.CompilationInfo;
50
import org.netbeans.api.java.source.ElementUtilities;
51
import org.netbeans.api.java.source.JavaSource;
52
import org.netbeans.api.java.source.JavaSource.Phase;
53
import org.netbeans.api.java.source.Task;
54
import org.netbeans.api.java.source.TreeMaker;
55
import org.netbeans.api.java.source.TreePathHandle;
56
import org.netbeans.api.java.source.WorkingCopy;
57
import org.netbeans.modules.java.hints.spi.AbstractHint;
58
import org.netbeans.spi.editor.hints.ChangeInfo;
59
import org.netbeans.spi.editor.hints.ErrorDescription;
60
import org.netbeans.spi.editor.hints.ErrorDescriptionFactory;
61
import org.netbeans.spi.editor.hints.Fix;
62
import org.openide.util.NbBundle;
63
import static org.netbeans.modules.editor.java.Utilities.getElementName;
64
65
/**
66
 * Hint offering to convert a qualified static method into a static import. e.g.
67
 * <code>Math.abs(-1)</code> -> <code>abs(-1)</code>.
68
 *
69
 * @author Sam Halliday
70
 * @see <a href="http://www.netbeans.org/issues/show_bug.cgi?id=89258">RFE 89258</a>
71
 */
72
public class StaticImport extends AbstractHint {
73
74
    private final AtomicBoolean cancel = new AtomicBoolean();
75
76
    public StaticImport() {
77
        super(true, false, HintSeverity.CURRENT_LINE_WARNING);
78
    }
79
80
    @Override
81
    public String getDescription() {
82
        return NbBundle.getMessage(StaticImport.class, "DSC_StaticImport");
83
    }
84
85
    public Set<Kind> getTreeKinds() {
86
        return EnumSet.of(Kind.METHOD_INVOCATION);
87
    }
88
89
    public List<ErrorDescription> run(CompilationInfo info, TreePath treePath) {
90
        if (treePath.getLeaf().getKind() != Kind.METHOD_INVOCATION) {
91
            return null;
92
        }
93
        cancel.set(false);
94
        MethodInvocationTree tree = (MethodInvocationTree) treePath.getLeaf();
95
        ExpressionTree identifier = tree.getMethodSelect();
96
        Element e = info.getTrees().getElement(new TreePath(treePath, identifier));
97
        if (e == null || !e.getModifiers().contains(Modifier.STATIC) || identifier.getKind() != Kind.MEMBER_SELECT) {
98
            return null;
99
        }
100
        int start = (int) info.getTrees().getSourcePositions().getStartPosition(info.getCompilationUnit(), identifier);
101
        int end = (int) info.getTrees().getSourcePositions().getEndPosition(info.getCompilationUnit(), identifier);
102
        // XXX is there a better way to ignore error cases
103
        for (Diagnostic diagnostic : info.getDiagnostics()) {
104
            if (diagnostic.getStartPosition() <= end && diagnostic.getEndPosition() >= start) {
105
                return null;
106
            }
107
        }
108
        // TODO ignore case where source code is less than Java 1.5
109
110
        String sn = e.getSimpleName().toString();
111
        String fqn = null;
112
        TreePath klassPath = getContainingClass(treePath);
113
        Element klassEl = info.getTrees().getElement(klassPath);
114
        if (!info.getTypes().isSubtype(klassEl.asType(), e.getEnclosingElement().asType())) {
115
            if (hasElementWithSimpleName(info, klassEl, 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
        String desc = NbBundle.getMessage(AddOverrideAnnotation.class, "HINT_StaticImport");
123
        ErrorDescription ed = ErrorDescriptionFactory.createErrorDescription(getSeverity().toEditorSeverity(), desc, fixes, info.getFileObject(), start, end);
124
        if (cancel.get()) {
125
            return null;
126
        }
127
        return Collections.singletonList(ed);
128
    }
129
130
    public String getId() {
131
        return StaticImport.class.getName();
132
    }
133
134
    public String getDisplayName() {
135
        return NbBundle.getMessage(StaticImport.class, "DSC_StaticImport");
136
    }
137
138
    public void cancel() {
139
        cancel.set(true);
140
    }
141
142
    private static final class FixImpl implements Fix, Task<WorkingCopy> {
143
144
        private final TreePathHandle handle;
145
        private final String fqn;
146
        private final String sn;
147
148
        private FixImpl(TreePathHandle handle, String fqn, String sn) {
149
            this.handle = handle;
150
            this.fqn = fqn;
151
            this.sn = sn;
152
        }
153
154
        public String getText() {
155
            return NbBundle.getMessage(StaticImport.class, "HINT_StaticImport");
156
        }
157
158
        public ChangeInfo implement() throws Exception {
159
            JavaSource js = JavaSource.forFileObject(handle.getFileObject());
160
            js.runModificationTask(this).commit();
161
            return null;
162
        }
163
164
        public void run(WorkingCopy copy) throws Exception {
165
            if (copy.toPhase(Phase.RESOLVED).compareTo(Phase.RESOLVED) < 0) {
166
                return;
167
            }
168
            TreePath path = handle.resolve(copy);
169
            if (path == null || path.getLeaf().getKind() != Kind.METHOD_INVOCATION) {
170
                return;
171
            }
172
            MethodInvocationTree tree = (MethodInvocationTree) path.getLeaf();
173
            TreeMaker make = copy.getTreeMaker();
174
            copy.rewrite(tree.getMethodSelect(), make.Identifier(sn));
175
            if (fqn == null) {
176
                return;
177
            }
178
            CompilationUnitTree cut = copy.getCompilationUnit();
179
            CompilationUnitTree nue = addStaticImports(cut, Collections.singleton(fqn), make);
180
            copy.rewrite(cut, nue);
181
        }
182
    }
183
184
    public static String getMethodFqn(Element e) {
185
        return getElementName(e.getEnclosingElement(), true) + "." + e.getSimpleName();
186
    }
187
188
    // returns the class
189
    public static TreePath getContainingClass(TreePath tp) {
190
        while (tp != null && tp.getLeaf().getKind() != Kind.CLASS) {
191
            tp = tp.getParentPath();
192
        }
193
        return tp;
194
    }
195
196
    // returns true if an element of kind is enclosed in el with simple name sn
197
    public static boolean hasElementWithSimpleName(CompilationInfo info, Element el, final String sn, final ElementKind kind) {
198
        Iterable<? extends Element> members =
199
                info.getElementUtilities().getMembers(el.asType(), new ElementUtilities.ElementAcceptor() {
200
201
            public boolean accept(Element e, TypeMirror type) {
202
                if (e.getKind() == kind && e.getSimpleName().toString().equals(sn)) {
203
                    return true;
204
                }
205
                return false;
206
            }
207
        });
208
        return members.iterator().hasNext();
209
    }
210
211
    // return true if a static import exists with a clashing simple name, but not fqn
212
    // caveat: clashes with supertype methods from other packages will return false
213
    public static boolean isStaticImportNameClash(CompilationInfo info, Element e) {
214
        String fqn = getMethodFqn(e);
215
        String sn = e.getSimpleName().toString();
216
        String pkg = info.getElements().getPackageOf(e).getQualifiedName().toString();
217
        for (ImportTree i : info.getCompilationUnit().getImports()) {
218
            if (!i.isStatic()) {
219
                continue;
220
            }
221
            String q = i.getQualifiedIdentifier().toString();
222
            if (q.endsWith(".*")) { //NOI18N
223
                TypeElement ie = info.getElements().getTypeElement(q.substring(0, q.length() - 2));
224
                if (ie == null) {
225
                    continue;
226
                }
227
                for (Element enclosed : ie.getEnclosedElements()) {
228
                    Set<Modifier> modifiers = enclosed.getModifiers();
229
                    if (enclosed.getKind() != ElementKind.METHOD || !modifiers.contains(Modifier.STATIC) || modifiers.contains(Modifier.PRIVATE)) {
230
                        continue;
231
                    }
232
                    String pkg1 = info.getElements().getPackageOf(enclosed).getQualifiedName().toString();
233
                    if (!pkg.endsWith(pkg1) && !modifiers.contains(Modifier.PUBLIC)) {
234
                        continue;
235
                    }
236
                    String sn1 = enclosed.getSimpleName().toString();
237
                    String fqn1 = getMethodFqn(enclosed);
238
                    if (sn.equals(sn1) && !fqn.equals(fqn1)) {
239
                        return true;
240
                    }
241
                }
242
            } else {
243
                int endIndex = q.lastIndexOf("."); //NOI18N
244
                if (endIndex == -1 || endIndex == q.length()) {
245
                    continue;
246
                }
247
                String fqn1 = q.substring(endIndex + 1);
248
                if (q.substring(endIndex).equals(sn) && !fqn.equals(fqn1)) {
249
                    return true;
250
                }
251
            }
252
        }
253
        return false;
254
    }
255
256
    // XXX can't use JavaFixAllImports.addImports because no support for static imports
257
    public static CompilationUnitTree addStaticImports(CompilationUnitTree cut, Collection<String> toImport, TreeMaker make) {
258
        List<ImportTree> imports = new ArrayList<ImportTree>(cut.getImports());
259
        for (String fqn : toImport) {
260
            ImportTree imp = make.Import(make.Identifier(fqn), true);
261
            for (ImportTree i : imports) {
262
                if (!i.isStatic()) {
263
                    continue;
264
                }
265
                String iS = i.getQualifiedIdentifier().toString();
266
                if (fqn.equals(iS) || (iS.endsWith(".*") && fqn.startsWith(iS.substring(0, iS.length() - 2)))) { // NOI18N
267
                    return cut;
268
                }
269
            }
270
            imports.add(imp); // XXX smart insertion
271
        }
272
        return make.CompilationUnit(cut.getPackageName(), imports, cut.getTypeDecls(), cut.getSourceFile());
273
    }
274
}
(-)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