More improvements to currysrc / srcgen

Focusing on handling ICU JavaDoc tags.

ICU SrcGen:
Adjusted for changes in CurrySrc.
Replaced instances of @icu, @icunote, @icuextended
with appropriate expanded text.
Removed instances of @draft, @provisional, @internal.
Turned of debug.

CurrySrc:
Switched many of the Javadoc manipulation transforms to modify
the AST rather than modifying the Document text directly.
Tidied up various classes, e.g. renaming, making final.

Bug: 22023363
Change-Id: I9390e5e14bdea139bab94f819602f9f9107cb8eb
diff --git a/tools/srcgen/currysrc/src/main/java/com/google/currysrc/Main.java b/tools/srcgen/currysrc/src/main/java/com/google/currysrc/Main.java
index a209c03..94c08c4 100644
--- a/tools/srcgen/currysrc/src/main/java/com/google/currysrc/Main.java
+++ b/tools/srcgen/currysrc/src/main/java/com/google/currysrc/Main.java
@@ -37,7 +37,7 @@
 /**
  * The main execution API for users of currysrc.
  */
-public class Main {
+public final class Main {
 
   private static final Charset JAVA_SOURCE_CHARSET = StandardCharsets.UTF_8;
 
diff --git a/tools/srcgen/currysrc/src/main/java/com/google/currysrc/api/input/CompoundDirectoryInputFileGenerator.java b/tools/srcgen/currysrc/src/main/java/com/google/currysrc/api/input/CompoundDirectoryInputFileGenerator.java
index 2ca1f77..c8ac8b5 100644
--- a/tools/srcgen/currysrc/src/main/java/com/google/currysrc/api/input/CompoundDirectoryInputFileGenerator.java
+++ b/tools/srcgen/currysrc/src/main/java/com/google/currysrc/api/input/CompoundDirectoryInputFileGenerator.java
@@ -25,7 +25,7 @@
  * An {@link InputFileGenerator} that can combine the output of other {@link InputFileGenerator}
  * instances.
  */
-public class CompoundDirectoryInputFileGenerator implements InputFileGenerator {
+public final class CompoundDirectoryInputFileGenerator implements InputFileGenerator {
 
   private List<InputFileGenerator> generators;
 
diff --git a/tools/srcgen/currysrc/src/main/java/com/google/currysrc/api/match/TypeName.java b/tools/srcgen/currysrc/src/main/java/com/google/currysrc/api/match/TypeName.java
index 312dfe3..f72b7c4 100644
--- a/tools/srcgen/currysrc/src/main/java/com/google/currysrc/api/match/TypeName.java
+++ b/tools/srcgen/currysrc/src/main/java/com/google/currysrc/api/match/TypeName.java
@@ -19,7 +19,7 @@
  * The name of a type: a class or an enum. The className is expected to contain $ to indicate
  * nested / inner classes.
  */
-public class TypeName {
+public final class TypeName {
 
   private final String packageName;
 
diff --git a/tools/srcgen/currysrc/src/main/java/com/google/currysrc/api/output/NullOutputSourceFileGenerator.java b/tools/srcgen/currysrc/src/main/java/com/google/currysrc/api/output/NullOutputSourceFileGenerator.java
index c27611f..8963fcb 100644
--- a/tools/srcgen/currysrc/src/main/java/com/google/currysrc/api/output/NullOutputSourceFileGenerator.java
+++ b/tools/srcgen/currysrc/src/main/java/com/google/currysrc/api/output/NullOutputSourceFileGenerator.java
@@ -22,7 +22,7 @@
 /**
  * Always returns {@code null}, preventing output file generation.
  */
-public class NullOutputSourceFileGenerator implements OutputSourceFileGenerator {
+public final class NullOutputSourceFileGenerator implements OutputSourceFileGenerator {
 
   /** The instance to use. */
   public final static NullOutputSourceFileGenerator INSTANCE = new NullOutputSourceFileGenerator();
diff --git a/tools/srcgen/currysrc/src/main/java/com/google/currysrc/api/transform/JavadocUtils.java b/tools/srcgen/currysrc/src/main/java/com/google/currysrc/api/transform/JavadocUtils.java
index 7794895..8678d63 100644
--- a/tools/srcgen/currysrc/src/main/java/com/google/currysrc/api/transform/JavadocUtils.java
+++ b/tools/srcgen/currysrc/src/main/java/com/google/currysrc/api/transform/JavadocUtils.java
@@ -15,8 +15,14 @@
  */
 package com.google.currysrc.api.transform;
 
+import com.google.currysrc.api.transform.ast.AstNodes;
+
+import org.eclipse.jdt.core.dom.AST;
 import org.eclipse.jdt.core.dom.BodyDeclaration;
 import org.eclipse.jdt.core.dom.Javadoc;
+import org.eclipse.jdt.core.dom.TagElement;
+import org.eclipse.jdt.core.dom.rewrite.ASTRewrite;
+import org.eclipse.jdt.core.dom.rewrite.ListRewrite;
 import org.eclipse.jface.text.BadLocationException;
 import org.eclipse.jface.text.Document;
 import org.eclipse.jface.text.IRegion;
@@ -29,61 +35,20 @@
   private JavadocUtils() {
   }
 
-  public static void insertCommentText(Document document, BodyDeclaration bodyDeclaration,
-      String text) {
-    Javadoc comment = bodyDeclaration.getJavadoc();
-    if (comment == null) {
-      try {
-        document.replace(bodyDeclaration.getStartPosition(), 0, "/** " + text + " */\n");
-      } catch (BadLocationException e) {
-        throw new AssertionError(e);
-      }
-    } else {
-      insertCommentText(document, comment, text);
+  public static void addJavadocTag(ASTRewrite rewrite, BodyDeclaration node, String tagText) {
+    Javadoc javadoc = node.getJavadoc();
+    if (javadoc == null) {
+      AST ast = node.getAST();
+      javadoc = (Javadoc) ast.createInstance(Javadoc.class);
+      rewrite.set(node, node.getJavadocProperty(), javadoc, null /* editGroup */);
     }
+    addJavadocTag(rewrite, javadoc, tagText);
   }
 
-  private static String determineIndent(String line) {
-    StringBuilder indentBuilder = new StringBuilder();
-    for (int i = 0; i < line.length(); i++) {
-      char c = line.charAt(i);
-      if (!Character.isWhitespace(c)) {
-        break;
-      }
-      indentBuilder.append(c);
-    }
-    return indentBuilder.toString();
-  }
-
-  public static void insertCommentText(Document document, Javadoc javadoc, String text) {
-    try {
-      int insertPosition;
-      String insertText;
-      int firstLineIndex = document.getLineOfOffset(javadoc.getStartPosition());
-      int lastLineIndex = document
-          .getLineOfOffset(javadoc.getStartPosition() + javadoc.getLength() - 1);
-      if (firstLineIndex == lastLineIndex) {
-        // Single line comment: /** foo */
-        IRegion lineInfo = document
-            .getLineInformationOfOffset(javadoc.getStartPosition() + javadoc.getLength() - 1);
-        String line = document.get(lineInfo.getOffset(), lineInfo.getLength());
-        String indent = determineIndent(line);
-        insertPosition = lineInfo.getOffset() + lineInfo.getLength() - 1 - "*/".length();
-        insertText = document.getDefaultLineDelimiter() + indent + "* " + text + document
-            .getDefaultLineDelimiter() + indent;
-      } else {
-        // TODO - write a decent Javadoc comment parser / generator instead.
-        IRegion lastLineInfo = document
-            .getLineInformationOfOffset(javadoc.getStartPosition() + javadoc.getLength() - 1);
-        String lastLine = document.get(lastLineInfo.getOffset(), lastLineInfo.getLength());
-        String indent = determineIndent(lastLine);
-        insertPosition = lastLineInfo.getOffset();
-        insertText = indent + "* " + text + document.getDefaultLineDelimiter();
-      }
-
-      document.replace(insertPosition, 0, insertText);
-    } catch (BadLocationException e) {
-      throw new AssertionError(e);
-    }
+  public static void addJavadocTag(ASTRewrite rewrite, Javadoc javadoc, String tagText) {
+    AST ast = javadoc.getAST();
+    TagElement tagElement = AstNodes.createTextTagElement(ast, tagText);
+    ListRewrite listRewrite = rewrite.getListRewrite(javadoc, Javadoc.TAGS_PROPERTY);
+    listRewrite.insertLast(tagElement, null /* editGroup */);
   }
 }
diff --git a/tools/srcgen/currysrc/src/main/java/com/google/currysrc/api/transform/ast/AstNodes.java b/tools/srcgen/currysrc/src/main/java/com/google/currysrc/api/transform/ast/AstNodes.java
new file mode 100644
index 0000000..fc213c7
--- /dev/null
+++ b/tools/srcgen/currysrc/src/main/java/com/google/currysrc/api/transform/ast/AstNodes.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.currysrc.api.transform.ast;
+
+import org.eclipse.jdt.core.dom.AST;
+import org.eclipse.jdt.core.dom.TagElement;
+import org.eclipse.jdt.core.dom.TextElement;
+
+/**
+ * Static utility methods for creating ASTNode instances.
+ */
+public final class AstNodes {
+
+  private AstNodes() {}
+
+  public static TagElement createTextTagElement(AST ast, String text) {
+    TagElement element = (TagElement) ast.createInstance(TagElement.class);
+    TextElement textElement = createTextElement(ast, text);
+    element.fragments().add(textElement);
+    return element;
+  }
+
+  public static TextElement createTextElement(AST ast, String text) {
+    TextElement textElement = ast.newTextElement();
+    textElement.setText(text);
+    return textElement;
+  }
+
+}
diff --git a/tools/srcgen/currysrc/src/main/java/com/google/currysrc/api/transform/ast/PackageMatcher.java b/tools/srcgen/currysrc/src/main/java/com/google/currysrc/api/transform/ast/PackageMatcher.java
index 0a007bf..6f2368a 100644
--- a/tools/srcgen/currysrc/src/main/java/com/google/currysrc/api/transform/ast/PackageMatcher.java
+++ b/tools/srcgen/currysrc/src/main/java/com/google/currysrc/api/transform/ast/PackageMatcher.java
@@ -21,7 +21,7 @@
 /**
  * Matches the package name associated with ASTNodes.
  */
-public class PackageMatcher {
+public final class PackageMatcher {
 
   protected final String packageName;
 
diff --git a/tools/srcgen/currysrc/src/main/java/com/google/currysrc/api/transform/ast/TypeLocater.java b/tools/srcgen/currysrc/src/main/java/com/google/currysrc/api/transform/ast/TypeLocater.java
index 9bb7939..0a9f2dc 100644
--- a/tools/srcgen/currysrc/src/main/java/com/google/currysrc/api/transform/ast/TypeLocater.java
+++ b/tools/srcgen/currysrc/src/main/java/com/google/currysrc/api/transform/ast/TypeLocater.java
@@ -109,8 +109,8 @@
 
     Iterator<String> classNameIterator = classNameElements.iterator();
     String topLevelClassName = classNameIterator.next();
-    for (AbstractTypeDeclaration abstractTypeDeclaration : (List<AbstractTypeDeclaration>) cu
-        .types()) {
+    for (AbstractTypeDeclaration abstractTypeDeclaration
+        : (List<AbstractTypeDeclaration>) cu.types()) {
       if (abstractTypeDeclaration.getName().getFullyQualifiedName().equals(topLevelClassName)) {
         // Top-level interface / class / enum match.
         return findNested(classNameIterator, abstractTypeDeclaration);
@@ -126,8 +126,8 @@
     }
 
     String subClassName = classNameIterator.next();
-    for (BodyDeclaration bodyDeclaration : (List<BodyDeclaration>) typeDeclaration
-        .bodyDeclarations()) {
+    for (BodyDeclaration bodyDeclaration
+        : (List<BodyDeclaration>) typeDeclaration.bodyDeclarations()) {
       if (bodyDeclaration instanceof AbstractTypeDeclaration) {
         AbstractTypeDeclaration subTypeDeclaration = (AbstractTypeDeclaration) bodyDeclaration;
         if (subTypeDeclaration.getName().getFullyQualifiedName().equals(subClassName)) {
diff --git a/tools/srcgen/currysrc/src/main/java/com/google/currysrc/transformers/BaseJavadocNodeScanner.java b/tools/srcgen/currysrc/src/main/java/com/google/currysrc/transformers/BaseJavadocNodeScanner.java
new file mode 100644
index 0000000..57cbc49
--- /dev/null
+++ b/tools/srcgen/currysrc/src/main/java/com/google/currysrc/transformers/BaseJavadocNodeScanner.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.currysrc.transformers;
+
+import com.google.common.collect.Lists;
+import com.google.currysrc.api.transform.AstTransformer;
+
+import org.eclipse.jdt.core.dom.Comment;
+import org.eclipse.jdt.core.dom.CompilationUnit;
+import org.eclipse.jdt.core.dom.Javadoc;
+import org.eclipse.jdt.core.dom.rewrite.ASTRewrite;
+
+import java.util.List;
+
+/**
+ * A base class for AstTransformers that modify Javadoc nodes. All Javadoc nodes in a
+ * {@code CompilationUnit} are considered.
+ */
+public abstract class BaseJavadocNodeScanner implements AstTransformer {
+
+  @Override public final void transform(CompilationUnit cu, ASTRewrite rewrite) {
+    List<Comment> comments = cu.getCommentList();
+    for (Comment comment : Lists.reverse(comments)) {
+      if (comment instanceof Javadoc) {
+        Javadoc javadoc = (Javadoc) comment;
+        visit(javadoc, rewrite);
+      }
+    }
+  }
+
+  protected abstract void visit(Javadoc javadoc, ASTRewrite rewrite);
+}
diff --git a/tools/srcgen/currysrc/src/main/java/com/google/currysrc/transformers/JavadocTagClasses.java b/tools/srcgen/currysrc/src/main/java/com/google/currysrc/transformers/BaseJavadocTagClasses.java
similarity index 80%
rename from tools/srcgen/currysrc/src/main/java/com/google/currysrc/transformers/JavadocTagClasses.java
rename to tools/srcgen/currysrc/src/main/java/com/google/currysrc/transformers/BaseJavadocTagClasses.java
index 6fca83d..2fa52b6 100644
--- a/tools/srcgen/currysrc/src/main/java/com/google/currysrc/transformers/JavadocTagClasses.java
+++ b/tools/srcgen/currysrc/src/main/java/com/google/currysrc/transformers/BaseJavadocTagClasses.java
@@ -16,7 +16,7 @@
 package com.google.currysrc.transformers;
 
 import com.google.common.collect.Lists;
-import com.google.currysrc.api.transform.DocumentTransformer;
+import com.google.currysrc.api.transform.AstTransformer;
 import com.google.currysrc.api.transform.JavadocUtils;
 
 import org.eclipse.jdt.core.dom.ASTVisitor;
@@ -24,7 +24,7 @@
 import org.eclipse.jdt.core.dom.CompilationUnit;
 import org.eclipse.jdt.core.dom.EnumDeclaration;
 import org.eclipse.jdt.core.dom.TypeDeclaration;
-import org.eclipse.jface.text.Document;
+import org.eclipse.jdt.core.dom.rewrite.ASTRewrite;
 
 import java.util.List;
 
@@ -32,16 +32,15 @@
  * Inserts the tag text to the Javadoc for any type declaration that matches
  * {@link #mustTag(AbstractTypeDeclaration)}.
  */
-public abstract class JavadocTagClasses implements DocumentTransformer {
+public abstract class BaseJavadocTagClasses implements AstTransformer {
 
   private final String tagText;
 
-  protected JavadocTagClasses(String tagText) {
+  protected BaseJavadocTagClasses(String tagText) {
     this.tagText = tagText;
   }
 
-  @Override
-  public void transform(CompilationUnit cu, final Document document) {
+  @Override public final void transform(CompilationUnit cu, ASTRewrite rewrite) {
     final List<AbstractTypeDeclaration> toHide = Lists.newArrayList();
     cu.accept(new ASTVisitor() {
       @Override
@@ -61,10 +60,8 @@
         return false;
       }
     });
-    // To avoid screwing up the offsets, attack in reverse order.
-    // TODO Is there a better way?
     for (AbstractTypeDeclaration node : Lists.reverse(toHide)) {
-      JavadocUtils.insertCommentText(document, node, tagText);
+      JavadocUtils.addJavadocTag(rewrite, node, tagText);
     }
   }
 
diff --git a/tools/srcgen/currysrc/src/main/java/com/google/currysrc/transformers/BaseJavadocTagJavadoc.java b/tools/srcgen/currysrc/src/main/java/com/google/currysrc/transformers/BaseJavadocTagJavadoc.java
new file mode 100644
index 0000000..f5afa54
--- /dev/null
+++ b/tools/srcgen/currysrc/src/main/java/com/google/currysrc/transformers/BaseJavadocTagJavadoc.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.currysrc.transformers;
+
+import com.google.currysrc.api.transform.JavadocUtils;
+
+import org.eclipse.jdt.core.dom.Javadoc;
+import org.eclipse.jdt.core.dom.rewrite.ASTRewrite;
+
+/**
+ * Inserts tag text to the Javadoc for any Javadoc comment that matches
+ * {@link #mustTag(org.eclipse.jdt.core.dom.Javadoc)}.
+ */
+public abstract class BaseJavadocTagJavadoc extends BaseJavadocNodeScanner {
+
+  private final String tagText;
+
+  protected BaseJavadocTagJavadoc(String tagText) {
+    this.tagText = tagText;
+  }
+
+  @Override protected final void visit(Javadoc javadoc, ASTRewrite rewrite) {
+    if (mustTag(javadoc)) {
+      JavadocUtils.addJavadocTag(rewrite, javadoc, tagText);
+    }
+  }
+
+  protected abstract boolean mustTag(Javadoc node);
+}
diff --git a/tools/srcgen/currysrc/src/main/java/com/google/currysrc/transformers/BaseModifyBodyDeclaration.java b/tools/srcgen/currysrc/src/main/java/com/google/currysrc/transformers/BaseModifyBodyDeclaration.java
deleted file mode 100644
index 5b0340e..0000000
--- a/tools/srcgen/currysrc/src/main/java/com/google/currysrc/transformers/BaseModifyBodyDeclaration.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.google.currysrc.transformers;
-
-import com.google.currysrc.api.transform.AstTransformer;
-import com.google.currysrc.api.transform.ast.BodyDeclarationLocater;
-
-import org.eclipse.jdt.core.dom.BodyDeclaration;
-import org.eclipse.jdt.core.dom.CompilationUnit;
-import org.eclipse.jdt.core.dom.rewrite.ASTRewrite;
-
-/**
- * A base-class for {@link AstTransformer}s that make modifications to a single
- * {@link BodyDeclaration} or surrounding nodes.
- */
-public abstract class BaseModifyBodyDeclaration implements AstTransformer {
-  private final BodyDeclarationLocater locater;
-
-  protected BaseModifyBodyDeclaration(BodyDeclarationLocater locater) {
-    this.locater = locater;
-  }
-
-  @Override public void transform(CompilationUnit cu, ASTRewrite rewrite) {
-    BodyDeclaration node = locater.find(cu);
-    if (node == null) {
-      return;
-    }
-
-    modifyNode(node, rewrite);
-  }
-
-  protected abstract void modifyNode(BodyDeclaration node, ASTRewrite rewrite);
-}
diff --git a/tools/srcgen/currysrc/src/main/java/com/google/currysrc/transformers/HidePublicClasses.java b/tools/srcgen/currysrc/src/main/java/com/google/currysrc/transformers/HidePublicClasses.java
index 209d76f..40e5998 100644
--- a/tools/srcgen/currysrc/src/main/java/com/google/currysrc/transformers/HidePublicClasses.java
+++ b/tools/srcgen/currysrc/src/main/java/com/google/currysrc/transformers/HidePublicClasses.java
@@ -25,7 +25,7 @@
 /**
  * Hides any public class that is not found in the whitelist.
  */
-public class HidePublicClasses extends JavadocTagClasses {
+public final class HidePublicClasses extends BaseJavadocTagClasses {
 
   private final List<TypeLocater> whitelist;
 
diff --git a/tools/srcgen/currysrc/src/main/java/com/google/currysrc/transformers/JavadocTagJavadoc.java b/tools/srcgen/currysrc/src/main/java/com/google/currysrc/transformers/JavadocTagJavadoc.java
deleted file mode 100644
index 13ebbc0..0000000
--- a/tools/srcgen/currysrc/src/main/java/com/google/currysrc/transformers/JavadocTagJavadoc.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.google.currysrc.transformers;
-
-import com.google.common.collect.Lists;
-import com.google.currysrc.api.transform.DocumentTransformer;
-import com.google.currysrc.api.transform.JavadocUtils;
-
-import org.eclipse.jdt.core.dom.Comment;
-import org.eclipse.jdt.core.dom.CompilationUnit;
-import org.eclipse.jdt.core.dom.Javadoc;
-import org.eclipse.jface.text.Document;
-
-import java.util.List;
-
-/**
- * Inserts the tag text to the Javadoc for any Javadoc comment that matches
- * {@link #mustTag(org.eclipse.jdt.core.dom.Javadoc)}.
- */
-public abstract class JavadocTagJavadoc implements DocumentTransformer {
-
-  private final String tagText;
-
-  protected JavadocTagJavadoc(String tagText) {
-    this.tagText = tagText;
-  }
-
-  @Override
-  public void transform(CompilationUnit cu, Document document) {
-    for (Comment comment : Lists.reverse((List<Comment>) cu.getCommentList())) {
-      if (comment.isDocComment()) {
-        Javadoc javadoc = (Javadoc) comment;
-        if (mustTag(javadoc)) {
-          JavadocUtils.insertCommentText(document, javadoc, tagText);
-        }
-      }
-    }
-  }
-
-  protected abstract boolean mustTag(Javadoc node);
-}
diff --git a/tools/srcgen/currysrc/src/main/java/com/google/currysrc/transformers/ModifyQualifiedNames.java b/tools/srcgen/currysrc/src/main/java/com/google/currysrc/transformers/ModifyQualifiedNames.java
index c7e01bd..e9e5eca 100644
--- a/tools/srcgen/currysrc/src/main/java/com/google/currysrc/transformers/ModifyQualifiedNames.java
+++ b/tools/srcgen/currysrc/src/main/java/com/google/currysrc/transformers/ModifyQualifiedNames.java
@@ -24,7 +24,7 @@
  * Changes any qualified names in the AST that start with {@code oldPrefix} to start with
  * {@code newPrefix} instead.
  */
-public class ModifyQualifiedNames implements AstTransformer {
+public final class ModifyQualifiedNames implements AstTransformer {
 
   private final String oldPrefix;
 
diff --git a/tools/srcgen/currysrc/src/main/java/com/google/currysrc/transformers/RemoveJavaDocTags.java b/tools/srcgen/currysrc/src/main/java/com/google/currysrc/transformers/RemoveJavaDocTags.java
index 67b9523..bee8bce 100644
--- a/tools/srcgen/currysrc/src/main/java/com/google/currysrc/transformers/RemoveJavaDocTags.java
+++ b/tools/srcgen/currysrc/src/main/java/com/google/currysrc/transformers/RemoveJavaDocTags.java
@@ -16,11 +16,7 @@
 package com.google.currysrc.transformers;
 
 import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Lists;
-import com.google.currysrc.api.transform.AstTransformer;
 
-import org.eclipse.jdt.core.dom.Comment;
-import org.eclipse.jdt.core.dom.CompilationUnit;
 import org.eclipse.jdt.core.dom.Javadoc;
 import org.eclipse.jdt.core.dom.TagElement;
 import org.eclipse.jdt.core.dom.rewrite.ASTRewrite;
@@ -31,7 +27,7 @@
 /**
  * Remove the specified JavaDoc tags from the AST. Assumes the Javadoc is well-formed.
  */
-public class RemoveJavaDocTags implements AstTransformer {
+public final class RemoveJavaDocTags extends BaseJavadocNodeScanner {
 
   private final Set<String> tagsToRemove;
 
@@ -43,22 +39,22 @@
     tagsToRemove = builder.build();
   }
 
-  @Override public void transform(CompilationUnit cu, ASTRewrite rewrite) {
-    List<Comment> comments = cu.getCommentList();
-    for (Comment comment : Lists.reverse(comments)) {
-      if (comment instanceof Javadoc) {
-        Javadoc javadoc = (Javadoc) comment;
-        for (TagElement tagElement : (List<TagElement>) javadoc.tags()) {
-          String tagName = tagElement.getTagName();
-          if (tagName == null) {
-            continue;
-          }
-          if (tagsToRemove.contains(tagName.toLowerCase())) {
-            rewrite.remove(tagElement, null /* editGroup */);
-          }
-        }
+  @Override
+  protected void visit(Javadoc javadoc, ASTRewrite rewrite) {
+    for (TagElement tagElement : (List<TagElement>) javadoc.tags()) {
+      String tagName = tagElement.getTagName();
+      if (tagName == null) {
+        continue;
+      }
+      if (tagsToRemove.contains(tagName.toLowerCase())) {
+        rewrite.remove(tagElement, null /* editGroup */);
       }
     }
   }
 
+  @Override public String toString() {
+    return "RemoveJavaDocTags{" +
+        "tagsToRemove=" + tagsToRemove +
+        '}';
+  }
 }
diff --git a/tools/srcgen/src/main/java/com/android/icu4j/srcgen/CaptureDeprecatedElements.java b/tools/srcgen/src/main/java/com/android/icu4j/srcgen/CaptureDeprecatedElements.java
index efc12f4..7890c57 100644
--- a/tools/srcgen/src/main/java/com/android/icu4j/srcgen/CaptureDeprecatedElements.java
+++ b/tools/srcgen/src/main/java/com/android/icu4j/srcgen/CaptureDeprecatedElements.java
@@ -159,7 +159,8 @@
     }
 
     private void trackDeprecationsRecursively(AbstractTypeDeclaration matchedType) {
-      for (BodyDeclaration bodyDeclaration : (List<BodyDeclaration>) matchedType.bodyDeclarations()) {
+      for (BodyDeclaration bodyDeclaration
+          : (List<BodyDeclaration>) matchedType.bodyDeclarations()) {
         if (isApiVisible(bodyDeclaration) && isDeprecated(bodyDeclaration)) {
           deprecatedElements.add(BodyDeclarationLocaters.toLocaterStringForm(bodyDeclaration));
           if (bodyDeclaration instanceof AbstractTypeDeclaration) {
diff --git a/tools/srcgen/src/main/java/com/android/icu4j/srcgen/HideDraftProvisionalInternal.java b/tools/srcgen/src/main/java/com/android/icu4j/srcgen/HideDraftProvisionalInternal.java
index ec5d766..ef9afb8 100644
--- a/tools/srcgen/src/main/java/com/android/icu4j/srcgen/HideDraftProvisionalInternal.java
+++ b/tools/srcgen/src/main/java/com/android/icu4j/srcgen/HideDraftProvisionalInternal.java
@@ -16,7 +16,7 @@
 package com.android.icu4j.srcgen;
 
 import com.google.common.collect.ImmutableSet;
-import com.google.currysrc.transformers.JavadocTagJavadoc;
+import com.google.currysrc.transformers.BaseJavadocTagJavadoc;
 
 import org.eclipse.jdt.core.dom.Javadoc;
 import org.eclipse.jdt.core.dom.TagElement;
@@ -28,7 +28,7 @@
  * Adds {@literal @}hide to all JavaDoc comments that contain any of {@literal @}draft,
  * {@literal @}provisional, {@literal @}internal}.
  */
-public class HideDraftProvisionalInternal extends JavadocTagJavadoc {
+public class HideDraftProvisionalInternal extends BaseJavadocTagJavadoc {
   private static final Set<String> toMatch = ImmutableSet.of("@draft", "@provisional", "@internal");
   private static final String HIDE_HIDDEN_ON_ANDROID =
       "@hide draft / provisional / internal are hidden on Android";
@@ -47,4 +47,8 @@
     }
     return false;
   }
+
+  @Override public String toString() {
+    return "HideDraftProvisionalInternal{}";
+  }
 }
diff --git a/tools/srcgen/src/main/java/com/android/icu4j/srcgen/HideOriginalDeprecatedSet.java b/tools/srcgen/src/main/java/com/android/icu4j/srcgen/HideOriginalDeprecatedSet.java
index d134c8c..bf82dee 100644
--- a/tools/srcgen/src/main/java/com/android/icu4j/srcgen/HideOriginalDeprecatedSet.java
+++ b/tools/srcgen/src/main/java/com/android/icu4j/srcgen/HideOriginalDeprecatedSet.java
@@ -16,14 +16,14 @@
 package com.android.icu4j.srcgen;
 
 import com.google.common.collect.Lists;
-import com.google.currysrc.api.transform.DocumentTransformer;
+import com.google.currysrc.api.transform.AstTransformer;
 import com.google.currysrc.api.transform.JavadocUtils;
 import com.google.currysrc.api.transform.ast.BodyDeclarationLocater;
 import com.google.currysrc.api.transform.ast.StartPositionComparator;
 
 import org.eclipse.jdt.core.dom.BodyDeclaration;
 import org.eclipse.jdt.core.dom.CompilationUnit;
-import org.eclipse.jface.text.Document;
+import org.eclipse.jdt.core.dom.rewrite.ASTRewrite;
 
 import java.util.Collections;
 import java.util.List;
@@ -33,7 +33,7 @@
  * blacklist. Used to hide all the deprecated methods that existed when Android made ICU public,
  * but does not touch those that were deprecated afterwards.
  */
-public class HideOriginalDeprecatedSet implements DocumentTransformer {
+public class HideOriginalDeprecatedSet implements AstTransformer {
   private static final String HIDDEN_TAG_COMMENT = "@hide original deprecated method";
   private final List<BodyDeclarationLocater> blacklist;
 
@@ -41,8 +41,7 @@
     this.blacklist = blacklist;
   }
 
-  @Override
-  public void transform(CompilationUnit cu, Document document) {
+  @Override public void transform(CompilationUnit cu, ASTRewrite rewrite) {
     List<BodyDeclaration> matchingNodes = Lists.newArrayList();
     // This is inefficient but it is very simple.
     for (BodyDeclarationLocater locater : blacklist) {
@@ -54,7 +53,14 @@
     // Tackle nodes in reverse order to avoid messing up the ASTNode offsets.
     Collections.sort(matchingNodes, new StartPositionComparator());
     for (BodyDeclaration bodyDeclaration : Lists.reverse(matchingNodes)) {
-      JavadocUtils.insertCommentText(document, bodyDeclaration, HIDDEN_TAG_COMMENT);
+      JavadocUtils.addJavadocTag(rewrite, bodyDeclaration, HIDDEN_TAG_COMMENT);
     }
   }
+
+
+  @Override public String toString() {
+    return "HideOriginalDeprecatedSet{" +
+        "blacklist=" + blacklist +
+        '}';
+  }
 }
diff --git a/tools/srcgen/src/main/java/com/android/icu4j/srcgen/Icu4jTransform.java b/tools/srcgen/src/main/java/com/android/icu4j/srcgen/Icu4jTransform.java
index 522d609..161bf47 100644
--- a/tools/srcgen/src/main/java/com/android/icu4j/srcgen/Icu4jTransform.java
+++ b/tools/srcgen/src/main/java/com/android/icu4j/srcgen/Icu4jTransform.java
@@ -502,7 +502,7 @@
       "method:android.icu.util.VersionInfo#javaVersion()",
   };
 
-  private static final boolean DEBUG = true;
+  private static final boolean DEBUG = false;
 
   private static final String ORIGINAL_ICU_PACKAGE = "com.ibm.icu";
 
@@ -550,12 +550,12 @@
 
           // Doc change: Insert a warning about the source code being generated.
           createMandatoryRule(new InsertHeader(SOURCE_CODE_HEADER)),
-          // Doc change: Hide all ICU public classes except those in the whitelist.
+          // AST change: Hide all ICU public classes except those in the whitelist.
           createHidePublicClassesRule(),
-          // Doc change: Hide ICU methods that are deprecated and Android does not want to make
+          // AST change: Hide ICU methods that are deprecated and Android does not want to make
           // public.
           createHideOriginalDeprecatedClassesRule(),
-          // Doc change: Explicitly hide any elements that are marked as
+          // AST change: Explicitly hide any elements that are marked as
           // @draft / @provisional / @internal
           createOptionalRule(new HideDraftProvisionalInternal()),
           // Doc change: Switch all embedded documentation references from com.ibm.icu to
@@ -567,12 +567,18 @@
           // Doc change: Hack around javadoc @stable / @author placement error upstream: this should
           // be fixed upstream.
           createFixupBidiClassDocRule(),
-          // AST change: Remove JavaDoc tags that Android has no need of.
-          createOptionalRule(new RemoveJavaDocTags("@stable")),
+          // AST change: Remove JavaDoc tags that Android has no need of:
+          // @hide has been added in place of @draft, @provisional and @internal
+          // @stable <ICU version> will not mean much on Android.
+          createOptionalRule(new RemoveJavaDocTags(
+              "@stable", "@draft", "@provisional", "@internal")),
+          // AST change: Replace @icu and @icuenhanced with standard text.
+          createOptionalRule(new ReplaceIcuTags()),
+
           // Doc change: Escape ICU tags we can't currently deal with.
           createOptionalRule(new MungeIcuJavaDocTags()),
 
-          // Doc change: Hide every public class until we're ready to make the Android subset
+          // AST change: Hide every public class until we're ready to make the Android subset
           // public. Comment the line below to see what Android docs look like with ICU as part of
           // the public API.
           createOptionalRule(new HidePublicClasses(Collections.<TypeLocater>emptyList(),
@@ -581,7 +587,7 @@
       return Lists.newArrayList(rules);
     }
 
-    private DocumentTransformRule createHideOriginalDeprecatedClassesRule() {
+    private TransformRule createHideOriginalDeprecatedClassesRule() {
       ImmutableList.Builder<BodyDeclarationLocater> deprecationBlacklistBuilder = ImmutableList
           .builder();
       for (String deprecatedElementLocaterString : INITIAL_DEPRECATED_SET) {
@@ -594,7 +600,7 @@
           new HideOriginalDeprecatedSet(deprecationBlacklistBuilder.build()));
     }
 
-    private DocumentTransformRule createHidePublicClassesRule() {
+    private TransformRule createHidePublicClassesRule() {
       ImmutableList.Builder<TypeLocater> apiClassesWhitelistBuilder = ImmutableList.builder();
       for (String publicClassName : PUBLIC_API_CLASSES) {
         apiClassesWhitelistBuilder.add(new TypeLocater(publicClassName));
diff --git a/tools/srcgen/src/main/java/com/android/icu4j/srcgen/MungeIcuJavaDocTags.java b/tools/srcgen/src/main/java/com/android/icu4j/srcgen/MungeIcuJavaDocTags.java
index 919fb81..65176af 100644
--- a/tools/srcgen/src/main/java/com/android/icu4j/srcgen/MungeIcuJavaDocTags.java
+++ b/tools/srcgen/src/main/java/com/android/icu4j/srcgen/MungeIcuJavaDocTags.java
@@ -34,9 +34,7 @@
 public class MungeIcuJavaDocTags extends BaseModifyCommentScanner {
 
   /** The set of problem tags. The pattern contains a capturing group for the "@". */
-  private final static Pattern PATTERN =
-      Pattern.compile(
-          "(@)(?:icu|internal|draft|provisional|icuenhanced|icunote|\\.jcite|obsolete)");
+  private final static Pattern PATTERN = Pattern.compile("(@)(?:\\.jcite)");
 
   @Override protected String generateReplacementText(Comment commentNode, String commentText) {
     if (!commentNode.isDocComment()) {
@@ -62,4 +60,8 @@
     }
     return adjustedCommentString.toString();
   }
+
+  @Override public String toString() {
+    return "MungeIcuJavaDocTags{" + PATTERN + "}";
+  }
 }
diff --git a/tools/srcgen/src/main/java/com/android/icu4j/srcgen/ReplaceIcuTags.java b/tools/srcgen/src/main/java/com/android/icu4j/srcgen/ReplaceIcuTags.java
new file mode 100644
index 0000000..1ce905e
--- /dev/null
+++ b/tools/srcgen/src/main/java/com/android/icu4j/srcgen/ReplaceIcuTags.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.icu4j.srcgen;
+
+import com.google.currysrc.api.transform.ast.AstNodes;
+import com.google.currysrc.transformers.BaseJavadocNodeScanner;
+
+import org.eclipse.jdt.core.dom.AST;
+import org.eclipse.jdt.core.dom.IDocElement;
+import org.eclipse.jdt.core.dom.Javadoc;
+import org.eclipse.jdt.core.dom.TagElement;
+import org.eclipse.jdt.core.dom.rewrite.ASTRewrite;
+
+import java.util.List;
+
+/**
+ * Replaces {@icu}, {@icuenhanced} and {@icunote} with text.
+ */
+public class ReplaceIcuTags extends BaseJavadocNodeScanner {
+
+  @Override protected void visit(Javadoc javadoc, ASTRewrite rewrite) {
+    AST ast = javadoc.getAST();
+    for (TagElement tag : (List<TagElement>) javadoc.tags()) {
+      recursivelyHandleTagElements(rewrite, ast, tag);
+    }
+  }
+
+  private void recursivelyHandleTagElements(ASTRewrite rewrite, AST ast, TagElement tag) {
+    String tagName = tag.getTagName();
+    List<IDocElement> fragments = tag.fragments();
+    if (tagName != null) {
+      if (tagName.equalsIgnoreCase("@icu")) {
+        // ICU replaces {@icu __usage__} with "Methods, fields, and other functionality specific to
+        // ICU are labeled '[icu]'"
+        // ICU replaces {@icu} with [icu]
+        if (fragments.size() == 0) {
+          rewrite.replace(tag, createIcuMarker(ast), null /* editGroup */);
+        } else {
+          IDocElement element = fragments.get(0);
+          if (element.toString().trim().equalsIgnoreCase("_usage_")) {
+            rewrite.replace(tag, createIcuUsageText(ast), null /* editGroup */);
+          } else {
+            throw new AssertionError("Unknown Javadoc tag: " + tag);
+          }
+        }
+        return;
+      } else if (tagName.equalsIgnoreCase("@icuenhanced")) {
+        // ICU replaces {@icuenhanced <classname>} with "[icu enhancement] ICU's replacement for
+        // <classname>"
+        IDocElement element = fragments.get(0);
+        rewrite.replace(tag, createIcuEnhancementText(ast, element), null /* editGroup */);
+        return;
+      } else if (tagName.equalsIgnoreCase("@icunote")) {
+        // ICU replaces {@icunote} with "[icu] Note:"
+        rewrite.replace(tag, createIcuNoteText(ast), null /* editGroup */);
+        return;
+      }
+    }
+
+    for (IDocElement fragment : fragments) {
+      if (fragment instanceof TagElement) {
+        recursivelyHandleTagElements(rewrite, ast, (TagElement) fragment);
+      }
+    }
+  }
+
+  private static TagElement createIcuEnhancementText(AST ast, IDocElement fragment) {
+    return AstNodes.createTextTagElement(ast,
+        "<strong>[icu enhancement]</strong> ICU's replacement for {@link" + fragment.toString()
+            + "}");
+  }
+
+  private static TagElement createIcuUsageText(AST ast) {
+    // Use of &nbsp; is a hacky way to preserve whitespace.
+    return AstNodes.createTextTagElement(ast,
+        "&nbsp;Methods, fields, and other functionality specific to ICU are labeled"
+            + " '<strong>[icu]</strong>'.");
+  }
+
+  private static TagElement createIcuNoteText(AST ast) {
+    return AstNodes.createTextTagElement(ast, "<strong>[icu] Note:</strong>");
+  }
+
+  private static TagElement createIcuMarker(AST ast) {
+    return AstNodes.createTextTagElement(ast, "<strong>[icu]</strong>");
+  }
+
+}