1 package com.puppycrawl.tools.checkstyle.checks; 20 21 import com.puppycrawl.tools.checkstyle.api.Check; 22 import com.puppycrawl.tools.checkstyle.api.DetailAST; 23 import com.puppycrawl.tools.checkstyle.api.FullIdent; 24 import com.puppycrawl.tools.checkstyle.api.LocalizedMessage; 25 import com.puppycrawl.tools.checkstyle.api.TokenTypes; 26 import java.util.HashSet ; 27 import java.util.HashMap ; 28 import java.util.Map ; 29 import java.util.Set ; 30 import java.util.Vector ; 31 32 40 public abstract class AbstractTypeAwareCheck extends Check 41 { 42 43 private final Set mImports = new HashSet (); 44 45 46 private FullIdent mPackageFullIdent; 47 48 49 private String mCurrentClass; 50 51 52 private ClassResolver mClassResolver; 53 54 55 private final Vector mTypeParams = new Vector (); 56 57 69 private boolean mLogLoadErrors; 70 71 77 public final void setLogLoadErrors(boolean aLogLoadErrors) 78 { 79 mLogLoadErrors = aLogLoadErrors; 80 } 81 82 86 private boolean mSuppressLoadErrors; 87 88 93 public final void setSuppressLoadErrors(boolean aSuppressLoadErrors) 94 { 95 mSuppressLoadErrors = aSuppressLoadErrors; 96 } 97 98 103 protected abstract void processAST(DetailAST aAST); 104 105 106 public final int[] getRequiredTokens() 107 { 108 return new int[] { 109 TokenTypes.PACKAGE_DEF, 110 TokenTypes.IMPORT, 111 TokenTypes.CLASS_DEF, 112 TokenTypes.ENUM_DEF, 113 }; 114 } 115 116 117 public void beginTree(DetailAST aRootAST) 118 { 119 mPackageFullIdent = FullIdent.createFullIdent(null); 120 mImports.clear(); 121 mImports.add("java.lang.*"); 123 mClassResolver = null; 124 mCurrentClass = ""; 125 mTypeParams.clear(); 126 } 127 128 129 public final void visitToken(DetailAST aAST) 130 { 131 if (aAST.getType() == TokenTypes.PACKAGE_DEF) { 132 processPackage(aAST); 133 } 134 else if (aAST.getType() == TokenTypes.IMPORT) { 135 processImport(aAST); 136 } 137 else if ((aAST.getType() == TokenTypes.CLASS_DEF) 138 || (aAST.getType() == TokenTypes.ENUM_DEF)) 139 { 140 processClass(aAST); 141 } 142 else { 143 if (aAST.getType() == TokenTypes.METHOD_DEF) { 144 processTypeParams(aAST); 145 } 146 processAST(aAST); 147 } 148 } 149 150 151 public final void leaveToken(DetailAST aAST) 152 { 153 if ((aAST.getType() == TokenTypes.CLASS_DEF) 154 || (aAST.getType() == TokenTypes.ENUM_DEF)) 155 { 156 int dotIdx = mCurrentClass.lastIndexOf("$"); 158 if (dotIdx == -1) { 159 dotIdx = mCurrentClass.lastIndexOf("."); 161 } 162 if (dotIdx == -1) { 163 mCurrentClass = ""; 165 } 166 else { 167 mCurrentClass = mCurrentClass.substring(0, dotIdx); 168 } 169 mTypeParams.remove(mTypeParams.size() - 1); 170 } 171 else if (aAST.getType() == TokenTypes.METHOD_DEF) { 172 mTypeParams.remove(mTypeParams.size() - 1); 173 } 174 else if ((aAST.getType() != TokenTypes.PACKAGE_DEF) 175 && (aAST.getType() != TokenTypes.IMPORT)) 176 { 177 leaveAST(aAST); 178 } 179 } 180 181 187 protected void leaveAST(DetailAST aAST) 188 { 189 } 190 191 199 protected boolean isUnchecked(Class aException) 200 { 201 return isSubclass(aException, RuntimeException .class) 202 || isSubclass(aException, Error .class); 203 } 204 205 215 protected boolean isSubclass(Class aChild, Class aParent) 216 { 217 return (aParent != null) && (aChild != null) 218 && aParent.isAssignableFrom(aChild); 219 } 220 221 222 private ClassResolver getClassResolver() 223 { 224 if (mClassResolver == null) { 225 mClassResolver = 226 new ClassResolver(getClassLoader(), 227 mPackageFullIdent.getText(), 228 mImports); 229 } 230 return mClassResolver; 231 } 232 233 240 protected final Class resolveClass(String aClassName, String aCurrentClass) 241 { 242 try { 243 return getClassResolver().resolve(aClassName, aCurrentClass); 244 } 245 catch (final ClassNotFoundException e) { 246 return null; 247 } 248 } 249 250 256 protected final Class tryLoadClass(Token aIdent, String aCurrentClass) 257 { 258 final Class clazz = resolveClass(aIdent.getText(), aCurrentClass); 259 if (clazz == null) { 260 logLoadError(aIdent); 261 } 262 return clazz; 263 } 264 265 270 protected abstract void logLoadError(Token aIdent); 271 272 279 protected final void logLoadErrorImpl(int aLineNo, int aColumnNo, 280 String aMsgKey, Object [] aValues) 281 { 282 if (!mLogLoadErrors) { 283 final LocalizedMessage msg = new LocalizedMessage(aLineNo, 284 aColumnNo, 285 getMessageBundle(), 286 aMsgKey, 287 aValues, 288 getSeverityLevel(), 289 getId(), 290 this.getClass()); 291 throw new RuntimeException (msg.getMessage()); 292 } 293 294 if (!mSuppressLoadErrors) { 295 log(aLineNo, aColumnNo, aMsgKey, aValues); 296 } 297 } 298 299 303 private void processPackage(DetailAST aAST) 304 { 305 final DetailAST nameAST = aAST.getLastChild().getPreviousSibling(); 306 mPackageFullIdent = FullIdent.createFullIdent(nameAST); 307 } 308 309 313 private void processImport(DetailAST aAST) 314 { 315 final FullIdent name = FullIdent.createFullIdentBelow(aAST); 316 if (name != null) { 317 mImports.add(name.getText()); 318 } 319 } 320 321 325 private void processTypeParams(DetailAST aAST) 326 { 327 final DetailAST typeParams = 328 aAST.findFirstToken(TokenTypes.TYPE_PARAMETERS); 329 330 final Map paramsMap = new HashMap (); 331 mTypeParams.add(paramsMap); 332 333 if (typeParams == null) { 334 return; 335 } 336 337 for (DetailAST child = (DetailAST) typeParams.getFirstChild(); 338 child != null; 339 child = (DetailAST) child.getNextSibling()) 340 { 341 if (child.getType() == TokenTypes.TYPE_PARAMETER) { 342 final DetailAST param = child; 343 final String alias = 344 param.findFirstToken(TokenTypes.IDENT).getText(); 345 final DetailAST bounds = 346 param.findFirstToken(TokenTypes.TYPE_UPPER_BOUNDS); 347 if (bounds != null) { 348 final FullIdent name = 349 FullIdent.createFullIdentBelow(bounds); 350 final ClassInfo ci = 351 createClassInfo(new Token(name), getCurrentClassName()); 352 paramsMap.put(alias, ci); 353 } 354 } 355 } 356 } 357 358 362 private void processClass(DetailAST aAST) 363 { 364 final DetailAST ident = aAST.findFirstToken(TokenTypes.IDENT); 365 mCurrentClass += ("".equals(mCurrentClass) ? "" : "$") 366 + ident.getText(); 367 368 processTypeParams(aAST); 369 } 370 371 375 protected final String getCurrentClassName() 376 { 377 return mCurrentClass; 378 } 379 380 386 protected final ClassInfo createClassInfo(final Token aName, 387 final String aSurroundingClass) 388 { 389 final ClassInfo ci = findClassAlias(aName.getText()); 390 if (ci != null) { 391 return new ClassAlias(aName, ci); 392 } 393 return new RegularClass(aName, aSurroundingClass, this); 394 } 395 396 401 protected final ClassInfo findClassAlias(final String aName) 402 { 403 ClassInfo ci = null; 404 for (int i = mTypeParams.size() - 1; i >= 0; i--) { 405 final Map paramMap = (Map ) mTypeParams.get(i); 406 ci = (ClassInfo) paramMap.get(aName); 407 if (ci != null) { 408 break; 409 } 410 } 411 return ci; 412 } 413 414 417 protected abstract static class ClassInfo 418 { 419 420 private final Token mName; 421 422 423 public final Token getName() 424 { 425 return mName; 426 } 427 428 429 public abstract Class getClazz(); 430 431 435 protected ClassInfo(final Token aName) 436 { 437 if (aName == null) { 438 throw new NullPointerException ( 439 "ClassInfo's name should be non-null"); 440 } 441 mName = aName; 442 } 443 } 444 445 446 private static final class RegularClass extends ClassInfo 447 { 448 449 private String mSurroundingClass; 450 451 private boolean mIsLoadable = true; 452 453 private Class mClass; 454 455 private final AbstractTypeAwareCheck mCheck; 456 457 463 private RegularClass(final Token aName, 464 final String aSurroundingClass, 465 final AbstractTypeAwareCheck aCheck) 466 { 467 super(aName); 468 mSurroundingClass = aSurroundingClass; 469 mCheck = aCheck; 470 } 471 472 private boolean isLoadable() 473 { 474 return mIsLoadable; 475 } 476 477 478 public Class getClazz() 479 { 480 if (isLoadable() && (mClass == null)) { 481 setClazz(mCheck.tryLoadClass(getName(), mSurroundingClass)); 482 } 483 return mClass; 484 } 485 486 490 private void setClazz(Class aClass) 491 { 492 mClass = aClass; 493 mIsLoadable = (mClass != null); 494 } 495 496 497 public String toString() 498 { 499 return "RegularClass[name=" + getName() 500 + ", in class=" + mSurroundingClass 501 + ", loadable=" + mIsLoadable 502 + ", class=" + mClass + "]"; 503 } 504 } 505 506 507 private static class ClassAlias extends ClassInfo 508 { 509 510 private final ClassInfo mClassInfo; 511 512 517 ClassAlias(final Token aName, ClassInfo aClassInfo) 518 { 519 super(aName); 520 mClassInfo = aClassInfo; 521 } 522 523 524 public final Class getClazz() 525 { 526 return mClassInfo.getClazz(); 527 } 528 529 530 public String toString() 531 { 532 return "ClassAlias[alias " + getName() 533 + " for " + mClassInfo + "]"; 534 } 535 } 536 537 540 protected static class Token 541 { 542 543 private final int mColumn; 544 545 private final int mLine; 546 547 private final String mText; 548 549 555 public Token(String aText, int aLine, int aColumn) 556 { 557 mText = aText; 558 mLine = aLine; 559 mColumn = aColumn; 560 } 561 562 566 public Token(FullIdent aFullIdent) 567 { 568 mText = aFullIdent.getText(); 569 mLine = aFullIdent.getLineNo(); 570 mColumn = aFullIdent.getColumnNo(); 571 } 572 573 574 public int getLineNo() 575 { 576 return mLine; 577 } 578 579 580 public int getColumnNo() 581 { 582 return mColumn; 583 } 584 585 586 public String getText() 587 { 588 return mText; 589 } 590 591 592 public String toString() 593 { 594 return "Token[" + getText() + "(" + getLineNo() 595 + "x" + getColumnNo() + ")]"; 596 } 597 } 598 } 599 | Popular Tags |