test/tools/javac/doctree/DocCommentTester.java

Wed, 14 Nov 2012 17:23:10 -0800

author
jjg
date
Wed, 14 Nov 2012 17:23:10 -0800
changeset 1409
33abf479f202
child 1419
a25c53e12bd0
permissions
-rw-r--r--

7021614: extend com.sun.source API to support parsing javadoc comments
Reviewed-by: ksrini, strarup

jjg@1409 1 /*
jjg@1409 2 * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
jjg@1409 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
jjg@1409 4 *
jjg@1409 5 * This code is free software; you can redistribute it and/or modify it
jjg@1409 6 * under the terms of the GNU General Public License version 2 only, as
jjg@1409 7 * published by the Free Software Foundation.
jjg@1409 8 *
jjg@1409 9 * This code is distributed in the hope that it will be useful, but WITHOUT
jjg@1409 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
jjg@1409 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
jjg@1409 12 * version 2 for more details (a copy is included in the LICENSE file that
jjg@1409 13 * accompanied this code).
jjg@1409 14 *
jjg@1409 15 * You should have received a copy of the GNU General Public License version
jjg@1409 16 * 2 along with this work; if not, write to the Free Software Foundation,
jjg@1409 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
jjg@1409 18 *
jjg@1409 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
jjg@1409 20 * or visit www.oracle.com if you need additional information or have any
jjg@1409 21 * questions.
jjg@1409 22 */
jjg@1409 23
jjg@1409 24 import java.io.File;
jjg@1409 25 import java.io.FileWriter;
jjg@1409 26 import java.io.IOException;
jjg@1409 27 import java.io.PrintWriter;
jjg@1409 28 import java.io.StringWriter;
jjg@1409 29 import java.io.Writer;
jjg@1409 30 import java.util.ArrayList;
jjg@1409 31 import java.util.List;
jjg@1409 32 import java.util.regex.Matcher;
jjg@1409 33 import java.util.regex.Pattern;
jjg@1409 34
jjg@1409 35 import javax.lang.model.element.Name;
jjg@1409 36 import javax.tools.JavaFileObject;
jjg@1409 37 import javax.tools.StandardJavaFileManager;
jjg@1409 38
jjg@1409 39 import com.sun.source.doctree.*;
jjg@1409 40 import com.sun.source.tree.ClassTree;
jjg@1409 41 import com.sun.source.tree.CompilationUnitTree;
jjg@1409 42 import com.sun.source.tree.MethodTree;
jjg@1409 43 import com.sun.source.tree.Tree;
jjg@1409 44 import com.sun.source.tree.VariableTree;
jjg@1409 45 import com.sun.source.util.DocTreeScanner;
jjg@1409 46 import com.sun.source.util.DocTrees;
jjg@1409 47 import com.sun.source.util.JavacTask;
jjg@1409 48 import com.sun.source.util.TreePath;
jjg@1409 49 import com.sun.source.util.TreePathScanner;
jjg@1409 50 import com.sun.tools.javac.api.JavacTool;
jjg@1409 51 import com.sun.tools.javac.tree.DCTree;
jjg@1409 52 import com.sun.tools.javac.tree.DCTree.DCDocComment;
jjg@1409 53 import com.sun.tools.javac.tree.DCTree.DCErroneous;
jjg@1409 54 import com.sun.tools.javac.tree.DocPretty;
jjg@1409 55
jjg@1409 56 public class DocCommentTester {
jjg@1409 57
jjg@1409 58 public static void main(String... args) throws Exception {
jjg@1409 59 new DocCommentTester().run(args);
jjg@1409 60 }
jjg@1409 61
jjg@1409 62 public void run(String... args) throws Exception {
jjg@1409 63 String testSrc = System.getProperty("test.src");
jjg@1409 64
jjg@1409 65 List<File> files = new ArrayList<File>();
jjg@1409 66 for (String arg: args)
jjg@1409 67 files.add(new File(testSrc, arg));
jjg@1409 68
jjg@1409 69 JavacTool javac = JavacTool.create();
jjg@1409 70 StandardJavaFileManager fm = javac.getStandardFileManager(null, null, null);
jjg@1409 71
jjg@1409 72 Iterable<? extends JavaFileObject> fos = fm.getJavaFileObjectsFromFiles(files);
jjg@1409 73
jjg@1409 74 JavacTask t = javac.getTask(null, fm, null, null, null, fos);
jjg@1409 75 final DocTrees trees = DocTrees.instance(t);
jjg@1409 76
jjg@1409 77 final Checker[] checkers = {
jjg@1409 78 new ASTChecker(this, trees),
jjg@1409 79 new PosChecker(this, trees),
jjg@1409 80 new PrettyChecker(this, trees)
jjg@1409 81 };
jjg@1409 82
jjg@1409 83 DeclScanner d = new DeclScanner() {
jjg@1409 84 @Override
jjg@1409 85 public Void visitCompilationUnit(CompilationUnitTree tree, Void ignore) {
jjg@1409 86 for (Checker c: checkers)
jjg@1409 87 c.visitCompilationUnit(tree);
jjg@1409 88 return super.visitCompilationUnit(tree, ignore);
jjg@1409 89 }
jjg@1409 90
jjg@1409 91 @Override
jjg@1409 92 void visitDecl(Tree tree, Name name) {
jjg@1409 93 TreePath path = getCurrentPath();
jjg@1409 94 String dc = trees.getDocComment(path);
jjg@1409 95 if (dc != null) {
jjg@1409 96 for (Checker c : checkers) {
jjg@1409 97 try {
jjg@1409 98 System.err.println(path.getLeaf().getKind()
jjg@1409 99 + " " + name
jjg@1409 100 + " " + c.getClass().getSimpleName());
jjg@1409 101
jjg@1409 102 c.check(path, name);
jjg@1409 103
jjg@1409 104 System.err.println();
jjg@1409 105 } catch (Exception e) {
jjg@1409 106 error("Exception " + e);
jjg@1409 107 e.printStackTrace(System.err);
jjg@1409 108 }
jjg@1409 109 }
jjg@1409 110 }
jjg@1409 111 }
jjg@1409 112 };
jjg@1409 113
jjg@1409 114 Iterable<? extends CompilationUnitTree> units = t.parse();
jjg@1409 115 for (CompilationUnitTree unit: units) {
jjg@1409 116 d.scan(unit, null);
jjg@1409 117 }
jjg@1409 118
jjg@1409 119 if (errors > 0)
jjg@1409 120 throw new Exception(errors + " errors occurred");
jjg@1409 121 }
jjg@1409 122
jjg@1409 123 static abstract class DeclScanner extends TreePathScanner<Void, Void> {
jjg@1409 124 abstract void visitDecl(Tree tree, Name name);
jjg@1409 125
jjg@1409 126 @Override
jjg@1409 127 public Void visitClass(ClassTree tree, Void ignore) {
jjg@1409 128 super.visitClass(tree, ignore);
jjg@1409 129 visitDecl(tree, tree.getSimpleName());
jjg@1409 130 return null;
jjg@1409 131 }
jjg@1409 132
jjg@1409 133 @Override
jjg@1409 134 public Void visitMethod(MethodTree tree, Void ignore) {
jjg@1409 135 super.visitMethod(tree, ignore);
jjg@1409 136 visitDecl(tree, tree.getName());
jjg@1409 137 return null;
jjg@1409 138 }
jjg@1409 139
jjg@1409 140 @Override
jjg@1409 141 public Void visitVariable(VariableTree tree, Void ignore) {
jjg@1409 142 super.visitVariable(tree, ignore);
jjg@1409 143 visitDecl(tree, tree.getName());
jjg@1409 144 return null;
jjg@1409 145 }
jjg@1409 146 }
jjg@1409 147
jjg@1409 148 /**
jjg@1409 149 * Base class for checkers to check the doc comment on a declaration
jjg@1409 150 * (when present.)
jjg@1409 151 */
jjg@1409 152 abstract class Checker {
jjg@1409 153 final DocTrees trees;
jjg@1409 154
jjg@1409 155 Checker(DocTrees trees) {
jjg@1409 156 this.trees = trees;
jjg@1409 157 }
jjg@1409 158
jjg@1409 159 void visitCompilationUnit(CompilationUnitTree tree) { }
jjg@1409 160
jjg@1409 161 abstract void check(TreePath tree, Name name) throws Exception;
jjg@1409 162
jjg@1409 163 void error(String msg) {
jjg@1409 164 DocCommentTester.this.error(msg);
jjg@1409 165 }
jjg@1409 166 }
jjg@1409 167
jjg@1409 168 void error(String msg) {
jjg@1409 169 System.err.println("Error: " + msg);
jjg@1409 170 errors++;
jjg@1409 171 }
jjg@1409 172
jjg@1409 173 int errors;
jjg@1409 174
jjg@1409 175 /**
jjg@1409 176 * Verify the structure of the DocTree AST by comparing it against golden text.
jjg@1409 177 */
jjg@1409 178 static class ASTChecker extends Checker {
jjg@1409 179 Printer printer = new Printer();
jjg@1409 180 String source;
jjg@1409 181
jjg@1409 182 ASTChecker(DocCommentTester test, DocTrees t) {
jjg@1409 183 test.super(t);
jjg@1409 184 }
jjg@1409 185
jjg@1409 186 @Override
jjg@1409 187 void visitCompilationUnit(CompilationUnitTree tree) {
jjg@1409 188 try {
jjg@1409 189 source = tree.getSourceFile().getCharContent(true).toString();
jjg@1409 190 } catch (IOException e) {
jjg@1409 191 source = "";
jjg@1409 192 }
jjg@1409 193 }
jjg@1409 194
jjg@1409 195 void check(TreePath path, Name name) {
jjg@1409 196 StringWriter out = new StringWriter();
jjg@1409 197 DocCommentTree dc = trees.getDocCommentTree(path);
jjg@1409 198 printer.print(dc, out);
jjg@1409 199 out.flush();
jjg@1409 200 String found = out.toString();
jjg@1409 201
jjg@1409 202 // Look for the first block comment after the first occurrence of name
jjg@1409 203 int start = source.indexOf("\n/*\n", findName(source, name));
jjg@1409 204 int end = source.indexOf("\n*/\n", start);
jjg@1409 205 String expect = source.substring(start + 4, end + 1);
jjg@1409 206 if (!found.equals(expect)) {
jjg@1409 207 System.err.println("Expect:\n" + expect);
jjg@1409 208 System.err.println("Found:\n" + found);
jjg@1409 209 error("AST mismatch for " + name);
jjg@1409 210 }
jjg@1409 211 }
jjg@1409 212
jjg@1409 213 /**
jjg@1409 214 * This main program is to set up the golden comments used by this
jjg@1409 215 * checker.
jjg@1409 216 * Usage:
jjg@1409 217 * java DocCommentTester$ASTChecker -o dir file...
jjg@1409 218 * The given files are written to the output directory with their
jjg@1409 219 * golden comments updated. The intent is that the files should
jjg@1409 220 * then be compared with the originals, e.g. with meld, and if the
jjg@1409 221 * changes are approved, the new files can be used to replace the old.
jjg@1409 222 */
jjg@1409 223 public static void main(String... args) throws Exception {
jjg@1409 224 List<File> files = new ArrayList<File>();
jjg@1409 225 File o = null;
jjg@1409 226 for (int i = 0; i < args.length; i++) {
jjg@1409 227 String arg = args[i];
jjg@1409 228 if (arg.equals("-o"))
jjg@1409 229 o = new File(args[++i]);
jjg@1409 230 else if (arg.startsWith("-"))
jjg@1409 231 throw new IllegalArgumentException(arg);
jjg@1409 232 else {
jjg@1409 233 files.add(new File(arg));
jjg@1409 234 }
jjg@1409 235 }
jjg@1409 236
jjg@1409 237 if (o == null)
jjg@1409 238 throw new IllegalArgumentException("no output dir specified");
jjg@1409 239 final File outDir = o;
jjg@1409 240
jjg@1409 241 JavacTool javac = JavacTool.create();
jjg@1409 242 StandardJavaFileManager fm = javac.getStandardFileManager(null, null, null);
jjg@1409 243 Iterable<? extends JavaFileObject> fos = fm.getJavaFileObjectsFromFiles(files);
jjg@1409 244
jjg@1409 245 JavacTask t = javac.getTask(null, fm, null, null, null, fos);
jjg@1409 246 final DocTrees trees = DocTrees.instance(t);
jjg@1409 247
jjg@1409 248 DeclScanner d = new DeclScanner() {
jjg@1409 249 Printer p = new Printer();
jjg@1409 250 String source;
jjg@1409 251
jjg@1409 252 @Override
jjg@1409 253 public Void visitCompilationUnit(CompilationUnitTree tree, Void ignore) {
jjg@1409 254 System.err.println("processing " + tree.getSourceFile().getName());
jjg@1409 255 try {
jjg@1409 256 source = tree.getSourceFile().getCharContent(true).toString();
jjg@1409 257 } catch (IOException e) {
jjg@1409 258 source = "";
jjg@1409 259 }
jjg@1409 260
jjg@1409 261 // remove existing gold by removing all block comments after the first '{'.
jjg@1409 262 int start = source.indexOf("{");
jjg@1409 263 while ((start = source.indexOf("\n/*\n", start)) != -1) {
jjg@1409 264 int end = source.indexOf("\n*/\n");
jjg@1409 265 source = source.substring(0, start + 1) + source.substring(end + 4);
jjg@1409 266 }
jjg@1409 267
jjg@1409 268 // process decls in compilation unit
jjg@1409 269 super.visitCompilationUnit(tree, ignore);
jjg@1409 270
jjg@1409 271 // write the modified source
jjg@1409 272 File f = new File(tree.getSourceFile().getName());
jjg@1409 273 File outFile = new File(outDir, f.getName());
jjg@1409 274 try {
jjg@1409 275 FileWriter out = new FileWriter(outFile);
jjg@1409 276 try {
jjg@1409 277 out.write(source);
jjg@1409 278 } finally {
jjg@1409 279 out.close();
jjg@1409 280 }
jjg@1409 281 } catch (IOException e) {
jjg@1409 282 System.err.println("Can't write " + tree.getSourceFile().getName()
jjg@1409 283 + " to " + outFile + ": " + e);
jjg@1409 284 }
jjg@1409 285 return null;
jjg@1409 286 }
jjg@1409 287
jjg@1409 288 @Override
jjg@1409 289 void visitDecl(Tree tree, Name name) {
jjg@1409 290 DocTree dc = trees.getDocCommentTree(getCurrentPath());
jjg@1409 291 if (dc != null) {
jjg@1409 292 StringWriter out = new StringWriter();
jjg@1409 293 p.print(dc, out);
jjg@1409 294 String found = out.toString();
jjg@1409 295
jjg@1409 296 // Look for the empty line after the first occurrence of name
jjg@1409 297 int pos = source.indexOf("\n\n", findName(source, name));
jjg@1409 298
jjg@1409 299 // Insert the golden comment
jjg@1409 300 source = source.substring(0, pos)
jjg@1409 301 + "\n/*\n"
jjg@1409 302 + found
jjg@1409 303 + "*/"
jjg@1409 304 + source.substring(pos);
jjg@1409 305 }
jjg@1409 306 }
jjg@1409 307
jjg@1409 308 };
jjg@1409 309
jjg@1409 310 Iterable<? extends CompilationUnitTree> units = t.parse();
jjg@1409 311 for (CompilationUnitTree unit: units) {
jjg@1409 312 d.scan(unit, null);
jjg@1409 313 }
jjg@1409 314 }
jjg@1409 315
jjg@1409 316 static int findName(String source, Name name) {
jjg@1409 317 Pattern p = Pattern.compile("\\s" + name + "[(;]");
jjg@1409 318 Matcher m = p.matcher(source);
jjg@1409 319 if (!m.find())
jjg@1409 320 throw new Error("cannot find " + name);
jjg@1409 321 return m.start();
jjg@1409 322 }
jjg@1409 323
jjg@1409 324 static class Printer implements DocTreeVisitor<Void, Void> {
jjg@1409 325 PrintWriter out;
jjg@1409 326
jjg@1409 327 void print(DocTree tree, Writer out) {
jjg@1409 328 this.out = (out instanceof PrintWriter)
jjg@1409 329 ? (PrintWriter) out : new PrintWriter(out);
jjg@1409 330 tree.accept(this, null);
jjg@1409 331 this.out.flush();
jjg@1409 332 }
jjg@1409 333
jjg@1409 334 public Void visitAttribute(AttributeTree node, Void p) {
jjg@1409 335 header(node);
jjg@1409 336 indent(+1);
jjg@1409 337 print("name", node.getName().toString());
jjg@1409 338 print("vkind", node.getValueKind().toString());
jjg@1409 339 print("value", node.getValue());
jjg@1409 340 indent(-1);
jjg@1409 341 indent();
jjg@1409 342 out.println("]");
jjg@1409 343 return null;
jjg@1409 344 }
jjg@1409 345
jjg@1409 346 public Void visitAuthor(AuthorTree node, Void p) {
jjg@1409 347 header(node);
jjg@1409 348 indent(+1);
jjg@1409 349 print("name", node.getName());
jjg@1409 350 indent(-1);
jjg@1409 351 indent();
jjg@1409 352 out.println("]");
jjg@1409 353 return null;
jjg@1409 354 }
jjg@1409 355
jjg@1409 356 public Void visitComment(CommentTree node, Void p) {
jjg@1409 357 header(node, compress(node.getBody()));
jjg@1409 358 return null;
jjg@1409 359 }
jjg@1409 360
jjg@1409 361 public Void visitDeprecated(DeprecatedTree node, Void p) {
jjg@1409 362 header(node);
jjg@1409 363 indent(+1);
jjg@1409 364 print("body", node.getBody());
jjg@1409 365 indent(-1);
jjg@1409 366 indent();
jjg@1409 367 out.println("]");
jjg@1409 368 return null;
jjg@1409 369 }
jjg@1409 370
jjg@1409 371 public Void visitDocComment(DocCommentTree node, Void p) {
jjg@1409 372 header(node);
jjg@1409 373 indent(+1);
jjg@1409 374 print("firstSentence", node.getFirstSentence());
jjg@1409 375 print("body", node.getBody());
jjg@1409 376 print("block tags", node.getBlockTags());
jjg@1409 377 indent(-1);
jjg@1409 378 indent();
jjg@1409 379 out.println("]");
jjg@1409 380 return null;
jjg@1409 381 }
jjg@1409 382
jjg@1409 383 public Void visitDocRoot(DocRootTree node, Void p) {
jjg@1409 384 header(node, "");
jjg@1409 385 return null;
jjg@1409 386 }
jjg@1409 387
jjg@1409 388 public Void visitEndElement(EndElementTree node, Void p) {
jjg@1409 389 header(node, node.getName().toString());
jjg@1409 390 return null;
jjg@1409 391 }
jjg@1409 392
jjg@1409 393 public Void visitEntity(EntityTree node, Void p) {
jjg@1409 394 header(node, node.getName().toString());
jjg@1409 395 return null;
jjg@1409 396 }
jjg@1409 397
jjg@1409 398 public Void visitErroneous(ErroneousTree node, Void p) {
jjg@1409 399 header(node);
jjg@1409 400 indent(+1);
jjg@1409 401 print("code", ((DCErroneous) node).diag.getCode());
jjg@1409 402 print("body", compress(node.getBody()));
jjg@1409 403 indent(-1);
jjg@1409 404 indent();
jjg@1409 405 out.println("]");
jjg@1409 406 return null;
jjg@1409 407 }
jjg@1409 408
jjg@1409 409 public Void visitIdentifier(IdentifierTree node, Void p) {
jjg@1409 410 header(node, compress(node.getName().toString()));
jjg@1409 411 return null;
jjg@1409 412 }
jjg@1409 413
jjg@1409 414 public Void visitInheritDoc(InheritDocTree node, Void p) {
jjg@1409 415 header(node, "");
jjg@1409 416 return null;
jjg@1409 417 }
jjg@1409 418
jjg@1409 419 public Void visitLink(LinkTree node, Void p) {
jjg@1409 420 header(node);
jjg@1409 421 indent(+1);
jjg@1409 422 print("reference", node.getReference());
jjg@1409 423 print("body", node.getLabel());
jjg@1409 424 indent(-1);
jjg@1409 425 indent();
jjg@1409 426 out.println("]");
jjg@1409 427 return null;
jjg@1409 428 }
jjg@1409 429
jjg@1409 430 public Void visitLiteral(LiteralTree node, Void p) {
jjg@1409 431 header(node, compress(node.getBody().getBody()));
jjg@1409 432 return null;
jjg@1409 433 }
jjg@1409 434
jjg@1409 435 public Void visitParam(ParamTree node, Void p) {
jjg@1409 436 header(node);
jjg@1409 437 indent(+1);
jjg@1409 438 print("name", node.getName());
jjg@1409 439 print("description", node.getDescription());
jjg@1409 440 indent(-1);
jjg@1409 441 indent();
jjg@1409 442 out.println("]");
jjg@1409 443 return null;
jjg@1409 444 }
jjg@1409 445
jjg@1409 446 public Void visitReference(ReferenceTree node, Void p) {
jjg@1409 447 header(node, compress(node.getSignature()));
jjg@1409 448 return null;
jjg@1409 449 }
jjg@1409 450
jjg@1409 451 public Void visitReturn(ReturnTree node, Void p) {
jjg@1409 452 header(node);
jjg@1409 453 indent(+1);
jjg@1409 454 print("description", node.getDescription());
jjg@1409 455 indent(-1);
jjg@1409 456 indent();
jjg@1409 457 out.println("]");
jjg@1409 458 return null;
jjg@1409 459 }
jjg@1409 460
jjg@1409 461 public Void visitSee(SeeTree node, Void p) {
jjg@1409 462 header(node);
jjg@1409 463 indent(+1);
jjg@1409 464 print("reference", node.getReference());
jjg@1409 465 indent(-1);
jjg@1409 466 indent();
jjg@1409 467 out.println("]");
jjg@1409 468 return null;
jjg@1409 469 }
jjg@1409 470
jjg@1409 471 public Void visitSerial(SerialTree node, Void p) {
jjg@1409 472 header(node);
jjg@1409 473 indent(+1);
jjg@1409 474 print("description", node.getDescription());
jjg@1409 475 indent(-1);
jjg@1409 476 indent();
jjg@1409 477 out.println("]");
jjg@1409 478 return null;
jjg@1409 479 }
jjg@1409 480
jjg@1409 481 public Void visitSerialData(SerialDataTree node, Void p) {
jjg@1409 482 header(node);
jjg@1409 483 indent(+1);
jjg@1409 484 print("description", node.getDescription());
jjg@1409 485 indent(-1);
jjg@1409 486 indent();
jjg@1409 487 out.println("]");
jjg@1409 488 return null;
jjg@1409 489 }
jjg@1409 490
jjg@1409 491 public Void visitSerialField(SerialFieldTree node, Void p) {
jjg@1409 492 header(node);
jjg@1409 493 indent(+1);
jjg@1409 494 print("name", node.getName());
jjg@1409 495 print("type", node.getType());
jjg@1409 496 print("description", node.getDescription());
jjg@1409 497 indent(-1);
jjg@1409 498 indent();
jjg@1409 499 out.println("]");
jjg@1409 500 return null;
jjg@1409 501 }
jjg@1409 502
jjg@1409 503 public Void visitSince(SinceTree node, Void p) {
jjg@1409 504 header(node);
jjg@1409 505 indent(+1);
jjg@1409 506 print("body", node.getBody());
jjg@1409 507 indent(-1);
jjg@1409 508 indent();
jjg@1409 509 out.println("]");
jjg@1409 510 return null;
jjg@1409 511 }
jjg@1409 512
jjg@1409 513 public Void visitStartElement(StartElementTree node, Void p) {
jjg@1409 514 header(node);
jjg@1409 515 indent(+1);
jjg@1409 516 indent();
jjg@1409 517 out.println("name:" + node.getName());
jjg@1409 518 print("attributes", node.getAttributes());
jjg@1409 519 indent(-1);
jjg@1409 520 indent();
jjg@1409 521 out.println("]");
jjg@1409 522 return null;
jjg@1409 523 }
jjg@1409 524
jjg@1409 525 public Void visitText(TextTree node, Void p) {
jjg@1409 526 header(node, compress(node.getBody()));
jjg@1409 527 return null;
jjg@1409 528 }
jjg@1409 529
jjg@1409 530 public Void visitThrows(ThrowsTree node, Void p) {
jjg@1409 531 header(node);
jjg@1409 532 indent(+1);
jjg@1409 533 print("exceptionName", node.getExceptionName());
jjg@1409 534 print("description", node.getDescription());
jjg@1409 535 indent(-1);
jjg@1409 536 indent();
jjg@1409 537 out.println("]");
jjg@1409 538 return null;
jjg@1409 539 }
jjg@1409 540
jjg@1409 541 public Void visitUnknownBlockTag(UnknownBlockTagTree node, Void p) {
jjg@1409 542 header(node);
jjg@1409 543 indent(+1);
jjg@1409 544 indent();
jjg@1409 545 out.println("tag:" + node.getTagName());
jjg@1409 546 print("content", node.getContent());
jjg@1409 547 indent(-1);
jjg@1409 548 indent();
jjg@1409 549 out.println("]");
jjg@1409 550 return null;
jjg@1409 551 }
jjg@1409 552
jjg@1409 553 public Void visitUnknownInlineTag(UnknownInlineTagTree node, Void p) {
jjg@1409 554 header(node);
jjg@1409 555 indent(+1);
jjg@1409 556 indent();
jjg@1409 557 out.println("tag:" + node.getTagName());
jjg@1409 558 print("content", node.getContent());
jjg@1409 559 indent(-1);
jjg@1409 560 indent();
jjg@1409 561 out.println("]");
jjg@1409 562 return null;
jjg@1409 563 }
jjg@1409 564
jjg@1409 565 public Void visitValue(ValueTree node, Void p) {
jjg@1409 566 header(node);
jjg@1409 567 indent(+1);
jjg@1409 568 print("reference", node.getReference());
jjg@1409 569 indent(-1);
jjg@1409 570 indent();
jjg@1409 571 out.println("]");
jjg@1409 572 return null;
jjg@1409 573 }
jjg@1409 574
jjg@1409 575 public Void visitVersion(VersionTree node, Void p) {
jjg@1409 576 header(node);
jjg@1409 577 indent(+1);
jjg@1409 578 print("body", node.getBody());
jjg@1409 579 indent(-1);
jjg@1409 580 indent();
jjg@1409 581 out.println("]");
jjg@1409 582 return null;
jjg@1409 583 }
jjg@1409 584
jjg@1409 585 public Void visitOther(DocTree node, Void p) {
jjg@1409 586 throw new UnsupportedOperationException("Not supported yet.");
jjg@1409 587 }
jjg@1409 588
jjg@1409 589 void header(DocTree node) {
jjg@1409 590 indent();
jjg@1409 591 out.println(simpleClassName(node) + "[" + node.getKind() + ", pos:" + ((DCTree) node).pos);
jjg@1409 592 }
jjg@1409 593
jjg@1409 594 void header(DocTree node, String rest) {
jjg@1409 595 indent();
jjg@1409 596 out.println(simpleClassName(node) + "[" + node.getKind() + ", pos:" + ((DCTree) node).pos
jjg@1409 597 + (rest.isEmpty() ? "" : ", " + rest)
jjg@1409 598 + "]");
jjg@1409 599 }
jjg@1409 600
jjg@1409 601 String simpleClassName(DocTree node) {
jjg@1409 602 return node.getClass().getSimpleName().replaceAll("DC(.*)", "$1");
jjg@1409 603 }
jjg@1409 604
jjg@1409 605 void print(String name, DocTree item) {
jjg@1409 606 indent();
jjg@1409 607 if (item == null)
jjg@1409 608 out.println(name + ": null");
jjg@1409 609 else {
jjg@1409 610 out.println(name + ":");
jjg@1409 611 indent(+1);
jjg@1409 612 item.accept(this, null);
jjg@1409 613 indent(-1);
jjg@1409 614 }
jjg@1409 615 }
jjg@1409 616
jjg@1409 617 void print(String name, String s) {
jjg@1409 618 indent();
jjg@1409 619 out.println(name + ": " + s);
jjg@1409 620 }
jjg@1409 621
jjg@1409 622 void print(String name, List<? extends DocTree> list) {
jjg@1409 623 indent();
jjg@1409 624 if (list == null)
jjg@1409 625 out.println(name + ": null");
jjg@1409 626 else if (list.isEmpty())
jjg@1409 627 out.println(name + ": empty");
jjg@1409 628 else {
jjg@1409 629 out.println(name + ": " + list.size());
jjg@1409 630 indent(+1);
jjg@1409 631 for (DocTree tree: list) {
jjg@1409 632 tree.accept(this, null);
jjg@1409 633 }
jjg@1409 634 indent(-1);
jjg@1409 635 }
jjg@1409 636 }
jjg@1409 637
jjg@1409 638 int indent = 0;
jjg@1409 639
jjg@1409 640 void indent() {
jjg@1409 641 for (int i = 0; i < indent; i++) {
jjg@1409 642 out.print(" ");
jjg@1409 643 }
jjg@1409 644 }
jjg@1409 645
jjg@1409 646 void indent(int n) {
jjg@1409 647 indent += n;
jjg@1409 648 }
jjg@1409 649
jjg@1409 650 String compress(String s) {
jjg@1409 651 s = s.replace("\n", "|").replace(" ", "_");
jjg@1409 652 return (s.length() < 32)
jjg@1409 653 ? s
jjg@1409 654 : s.substring(0, 16) + "..." + s.substring(16);
jjg@1409 655 }
jjg@1409 656
jjg@1409 657 String quote(String s) {
jjg@1409 658 if (s.contains("\""))
jjg@1409 659 return "'" + s + "'";
jjg@1409 660 else if (s.contains("'") || s.contains(" "))
jjg@1409 661 return '"' + s + '"';
jjg@1409 662 else
jjg@1409 663 return s;
jjg@1409 664 }
jjg@1409 665
jjg@1409 666
jjg@1409 667 }
jjg@1409 668 }
jjg@1409 669
jjg@1409 670 /**
jjg@1409 671 * Verify the reported tree positions by comparing the characters found
jjg@1409 672 * at and after the reported position with the beginning of the pretty-
jjg@1409 673 * printed text.
jjg@1409 674 */
jjg@1409 675 static class PosChecker extends Checker {
jjg@1409 676 PosChecker(DocCommentTester test, DocTrees t) {
jjg@1409 677 test.super(t);
jjg@1409 678 }
jjg@1409 679
jjg@1409 680 @Override
jjg@1409 681 void check(TreePath path, Name name) throws Exception {
jjg@1409 682 JavaFileObject fo = path.getCompilationUnit().getSourceFile();
jjg@1409 683 final CharSequence cs = fo.getCharContent(true);
jjg@1409 684
jjg@1409 685 final DCDocComment dc = (DCDocComment) trees.getDocCommentTree(path);
jjg@1409 686 DCTree t = (DCTree) trees.getDocCommentTree(path);
jjg@1409 687
jjg@1409 688 DocTreeScanner scanner = new DocTreeScanner<Void,Void>() {
jjg@1409 689 @Override
jjg@1409 690 public Void scan(DocTree node, Void ignore) {
jjg@1409 691 if (node != null) {
jjg@1409 692 try {
jjg@1409 693 String expect = getExpectText(node);
jjg@1409 694 long pos = ((DCTree) node).getSourcePosition(dc);
jjg@1409 695 String found = getFoundText(cs, (int) pos, expect.length());
jjg@1409 696 if (!found.equals(expect)) {
jjg@1409 697 System.err.println("expect: " + expect);
jjg@1409 698 System.err.println("found: " + found);
jjg@1409 699 error("mismatch");
jjg@1409 700 }
jjg@1409 701
jjg@1409 702 } catch (StringIndexOutOfBoundsException e) {
jjg@1409 703 error(node.getClass() + ": " + e.toString());
jjg@1409 704 e.printStackTrace();
jjg@1409 705 }
jjg@1409 706 }
jjg@1409 707 return super.scan(node, ignore);
jjg@1409 708 }
jjg@1409 709 };
jjg@1409 710
jjg@1409 711 scanner.scan(t, null);
jjg@1409 712 }
jjg@1409 713
jjg@1409 714 String getExpectText(DocTree t) {
jjg@1409 715 StringWriter sw = new StringWriter();
jjg@1409 716 DocPretty p = new DocPretty(sw);
jjg@1409 717 try { p.print(t); } catch (IOException never) { }
jjg@1409 718 String s = sw.toString();
jjg@1409 719 if (s.length() <= 1)
jjg@1409 720 return s;
jjg@1409 721 int ws = s.replaceAll("\\s+", " ").indexOf(" ");
jjg@1409 722 if (ws != -1) s = s.substring(0, ws);
jjg@1409 723 return (s.length() < 5) ? s : s.substring(0, 5);
jjg@1409 724 }
jjg@1409 725
jjg@1409 726 String getFoundText(CharSequence cs, int pos, int len) {
jjg@1409 727 return (pos == -1) ? "" : cs.subSequence(pos, Math.min(pos + len, cs.length())).toString();
jjg@1409 728 }
jjg@1409 729 }
jjg@1409 730
jjg@1409 731 /**
jjg@1409 732 * Verify the pretty printed text against a normalized form of the
jjg@1409 733 * original doc comment.
jjg@1409 734 */
jjg@1409 735 static class PrettyChecker extends Checker {
jjg@1409 736
jjg@1409 737 PrettyChecker(DocCommentTester test, DocTrees t) {
jjg@1409 738 test.super(t);
jjg@1409 739 }
jjg@1409 740
jjg@1409 741 @Override
jjg@1409 742 void check(TreePath path, Name name) throws Exception {
jjg@1409 743 String raw = trees.getDocComment(path);
jjg@1409 744 String normRaw = normalize(raw);
jjg@1409 745
jjg@1409 746 StringWriter out = new StringWriter();
jjg@1409 747 DocPretty dp = new DocPretty(out);
jjg@1409 748 dp.print(trees.getDocCommentTree(path));
jjg@1409 749 String pretty = out.toString();
jjg@1409 750
jjg@1409 751 if (!pretty.equals(normRaw)) {
jjg@1409 752 error("mismatch");
jjg@1409 753 System.err.println("*** expected:");
jjg@1409 754 System.err.println(normRaw.replace(" ", "_"));
jjg@1409 755 System.err.println("*** found:");
jjg@1409 756 System.err.println(pretty.replace(" ", "_"));
jjg@1409 757 // throw new Error();
jjg@1409 758 }
jjg@1409 759 }
jjg@1409 760
jjg@1409 761 /**
jjg@1409 762 * Normalize white space in places where the tree does not preserve it.
jjg@1409 763 */
jjg@1409 764 String normalize(String s) {
jjg@1409 765 return s.trim()
jjg@1409 766 .replaceFirst("\\.\\s++([^@])", ". $1")
jjg@1409 767 .replaceFirst("\\.\\s*\\n *@", ".\n@")
jjg@1409 768 .replaceFirst("\\s+<(/?p|pre|h[1-6])>", " <$1>")
jjg@1409 769 .replaceAll("\\{@docRoot\\s+\\}", "{@docRoot}")
jjg@1409 770 .replaceAll("\\{@inheritDoc\\s+\\}", "{@inheritDoc}")
jjg@1409 771 .replaceAll("(\\{@value\\s+[^}]+)\\s+(\\})", "$1$2")
jjg@1409 772 .replaceAll("\n[ \t]+@", "\n@");
jjg@1409 773 }
jjg@1409 774
jjg@1409 775 }
jjg@1409 776
jjg@1409 777 }
jjg@1409 778

mercurial