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(); |