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 212656
Collapse All | Expand All

(-)a/java.hints/nbproject/project.xml (+8 lines)
Lines 112-117 Link Here
112
                    </run-dependency>
112
                    </run-dependency>
113
                </dependency>
113
                </dependency>
114
                <dependency>
114
                <dependency>
115
                    <code-name-base>org.netbeans.modules.apisupport.ant</code-name-base>
116
                    <build-prerequisite/>
117
                    <compile-dependency/>
118
                    <run-dependency>
119
                        <specification-version>2.53</specification-version>
120
                    </run-dependency>
121
                </dependency>
122
                <dependency>
115
                    <code-name-base>org.netbeans.modules.code.analysis</code-name-base>
123
                    <code-name-base>org.netbeans.modules.code.analysis</code-name-base>
116
                    <build-prerequisite/>
124
                    <build-prerequisite/>
117
                    <compile-dependency/>
125
                    <compile-dependency/>
(-)a/java.hints/src/org/netbeans/modules/java/hints/errors/Bundle.properties (+4 lines)
Lines 195-197 Link Here
195
FIX_ChangeMethodReturnType=Change method return type to {0}
195
FIX_ChangeMethodReturnType=Change method return type to {0}
196
DN_MissingReturnStatement=Handling of Missing Return Statement compiler error
196
DN_MissingReturnStatement=Handling of Missing Return Statement compiler error
197
FIX_AddReturnStatement=Add return statement
197
FIX_AddReturnStatement=Add return statement
198
199
LBL_Module_Dependency_Search_DisplayName=Add Module Dependency
200
FIX_Module_Dependency_Search=Search Module Dependency for {0}
201
FIX_Module_Dependency_UpdatingDependencies=Update dependencies
(-)a/java.hints/src/org/netbeans/modules/java/hints/errors/SearchModuleDependency.java (+405 lines)
Line 0 Link Here
1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
4
 * Copyright 2010 Oracle and/or its affiliates. All rights reserved.
5
 *
6
 * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
7
 * Other names may be trademarks of their respective owners.
8
 *
9
 * The contents of this file are subject to the terms of either the GNU
10
 * General Public License Version 2 only ("GPL") or the Common
11
 * Development and Distribution License("CDDL") (collectively, the
12
 * "License"). You may not use this file except in compliance with the
13
 * License. You can obtain a copy of the License at
14
 * http://www.netbeans.org/cddl-gplv2.html
15
 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
16
 * specific language governing permissions and limitations under the
17
 * License.  When distributing the software, include this License Header
18
 * Notice in each file and include the License file at
19
 * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
20
 * particular file as subject to the "Classpath" exception as provided
21
 * by Oracle in the GPL Version 2 section of the License file that
22
 * accompanied this code. If applicable, add the following below the
23
 * License Header, with the fields enclosed by brackets [] replaced by
24
 * your own identifying information:
25
 * "Portions Copyrighted [year] [name of copyright owner]"
26
 *
27
 * If you wish your version of this file to be governed by only the CDDL
28
 * or only the GPL Version 2, indicate your decision by adding
29
 * "[Contributor] elects to include this software in this distribution
30
 * under the [CDDL or GPL Version 2] license." If you do not indicate a
31
 * single choice of license, a recipient has the option to distribute
32
 * your version of this file under either the CDDL, the GPL Version 2 or
33
 * to extend the choice of license to its licensees as provided above.
34
 * However, if you add GPL Version 2 code and therefore, elected the GPL
35
 * Version 2 license, then the option applies only if the new code is
36
 * made subject to such option by the copyright holder.
37
 *
38
 * Contributor(s): 
39
 *      theanuradha@netbeans.org
40
 *
41
 * Portions Copyrighted 2008 Sun Microsystems, Inc.
42
 * Portions Copyrighted 2012 markiewb@netbeans.org
43
 */
44
package org.netbeans.modules.java.hints.errors;
45
46
import com.sun.source.tree.ArrayTypeTree;
47
import com.sun.source.tree.ExpressionTree;
48
import com.sun.source.tree.IdentifierTree;
49
import com.sun.source.tree.MethodInvocationTree;
50
import com.sun.source.tree.NewArrayTree;
51
import com.sun.source.tree.NewClassTree;
52
import com.sun.source.tree.ParameterizedTypeTree;
53
import com.sun.source.tree.Tree;
54
import com.sun.source.tree.Tree.Kind;
55
import com.sun.source.tree.VariableTree;
56
import com.sun.source.util.TreePath;
57
import java.io.IOException;
58
import java.text.MessageFormat;
59
import java.util.ArrayList;
60
import java.util.Arrays;
61
import java.util.Collections;
62
import java.util.HashSet;
63
import java.util.List;
64
import java.util.Set;
65
import java.util.concurrent.atomic.AtomicBoolean;
66
import javax.lang.model.element.Name;
67
import org.netbeans.api.java.lexer.JavaTokenId;
68
import org.netbeans.api.java.source.CompilationInfo;
69
import org.netbeans.api.lexer.Token;
70
import org.netbeans.api.lexer.TokenHierarchy;
71
import org.netbeans.api.lexer.TokenSequence;
72
import org.netbeans.api.progress.ProgressUtils;
73
import org.netbeans.api.project.FileOwnerQuery;
74
import org.netbeans.api.project.Project;
75
import org.netbeans.api.project.ProjectManager;
76
import org.netbeans.modules.apisupport.project.ModuleDependency;
77
import org.netbeans.modules.apisupport.project.NbModuleProject;
78
import org.netbeans.modules.apisupport.project.ProjectXMLManager;
79
import org.netbeans.modules.apisupport.project.ui.customizer.AddModulePanel;
80
import org.netbeans.modules.apisupport.project.ui.customizer.SingleModuleProperties;
81
import org.netbeans.modules.java.hints.spi.ErrorRule.Data;
82
import org.netbeans.spi.editor.hints.ChangeInfo;
83
import org.netbeans.spi.editor.hints.EnhancedFix;
84
import org.netbeans.spi.editor.hints.Fix;
85
import org.openide.DialogDisplayer;
86
import org.openide.NotifyDescriptor;
87
import org.openide.filesystems.FileObject;
88
import org.openide.util.Exceptions;
89
import org.openide.util.NbBundle;
90
import org.openide.util.NbBundle.Messages;
91
import static org.netbeans.modules.java.hints.errors.Bundle.*;
92
93
/**
94
 * Fixable hint for an unresolved class which opens the NetBeans plattform
95
 * module dependency dialog. The dialog will be prefilled with the name of the
96
 * unresolved class.
97
 *
98
 * <ul> <li>Hint activation code taken from
99
 * {@link org.netbeans.modules.maven.hints.errors.SearchClassDependencyInRepo}</li>
100
 * <li>Dialog opening code taken from
101
 * {@link org.netbeans.modules.apisupport.project.ui.LibrariesNode.AddModuleDependencyAction}</li>
102
 * </ul>
103
 *
104
 * @author Anuradha G
105
 * @author markiewb
106
 */
107
public class SearchModuleDependency implements org.netbeans.modules.java.hints.spi.ErrorRule<Void> {
108
109
    private AtomicBoolean cancel = new AtomicBoolean(false);
110
111
    public SearchModuleDependency() {
112
    }
113
114
    @Override
115
    public Set<String> getCodes() {
116
        return new HashSet<String>(Arrays.asList(
117
                "compiler.err.cant.resolve",//NOI18N
118
                "compiler.err.cant.resolve.location",//NOI18N
119
                "compiler.err.doesnt.exist",//NOI18N
120
                "compiler.err.not.stmt"));//NOI18N
121
122
    }
123
124
    private boolean isHintEnabled() {
125
        //TODO provide an option to disable this hint 
126
        return true;
127
    }
128
129
    @Override
130
    public List<Fix> run(final CompilationInfo info, String diagnosticKey,
131
            final int offset, TreePath treePath, Data<Void> data) {
132
        cancel.set(false);
133
        if (!isHintEnabled()) {
134
            return Collections.emptyList();
135
        }
136
        //copyed from ImportClass
137
        int errorPosition = offset + 1; //TODO: +1 required to work OK, rethink
138
139
        if (errorPosition == (-1)) {
140
141
            return Collections.<Fix>emptyList();
142
        }
143
        //copyed from ImportClass-end
144
        FileObject fileObject = info.getFileObject();
145
        Project project = FileOwnerQuery.getOwner(fileObject);
146
        if (project == null) {
147
            return Collections.emptyList();
148
        }
149
        NbModuleProject nbModuleProject = project.getLookup().lookup(NbModuleProject.class);
150
151
152
        //copyed from ImportClass
153
        TreePath path = info.getTreeUtilities().pathFor(errorPosition);
154
        if (path.getParentPath() == null) {
155
            return Collections.emptyList();
156
        }
157
158
        Tree leaf = path.getParentPath().getLeaf();
159
160
        switch (leaf.getKind()) {
161
            case METHOD_INVOCATION: {
162
                MethodInvocationTree mit = (MethodInvocationTree) leaf;
163
164
                if (!mit.getTypeArguments().contains(path.getLeaf())) {
165
                    return Collections.<Fix>emptyList();
166
                }
167
            }
168
            //genaric handling
169
170
            case PARAMETERIZED_TYPE: {
171
                leaf = path.getParentPath().getParentPath().getLeaf();
172
            }
173
            break;
174
            case ARRAY_TYPE: {
175
                leaf = path.getParentPath().getParentPath().getLeaf();
176
            }
177
            break;
178
        }
179
        switch (leaf.getKind()) {
180
            case VARIABLE: {
181
                Name typeName = null;
182
                VariableTree variableTree = (VariableTree) leaf;
183
                if (variableTree.getType() != null) {
184
                    switch (variableTree.getType().getKind()) {
185
                        case IDENTIFIER: {
186
                            typeName = ((IdentifierTree) variableTree.getType()).getName();
187
                        }
188
                        break;
189
                        case PARAMETERIZED_TYPE: {
190
                            ParameterizedTypeTree ptt = ((ParameterizedTypeTree) variableTree.getType());
191
                            if (ptt.getType() != null && ptt.getType().getKind() == Kind.IDENTIFIER) {
192
                                typeName = ((IdentifierTree) ptt.getType()).getName();
193
                            }
194
                        }
195
                        break;
196
                        case ARRAY_TYPE: {
197
                            ArrayTypeTree ptt = ((ArrayTypeTree) variableTree.getType());
198
                            if (ptt.getType() != null && ptt.getType().getKind() == Kind.IDENTIFIER) {
199
                                typeName = ((IdentifierTree) ptt.getType()).getName();
200
                            }
201
                        }
202
                        break;
203
204
                    }
205
                }
206
207
                ExpressionTree initializer = variableTree.getInitializer();
208
                if (typeName != null && initializer != null) {
209
210
                    Name itName = null;
211
                    switch (initializer.getKind()) {
212
                        case NEW_CLASS: {
213
                            ExpressionTree identifier;
214
                            NewClassTree classTree = (NewClassTree) initializer;
215
                            identifier = classTree.getIdentifier();
216
217
                            if (identifier != null) {
218
219
                                switch (identifier.getKind()) {
220
                                    case IDENTIFIER:
221
                                        itName = ((IdentifierTree) identifier).getName();
222
                                        break;
223
                                    case PARAMETERIZED_TYPE: {
224
225
                                        ParameterizedTypeTree ptt = ((ParameterizedTypeTree) identifier);
226
                                        if (ptt.getType() != null && ptt.getType().getKind() == Kind.IDENTIFIER) {
227
                                            itName = ((IdentifierTree) ptt.getType()).getName();
228
                                        }
229
                                    }
230
                                    break;
231
                                }
232
                            }
233
                        }
234
                        break;
235
                        case NEW_ARRAY: {
236
                            NewArrayTree arrayTree = (NewArrayTree) initializer;
237
                            Tree type = arrayTree.getType();
238
                            if (type != null) {
239
                                if (type.getKind().equals(Kind.IDENTIFIER)) {
240
                                    itName = ((IdentifierTree) type).getName();
241
                                }
242
                            }
243
                        }
244
                        break;
245
                    }
246
247
                    if (typeName.equals(itName)) {
248
                        return Collections.<Fix>emptyList();
249
                    }
250
                }
251
            }
252
            break;
253
254
        }
255
256
        String simpleOrQualifiedName = null;
257
258
        // XXX somewhat crude; is there a simpler way?
259
        TreePath p = path;
260
        while (p != null) {
261
            TreePath parent = p.getParentPath();
262
            if (parent == null) {
263
                break;
264
            }
265
            Kind parentKind = parent.getLeaf().getKind();
266
            if (parentKind == Kind.IMPORT) {
267
                simpleOrQualifiedName = p.getLeaf().toString();
268
                break;
269
            } else if (parentKind == Kind.MEMBER_SELECT || parentKind == Kind.IDENTIFIER) {
270
                p = parent;
271
            } else {
272
                break;
273
            }
274
        }
275
276
        if (simpleOrQualifiedName == null) {
277
            try {
278
                Token<?> ident = findUnresolvedElementToken(info, offset);
279
                if (ident == null) {
280
                    return Collections.<Fix>emptyList();
281
                }
282
                simpleOrQualifiedName = ident.text().toString();
283
            } catch (IOException e) {
284
                Exceptions.printStackTrace(e);
285
                return Collections.<Fix>emptyList();
286
            }
287
        }
288
289
        //copyed from ImportClass-end
290
        if (cancel.get()) {
291
            return Collections.<Fix>emptyList();
292
        }
293
        //#212331 star static imports need to be stripped of the .* part. 
294
        if (simpleOrQualifiedName.endsWith(".*")) {
295
            simpleOrQualifiedName = simpleOrQualifiedName.substring(0, simpleOrQualifiedName.length() - ".*".length());
296
        }
297
298
        List<Fix> fixes = new ArrayList<Fix>();
299
        fixes.add(new OpenDependencyDialogFix(nbModuleProject, simpleOrQualifiedName));
300
        return fixes;
301
    }
302
303
    //copyed from ImportClass
304
    private static Token findUnresolvedElementToken(CompilationInfo info, int offset) throws IOException {
305
        TokenHierarchy<?> th = info.getTokenHierarchy();
306
        TokenSequence<JavaTokenId> ts = th.tokenSequence(JavaTokenId.language());
307
308
        if (ts == null) {
309
            return null;
310
        }
311
312
        ts.move(offset);
313
        if (ts.moveNext()) {
314
            Token t = ts.token();
315
316
            if (t.id() == JavaTokenId.DOT) {
317
                ts.moveNext();
318
                t = ts.token();
319
            } else {
320
                if (t.id() == JavaTokenId.LT) {
321
                    ts.moveNext();
322
                    t = ts.token();
323
                } else {
324
                    if (t.id() == JavaTokenId.NEW) {
325
                        boolean cont = ts.moveNext();
326
327
                        while (cont && ts.token().id() == JavaTokenId.WHITESPACE) {
328
                            cont = ts.moveNext();
329
                        }
330
331
                        if (!cont) {
332
                            return null;
333
                        }
334
                        t = ts.token();
335
                    }
336
                }
337
            }
338
339
            if (t.id() == JavaTokenId.IDENTIFIER) {
340
                return ts.offsetToken();
341
            }
342
        }
343
        return null;
344
    }
345
346
    @Override
347
    public String getId() {
348
        return "NBM_MISSING_CLASS";//NOI18N
349
    }
350
351
    @Override
352
    public String getDisplayName() {
353
        return NbBundle.getMessage(SearchModuleDependency.class, "LBL_Module_Dependency_Search_DisplayName");
354
    }
355
356
    @Override
357
    public void cancel() {
358
        //cancel task
359
        cancel.set(true);
360
    }
361
362
    static final class OpenDependencyDialogFix implements EnhancedFix {
363
364
        private NbModuleProject project;
365
        private String clazz;
366
367
        public OpenDependencyDialogFix(NbModuleProject project, String clazz) {
368
            this.project = project;
369
            this.clazz = clazz;
370
        }
371
372
        @Override
373
        public CharSequence getSortText() {
374
            return getText();
375
        }
376
377
        @Override
378
        public String getText() {
379
            return NbBundle.getMessage(OpenDependencyDialogFix.class, "FIX_Module_Dependency_Search", clazz);
380
        }
381
382
        @Override
383
        public ChangeInfo implement() throws Exception {
384
            SingleModuleProperties props = SingleModuleProperties.getInstance(project);
385
            final ModuleDependency[] newDeps = AddModulePanel.selectDependencies(props, clazz);
386
            final AtomicBoolean cancel = new AtomicBoolean();
387
            ProgressUtils.runOffEventDispatchThread(new Runnable() {
388
                public @Override
389
                void run() {
390
                    ProjectXMLManager pxm = new ProjectXMLManager(project);
391
                    try {
392
                        pxm.addDependencies(new HashSet<ModuleDependency>(Arrays.asList(newDeps))); // XXX cannot cancel
393
                        ProjectManager.getDefault().saveProject(project);
394
                    } catch (IOException e) {
395
//                        LOG.log(Level.INFO, "Cannot add selected dependencies: " + Arrays.asList(newDeps), e);
396
                    } catch (ProjectXMLManager.CyclicDependencyException ex) {
397
                        NotifyDescriptor.Message msg = new NotifyDescriptor.Message(ex.getLocalizedMessage(), NotifyDescriptor.WARNING_MESSAGE);
398
                        DialogDisplayer.getDefault().notify(msg);
399
                    }
400
                }
401
            }, NbBundle.getMessage(SearchModuleDependency.class, "FIX_Module_Dependency_UpdatingDependencies"), cancel, false);
402
            return null;
403
        }
404
    }
405
}
(-)a/java.hints/src/org/netbeans/modules/java/hints/resources/layer.xml (+1 lines)
Lines 184-189 Link Here
184
                <file name="org-netbeans-modules-java-hints-errors-VarArgsCast.instance"/>
184
                <file name="org-netbeans-modules-java-hints-errors-VarArgsCast.instance"/>
185
                <file name="org-netbeans-modules-java-hints-errors-ChangeMethodReturnType.instance"/>
185
                <file name="org-netbeans-modules-java-hints-errors-ChangeMethodReturnType.instance"/>
186
                <file name="org-netbeans-modules-java-hints-errors-MissingReturnStatement.instance"/>
186
                <file name="org-netbeans-modules-java-hints-errors-MissingReturnStatement.instance"/>
187
                <file name="org-netbeans-modules-java-hints-errors-SearchModuleDependency.instance"/>
187
                <file name="org-netbeans-modules-java-hints-SerialVersionUID.instance"/>
188
                <file name="org-netbeans-modules-java-hints-SerialVersionUID.instance"/>
188
                <folder name="text">
189
                <folder name="text">
189
                    <folder name="x-jsp">
190
                    <folder name="x-jsp">

Return to bug 212656