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

(-)a/apisupport.refactoring/src/org/netbeans/modules/apisupport/hints/UseNbBundleMessages.java (-157 / +179 lines)
Lines 89-94 Link Here
89
import com.sun.source.tree.Tree.Kind;
89
import com.sun.source.tree.Tree.Kind;
90
import com.sun.source.util.SourcePositions;
90
import com.sun.source.util.SourcePositions;
91
import com.sun.source.util.TreePath;
91
import com.sun.source.util.TreePath;
92
import org.netbeans.api.java.source.TreePathHandle;
92
93
93
public class UseNbBundleMessages extends AbstractHint {
94
public class UseNbBundleMessages extends AbstractHint {
94
95
Lines 225-387 Link Here
225
                return warning(UseNbBundleMessages_no_such_key(key), span, compilationInfo);
226
                return warning(UseNbBundleMessages_no_such_key(key), span, compilationInfo);
226
            }
227
            }
227
        }
228
        }
228
        return Collections.singletonList(ErrorDescriptionFactory.createErrorDescription(getSeverity().toEditorSeverity(), UseNbBundleMessages_error_text(), Collections.<Fix>singletonList(new Fix() {
229
        return Collections.singletonList(ErrorDescriptionFactory.createErrorDescription(getSeverity().toEditorSeverity(), UseNbBundleMessages_error_text(), Collections.<Fix>singletonList(new MyFix(src, key, TreePathHandle.create(treePath, compilationInfo), isAlreadyRegistered, ep, bundleProperties)), compilationInfo.getFileObject(), span[0], span[1]));
229
            public @Override String getText() {
230
    }
230
                return UseNbBundleMessages_displayName();
231
    
232
    private static class MyFix implements  Fix {
233
        
234
        final private FileObject src;
235
        final String key;
236
        final private TreePathHandle treePathHandle;
237
        final boolean isAlreadyRegistered;
238
        final EditableProperties ep;
239
        final FileObject bundleProperties;
240
241
        private MyFix(FileObject src, String key, TreePathHandle treePathHandle, boolean isAlreadyRegistered, EditableProperties ep, FileObject bundleProperties) {
242
            this.src = src;
243
            this.key = key;
244
            this.treePathHandle = treePathHandle;
245
            this.isAlreadyRegistered = isAlreadyRegistered;
246
            this.ep = ep;
247
            this.bundleProperties = bundleProperties;
248
        }
249
        
250
        public @Override String getText() {
251
            return UseNbBundleMessages_displayName();
252
        }
253
        public @Override ChangeInfo implement() throws Exception {
254
            JavaSource js = JavaSource.forFileObject(src);
255
            if (js == null) {
256
                throw new Exception("No source info for " + src);
231
            }
257
            }
232
            public @Override ChangeInfo implement() throws Exception {
258
            js.runModificationTask(new Task<WorkingCopy>() {
233
                JavaSource js = JavaSource.forFileObject(src);
259
                public @Override void run(WorkingCopy wc) throws Exception {
234
                if (js == null) {
260
                    wc.toPhase(JavaSource.Phase.RESOLVED);                    
235
                    throw new Exception("No source info for " + src);
261
                    TreeMaker make = wc.getTreeMaker();
236
                }
262
                    final TreePath treePath = treePathHandle.resolve(wc);
237
                js.runModificationTask(new Task<WorkingCopy>() {
263
                    final MethodInvocationTree mit = treePath.getLeaf().getKind() == Tree.Kind.METHOD_INVOCATION ? (MethodInvocationTree)treePath.getLeaf() : null;
238
                    public @Override void run(WorkingCopy wc) throws Exception {
264
                    if (mit != null) {
239
                        wc.toPhase(JavaSource.Phase.RESOLVED);
265
                        CompilationUnitTree cut = wc.getCompilationUnit();
240
                        TreeMaker make = wc.getTreeMaker();
266
                        boolean imported = false;
241
                        if (mit != null) {
267
                        String importBundleStar = cut.getPackageName() + ".Bundle.*";
242
                            CompilationUnitTree cut = wc.getCompilationUnit();
268
                        for (ImportTree it : cut.getImports()) {
243
                            boolean imported = false;
269
                            if (it.isStatic() && it.getQualifiedIdentifier().toString().equals(importBundleStar)) {
244
                            String importBundleStar = cut.getPackageName() + ".Bundle.*";
270
                                imported = true;
245
                            for (ImportTree it : cut.getImports()) {
271
                                break;
246
                                if (it.isStatic() && it.getQualifiedIdentifier().toString().equals(importBundleStar)) {
247
                                    imported = true;
248
                                    break;
249
                                }
250
                            }
251
                            if (!imported) {
252
                                wc.rewrite(cut, make.addCompUnitImport(cut, make.Import(make.Identifier(importBundleStar), true)));
253
                            }
254
                            List<? extends ExpressionTree> args = mit.getArguments();
255
                            List<? extends ExpressionTree> params;
256
                            if (args.size() == 3 && args.get(2).getKind() == Kind.NEW_ARRAY) {
257
                                params = ((NewArrayTree) args.get(2)).getInitializers();
258
                            } else {
259
                                params = args.subList(2, args.size());
260
                            }
261
                            wc.rewrite(mit, make.MethodInvocation(Collections.<ExpressionTree>emptyList(), make.Identifier(toIdentifier(key)), params));
262
                        } // else annotation value, nothing to change
263
                        if (!isAlreadyRegistered) {
264
                            Tree enclosing = findEnclosingElement(wc, treePath);
265
                            Tree modifiers;
266
                            if (enclosing.getKind() == Kind.METHOD) {
267
                                modifiers = ((MethodTree) enclosing).getModifiers();
268
                            } else if (enclosing.getKind() == Kind.COMPILATION_UNIT) {
269
                                modifiers = enclosing;
270
                            } else {
271
                                modifiers = ((ClassTree) enclosing).getModifiers();
272
                            }
273
                            List<ExpressionTree> lines = new ArrayList<ExpressionTree>();
274
                            for (String comment : ep.getComment(key)) {
275
                                lines.add(make.Literal(comment));
276
                            }
277
                            lines.add(make.Literal(key + '=' + ep.remove(key)));
278
                            wc.rewrite(modifiers, addMessage(wc, modifiers, lines));
279
                        }
280
                        // XXX remove NbBundle import if now unused
281
                    }
282
                    // borrowed from FindBugsHint:
283
                    private Tree addMessage(WorkingCopy wc, /*Modifiers|CompilationUnit*/Tree original, List<ExpressionTree> lines) throws Exception {
284
                        TreeMaker make = wc.getTreeMaker();
285
                        // First try to insert into a value list for an existing annotation:
286
                        List<? extends AnnotationTree> anns;
287
                        if (original.getKind() == Kind.COMPILATION_UNIT) {
288
                            anns = ((CompilationUnitTree) original).getPackageAnnotations();
289
                        } else {
290
                            anns = ((ModifiersTree) original).getAnnotations();
291
                        }
292
                        for (int i = 0; i < anns.size(); i++) {
293
                            AnnotationTree ann = anns.get(i);
294
                            Tree annotationType = ann.getAnnotationType();
295
                            // XXX clumsy and imprecise, but how to find the FQN of the annotation type given a Tree? Want a TypeMirror for it.
296
                            if (annotationType.toString().matches("((org[.]openide[.]util[.])?NbBundle[.])?Messages")) {
297
                                List<? extends ExpressionTree> args = ann.getArguments();
298
                                if (args.size() != 1) {
299
                                    throw new Exception("expecting just one arg for @Messages");
300
                                }
301
                                AssignmentTree assign = (AssignmentTree) args.get(0);
302
                                if (!assign.getVariable().toString().equals("value")) {
303
                                    throw new Exception("expected value=... for @Messages");
304
                                }
305
                                ExpressionTree arg = assign.getExpression();
306
                                NewArrayTree arr;
307
                                if (arg.getKind() == Tree.Kind.STRING_LITERAL) {
308
                                    arr = make.NewArray(null, Collections.<ExpressionTree>emptyList(), Collections.singletonList(arg));
309
                                } else if (arg.getKind() == Tree.Kind.NEW_ARRAY) {
310
                                    arr = (NewArrayTree) arg;
311
                                } else {
312
                                    throw new Exception("unknown arg kind " + arg.getKind() + ": " + arg);
313
                                }
314
                                for (ExpressionTree line : lines) {
315
                                    arr = make.addNewArrayInitializer(arr, line);
316
                                }
317
                                ann = make.Annotation(annotationType, Collections.singletonList(arr));
318
                                if (original.getKind() == Kind.COMPILATION_UNIT) {
319
                                    CompilationUnitTree cut = (CompilationUnitTree) original;
320
                                    List<AnnotationTree> newAnns = new ArrayList<AnnotationTree>(anns);
321
                                    newAnns.set(i, ann);
322
                                    return make.CompilationUnit(newAnns, cut.getPackageName(), cut.getImports(), cut.getTypeDecls(), cut.getSourceFile());
323
                                } else {
324
                                    return make.insertModifiersAnnotation(make.removeModifiersAnnotation((ModifiersTree) original, i), i, ann);
325
                                }
326
                            }
272
                            }
327
                        }
273
                        }
328
                        // Not found, so create a new annotation:
274
                        if (!imported) {
329
                        List<ExpressionTree> values;
275
                            wc.rewrite(cut, make.addCompUnitImport(cut, make.Import(make.Identifier(importBundleStar), true)));
330
                        if (lines.size() > 1) { // @Messages({"# ...", "k=v"})
331
                            values = Collections.<ExpressionTree>singletonList(make.NewArray(null, Collections.<ExpressionTree>emptyList(), lines));
332
                        } else { // @Messages("k=v")
333
                            values = lines;
334
                        }
276
                        }
335
                        AnnotationTree atMessages = make.Annotation(make.QualIdent("org.openide.util.NbBundle.Messages"), values);
277
                        List<? extends ExpressionTree> args = mit.getArguments();
336
                        Tree result;
278
                        List<? extends ExpressionTree> params;
337
                        if (original.getKind() == Kind.COMPILATION_UNIT) {
279
                        if (args.size() == 3 && args.get(2).getKind() == Kind.NEW_ARRAY) {
338
                            CompilationUnitTree cut = (CompilationUnitTree) original;
280
                            params = ((NewArrayTree) args.get(2)).getInitializers();
339
                            List<AnnotationTree> newAnns = new ArrayList<AnnotationTree>(anns);
340
                            newAnns.add(atMessages);
341
                            result = make.CompilationUnit(newAnns, cut.getPackageName(), cut.getImports(), cut.getTypeDecls(), cut.getSourceFile());
342
                        } else {
281
                        } else {
343
                            result = make.addModifiersAnnotation((ModifiersTree) original, atMessages);
282
                            params = args.subList(2, args.size());
344
                        }
283
                        }
345
                        return GeneratorUtilities.get(wc).importFQNs(result);
284
                        wc.rewrite(mit, make.MethodInvocation(Collections.<ExpressionTree>emptyList(), make.Identifier(toIdentifier(key)), params));
285
                    } // else annotation value, nothing to change
286
                    if (!isAlreadyRegistered) {
287
                        Tree enclosing = findEnclosingElement(wc, treePath);
288
                        Tree modifiers;
289
                        if (enclosing.getKind() == Kind.METHOD) {
290
                            modifiers = ((MethodTree) enclosing).getModifiers();
291
                        } else if (enclosing.getKind() == Kind.COMPILATION_UNIT) {
292
                            modifiers = enclosing;
293
                        } else {
294
                            modifiers = ((ClassTree) enclosing).getModifiers();
295
                        }
296
                        List<ExpressionTree> lines = new ArrayList<ExpressionTree>();
297
                        for (String comment : ep.getComment(key)) {
298
                            lines.add(make.Literal(comment));
299
                        }
300
                        lines.add(make.Literal(key + '=' + ep.remove(key)));
301
                        wc.rewrite(modifiers, addMessage(wc, modifiers, lines));
346
                    }
302
                    }
347
                    private Tree findEnclosingElement(WorkingCopy wc, TreePath treePath) {
303
                    // XXX remove NbBundle import if now unused
348
                        Tree leaf = treePath.getLeaf();
304
                }
349
                        Kind kind = leaf.getKind();
305
                // borrowed from FindBugsHint:
350
                        switch (kind) {
306
                private Tree addMessage(WorkingCopy wc, /*Modifiers|CompilationUnit*/Tree original, List<ExpressionTree> lines) throws Exception {
351
                        case CLASS:
307
                    TreeMaker make = wc.getTreeMaker();
352
                        case ENUM:
308
                    // First try to insert into a value list for an existing annotation:
353
                        case INTERFACE:
309
                    List<? extends AnnotationTree> anns;
354
                        case ANNOTATION_TYPE:
310
                    if (original.getKind() == Kind.COMPILATION_UNIT) {
355
                        case METHOD: // (or constructor)
311
                        anns = ((CompilationUnitTree) original).getPackageAnnotations();
356
                            Element e = wc.getTrees().getElement(treePath);
312
                    } else {
357
                            if (e != null) {
313
                        anns = ((ModifiersTree) original).getAnnotations();
358
                                TypeElement type = kind == Kind.METHOD ? wc.getElementUtilities().enclosingTypeElement(e) : (TypeElement) e;
314
                    }
359
                                if (type == null || !wc.getElementUtilities().isLocal(type)) {
315
                    for (int i = 0; i < anns.size(); i++) {
360
                                    return leaf;
316
                        AnnotationTree ann = anns.get(i);
361
                                } // else part of an inner class
317
                        Tree annotationType = ann.getAnnotationType();
318
                        // XXX clumsy and imprecise, but how to find the FQN of the annotation type given a Tree? Want a TypeMirror for it.
319
                        if (annotationType.toString().matches("((org[.]openide[.]util[.])?NbBundle[.])?Messages")) {
320
                            List<? extends ExpressionTree> args = ann.getArguments();
321
                            if (args.size() != 1) {
322
                                throw new Exception("expecting just one arg for @Messages");
362
                            }
323
                            }
363
                            break;
324
                            AssignmentTree assign = (AssignmentTree) args.get(0);
364
                        case COMPILATION_UNIT:
325
                            if (!assign.getVariable().toString().equals("value")) {
365
                            return leaf;
326
                                throw new Exception("expected value=... for @Messages");
327
                            }
328
                            ExpressionTree arg = assign.getExpression();
329
                            NewArrayTree arr;
330
                            if (arg.getKind() == Tree.Kind.STRING_LITERAL) {
331
                                arr = make.NewArray(null, Collections.<ExpressionTree>emptyList(), Collections.singletonList(arg));
332
                            } else if (arg.getKind() == Tree.Kind.NEW_ARRAY) {
333
                                arr = (NewArrayTree) arg;
334
                            } else {
335
                                throw new Exception("unknown arg kind " + arg.getKind() + ": " + arg);
336
                            }
337
                            for (ExpressionTree line : lines) {
338
                                arr = make.addNewArrayInitializer(arr, line);
339
                            }
340
                            ann = make.Annotation(annotationType, Collections.singletonList(arr));
341
                            if (original.getKind() == Kind.COMPILATION_UNIT) {
342
                                CompilationUnitTree cut = (CompilationUnitTree) original;
343
                                List<AnnotationTree> newAnns = new ArrayList<AnnotationTree>(anns);
344
                                newAnns.set(i, ann);
345
                                return make.CompilationUnit(newAnns, cut.getPackageName(), cut.getImports(), cut.getTypeDecls(), cut.getSourceFile());
346
                            } else {
347
                                return make.insertModifiersAnnotation(make.removeModifiersAnnotation((ModifiersTree) original, i), i, ann);
348
                            }
366
                        }
349
                        }
367
                        TreePath parentPath = treePath.getParentPath();
350
                    }
368
                        if (parentPath == null) {
351
                    // Not found, so create a new annotation:
369
                            return null;
352
                    List<ExpressionTree> values;
353
                    if (lines.size() > 1) { // @Messages({"# ...", "k=v"})
354
                        values = Collections.<ExpressionTree>singletonList(make.NewArray(null, Collections.<ExpressionTree>emptyList(), lines));
355
                    } else { // @Messages("k=v")
356
                        values = lines;
357
                    }
358
                    AnnotationTree atMessages = make.Annotation(make.QualIdent("org.openide.util.NbBundle.Messages"), values);
359
                    Tree result;
360
                    if (original.getKind() == Kind.COMPILATION_UNIT) {
361
                        CompilationUnitTree cut = (CompilationUnitTree) original;
362
                        List<AnnotationTree> newAnns = new ArrayList<AnnotationTree>(anns);
363
                        newAnns.add(atMessages);
364
                        result = make.CompilationUnit(newAnns, cut.getPackageName(), cut.getImports(), cut.getTypeDecls(), cut.getSourceFile());
365
                    } else {
366
                        result = make.addModifiersAnnotation((ModifiersTree) original, atMessages);
367
                    }
368
                    return GeneratorUtilities.get(wc).importFQNs(result);
369
                }
370
                private Tree findEnclosingElement(WorkingCopy wc, TreePath treePath) {
371
                    Tree leaf = treePath.getLeaf();
372
                    Kind kind = leaf.getKind();
373
                    switch (kind) {
374
                    case CLASS:
375
                    case ENUM:
376
                    case INTERFACE:
377
                    case ANNOTATION_TYPE:
378
                    case METHOD: // (or constructor)
379
                        Element e = wc.getTrees().getElement(treePath);
380
                        if (e != null) {
381
                            TypeElement type = kind == Kind.METHOD ? wc.getElementUtilities().enclosingTypeElement(e) : (TypeElement) e;
382
                            if (type == null || !wc.getElementUtilities().isLocal(type)) {
383
                                return leaf;
384
                            } // else part of an inner class
370
                        }
385
                        }
371
                        return findEnclosingElement(wc, parentPath);
386
                        break;
387
                    case COMPILATION_UNIT:
388
                        return leaf;
372
                    }
389
                    }
373
                }).commit();
390
                    TreePath parentPath = treePath.getParentPath();
374
                if (!isAlreadyRegistered) {
391
                    if (parentPath == null) {
375
                    OutputStream os = bundleProperties.getOutputStream();
392
                        return null;
376
                    try {
377
                        ep.store(os);
378
                    } finally {
379
                        os.close();
380
                    }
393
                    }
394
                    return findEnclosingElement(wc, parentPath);
381
                }
395
                }
382
                return null;
396
            }).commit();
397
            if (!isAlreadyRegistered) {
398
                OutputStream os = bundleProperties.getOutputStream();
399
                try {
400
                    ep.store(os);
401
                } finally {
402
                    os.close();
403
                }
383
            }
404
            }
384
        }), compilationInfo.getFileObject(), span[0], span[1]));
405
            return null;
406
        }
407
408
        // Copied from NbBundleProcessor
409
        private String toIdentifier(String key) {
410
            if (Utilities.isJavaIdentifier(key)) {
411
                return key;
412
            } else {
413
                String i = key.replaceAll("[^\\p{javaJavaIdentifierPart}]+", "_");
414
                if (Utilities.isJavaIdentifier(i)) {
415
                    return i;
416
                } else {
417
                    return "_" + i;
418
                }
419
            }
420
        }
385
    }
421
    }
386
422
387
    private List<ErrorDescription> warning(String text, int[] span, CompilationInfo compilationInfo) {
423
    private List<ErrorDescription> warning(String text, int[] span, CompilationInfo compilationInfo) {
Lines 392-411 Link Here
392
        // Probably nothing to do.
428
        // Probably nothing to do.
393
    }
429
    }
394
430
395
    // Copied from NbBundleProcessor
396
    private String toIdentifier(String key) {
397
        if (Utilities.isJavaIdentifier(key)) {
398
            return key;
399
        } else {
400
            String i = key.replaceAll("[^\\p{javaJavaIdentifierPart}]+", "_");
401
            if (Utilities.isJavaIdentifier(i)) {
402
                return i;
403
            } else {
404
                return "_" + i;
405
            }
406
        }
407
    }
408
409
    private static boolean isAlreadyRegistered(TreePath treePath, String key) {
431
    private static boolean isAlreadyRegistered(TreePath treePath, String key) {
410
        ModifiersTree modifiers;
432
        ModifiersTree modifiers;
411
        Tree tree = treePath.getLeaf();
433
        Tree tree = treePath.getLeaf();

Return to bug 201871