1 package jdiff; 2 3 import java.io.*; 4 import java.util.*; 5 6 /** 7 * The internal representation of an API. 8 * 9 * RootDoc could have been used for representing this, but 10 * you cannot serialize a RootDoc object - see 11 * http://developer.java.sun.com/developer/bugParade/bugs/4125581.html 12 * You might be able use Javadoc.Main() to create another RootDoc, but the 13 * methods are package private. You can run javadoc in J2SE1.4, see: 14 * http://java.sun.com/j2se/1.4/docs/tooldocs/javadoc/standard-doclet.html#runningprogrammatically 15 * but you still can't get the RootDoc object. 16 * 17 * The advantage of writing out an XML representation of each API is that 18 * later runs of JDiff don't have to have Javadoc scan all the files again, 19 * a possibly lengthy process. XML also permits other source code in 20 * languages other than Java to be scanned to produce XML, and then versions 21 * of JDiff can be used to create documents describing the difference in those 22 * APIs. 23 * 24 * See the file LICENSE.txt for copyright details. 25 * @author Matthew Doar, mdoar (at) pobox.com 26 */ 27 public class API { 28 29 /** 30 * The list of all the top-level packages. 31 * Each package contains classes, each class contains members, and so on. 32 */ 33 public List packages_; // PackageAPI[] 34 35 /** 36 * The list of all the classes. 37 * This is used to generate the methods and fields which are inherited, 38 * rather than storing them in the XML file. 39 */ 40 public Hashtable classes_; 41 42 /** 43 * The String which identifies this API, e.g. "SuperProduct 1.3". 44 */ 45 public String name_ = null; 46 47 /** The current package being added to during parsing. */ 48 public PackageAPI currPkg_ = null; 49 /** The current class being added to during parsing. */ 50 public ClassAPI currClass_ = null; 51 /** The current constructor being added to during parsing. */ 52 public ConstructorAPI currCtor_ = null; 53 /** The current method being added to during parsing. */ 54 public MethodAPI currMethod_ = null; 55 /** The current field being added to during parsing. */ 56 public FieldAPI currField_ = null; 57 58 /** Default constructor. */ 59 public API() { 60 packages_ = new ArrayList(); //PackageAPI[] 61 classes_ = new Hashtable(); //ClassAPI 62 } 63 64 // 65 // Methods to display the contents of an API object. 66 // 67 68 /** Amount by which to increment each indentation. */ 69 public static final int indentInc = 2; 70 71 /** Display the contents of the API object. */ 72 public void dump() { 73 int indent = 0; 74 Iterator iter = packages_.iterator(); 75 while (iter.hasNext()) { 76 dumpPackage((PackageAPI)(iter.next()), indent); 77 } 78 } 79 80 /** 81 * Display the contents of a PackageAPI object. 82 * 83 * @param pkg The given PackageAPI object. 84 * @param indent The number of spaces to indent the output. 85 */ 86 public void dumpPackage(PackageAPI pkg, int indent) { 87 for (int i = 0; i < indent; i++) System.out.print(" "); 88 System.out.println("Package Name: " + pkg.name_); 89 Iterator iter = pkg.classes_.iterator(); 90 while (iter.hasNext()) { 91 dumpClass((ClassAPI)(iter.next()), indent + indentInc); 92 } 93 // Display documentation 94 if (pkg.doc_ != null) { 95 System.out.print("Package doc block:"); 96 System.out.println("\"" + pkg.doc_ + "\""); 97 } 98 } 99 100 /** 101 * Display the contents of a ClassAPI object. 102 * 103 * @param c The given ClassAPI object. 104 * @param indent The number of spaces to indent the output. 105 */ 106 public static void dumpClass(ClassAPI c, int indent) { 107 for (int i = 0; i < indent; i++) System.out.print(" "); 108 if (c.isInterface_) 109 System.out.println("Interface name: " + c.name_); 110 else 111 System.out.println("Class Name: " + c.name_); 112 if (c.extends_ != null) { 113 for (int i = 0; i < indent; i++) System.out.print(" "); 114 System.out.println("Extends: " + c.extends_); 115 } 116 if (c.implements_.size() != 0) { 117 for (int i = 0; i < indent; i++) System.out.print(" "); 118 System.out.println("Implements: "); 119 Iterator iter = c.implements_.iterator(); 120 while (iter.hasNext()) { 121 String interfaceImpl = (String)(iter.next()); 122 for (int i = 0; i < indent + 2; i++) System.out.print(" "); 123 System.out.println(" " + interfaceImpl); 124 } 125 } 126 // Dump modifiers specific to a class 127 if (c.isAbstract_) 128 System.out.print("abstract "); 129 // Dump modifiers common to all 130 dumpModifiers(c.modifiers_, indent); 131 // Dump ctors 132 Iterator iter = c.ctors_.iterator(); 133 while (iter.hasNext()) { 134 dumpCtor((ConstructorAPI)(iter.next()), indent + indentInc); 135 } 136 // Dump methods 137 iter = c.methods_.iterator(); 138 while (iter.hasNext()) { 139 dumpMethod((MethodAPI)(iter.next()), indent + indentInc); 140 } 141 // Dump fields 142 iter = c.fields_.iterator(); 143 while (iter.hasNext()) { 144 dumpField((FieldAPI)(iter.next()), indent + indentInc); 145 } 146 // Display documentation 147 if (c.doc_ != null) { 148 System.out.print("Class doc block:"); 149 System.out.println("\"" + c.doc_ + "\""); 150 } else 151 System.out.println(); 152 } 153 154 /** 155 * Display the contents of the Modifiers object. 156 * 157 * @param c The given Modifiers object. 158 * @param indent The number of spaces to indent the output. 159 */ 160 public static void dumpModifiers(Modifiers m, int indent) { 161 for (int i = 0; i < indent; i++) System.out.print(" "); 162 if (m.isStatic) 163 System.out.print("static "); 164 if (m.isFinal) 165 System.out.print("final "); 166 if (m.visibility != null) 167 System.out.print("visibility = " + m.visibility + " "); 168 // Flush the line 169 System.out.println(); 170 } 171 172 /** 173 * Display the contents of a constructor. 174 * 175 * @param c The given constructor object. 176 * @param indent The number of spaces to indent the output. 177 */ 178 public static void dumpCtor(ConstructorAPI c, int indent) { 179 for (int i = 0; i < indent; i++) System.out.print(" "); 180 System.out.println("Ctor type: " + c.type_); 181 // Display exceptions 182 System.out.print("exceptions: " + c.exceptions_ + " "); 183 // Dump modifiers common to all 184 dumpModifiers(c.modifiers_, indent); 185 // Display documentation 186 if (c.doc_ != null) { 187 System.out.print("Ctor doc block:"); 188 System.out.println("\"" + c.doc_ + "\""); 189 } 190 } 191 192 /** 193 * Display the contents of a MethodAPI object. 194 * 195 * @param m The given MethodAPI object. 196 * @param indent The number of spaces to indent the output. 197 */ 198 public static void dumpMethod(MethodAPI m, int indent) { 199 if (m.inheritedFrom_ != null) 200 return; 201 for (int i = 0; i < indent; i++) System.out.print(" "); 202 System.out.print("Method Name: " + m.name_); 203 if (m.inheritedFrom_ != null) 204 System.out.println(", inherited from: " + m.inheritedFrom_); 205 if (m.returnType_ != null) 206 System.out.println(", return type: " + m.returnType_); 207 else 208 System.out.println(); 209 // Dump modifiers specific to a method 210 if (m.isAbstract_) 211 System.out.print("abstract "); 212 if (m.isNative_) 213 System.out.print("native "); 214 if (m.isSynchronized_) 215 System.out.print("synchronized "); 216 // Display exceptions 217 System.out.print("exceptions: " + m.exceptions_ + " "); 218 // Dump modifiers common to all 219 dumpModifiers(m.modifiers_, indent); 220 221 Iterator iter = m.params_.iterator(); 222 while (iter.hasNext()) { 223 dumpParam((ParamAPI)(iter.next()), indent + indentInc); 224 } 225 // Display documentation 226 if (m.doc_ != null) { 227 System.out.print("Method doc block:"); 228 System.out.println("\"" + m.doc_ + "\""); 229 } 230 } 231 232 /** 233 * Display the contents of a field. 234 * Does not show inherited fields. 235 * 236 * @param f The given field object. 237 * @param indent The number of spaces to indent the output. 238 */ 239 public static void dumpField(FieldAPI f, int indent) { 240 if (f.inheritedFrom_ != null) 241 return; 242 for (int i = 0; i < indent; i++) System.out.print(" "); 243 System.out.println("Field Name: " + f.name_ + ", type: " + f.type_); 244 if (f.inheritedFrom_ != null) 245 System.out.println(", inherited from: " + f.inheritedFrom_); 246 if (f.isTransient_) 247 System.out.print("transient "); 248 if (f.isVolatile_) 249 System.out.print("volatile "); 250 // Dump modifiers common to all 251 dumpModifiers(f.modifiers_, indent); 252 // Display documentation 253 if (f.doc_ != null) 254 System.out.print("Field doc block:"); 255 System.out.println("\"" + f.doc_ + "\""); 256 } 257 258 /** 259 * Display the contents of a parameter. 260 * 261 * @param p The given parameter object. 262 * @param indent The number of spaces to indent the output. 263 */ 264 public static void dumpParam(ParamAPI p, int indent) { 265 for (int i = 0; i < indent; i++) System.out.print(" "); 266 System.out.println("Param Name: " + p.name_ + ", type: " + p.type_); 267 } 268 269 /** 270 * Convert all HTML tags to text by placing them inside a CDATA element. 271 * Characters still have to be valid Unicode characters as defined by the 272 * parser. 273 */ 274 public static String stuffHTMLTags(String htmlText) { 275 if (htmlText.indexOf("]]>") != -1) { 276 System.out.println("Warning: illegal string ]]> found in text. Ignoring the comment."); 277 return ""; 278 } 279 return "<![CDATA[" + htmlText + "]]>"; 280 } 281 282 /** 283 * Convert all HTML tags to text by stuffing text into the HTML tag 284 * to stop it being an HTML or XML tag. E.g. "<code>foo</code>" 285 * becomes "lEsS_tHaNcode>foolEsS_tHaN/code>". Replace all < 286 * characters 287 * with the string "lEsS_tHaN". Also replace & character with the 288 * string "aNd_cHaR" to avoid text entities. Also replace " 289 * character with the 290 * string "qUoTe_cHaR". 291 */ 292 public static String hideHTMLTags(String htmlText) { 293 StringBuffer sb = new StringBuffer(htmlText); 294 int i = 0; 295 while (i < sb.length()) { 296 if (sb.charAt(i) == '<') { 297 sb.setCharAt(i ,'l'); 298 sb.insert(i+1, "EsS_tHaN"); 299 } else if (sb.charAt(i) == '&') { 300 sb.setCharAt(i ,'a'); 301 sb.insert(i+1, "Nd_cHaR"); 302 } else if (sb.charAt(i) == '"') { 303 sb.setCharAt(i ,'q'); 304 sb.insert(i+1, "uote_cHaR"); 305 } 306 i++; 307 } 308 return sb.toString(); 309 } 310 311 /** 312 * Convert text with stuffed HTML tags ("lEsS_tHaN", etc) into HTML text. 313 */ 314 public static String showHTMLTags(String text) { 315 StringBuffer sb = new StringBuffer(text); 316 StringBuffer res = new StringBuffer(); 317 int len = sb.length(); 318 res.setLength(len); 319 int i = 0; 320 int resIdx = 0; 321 while (i < len) { 322 char c = sb.charAt(i); 323 if (len - i > 8 && c == 'l' && 324 sb.charAt(i+1) == 'E' && 325 sb.charAt(i+2) == 's' && 326 sb.charAt(i+3) == 'S' && 327 sb.charAt(i+4) == '_' && 328 sb.charAt(i+5) == 't' && 329 sb.charAt(i+6) == 'H' && 330 sb.charAt(i+7) == 'a' && 331 sb.charAt(i+8) == 'N') { 332 res.setCharAt(resIdx ,'<'); 333 i += 8; 334 } else if (len - i > 9 && c == 'q' && 335 sb.charAt(i+1) == 'U' && 336 sb.charAt(i+2) == 'o' && 337 sb.charAt(i+3) == 'T' && 338 sb.charAt(i+4) == 'e' && 339 sb.charAt(i+5) == '_' && 340 sb.charAt(i+6) == 'c' && 341 sb.charAt(i+7) == 'H' && 342 sb.charAt(i+8) == 'a' && 343 sb.charAt(i+9) == 'R') { 344 res.setCharAt(resIdx ,'"'); 345 i += 9; 346 } else if (len - i > 7 && c == 'a' && 347 sb.charAt(i+1) == 'N' && 348 sb.charAt(i+2) == 'd' && 349 sb.charAt(i+3) == '_' && 350 sb.charAt(i+4) == 'c' && 351 sb.charAt(i+5) == 'H' && 352 sb.charAt(i+6) == 'a' && 353 sb.charAt(i+7) == 'R') { 354 res.setCharAt(resIdx ,'&'); 355 i += 7; 356 } else { 357 res.setCharAt(resIdx, c); 358 } 359 i++; 360 resIdx++; 361 } 362 res.setLength(resIdx); 363 return res.toString(); 364 } 365 366 /** 367 * <b>NOT USED</b>. 368 * 369 * Replace all instances of <p> with <p/>. Just for the small number 370 * of HMTL tags which don't require a matching end tag. 371 * Also make HTML conform to the simple HTML requirements such as 372 * no double hyphens. Double hyphens are replaced by - and the character 373 * entity for a hyphen. 374 * 375 * Cases where this fails and has to be corrected in the XML by hand: 376 * Attributes' values missing their double quotes , e.g. size=-2 377 * Mangled HTML tags e.g. <ttt> 378 * 379 * <p><b>NOT USED</b>. There is often too much bad HTML in 380 * doc blocks to try to handle every case correctly. Better just to 381 * stuff the *lt; and &: characters with stuffHTMLTags(). Though 382 * the resulting XML is not as elegant, it does the job with less 383 * intervention by the user. 384 */ 385 public static String convertHTMLTagsToXHTML(String htmlText) { 386 StringBuffer sb = new StringBuffer(htmlText); 387 int i = 0; 388 boolean inTag = false; 389 String tag = null; 390 // Needs to re-evaluate this length at each loop 391 while (i < sb.length()) { 392 char c = sb.charAt(i); 393 if (inTag) { 394 if (c == '>') { 395 // OPTION Could fail at or fix some errorneous tags here 396 // Make the best guess as to whether this tag is terminated 397 if (Comments.isMinimizedTag(tag) && 398 htmlText.indexOf("</" + tag + ">", i) == -1) 399 sb.insert(i, "/"); 400 inTag = false; 401 } else { 402 // OPTION could also make sure that attribute values are 403 // surrounded by quotes. 404 tag += c; 405 } 406 } 407 if (c == '<') { 408 inTag = true; 409 tag = ""; 410 } 411 // -- is not allowed in XML, but !-- is part of an comment, 412 // and --> is also part of a comment 413 if (c == '-' && i > 0 && sb.charAt(i-1) == '-') { 414 if (!(i > 1 && sb.charAt(i-2) == '!')) { 415 sb.setCharAt(i, '&'); 416 sb.insert(i+1, "#045;"); 417 i += 5; 418 } 419 } 420 i++; 421 } 422 if (inTag) { 423 // Oops. Someone forgot to close their HTML tag, e.g. "<code." 424 // Close it for them. 425 sb.insert(i, ">"); 426 } 427 return sb.toString(); 428 } 429 } 430