Code drop from //branches/cupcake/...@124589
diff --git a/tools/droiddoc/src/Android.mk b/tools/droiddoc/src/Android.mk
index c0c583d..abd2581 100644
--- a/tools/droiddoc/src/Android.mk
+++ b/tools/droiddoc/src/Android.mk
@@ -39,6 +39,7 @@
 	LiteralTagInfo.java \
 	MemberInfo.java \
 	MethodInfo.java \
+	NavTree.java \
 	PackageInfo.java \
 	ParamTagInfo.java \
 	ParameterInfo.java \
diff --git a/tools/droiddoc/src/DocFile.java b/tools/droiddoc/src/DocFile.java
index 38ac55c..f53a35c 100644
--- a/tools/droiddoc/src/DocFile.java
+++ b/tools/droiddoc/src/DocFile.java
@@ -116,21 +116,21 @@
 
         hdf.setValue("commentText", commentText);
         
-        if(outfile.indexOf("sdk/") != -1) {
-          hdf.setValue("sdk", "true");
-          if(outfile.indexOf("index.html") != -1) {
-            ClearPage.write(hdf, "sdkpage.cs", outfile);
-          }else{
+        if (outfile.indexOf("sdk/") != -1) {
+            hdf.setValue("sdk", "true");
+            if (outfile.indexOf("index.html") != -1) {
+                ClearPage.write(hdf, "sdkpage.cs", outfile);
+            } else {
+                ClearPage.write(hdf, "docpage.cs", outfile);
+            }
+        } else if (outfile.indexOf("guide/") != -1){
+            hdf.setValue("guide", "true");
             ClearPage.write(hdf, "docpage.cs", outfile);
-          }
-        }else if(outfile.indexOf("guide/") != -1){
-          hdf.setValue("guide", "true");
-          ClearPage.write(hdf, "docpage.cs", outfile);
-        }else if(outfile.indexOf("publish/") != -1){
-          hdf.setValue("publish", "true");
-          ClearPage.write(hdf, "docpage.cs", outfile);
-        }else{
-          ClearPage.write(hdf, "nosidenavpage.cs", outfile);
+        } else if (outfile.indexOf("publish/") != -1){
+            hdf.setValue("publish", "true");
+            ClearPage.write(hdf, "docpage.cs", outfile);
+        } else {
+            ClearPage.write(hdf, "nosidenavpage.cs", outfile);
         }
     }
 
diff --git a/tools/droiddoc/src/DroidDoc.java b/tools/droiddoc/src/DroidDoc.java
index b0412c9..23ff654 100644
--- a/tools/droiddoc/src/DroidDoc.java
+++ b/tools/droiddoc/src/DroidDoc.java
@@ -209,6 +209,9 @@
             writeHTMLPages();
         }
 
+        // Navigation tree
+        NavTree.writeNavTree(javadocDir);
+
         // Packages Pages
         writePackages(javadocDir
                         + (ClearPage.htmlDir!=null
@@ -475,7 +478,7 @@
                     classesToCheck = pkg.interfaces();
                     break;
                 default:
-                    System.out.println("Error reading package: " + pkg.name());
+                    System.err.println("Error reading package: " + pkg.name());
                     break;
                 }
                 for (ClassInfo cl : classesToCheck) {
@@ -516,24 +519,17 @@
                 if (len > 3 && ".cs".equals(templ.substring(len-3))) {
                     HDF data = makeHDF();
                     String filename = templ.substring(0,len-3) + htmlExtension;
-                    System.out.println("Writing CS:  " + filename);
                     ClearPage.write(data, templ, filename);
                 }
                 else if (len > 3 && ".jd".equals(templ.substring(len-3))) {
                     String filename = templ.substring(0,len-3) + htmlExtension;
-                    System.out.println("Writing JD:  " + filename);
                     DocFile.writePage(f.getAbsolutePath(), relative, filename);
                 }
                 else {
-//                    System.out.println("relative=" + relative
-//                            + " f.getAbsolutePath()=" + f.getAbsolutePath()
-//                            + " templ=" + templ);
-                    System.out.println("Copying:     " + templ);
                     ClearPage.copyFile(f, templ);
                 }
             }
             else if (f.isDirectory()) {
-                System.out.println("Writing dir: " + relative + f.getName() + "/");
                 writeDirectory(f, relative + f.getName() + "/");
             }
         }
@@ -543,7 +539,7 @@
     {
         File f = new File(ClearPage.htmlDir);
         if (!f.isDirectory()) {
-            System.out.println("htmlDir not a directory: " + ClearPage.htmlDir);
+            System.err.println("htmlDir not a directory: " + ClearPage.htmlDir);
         }
         writeDirectory(f, "");
     }
@@ -644,7 +640,7 @@
             }
         }
         catch (FileNotFoundException e) {
-            System.out.println("error writing file: " + filename);
+            System.err.println("error writing file: " + filename);
         }
         finally {
             if (stream != null) {
@@ -653,13 +649,13 @@
         }
     }
 
-    public static void writePackages(String filename)
-    {
-        System.out.println("Writing packages...");
-        HDF data = makePackageHDF();
+    private static PackageInfo[] sVisiblePackages = null;
+    public static PackageInfo[] choosePackages() {
+        if (sVisiblePackages != null) {
+            return sVisiblePackages;
+        }
 
         ClassInfo[] classes = Converter.rootClasses();
-
         SortedMap<String, PackageInfo> sorted = new TreeMap<String, PackageInfo>();
         for (ClassInfo cl: classes) {
             PackageInfo pkg = cl.containingPackage();
@@ -672,7 +668,8 @@
             sorted.put(name, pkg);
         }
 
-        int i = 0;
+        ArrayList<PackageInfo> result = new ArrayList();
+
         for (String s: sorted.keySet()) {
             PackageInfo pkg = sorted.get(s);
 
@@ -680,10 +677,13 @@
                 continue;
             }
             Boolean allHidden = true;
-            int pass = 1;
-            ClassInfo[] classesToCheck = pkg.ordinaryClasses();
+            int pass = 0;
+            ClassInfo[] classesToCheck = null;
             while (pass < 5 ) {
                 switch(pass) {
+                case 0:
+                    classesToCheck = pkg.ordinaryClasses();
+                    break;
                 case 1:
                     classesToCheck = pkg.enums();
                     break;
@@ -697,7 +697,7 @@
                     classesToCheck = pkg.interfaces();
                     break;
                 default:
-                    System.out.println("Error reading package: " + pkg.name());
+                    System.err.println("Error reading package: " + pkg.name());
                     break;
                 }
                 for (ClassInfo cl : classesToCheck) {
@@ -715,8 +715,26 @@
                 continue;
             }
 
+            result.add(pkg);
+        }
+
+        sVisiblePackages = result.toArray(new PackageInfo[result.size()]);
+        return sVisiblePackages;
+    }
+
+    public static void writePackages(String filename)
+    {
+        HDF data = makePackageHDF();
+
+        int i = 0;
+        for (PackageInfo pkg: choosePackages()) {
             writePackage(pkg);
 
+            data.setValue("docs.packages." + i + ".name", pkg.name());
+            data.setValue("docs.packages." + i + ".link", pkg.htmlPage());
+            TagInfo.makeHDF(data, "docs.packages." + i + ".shortDescr",
+                            pkg.firstSentenceTags());
+
             i++;
         }
 
@@ -739,7 +757,6 @@
         HDF data = makePackageHDF();
 
         String name = pkg.name();
-        System.out.println("Writing " + name);
 
         data.setValue("package.name", name);
         data.setValue("package.descr", "...description...");
@@ -762,8 +779,7 @@
         setPageTitle(data, name);
         ClearPage.write(data, "package.cs", filename);
 
-        filename = filename.substring(0, filename.lastIndexOf('/')+1)
-            + "package-descr" + htmlExtension;
+        filename = pkg.fullDescriptionHtmlPage();
         setPageTitle(data, name + " Details");
         ClearPage.write(data, "package-descr.cs", filename);
 
@@ -804,8 +820,6 @@
                             sorted[j].label = sorted[j].label + " (" + pkg.name() + ")";
                         }
                     }
-                } else {
-                    //System.out.println("not duplicate: " + sorted[i].label);
                 }
                 firstMatch = i;
                 lastName = s;
@@ -838,7 +852,7 @@
             cl.makeKeywordEntries(keywords);
         }
 
-        HDF data = makePackageHDF();
+        HDF data = makeHDF();
 
         Collections.sort(keywords);
         
@@ -884,7 +898,6 @@
     {
         cl.makeHDF(data);
 
-        System.out.println("Writing " + cl.name());
         setPageTitle(data, cl.name());
         ClearPage.write(data, "class.cs", cl.htmlPage());
 
diff --git a/tools/droiddoc/src/LinkReference.java b/tools/droiddoc/src/LinkReference.java
index fa821cf..bbcd4db 100644
--- a/tools/droiddoc/src/LinkReference.java
+++ b/tools/droiddoc/src/LinkReference.java
@@ -376,7 +376,8 @@
         if (result.href == null && !skipHref) {
             if (printOnErrors && (base == null || base.checkLevel())) {
                 Errors.error(Errors.UNRESOLVED_LINK, pos,
-                        "Unresolved link/see tag: " + text.trim());
+                        "Unresolved link/see tag \"" + text.trim()
+                        + "\" in " + ((base != null) ? base.qualifiedName() : "[null]"));
             }
             result.makeError();
         }
diff --git a/tools/droiddoc/src/MethodInfo.java b/tools/droiddoc/src/MethodInfo.java
index ed98378..ca30665 100644
--- a/tools/droiddoc/src/MethodInfo.java
+++ b/tools/droiddoc/src/MethodInfo.java
@@ -266,9 +266,12 @@
                         ClassInfo[] thrownExceptions, SourcePositionInfo position,
                         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, isProtected, isPackagePrivate, isPrivate,
-                isFinal, isStatic, isSynthetic, kind, position, annotations);
+                ((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
diff --git a/tools/droiddoc/src/NavTree.java b/tools/droiddoc/src/NavTree.java
new file mode 100644
index 0000000..9eef0ce
--- /dev/null
+++ b/tools/droiddoc/src/NavTree.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+import org.clearsilver.HDF;
+
+import java.util.ArrayList;
+
+public class NavTree {
+
+    public static void writeNavTree(String dir) {
+        ArrayList<Node> children = new ArrayList();
+        for (PackageInfo pkg: DroidDoc.choosePackages()) {
+            children.add(makePackageNode(pkg));
+        }
+        Node node = new Node("Reference", dir + "packages.html", children);
+
+        StringBuilder buf = new StringBuilder();
+        if (false) {
+            // if you want a root node
+            buf.append("[");
+            node.render(buf);
+            buf.append("]");
+        } else {
+            // if you don't want a root node
+            node.renderChildren(buf);
+        }
+
+        HDF data = DroidDoc.makeHDF();
+        data.setValue("reference_tree", buf.toString());
+        ClearPage.write(data, "navtree_data.cs", "navtree_data.js");
+    }
+
+    private static Node makePackageNode(PackageInfo pkg) {
+        ArrayList<Node> children = new ArrayList();
+
+        children.add(new Node("Description", pkg.fullDescriptionHtmlPage(), null));
+
+        addClassNodes(children, "Interfaces", pkg.interfaces());
+        addClassNodes(children, "Classes", pkg.ordinaryClasses());
+        addClassNodes(children, "Enums", pkg.enums());
+        addClassNodes(children, "Exceptions", pkg.exceptions());
+        addClassNodes(children, "Errors", pkg.errors());
+
+        return new Node(pkg.name(), pkg.htmlPage(), children);
+    }
+
+    private static void addClassNodes(ArrayList<Node> parent, String label, ClassInfo[] classes) {
+        ArrayList<Node> children = new ArrayList();
+
+        for (ClassInfo cl: classes) {
+            if (cl.checkLevel()) {
+                children.add(new Node(cl.name(), cl.htmlPage(), null));
+            }
+        }
+
+        if (children.size() > 0) {
+            parent.add(new Node(label, null, children));
+        }
+    }
+
+    private static class Node {
+        private String mLabel;
+        private String mLink;
+        ArrayList<Node> mChildren;
+
+        Node(String label, String link, ArrayList<Node> children) {
+            mLabel = label;
+            mLink = link;
+            mChildren = children;
+        }
+
+        static void renderString(StringBuilder buf, String s) {
+            if (s == null) {
+                buf.append("null");
+            } else {
+                buf.append('"');
+                final int N = s.length();
+                for (int i=0; i<N; i++) {
+                    char c = s.charAt(i);
+                    if (c >= ' ' && c <= '~' && c != '"' && c != '\\') {
+                        buf.append(c);
+                    } else {
+                        buf.append("\\u");
+                        for (int j=0; i<4; i++) {
+                            char x = (char)(c & 0x000f);
+                            if (x > 10) {
+                                x = (char)(x - 10 + 'a');
+                            } else {
+                                x = (char)(x + '0');
+                            }
+                            buf.append(x);
+                            c >>= 4;
+                        }
+                    }
+                }
+                buf.append('"');
+            }
+        }
+
+        void renderChildren(StringBuilder buf) {
+            ArrayList<Node> list = mChildren;
+            if (list == null || list.size() == 0) {
+                // We output null for no children.  That way empty lists here can just
+                // be a byproduct of how we generate the lists.
+                buf.append("null");
+            } else {
+                buf.append("[ ");
+                final int N = list.size();
+                for (int i=0; i<N; i++) {
+                    list.get(i).render(buf);
+                    if (i != N-1) {
+                        buf.append(", ");
+                    }
+                }
+                buf.append(" ]\n");
+            }
+        }
+
+        void render(StringBuilder buf) {
+            buf.append("[ ");
+            renderString(buf, mLabel);
+            buf.append(", ");
+            renderString(buf, mLink);
+            buf.append(", ");
+            renderChildren(buf);
+            buf.append(" ]");
+        }
+    }
+}
diff --git a/tools/droiddoc/src/PackageInfo.java b/tools/droiddoc/src/PackageInfo.java
index c696112..09b73d4 100644
--- a/tools/droiddoc/src/PackageInfo.java
+++ b/tools/droiddoc/src/PackageInfo.java
@@ -49,11 +49,10 @@
         return s;
     }
 
-    public String htmlLinksPage()
-    {
+    public String fullDescriptionHtmlPage() {
         String s = mName;
         s = s.replace('.', '/');
-        s += "/package-links.html";
+        s += "/package-descr.html";
         s = DroidDoc.javadocDir + s;
         return s;
     }
diff --git a/tools/droiddoc/src/Stubs.java b/tools/droiddoc/src/Stubs.java
index a233d68..806498b 100644
--- a/tools/droiddoc/src/Stubs.java
+++ b/tools/droiddoc/src/Stubs.java
@@ -83,8 +83,9 @@
                         if (!t.isPrimitive()) {
                             if (t.asClassInfo().isHidden()) {
                                 Errors.error(Errors.UNAVAILABLE_SYMBOL,
-                                        p.position(), "Reference to unavailable class "
-                                        + t.fullName());
+                                        m.position(), "Parameter of hidden type "
+                                        + t.fullName() + " in "
+                                        + cl.qualifiedName() + "." + m.name() + "()");
                             }
                         }
                     }
@@ -255,10 +256,19 @@
                           if (pInfo.type().typeArguments() != null){
                               for (TypeInfo tInfoType : pInfo.type().typeArguments()){
                                   if (tInfoType.asClassInfo() != null){
-                                      cantStripThis(tInfoType.asClassInfo(), notStrippable, 
-                                                    "10:" +  
-                                                    mInfo.realContainingClass().qualifiedName() + ":" + 
-                                                    mInfo.name());
+                                      ClassInfo tcl = tInfoType.asClassInfo();
+                                      if (tcl.isHidden()) {
+                                          Errors.error(Errors.UNAVAILABLE_SYMBOL, mInfo.position(),
+                                                  "Parameter of hidden type "
+                                                  + tInfoType.fullName() + " in "
+                                                  + mInfo.containingClass().qualifiedName()
+                                                  + '.' + mInfo.name() + "()");
+                                      } else {
+                                          cantStripThis(tcl, notStrippable, 
+                                                  "10:" +  
+                                                  mInfo.realContainingClass().qualifiedName() + ":" + 
+                                                  mInfo.name());
+                                      }
                                   }
                               }
                           }
@@ -552,14 +562,8 @@
         int count = 1;
         int size = method.parameters().length;
         for (ParameterInfo param: method.parameters()) {
-            String fullTypeName = param.type().fullName(method.typeVariables());
-            if (count == size && method.isVarArgs()) {
-                // TODO: note that this does not attempt to handle hypothetical
-                // vararg methods whose last parameter is a list of arrays, e.g.
-                // "Object[]...".
-                fullTypeName = param.type().qualifiedTypeName() + "...";
-            }
-            stream.print(comma + fullTypeName + " " + param.name());
+            stream.print(comma + fullParameterTypeName(method, param.type(), count == size)
+                    + " " + param.name());
             comma = ", ";
             count++;
         }
@@ -852,6 +856,7 @@
     
     static void writeMethodXML(PrintStream xmlWriter, MethodInfo mi) {
         String scope = DroidDoc.scope(mi);
+
         String deprecatedString = "";
         if (mi.isDeprecated()) {
             deprecatedString = "deprecated";
@@ -860,7 +865,7 @@
         }
         xmlWriter.println("<method name=\"" + mi.name() + "\"\n" 
                 + ((mi.returnType() != null)
-                        ? " return=\"" + mi.returnType().qualifiedTypeName() + "\"\n"
+                        ? " return=\"" + makeXMLcompliant(fullParameterTypeName(mi, mi.returnType(), false)) + "\"\n"
                         : "") 
                 + " abstract=\"" + mi.isAbstract() + "\"\n"
                 + " native=\"" + mi.isNative() + "\"\n"
@@ -873,8 +878,11 @@
                 + ">");
 
         // write parameters in declaration order
+        int numParameters = mi.parameters().length;
+        int count = 0;
         for (ParameterInfo pi : mi.parameters()) {
-            writeParameterXML(xmlWriter, pi);
+            count++;
+            writeParameterXML(xmlWriter, mi, pi, count == numParameters);
         }
         
         // but write exceptions in canonicalized order
@@ -905,6 +913,13 @@
                 //+ " source=\"" + mi.position() + "\"\n"
                 + ">");
 
+        int numParameters = mi.parameters().length;
+        int count = 0;
+        for (ParameterInfo pi : mi.parameters()) {
+            count++;
+            writeParameterXML(xmlWriter, mi, pi, count == numParameters);
+        }
+        
         ClassInfo[] exceptions = mi.thrownExceptions();
         Arrays.sort(exceptions, ClassInfo.comparator);
         for (ClassInfo pi : exceptions) {
@@ -915,9 +930,10 @@
         xmlWriter.println("</constructor>");
   }
     
-    static void writeParameterXML(PrintStream xmlWriter, ParameterInfo pi) {
+    static void writeParameterXML(PrintStream xmlWriter, MethodInfo method,
+            ParameterInfo pi, boolean isLast) {
         xmlWriter.println("<parameter name=\"" + pi.name() + "\" type=\"" +
-                          pi.type().qualifiedTypeName() + "\">");
+                makeXMLcompliant(fullParameterTypeName(method, pi.type(), isLast)) + "\">");
         xmlWriter.println("</parameter>");
     }
     
@@ -931,8 +947,12 @@
         }
         //need to make sure value is valid XML
         String value  = makeXMLcompliant(fi.constantLiteralValue());
+
+        String fullTypeName = makeXMLcompliant(fi.type().qualifiedTypeName())
+                + fi.type().dimension();
+
         xmlWriter.println("<field name=\"" + fi.name() +"\"\n"
-                          + " type=\"" + fi.type().qualifiedTypeName() + "\"\n"
+                          + " type=\"" + fullTypeName + "\"\n"
                           + " transient=\"" + fi.isTransient() + "\"\n"
                           + " volatile=\"" + fi.isVolatile() + "\"\n"
                           + (fieldIsInitialized(fi) ? " value=\"" + value + "\"\n" : "") 
@@ -954,5 +974,16 @@
         returnString = returnString.replaceAll("'", "&pos;");
         return returnString;
     }
+    
+    static String fullParameterTypeName(MethodInfo method, TypeInfo type, boolean isLast) {
+        String fullTypeName = type.fullName(method.typeVariables());
+        if (isLast && method.isVarArgs()) {
+            // TODO: note that this does not attempt to handle hypothetical
+            // vararg methods whose last parameter is a list of arrays, e.g.
+            // "Object[]...".
+            fullTypeName = type.qualifiedTypeName() + "...";
+        }
+        return fullTypeName;
+    }
 }