Fix android hierarchy ref page template. am: bbac6c38c5 am: 302b861409
am: c6a9a703ea

* commit 'c6a9a703ea158fb2311d7dfcbd6db2a7e49a8523':
  Fix android hierarchy ref page template.

Change-Id: Ic3b001c175b31f74500e56726adbc7d154ad4cc2
diff --git a/README.version b/README.version
new file mode 100644
index 0000000..f7c1fa0
--- /dev/null
+++ b/README.version
@@ -0,0 +1,3 @@
+URL: https://code.google.com/p/doclava/downloads/detail?name=doclava-1.0.6-bundle.zip&can=2&q=
+Version: 1.0.6
+BugComponent: 27745
diff --git a/res/assets/templates/class.cs b/res/assets/templates/class.cs
index e35e938..8854eaf 100644
--- a/res/assets/templates/class.cs
+++ b/res/assets/templates/class.cs
@@ -177,8 +177,9 @@
     <tr class="<?cs if:count % #2 ?>alt-color<?cs /if ?> api apilevel-<?cs var:method.since ?>" >
         <td class="jd-typecol"><nobr>
             <?cs var:method.abstract ?>
-            <?cs var:method.final ?>
+            <?cs var:method.default ?>
             <?cs var:method.static ?>
+            <?cs var:method.final ?>
             <?cs call:type_link(method.generic) ?>
             <?cs call:type_link(method.returnType) ?></nobr>
         </td>
@@ -523,10 +524,11 @@
 <div class="jd-details api apilevel-<?cs var:method.since ?>"> 
     <h4 class="jd-details-title">
       <span class="normal">
-        <?cs var:method.scope ?> 
-        <?cs var:method.static ?> 
-        <?cs var:method.final ?> 
-        <?cs var:method.abstract ?> 
+        <?cs var:method.scope ?>
+        <?cs var:method.abstract ?>
+        <?cs var:method.default ?>
+        <?cs var:method.static ?>
+        <?cs var:method.final ?>
         <?cs call:type_link(method.returnType) ?>
       </span>
       <span class="sympad"><?cs var:method.name ?></span>
diff --git a/res/assets/templates/macros.cs b/res/assets/templates/macros.cs
index a462e0d..750a31d 100644
--- a/res/assets/templates/macros.cs
+++ b/res/assets/templates/macros.cs
@@ -132,6 +132,30 @@
   /each ?><?cs
 /def ?>
 
+<?cs # Print output for block tags that are not "standard" javadoc tags ?><?cs
+def:block_tag_list(tags) ?><?cs
+  each:tag = tags ?><?cs
+      if:tag.kind == "@apiNote" ?>
+        <div class="jd-tagdata">
+          <h5 class="jd-tagtitle">API Note:</h5>
+          <ul class="nolist"><li><?cs call:tag_list(tag.commentTags) ?></li></ul>
+        </div><?cs
+      /if ?><?cs
+      if:tag.kind == "@implSpec" ?>
+        <div class="jd-tagdata">
+          <h5 class="jd-tagtitle">Implementation Requirements:</h5>
+          <ul class="nolist"><li><?cs call:tag_list(tag.commentTags) ?></li></ul>
+        </div><?cs
+      /if ?><?cs
+      if:tag.kind == "@implNote" ?>
+        <div class="jd-tagdata">
+          <h5 class="jd-tagtitle">Implementation Note:</h5>
+          <ul class="nolist"><li><?cs call:tag_list(tag.commentTags) ?></li></ul>
+        </div><?cs
+      /if ?><?cs
+  /each ?><?cs
+/def ?>
+
 <?cs # Show the short-form description of something.  These come from shortDescr and deprecated ?><?cs
 def:short_descr(obj) ?><?cs
   if:subcount(obj.deprecated) ?><em><?cs
@@ -255,8 +279,10 @@
         each:attr=obj.attrRefs ?>
             <li><a href="<?cs var:toroot ?><?cs var:attr.href ?>"><?cs var:attr.name ?></a></li><?cs
         /each ?>
-      </ul>
-  <?cs
+      </ul><?cs
+  /if ?><?cs
+  if:subcount(obj.blockTags) ?>
+    <?cs call:block_tag_list(obj.blockTags) ?><?cs
   /if ?><?cs
   #
   # Print the @param tags
@@ -414,4 +440,3 @@
 /def ?>
 
 <?cs include:"components.cs" ?>
-
diff --git a/src/com/google/doclava/CodeTagInfo.java b/src/com/google/doclava/CodeTagInfo.java
index 56ffd13..ac69733 100644
--- a/src/com/google/doclava/CodeTagInfo.java
+++ b/src/com/google/doclava/CodeTagInfo.java
@@ -31,9 +31,6 @@
   }
 
   public CodeTagInfo(String text, SourcePositionInfo sp) {
-    // TODO: the correct behavior is to escape the text,
-    // but we'll have to update the Android sources before making the switch.
-    //super("@code", "@code", encode(text), sp);
-    super("@code", "@code", text, sp);
+    super("@code", "@code", encode(text), sp);
   }
 }
diff --git a/src/com/google/doclava/Comment.java b/src/com/google/doclava/Comment.java
index 616ccc4..1a4498f 100644
--- a/src/com/google/doclava/Comment.java
+++ b/src/com/google/doclava/Comment.java
@@ -28,6 +28,7 @@
       Pattern.compile("((.*?)\\.)[ \t\r\n\\<](.*)", Pattern.DOTALL);
 
   private static final Set<String> KNOWN_TAGS = new HashSet<String>(Arrays.asList(new String[] {
+          "@apiNote",
           "@author",
           "@since",
           "@version",
@@ -41,6 +42,8 @@
           "@sample",
           "@include",
           "@serial",
+          "@implNote",
+          "@implSpec",
       }));
 
   public Comment(String text, ContainerInfo base, SourcePositionInfo sp) {
@@ -164,9 +167,15 @@
   }
 
   private int findEndIndexOfInlineTag(String text, int fromIndex, int toIndex) {
+      int braceDepth = 0;
       for (int i = fromIndex; i < toIndex; i++) {
-          if (text.charAt(i) == '}') {
-              return i;
+          if (text.charAt(i) == '{') {
+              braceDepth++;
+          } else if (text.charAt(i) == '}') {
+              braceDepth--;
+              if (braceDepth == 0) {
+                  return i;
+              }
           }
       }
 
@@ -356,6 +365,8 @@
       mUndeprecateTagsList.add(new TextTagInfo("@undeprecate", "@undeprecate", text, pos));
     } else if (name.equals("@include") || name.equals("@sample")) {
       mInlineTagsList.add(new SampleTagInfo(name, "@include", text, mBase, pos));
+    } else if (name.equals("@apiNote") || name.equals("@implSpec") || name.equals("@implNote")) {
+      mTagsList.add(new ParsedTagInfo(name, name, text, mBase, pos));
     } else {
       boolean known = KNOWN_TAGS.contains(name);
       if (!known) {
@@ -424,6 +435,11 @@
     return results.toArray(TagInfo.getArray(results.size()));
   }
 
+  public TagInfo[] blockTags() {
+    init();
+    return mTags;
+  }
+
   public ParamTagInfo[] paramTags() {
     init();
     return mParamTags;
@@ -522,6 +538,7 @@
     mInitialized = true;
 
     mInlineTags = mInlineTagsList.toArray(TagInfo.getArray(mInlineTagsList.size()));
+    mTags = mTagsList.toArray(TagInfo.getArray(mTagsList.size()));
     mParamTags = mParamTagsList.toArray(ParamTagInfo.getArray(mParamTagsList.size()));
     mSeeTags = mSeeTagsList.toArray(SeeTagInfo.getArray(mSeeTagsList.size()));
     mThrowsTags = mThrowsTagsList.toArray(ThrowsTagInfo.getArray(mThrowsTagsList.size()));
@@ -533,6 +550,7 @@
     mAttrTags = mAttrTagsList.toArray(AttrTagInfo.getArray(mAttrTagsList.size()));
     mBriefTags = mBriefTagsList.toArray(TagInfo.getArray(mBriefTagsList.size()));
 
+    mTagsList = null;
     mParamTagsList = null;
     mSeeTagsList = null;
     mThrowsTagsList = null;
diff --git a/src/com/google/doclava/Converter.java b/src/com/google/doclava/Converter.java
index 3153b41..1bc3563 100644
--- a/src/com/google/doclava/Converter.java
+++ b/src/com/google/doclava/Converter.java
@@ -227,7 +227,8 @@
     if (p == null) return null;
     ParameterInfo pi =
         new ParameterInfo(p.name(), p.typeName(), Converter.obtainType(p.type()), isVarArg,
-          Converter.convertSourcePosition(pos));
+          Converter.convertSourcePosition(pos),
+          Arrays.asList(Converter.convertAnnotationInstances(p.annotations())));
     return pi;
   }
 
@@ -403,8 +404,9 @@
                     m.name(), m.signature(), Converter.obtainClass(m.containingClass()),
                     Converter.obtainClass(m.containingClass()), m.isPublic(), m.isProtected(), m
                     .isPackagePrivate(), m.isPrivate(), m.isFinal(), m.isStatic(), m.isSynthetic(),
-                    m.isAbstract(), m.isSynchronized(), m.isNative(), true, "annotationElement",
-                    m.flatSignature(), Converter.obtainMethod(m.overriddenMethod()),
+                    m.isAbstract(), m.isSynchronized(), m.isNative(), m.isDefault(), true,
+                    "annotationElement", m.flatSignature(),
+                    Converter.obtainMethod(m.overriddenMethod()),
                     Converter.obtainType(m.returnType()),
                     new ArrayList<ParameterInfo>(Arrays.asList(
                             Converter.convertParameters(m.parameters(), m))),
@@ -424,8 +426,8 @@
                     Converter.obtainClass(m.containingClass()),
                     Converter.obtainClass(m.containingClass()), m.isPublic(), m.isProtected(),
                     m.isPackagePrivate(), m.isPrivate(), m.isFinal(), m.isStatic(), m.isSynthetic(),
-                    m.isAbstract(), m.isSynchronized(), m.isNative(), false, "method",
-                    m.flatSignature(), Converter.obtainMethod(m.overriddenMethod()),
+                    m.isAbstract(), m.isSynchronized(), m.isNative(), m.isDefault(), false,
+                    "method", m.flatSignature(), Converter.obtainMethod(m.overriddenMethod()),
                     Converter.obtainType(m.returnType()),
                     new ArrayList<ParameterInfo>(Arrays.asList(
                             Converter.convertParameters(m.parameters(), m))),
@@ -439,12 +441,26 @@
         return result;
       } else {
         ConstructorDoc m = (ConstructorDoc) o;
+        // Workaround for a JavaDoc behavior change introduced in OpenJDK 8 that breaks
+        // links in documentation and the content of API files like current.txt.
+        // http://b/18051133.
+        String name = m.name();
+        ClassDoc containingClass = m.containingClass();
+        if (containingClass.containingClass() != null) {
+          // This should detect the new behavior and be bypassed otherwise.
+          if (!name.contains(".")) {
+            // Constructors of inner classes do not contain the name of the enclosing class
+            // with OpenJDK 8. This simulates the old behavior:
+            name = containingClass.name();
+          }
+        }
+        // End of workaround.
         MethodInfo result =
-            new MethodInfo(m.getRawCommentText(), new ArrayList<TypeInfo>(Arrays.asList(Converter.convertTypes(m.typeParameters()))), m
-                .name(), m.signature(), Converter.obtainClass(m.containingClass()), Converter
+            new MethodInfo(m.getRawCommentText(), new ArrayList<TypeInfo>(Arrays.asList(Converter.convertTypes(m.typeParameters()))), 
+                name, m.signature(), Converter.obtainClass(m.containingClass()), Converter
                 .obtainClass(m.containingClass()), m.isPublic(), m.isProtected(), m
                 .isPackagePrivate(), m.isPrivate(), m.isFinal(), m.isStatic(), m.isSynthetic(),
-                false, m.isSynchronized(), m.isNative(), false, "constructor", m.flatSignature(),
+                false, m.isSynchronized(), m.isNative(), false/*isDefault*/, false, "constructor", m.flatSignature(),
                 null, null, new ArrayList<ParameterInfo>(Arrays.asList(Converter.convertParameters(m.parameters(), m))),
                 new ArrayList<ClassInfo>(Arrays.asList(Converter.convertClasses(m.thrownExceptions()))), Converter.convertSourcePosition(m
                     .position()), new ArrayList<AnnotationInstanceInfo>(Arrays.asList(Converter.convertAnnotationInstances(m.annotations()))));
diff --git a/src/com/google/doclava/Doclava.java b/src/com/google/doclava/Doclava.java
index 4ca7e6b..c1d29ee 100644
--- a/src/com/google/doclava/Doclava.java
+++ b/src/com/google/doclava/Doclava.java
@@ -305,6 +305,7 @@
         documentAnnotationsPath = a[1];
       } else if (a[0].equals("-referenceonly")) {
         referenceOnly = true;
+        mHDFData.add(new String[] {"referenceonly", "1"});
       }
     }
 
@@ -850,11 +851,11 @@
         String templ = relative + f.getName();
         int len = templ.length();
         if (len > 3 && ".cs".equals(templ.substring(len - 3))) {
-          Data data = makeHDF();
+          Data data = makePackageHDF();
           String filename = templ.substring(0, len - 3) + htmlExtension;
           ClearPage.write(data, templ, filename, js);
         } else if (len > 3 && ".jd".equals(templ.substring(len - 3))) {
-          Data data = makeHDF();
+          Data data = makePackageHDF();
           String filename = templ.substring(0, len - 3) + htmlExtension;
           DocFile.writePage(f.getAbsolutePath(), relative, filename, data);
         } else if(!f.getName().equals(".DS_Store")){
diff --git a/src/com/google/doclava/InfoBuilder.java b/src/com/google/doclava/InfoBuilder.java
index a07f990..895f5b6 100644
--- a/src/com/google/doclava/InfoBuilder.java
+++ b/src/com/google/doclava/InfoBuilder.java
@@ -29,6 +29,7 @@
 
 import java.io.IOException;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
@@ -1069,7 +1070,7 @@
                 modifiers.isPrivate(), modifiers.isFinal(),
                 modifiers.isStatic(), modifiers.isSynthetic(),
                 modifiers.isAbstract(), modifiers.isSynchronized(),
-                false, isAnnotationElement, kind, flatSignature.toString(),
+                false, modifiers.isDefault(), isAnnotationElement, kind, flatSignature.toString(),
                 null, returnType, parameters, thrownExceptions,
                 commentAndPosition.getPosition(), modifiers.getAnnotations());
 
@@ -1142,7 +1143,8 @@
                     commentAndPosition.setPosition(paramPart);
 
                     parameters.add(new ParameterInfo(name, type.qualifiedTypeName(), type,
-                            isVarArg, commentAndPosition.getPosition()));
+                            isVarArg, commentAndPosition.getPosition(),
+                            Collections.<AnnotationInstanceInfo>emptyList()));
                 }
             }
         }
@@ -1852,6 +1854,7 @@
         private boolean mIsSynthetic = false;
         private boolean mIsSynchronized = false;
         private boolean mIsStrictfp = false;
+        private boolean mIsDefault = false;
         private InfoBuilder mBuilder;
         private ArrayList<AnnotationInstanceInfo> mAnnotations;
 
@@ -1893,6 +1896,8 @@
                     mIsSynchronized = true;
                 }  else if ("strictfp".equals(modifier)) {
                     mIsStrictfp = true;
+                }  else if ("default".equals(modifier)) {
+                    mIsDefault = true;
                 } else if ("annotation".equals(modifier)) {
                     mAnnotations.add(buildAnnotationInstance((ParseTree) child, mBuilder));
                 }
@@ -1948,6 +1953,10 @@
             return mIsStrictfp;
         }
 
+        public boolean isDefault() {
+            return mIsDefault;
+        }
+
         public ArrayList<AnnotationInstanceInfo> getAnnotations() {
             return mAnnotations;
         }
diff --git a/src/com/google/doclava/MethodInfo.java b/src/com/google/doclava/MethodInfo.java
index f2f040e..5d6188a 100644
--- a/src/com/google/doclava/MethodInfo.java
+++ b/src/com/google/doclava/MethodInfo.java
@@ -252,7 +252,7 @@
         new MethodInfo(getRawCommentText(), mTypeParameters, name(), signature(),
             newContainingClass, realContainingClass(), isPublic(), isProtected(),
             isPackagePrivate(), isPrivate(), isFinal(), isStatic(), isSynthetic(), mIsAbstract,
-            mIsSynchronized, mIsNative, mIsAnnotationElement, kind(), mFlatSignature,
+            mIsSynchronized, mIsNative, mIsDefault, mIsAnnotationElement, kind(), mFlatSignature,
             mOverriddenMethod, returnType, mParameters, mThrownExceptions, position(),
             annotations());
     result.init(mDefaultAnnotationElementValue);
@@ -263,10 +263,10 @@
       String signature, ClassInfo containingClass, ClassInfo realContainingClass, boolean isPublic,
       boolean isProtected, boolean isPackagePrivate, boolean isPrivate, boolean isFinal,
       boolean isStatic, boolean isSynthetic, boolean isAbstract, boolean isSynchronized,
-      boolean isNative, boolean isAnnotationElement, String kind, String flatSignature,
-      MethodInfo overriddenMethod, TypeInfo returnType, ArrayList<ParameterInfo> parameters,
-      ArrayList<ClassInfo> thrownExceptions, SourcePositionInfo position,
-      ArrayList<AnnotationInstanceInfo> annotations) {
+      boolean isNative, boolean isDefault, boolean isAnnotationElement, String kind,
+      String flatSignature, MethodInfo overriddenMethod, TypeInfo returnType,
+      ArrayList<ParameterInfo> parameters, ArrayList<ClassInfo> thrownExceptions,
+      SourcePositionInfo position, ArrayList<AnnotationInstanceInfo> annotations) {
     // Explicitly coerce 'final' state of Java6-compiled enum values() method, to match
     // the Java5-emitted base API description.
     super(rawCommentText, name, signature, containingClass, realContainingClass, isPublic,
@@ -274,19 +274,13 @@
         ((name.equals("values") && containingClass.isEnum()) ? true : isFinal),
         isStatic, isSynthetic, kind, position, annotations);
 
-    // The underlying MethodDoc for an interface's declared methods winds up being marked
-    // non-abstract. Correct that here by looking at the immediate-parent class, and marking
-    // this method abstract if it is an unimplemented interface method.
-    if (containingClass.isInterface()) {
-      isAbstract = true;
-    }
-
     mReasonOpened = "0:0";
     mIsAnnotationElement = isAnnotationElement;
     mTypeParameters = typeParameters;
     mIsAbstract = isAbstract;
     mIsSynchronized = isSynchronized;
     mIsNative = isNative;
+    mIsDefault = isDefault;
     mFlatSignature = flatSignature;
     mOverriddenMethod = overriddenMethod;
     mReturnType = returnType;
@@ -310,6 +304,10 @@
     return mIsNative;
   }
 
+  public boolean isDefault() {
+    return mIsDefault;
+  }
+
   public String flatSignature() {
     return mFlatSignature;
   }
@@ -318,6 +316,10 @@
     return new InlineTags();
   }
 
+  public TagInfo[] blockTags() {
+    return comment().blockTags();
+  }
+
   public InheritedTags firstSentenceTags() {
     return new FirstSentenceTags();
   }
@@ -454,7 +456,7 @@
 
       // Complain about misnamed @param tags
       for (ParamTagInfo tag : paramTags) {
-        if (!isParamTagInMethod(tag)){
+        if (!isParamTagInMethod(tag) && !tag.isTypeParameter()){
           Errors.error(Errors.UNKNOWN_PARAM_TAG_NAME, tag.position(),
               "@param tag with name that doesn't match the parameter list: '"
               + tag.parameterName() + "'");
@@ -545,15 +547,15 @@
       }
       int i = 0;
       for (ParameterInfo mine : mParameters) {
-        if (!mine.matchesDimension(dimensions[i], varargs)) {
+        // If the method we're matching against is a varargs method (varargs == true), then
+        // only its last parameter is varargs.
+        if (!mine.matchesDimension(dimensions[i], (i == params.length - 1) ? varargs : false)) {
           return false;
         }
         TypeInfo myType = mine.type();
         String qualifiedName = myType.qualifiedTypeName();
         String realType = myType.isPrimitive() ? "" : myType.asClassInfo().qualifiedName();
         String s = params[i];
-        int slen = s.length();
-        int qnlen = qualifiedName.length();
 
         // Check for a matching generic name or best known type
         if (!matchesType(qualifiedName, s) && !matchesType(realType, s)) {
@@ -593,12 +595,14 @@
       data.setValue(base + ".abstract", mIsAbstract ? "abstract" : "");
     }
 
+    data.setValue(base + ".default", mIsDefault ? "default" : "");
     data.setValue(base + ".synchronized", mIsSynchronized ? "synchronized" : "");
     data.setValue(base + ".final", isFinal() ? "final" : "");
     data.setValue(base + ".static", isStatic() ? "static" : "");
 
     TagInfo.makeHDF(data, base + ".shortDescr", firstSentenceTags());
     TagInfo.makeHDF(data, base + ".descr", inlineTags());
+    TagInfo.makeHDF(data, base + ".blockTags", blockTags());
     TagInfo.makeHDF(data, base + ".deprecated", deprecatedTags());
     TagInfo.makeHDF(data, base + ".seeAlso", seeTags());
     data.setValue(base + ".since", getSince());
@@ -736,6 +740,7 @@
   private boolean mIsVarargs;
   private boolean mDeprecatedKnown;
   private boolean mIsDeprecated;
+  private boolean mIsDefault;
   private ArrayList<ParameterInfo> mParameters;
   private ArrayList<ClassInfo> mThrownExceptions;
   private String[] mParamStrings;
diff --git a/src/com/google/doclava/ParameterInfo.java b/src/com/google/doclava/ParameterInfo.java
index b911283..10000df 100644
--- a/src/com/google/doclava/ParameterInfo.java
+++ b/src/com/google/doclava/ParameterInfo.java
@@ -20,16 +20,18 @@
 
 import java.util.Collections;
 import java.util.HashSet;
+import java.util.List;
 import java.util.Map;
 
 public class ParameterInfo {
   public ParameterInfo(String name, String typeName, TypeInfo type, boolean isVarArg,
-      SourcePositionInfo position) {
+      SourcePositionInfo position, List<AnnotationInstanceInfo> annotationInstanceInfos) {
     mName = name;
     mTypeName = typeName;
     mType = type;
     mIsVarArg = isVarArg;
     mPosition = position;
+    mAnnotations = annotationInstanceInfos;
   }
 
   /**
@@ -37,7 +39,8 @@
    */
   public ParameterInfo cloneWithTypeArguments(Map<String, TypeInfo> typeArgumentMapping) {
     return new ParameterInfo(
-        mName, mTypeName, mType.getTypeWithArguments(typeArgumentMapping), mIsVarArg, mPosition);
+        mName, mTypeName, mType.getTypeWithArguments(typeArgumentMapping),
+        mIsVarArg, mPosition, mAnnotations);
   }
 
   TypeInfo type() {
@@ -60,6 +63,10 @@
     return mIsVarArg;
   }
 
+  List<AnnotationInstanceInfo> annotations() {
+    return mAnnotations;
+  }
+
   public void makeHDF(Data data, String base, boolean isLastVararg, HashSet<String> typeVariables) {
     makeHDF(data, base, isLastVararg, typeVariables, Collections.<String, TypeInfo>emptyMap());
   }
@@ -100,4 +107,5 @@
   TypeInfo mType;
   boolean mIsVarArg;
   SourcePositionInfo mPosition;
+  List<AnnotationInstanceInfo> mAnnotations;
 }
diff --git a/src/com/google/doclava/ParsedTagInfo.java b/src/com/google/doclava/ParsedTagInfo.java
index aad3767..ded9522 100755
--- a/src/com/google/doclava/ParsedTagInfo.java
+++ b/src/com/google/doclava/ParsedTagInfo.java
@@ -16,6 +16,7 @@
 
 package com.google.doclava;
 
+import com.google.clearsilver.jsilver.data.Data;
 import java.util.ArrayList;
 
 public class ParsedTagInfo extends TagInfo {
@@ -46,6 +47,12 @@
     mCommentText = comment;
   }
 
+  @Override
+  public void makeHDF(Data data, String base) {
+    super.makeHDF(data, base);
+    TagInfo.makeHDF(data, base + ".commentTags", commentTags());
+  }
+
   public static <T extends ParsedTagInfo> TagInfo[] joinTags(T[] tags) {
     ArrayList<TagInfo> list = new ArrayList<TagInfo>();
     final int N = tags.length;
diff --git a/src/com/google/doclava/Stubs.java b/src/com/google/doclava/Stubs.java
index efcf89b..6de86a1 100644
--- a/src/com/google/doclava/Stubs.java
+++ b/src/com/google/doclava/Stubs.java
@@ -17,10 +17,16 @@
 package com.google.doclava;
 
 import java.io.BufferedOutputStream;
+import java.io.BufferedReader;
 import java.io.File;
+import java.io.FileInputStream;
 import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
 import java.io.PrintStream;
+import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
@@ -29,6 +35,7 @@
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Scanner;
 import java.util.Set;
 import java.util.regex.Pattern;
 
@@ -445,12 +452,63 @@
 
   static void writeClassFile(PrintStream stream, HashSet<ClassInfo> notStrippable, ClassInfo cl) {
     PackageInfo pkg = cl.containingPackage();
+    if (cl.containingClass() == null) {
+        stream.print(parseLicenseHeader(cl.position()));
+    }
     if (pkg != null) {
       stream.println("package " + pkg.name() + ";");
     }
     writeClass(stream, notStrippable, cl);
   }
 
+  private static String parseLicenseHeader(/* @Nonnull */ SourcePositionInfo positionInfo) {
+    if (positionInfo == null) {
+      throw new NullPointerException("positionInfo == null");
+    }
+
+    try {
+      final File sourceFile = new File(positionInfo.file);
+      if (!sourceFile.exists()) {
+        throw new IllegalArgumentException("Unable to find " + sourceFile +
+                ". This is usually because doclava has been asked to generate stubs for a file " +
+                "that isn't present in the list of input source files but exists in the input " +
+                "classpath.");
+      }
+      return parseLicenseHeader(new FileInputStream(sourceFile));
+    } catch (IOException ioe) {
+      throw new RuntimeException("Unable to parse license header for: " + positionInfo.file, ioe);
+    }
+  }
+
+  /* @VisibleForTesting */
+  static String parseLicenseHeader(InputStream input) throws IOException {
+    StringBuilder builder = new StringBuilder(8192);
+    try (Scanner scanner  = new Scanner(
+          new BufferedReader(new InputStreamReader(input, StandardCharsets.UTF_8)))) {
+      String line;
+      while (scanner.hasNextLine()) {
+        line = scanner.nextLine().trim();
+        // Use an extremely simple strategy for parsing license headers : assume that
+        // all file content before the first "package " or "import " directive is a license
+        // header. In some cases this might contain more than just the license header, but we
+        // don't care.
+        if (line.startsWith("package ") || line.startsWith("import ")) {
+          break;
+        }
+        builder.append(line);
+        builder.append("\n");
+      }
+
+      // We've reached the end of the file without reaching any package or import
+      // directives.
+      if (!scanner.hasNextLine()) {
+        throw new IOException("Unable to parse license header");
+      }
+    }
+
+    return builder.toString();
+  }
+
   static void writeClass(PrintStream stream, HashSet<ClassInfo> notStrippable, ClassInfo cl) {
     writeAnnotations(stream, cl.annotations(), cl.isDeprecated());
 
@@ -632,6 +690,9 @@
 
     writeAnnotations(stream, method.annotations(), method.isDeprecated());
 
+    if (method.isDefault()) {
+      stream.print("default ");
+    }
     stream.print(method.scope() + " ");
     if (method.isStatic()) {
       stream.print("static ");
@@ -667,7 +728,9 @@
     int count = 1;
     int size = method.parameters().size();
     for (ParameterInfo param : method.parameters()) {
-      stream.print(comma + fullParameterTypeName(method, param.type(), count == size) + " "
+      stream.print(comma);
+      writeAnnotations(stream, param.annotations(), false);
+      stream.print(fullParameterTypeName(method, param.type(), count == size) + " "
           + param.name());
       comma = ", ";
       count++;
@@ -682,7 +745,7 @@
         comma = ", ";
       }
     }
-    if (method.isAbstract() || method.isNative() || method.containingClass().isInterface()) {
+    if (method.isAbstract() || method.isNative() || (method.containingClass().isInterface() && (!method.isDefault() && !method.isStatic()))) {
       stream.println(";");
     } else {
       stream.print(" { ");
@@ -1353,6 +1416,9 @@
   static void writeMethodApi(PrintStream apiWriter, MethodInfo mi) {
     apiWriter.print("    method ");
     apiWriter.print(mi.scope());
+    if (mi.isDefault()) {
+      apiWriter.print(" default");
+    }
     if (mi.isStatic()) {
       apiWriter.print(" static");
     }
@@ -1543,9 +1609,7 @@
 
   static void writeConstructorKeepList(PrintStream keepListWriter, MethodInfo mi) {
     keepListWriter.print("    ");
-    String name = mi.name();
-    name = name.replace(".", "$");
-    keepListWriter.print(name);
+    keepListWriter.print("<init>");
 
     writeParametersKeepList(keepListWriter, mi, mi.parameters());
     keepListWriter.print(";\n");
diff --git a/src/com/google/doclava/apicheck/ApiFile.java b/src/com/google/doclava/apicheck/ApiFile.java
index 3f96943..2d958d4 100644
--- a/src/com/google/doclava/apicheck/ApiFile.java
+++ b/src/com/google/doclava/apicheck/ApiFile.java
@@ -29,7 +29,9 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.LinkedList;
+import java.util.List;
 
 class ApiFile {
 
@@ -252,7 +254,7 @@
     method = new MethodInfo(""/*rawCommentText*/, new ArrayList<TypeInfo>()/*typeParameters*/,
         name, null/*signature*/, cl, cl, pub, prot, pkgpriv, false/*isPrivate*/, false/*isFinal*/,
         false/*isStatic*/, false/*isSynthetic*/, false/*isAbstract*/, false/*isSynthetic*/,
-        false/*isNative*/,
+        false/*isNative*/, false/* isDefault */,
         false /*isAnnotationElement*/, "constructor", null/*flatSignature*/,
         null/*overriddenMethod*/, cl.asTypeInfo(), new ArrayList<ParameterInfo>(),
         new ArrayList<ClassInfo>()/*thrownExceptions*/, tokenizer.pos(),
@@ -280,6 +282,7 @@
     boolean abs = false;
     boolean dep = false;
     boolean syn = false;
+    boolean def = false;
     String type;
     String name;
     String ext = null;
@@ -294,6 +297,10 @@
     } else {
       pkgpriv = true;
     }
+    if ("default".equals(token)) {
+      def = true;
+      token = tokenizer.requireToken();
+    }
     if ("static".equals(token)) {
       stat = true;
       token = tokenizer.requireToken();
@@ -321,7 +328,7 @@
     name = token;
     method = new MethodInfo(""/*rawCommentText*/, new ArrayList<TypeInfo>()/*typeParameters*/,
         name, null/*signature*/, cl, cl, pub, prot, pkgpriv, false/*isPrivate*/, fin,
-        stat, false/*isSynthetic*/, abs/*isAbstract*/, syn, false/*isNative*/,
+        stat, false/*isSynthetic*/, abs/*isAbstract*/, syn, false/*isNative*/, def/*isDefault*/,
         false /*isAnnotationElement*/, "method", null/*flatSignature*/, null/*overriddenMethod*/,
         Converter.obtainTypeFromString(type), new ArrayList<ParameterInfo>(),
         new ArrayList<ClassInfo>()/*thrownExceptions*/, tokenizer.pos(),
@@ -488,10 +495,13 @@
       } else {
         throw new ApiParseException("expected , found " + token, tokenizer.getLine());
       }
+      // api file does not preserve annotations.
+      List<AnnotationInstanceInfo> annotations = Collections.emptyList();
       method.addParameter(new ParameterInfo(name, type,
             Converter.obtainTypeFromString(type),
             type.endsWith("..."),
-            tokenizer.pos()));
+            tokenizer.pos(),
+            annotations));
       if (type.endsWith("...")) {
         method.setVarargs(true);
       }
diff --git a/src/com/google/doclava/apicheck/XmlApiFile.java b/src/com/google/doclava/apicheck/XmlApiFile.java
index 8b15d09..d36766b 100644
--- a/src/com/google/doclava/apicheck/XmlApiFile.java
+++ b/src/com/google/doclava/apicheck/XmlApiFile.java
@@ -35,6 +35,8 @@
 
 import java.io.InputStream;
 import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
 import java.util.Stack;
 
 class XmlApiFile extends DefaultHandler {
@@ -137,6 +139,7 @@
       boolean isAbstract = Boolean.valueOf(attributes.getValue("abstract"));
       boolean isSynchronized = Boolean.valueOf(attributes.getValue("synchronized"));
       boolean isNative = Boolean.valueOf(attributes.getValue("native"));
+      boolean isDefault = Boolean.valueOf(attributes.getValue("default"));
       boolean isAnnotationElement = false; // TODO
       String kind = qName;
       String flatSignature = null; // TODO
@@ -150,9 +153,9 @@
       mCurrentMethod = 
           new MethodInfo(rawCommentText, typeParameters, name, signature, containingClass,
           realContainingClass, isPublic, isProtected, isPackagePrivate, isPrivate, isFinal,
-          isStatic, isSynthetic, isAbstract, isSynchronized, isNative, isAnnotationElement, kind,
-          flatSignature, overriddenMethod, returnType, parameters, thrownExceptions, position,
-          annotations);
+          isStatic, isSynthetic, isAbstract, isSynchronized, isNative, isDefault,
+          isAnnotationElement, kind, flatSignature, overriddenMethod, returnType, parameters,
+          thrownExceptions, position, annotations);
       
       mCurrentMethod.setDeprecated("deprecated".equals(attributes.getValue("deprecated")));
     } else if (qName.equals("constructor")) {
@@ -164,9 +167,9 @@
               attributes.getValue("name"), null/*signature*/, mCurrentClass, mCurrentClass,
               pub, prot, pkgpriv, false/*isPrivate*/, false/*isFinal*/, false/*isStatic*/,
               false/*isSynthetic*/, false/*isAbstract*/, false/*isSynthetic*/, false/*isNative*/,
-              false /*isAnnotationElement*/, "constructor", null/*flatSignature*/,
-              null/*overriddenMethod*/, mCurrentClass.asTypeInfo(), new ArrayList<ParameterInfo>(),
-              new ArrayList<ClassInfo>()/*thrownExceptions*/,
+              false/*isDefault*/, false/*isAnnotationElement*/, "constructor",
+              null/*flatSignature*/, null/*overriddenMethod*/, mCurrentClass.asTypeInfo(),
+              new ArrayList<ParameterInfo>(), new ArrayList<ClassInfo>()/*thrownExceptions*/,
               SourcePositionInfo.fromXml(attributes.getValue("source")),
               new ArrayList<AnnotationInstanceInfo>()/*annotations*/);
       mCurrentMethod.setDeprecated("deprecated".equals(attributes.getValue("deprecated")));
@@ -202,8 +205,10 @@
       TypeInfo type = Converter.obtainTypeFromString(typeName);
       boolean isVarArg = typeName.endsWith("...");
       SourcePositionInfo position = null;
-      
-      mCurrentMethod.addParameter(new ParameterInfo(name, typeName, type, isVarArg, position));
+      List<AnnotationInstanceInfo> annotations = Collections.emptyList();
+
+      mCurrentMethod.addParameter(
+          new ParameterInfo(name, typeName, type, isVarArg, position, annotations));
       mCurrentMethod.setVarargs(isVarArg);
     } else if (qName.equals("exception")) {
       mCurrentMethod.addException(attributes.getValue("type"));
diff --git a/test/com/google/doclava/StubsTest.java b/test/com/google/doclava/StubsTest.java
new file mode 100644
index 0000000..c2e726c
--- /dev/null
+++ b/test/com/google/doclava/StubsTest.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2016 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.doclava;
+
+import junit.framework.TestCase;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+
+public class StubsTest extends TestCase {
+
+    public void testLicenseWithPackage() throws IOException {
+        assertEquals("// Foo\n", parseLicenseHeader(
+                "// Foo",
+                "package foo",
+                ""));
+
+        assertEquals("// Foo\n// Bar\n", parseLicenseHeader(
+                "// Foo",
+                "// Bar",
+                "package foo",
+                ""));
+
+        assertEquals("// Foo\n// Bar\n", parseLicenseHeader(
+                "// Foo",
+                "// Bar",
+                "   package foo",
+                ""));
+
+        assertEquals("// Foo\n// Bar package bar food\n", parseLicenseHeader(
+                "// Foo",
+                "// Bar package bar food",
+                "package foo",
+                ""));
+    }
+
+    public void testLicenseWithImport() throws IOException {
+        assertEquals("// Foo\n", parseLicenseHeader(
+                "// Foo",
+                "import foo",
+                ""));
+
+        assertEquals("// Foo\n// Bar\n", parseLicenseHeader(
+                "// Foo",
+                "// Bar",
+                "import foo",
+                ""));
+
+        assertEquals("// Foo\n// Bar\n", parseLicenseHeader(
+                "// Foo",
+                "// Bar",
+                "   import foo;",
+                ""));
+
+        assertEquals("// Foo\n// Bar import bar food\n", parseLicenseHeader(
+                "// Foo",
+                "// Bar import bar food",
+                "import foo",
+                ""));
+    }
+
+    public void testLineStripping() throws IOException {
+        assertEquals("/*\n* Foo\n*/\n//\n// Bar\n//\n", parseLicenseHeader(
+                "/*",
+                "          * Foo",
+                "                        */",
+                "//",
+                "   // Bar",
+                "            //",
+                "package foo",
+                ""));
+    }
+
+    public void testMultipleComments() throws IOException {
+        assertEquals("/*\n* Foo\n*/\n/*\n* Bar\n*/\n", parseLicenseHeader(
+                "/*",
+                " * Foo",
+                " */",
+                "/*",
+                " * Bar",
+                " */",
+                "package foo",
+                ""));
+    }
+
+    private static String parseLicenseHeader(String... lines) throws IOException {
+        StringBuilder builder = new StringBuilder(8192);
+        for (String line : lines) {
+            builder.append(line);
+            builder.append('\n');
+        }
+
+        return Stubs.parseLicenseHeader(
+                new ByteArrayInputStream(builder.toString().getBytes(StandardCharsets.UTF_8)));
+    }
+}
+