1 19 20 package org.netbeans.modules.editor.java; 21 22 import com.sun.source.tree.*; 23 import com.sun.source.util.*; 24 25 import java.io.IOException ; 26 import java.util.*; 27 import javax.lang.model.element.*; 28 import javax.lang.model.type.*; 29 import javax.lang.model.util.Types; 30 import javax.swing.text.JTextComponent ; 31 32 import org.netbeans.api.java.source.*; 33 import org.netbeans.lib.editor.codetemplates.spi.*; 34 import org.openide.util.Exceptions; 35 36 40 public class JavaCodeTemplateProcessor implements CodeTemplateProcessor { 41 42 public static final String INSTANCE_OF = "instanceof"; public static final String ARRAY = "array"; public static final String ITERABLE = "iterable"; public static final String TYPE = "type"; public static final String ITERABLE_ELEMENT_TYPE = "iterableElementType"; public static final String LEFT_SIDE_TYPE = "leftSideType"; public static final String RIGHT_SIDE_TYPE = "rightSideType"; public static final String CAST = "cast"; public static final String NEW_VAR_NAME = "newVarName"; public static final String NAMED = "named"; 53 private static final String FALSE = "false"; private static final String NULL = "null"; 56 private CodeTemplateInsertRequest request; 57 58 private CompilationInfo cInfo = null; 59 private TreePath treePath = null; 60 private Scope scope = null; 61 private TypeElement enclClass = null; 62 private Iterable <? extends Element> locals = null; 63 private Map<CodeTemplateParameter, String > param2hints = new HashMap<CodeTemplateParameter, String >(); 64 private Map<CodeTemplateParameter, TypeMirror> param2types = new HashMap<CodeTemplateParameter, TypeMirror>(); 65 private ErrChecker errChecker = new ErrChecker(); 66 67 private JavaCodeTemplateProcessor(CodeTemplateInsertRequest request) { 68 this.request = request; 69 } 70 71 public synchronized void updateDefaultValues() { 72 boolean cont = true; 73 while (cont) { 74 cont = false; 75 for (Object p : request.getMasterParameters()) { 76 CodeTemplateParameter param = (CodeTemplateParameter)p; 77 String value = getProposedValue(param); 78 if (value != null && !value.equals(param.getValue())) { 79 param.setValue(value); 80 cont = true; 81 } 82 } 83 } 84 updateImports(); 85 } 86 87 public void parameterValueChanged(CodeTemplateParameter masterParameter, boolean typingChange) { 88 if (typingChange) { 89 for (Object p : request.getMasterParameters()) { 90 CodeTemplateParameter param = (CodeTemplateParameter)p; 91 if (!param.isUserModified()) { 92 String value = getProposedValue(param); 93 if (value != null && !value.equals(param.getValue())) 94 param.setValue(value); 95 } else { 96 param2types.remove(param); 97 } 98 } 99 updateImports(); 100 } 101 } 102 103 public void release() { 104 } 105 106 private void updateImports() { 107 if (!param2types.isEmpty()) { 108 AutoImport imp = AutoImport.get(cInfo); 109 for (Map.Entry<CodeTemplateParameter, TypeMirror> entry : param2types.entrySet()) { 110 CodeTemplateParameter param = entry.getKey(); 111 TypeMirror tm = param2types.get(param); 112 TreePath tp = cInfo.getTreeUtilities().pathFor(request.getInsertTextOffset() + param.getInsertTextOffset()); 113 CharSequence typeName = imp.resolveImport(tp, (DeclaredType)tm); 114 if (CAST.equals(param2hints.get(param))) { 115 param.setValue("(" + typeName + ")"); } else if (INSTANCE_OF.equals(param2hints.get(param))) { 117 String value = param.getValue().substring(param.getValue().lastIndexOf('.') + 1); param.setValue(typeName + "." + value); } else { 120 param.setValue(typeName.toString()); 121 } 122 } 123 } 124 } 125 126 private String getProposedValue(CodeTemplateParameter param) { 127 param2hints.remove(param); 128 param2types.remove(param); 129 String name = null; 130 for (Object e : param.getHints().entrySet()) { 131 Map.Entry entry = (Map.Entry)e; 132 if (INSTANCE_OF.equals(entry.getKey())) { 133 VariableElement ve = instanceOf((String )entry.getValue(), name); 134 if (ve != null) { 135 param2hints.put(param, INSTANCE_OF); 136 return ve.getSimpleName().toString(); 137 } else if (name == null) { 138 ve = staticInstanceOf((String )entry.getValue(), name); 139 if (ve != null) { 140 param2hints.put(param, INSTANCE_OF); 141 TypeMirror tm = ve.getEnclosingElement().asType(); 142 if (tm.getKind() == TypeKind.DECLARED) 143 param2types.put(param, tm); 144 return Utilities.getTypeName(tm, true) + "." + ve.getSimpleName(); 145 } else { 146 return valueOf((String )entry.getValue()); 147 } 148 } 149 } else if (ARRAY.equals(entry.getKey())) { 150 VariableElement ve = array(); 151 if (ve != null) { 152 param2hints.put(param, ARRAY); 153 return ve.getSimpleName().toString(); 154 } 155 } else if (ITERABLE.equals(entry.getKey())) { 156 VariableElement ve = iterable(); 157 if (ve != null) { 158 param2hints.put(param, ITERABLE); 159 return ve.getSimpleName().toString(); 160 } 161 } else if (TYPE.equals(entry.getKey())) { 162 TypeMirror tm = type((String )entry.getValue()); 163 if (tm != null && tm.getKind() != TypeKind.ERROR) { 164 if (tm.getKind() == TypeKind.TYPEVAR) 165 tm = ((TypeVariable)tm).getUpperBound(); 166 String value = Utilities.getTypeName(tm, true).toString(); 167 if (value != null) { 168 param2hints.put(param, TYPE); 169 if (tm.getKind() == TypeKind.DECLARED) 170 param2types.put(param, tm); 171 return value; 172 } 173 } 174 } else if (ITERABLE_ELEMENT_TYPE.equals(entry.getKey())) { 175 TypeMirror tm = iterableElementType(param.getInsertTextOffset() + 1); 176 if (tm != null && tm.getKind() != TypeKind.ERROR) { 177 if (tm.getKind() == TypeKind.TYPEVAR) 178 tm = ((TypeVariable)tm).getUpperBound(); 179 String value = Utilities.getTypeName(tm, true).toString(); 180 if (value != null) { 181 param2hints.put(param, ITERABLE_ELEMENT_TYPE); 182 if (tm.getKind() == TypeKind.DECLARED) 183 param2types.put(param, tm); 184 return value; 185 } 186 } 187 } else if (LEFT_SIDE_TYPE.equals(entry.getKey())) { 188 TypeMirror tm = assignmentSideType(param.getInsertTextOffset() + 1, true); 189 if (tm != null && tm.getKind() != TypeKind.ERROR) { 190 if (tm.getKind() == TypeKind.TYPEVAR) 191 tm = ((TypeVariable)tm).getUpperBound(); 192 String value = Utilities.getTypeName(tm, true).toString(); 193 if (value != null) { 194 param2hints.put(param, LEFT_SIDE_TYPE); 195 if (tm.getKind() == TypeKind.DECLARED) 196 param2types.put(param, tm); 197 return value; 198 } 199 } 200 } else if (RIGHT_SIDE_TYPE.equals(entry.getKey())) { 201 TypeMirror tm = assignmentSideType(param.getInsertTextOffset() + 1, false); 202 if (tm != null && tm.getKind() != TypeKind.ERROR) { 203 if (tm.getKind() == TypeKind.TYPEVAR) 204 tm = ((TypeVariable)tm).getUpperBound(); 205 String value = Utilities.getTypeName(tm, true).toString(); 206 if (value != null) { 207 param2hints.put(param, RIGHT_SIDE_TYPE); 208 if (tm.getKind() == TypeKind.DECLARED) 209 param2types.put(param, tm); 210 return value; 211 } 212 } 213 } else if (CAST.equals(entry.getKey())) { 214 TypeMirror tm = cast(param.getInsertTextOffset() + 1); 215 if (tm == null) { 216 param2hints.put(param, CAST); 217 param2types.remove(param); 218 return ""; } else if (tm.getKind() != TypeKind.ERROR) { 220 String value = Utilities.getTypeName(tm, true).toString(); 221 if (value != null) { 222 param2hints.put(param, CAST); 223 if (tm.getKind() == TypeKind.DECLARED) 224 param2types.put(param, tm); return "(" + value + ")"; } 227 } 228 } else if (NEW_VAR_NAME.equals(entry.getKey())) { 229 param2hints.put(param, NEW_VAR_NAME); 230 return newVarName(param.getInsertTextOffset() + 1); 231 } else if (NAMED.equals(entry.getKey())) { 232 name = param.getName(); 233 } 234 } 235 return null; 236 } 237 238 private VariableElement instanceOf(String typeName, String name) { 239 try { 240 if (initParsing()) { 241 TypeMirror type = cInfo.getTreeUtilities().parseType(typeName, enclClass); 242 VariableElement closest = null; 243 int distance = Integer.MAX_VALUE; 244 if (type != null) { 245 Types types = cInfo.getTypes(); 246 for (Element e : locals) { 247 if (e instanceof VariableElement && types.isAssignable(e.asType(), type)) { 248 if (name == null) 249 return (VariableElement)e; 250 int d = UiUtils.getDistance(e.getSimpleName().toString(), name); 251 if (d < distance) { 252 distance = d; 253 closest = (VariableElement)e; 254 } 255 } 256 } 257 } 258 return closest; 259 } 260 } catch (Exception e) { 261 } 262 return null; 263 } 264 265 private VariableElement staticInstanceOf(String typeName, String name) { 266 try { 267 if (initParsing()) { 268 final TreeUtilities tu = cInfo.getTreeUtilities(); 269 TypeMirror type = tu.parseType(typeName, enclClass); 270 VariableElement closest = null; 271 int distance = Integer.MAX_VALUE; 272 if (type != null) { 273 final Types types = cInfo.getTypes(); 274 if (type.getKind() == TypeKind.DECLARED) { 275 final DeclaredType dType = (DeclaredType)type; 276 final TypeElement element = (TypeElement)dType.asElement(); 277 final boolean isStatic = element.getKind().isClass() || element.getKind().isInterface(); 278 ElementUtilities.ElementAcceptor acceptor = new ElementUtilities.ElementAcceptor() { 279 public boolean accept(Element e, TypeMirror t) { 280 return e.getKind().isField() && 281 (!isStatic || e.getModifiers().contains(Modifier.STATIC)) && 282 tu.isAccessible(scope, e, t) && 283 (e.getKind().isField() && types.isAssignable(((VariableElement)e).asType(), dType) || e.getKind() == ElementKind.METHOD && types.isAssignable(((ExecutableElement)e).getReturnType(), dType)); 284 } 285 }; 286 for (Element ee : cInfo.getElementUtilities().getMembers(dType, acceptor)) { 287 if (name == null) 288 return (VariableElement)ee; 289 int d = UiUtils.getDistance(ee.getSimpleName().toString(), name); 290 if (d < distance) { 291 distance = d; 292 closest = (VariableElement)ee; 293 } 294 } 295 } 296 } 297 return closest; 298 } 299 } catch (Exception e) { 300 } 301 return null; 302 } 303 304 private String valueOf(String typeName) { 305 try { 306 if (initParsing()) { 307 TypeMirror type = cInfo.getTreeUtilities().parseType(typeName, enclClass); 308 if (type != null) { 309 if (type.getKind() == TypeKind.DECLARED) 310 return NULL; 311 else if (type.getKind() == TypeKind.BOOLEAN) 312 return FALSE; 313 } 314 } 315 } catch (Exception e) { 316 } 317 return null; 318 } 319 320 private VariableElement array() { 321 if (initParsing()) { 322 for (Element e : locals) { 323 if (e instanceof VariableElement && e.asType().getKind() == TypeKind.ARRAY) 324 return (VariableElement)e; 325 } 326 } 327 return null; 328 } 329 330 private VariableElement iterable() { 331 if (initParsing()) { 332 TypeMirror iterableType = cInfo.getTypes().getDeclaredType(cInfo.getElements().getTypeElement("java.lang.Iterable")); for (Element e : locals) { 334 if (e instanceof VariableElement && (e.asType().getKind() == TypeKind.ARRAY || cInfo.getTypes().isAssignable(e.asType(), iterableType))) 335 return (VariableElement)e; 336 } 337 } 338 return null; 339 } 340 341 private TypeMirror type(String typeName) { 342 return initParsing() ? cInfo.getTreeUtilities().parseType(typeName, enclClass) : null; 343 } 344 345 private TypeMirror iterableElementType(int caretOffset) { 346 try { 347 if (initParsing()) { 348 SourcePositions[] sourcePositions = new SourcePositions[1]; 349 TreeUtilities tu = cInfo.getTreeUtilities(); 350 StatementTree stmt = tu.parseStatement(request.getInsertText(), sourcePositions); 351 if (errChecker.containsErrors(stmt)) 352 return null; 353 TreePath path = tu.pathFor(new TreePath(treePath, stmt), caretOffset, sourcePositions[0]); 354 TreePath loop = Utilities.getPathElementOfKind(Tree.Kind.ENHANCED_FOR_LOOP, path); 355 if (loop != null) { 356 tu.attributeTree(stmt, scope); 357 TypeMirror type = cInfo.getTrees().getTypeMirror(new TreePath(loop, ((EnhancedForLoopTree)loop.getLeaf()).getExpression())); 358 switch (type.getKind()) { 359 case ARRAY: 360 type = ((ArrayType)type).getComponentType(); 361 return type; 362 case DECLARED: 363 Iterator<? extends TypeMirror> types = ((DeclaredType)type).getTypeArguments().iterator(); 364 if (types.hasNext()) 365 return types.next(); 366 return cInfo.getElements().getTypeElement("java.lang.Object").asType(); } 368 } 369 } 370 } catch (Exception e) { 371 } 372 return null; 373 } 374 375 private TypeMirror assignmentSideType(int caretOffset, boolean left) { 376 try { 377 if (initParsing()) { 378 SourcePositions[] sourcePositions = new SourcePositions[1]; 379 TreeUtilities tu = cInfo.getTreeUtilities(); 380 StatementTree stmt = tu.parseStatement(request.getInsertText(), sourcePositions); 381 if (errChecker.containsErrors(stmt)) 382 return null; 383 TreePath path = tu.pathFor(new TreePath(treePath, stmt), caretOffset, sourcePositions[0]); 384 TreePath tree = Utilities.getPathElementOfKind(EnumSet.of(Tree.Kind.ASSIGNMENT, Tree.Kind.VARIABLE), path); 385 if (tree == null) 386 return null; 387 tu.attributeTree(stmt, scope); 388 if (tree.getLeaf().getKind() == Tree.Kind.ASSIGNMENT) { 389 AssignmentTree as = (AssignmentTree)tree.getLeaf(); 390 TreePath type = new TreePath(tree, left ? as.getVariable() : as.getExpression()); 391 return cInfo.getTrees().getTypeMirror(type); 392 } 393 VariableTree vd = (VariableTree)tree.getLeaf(); 394 TreePath type = new TreePath(tree, left ? vd.getType() : vd.getInitializer()); 395 return cInfo.getTrees().getTypeMirror(type); 396 } 397 } catch (Exception e) { 398 } 399 return null; 400 } 401 402 private TypeMirror cast(int caretOffset) { 403 try { 404 if (initParsing()) { 405 SourcePositions[] sourcePositions = new SourcePositions[1]; 406 TreeUtilities tu = cInfo.getTreeUtilities(); 407 StatementTree stmt = tu.parseStatement(request.getInsertText(), sourcePositions); 408 if (errChecker.containsErrors(stmt)) 409 return null; 410 TreePath path = tu.pathFor(new TreePath(treePath, stmt), caretOffset, sourcePositions[0]); 411 TreePath tree = Utilities.getPathElementOfKind(EnumSet.of(Tree.Kind.ASSIGNMENT, Tree.Kind.VARIABLE), path); 412 if (tree == null) 413 return null; 414 tu.attributeTree(stmt, scope); 415 if (tree.getLeaf().getKind() == Tree.Kind.ASSIGNMENT) { 416 AssignmentTree as = (AssignmentTree)tree.getLeaf(); 417 TypeMirror left = cInfo.getTrees().getTypeMirror(new TreePath(tree, as.getVariable())); 418 TreePath exp = new TreePath(tree, as.getExpression()); 419 if (exp.getLeaf() instanceof TypeCastTree) 420 exp = new TreePath(exp, ((TypeCastTree)exp.getLeaf()).getExpression()); 421 TypeMirror right = cInfo.getTrees().getTypeMirror(exp); 422 if (right == null || left == null) 423 return null; 424 if (cInfo.getTypes().isAssignable(right, left)) 425 return null; 426 return left; 427 } 428 VariableTree vd = (VariableTree)tree.getLeaf(); 429 TypeMirror left = cInfo.getTrees().getTypeMirror(new TreePath(tree, vd.getType())); 430 TreePath exp = new TreePath(tree, vd.getInitializer()); 431 if (exp.getLeaf() instanceof TypeCastTree) 432 exp = new TreePath(exp, ((TypeCastTree)exp.getLeaf()).getExpression()); 433 TypeMirror right = cInfo.getTrees().getTypeMirror(exp); 434 if (right == null) 435 return null; 436 if (left == null) 437 return null; 438 if (right.getKind() != TypeKind.ERROR && cInfo.getTypes().isAssignable(right, left)) 439 return null; 440 return left; 441 } 442 } catch (Exception e) { 443 } 444 return null; 445 } 446 447 private String newVarName(int caretOffset) { 448 try { 449 if (initParsing()) { 450 SourcePositions[] sourcePositions = new SourcePositions[1]; 451 TreeUtilities tu = cInfo.getTreeUtilities(); 452 StatementTree stmt = tu.parseStatement(request.getInsertText(), sourcePositions); 453 if (errChecker.containsErrors(stmt)) 454 return null; 455 TreePath path = tu.pathFor(new TreePath(treePath, stmt), caretOffset, sourcePositions[0]); 456 TreePath decl = Utilities.getPathElementOfKind(Tree.Kind.VARIABLE, path); 457 if (decl != null) { 458 Scope s = tu.attributeTreeTo(stmt, scope, decl.getLeaf()); 459 TypeMirror type = cInfo.getTrees().getTypeMirror(decl); 460 boolean isConst = ((VariableTree)decl.getLeaf()).getModifiers().getFlags().containsAll(EnumSet.of(Modifier.FINAL, Modifier.STATIC)); 461 final Name varName = ((VariableTree)decl.getLeaf()).getName(); 462 ElementUtilities.ElementAcceptor acceptor = new ElementUtilities.ElementAcceptor() { 463 public boolean accept(Element e, TypeMirror t) { 464 switch(e.getKind()) { 465 case EXCEPTION_PARAMETER: 466 case LOCAL_VARIABLE: 467 case PARAMETER: 468 return varName != e.getSimpleName(); 469 default: 470 return false; 471 } 472 } 473 }; 474 Iterator<String > names = Utilities.varNamesSuggestions(type, null, cInfo.getTypes(), cInfo.getElements(), cInfo.getElementUtilities().getLocalVars(s, acceptor), isConst).iterator(); 475 if (names.hasNext()) 476 return names.next(); 477 } 478 } 479 } catch (Exception e) { 480 } 481 return null; 482 } 483 484 private boolean initParsing() { 485 if (cInfo == null) { 486 JTextComponent c = request.getComponent(); 487 final int caretOffset = c.getCaret().getDot(); 488 JavaSource js = JavaSource.forDocument(c.getDocument()); 489 if (js != null) { 490 try { 491 js.runUserActionTask(new CancellableTask<CompilationController>() { 492 public void cancel() { 493 } 494 495 public void run(final CompilationController controller) throws IOException { 496 controller.toPhase(JavaSource.Phase.RESOLVED); 497 cInfo = controller; 498 final TreeUtilities tu = cInfo.getTreeUtilities(); 499 treePath = tu.pathFor(caretOffset); 500 scope = tu.scopeFor(caretOffset); 501 enclClass = scope.getEnclosingClass(); 502 final boolean isStatic = enclClass != null ? tu.isStaticContext(scope) : false; 503 if (enclClass == null) { 504 CompilationUnitTree cut = treePath.getCompilationUnit(); 505 Iterator<? extends Tree> it = cut.getTypeDecls().iterator(); 506 if (it.hasNext()) 507 enclClass = (TypeElement)cInfo.getTrees().getElement(TreePath.getPath(cut, it.next())); 508 } 509 final Trees trees = controller.getTrees(); 510 final SourcePositions sp = trees.getSourcePositions(); 511 final Collection<? extends Element> illegalForwardRefs = Utilities.getForwardReferences(treePath, caretOffset, sp, trees);; 512 final ExecutableElement method = scope.getEnclosingMethod(); 513 ElementUtilities.ElementAcceptor acceptor = new ElementUtilities.ElementAcceptor() { 514 public boolean accept(Element e, TypeMirror t) { 515 switch (e.getKind()) { 516 case LOCAL_VARIABLE: 517 if (isStatic && e.getSimpleName().contentEquals("this") || e.getSimpleName().contentEquals("super")) return false; 519 case EXCEPTION_PARAMETER: 520 case PARAMETER: 521 return (method == e.getEnclosingElement() || e.getModifiers().contains(Modifier.FINAL)) && 522 !illegalForwardRefs.contains(e); 523 case FIELD: 524 if (illegalForwardRefs.contains(e)) 525 return false; 526 default: 527 return (!isStatic || e.getModifiers().contains(Modifier.STATIC)) && tu.isAccessible(scope, e, t); 528 } 529 } 530 }; 531 locals = cInfo.getElementUtilities().getLocalMembersAndVars(scope, acceptor); 532 } 533 },false); 534 } catch(IOException ioe) { 535 Exceptions.printStackTrace(ioe); 536 } 537 } 538 } 539 return cInfo != null; 540 } 541 542 public static final class Factory implements CodeTemplateProcessorFactory { 543 544 public CodeTemplateProcessor createProcessor(CodeTemplateInsertRequest request) { 545 return new JavaCodeTemplateProcessor(request); 546 } 547 } 548 549 550 private static class ErrChecker extends TreeScanner<Void , Void > { 551 private boolean containsErrors; 552 553 public boolean containsErrors(Tree tree) { 554 containsErrors = false; 555 scan(tree, null); 556 return containsErrors; 557 } 558 559 public Void visitErroneous(ErroneousTree node, Void p) { 560 containsErrors = true; 561 return null; 562 } 563 564 public Void scan(Tree node, Void p) { 565 if (containsErrors) 566 return null; 567 return super.scan(node, p); 568 } 569 } 570 } 571 | Popular Tags |