JavacElements.java revision 3792:d516975e8110
1/* 2 * Copyright (c) 2005, 2016, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26package com.sun.tools.javac.model; 27 28import java.util.Map; 29 30import javax.lang.model.SourceVersion; 31import javax.lang.model.element.*; 32import javax.lang.model.type.DeclaredType; 33import javax.lang.model.util.Elements; 34import javax.tools.JavaFileObject; 35import static javax.lang.model.util.ElementFilter.methodsIn; 36 37import com.sun.source.util.JavacTask; 38import com.sun.tools.javac.api.JavacTaskImpl; 39import com.sun.tools.javac.code.*; 40import com.sun.tools.javac.code.Scope.WriteableScope; 41import com.sun.tools.javac.code.Symbol.*; 42import com.sun.tools.javac.comp.AttrContext; 43import com.sun.tools.javac.comp.Enter; 44import com.sun.tools.javac.comp.Env; 45import com.sun.tools.javac.main.JavaCompiler; 46import com.sun.tools.javac.processing.PrintingProcessor; 47import com.sun.tools.javac.tree.JCTree; 48import com.sun.tools.javac.tree.JCTree.*; 49import com.sun.tools.javac.tree.TreeInfo; 50import com.sun.tools.javac.tree.TreeScanner; 51import com.sun.tools.javac.util.*; 52import com.sun.tools.javac.util.DefinedBy.Api; 53import com.sun.tools.javac.util.Name; 54import static com.sun.tools.javac.code.Kinds.Kind.*; 55import static com.sun.tools.javac.code.Scope.LookupKind.NON_RECURSIVE; 56import static com.sun.tools.javac.code.TypeTag.CLASS; 57import com.sun.tools.javac.comp.CompileStates; 58import com.sun.tools.javac.comp.CompileStates.CompileState; 59import com.sun.tools.javac.comp.Modules; 60import static com.sun.tools.javac.tree.JCTree.Tag.*; 61 62/** 63 * Utility methods for operating on program elements. 64 * 65 * <p><b>This is NOT part of any supported API. 66 * If you write code that depends on this, you do so at your own 67 * risk. This code and its internal interfaces are subject to change 68 * or deletion without notice.</b></p> 69 */ 70public class JavacElements implements Elements { 71 72 private final JavaCompiler javaCompiler; 73 private final Symtab syms; 74 private final Modules modules; 75 private final Names names; 76 private final Types types; 77 private final Enter enter; 78 private final JavacTaskImpl javacTaskImpl; 79 private final CompileStates compileStates; 80 81 public static JavacElements instance(Context context) { 82 JavacElements instance = context.get(JavacElements.class); 83 if (instance == null) 84 instance = new JavacElements(context); 85 return instance; 86 } 87 88 protected JavacElements(Context context) { 89 context.put(JavacElements.class, this); 90 javaCompiler = JavaCompiler.instance(context); 91 syms = Symtab.instance(context); 92 modules = Modules.instance(context); 93 names = Names.instance(context); 94 types = Types.instance(context); 95 enter = Enter.instance(context); 96 JavacTask t = context.get(JavacTask.class); 97 javacTaskImpl = t instanceof JavacTaskImpl ? (JavacTaskImpl) t : null; 98 compileStates = CompileStates.instance(context); 99 } 100 101 @Override @DefinedBy(Api.LANGUAGE_MODEL) 102 public ModuleSymbol getModuleElement(CharSequence name) { 103 ensureEntered("getModuleElement"); 104 String strName = name.toString(); 105 if (strName.equals("")) 106 return syms.unnamedModule; 107 return modules.getObservableModule(names.fromString(strName)); 108 } 109 110 @Override @DefinedBy(Api.LANGUAGE_MODEL) 111 public PackageSymbol getPackageElement(CharSequence name) { 112 ensureEntered("getPackageElement"); 113 return getPackageElement(modules.getDefaultModule(), name); 114 } 115 116 @Override @DefinedBy(Api.LANGUAGE_MODEL) 117 public PackageSymbol getPackageElement(ModuleElement module, CharSequence name) { 118 String strName = name.toString(); 119 if (strName.equals("")) 120 return syms.unnamedModule.unnamedPackage; 121 return SourceVersion.isName(strName) 122 ? nameToSymbol((ModuleSymbol) module, strName, PackageSymbol.class) 123 : null; 124 } 125 126 @Override @DefinedBy(Api.LANGUAGE_MODEL) 127 public ClassSymbol getTypeElement(CharSequence name) { 128 ensureEntered("getTypeElement"); 129 return getTypeElement(modules.getDefaultModule(), name); 130 } 131 132 @Override @DefinedBy(Api.LANGUAGE_MODEL) 133 public ClassSymbol getTypeElement(ModuleElement module, CharSequence name) { 134 String strName = name.toString(); 135 return SourceVersion.isName(strName) 136 ? nameToSymbol((ModuleSymbol) module, strName, ClassSymbol.class) 137 : null; 138 } 139 140 /** 141 * Returns a symbol given the type's or package's canonical name, 142 * or null if the name isn't found. 143 */ 144 private <S extends Symbol> S nameToSymbol(ModuleSymbol module, String nameStr, Class<S> clazz) { 145 Name name = names.fromString(nameStr); 146 // First check cache. 147 Symbol sym = (clazz == ClassSymbol.class) 148 ? syms.getClass(module, name) 149 : syms.lookupPackage(module, name); 150 151 try { 152 if (sym == null) 153 sym = javaCompiler.resolveIdent(module, nameStr); 154 155 sym.complete(); 156 157 return (sym.kind != ERR && 158 sym.exists() && 159 clazz.isInstance(sym) && 160 name.equals(sym.getQualifiedName())) 161 ? clazz.cast(sym) 162 : null; 163 } catch (CompletionFailure e) { 164 return null; 165 } 166 } 167 168 public JavacSourcePosition getSourcePosition(Element e) { 169 Pair<JCTree, JCCompilationUnit> treeTop = getTreeAndTopLevel(e); 170 if (treeTop == null) 171 return null; 172 JCTree tree = treeTop.fst; 173 JCCompilationUnit toplevel = treeTop.snd; 174 JavaFileObject sourcefile = toplevel.sourcefile; 175 if (sourcefile == null) 176 return null; 177 return new JavacSourcePosition(sourcefile, tree.pos, toplevel.lineMap); 178 } 179 180 public JavacSourcePosition getSourcePosition(Element e, AnnotationMirror a) { 181 Pair<JCTree, JCCompilationUnit> treeTop = getTreeAndTopLevel(e); 182 if (treeTop == null) 183 return null; 184 JCTree tree = treeTop.fst; 185 JCCompilationUnit toplevel = treeTop.snd; 186 JavaFileObject sourcefile = toplevel.sourcefile; 187 if (sourcefile == null) 188 return null; 189 190 JCTree annoTree = matchAnnoToTree(a, e, tree); 191 if (annoTree == null) 192 return null; 193 return new JavacSourcePosition(sourcefile, annoTree.pos, 194 toplevel.lineMap); 195 } 196 197 public JavacSourcePosition getSourcePosition(Element e, AnnotationMirror a, 198 AnnotationValue v) { 199 // TODO: better accuracy in getSourcePosition(... AnnotationValue) 200 return getSourcePosition(e, a); 201 } 202 203 /** 204 * Returns the tree for an annotation given the annotated element 205 * and the element's own tree. Returns null if the tree cannot be found. 206 */ 207 private JCTree matchAnnoToTree(AnnotationMirror findme, 208 Element e, JCTree tree) { 209 Symbol sym = cast(Symbol.class, e); 210 class Vis extends JCTree.Visitor { 211 List<JCAnnotation> result = null; 212 public void visitPackageDef(JCPackageDecl tree) { 213 result = tree.annotations; 214 } 215 public void visitClassDef(JCClassDecl tree) { 216 result = tree.mods.annotations; 217 } 218 public void visitMethodDef(JCMethodDecl tree) { 219 result = tree.mods.annotations; 220 } 221 public void visitVarDef(JCVariableDecl tree) { 222 result = tree.mods.annotations; 223 } 224 @Override 225 public void visitTypeParameter(JCTypeParameter tree) { 226 result = tree.annotations; 227 } 228 } 229 Vis vis = new Vis(); 230 tree.accept(vis); 231 if (vis.result == null) 232 return null; 233 234 List<Attribute.Compound> annos = sym.getAnnotationMirrors(); 235 return matchAnnoToTree(cast(Attribute.Compound.class, findme), 236 annos, 237 vis.result); 238 } 239 240 /** 241 * Returns the tree for an annotation given a list of annotations 242 * in which to search (recursively) and their corresponding trees. 243 * Returns null if the tree cannot be found. 244 */ 245 private JCTree matchAnnoToTree(Attribute.Compound findme, 246 List<Attribute.Compound> annos, 247 List<JCAnnotation> trees) { 248 for (Attribute.Compound anno : annos) { 249 for (JCAnnotation tree : trees) { 250 JCTree match = matchAnnoToTree(findme, anno, tree); 251 if (match != null) 252 return match; 253 } 254 } 255 return null; 256 } 257 258 /** 259 * Returns the tree for an annotation given an Attribute to 260 * search (recursively) and its corresponding tree. 261 * Returns null if the tree cannot be found. 262 */ 263 private JCTree matchAnnoToTree(final Attribute.Compound findme, 264 final Attribute attr, 265 final JCTree tree) { 266 if (attr == findme) 267 return (tree.type.tsym == findme.type.tsym) ? tree : null; 268 269 class Vis implements Attribute.Visitor { 270 JCTree result = null; 271 public void visitConstant(Attribute.Constant value) { 272 } 273 public void visitClass(Attribute.Class clazz) { 274 } 275 public void visitCompound(Attribute.Compound anno) { 276 for (Pair<MethodSymbol, Attribute> pair : anno.values) { 277 JCExpression expr = scanForAssign(pair.fst, tree); 278 if (expr != null) { 279 JCTree match = matchAnnoToTree(findme, pair.snd, expr); 280 if (match != null) { 281 result = match; 282 return; 283 } 284 } 285 } 286 } 287 public void visitArray(Attribute.Array array) { 288 if (tree.hasTag(NEWARRAY) && 289 types.elemtype(array.type).tsym == findme.type.tsym) { 290 List<JCExpression> elems = ((JCNewArray) tree).elems; 291 for (Attribute value : array.values) { 292 if (value == findme) { 293 result = elems.head; 294 return; 295 } 296 elems = elems.tail; 297 } 298 } 299 } 300 public void visitEnum(Attribute.Enum e) { 301 } 302 public void visitError(Attribute.Error e) { 303 } 304 } 305 Vis vis = new Vis(); 306 attr.accept(vis); 307 return vis.result; 308 } 309 310 /** 311 * Scans for a JCAssign node with a LHS matching a given 312 * symbol, and returns its RHS. Does not scan nested JCAnnotations. 313 */ 314 private JCExpression scanForAssign(final MethodSymbol sym, 315 final JCTree tree) { 316 class TS extends TreeScanner { 317 JCExpression result = null; 318 public void scan(JCTree t) { 319 if (t != null && result == null) 320 t.accept(this); 321 } 322 public void visitAnnotation(JCAnnotation t) { 323 if (t == tree) 324 scan(t.args); 325 } 326 public void visitAssign(JCAssign t) { 327 if (t.lhs.hasTag(IDENT)) { 328 JCIdent ident = (JCIdent) t.lhs; 329 if (ident.sym == sym) 330 result = t.rhs; 331 } 332 } 333 } 334 TS scanner = new TS(); 335 tree.accept(scanner); 336 return scanner.result; 337 } 338 339 /** 340 * Returns the tree node corresponding to this element, or null 341 * if none can be found. 342 */ 343 public JCTree getTree(Element e) { 344 Pair<JCTree, ?> treeTop = getTreeAndTopLevel(e); 345 return (treeTop != null) ? treeTop.fst : null; 346 } 347 348 @DefinedBy(Api.LANGUAGE_MODEL) 349 public String getDocComment(Element e) { 350 // Our doc comment is contained in a map in our toplevel, 351 // indexed by our tree. Find our enter environment, which gives 352 // us our toplevel. It also gives us a tree that contains our 353 // tree: walk it to find our tree. This is painful. 354 Pair<JCTree, JCCompilationUnit> treeTop = getTreeAndTopLevel(e); 355 if (treeTop == null) 356 return null; 357 JCTree tree = treeTop.fst; 358 JCCompilationUnit toplevel = treeTop.snd; 359 if (toplevel.docComments == null) 360 return null; 361 return toplevel.docComments.getCommentText(tree); 362 } 363 364 @DefinedBy(Api.LANGUAGE_MODEL) 365 public PackageElement getPackageOf(Element e) { 366 return cast(Symbol.class, e).packge(); 367 } 368 369 @DefinedBy(Api.LANGUAGE_MODEL) 370 public ModuleElement getModuleOf(Element e) { 371 Symbol sym = cast(Symbol.class, e); 372 return (sym.kind == MDL) ? ((ModuleElement) e) : sym.packge().modle; 373 } 374 375 @DefinedBy(Api.LANGUAGE_MODEL) 376 public boolean isDeprecated(Element e) { 377 Symbol sym = cast(Symbol.class, e); 378 sym.complete(); 379 return sym.isDeprecated(); 380 } 381 382 @DefinedBy(Api.LANGUAGE_MODEL) 383 public Name getBinaryName(TypeElement type) { 384 return cast(TypeSymbol.class, type).flatName(); 385 } 386 387 @DefinedBy(Api.LANGUAGE_MODEL) 388 public Map<MethodSymbol, Attribute> getElementValuesWithDefaults( 389 AnnotationMirror a) { 390 Attribute.Compound anno = cast(Attribute.Compound.class, a); 391 DeclaredType annotype = a.getAnnotationType(); 392 Map<MethodSymbol, Attribute> valmap = anno.getElementValues(); 393 394 for (ExecutableElement ex : 395 methodsIn(annotype.asElement().getEnclosedElements())) { 396 MethodSymbol meth = (MethodSymbol) ex; 397 Attribute defaultValue = meth.getDefaultValue(); 398 if (defaultValue != null && !valmap.containsKey(meth)) { 399 valmap.put(meth, defaultValue); 400 } 401 } 402 return valmap; 403 } 404 405 /** 406 * {@inheritDoc} 407 */ 408 @DefinedBy(Api.LANGUAGE_MODEL) 409 public FilteredMemberList getAllMembers(TypeElement element) { 410 Symbol sym = cast(Symbol.class, element); 411 WriteableScope scope = sym.members().dupUnshared(); 412 List<Type> closure = types.closure(sym.asType()); 413 for (Type t : closure) 414 addMembers(scope, t); 415 return new FilteredMemberList(scope); 416 } 417 // where 418 private void addMembers(WriteableScope scope, Type type) { 419 members: 420 for (Symbol e : type.asElement().members().getSymbols(NON_RECURSIVE)) { 421 for (Symbol overrider : scope.getSymbolsByName(e.getSimpleName())) { 422 if (overrider.kind == e.kind && (overrider.flags() & Flags.SYNTHETIC) == 0) { 423 if (overrider.getKind() == ElementKind.METHOD && 424 overrides((ExecutableElement)overrider, (ExecutableElement)e, (TypeElement)type.asElement())) { 425 continue members; 426 } 427 } 428 } 429 boolean derived = e.getEnclosingElement() != scope.owner; 430 ElementKind kind = e.getKind(); 431 boolean initializer = kind == ElementKind.CONSTRUCTOR 432 || kind == ElementKind.INSTANCE_INIT 433 || kind == ElementKind.STATIC_INIT; 434 if (!derived || (!initializer && e.isInheritedIn(scope.owner, types))) 435 scope.enter(e); 436 } 437 } 438 439 /** 440 * Returns all annotations of an element, whether 441 * inherited or directly present. 442 * 443 * @param e the element being examined 444 * @return all annotations of the element 445 */ 446 @Override @DefinedBy(Api.LANGUAGE_MODEL) 447 public List<Attribute.Compound> getAllAnnotationMirrors(Element e) { 448 Symbol sym = cast(Symbol.class, e); 449 List<Attribute.Compound> annos = sym.getAnnotationMirrors(); 450 while (sym.getKind() == ElementKind.CLASS) { 451 Type sup = ((ClassSymbol) sym).getSuperclass(); 452 if (!sup.hasTag(CLASS) || sup.isErroneous() || 453 sup.tsym == syms.objectType.tsym) { 454 break; 455 } 456 sym = sup.tsym; 457 List<Attribute.Compound> oldAnnos = annos; 458 List<Attribute.Compound> newAnnos = sym.getAnnotationMirrors(); 459 for (Attribute.Compound anno : newAnnos) { 460 if (isInherited(anno.type) && 461 !containsAnnoOfType(oldAnnos, anno.type)) { 462 annos = annos.prepend(anno); 463 } 464 } 465 } 466 return annos; 467 } 468 469 /** 470 * Tests whether an annotation type is @Inherited. 471 */ 472 private boolean isInherited(Type annotype) { 473 return annotype.tsym.attribute(syms.inheritedType.tsym) != null; 474 } 475 476 /** 477 * Tests whether a list of annotations contains an annotation 478 * of a given type. 479 */ 480 private static boolean containsAnnoOfType(List<Attribute.Compound> annos, 481 Type type) { 482 for (Attribute.Compound anno : annos) { 483 if (anno.type.tsym == type.tsym) 484 return true; 485 } 486 return false; 487 } 488 489 @DefinedBy(Api.LANGUAGE_MODEL) 490 public boolean hides(Element hiderEl, Element hideeEl) { 491 Symbol hider = cast(Symbol.class, hiderEl); 492 Symbol hidee = cast(Symbol.class, hideeEl); 493 494 // Fields only hide fields; methods only methods; types only types. 495 // Names must match. Nothing hides itself (just try it). 496 if (hider == hidee || 497 hider.kind != hidee.kind || 498 hider.name != hidee.name) { 499 return false; 500 } 501 502 // Only static methods can hide other methods. 503 // Methods only hide methods with matching signatures. 504 if (hider.kind == MTH) { 505 if (!hider.isStatic() || 506 !types.isSubSignature(hider.type, hidee.type)) { 507 return false; 508 } 509 } 510 511 // Hider must be in a subclass of hidee's class. 512 // Note that if M1 hides M2, and M2 hides M3, and M3 is accessible 513 // in M1's class, then M1 and M2 both hide M3. 514 ClassSymbol hiderClass = hider.owner.enclClass(); 515 ClassSymbol hideeClass = hidee.owner.enclClass(); 516 if (hiderClass == null || hideeClass == null || 517 !hiderClass.isSubClass(hideeClass, types)) { 518 return false; 519 } 520 521 // Hidee must be accessible in hider's class. 522 // The method isInheritedIn is poorly named: it checks only access. 523 return hidee.isInheritedIn(hiderClass, types); 524 } 525 526 @DefinedBy(Api.LANGUAGE_MODEL) 527 public boolean overrides(ExecutableElement riderEl, 528 ExecutableElement rideeEl, TypeElement typeEl) { 529 MethodSymbol rider = cast(MethodSymbol.class, riderEl); 530 MethodSymbol ridee = cast(MethodSymbol.class, rideeEl); 531 ClassSymbol origin = cast(ClassSymbol.class, typeEl); 532 533 return rider.name == ridee.name && 534 535 // not reflexive as per JLS 536 rider != ridee && 537 538 // we don't care if ridee is static, though that wouldn't 539 // compile 540 !rider.isStatic() && 541 542 // Symbol.overrides assumes the following 543 ridee.isMemberOf(origin, types) && 544 545 // check access and signatures; don't check return types 546 rider.overrides(ridee, origin, types, false); 547 } 548 549 @DefinedBy(Api.LANGUAGE_MODEL) 550 public String getConstantExpression(Object value) { 551 return Constants.format(value); 552 } 553 554 /** 555 * Print a representation of the elements to the given writer in 556 * the specified order. The main purpose of this method is for 557 * diagnostics. The exact format of the output is <em>not</em> 558 * specified and is subject to change. 559 * 560 * @param w the writer to print the output to 561 * @param elements the elements to print 562 */ 563 @DefinedBy(Api.LANGUAGE_MODEL) 564 public void printElements(java.io.Writer w, Element... elements) { 565 for (Element element : elements) 566 (new PrintingProcessor.PrintingElementVisitor(w, this)).visit(element).flush(); 567 } 568 569 @DefinedBy(Api.LANGUAGE_MODEL) 570 public Name getName(CharSequence cs) { 571 return names.fromString(cs.toString()); 572 } 573 574 @Override @DefinedBy(Api.LANGUAGE_MODEL) 575 public boolean isFunctionalInterface(TypeElement element) { 576 if (element.getKind() != ElementKind.INTERFACE) 577 return false; 578 else { 579 TypeSymbol tsym = cast(TypeSymbol.class, element); 580 return types.isFunctionalInterface(tsym); 581 } 582 } 583 584 /** 585 * Returns the tree node and compilation unit corresponding to this 586 * element, or null if they can't be found. 587 */ 588 private Pair<JCTree, JCCompilationUnit> getTreeAndTopLevel(Element e) { 589 Symbol sym = cast(Symbol.class, e); 590 Env<AttrContext> enterEnv = getEnterEnv(sym); 591 if (enterEnv == null) 592 return null; 593 JCTree tree = TreeInfo.declarationFor(sym, enterEnv.tree); 594 if (tree == null || enterEnv.toplevel == null) 595 return null; 596 return new Pair<>(tree, enterEnv.toplevel); 597 } 598 599 /** 600 * Returns the best approximation for the tree node and compilation unit 601 * corresponding to the given element, annotation and value. 602 * If the element is null, null is returned. 603 * If the annotation is null or cannot be found, the tree node and 604 * compilation unit for the element is returned. 605 * If the annotation value is null or cannot be found, the tree node and 606 * compilation unit for the annotation is returned. 607 */ 608 public Pair<JCTree, JCCompilationUnit> getTreeAndTopLevel( 609 Element e, AnnotationMirror a, AnnotationValue v) { 610 if (e == null) 611 return null; 612 613 Pair<JCTree, JCCompilationUnit> elemTreeTop = getTreeAndTopLevel(e); 614 if (elemTreeTop == null) 615 return null; 616 617 if (a == null) 618 return elemTreeTop; 619 620 JCTree annoTree = matchAnnoToTree(a, e, elemTreeTop.fst); 621 if (annoTree == null) 622 return elemTreeTop; 623 624 // 6388543: if v != null, we should search within annoTree to find 625 // the tree matching v. For now, we ignore v and return the tree of 626 // the annotation. 627 return new Pair<>(annoTree, elemTreeTop.snd); 628 } 629 630 /** 631 * Returns a symbol's enter environment, or null if it has none. 632 */ 633 private Env<AttrContext> getEnterEnv(Symbol sym) { 634 // Get enclosing class of sym, or sym itself if it is a class 635 // package, or module. 636 TypeSymbol ts = null; 637 switch (sym.kind) { 638 case PCK: 639 ts = (PackageSymbol)sym; 640 break; 641 case MDL: 642 ts = (ModuleSymbol)sym; 643 break; 644 default: 645 ts = sym.enclClass(); 646 } 647 return (ts != null) 648 ? enter.getEnv(ts) 649 : null; 650 } 651 652 private void ensureEntered(String methodName) { 653 if (javacTaskImpl != null) { 654 javacTaskImpl.ensureEntered(); 655 } 656 if (!javaCompiler.isEnterDone()) { 657 throw new IllegalStateException("Cannot use Elements." + methodName + " before the TaskEvent.Kind.ENTER finished event."); 658 } 659 } 660 661 /** 662 * Returns an object cast to the specified type. 663 * @throws NullPointerException if the object is {@code null} 664 * @throws IllegalArgumentException if the object is of the wrong type 665 */ 666 private static <T> T cast(Class<T> clazz, Object o) { 667 if (! clazz.isInstance(o)) 668 throw new IllegalArgumentException(o.toString()); 669 return clazz.cast(o); 670 } 671} 672