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.editor/src/org/netbeans/modules/java/editor/imports/JavaFixAllImports.java (-30 / +11 lines)
Lines 51-57 Link Here
51
import java.awt.Toolkit;
51
import java.awt.Toolkit;
52
import java.io.IOException;
52
import java.io.IOException;
53
import java.util.ArrayList;
53
import java.util.ArrayList;
54
import java.util.Collections;
55
import java.util.HashMap;
54
import java.util.HashMap;
56
import java.util.List;
55
import java.util.List;
57
import java.util.Map;
56
import java.util.Map;
Lines 62-67 Link Here
62
import org.netbeans.api.java.source.Task;
61
import org.netbeans.api.java.source.Task;
63
import org.netbeans.api.java.source.JavaSource;
62
import org.netbeans.api.java.source.JavaSource;
64
import org.netbeans.api.java.source.JavaSource.Phase;
63
import org.netbeans.api.java.source.JavaSource.Phase;
64
import org.netbeans.api.java.source.SourceUtils;
65
import org.netbeans.api.java.source.TreeMaker;
65
import org.netbeans.api.java.source.TreeMaker;
66
import org.netbeans.api.java.source.TreePathHandle;
66
import org.netbeans.api.java.source.TreePathHandle;
67
import org.netbeans.api.java.source.WorkingCopy;
67
import org.netbeans.api.java.source.WorkingCopy;
Lines 332-368 Link Here
332
        }
332
        }
333
    }
333
    }
334
334
335
    //XXX: copied from SourceUtils.addImports. Ideally, should be on one place only:
335
    /**
336
     * @param cut
337
     * @param toImport
338
     * @param make
339
     * @return
340
     * @deprecated use {@link SourceUtils#addImports(CompilationUnitTree, List, TreeMaker)}
341
     * @throws IOException a big fat lie! Kept for legacy.
342
     */
343
    @Deprecated
336
    public static CompilationUnitTree addImports(CompilationUnitTree cut, List<String> toImport, TreeMaker make)
344
    public static CompilationUnitTree addImports(CompilationUnitTree cut, List<String> toImport, TreeMaker make)
337
        throws IOException {
345
        throws IOException {
338
        // do not modify the list given by the caller (may be reused or immutable).
346
        return SourceUtils.addImports(cut, toImport, make);
339
        toImport = new ArrayList<String>(toImport); 
340
        Collections.sort(toImport);
341
342
        List<ImportTree> imports = new ArrayList<ImportTree>(cut.getImports());
343
        int currentToImport = toImport.size() - 1;
344
        int currentExisting = imports.size() - 1;
345
        
346
        while (currentToImport >= 0 && currentExisting >= 0) {
347
            String currentToImportText = toImport.get(currentToImport);
348
            
349
            while (currentExisting >= 0 && (imports.get(currentExisting).isStatic() || imports.get(currentExisting).getQualifiedIdentifier().toString().compareTo(currentToImportText) > 0))
350
                currentExisting--;
351
            
352
            if (currentExisting >= 0) {
353
                imports.add(currentExisting+1, make.Import(make.Identifier(currentToImportText), false));
354
                currentToImport--;
355
            }
356
        }
357
        // we are at the head of import section and we still have some imports
358
        // to add, put them to the very beginning
359
        while (currentToImport >= 0) {
360
            String importText = toImport.get(currentToImport);
361
            imports.add(0, make.Import(make.Identifier(importText), false));
362
            currentToImport--;
363
        }
364
        // return a copy of the unit with changed imports section
365
        return make.CompilationUnit(cut.getPackageName(), imports, cut.getTypeDecls(), cut.getSourceFile());
366
    }
347
    }
367
    
348
    
368
}
349
}
(-)a/java.hints/src/org/netbeans/modules/java/hints/Bundle.properties (-1 / +7 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 263-268 Link Here
263
ERR_SynchronizationOnNonFinalField=Synchronization on non-final field
263
ERR_SynchronizationOnNonFinalField=Synchronization on non-final field
264
DN_SynchronizationOnNonFinalField=Synchronization on non-final field
264
DN_SynchronizationOnNonFinalField=Synchronization on non-final field
265
265
266
DN_StaticImport=Convert method to static import
267
DSC_StaticImport=Convert method to static import
268
ERR_StaticImport=Convert method to static import
269
HINT_StaticImport=Convert {0} to static import
270
HINT_StaticImport2=Add static import for {0}
271
266
HINT_SuspiciousCall=Suspicious call to {0}:\nExpected type {2}, actual type {1}
272
HINT_SuspiciousCall=Suspicious call to {0}:\nExpected type {2}, actual type {1}
267
HINT_SuspiciousCallIncompatibleTypes=Suspicious call to {0}:\nGiven object cannot contain instances of {1} (expected {2})
273
HINT_SuspiciousCallIncompatibleTypes=Suspicious call to {0}:\nGiven object cannot contain instances of {1} (expected {2})
268
DN_CollectionRemove=Suspicous method call
274
DN_CollectionRemove=Suspicous method call
(-)0df446056874 (+373 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.ImportTree;
32
import com.sun.source.tree.Tree.Kind;
33
import com.sun.source.util.TreePath;
34
import java.util.Collections;
35
import java.util.EnumSet;
36
import java.util.List;
37
import java.util.Set;
38
import java.util.concurrent.atomic.AtomicBoolean;
39
import javax.lang.model.element.Element;
40
import javax.lang.model.element.ElementKind;
41
import javax.lang.model.element.Modifier;
42
import javax.lang.model.element.TypeElement;
43
import javax.lang.model.type.TypeMirror;
44
import javax.lang.model.util.Types;
45
import org.netbeans.api.java.queries.SourceLevelQuery;
46
import org.netbeans.api.java.source.CompilationInfo;
47
import org.netbeans.api.java.source.ElementUtilities;
48
import org.netbeans.api.java.source.JavaSource;
49
import org.netbeans.api.java.source.JavaSource.Phase;
50
import org.netbeans.api.java.source.SourceUtils;
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
 * <p>
67
 * Future versions might support other member types.
68
 *
69
 * @author Sam Halliday
70
 * @see <a href="http://www.netbeans.org/issues/show_bug.cgi?id=89258">RFE 89258</a>
71
 * @see <a href="http://java.sun.com/j2se/1.5.0/docs/guide/language/static-import.html>Static Imports</a>
72
 */
73
public class StaticImport extends AbstractHint {
74
75
    private final AtomicBoolean cancel = new AtomicBoolean();
76
77
    public StaticImport() {
78
        super(true, false, HintSeverity.CURRENT_LINE_WARNING);
79
    }
80
81
    @Override
82
    public String getDescription() {
83
        return NbBundle.getMessage(StaticImport.class, "DSC_StaticImport");
84
    }
85
86
    public Set<Kind> getTreeKinds() {
87
        return EnumSet.of(Kind.MEMBER_SELECT);
88
    }
89
90
    public List<ErrorDescription> run(CompilationInfo info, TreePath treePath) {
91
        if (treePath == null || treePath.getLeaf().getKind() != Kind.MEMBER_SELECT) {
92
            return null;
93
        }
94
        cancel.set(false);
95
        TreePath mitp = treePath.getParentPath();
96
        if (mitp == null || mitp.getLeaf().getKind() != Kind.METHOD_INVOCATION) {
97
            return null;
98
        }
99
        Element e = info.getTrees().getElement(treePath);
100
        if (e == null || !e.getModifiers().contains(Modifier.STATIC)) {
101
            return null;
102
        }
103
        if (!supportsStaticImports(info)) {
104
            return null;
105
        }
106
        Element enclosingEl = e.getEnclosingElement();
107
        if (enclosingEl == null) {
108
            return null;
109
        }
110
        String sn = e.getSimpleName().toString();
111
        if (!isValidStaticMethod(info, getElementName(enclosingEl, true).toString(), sn)) {
112
            return null;
113
        }
114
        Element klass = info.getTrees().getElement(getContainingClass(treePath));
115
        String fqn = null;
116
        String fqn1 = getMethodFqn(e);
117
        if (!isSubTypeOrInnerOfSubType(info, klass, enclosingEl) && !isStaticallyImported(info, fqn1)) {
118
            if (hasMethodNameClash(info, klass, sn) || hasStaticImportSimpleNameClash(info, sn)) {
119
                return null;
120
            }
121
            fqn = fqn1;
122
        }
123
        List<Fix> fixes = Collections.<Fix>singletonList(new FixImpl(TreePathHandle.create(treePath, info), fqn, sn));
124
        String desc = NbBundle.getMessage(AddOverrideAnnotation.class, "ERR_StaticImport");
125
        int start = (int) info.getTrees().getSourcePositions().getStartPosition(info.getCompilationUnit(), treePath.getLeaf());
126
        int end = (int) info.getTrees().getSourcePositions().getEndPosition(info.getCompilationUnit(), treePath.getLeaf());
127
        ErrorDescription ed = ErrorDescriptionFactory.createErrorDescription(getSeverity().toEditorSeverity(), desc, fixes, info.getFileObject(), start, end);
128
        if (cancel.get()) {
129
            return null;
130
        }
131
        return Collections.singletonList(ed);
132
    }
133
134
    public String getId() {
135
        return StaticImport.class.getName();
136
    }
137
138
    public String getDisplayName() {
139
        return NbBundle.getMessage(StaticImport.class, "DN_StaticImport");
140
    }
141
142
    public void cancel() {
143
        cancel.set(true);
144
    }
145
146
    /**
147
     * @param info
148
     * @return true if the source level supports the static import language feature
149
     */
150
    public static boolean supportsStaticImports(CompilationInfo info) {
151
        String level = SourceLevelQuery.getSourceLevel(info.getFileObject());
152
        if (level == null) {
153
            return false;
154
        }
155
        try {
156
            double dLevel = Double.valueOf(level);
157
            if (dLevel < 1.5) {
158
                return false;
159
            }
160
            return true;
161
        } catch (NumberFormatException e) {
162
            return false;
163
        }
164
    }
165
166
    public static final class FixImpl implements Fix, Task<WorkingCopy> {
167
168
        private final TreePathHandle handle;
169
        private final String fqn;
170
        private final String sn;
171
172
        /**
173
         * @param handle to the MEMBER_SELECT
174
         * @param fqn to static import, or null to not perform any imports
175
         * @param sn simple name
176
         */
177
        public FixImpl(TreePathHandle handle, String fqn, String sn) {
178
            this.handle = handle;
179
            this.fqn = fqn;
180
            this.sn = sn;
181
        }
182
183
        public String getText() {
184
            if (fqn == null) {
185
                return NbBundle.getMessage(StaticImport.class, "HINT_StaticImport", sn);
186
            } else {
187
                return NbBundle.getMessage(StaticImport.class, "HINT_StaticImport2", fqn);
188
            }
189
        }
190
191
        public ChangeInfo implement() throws Exception {
192
            JavaSource js = JavaSource.forFileObject(handle.getFileObject());
193
            js.runModificationTask(this).commit();
194
            return null;
195
        }
196
197
        public void run(WorkingCopy copy) throws Exception {
198
            if (copy.toPhase(Phase.RESOLVED).compareTo(Phase.RESOLVED) < 0) {
199
                return;
200
            }
201
            TreePath treePath = handle.resolve(copy);
202
            if (treePath == null || treePath.getLeaf().getKind() != Kind.MEMBER_SELECT) {
203
                return;
204
            }
205
            TreePath mitp = treePath.getParentPath();
206
            if (mitp == null || mitp.getLeaf().getKind() != Kind.METHOD_INVOCATION) {
207
                return;
208
            }
209
            Element e = copy.getTrees().getElement(treePath);
210
            if (e == null || !e.getModifiers().contains(Modifier.STATIC)) {
211
                return;
212
            }
213
            TreeMaker make = copy.getTreeMaker();
214
            copy.rewrite(treePath.getLeaf(), make.Identifier(sn));
215
            if (fqn == null) {
216
                return;
217
            }
218
            CompilationUnitTree cut = copy.getCompilationUnit();
219
            CompilationUnitTree nue = SourceUtils.addStaticImports(cut, Collections.singletonList(fqn), make);
220
            copy.rewrite(cut, nue);
221
        }
222
    }
223
224
    // returns true if a METHOD is enclosed in element with simple name sn
225
    private static boolean hasMethodWithSimpleName(CompilationInfo info, Element element, final String sn) {
226
        Iterable<? extends Element> members =
227
                info.getElementUtilities().getMembers(element.asType(), new ElementUtilities.ElementAcceptor() {
228
229
            public boolean accept(Element e, TypeMirror type) {
230
                if (e.getKind() == ElementKind.METHOD && e.getSimpleName().toString().equals(sn)) {
231
                    return true;
232
                }
233
                return false;
234
            }
235
        });
236
        return members.iterator().hasNext();
237
    }
238
239
    /**
240
     * @param info
241
     * @param simpleName of static method.
242
     * @return true if a static import exists with the same simple name.
243
     * Caveat, expect false positives on protected and default visibility methods from wildcard static imports.
244
     */
245
    public static boolean hasStaticImportSimpleNameClash(CompilationInfo info, String simpleName) {
246
        for (ImportTree i : info.getCompilationUnit().getImports()) {
247
            if (!i.isStatic()) {
248
                continue;
249
            }
250
            String q = i.getQualifiedIdentifier().toString();
251
            if (q.endsWith(".*")) { //NOI18N
252
                TypeElement ie = info.getElements().getTypeElement(q.substring(0, q.length() - 2));
253
                if (ie == null) {
254
                    continue;
255
                }
256
                for (Element enclosed : ie.getEnclosedElements()) {
257
                    Set<Modifier> modifiers = enclosed.getModifiers();
258
                    if (enclosed.getKind() != ElementKind.METHOD || !modifiers.contains(Modifier.STATIC) || modifiers.contains(Modifier.PRIVATE)) {
259
                        continue;
260
                    }
261
                    String sn1 = enclosed.getSimpleName().toString();
262
                    if (simpleName.equals(sn1)) {
263
                        return true;
264
                    }
265
                }
266
            } else {
267
                int endIndex = q.lastIndexOf("."); //NOI18N
268
                if (endIndex == -1 || endIndex >= q.length() - 1) {
269
                    continue;
270
                }
271
                if (q.substring(endIndex).equals(simpleName)) {
272
                    return true;
273
                }
274
            }
275
        }
276
        return false;
277
    }
278
279
    /**
280
     * @param info
281
     * @param t1
282
     * @param t3
283
     * @return true iff the first type (or its containing class in the case of inner classes)
284
     * is a subtype of the second.
285
     * @see Types#isSubtype(javax.lang.model.type.TypeMirror, javax.lang.model.type.TypeMirror)
286
     */
287
    private static boolean isSubTypeOrInnerOfSubType(CompilationInfo info, Element t1, Element t2) {
288
        boolean isSubtype = info.getTypes().isSubtype(t1.asType(), t2.asType());
289
        boolean isInnerClass = t1.getEnclosingElement().getKind() == ElementKind.CLASS;
290
        return isSubtype || (isInnerClass && info.getTypes().isSubtype(t1.getEnclosingElement().asType(), t2.asType()));
291
    }
292
293
    /**
294
     * @param info
295
     * @param klass the element for a CLASS
296
     * @param member the STATIC, MEMBER_SELECT Element for a MethodInvocationTree
297
     * @return true if member has a simple name which would clash with local or inherited
298
     * methods in klass (which may be an inner or static class).
299
     */
300
    public static boolean hasMethodNameClash(CompilationInfo info, Element klass, String simpleName) {
301
        assert klass != null;
302
        assert klass.getKind() == ElementKind.CLASS;
303
304
        // check the members and inherited members of the klass
305
        if (hasMethodWithSimpleName(info, klass, simpleName)) {
306
            return true;
307
        }
308
        Element klassEnclosing = klass.getEnclosingElement();
309
        return (klassEnclosing != null && klassEnclosing.getKind() == ElementKind.CLASS && hasMethodWithSimpleName(info, klassEnclosing, simpleName));
310
    }
311
312
    /**
313
     * @param e
314
     * @return the FQN for a METHOD Element
315
     */
316
    public static String getMethodFqn(Element e) {
317
        // XXX or alternatively, upgrade getElementName to handle METHOD
318
        assert e.getKind() == ElementKind.METHOD;
319
        return getElementName(e.getEnclosingElement(), true) + "." + e.getSimpleName();
320
    }
321
322
    /**
323
     * @param tp
324
     * @return the first path which is a CLASS or null if none found
325
     */
326
    public static TreePath getContainingClass(TreePath tp) {
327
        while (tp != null && tp.getLeaf().getKind() != Kind.CLASS) {
328
            tp = tp.getParentPath();
329
        }
330
        return tp;
331
    }
332
333
    // return true if the fqn already has a static import
334
    private static boolean isStaticallyImported(CompilationInfo info, String fqn) {
335
        for (ImportTree i : info.getCompilationUnit().getImports()) {
336
            if (!i.isStatic()) {
337
                continue;
338
            }
339
            String q = i.getQualifiedIdentifier().toString();
340
            if (q.endsWith(".*") && fqn.startsWith(q.substring(0, q.length() - 1))) { //NOI18N
341
                return true;
342
            }
343
            if (q.equals(fqn)) {
344
                return true;
345
            }
346
        }
347
        return false;
348
    }
349
350
    /**
351
     * @param info
352
     * @param fqn of the containing class
353
     * @param simpleName of the method
354
     * @return true if {@code fqn.simpleName} represents a valid static method
355
     */
356
    public static boolean isValidStaticMethod(CompilationInfo info, String fqn, String simpleName) {
357
        TypeElement ie = info.getElements().getTypeElement(fqn);
358
        if (ie == null) {
359
            return false;
360
        }
361
        for (Element enclosed : ie.getEnclosedElements()) {
362
            Set<Modifier> modifiers = enclosed.getModifiers();
363
            if (enclosed.getKind() != ElementKind.METHOD || !modifiers.contains(Modifier.STATIC) || modifiers.contains(Modifier.PRIVATE)) {
364
                continue;
365
            }
366
            String sn1 = enclosed.getSimpleName().toString();
367
            if (simpleName.equals(sn1)) {
368
                return true;
369
            }
370
        }
371
        return false;
372
    }
373
}
(-)a/java.hints/src/org/netbeans/modules/java/hints/errors/ImportClass.java (-2 / +33 lines)
Lines 43-48 Link Here
43
43
44
import com.sun.source.tree.CompilationUnitTree;
44
import com.sun.source.tree.CompilationUnitTree;
45
import com.sun.source.tree.ImportTree;
45
import com.sun.source.tree.ImportTree;
46
import com.sun.source.tree.MemberSelectTree;
46
import com.sun.source.tree.MethodInvocationTree;
47
import com.sun.source.tree.MethodInvocationTree;
47
import com.sun.source.tree.Tree.Kind;
48
import com.sun.source.tree.Tree.Kind;
48
import com.sun.source.util.TreePath;
49
import com.sun.source.util.TreePath;
Lines 57-72 Link Here
57
import java.util.Set;
58
import java.util.Set;
58
import java.util.logging.Level;
59
import java.util.logging.Level;
59
import java.util.logging.Logger;
60
import java.util.logging.Logger;
61
import javax.lang.model.element.Element;
60
import javax.lang.model.element.TypeElement;
62
import javax.lang.model.element.TypeElement;
61
import org.netbeans.api.java.source.Task;
63
import org.netbeans.api.java.source.Task;
62
import org.netbeans.api.java.source.CompilationInfo;
64
import org.netbeans.api.java.source.CompilationInfo;
63
import org.netbeans.api.java.source.JavaSource;
65
import org.netbeans.api.java.source.JavaSource;
64
import org.netbeans.api.java.source.JavaSource.Phase;
66
import org.netbeans.api.java.source.JavaSource.Phase;
67
import org.netbeans.api.java.source.SourceUtils;
68
import org.netbeans.api.java.source.TreePathHandle;
65
import org.netbeans.api.java.source.WorkingCopy;
69
import org.netbeans.api.java.source.WorkingCopy;
66
import org.netbeans.api.lexer.Token;
70
import org.netbeans.api.lexer.Token;
67
import org.netbeans.modules.editor.java.Utilities;
71
import org.netbeans.modules.editor.java.Utilities;
68
import org.netbeans.modules.java.editor.imports.ComputeImports;
72
import org.netbeans.modules.java.editor.imports.ComputeImports;
69
import org.netbeans.modules.java.editor.imports.JavaFixAllImports;
73
import org.netbeans.modules.java.hints.StaticImport;
70
import org.netbeans.modules.java.hints.errors.ImportClass.ImportCandidatesHolder;
74
import org.netbeans.modules.java.hints.errors.ImportClass.ImportCandidatesHolder;
71
import org.netbeans.modules.java.hints.infrastructure.CreatorBasedLazyFixList;
75
import org.netbeans.modules.java.hints.infrastructure.CreatorBasedLazyFixList;
72
import org.netbeans.modules.java.hints.infrastructure.ErrorHintsProvider;
76
import org.netbeans.modules.java.hints.infrastructure.ErrorHintsProvider;
Lines 164-169 Link Here
164
        List<String> unfiltered = candidates.getB();
168
        List<String> unfiltered = candidates.getB();
165
        List<Fix> fixes = new ArrayList<Fix>();
169
        List<Fix> fixes = new ArrayList<Fix>();
166
        
170
        
171
        String staticSimpleName = null;
172
        TreePath parent = null;
173
        if (path.getLeaf().getKind() == Kind.IDENTIFIER) {
174
            parent = path.getParentPath();
175
            if (parent != null && parent.getLeaf().getKind() == Kind.MEMBER_SELECT) {
176
                TreePath mitPath = parent.getParentPath();
177
                if (mitPath != null && mitPath.getLeaf().getKind() == Kind.METHOD_INVOCATION) {
178
                    MemberSelectTree mst = (MemberSelectTree) parent.getLeaf();
179
                    String sn = mst.getIdentifier().toString();
180
                    if (sn.length() != 0) {
181
                        TreePath klass = StaticImport.getContainingClass(path);
182
                        Element klassElement = info.getTrees().getElement(klass);
183
                        if (StaticImport.supportsStaticImports(info) && !StaticImport.hasMethodNameClash(info, klassElement, sn) && !StaticImport.hasStaticImportSimpleNameClash(info, sn)) {
184
                            staticSimpleName = sn;
185
                        }
186
                    }
187
                }
188
            }
189
        }
190
167
        if (unfiltered != null && filtered != null) {
191
        if (unfiltered != null && filtered != null) {
168
            for (String fqn : unfiltered) {
192
            for (String fqn : unfiltered) {
169
                StringBuilder sort = new StringBuilder();
193
                StringBuilder sort = new StringBuilder();
Lines 186-191 Link Here
186
                sort.append(fqn);
210
                sort.append(fqn);
187
                
211
                
188
                fixes.add(new FixImport(file, fqn, sort.toString(), prefered));
212
                fixes.add(new FixImport(file, fqn, sort.toString(), prefered));
213
214
                if (staticSimpleName != null) {
215
                    String mFqn = fqn + "." + staticSimpleName; //NOI18N
216
                    if (StaticImport.isValidStaticMethod(info, fqn, staticSimpleName)) {
217
                        fixes.add(new StaticImport.FixImpl(TreePathHandle.create(parent, info), mFqn, staticSimpleName));
218
                    }
219
                }
189
            }
220
            }
190
        }
221
        }
191
        
222
        
Lines 340-346 Link Here
340
                            return ;
371
                            return ;
341
                        }
372
                        }
342
                        
373
                        
343
                        CompilationUnitTree cut = JavaFixAllImports.addImports(
374
                        CompilationUnitTree cut = SourceUtils.addImports(
344
                            copy.getCompilationUnit(),
375
                            copy.getCompilationUnit(),
345
                            Collections.singletonList(te.getQualifiedName().toString()),
376
                            Collections.singletonList(te.getQualifiedName().toString()),
346
                            copy.getTreeMaker()
377
                            copy.getTreeMaker()
(-)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
                    <file name="org-netbeans-modules-java-hints-FieldForUnusedParam.instance"/>
143
                    <file name="org-netbeans-modules-java-hints-FieldForUnusedParam.instance"/>
(-)a/java.hints/test/unit/data/goldenfiles/org/netbeans/modules/java/hints/errors/ImportClassTest/testImportHint4-hints.pass (+1 lines)
Lines 1-2 Link Here
1
Add import for java.util.Collections
1
Add import for java.util.Collections
2
Add import for java.util.LinkedList
2
Add import for java.util.LinkedList
3
Add static import for java.util.Collections.emptyList
(-)0df446056874 (+1 lines)
Added Link Here
1
Add import for java.util.Calendar
(-)0df446056874 (+1 lines)
Added Link Here
1
Add import for javax.crypto.KeyAgreement
(-)0df446056874 (+1 lines)
Added Link Here
1
Add import for java.util.Collections
(-)0df446056874 (+2 lines)
Added Link Here
1
Add import for java.util.Collections
2
Add static import for java.util.Collections.emptySet
(-)0df446056874 (+10 lines)
Added Link Here
1
package org.netbeans.test.java.hints;
2
3
import static java.util.Collections.emptySet;
4
5
public class ImportTestStatic4 {
6
7
    public ImportTestStatic4() {
8
        emptySet();
9
    }
10
}
(-)0df446056874 (+11 lines)
Added Link Here
1
package org.netbeans.test.java.hints;
2
3
public class ImportTestStatic1 {
4
5
    public ImportTestStatic1() {
6
        Calendar.getInstance();
7
    }
8
9
    public void getInstance() {
10
    }
11
}
(-)0df446056874 (+11 lines)
Added Link Here
1
package org.netbeans.test.java.hints;
2
3
import static java.util.Calendar.*;
4
5
public class ImportTestStatic2 {
6
7
    public ImportTestStatic2() throws Exception {
8
        getInstance();
9
        KeyAgreement.getInstance("");
10
    }
11
}
(-)0df446056874 (+8 lines)
Added Link Here
1
package org.netbeans.test.java.hints;
2
3
public class ImportTestStatic3 {
4
5
    public ImportTestStatic3() throws Exception {
6
        Collections.doesNotExist();
7
    }
8
}
(-)0df446056874 (+8 lines)
Added Link Here
1
package org.netbeans.test.java.hints;
2
3
public class ImportTestStatic4 {
4
5
    public ImportTestStatic4() {
6
        Collections.emptySet();
7
    }
8
}
(-)0df446056874 (+142 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
package org.netbeans.modules.java.hints;
25
26
import com.sun.source.util.TreePath;
27
import java.util.List;
28
import org.netbeans.api.java.source.CompilationInfo;
29
import org.netbeans.modules.java.hints.errors.ImportClassTest;
30
import org.netbeans.modules.java.hints.infrastructure.TreeRuleTestBase;
31
import org.netbeans.spi.editor.hints.ErrorDescription;
32
import org.netbeans.spi.editor.hints.Fix;
33
import org.openide.util.NbBundle;
34
35
/**
36
 * The following shell script was used to generate the code snippets
37
 *
38
 * <code>cat test/unit/data/test/Test.java | tr '\n' ' ' | tr '\t' ' ' | sed -E 's| +| |g' | sed 's|"|\\"|g'</code>
39
 *
40
 * but it will break if the source includes single-line comments or strange characters in Strings.
41
 *
42
 * @author Samuel Halliday
43
 * @see ImportClassTest
44
 */
45
public class StaticImportTest extends TreeRuleTestBase {
46
47
    private final StaticImport computer = new StaticImport();
48
49
    public StaticImportTest(String name) {
50
        super(name);
51
    }
52
53
    public void testStaticImportHint1() throws Exception {
54
        String test = "package test; public class Test { public Test() { Math.|abs(1); } }";
55
        String golden = "package test; import static java.lang.Math.abs; public class Test { public Test() { abs(1); } }";
56
        performFixTest(test, golden);
57
    }
58
59
    public void testStaticImportHint2() throws Exception {
60
        String test = "package test; public class Test { public Test() { Test.get|Logger(); } public static void getLogger() { } }";
61
        String golden = "package test; public class Test { public Test() { getLogger(); } public static void getLogger() { } }";
62
        performFixTest(test, golden);
63
    }
64
65
    public void testStaticImportHint3() throws Exception {
66
        String test = "package test; public class Test extends Foo { public Test() { Foo.f|oo(); } } class Foo { static protected void foo() { } }";
67
        String golden = "package test; public class Test extends Foo { public Test() { foo(); } } class Foo { static protected void foo() { } }";
68
        performFixTest(test, golden);
69
    }
70
71
    public void testStaticImportHint4() throws Exception {
72
        String test = "package test; import java.util.Calendar; import static java.util.Calendar.*; public class Test { public Test() { Calendar.getInstance|(); } }";
73
        String golden = "package test; import java.util.Calendar; import static java.util.Calendar.*; public class Test { public Test() { getInstance(); } }";
74
        performFixTest(test, golden);
75
    }
76
77
    public void testStaticImportHint5() throws Exception {
78
        String test = "package test; import java.util.logging.Logger; public class Test { public Test() { Logger.getLogger|(\"\"); } public static void getLogger() { } }";
79
        performAnalysisTest(test);
80
    }
81
82
    public void testStaticImportHint6() throws Exception {
83
        String test = "package test; public class Test extends Foo { public Test() { Bar.foo|(); } } class Foo { static protected void foo() { } } class Bar { static protected void foo() { } }";
84
        performAnalysisTest(test);
85
    }
86
87
    public void testStaticImportHint7() throws Exception {
88
        String test = "package test; import javax.crypto.KeyAgreement; import static java.util.Calendar.*; public class Test { public Test() throws Exception { KeyAgreement.getInstance|(\"\"); } }";
89
        performAnalysisTest(test);
90
    }
91
92
    public void testStaticImportHint8() throws Exception {
93
        String test = "package test; public class Test extends Foo { class FooBar { FooBar() { Foo.foo|(); } } } class Foo { static protected void foo() { } }";
94
        String golden = "package test; public class Test extends Foo { class FooBar { FooBar() { foo(); } } } class Foo { static protected void foo() { } }";
95
        performFixTest(test, golden);
96
    }
97
98
    public void testStaticImportHint9() throws Exception {
99
        String test = "package test; public class Test extends Foo { class FooBar { FooBar() { Bar.foo|(); } } } class Foo { static protected void foo() { } } class Bar { static protected void foo() { } }";
100
        performAnalysisTest(test);
101
    }
102
103
    // test is single line source code for test.Test, | in the member select, space before
104
    // golden is the output to test against
105
    // sn is the simple name of the static method
106
    private void performFixTest(String test, String golden) throws Exception {
107
        int offset = test.indexOf("|");
108
        assertTrue(offset != -1);
109
        int end = test.indexOf("(", offset) - 1;
110
        assertTrue(end > 0);
111
        int start = test.lastIndexOf(" ", offset) + 1;
112
        assertTrue(start > 0);
113
        performFixTest("test/Test.java",
114
                test.replace("|", ""),
115
                offset,
116
                "0:" + start + "-0:" + end + ":hint:" + NbBundle.getMessage(StaticImport.class, "DSC_StaticImport"),
117
                StaticImport.class.getSimpleName(),
118
                golden);
119
    }
120
121
    // test is single line source code for test.Test, | in the member select, space before
122
    // completes successfully if there are no hints presented
123
    private void performAnalysisTest(String test) throws Exception {
124
        int offset = test.indexOf("|");
125
        assertTrue(offset != -1);
126
        performAnalysisTest("test/Test.java", test.replace("|", ""), offset);
127
    }
128
129
    @Override
130
    protected List<ErrorDescription> computeErrors(CompilationInfo info, TreePath path) {
131
        return computer.run(info, path);
132
    }
133
134
    @Override
135
    protected String toDebugString(CompilationInfo info, Fix f) {
136
        if (f instanceof StaticImport.FixImpl) {
137
            return StaticImport.class.getSimpleName();
138
        } else {
139
            return super.toDebugString(info, f);
140
        }
141
    }
142
}
(-)a/java.hints/test/unit/src/org/netbeans/modules/java/hints/errors/ImportClassTest.java (-1 / +17 lines)
Lines 74-80 Link Here
74
    }
74
    }
75
    
75
    
76
    public void testImportHint4() throws Exception {
76
    public void testImportHint4() throws Exception {
77
        performTest("ImportTest4", "java.util.Collections", 7, 13);
77
        performTest("ImportTest4", "Add import for java.util.Collections", 7, 13);
78
    }
78
    }
79
    
79
    
80
    public void testImportHint5() throws Exception {
80
    public void testImportHint5() throws Exception {
Lines 85-90 Link Here
85
        performTest("ImportTest6", "java.util.Collections", 7, 13);
85
        performTest("ImportTest6", "java.util.Collections", 7, 13);
86
    }
86
    }
87
    
87
    
88
    public void testImportHintStatic1() throws Exception {
89
        performTestDoNotPerform("ImportTestStatic1", 6, 13);
90
    }
91
92
    public void testImportHintStatic2() throws Exception {
93
        performTestDoNotPerform("ImportTestStatic2", 9, 15);
94
    }
95
96
    public void testImportHintStatic3() throws Exception {
97
        performTestDoNotPerform("ImportTestStatic3", 6, 14);
98
    }
99
100
    public void testImportHintStatic4() throws Exception {
101
        performTest("ImportTestStatic4", "static import", 6, 15);
102
    }
103
88
    public void testImportHintDoNotPropose() throws Exception {
104
    public void testImportHintDoNotPropose() throws Exception {
89
        performTestDoNotPerform("ImportHintDoNotPropose", 10, 24);
105
        performTestDoNotPerform("ImportHintDoNotPropose", 10, 24);
90
        performTestDoNotPerform("ImportHintDoNotPropose", 11, 24);
106
        performTestDoNotPerform("ImportHintDoNotPropose", 11, 24);
(-)a/java.source/src/org/netbeans/api/java/source/SourceUtils.java (-7 / +27 lines)
Lines 300-310 Link Here
300
    }
300
    }
301
    
301
    
302
    /**
302
    /**
303
     *
303
     * @param cut
304
     *
304
     * @param toImport
305
     * @param make
306
     * @return
307
     * @see #addStaticImports
305
     */
308
     */
306
    private static CompilationUnitTree addImports(CompilationUnitTree cut, List<String> toImport, TreeMaker make)
309
    public static CompilationUnitTree addImports(CompilationUnitTree cut, List<String> toImport, TreeMaker make) {
307
        throws IOException {
310
        return addImports(cut, toImport, make, false);
311
    }
312
313
    /**
314
     * @param cut
315
     * @param toImport
316
     * @param make
317
     * @return
318
     * @see #addImports
319
     */
320
    public static CompilationUnitTree addStaticImports(CompilationUnitTree cut, List<String> toImport, TreeMaker make) {
321
        return addImports(cut, toImport, make, true);
322
    }
323
324
    private static CompilationUnitTree addImports(CompilationUnitTree cut, List<String> toImport, TreeMaker make, boolean doStatic) {
325
        // XXX old (private) version claimed to throw IOException, but it didn't really.
308
        // do not modify the list given by the caller (may be reused or immutable).
326
        // do not modify the list given by the caller (may be reused or immutable).
309
        toImport = new ArrayList<String>(toImport); 
327
        toImport = new ArrayList<String>(toImport); 
310
        Collections.sort(toImport);
328
        Collections.sort(toImport);
Lines 316-326 Link Here
316
        while (currentToImport >= 0 && currentExisting >= 0) {
334
        while (currentToImport >= 0 && currentExisting >= 0) {
317
            String currentToImportText = toImport.get(currentToImport);
335
            String currentToImportText = toImport.get(currentToImport);
318
            
336
            
319
            while (currentExisting >= 0 && (imports.get(currentExisting).isStatic() || imports.get(currentExisting).getQualifiedIdentifier().toString().compareTo(currentToImportText) > 0))
337
            boolean ignore = (doStatic ^ imports.get(currentExisting).isStatic());
338
            while (currentExisting >= 0 && (ignore || imports.get(currentExisting).getQualifiedIdentifier().toString().compareTo(currentToImportText) > 0)) {
320
                currentExisting--;
339
                currentExisting--;
340
            }
321
            
341
            
322
            if (currentExisting >= 0) {
342
            if (currentExisting >= 0) {
323
                imports.add(currentExisting+1, make.Import(make.Identifier(currentToImportText), false));
343
                imports.add(currentExisting+1, make.Import(make.Identifier(currentToImportText), doStatic));
324
                currentToImport--;
344
                currentToImport--;
325
            }
345
            }
326
        }
346
        }
Lines 328-334 Link Here
328
        // to add, put them to the very beginning
348
        // to add, put them to the very beginning
329
        while (currentToImport >= 0) {
349
        while (currentToImport >= 0) {
330
            String importText = toImport.get(currentToImport);
350
            String importText = toImport.get(currentToImport);
331
            imports.add(0, make.Import(make.Identifier(importText), false));
351
            imports.add(0, make.Import(make.Identifier(importText), doStatic));
332
            currentToImport--;
352
            currentToImport--;
333
        }
353
        }
334
        // return a copy of the unit with changed imports section
354
        // return a copy of the unit with changed imports section

Return to bug 89258