test/tools/javac/doctree/DocCommentTester.java

Thu, 21 Feb 2013 15:26:46 +0000

author
mcimadamore
date
Thu, 21 Feb 2013 15:26:46 +0000
changeset 1599
9f0ec00514b6
parent 1419
a25c53e12bd0
child 2525
2eb010b6cb22
permissions
-rw-r--r--

8007461: Regression: bad overload resolution when inner class and outer class have method with same name
Summary: Fix regression in varargs method resolution introduced by bad refactoring
Reviewed-by: jjg

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

mercurial