1 package org.incava.doctorj; 2 3 import java.util.*; 4 import net.sourceforge.pmd.ast.*; 5 import org.incava.analysis.Report; 6 import org.incava.java.*; 7 import org.incava.javadoc.*; 8 import org.incava.text.SpellChecker; 9 10 11 14 public class ExceptionDocAnalyzer extends DocAnalyzer 15 { 16 public final static String MSG_EXCEPTION_WITHOUT_CLASS_NAME = "Exception without class name"; 17 18 public final static String MSG_EXCEPTION_WITHOUT_DESCRIPTION = "Exception without description"; 19 20 public final static String MSG_EXCEPTIONS_NOT_ALPHABETICAL = "Exceptions not alphabetical"; 21 22 public final static String MSG_EXCEPTION_NOT_IN_THROWS_LIST = "Exception not in throws list"; 23 24 public final static String MSG_EXCEPTION_MISSPELLED = "Exception misspelled"; 25 26 public final static String MSG_EXCEPTION_NOT_DOCUMENTED = "Exception not documented"; 27 28 protected final static int CHKLVL_EXCEPTIONS_ALPHABETICAL = 2; 29 30 protected final static int CHKLVL_EXCEPTION_DOC_EXISTS = 1; 31 32 private static Map excToRuntime = new HashMap(); 33 34 private static Collection reportedExceptions = new ArrayList(); 35 36 protected final static String [] KNOWN_RUNTIME_EXCEPTIONS = new String [] { 37 "java.awt.color.CMMException", 38 "java.awt.color.ProfileDataException", 39 40 "java.awt.geom.IllegalPathStateException", 41 42 "java.awt.image.ImagingOpException", 43 "java.awt.image.RasterFormatException", 44 45 "java.lang.ArithmeticException", 46 "java.lang.ArrayStoreException", 47 "java.lang.ClassCastException", 48 "java.lang.EnumConstantNotPresentException", 49 "java.lang.IllegalArgumentException", 50 "java.lang.IllegalMonitorStateException", 51 "java.lang.IllegalStateException", 52 "java.lang.IndexOutOfBoundsException", 53 "java.lang.NegativeArraySizeException", 54 "java.lang.NullPointerException", 55 "java.lang.SecurityException", 56 "java.lang.TypeNotPresentException", 57 "java.lang.UnsupportedOperationException", 58 59 "java.lang.annotation.AnnotationTypeMismatchException", 60 "java.lang.annotation.IncompleteAnnotationException", 61 62 "java.lang.reflect.MalformedParameterizedTypeException", 63 "java.lang.reflect.UndeclaredThrowableException", 64 65 "java.nio.BufferOverflowException", 66 "java.nio.BufferUnderflowException", 67 68 "java.security.ProviderException", 69 70 "java.util.ConcurrentModificationException", 71 "java.util.EmptyStackException", 72 "java.util.MissingResourceException", 73 "java.util.NoSuchElementException", 74 75 "java.util.concurrent.RejectedExecutionException", 76 77 "javax.management.JMRuntimeException", 78 79 "javax.print.attribute.UnmodifiableSetException", 80 81 "javax.swing.undo.CannotRedoException", 82 "javax.swing.undo.CannotUndoException", 83 84 "org.omg.CORBA.SystemException", 85 86 "org.w3c.dom.DOMException", 87 88 "org.w3c.dom.events.EventException", 89 90 "org.w3c.dom.ls.LSException", 91 }; 92 93 static { 94 for (int ki = 0; ki < KNOWN_RUNTIME_EXCEPTIONS.length; ++ki) { 95 String ke = KNOWN_RUNTIME_EXCEPTIONS[ki]; 96 excToRuntime.put(ke, Boolean.TRUE); 97 } 98 } 99 100 private JavadocNode _javadoc; 101 102 private ASTNameList _throwsList; 103 104 private SimpleNode _function; 105 106 private List _documentedExceptions = new ArrayList(); 107 108 private int _nodeLevel; 109 110 private Map _importMap; 111 112 119 public ExceptionDocAnalyzer(Report report, JavadocNode javadoc, SimpleNode function, int nodeLevel) 120 { 121 super(report); 122 123 _javadoc = javadoc; 124 _throwsList = FunctionUtil.getThrowsList(function); 125 _function = function; 126 _nodeLevel = nodeLevel; 127 _importMap = null; 128 } 129 130 public void run() 131 { 132 138 boolean alphabeticalReported = false; 139 String previousException = null; 140 141 SimpleNode node = _function; 142 while (node != null && !(node instanceof ASTCompilationUnit)) { 143 node = SimpleNodeUtil.getParent(node); 144 } 145 146 ASTCompilationUnit cu = (ASTCompilationUnit)node; 147 ASTImportDeclaration[] imports = CompilationUnitUtil.getImports(cu); 148 _importMap = makeImportMap(imports); 149 150 JavadocTaggedNode[] taggedComments = _javadoc.getTaggedComments(); 151 for (int ti = 0; ti < taggedComments.length; ++ti) { 152 JavadocTaggedNode jtn = taggedComments[ti]; 153 JavadocTag tag = jtn.getTag(); 154 155 if (tag.text.equals(JavadocTags.EXCEPTION) || tag.text.equals(JavadocTags.THROWS)) { 156 JavadocElement tgt = jtn.getTarget(); 157 158 if (tgt == null) { 159 if (Options.warningLevel >= CHKLVL_TAG_CONTENT + _nodeLevel) { 160 addViolation(MSG_EXCEPTION_WITHOUT_CLASS_NAME, tag.start, tag.end); 161 } 162 } 163 else { 164 if (jtn.getDescriptionNonTarget() == null && Options.warningLevel >= CHKLVL_TAG_CONTENT + _nodeLevel) { 165 addViolation(MSG_EXCEPTION_WITHOUT_DESCRIPTION, tgt.start, tgt.end); 166 } 167 168 String shortName = getShortName(tgt.text); 169 String fullName = tgt.text; 170 Class cls = null; 171 172 if (fullName.indexOf('.') >= 0) { 173 cls = loadClass(fullName); 174 } 175 else { 176 fullName = getExactMatch(fullName); 177 178 if (fullName == null) { 179 Iterator iit = _importMap.keySet().iterator(); 180 while (cls == null && iit.hasNext()) { 181 String impName = (String )iit.next(); 182 String shImpName = getShortName(impName); 183 if (shImpName.equals("*")) { 184 fullName = impName.substring(0, impName.indexOf("*")) + shortName; 186 cls = loadClass(fullName); 187 } 188 else { 189 } 191 } 192 193 if (cls == null) { 194 fullName = "java.lang." + shortName; 196 cls = loadClass(fullName); 197 } 198 } 199 else { 200 cls = loadClass(fullName); 201 } 202 } 203 204 checkAgainstCode(tag, tgt, shortName, fullName, cls); 205 206 if (!alphabeticalReported && 207 Options.warningLevel >= CHKLVL_EXCEPTIONS_ALPHABETICAL + _nodeLevel && 208 previousException != null && previousException.compareTo(shortName) > 0) { 209 210 addViolation(MSG_EXCEPTIONS_NOT_ALPHABETICAL, tgt.start, tgt.end); 211 alphabeticalReported = true; 212 } 213 214 previousException = shortName; 215 } 216 } 217 } 218 219 if (_throwsList != null && Options.warningLevel >= CHKLVL_EXCEPTION_DOC_EXISTS + _nodeLevel) { 220 reportUndocumentedExceptions(); 221 } 222 } 223 224 protected Map makeImportMap(ASTImportDeclaration[] imports) 225 { 226 Map namesToImp = new HashMap(); 227 228 for (int ii = 0; ii < imports.length; ++ii) { 229 ASTImportDeclaration imp = imports[ii]; 230 StringBuffer buf = new StringBuffer (); 231 Token tk = imp.getFirstToken().next; 232 233 while (tk != null) { 234 if (tk == imp.getLastToken()) { 235 break; 236 } 237 else { 238 buf.append(tk.image); 239 tk = tk.next; 240 } 241 } 242 243 namesToImp.put(buf.toString(), imp); 244 } 245 246 return namesToImp; 247 } 248 249 253 protected String getShortName(String name) 254 { 255 int lastDot = name.lastIndexOf('.'); 256 String shName = lastDot == -1 ? name : name.substring(lastDot + 1); 257 return shName; 258 } 259 260 protected String getExactMatch(String name) 261 { 262 Iterator iit = _importMap.keySet().iterator(); 263 while (iit.hasNext()) { 264 String impName = (String )iit.next(); 265 String shImpName = getShortName(impName); 266 if (shImpName.equals("*")) { 267 } 269 else if (shImpName.equals(name)) { 270 return impName; 271 } 272 } 273 274 return null; 275 } 276 277 protected Class loadClass(String clsName) 278 { 279 try { 280 Class cls = Class.forName(clsName); 281 return cls; 282 } 283 catch (Exception e) { 284 return null; 286 } 287 } 288 289 293 protected boolean isRuntimeException(Class excClass) 294 { 295 if (excClass == null) { 296 return false; 297 } 298 else { 299 String excName = excClass.getName(); 300 Boolean val = (Boolean )excToRuntime.get(excName); 301 if (val == null) { 302 val = new Boolean (RuntimeException .class.isAssignableFrom(excClass) || 303 Error .class.isAssignableFrom(excClass)); 304 excToRuntime.put(excName, val); 305 } 306 return val.booleanValue(); 307 } 308 } 309 310 protected void checkAgainstCode(JavadocTag tag, JavadocElement tgt, String shortExcName, String fullExcName, Class excClass) 311 { 312 ASTName name = getMatchingException(shortExcName); 313 if (name == null) { 314 name = getClosestMatchingException(shortExcName); 315 if (name == null) { 316 if (isRuntimeException(excClass)) { 317 } 319 else if (excClass != null || !reportedExceptions.contains(fullExcName)) { 320 addViolation(MSG_EXCEPTION_NOT_IN_THROWS_LIST, tgt.start, tgt.end); 322 323 reportedExceptions.add(fullExcName); 325 } 326 else { 327 } 331 } 332 else { 333 addViolation(MSG_EXCEPTION_MISSPELLED, tgt.start, tgt.end); 335 _documentedExceptions.add(name.getLastToken().image); 336 } 337 } 338 else { 339 _documentedExceptions.add(shortExcName); 340 } 341 } 342 343 protected void reportUndocumentedExceptions() 344 { 345 ASTName[] names = ThrowsUtil.getNames(_throwsList); 346 347 for (int ni = 0; ni < names.length; ++ni) { 348 ASTName name = names[ni]; 349 350 Token nameToken = name.getLastToken(); 352 353 tr.Ace.log("considering name: " + name + " (" + nameToken + ")"); 354 if (!_documentedExceptions.contains(nameToken.image)) { 355 addViolation(MSG_EXCEPTION_NOT_DOCUMENTED, 356 nameToken.beginLine, nameToken.beginColumn, 357 nameToken.beginLine, nameToken.beginColumn + nameToken.image.length() - 1); 358 } 359 } 360 } 361 362 365 protected ASTName getMatchingException(String str) 366 { 367 if (_throwsList == null) { 368 return null; 369 } 370 else { 371 ASTName[] names = ThrowsUtil.getNames(_throwsList); 372 373 for (int ni = 0; ni < names.length; ++ni) { 374 ASTName name = names[ni]; 375 Token nameToken = name.getLastToken(); 377 tr.Ace.log("considering name: " + name + " (" + nameToken + ")"); 378 if (nameToken.image.equals(str)) { 379 return name; 380 } 381 } 382 tr.Ace.log("no exact match for '" + str + "'"); 383 return null; 384 } 385 } 386 387 390 protected ASTName getClosestMatchingException(String str) 391 { 392 if (_throwsList == null) { 393 return null; 394 } 395 else { 396 SpellChecker spellChecker = new SpellChecker(); 397 int bestDistance = -1; 398 ASTName bestName = null; 399 ASTName[] names = ThrowsUtil.getNames(_throwsList); 400 401 for (int ni = 0; ni < names.length; ++ni) { 402 ASTName name = names[ni]; 403 Token nameToken = name.getLastToken(); 404 int dist = spellChecker.editDistance(nameToken.image, str); 405 406 if (dist >= 0 && dist <= SpellChecker.DEFAULT_MAX_DISTANCE && (bestDistance == -1 || dist < bestDistance)) { 407 bestDistance = dist; 408 bestName = name; 409 } 410 } 411 412 return bestName; 413 } 414 } 415 416 } 417
| Popular Tags
|