auto import from //depot/cupcake/@135843
diff --git a/tools/droiddoc/Android.mk b/tools/droiddoc/Android.mk
new file mode 100644
index 0000000..d2d7a95
--- /dev/null
+++ b/tools/droiddoc/Android.mk
@@ -0,0 +1,18 @@
+# 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.
+
+LOCAL_PATH := $(call my-dir)
+
+include $(LOCAL_PATH)/src/Android.mk
+
diff --git a/tools/droiddoc/src/Android.mk b/tools/droiddoc/src/Android.mk
new file mode 100644
index 0000000..abd2581
--- /dev/null
+++ b/tools/droiddoc/src/Android.mk
@@ -0,0 +1,69 @@
+# 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.
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := docs
+
+LOCAL_SRC_FILES := \
+    AnnotationInstanceInfo.java \
+    AnnotationValueInfo.java \
+	AttributeInfo.java \
+	AttrTagInfo.java \
+	ClassInfo.java \
+	DroidDoc.java \
+	ClearPage.java \
+	Comment.java \
+	ContainerInfo.java \
+	Converter.java \
+	DocFile.java \
+	DocInfo.java \
+	Errors.java \
+	FieldInfo.java \
+	Hierarchy.java \
+	InheritedTags.java \
+	KeywordEntry.java \
+    LinkReference.java \
+	LiteralTagInfo.java \
+	MemberInfo.java \
+	MethodInfo.java \
+	NavTree.java \
+	PackageInfo.java \
+	ParamTagInfo.java \
+	ParameterInfo.java \
+	ParsedTagInfo.java \
+	Proofread.java \
+	SampleCode.java \
+	SampleTagInfo.java \
+    Scoped.java \
+	SeeTagInfo.java \
+	Sorter.java \
+	SourcePositionInfo.java \
+    Stubs.java \
+	TagInfo.java \
+    TextTagInfo.java \
+	ThrowsTagInfo.java \
+	TodoFile.java \
+	TypeInfo.java
+
+LOCAL_JAVA_LIBRARIES := \
+	clearsilver
+
+LOCAL_CLASSPATH := \
+	$(HOST_JDK_TOOLS_JAR)
+
+LOCAL_MODULE:= droiddoc
+
+include $(BUILD_HOST_JAVA_LIBRARY)
diff --git a/tools/droiddoc/src/AnnotationInstanceInfo.java b/tools/droiddoc/src/AnnotationInstanceInfo.java
new file mode 100644
index 0000000..07d4aa3
--- /dev/null
+++ b/tools/droiddoc/src/AnnotationInstanceInfo.java
@@ -0,0 +1,59 @@
+/*
+ * 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.
+ */
+
+class AnnotationInstanceInfo
+{
+    private ClassInfo mType;
+    private AnnotationValueInfo[] mElementValues;
+
+    public AnnotationInstanceInfo(ClassInfo type, AnnotationValueInfo[] elementValues)
+    {
+        mType = type;
+        mElementValues = elementValues;
+    }
+
+    ClassInfo type()
+    {
+        return mType;
+    }
+
+    AnnotationValueInfo[] elementValues()
+    {
+        return mElementValues;
+    }
+
+    public String toString()
+    {
+        StringBuilder str = new StringBuilder();
+        str.append("@");
+        str.append(mType.qualifiedName());
+        str.append("(");
+        AnnotationValueInfo[] values = mElementValues;
+        final int N = values.length;
+        for (int i=0; i<N; i++) {
+            AnnotationValueInfo value = values[i];
+            str.append(value.element().name());
+            str.append("=");
+            str.append(value.valueString());
+            if (i != N-1) {
+                str.append(",");
+            }
+        }
+        str.append(")");
+        return str.toString();
+    }
+}
+
diff --git a/tools/droiddoc/src/AnnotationValueInfo.java b/tools/droiddoc/src/AnnotationValueInfo.java
new file mode 100644
index 0000000..a2d869a
--- /dev/null
+++ b/tools/droiddoc/src/AnnotationValueInfo.java
@@ -0,0 +1,79 @@
+/*
+ * 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.
+ */
+
+public class AnnotationValueInfo
+{
+    private Object mValue;
+    private String mString;
+    private MethodInfo mElement;
+
+    public AnnotationValueInfo(MethodInfo element)
+    {
+        mElement = element;
+    }
+
+    public void init(Object value)
+    {
+        mValue = value;
+    }
+
+    public MethodInfo element()
+    {
+        return mElement;
+    }
+
+    public Object value()
+    {
+        return mValue;
+    }
+
+    public String valueString()
+    {
+        Object v = mValue;
+        if (v instanceof TypeInfo) {
+            return ((TypeInfo)v).fullName();
+        }
+        else if (v instanceof FieldInfo) {
+            StringBuilder str = new StringBuilder();
+            FieldInfo f = (FieldInfo)v;
+            str.append(f.containingClass().qualifiedName());
+            str.append('.');
+            str.append(f.name());
+            return str.toString();
+        }
+        else if (v instanceof AnnotationInstanceInfo) {
+            return v.toString();
+        }
+        else if (v instanceof AnnotationValueInfo[]) {
+            StringBuilder str = new StringBuilder();
+            AnnotationValueInfo[] array = (AnnotationValueInfo[])v;
+            final int N = array.length;
+            str.append("{");
+            for (int i=0; i<array.length; i++) {
+                str.append(array[i].valueString());
+                if (i != N-1) {
+                    str.append(",");
+                }
+            }
+            str.append("}");
+            return str.toString();
+        }
+        else {
+            return FieldInfo.constantLiteralValue(v);
+        }
+    }
+}
+
diff --git a/tools/droiddoc/src/AttrTagInfo.java b/tools/droiddoc/src/AttrTagInfo.java
new file mode 100644
index 0000000..abc5452
--- /dev/null
+++ b/tools/droiddoc/src/AttrTagInfo.java
@@ -0,0 +1,140 @@
+/*
+ * 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 java.util.regex.Pattern;
+import java.util.regex.Matcher;
+import org.clearsilver.HDF;
+import org.clearsilver.CS;
+
+
+public class AttrTagInfo extends TagInfo
+{
+    private static final String REF_COMMAND = "ref";
+    private static final String NAME_COMMAND = "name";
+    private static final String DESCRIPTION_COMMAND = "description";
+    private static final Pattern TEXT = Pattern.compile("(\\S+)\\s*(.*)", Pattern.DOTALL);
+    private static final Pattern NAME_TEXT = Pattern.compile("(\\S+)(.*)",
+                                                Pattern.DOTALL);
+
+    private ContainerInfo mBase;
+    private String mCommand;
+
+    // if mCommand == "ref"
+    private FieldInfo mRefField;
+    private AttributeInfo mAttrInfo;
+
+    // if mCommand == "name"
+    private String mAttrName;
+
+    // if mCommand == "description"
+    private Comment mDescrComment;
+
+    AttrTagInfo(String name, String kind, String text, ContainerInfo base,
+            SourcePositionInfo position)
+    {
+        super(name, kind, text, position);
+        mBase = base;
+
+        parse(text, base, position);
+    }
+
+    void parse(String text, ContainerInfo base, SourcePositionInfo position) {
+        Matcher m;
+
+        m = TEXT.matcher(text);
+        if (!m.matches()) {
+            Errors.error(Errors.BAD_ATTR_TAG, position, "Bad @attr tag: " + text);
+            return;
+        }
+
+        String command = m.group(1);
+        String more = m.group(2);
+
+        if (REF_COMMAND.equals(command)) {
+            String ref = more.trim();
+            LinkReference linkRef = LinkReference.parse(ref, mBase, position, false);
+            if (!linkRef.good) {
+                Errors.error(Errors.BAD_ATTR_TAG, position, "Unresolved @attr ref: " + ref);
+                return;
+            }
+            if (!(linkRef.memberInfo instanceof FieldInfo)) {
+                Errors.error(Errors.BAD_ATTR_TAG, position, "@attr must be a field: " + ref);
+                return;
+            }
+            mCommand = command;
+            mRefField = (FieldInfo)linkRef.memberInfo;
+        }
+        else if (NAME_COMMAND.equals(command)) {
+            m = NAME_TEXT.matcher(more);
+            if (!m.matches() || m.group(2).trim().length() != 0) {
+                Errors.error(Errors.BAD_ATTR_TAG, position, "Bad @attr name tag: " + more);
+                return;
+            }
+            mCommand = command;
+            mAttrName = m.group(1);
+        }
+        else if (DESCRIPTION_COMMAND.equals(command)) {
+            mCommand = command;
+            mDescrComment = new Comment(more, base, position);
+        }
+        else {
+            Errors.error(Errors.BAD_ATTR_TAG, position, "Bad @attr command: " + command);
+        }
+    }
+
+    public FieldInfo reference() {
+        return REF_COMMAND.equals(mCommand) ? mRefField : null;
+    }
+    
+    public String name() {
+        return NAME_COMMAND.equals(mCommand) ? mAttrName : null;
+    }
+
+    public Comment description() {
+        return DESCRIPTION_COMMAND.equals(mCommand) ? mDescrComment : null;
+    }
+
+    public void makeHDF(HDF data, String base)
+    {
+        super.makeHDF(data, base);
+    }
+
+    public void setAttribute(AttributeInfo info) {
+        mAttrInfo = info;
+    }
+
+    public static void makeReferenceHDF(HDF data, String base, AttrTagInfo[] tags)
+    {
+        int i=0;
+        for (AttrTagInfo t: tags) {
+            if (REF_COMMAND.equals(t.mCommand)) {
+                if (t.mAttrInfo == null) {
+                    String msg = "ERROR: unlinked attr: " + t.mRefField.name();
+                    if (false) {
+                        System.out.println(msg);
+                    } else {
+                        throw new RuntimeException(msg);
+                    }
+                } else {
+                    data.setValue(base + "." + i + ".name", t.mAttrInfo.name());
+                    data.setValue(base + "." + i + ".href", t.mAttrInfo.htmlPage());
+                    i++;
+                }
+            }
+        }
+    }
+
+}
diff --git a/tools/droiddoc/src/AttributeInfo.java b/tools/droiddoc/src/AttributeInfo.java
new file mode 100644
index 0000000..a24106b
--- /dev/null
+++ b/tools/droiddoc/src/AttributeInfo.java
@@ -0,0 +1,100 @@
+/*
+ * 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 java.util.ArrayList;
+import java.util.Comparator;
+
+import org.clearsilver.HDF;
+import org.clearsilver.CS;
+
+public class AttributeInfo {
+    public static final Comparator<AttributeInfo> comparator = new Comparator<AttributeInfo>() {
+        public int compare(AttributeInfo a, AttributeInfo b) {
+            return a.name().compareTo(b.name());
+        }
+    };
+    
+    public FieldInfo attrField;
+    public ArrayList<MethodInfo> methods = new ArrayList<MethodInfo>();
+    
+    private ClassInfo mClass;
+    private String mName;
+    private Comment mComment;
+
+    public AttributeInfo(ClassInfo cl, FieldInfo f) {
+        mClass = cl;
+        attrField = f;
+    }
+
+    public String name() {
+        if (mName == null) {
+            for (AttrTagInfo comment: attrField.comment().attrTags()) {
+                String n = comment.name();
+                if (n != null) {
+                    mName = n;
+                    return n;
+                }
+            }
+        }
+        return mName;
+    }
+
+    public Comment comment() {
+        if (mComment == null) {
+            for (AttrTagInfo attr: attrField.comment().attrTags()) {
+                Comment c = attr.description();
+                if (c != null) {
+                    mComment = c;
+                    return c;
+                }
+            }
+        }
+        if (mComment == null) {
+            return new Comment("", mClass, new SourcePositionInfo());
+        }
+        return mComment;
+    }
+    
+    public String anchor() {
+        return "attr_" + name();
+    }
+    public String htmlPage() {
+        return mClass.htmlPage() + "#" + anchor();
+    }
+
+    public void makeHDF(HDF data, String base) {
+        data.setValue(base + ".name", name());
+        data.setValue(base + ".anchor", anchor());
+        data.setValue(base + ".href", htmlPage());
+        data.setValue(base + ".R.name", attrField.name());
+        data.setValue(base + ".R.href", attrField.htmlPage());
+        TagInfo.makeHDF(data, base + ".deprecated", attrField.comment().deprecatedTags());
+        TagInfo.makeHDF(data, base + ".shortDescr", comment().briefTags());
+        TagInfo.makeHDF(data, base + ".descr", comment().tags());
+
+        int i=0;
+        for (MethodInfo m: methods) {
+            String s = base + ".methods." + i;
+            data.setValue(s + ".href", m.htmlPage());
+            data.setValue(s + ".name", m.name() + m.prettySignature());
+        }
+    }
+
+    public boolean checkLevel() {
+        return attrField.checkLevel();
+    }
+}
+
diff --git a/tools/droiddoc/src/ClassInfo.java b/tools/droiddoc/src/ClassInfo.java
new file mode 100644
index 0000000..36edbf8
--- /dev/null
+++ b/tools/droiddoc/src/ClassInfo.java
@@ -0,0 +1,1463 @@
+/*
+ * 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 com.sun.javadoc.*;
+import com.sun.tools.doclets.*;
+import org.clearsilver.HDF;
+import org.clearsilver.CS;
+import java.util.*;
+import java.io.*;
+
+public class ClassInfo extends DocInfo implements ContainerInfo, Comparable, Scoped
+{
+    public static final Comparator<ClassInfo> comparator = new Comparator<ClassInfo>() {
+        public int compare(ClassInfo a, ClassInfo b) {
+            return a.name().compareTo(b.name());
+        }
+    };
+
+    public static final Comparator<ClassInfo> qualifiedComparator = new Comparator<ClassInfo>() {
+        public int compare(ClassInfo a, ClassInfo b) {
+            return a.qualifiedName().compareTo(b.qualifiedName());
+        }
+    };
+
+    public ClassInfo(
+            ClassDoc cl,
+            String rawCommentText, SourcePositionInfo position,
+            boolean isPublic, boolean isProtected, boolean isPackagePrivate,
+            boolean isPrivate, boolean isStatic,
+            boolean isInterface, boolean isAbstract, boolean isOrdinaryClass,
+            boolean isException, boolean isError, boolean isEnum, boolean isAnnotation,
+            boolean isFinal, boolean isIncluded, String name,
+            String qualifiedName, String qualifiedTypeName, boolean isPrimitive)
+    {
+        super(rawCommentText, position);
+
+        mClass = cl;
+        mIsPublic = isPublic;
+        mIsProtected = isProtected;
+        mIsPackagePrivate = isPackagePrivate;
+        mIsPrivate = isPrivate;
+        mIsStatic = isStatic;
+        mIsInterface = isInterface;
+        mIsAbstract = isAbstract;
+        mIsOrdinaryClass = isOrdinaryClass;
+        mIsException = isException;
+        mIsError = isError;
+        mIsEnum = isEnum;
+        mIsAnnotation = isAnnotation;
+        mIsFinal = isFinal;
+        mIsIncluded = isIncluded;
+        mName = name;
+        mQualifiedName = qualifiedName;
+        mQualifiedTypeName = qualifiedTypeName;
+        mIsPrimitive = isPrimitive;
+        mNameParts = name.split("\\.");
+    }
+
+    public void init(TypeInfo typeInfo, ClassInfo[] interfaces, TypeInfo[] interfaceTypes,
+            ClassInfo[] innerClasses,
+            MethodInfo[] constructors, MethodInfo[] methods, MethodInfo[] annotationElements,
+            FieldInfo[] fields, FieldInfo[] enumConstants,
+            PackageInfo containingPackage, ClassInfo containingClass,
+            ClassInfo superclass, TypeInfo superclassType, AnnotationInstanceInfo[] annotations)
+    {
+        mTypeInfo = typeInfo;
+        mRealInterfaces = interfaces;
+        mRealInterfaceTypes = interfaceTypes;
+        mInnerClasses = innerClasses;
+        mAllConstructors = constructors;
+        mAllSelfMethods = methods;
+        mAnnotationElements = annotationElements;
+        mAllSelfFields = fields;
+        mEnumConstants = enumConstants;
+        mContainingPackage = containingPackage;
+        mContainingClass = containingClass;
+        mRealSuperclass = superclass;
+        mRealSuperclassType = superclassType;
+        mAnnotations = annotations;
+
+        // after providing new methods and new superclass info,clear any cached
+        // lists of self + superclass methods, ctors, etc.
+        mSuperclassInit = false;
+        mConstructors = null;
+        mMethods = null;
+        mSelfMethods = null;
+        mFields = null;
+        mSelfFields = null;
+        mSelfAttributes = null;
+        mDeprecatedKnown = false;
+        
+        Arrays.sort(mEnumConstants, FieldInfo.comparator);
+        Arrays.sort(mInnerClasses, ClassInfo.comparator);
+    }
+
+    public void init2() {
+        // calling this here forces the AttrTagInfo objects to be linked to the AttribtueInfo
+        // objects
+        selfAttributes();
+    }
+    
+    public void init3(TypeInfo[] types, ClassInfo[] realInnerClasses){
+      mTypeParameters = types;
+      mRealInnerClasses = realInnerClasses;
+    }
+    
+    public ClassInfo[] getRealInnerClasses(){
+      return mRealInnerClasses;
+    }
+    
+    public TypeInfo[] getTypeParameters(){
+      return mTypeParameters;
+    }
+
+    public boolean checkLevel()
+    {
+        int val = mCheckLevel;
+        if (val >= 0) {
+            return val != 0;
+        } else {
+            boolean v = DroidDoc.checkLevel(mIsPublic, mIsProtected,
+                                                mIsPackagePrivate, mIsPrivate, isHidden());
+            mCheckLevel = v ? 1 : 0;
+            return v;
+        }
+    }
+
+    public int compareTo(Object that) {
+        if (that instanceof ClassInfo) {
+            return mQualifiedName.compareTo(((ClassInfo)that).mQualifiedName);
+        } else {
+            return this.hashCode() - that.hashCode();
+        }
+    }
+
+    public ContainerInfo parent()
+    {
+        return this;
+    }
+
+    public boolean isPublic()
+    {
+        return mIsPublic;
+    }
+
+    public boolean isProtected()
+    {
+        return mIsProtected;
+    }
+
+    public boolean isPackagePrivate()
+    {
+        return mIsPackagePrivate;
+    }
+
+    public boolean isPrivate()
+    {
+        return mIsPrivate;
+    }
+
+    public boolean isStatic()
+    {
+        return mIsStatic;
+    }
+
+    public boolean isInterface()
+    {
+        return mIsInterface;
+    }
+
+    public boolean isAbstract()
+    {
+        return mIsAbstract;
+    }
+
+    public PackageInfo containingPackage()
+    {
+        return mContainingPackage;
+    }
+
+    public ClassInfo containingClass()
+    {
+        return mContainingClass;
+    }
+
+    public boolean isOrdinaryClass()
+    {
+        return mIsOrdinaryClass;
+    }
+
+    public boolean isException()
+    {
+        return mIsException;
+    }
+
+    public boolean isError()
+    {
+        return mIsError;
+    }
+
+    public boolean isEnum()
+    {
+        return mIsEnum;
+    }
+
+    public boolean isAnnotation()
+    {
+        return mIsAnnotation;
+    }
+
+    public boolean isFinal()
+    {
+        return mIsFinal;
+    }
+
+    public boolean isIncluded()
+    {
+        return mIsIncluded;
+    }
+
+    public HashSet<String> typeVariables()
+    {
+        HashSet<String> result = TypeInfo.typeVariables(mTypeInfo.typeArguments());
+        ClassInfo cl = containingClass();
+        while (cl != null) {
+            TypeInfo[] types = cl.asTypeInfo().typeArguments();
+            if (types != null) {
+                TypeInfo.typeVariables(types, result);
+            }
+            cl = cl.containingClass();
+        }
+        return result;
+    }
+
+    private static void gatherHiddenInterfaces(ClassInfo cl, HashSet<ClassInfo> interfaces) {
+        for (ClassInfo iface: cl.mRealInterfaces) {
+            if (iface.checkLevel()) {
+                interfaces.add(iface);
+            } else {
+                gatherHiddenInterfaces(iface, interfaces);
+            }
+        }
+    }
+
+    public ClassInfo[] interfaces()
+    {
+        if (mInterfaces == null) {
+            if (checkLevel()) {
+                HashSet<ClassInfo> interfaces = new HashSet<ClassInfo>();
+                ClassInfo superclass = mRealSuperclass;
+                while (superclass != null && !superclass.checkLevel()) {
+                    gatherHiddenInterfaces(superclass, interfaces);
+                    superclass = superclass.mRealSuperclass;
+                }
+                gatherHiddenInterfaces(this, interfaces);
+                mInterfaces = interfaces.toArray(new ClassInfo[interfaces.size()]);
+            } else {
+                // put something here in case someone uses it
+                mInterfaces = mRealInterfaces;
+            }
+            Arrays.sort(mInterfaces, ClassInfo.qualifiedComparator);
+        }
+        return mInterfaces;
+    }
+
+    public ClassInfo[] realInterfaces()
+    {
+        return mRealInterfaces;
+    }
+
+    TypeInfo[] realInterfaceTypes()
+    {
+        return mRealInterfaceTypes;
+    }
+
+    public String name()
+    {
+        return mName;
+    }
+
+    public String[] nameParts()
+    {
+        return mNameParts;
+    }
+
+    public String leafName()
+    {
+        return mNameParts[mNameParts.length-1];
+    }
+
+    public String qualifiedName()
+    {
+        return mQualifiedName;
+    }
+
+    public String qualifiedTypeName()
+    {
+        return mQualifiedTypeName;
+    }
+
+    public boolean isPrimitive()
+    {
+        return mIsPrimitive;
+    }
+
+    public MethodInfo[] allConstructors() {
+        return mAllConstructors;
+    }
+
+    public MethodInfo[] constructors()
+    {
+        if (mConstructors == null) {
+            MethodInfo[] methods = mAllConstructors;
+            ArrayList<MethodInfo> ctors = new ArrayList<MethodInfo>();
+            for (int i=0; i<methods.length; i++) {
+                MethodInfo m = methods[i];
+                if (!m.isHidden()) {
+                    ctors.add(m);
+                }
+            }
+            mConstructors = ctors.toArray(new MethodInfo[ctors.size()]);
+            Arrays.sort(mConstructors, MethodInfo.comparator);
+        }
+        return mConstructors;
+    }
+
+    public ClassInfo[] innerClasses()
+    {
+        return mInnerClasses;
+    }
+
+    public TagInfo[] inlineTags()
+    {
+        return comment().tags();
+    }
+
+    public TagInfo[] firstSentenceTags()
+    {
+        return comment().briefTags();
+    }
+    
+    public boolean isDeprecated() {
+        boolean deprecated = false;
+        if (!mDeprecatedKnown) {
+            boolean commentDeprecated = (comment().deprecatedTags().length > 0);
+            boolean annotationDeprecated = false;
+            for (AnnotationInstanceInfo annotation : annotations()) {
+                if (annotation.type().qualifiedName().equals("java.lang.Deprecated")) {
+                    annotationDeprecated = true;
+                    break;
+                }
+            }
+
+            if (commentDeprecated != annotationDeprecated) {
+                Errors.error(Errors.DEPRECATION_MISMATCH, position(),
+                        "Class " + qualifiedName()
+                        + ": @Deprecated annotation and @deprecated comment do not match");
+            }
+
+            mIsDeprecated = commentDeprecated | annotationDeprecated;
+            mDeprecatedKnown = true;
+        }
+        return mIsDeprecated;
+    }
+
+    public TagInfo[] deprecatedTags()
+    {
+        TagInfo[] result = comment().deprecatedTags();
+        if (result.length == 0) {
+            if (comment().undeprecateTags().length == 0) {
+                if (superclass() != null) {
+                    result = superclass().deprecatedTags();
+                }
+            }
+        }
+        // should we also do the interfaces?
+        return result;
+    }
+
+    public MethodInfo[] methods()
+    {
+        if (mMethods == null) {
+            TreeMap<String,MethodInfo> all = new TreeMap<String,MethodInfo>();
+
+            ClassInfo[] ifaces = interfaces();
+            for (ClassInfo iface: ifaces) {
+                if (iface != null) {
+                    MethodInfo[] inhereted = iface.methods();
+                    for (MethodInfo method: inhereted) {
+                        String key = method.name() + method.signature();
+                        all.put(key, method);
+                    }
+                }
+            }
+
+            ClassInfo superclass = superclass();
+            if (superclass != null) {
+                MethodInfo[] inhereted = superclass.methods();
+                for (MethodInfo method: inhereted) {
+                    String key = method.name() + method.signature();
+                    all.put(key, method);
+                }
+            }
+
+            MethodInfo[] methods = selfMethods();
+            for (MethodInfo method: methods) {
+                String key = method.name() + method.signature();
+                MethodInfo old = all.put(key, method);
+            }
+
+            mMethods = all.values().toArray(new MethodInfo[all.size()]);
+        }
+        return mMethods;
+    }
+
+    public MethodInfo[] annotationElements()
+    {
+        return mAnnotationElements;
+    }
+
+    public AnnotationInstanceInfo[] annotations()
+    {
+        return mAnnotations;
+    }
+
+    private static void addFields(ClassInfo cl, TreeMap<String,FieldInfo> all)
+    {
+        FieldInfo[] fields = cl.fields();
+        int N = fields.length;
+        for (int i=0; i<N; i++) {
+            FieldInfo f = fields[i];
+            all.put(f.name(), f);
+        }
+    }
+
+    public FieldInfo[] fields()
+    {
+        if (mFields == null) {
+            int N;
+            TreeMap<String,FieldInfo> all = new TreeMap<String,FieldInfo>();
+
+            ClassInfo[] interfaces = interfaces();
+            N = interfaces.length;
+            for (int i=0; i<N; i++) {
+                addFields(interfaces[i], all);
+            }
+
+            ClassInfo superclass = superclass();
+            if (superclass != null) {
+                addFields(superclass, all);
+            }
+
+            FieldInfo[] fields = selfFields();
+            N = fields.length;
+            for (int i=0; i<N; i++) {
+                FieldInfo f = fields[i];
+                if (!f.isHidden()) {
+                    String key = f.name();
+                    all.put(key, f);
+                }
+            }
+
+            mFields = all.values().toArray(new FieldInfo[0]);
+        }
+        return mFields;
+    }
+
+    public void gatherFields(ClassInfo owner, ClassInfo cl, HashMap<String,FieldInfo> fields) {
+        FieldInfo[] flds = cl.selfFields();
+        for (FieldInfo f: flds) {
+            if (f.checkLevel()) {
+                fields.put(f.name(), f.cloneForClass(owner));
+            }
+        }
+    }
+
+    public FieldInfo[] selfFields()
+    {
+        if (mSelfFields == null) {
+            HashMap<String,FieldInfo> fields = new HashMap<String,FieldInfo>();
+            // our hidden parents
+            if (mRealSuperclass != null && !mRealSuperclass.checkLevel()) {
+                gatherFields(this, mRealSuperclass, fields);
+            }
+            for (ClassInfo iface: mRealInterfaces) {
+                if (!iface.checkLevel()) {
+                    gatherFields(this, iface, fields);
+                }
+            }
+            // mine
+            FieldInfo[] selfFields = mAllSelfFields;
+            for (int i=0; i<selfFields.length; i++) {
+                FieldInfo f = selfFields[i];
+                if (!f.isHidden()) {
+                    fields.put(f.name(), f);
+                }
+            }
+            // combine and return in
+            mSelfFields = fields.values().toArray(new FieldInfo[fields.size()]);
+            Arrays.sort(mSelfFields, FieldInfo.comparator);
+        }
+        return mSelfFields;
+    }
+
+    public FieldInfo[] allSelfFields() {
+        return mAllSelfFields;
+    }
+
+    public void gatherMethods(ClassInfo owner, ClassInfo cl, HashMap<String,MethodInfo> methods) {
+        MethodInfo[] meth = cl.selfMethods();
+        for (MethodInfo m: meth) {
+            if (m.checkLevel()) {
+                methods.put(m.name()+m.signature(), m.cloneForClass(owner));
+            }
+        }
+    }
+
+    public MethodInfo[] selfMethods()
+    {
+        if (mSelfMethods == null) {
+            HashMap<String,MethodInfo> methods = new HashMap<String,MethodInfo>();
+            // our hidden parents
+            if (mRealSuperclass != null && !mRealSuperclass.checkLevel()) {
+                gatherMethods(this, mRealSuperclass, methods);
+            }
+            for (ClassInfo iface: mRealInterfaces) {
+                if (!iface.checkLevel()) {
+                    gatherMethods(this, iface, methods);
+                }
+            }
+            // mine
+            MethodInfo[] selfMethods = mAllSelfMethods;
+            for (int i=0; i<selfMethods.length; i++) {
+                MethodInfo m = selfMethods[i];
+                if (m.checkLevel()) {
+                    methods.put(m.name()+m.signature(), m);
+                }
+            }
+            // combine and return it
+            mSelfMethods = methods.values().toArray(new MethodInfo[methods.size()]);
+            Arrays.sort(mSelfMethods, MethodInfo.comparator);
+        }
+        return mSelfMethods;
+    }
+
+    public MethodInfo[] allSelfMethods() {
+        return mAllSelfMethods;
+    }
+    
+    public void addMethod(MethodInfo method) {
+        MethodInfo[] methods = new MethodInfo[mAllSelfMethods.length + 1];
+        int i = 0;
+        for (MethodInfo m : mAllSelfMethods) {
+            methods[i] = m;
+            i++;
+        }
+        methods[i] = method;
+        mAllSelfMethods = methods;
+    }
+
+    public AttributeInfo[] selfAttributes()
+    {
+        if (mSelfAttributes == null) {
+            TreeMap<FieldInfo,AttributeInfo> attrs = new TreeMap<FieldInfo,AttributeInfo>();
+
+            // the ones in the class comment won't have any methods
+            for (AttrTagInfo tag: comment().attrTags()) {
+                FieldInfo field = tag.reference();
+                if (field != null) {
+                    AttributeInfo attr = attrs.get(field);
+                    if (attr == null) {
+                        attr = new AttributeInfo(this, field);
+                        attrs.put(field, attr);
+                    }
+                    tag.setAttribute(attr);
+                }
+            }
+
+            // in the methods
+            for (MethodInfo m: selfMethods()) {
+                for (AttrTagInfo tag: m.comment().attrTags()) {
+                    FieldInfo field = tag.reference();
+                    if (field != null) {
+                        AttributeInfo attr = attrs.get(field);
+                        if (attr == null) {
+                            attr = new AttributeInfo(this, field);
+                            attrs.put(field, attr);
+                        }
+                        tag.setAttribute(attr);
+                        attr.methods.add(m);
+                    }
+                }
+            }
+            
+            //constructors too
+           for (MethodInfo m: constructors()) {
+              for (AttrTagInfo tag: m.comment().attrTags()) {
+                  FieldInfo field = tag.reference();
+                  if (field != null) {
+                      AttributeInfo attr = attrs.get(field);
+                      if (attr == null) {
+                          attr = new AttributeInfo(this, field);
+                          attrs.put(field, attr);
+                      }
+                      tag.setAttribute(attr);
+                      attr.methods.add(m);
+                  }
+              }
+          }
+
+            mSelfAttributes = attrs.values().toArray(new AttributeInfo[attrs.size()]);
+            Arrays.sort(mSelfAttributes, AttributeInfo.comparator);
+        }
+        return mSelfAttributes;
+    }
+
+    public FieldInfo[] enumConstants()
+    {
+        return mEnumConstants;
+    }
+
+    public ClassInfo superclass()
+    {
+        if (!mSuperclassInit) {
+            if (this.checkLevel()) {
+                // rearrange our little inheritance hierarchy, because we need to hide classes that
+                // don't pass checkLevel
+                ClassInfo superclass = mRealSuperclass;
+                while (superclass != null && !superclass.checkLevel()) {
+                    superclass = superclass.mRealSuperclass;
+                }
+                mSuperclass = superclass;
+            } else {
+                mSuperclass = mRealSuperclass;
+            }
+        }
+        return mSuperclass;
+    }
+
+    public ClassInfo realSuperclass()
+    {
+        return mRealSuperclass;
+    }
+
+    /** always the real superclass, not the collapsed one we get through superclass(),
+     * also has the type parameter info if it's generic.
+     */
+    public TypeInfo superclassType()
+    {
+        return mRealSuperclassType;
+    }
+
+    public TypeInfo asTypeInfo()
+    {
+        return mTypeInfo;
+    }
+
+    TypeInfo[] interfaceTypes()
+    {
+        ClassInfo[] infos = interfaces();
+        int len = infos.length;
+        TypeInfo[] types = new TypeInfo[len];
+        for (int i=0; i<len; i++) {
+            types[i] = infos[i].asTypeInfo();
+        }
+        return types;
+    }
+
+    public String htmlPage()
+    {
+        String s = containingPackage().name();
+        s = s.replace('.', '/');
+        s += '/';
+        s += name();
+        s += ".html";
+        s = DroidDoc.javadocDir + s;
+        return s;
+    }
+
+    /** Even indirectly */
+    public boolean isDerivedFrom(ClassInfo cl)
+    {
+        ClassInfo dad = this.superclass();
+        if (dad != null) {
+            if (dad.equals(cl)) {
+                return true;
+            } else {
+                if (dad.isDerivedFrom(cl)) {
+                    return true;
+                }
+            }
+        }
+        for (ClassInfo iface: interfaces()) {
+            if (iface.equals(cl)) {
+                return true;
+            } else {
+                if (iface.isDerivedFrom(cl)) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    public void makeKeywordEntries(List<KeywordEntry> keywords)
+    {
+        if (!checkLevel()) {
+            return;
+        }
+
+        String htmlPage = htmlPage();
+        String qualifiedName = qualifiedName();
+
+        keywords.add(new KeywordEntry(name(), htmlPage,
+                "class in " + containingPackage().name()));
+
+        FieldInfo[] fields = selfFields();
+        FieldInfo[] enumConstants = enumConstants();
+        MethodInfo[] ctors = constructors();
+        MethodInfo[] methods = selfMethods();
+
+        // enum constants
+        for (FieldInfo field: enumConstants()) {
+            if (field.checkLevel()) {
+                keywords.add(new KeywordEntry(field.name(),
+                            htmlPage + "#" + field.anchor(),
+                            "enum constant in " + qualifiedName));
+            }
+        }
+
+        // constants
+        for (FieldInfo field: fields) {
+            if (field.isConstant() && field.checkLevel()) {
+                keywords.add(new KeywordEntry(field.name(),
+                            htmlPage + "#" + field.anchor(),
+                            "constant in " + qualifiedName));
+            }
+        }
+
+        // fields
+        for (FieldInfo field: fields) {
+            if (!field.isConstant() && field.checkLevel()) {
+                keywords.add(new KeywordEntry(field.name(),
+                            htmlPage + "#" + field.anchor(),
+                            "field in " + qualifiedName));
+            }
+        }
+
+        // public constructors
+        for (MethodInfo m: ctors) {
+            if (m.isPublic() && m.checkLevel()) {
+                keywords.add(new KeywordEntry(m.name() + m.prettySignature(),
+                            htmlPage + "#" + m.anchor(),
+                            "constructor in " + qualifiedName));
+            }
+        }
+
+        // protected constructors
+        if (DroidDoc.checkLevel(DroidDoc.SHOW_PROTECTED)) {
+            for (MethodInfo m: ctors) {
+                if (m.isProtected() && m.checkLevel()) {
+                    keywords.add(new KeywordEntry(m.name() + m.prettySignature(),
+                                htmlPage + "#" + m.anchor(),
+                                "constructor in " + qualifiedName));
+                }
+            }
+        }
+
+        // package private constructors
+        if (DroidDoc.checkLevel(DroidDoc.SHOW_PACKAGE)) {
+            for (MethodInfo m: ctors) {
+                if (m.isPackagePrivate() && m.checkLevel()) {
+                    keywords.add(new KeywordEntry(m.name() + m.prettySignature(),
+                                htmlPage + "#" + m.anchor(),
+                                "constructor in " + qualifiedName));
+                }
+            }
+        }
+
+        // private constructors
+        if (DroidDoc.checkLevel(DroidDoc.SHOW_PRIVATE)) {
+            for (MethodInfo m: ctors) {
+                if (m.isPrivate() && m.checkLevel()) {
+                    keywords.add(new KeywordEntry(m.name() + m.prettySignature(),
+                                htmlPage + "#" + m.anchor(),
+                                "constructor in " + qualifiedName));
+                }
+            }
+        }
+
+        // public methods
+        for (MethodInfo m: methods) {
+            if (m.isPublic() && m.checkLevel()) {
+                keywords.add(new KeywordEntry(m.name() + m.prettySignature(),
+                            htmlPage + "#" + m.anchor(),
+                            "method in " + qualifiedName));
+            }
+        }
+
+        // protected methods
+        if (DroidDoc.checkLevel(DroidDoc.SHOW_PROTECTED)) {
+            for (MethodInfo m: methods) {
+                if (m.isProtected() && m.checkLevel()) {
+                    keywords.add(new KeywordEntry(m.name() + m.prettySignature(),
+                                htmlPage + "#" + m.anchor(),
+                                "method in " + qualifiedName));
+                }
+            }
+        }
+
+        // package private methods
+        if (DroidDoc.checkLevel(DroidDoc.SHOW_PACKAGE)) {
+            for (MethodInfo m: methods) {
+                if (m.isPackagePrivate() && m.checkLevel()) {
+                    keywords.add(new KeywordEntry(m.name() + m.prettySignature(),
+                                htmlPage + "#" + m.anchor(),
+                                "method in " + qualifiedName));
+                }
+            }
+        }
+
+        // private methods
+        if (DroidDoc.checkLevel(DroidDoc.SHOW_PRIVATE)) {
+            for (MethodInfo m: methods) {
+                if (m.isPrivate() && m.checkLevel()) {
+                    keywords.add(new KeywordEntry(m.name() + m.prettySignature(),
+                                htmlPage + "#" + m.anchor(),
+                                "method in " + qualifiedName));
+                }
+            }
+        }
+    }
+
+    public void makeLink(HDF data, String base)
+    {
+        data.setValue(base + ".label", this.name());
+        if (!this.isPrimitive() && this.isIncluded() && this.checkLevel()) {
+            data.setValue(base + ".link", this.htmlPage());
+        }
+    }
+
+    public static void makeLinkListHDF(HDF data, String base, ClassInfo[] classes) {
+        final int N = classes.length;
+        for (int i=0; i<N; i++) {
+            ClassInfo cl = classes[i];
+            if (cl.checkLevel()) {
+                cl.asTypeInfo().makeHDF(data, base + "." + i);
+            }
+        }
+    }
+
+    /**
+     * Used in lists of this class (packages, nested classes, known subclasses)
+     */
+    public void makeShortDescrHDF(HDF data, String base)
+    {
+        mTypeInfo.makeHDF(data, base + ".type");
+        data.setValue(base + ".kind", this.kind());
+        TagInfo.makeHDF(data, base + ".shortDescr", this.firstSentenceTags());
+        TagInfo.makeHDF(data, base + ".deprecated", deprecatedTags());
+    }
+
+    /**
+     * Turns into the main class page
+     */
+    public void makeHDF(HDF data)
+    {
+        int i, j, n;
+        String name = name();
+        String qualified = qualifiedName();
+        AttributeInfo[] selfAttributes = selfAttributes();
+        MethodInfo[] methods = selfMethods();
+        FieldInfo[] fields = selfFields();
+        FieldInfo[] enumConstants = enumConstants();
+        MethodInfo[] ctors = constructors();
+        ClassInfo[] inners = innerClasses();
+
+        // class name
+        mTypeInfo.makeHDF(data, "class.type");
+        mTypeInfo.makeQualifiedHDF(data, "class.qualifiedType");
+        data.setValue("class.name", name);
+        data.setValue("class.qualified", qualified);
+        String scope = "";
+        if (isProtected()) {
+            data.setValue("class.scope", "protected");
+        }
+        else if (isPublic()) {
+            data.setValue("class.scope", "public");
+        }
+        if (isStatic()) {
+            data.setValue("class.static", "static");
+        }
+        if (isFinal()) {
+            data.setValue("class.final", "final");
+        }
+        if (isAbstract() && !isInterface()) {
+            data.setValue("class.abstract", "abstract");
+        }
+
+        // class info
+        String kind = kind();
+        if (kind != null) {
+            data.setValue("class.kind", kind);
+        }
+
+        // the containing package -- note that this can be passed to type_link,
+        // but it also contains the list of all of the packages
+        containingPackage().makeClassLinkListHDF(data, "class.package");
+
+        // inheritance hierarchy
+        Vector<ClassInfo> superClasses = new Vector<ClassInfo>();
+        superClasses.add(this);
+        ClassInfo supr = superclass();
+        while (supr != null) {
+            superClasses.add(supr);
+            supr = supr.superclass();
+        }
+        n = superClasses.size();
+        for (i=0; i<n; i++) {
+            supr = superClasses.elementAt(n-i-1);
+
+            supr.asTypeInfo().makeQualifiedHDF(data, "class.inheritance." + i + ".class");
+            supr.asTypeInfo().makeHDF(data, "class.inheritance." + i + ".short_class");
+            j = 0;
+            for (TypeInfo t: supr.interfaceTypes()) {
+                t.makeHDF(data, "class.inheritance." + i + ".interfaces." + j);
+                j++;
+            }
+        }
+
+        // class description
+        TagInfo.makeHDF(data, "class.descr", inlineTags());
+        TagInfo.makeHDF(data, "class.seeAlso", comment().seeTags());
+        TagInfo.makeHDF(data, "class.deprecated", deprecatedTags());
+
+        // known subclasses
+        TreeMap<String, ClassInfo> direct = new TreeMap<String, ClassInfo>();
+        TreeMap<String, ClassInfo> indirect = new TreeMap<String, ClassInfo>();
+        ClassInfo[] all = Converter.rootClasses();
+        for (ClassInfo cl: all) {
+            if (cl.superclass() != null && cl.superclass().equals(this)) {
+                direct.put(cl.name(), cl);
+            }
+            else if (cl.isDerivedFrom(this)) {
+                indirect.put(cl.name(), cl);
+            }
+        }
+        // direct
+        i = 0;
+        for (ClassInfo cl: direct.values()) {
+            if (cl.checkLevel()) {
+                cl.makeShortDescrHDF(data, "class.subclasses.direct." + i);
+            }
+            i++;
+        }
+        // indirect
+        i = 0;
+        for (ClassInfo cl: indirect.values()) {
+            if (cl.checkLevel()) {
+                cl.makeShortDescrHDF(data, "class.subclasses.indirect." + i);
+            }
+            i++;
+        }
+
+        // nested classes
+        i=0;
+        for (ClassInfo inner: inners) {
+            if (inner.checkLevel()) {
+                inner.makeShortDescrHDF(data, "class.inners." + i);
+            }
+            i++;
+        }
+
+        // enum constants
+        i=0;
+        for (FieldInfo field: enumConstants) {
+            if (field.isConstant()) {
+                field.makeHDF(data, "class.enumConstants." + i);
+                i++;
+            }
+        }
+
+        // constants
+        i=0;
+        for (FieldInfo field: fields) {
+            if (field.isConstant()) {
+                field.makeHDF(data, "class.constants." + i);
+                i++;
+            }
+        }
+
+        // fields
+        i=0;
+        for (FieldInfo field: fields) {
+            if (!field.isConstant()) {
+                field.makeHDF(data, "class.fields." + i);
+                i++;
+            }
+        }
+
+        // public constructors
+        i=0;
+        for (MethodInfo ctor: ctors) {
+            if (ctor.isPublic()) {
+                ctor.makeHDF(data, "class.ctors.public." + i);
+                i++;
+            }
+        }
+
+        // protected constructors
+        if (DroidDoc.checkLevel(DroidDoc.SHOW_PROTECTED)) {
+            i=0;
+            for (MethodInfo ctor: ctors) {
+                if (ctor.isProtected()) {
+                    ctor.makeHDF(data, "class.ctors.protected." + i);
+                    i++;
+                }
+            }
+        }
+
+        // package private constructors
+        if (DroidDoc.checkLevel(DroidDoc.SHOW_PACKAGE)) {
+            i=0;
+            for (MethodInfo ctor: ctors) {
+                if (ctor.isPackagePrivate()) {
+                    ctor.makeHDF(data, "class.ctors.package." + i);
+                    i++;
+                }
+            }
+        }
+
+        // private constructors
+        if (DroidDoc.checkLevel(DroidDoc.SHOW_PRIVATE)) {
+            i=0;
+            for (MethodInfo ctor: ctors) {
+                if (ctor.isPrivate()) {
+                    ctor.makeHDF(data, "class.ctors.private." + i);
+                    i++;
+                }
+            }
+        }
+
+        // public methods
+        i=0;
+        for (MethodInfo method: methods) {
+            if (method.isPublic()) {
+                method.makeHDF(data, "class.methods.public." + i);
+                i++;
+            }
+        }
+
+        // protected methods
+        if (DroidDoc.checkLevel(DroidDoc.SHOW_PROTECTED)) {
+            i=0;
+            for (MethodInfo method: methods) {
+                if (method.isProtected()) {
+                    method.makeHDF(data, "class.methods.protected." + i);
+                    i++;
+                }
+            }
+        }
+
+        // package private methods
+        if (DroidDoc.checkLevel(DroidDoc.SHOW_PACKAGE)) {
+            i=0;
+            for (MethodInfo method: methods) {
+                if (method.isPackagePrivate()) {
+                    method.makeHDF(data, "class.methods.package." + i);
+                    i++;
+                }
+            }
+        }
+
+        // private methods
+        if (DroidDoc.checkLevel(DroidDoc.SHOW_PRIVATE)) {
+            i=0;
+            for (MethodInfo method: methods) {
+                if (method.isPrivate()) {
+                    method.makeHDF(data, "class.methods.private." + i);
+                    i++;
+                }
+            }
+        }
+
+        // xml attributes
+        i=0;
+        for (AttributeInfo attr: selfAttributes) {
+            if (attr.checkLevel()) {
+                attr.makeHDF(data, "class.attrs." + i);
+                i++;
+            }
+        }
+
+        // inherited methods
+        Set<ClassInfo> interfaces = new TreeSet<ClassInfo>();
+        addInterfaces(interfaces(), interfaces);
+        ClassInfo cl = superclass();
+        i=0;
+        while (cl != null) {
+            addInterfaces(cl.interfaces(), interfaces);
+            makeInheritedHDF(data, i, cl);
+            cl = cl.superclass();
+            i++;
+        }
+        for (ClassInfo iface: interfaces) {
+            makeInheritedHDF(data, i, iface);
+            i++;
+        }
+    }
+
+    private static void addInterfaces(ClassInfo[] ifaces, Set<ClassInfo> out)
+    {
+        for (ClassInfo cl: ifaces) {
+            out.add(cl);
+            addInterfaces(cl.interfaces(), out);
+        }
+    }
+
+    private static void makeInheritedHDF(HDF data, int index, ClassInfo cl)
+    {
+        int i;
+
+        String base = "class.inherited." + index;
+        data.setValue(base + ".qualified", cl.qualifiedName());
+        if (cl.checkLevel()) {
+            data.setValue(base + ".link", cl.htmlPage());
+        }
+        String kind = cl.kind();
+        if (kind != null) {
+            data.setValue(base + ".kind", kind);
+        }
+        
+        // xml attributes
+        i=0;
+        for (AttributeInfo attr: cl.selfAttributes()) {
+            attr.makeHDF(data, base + ".attrs." + i);
+            i++;
+        }
+
+        // methods
+        i=0;
+        for (MethodInfo method: cl.selfMethods()) {
+            method.makeHDF(data, base + ".methods." + i);
+            i++;
+        }
+
+        // fields
+        i=0;
+        for (FieldInfo field: cl.selfFields()) {
+            if (!field.isConstant()) {
+                field.makeHDF(data, base + ".fields." + i);
+                i++;
+            }
+        }
+
+        // constants
+        i=0;
+        for (FieldInfo field: cl.selfFields()) {
+            if (field.isConstant()) {
+                field.makeHDF(data, base + ".constants." + i);
+                i++;
+            }
+        }
+    }
+
+    public boolean isHidden()
+    {
+        int val = mHidden;
+        if (val >= 0) {
+            return val != 0;
+        } else {
+            boolean v = isHiddenImpl();
+            mHidden = v ? 1 : 0;
+            return v;
+        }
+    }
+
+    public boolean isHiddenImpl()
+    {
+        ClassInfo cl = this;
+        while (cl != null) {
+            PackageInfo pkg = cl.containingPackage();
+            if (pkg.isHidden()) {
+                return true;
+            }
+            if (cl.comment().isHidden()) {
+                return true;
+            }
+            cl = cl.containingClass();
+        }
+        return false;
+    }
+
+    private MethodInfo matchMethod(MethodInfo[] methods, String name,
+                                    String[] params, String[] dimensions)
+    {
+        int len = methods.length;
+        for (int i=0; i<len; i++) {
+            MethodInfo method = methods[i];
+            if (method.name().equals(name)) {
+                if (params == null) {
+                    return method;
+                } else {
+                    if (method.matchesParams(params, dimensions)) {
+                        return method;
+                    }
+                }
+            }
+        }
+        return null;
+    }
+
+    public MethodInfo findMethod(String name,
+                                    String[] params, String[] dimensions)
+    {
+        // first look on our class, and our superclasses
+
+        // for methods
+        MethodInfo rv;
+        rv = matchMethod(methods(), name, params, dimensions);
+
+        if (rv != null) {
+            return rv;
+        }
+
+        // for constructors
+        rv = matchMethod(constructors(), name, params, dimensions);
+        if (rv != null) {
+            return rv;
+        }
+
+        // then recursively look at our containing class
+        ClassInfo containing = containingClass();
+        if (containing != null) {
+            return containing.findMethod(name, params, dimensions);
+        }
+
+        return null;
+    }
+
+    private ClassInfo searchInnerClasses(String[] nameParts, int index)
+    {
+        String part = nameParts[index];
+
+        ClassInfo[] inners = mInnerClasses;
+        for (ClassInfo in: inners) {
+            String[] innerParts = in.nameParts();
+            if (part.equals(innerParts[innerParts.length-1])) {
+                if (index == nameParts.length-1) {
+                    return in;
+                } else {
+                    return in.searchInnerClasses(nameParts, index+1);
+                }
+            }
+        }
+        return null;
+    }
+
+    public ClassInfo extendedFindClass(String className)
+    {
+        // ClassDoc.findClass has this bug that we're working around here:
+        // If you have a class PackageManager with an inner class PackageInfo
+        // and you call it with "PackageInfo" it doesn't find it.
+        return searchInnerClasses(className.split("\\."), 0);
+    }
+
+    public ClassInfo findClass(String className)
+    {
+        return Converter.obtainClass(mClass.findClass(className));
+    }
+
+    public ClassInfo findInnerClass(String className)
+    {
+        // ClassDoc.findClass won't find inner classes.  To deal with that,
+        // we try what they gave us first, but if that didn't work, then
+        // we see if there are any periods in className, and start searching
+        // from there.
+        String[] nodes = className.split("\\.");
+        ClassDoc cl = mClass;
+        for (String n: nodes) {
+            cl = cl.findClass(n);
+            if (cl == null) {
+                return null;
+            }
+        }
+        return Converter.obtainClass(cl);
+    }
+
+    public FieldInfo findField(String name)
+    {
+        // first look on our class, and our superclasses
+        for (FieldInfo f: fields()) {
+            if (f.name().equals(name)) {
+                return f;
+            }
+        }
+        
+        // then look at our enum constants (these are really fields, maybe
+        // they should be mixed into fields().  not sure)
+        for (FieldInfo f: enumConstants()) {
+            if (f.name().equals(name)) {
+                return f;
+            }
+        }
+
+        // then recursively look at our containing class
+        ClassInfo containing = containingClass();
+        if (containing != null) {
+            return containing.findField(name);
+        }
+
+        return null;
+    }
+
+    public static ClassInfo[] sortByName(ClassInfo[] classes)
+    {
+        int i;
+        Sorter[] sorted = new Sorter[classes.length];
+        for (i=0; i<sorted.length; i++) {
+            ClassInfo cl = classes[i];
+            sorted[i] = new Sorter(cl.name(), cl);
+        }
+
+        Arrays.sort(sorted);
+
+        ClassInfo[] rv = new ClassInfo[classes.length];
+        for (i=0; i<rv.length; i++) {
+            rv[i] = (ClassInfo)sorted[i].data;
+        }
+
+        return rv;
+    }
+
+    public boolean equals(ClassInfo that)
+    {
+        if (that != null) {
+            return this.qualifiedName().equals(that.qualifiedName());
+        } else {
+            return false;
+        }
+    }
+    
+    public void setNonWrittenConstructors(MethodInfo[] nonWritten) {
+        mNonWrittenConstructors = nonWritten;
+    }
+    
+    public MethodInfo[] getNonWrittenConstructors() {
+        return mNonWrittenConstructors;
+    }
+
+    public String kind()
+    {
+        if (isOrdinaryClass()) {
+            return "class";
+        }
+        else if (isInterface()) {
+            return "interface";
+        }
+        else if (isEnum()) {
+            return "enum";
+        }
+        else if (isError()) {
+            return "class";
+        }
+        else if (isException()) {
+            return "class";
+        }
+        else if (isAnnotation()) {
+            return "@interface";
+        }
+        return null;
+    }
+    
+    public void setHiddenMethods(MethodInfo[] mInfo){
+        mHiddenMethods = mInfo;
+    }
+    public MethodInfo[] getHiddenMethods(){
+        return mHiddenMethods;
+    }
+    public String toString(){
+        return this.qualifiedName();
+    }
+    
+    public void setReasonIncluded(String reason) {
+        mReasonIncluded = reason;
+    }
+    
+    public String getReasonIncluded() {
+        return mReasonIncluded; 
+    }
+
+    private ClassDoc mClass;
+
+    // ctor
+    private boolean mIsPublic;
+    private boolean mIsProtected;
+    private boolean mIsPackagePrivate;
+    private boolean mIsPrivate;
+    private boolean mIsStatic;
+    private boolean mIsInterface;
+    private boolean mIsAbstract;
+    private boolean mIsOrdinaryClass;
+    private boolean mIsException;
+    private boolean mIsError;
+    private boolean mIsEnum;
+    private boolean mIsAnnotation;
+    private boolean mIsFinal;
+    private boolean mIsIncluded;
+    private String mName;
+    private String mQualifiedName;
+    private String mQualifiedTypeName;
+    private boolean mIsPrimitive;
+    private TypeInfo mTypeInfo;
+    private String[] mNameParts;
+
+    // init
+    private ClassInfo[] mRealInterfaces;
+    private ClassInfo[] mInterfaces;
+    private TypeInfo[] mRealInterfaceTypes;
+    private ClassInfo[] mInnerClasses;
+    private MethodInfo[] mAllConstructors;
+    private MethodInfo[] mAllSelfMethods;
+    private MethodInfo[] mAnnotationElements; // if this class is an annotation
+    private FieldInfo[] mAllSelfFields;
+    private FieldInfo[] mEnumConstants;
+    private PackageInfo mContainingPackage;
+    private ClassInfo mContainingClass;
+    private ClassInfo mRealSuperclass;
+    private TypeInfo mRealSuperclassType;
+    private ClassInfo mSuperclass;
+    private AnnotationInstanceInfo[] mAnnotations;
+    private boolean mSuperclassInit;
+    private boolean mDeprecatedKnown;
+
+    // lazy
+    private MethodInfo[] mConstructors;
+    private ClassInfo[] mRealInnerClasses;
+    private MethodInfo[] mSelfMethods;
+    private FieldInfo[] mSelfFields;
+    private AttributeInfo[] mSelfAttributes;
+    private MethodInfo[] mMethods;
+    private FieldInfo[] mFields;
+    private TypeInfo[] mTypeParameters;
+    private MethodInfo[] mHiddenMethods;
+    private int mHidden = -1;
+    private int mCheckLevel = -1;
+    private String mReasonIncluded;
+    private MethodInfo[] mNonWrittenConstructors;
+    private boolean mIsDeprecated;
+}
diff --git a/tools/droiddoc/src/ClearPage.java b/tools/droiddoc/src/ClearPage.java
new file mode 100644
index 0000000..2a8fced
--- /dev/null
+++ b/tools/droiddoc/src/ClearPage.java
@@ -0,0 +1,226 @@
+/*
+ * 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 com.sun.javadoc.*;
+import org.clearsilver.HDF;
+import org.clearsilver.CS;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.OutputStreamWriter;
+import java.io.IOException;
+import java.util.ArrayList;
+
+public class ClearPage
+{
+    /*
+    public ClearPage()
+    {
+        String templ = "templates/index.cs";
+        String filename = "docs/index.html";
+
+        data.setValue("A.B.C", "1");
+        data.setValue("A.B.D", "2");
+    }
+    */
+
+    public static ArrayList<String> hdfFiles = new ArrayList<String>();
+
+    private static ArrayList<String> mTemplateDirs = new ArrayList<String>();
+    private static boolean mTemplateDirSet = false;
+
+    public static String outputDir = "docs";
+    public static String htmlDir = null;
+    public static String toroot = null;
+
+    public static void addTemplateDir(String dir)
+    {
+        mTemplateDirSet = true;
+        mTemplateDirs.add(dir);
+
+        File hdfFile = new File(dir, "data.hdf");
+        if (hdfFile.canRead()) {
+            hdfFiles.add(hdfFile.getPath());
+        }
+    }
+
+    private static int countSlashes(String s)
+    {
+        final int N = s.length();
+        int slashcount = 0;
+        for (int i=0; i<N; i++) {
+            if (s.charAt(i) == '/') {
+                slashcount++;
+            }
+        }
+        return slashcount;
+    }
+
+    public static void write(HDF data, String templ, String filename)
+    {
+        write(data, templ, filename, false);
+    }
+
+    public static void write(HDF data, String templ, String filename, boolean fullPath)
+    {
+        if (htmlDir != null) {
+            data.setValue("hasindex", "true");
+        }
+
+        String toroot;
+        if (ClearPage.toroot != null) {
+            toroot = ClearPage.toroot;
+        } else {
+            int slashcount = countSlashes(filename);
+            if (slashcount > 0) {
+                toroot = "";
+                for (int i=0; i<slashcount; i++) {
+                    toroot += "../";
+                }
+            } else {
+                toroot = "./";
+            }
+        }
+        data.setValue("toroot", toroot);
+
+        data.setValue("filename", filename);
+
+        if (!fullPath) {
+            filename = outputDir + "/" + filename;
+        }
+
+        int i=0;
+        if (htmlDir != null) {
+            data.setValue("hdf.loadpaths." + i, htmlDir);
+            i++;
+        }
+        if (mTemplateDirSet) {
+            for (String dir: mTemplateDirs) {
+                data.setValue("hdf.loadpaths." + i, dir);
+                i++;
+            }
+        } else {
+            data.setValue("hdf.loadpaths." + i, "templates");
+        }
+        
+        CS cs = new CS(data);
+        cs.parseFile(templ);
+
+        File file = new File(outputFilename(filename));
+        
+        ensureDirectory(file);
+
+        OutputStreamWriter stream = null;
+        try {
+            stream = new OutputStreamWriter(
+                            new FileOutputStream(file));
+            String rendered = cs.render();
+            stream.write(rendered, 0, rendered.length());
+        }
+        catch (IOException e) {
+            System.out.println("error: " + e.getMessage() + "; when writing file: " + filename);
+        }
+        finally {
+            if (stream != null) {
+                try {
+                    stream.close();
+                }
+                catch (IOException e) {
+                }
+            }
+        }
+    }
+
+    // recursively create the directories to the output
+    public static void ensureDirectory(File f)
+    {
+        File parent = f.getParentFile();
+        if (parent != null) {
+            parent.mkdirs();
+        }
+    }
+
+    public static void copyFile(File from, String toPath)
+    {
+        File to = new File(outputDir + "/" + toPath);
+        FileInputStream in;
+        FileOutputStream out;
+        try {
+            if (!from.exists()) {
+                throw new IOException();
+            }
+            in = new FileInputStream(from);
+        }
+        catch (IOException e) {
+            System.err.println(from.getAbsolutePath() + ": Error opening file");
+            return ;
+        }
+        ensureDirectory(to);
+        try {
+            out = new FileOutputStream(to);
+        }
+        catch (IOException e) {
+            System.err.println(from.getAbsolutePath() + ": Error opening file");
+            return ;
+        }
+
+        long sizel = from.length();
+        final int maxsize = 64*1024;
+        int size = sizel > maxsize ? maxsize : (int)sizel;
+        byte[] buf = new byte[size];
+        while (true) {
+            try {
+                size = in.read(buf);
+            }
+            catch (IOException e) {
+                System.err.println(from.getAbsolutePath()
+                        + ": error reading file");
+                break;
+            }
+            if (size > 0) {
+                try {
+                    out.write(buf, 0, size);
+                }
+                catch (IOException e) {
+                    System.err.println(from.getAbsolutePath()
+                        + ": error writing file");
+                }
+            } else {
+                break;
+            }
+        }
+        try {
+            in.close();
+        }
+        catch (IOException e) {
+        }
+        try {
+            out.close();
+        }
+        catch (IOException e) {
+        }
+    }
+    
+    /** Takes a string that ends w/ .html and changes the .html to htmlExtension */
+    public static String outputFilename(String htmlFile) {
+        if (!DroidDoc.htmlExtension.equals(".html") && htmlFile.endsWith(".html")) {
+            return htmlFile.substring(0, htmlFile.length()-5) + DroidDoc.htmlExtension;
+        } else {
+            return htmlFile;
+        }
+    }
+
+}
diff --git a/tools/droiddoc/src/Comment.java b/tools/droiddoc/src/Comment.java
new file mode 100644
index 0000000..3a24357
--- /dev/null
+++ b/tools/droiddoc/src/Comment.java
@@ -0,0 +1,394 @@
+/*
+ * 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 java.util.regex.Pattern;
+import java.util.regex.Matcher;
+import java.util.ArrayList;
+
+public class Comment
+{
+    static final Pattern LEADING_WHITESPACE = Pattern.compile(
+                                "^[ \t\n\r]*(.*)$",
+                                Pattern.DOTALL);
+
+    static final Pattern TAG_BEGIN = Pattern.compile(
+                                "[\r\n][\r\n \t]*@",
+                                Pattern.DOTALL);
+
+    static final Pattern TAG = Pattern.compile(
+                                "(@[^ \t\r\n]+)[ \t\r\n]+(.*)",
+                                Pattern.DOTALL);
+
+    static final Pattern INLINE_TAG = Pattern.compile(
+                                "(.*?)\\{(@[^ \t\r\n\\}]+)[ \t\r\n]*(.*?)\\}",
+                                Pattern.DOTALL);
+
+    static final Pattern FIRST_SENTENCE = Pattern.compile(
+                                "((.*?)\\.)[ \t\r\n\\<](.*)",
+                                Pattern.DOTALL);
+
+    private static final String[] KNOWN_TAGS = new String[] {
+            "@author",
+            "@since",
+            "@version",
+            "@deprecated",
+            "@undeprecate",
+            "@docRoot",
+            "@inheritDoc",
+            "@more",
+            "@code",
+            "@samplecode",
+            "@sample",
+            "@include",
+            "@serial",
+            "@com.intel.drl.spec_ref",
+            "@ar.org.fitc.spec_ref",
+        };
+
+    public Comment(String text, ContainerInfo base, SourcePositionInfo sp)
+    {
+        mText = text;
+        mBase = base;
+        // sp now points to the end of the text, not the beginning!
+        mPosition = SourcePositionInfo.findBeginning(sp, text);
+    }
+
+    private void parseRegex(String text)
+    {
+        Matcher m;
+
+        m = LEADING_WHITESPACE.matcher(text);
+        m.matches();
+        text = m.group(1);
+
+        m = TAG_BEGIN.matcher(text);
+
+        int start = 0;
+        int end = 0;
+        while (m.find()) {
+            end = m.start();
+
+            tag(text, start, end);
+
+            start = m.end()-1; // -1 is the @
+        }
+        end = text.length();
+        tag(text, start, end);
+    }
+
+    private void tag(String text, int start, int end)
+    {
+        SourcePositionInfo pos = SourcePositionInfo.add(mPosition, mText, start);
+
+        if (start >= 0 && end > 0 && (end-start) > 0) {
+            text = text.substring(start, end);
+
+            Matcher m = TAG.matcher(text);
+            if (m.matches()) {
+                // out of line tag
+                tag(m.group(1), m.group(2), false, pos);
+            } else {
+                // look for inline tags
+                m = INLINE_TAG.matcher(text);
+                start = 0;
+                while (m.find()) {
+                    String str = m.group(1);
+                    String tagname = m.group(2);
+                    String tagvalue = m.group(3);
+                    tag(null, m.group(1), true, pos);
+                    tag(tagname, tagvalue, true, pos);
+                    start = m.end();
+                }
+                int len = text.length();
+                if (start != len) {
+                    tag(null, text.substring(start), true, pos);
+                }
+            }
+        }
+    }
+
+    private void tag(String name, String text, boolean isInline, SourcePositionInfo pos)
+    {
+        /*
+        String s = isInline ? "inline" : "outofline";
+        System.out.println("---> " + s
+                + " name=[" + name + "] text=[" + text + "]");
+        */
+        if (name == null) {
+            mInlineTagsList.add(new TextTagInfo("Text", "Text", text, pos));
+        }
+        else if (name.equals("@param")) {
+            mParamTagsList.add(new ParamTagInfo("@param", "@param", text, mBase, pos));
+        }
+        else if (name.equals("@see")) {
+            mSeeTagsList.add(new SeeTagInfo("@see", "@see", text, mBase, pos));
+        }
+        else if (name.equals("@link") || name.equals("@linkplain")) {
+            mInlineTagsList.add(new SeeTagInfo(name, "@see", text, mBase, pos));
+        }
+        else if (name.equals("@throws") || name.equals("@exception")) {
+            mThrowsTagsList.add(new ThrowsTagInfo("@throws", "@throws", text, mBase, pos));
+        }
+        else if (name.equals("@return")) {
+            mReturnTagsList.add(new ParsedTagInfo("@return", "@return", text, mBase, pos));
+        }
+        else if (name.equals("@deprecated")) {
+            if (text.length() == 0) {
+                Errors.error(Errors.MISSING_COMMENT, pos,
+                        "@deprecated tag with no explanatory comment");
+                text = "No replacement.";
+            }
+            mDeprecatedTagsList.add(new ParsedTagInfo("@deprecated", "@deprecated", text, mBase, pos));
+        }
+        else if (name.equals("@literal")) {
+            mInlineTagsList.add(new LiteralTagInfo(name, name, text, pos));
+        }
+        else if (name.equals("@hide") || name.equals("@doconly")) {
+            // nothing
+        }
+        else if (name.equals("@attr")) {
+            AttrTagInfo tag = new AttrTagInfo("@attr", "@attr", text, mBase, pos);
+            mAttrTagsList.add(tag);
+            Comment c = tag.description();
+            if (c != null) {
+                for (TagInfo t: c.tags()) {
+                    mInlineTagsList.add(t);
+                }
+            }
+        }
+        else if (name.equals("@undeprecate")) {
+            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 {
+            boolean known = false;
+            for (String s: KNOWN_TAGS) {
+                if (s.equals(name)) {
+                    known = true;
+                    break;
+                }
+            }
+            if (!known) {
+                Errors.error(Errors.UNKNOWN_TAG, pos == null ? null : new SourcePositionInfo(pos),
+                        "Unknown tag: " + name);
+            }
+            TagInfo t = new TextTagInfo(name, name, text, pos);
+            if (isInline) {
+                mInlineTagsList.add(t);
+            } else {
+                mTagsList.add(t);
+            }
+        }
+    }
+
+    private void parseBriefTags()
+    {
+        int N = mInlineTagsList.size();
+
+        // look for "@more" tag, which means that we might go past the first sentence.
+        int more = -1;
+        for (int i=0; i<N; i++) {
+            if (mInlineTagsList.get(i).name().equals("@more")) {
+                more = i;
+            } 
+        }
+          if (more >= 0) {
+            for (int i=0; i<more; i++) {
+                mBriefTagsList.add(mInlineTagsList.get(i));
+            }
+        } else {
+            for (int i=0; i<N; i++) {
+                TagInfo t = mInlineTagsList.get(i);
+                if (t.name().equals("Text")) {
+                    Matcher m = FIRST_SENTENCE.matcher(t.text());
+                    if (m.matches()) {
+                        String text = m.group(1);
+                        TagInfo firstSentenceTag = new TagInfo(t.name(), t.kind(), text, t.position());
+                        mBriefTagsList.add(firstSentenceTag);
+                        break;
+                    }
+                }
+                mBriefTagsList.add(t);
+                
+            }
+        }
+    }
+
+    public TagInfo[] tags()
+    {
+        init();
+        return mInlineTags;
+    }
+
+    public TagInfo[] tags(String name)
+    {
+        init();
+        ArrayList<TagInfo> results = new ArrayList<TagInfo>();
+        int N = mInlineTagsList.size();
+        for (int i=0; i<N; i++) {
+            TagInfo t = mInlineTagsList.get(i);
+            if (t.name().equals(name)) {
+                results.add(t);
+            }
+        }
+        return results.toArray(new TagInfo[results.size()]);
+    }
+
+    public ParamTagInfo[] paramTags()
+    {
+        init();
+        return mParamTags;
+    }
+
+    public SeeTagInfo[] seeTags()
+    {
+        init();
+        return mSeeTags;
+    }
+
+    public ThrowsTagInfo[] throwsTags()
+    {
+        init();
+        return mThrowsTags;
+    }
+
+    public TagInfo[] returnTags()
+    {
+        init();
+        return mReturnTags;
+    }
+
+    public TagInfo[] deprecatedTags()
+    {
+        init();
+        return mDeprecatedTags;
+    }
+
+    public TagInfo[] undeprecateTags()
+    {
+        init();
+        return mUndeprecateTags;
+    }
+
+    public AttrTagInfo[] attrTags()
+    {
+        init();
+        return mAttrTags;
+    }
+
+    public TagInfo[] briefTags()
+    {
+        init();
+        return mBriefTags;
+    }
+
+    public boolean isHidden()
+    {
+        if (mHidden >= 0) {
+            return mHidden != 0;
+        } else {
+            if (DroidDoc.checkLevel(DroidDoc.SHOW_HIDDEN)) {
+                mHidden = 0;
+                return false;
+            }
+            boolean b = mText.indexOf("@hide") >= 0;
+            mHidden = b ? 1 : 0;
+            return b;
+        }
+    }
+    
+    public boolean isDocOnly() {
+        if (mDocOnly >= 0) {
+            return mDocOnly != 0;
+        } else {
+            boolean b = (mText != null) && (mText.indexOf("@doconly") >= 0);
+            mDocOnly = b ? 1 : 0;
+            return b;
+        }
+    }
+
+    private void init()
+    {
+        if (!mInitialized) {
+            initImpl();
+        }
+    }
+
+    private void initImpl()
+    {
+        isHidden();
+        isDocOnly();
+        parseRegex(mText);
+        parseBriefTags();
+        mText = null;
+        mInitialized = true;
+
+        mInlineTags = mInlineTagsList.toArray(new TagInfo[mInlineTagsList.size()]);
+        mParamTags = mParamTagsList.toArray(new ParamTagInfo[mParamTagsList.size()]);
+        mSeeTags = mSeeTagsList.toArray(new SeeTagInfo[mSeeTagsList.size()]);
+        mThrowsTags = mThrowsTagsList.toArray(new ThrowsTagInfo[mThrowsTagsList.size()]);
+        mReturnTags = ParsedTagInfo.joinTags(mReturnTagsList.toArray(
+                                             new ParsedTagInfo[mReturnTagsList.size()]));
+        mDeprecatedTags = ParsedTagInfo.joinTags(mDeprecatedTagsList.toArray(
+                                        new ParsedTagInfo[mDeprecatedTagsList.size()]));
+        mUndeprecateTags = mUndeprecateTagsList.toArray(new TagInfo[mUndeprecateTagsList.size()]);
+        mAttrTags = mAttrTagsList.toArray(new AttrTagInfo[mAttrTagsList.size()]);
+        mBriefTags = mBriefTagsList.toArray(new TagInfo[mBriefTagsList.size()]);
+
+        mParamTagsList = null;
+        mSeeTagsList = null;
+        mThrowsTagsList = null;
+        mReturnTagsList = null;
+        mDeprecatedTagsList = null;
+        mUndeprecateTagsList = null;
+        mAttrTagsList = null;
+        mBriefTagsList = null;
+    }
+
+    boolean mInitialized;
+    int mHidden = -1;
+    int mDocOnly = -1;
+    String mText;
+    ContainerInfo mBase;
+    SourcePositionInfo mPosition;
+    int mLine = 1;
+
+    TagInfo[] mInlineTags;
+    TagInfo[] mTags;
+    ParamTagInfo[] mParamTags;
+    SeeTagInfo[] mSeeTags;
+    ThrowsTagInfo[] mThrowsTags;
+    TagInfo[] mBriefTags;
+    TagInfo[] mReturnTags;
+    TagInfo[] mDeprecatedTags;
+    TagInfo[] mUndeprecateTags;
+    AttrTagInfo[] mAttrTags;
+
+    ArrayList<TagInfo> mInlineTagsList = new ArrayList<TagInfo>();
+    ArrayList<TagInfo> mTagsList = new ArrayList<TagInfo>();
+    ArrayList<ParamTagInfo> mParamTagsList = new ArrayList<ParamTagInfo>();
+    ArrayList<SeeTagInfo> mSeeTagsList = new ArrayList<SeeTagInfo>();
+    ArrayList<ThrowsTagInfo> mThrowsTagsList = new ArrayList<ThrowsTagInfo>();
+    ArrayList<TagInfo> mBriefTagsList = new ArrayList<TagInfo>();
+    ArrayList<ParsedTagInfo> mReturnTagsList = new ArrayList<ParsedTagInfo>();
+    ArrayList<ParsedTagInfo> mDeprecatedTagsList = new ArrayList<ParsedTagInfo>();
+    ArrayList<TagInfo> mUndeprecateTagsList = new ArrayList<TagInfo>();
+    ArrayList<AttrTagInfo> mAttrTagsList = new ArrayList<AttrTagInfo>();
+
+    
+}
diff --git a/tools/droiddoc/src/ContainerInfo.java b/tools/droiddoc/src/ContainerInfo.java
new file mode 100644
index 0000000..b8a3e10
--- /dev/null
+++ b/tools/droiddoc/src/ContainerInfo.java
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+
+public interface ContainerInfo
+{
+    public String qualifiedName();
+    public boolean checkLevel();
+}
diff --git a/tools/droiddoc/src/Converter.java b/tools/droiddoc/src/Converter.java
new file mode 100644
index 0000000..4014f7f
--- /dev/null
+++ b/tools/droiddoc/src/Converter.java
@@ -0,0 +1,744 @@
+/*
+ * 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 com.sun.javadoc.*;
+import com.sun.tools.doclets.*;
+import org.clearsilver.HDF;
+import org.clearsilver.CS;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+
+public class Converter
+{
+    private static RootDoc root;
+
+    public static void makeInfo(RootDoc r)
+    {
+        root = r;
+
+        int N, i;
+
+        // create the objects
+        ClassDoc[] classDocs = r.classes();
+        N = classDocs.length;
+        for (i=0; i<N; i++) {
+            Converter.obtainClass(classDocs[i]);
+        }
+        ArrayList<ClassInfo> classesNeedingInit2 = new ArrayList<ClassInfo>();
+        // fill in the fields that reference other classes
+        while (mClassesNeedingInit.size() > 0) {
+            i = mClassesNeedingInit.size()-1;
+            ClassNeedingInit clni = mClassesNeedingInit.get(i);
+            mClassesNeedingInit.remove(i);
+
+            initClass(clni.c, clni.cl);
+            classesNeedingInit2.add(clni.cl);
+        }
+        mClassesNeedingInit = null;
+        for (ClassInfo cl: classesNeedingInit2) {
+            cl.init2();
+        }
+
+        finishAnnotationValueInit();
+
+        // fill in the "root" stuff
+        mRootClasses = Converter.convertClasses(r.classes());
+    }
+
+    private static ClassInfo[] mRootClasses;
+    public static ClassInfo[] rootClasses()
+    {
+        return mRootClasses;
+    }
+
+    public static ClassInfo[] allClasses() {
+        return (ClassInfo[])mClasses.all();
+    }
+
+    private static void initClass(ClassDoc c, ClassInfo cl)
+    {
+        MethodDoc[] annotationElements;
+        if (c instanceof AnnotationTypeDoc) {
+            annotationElements = ((AnnotationTypeDoc)c).elements();
+        } else {
+            annotationElements = new MethodDoc[0];
+        }
+        cl.init(Converter.obtainType(c),
+                Converter.convertClasses(c.interfaces()),
+                Converter.convertTypes(c.interfaceTypes()),
+                Converter.convertClasses(c.innerClasses()),
+                Converter.convertMethods(c.constructors(false)),
+                Converter.convertMethods(c.methods(false)),
+                Converter.convertMethods(annotationElements),
+                Converter.convertFields(c.fields(false)),
+                Converter.convertFields(c.enumConstants()),
+                Converter.obtainPackage(c.containingPackage()),
+                Converter.obtainClass(c.containingClass()),
+                Converter.obtainClass(c.superclass()),
+                Converter.obtainType(c.superclassType()),
+                Converter.convertAnnotationInstances(c.annotations())
+                );
+          cl.setHiddenMethods(Converter.getHiddenMethods(c.methods(false)));
+          cl.setNonWrittenConstructors(Converter.convertNonWrittenConstructors(c.constructors(false)));
+          cl.init3(Converter.convertTypes(c.typeParameters()), Converter.convertClasses(c.innerClasses(false)));
+    }
+
+    public static ClassInfo obtainClass(String className)
+    {
+        return Converter.obtainClass(root.classNamed(className));
+    }
+
+    public static PackageInfo obtainPackage(String packageName)
+    {
+        return Converter.obtainPackage(root.packageNamed(packageName));
+    }
+
+    private static TagInfo convertTag(Tag tag)
+    {
+        return new TextTagInfo(tag.name(), tag.kind(), tag.text(),
+                                Converter.convertSourcePosition(tag.position()));
+    }
+
+    private static ThrowsTagInfo convertThrowsTag(ThrowsTag tag,
+                                                ContainerInfo base)
+    {
+        return new ThrowsTagInfo(tag.name(), tag.text(), tag.kind(),
+                              Converter.obtainClass(tag.exception()),
+                              tag.exceptionComment(), base,
+                              Converter.convertSourcePosition(tag.position()));
+    }
+
+    private static ParamTagInfo convertParamTag(ParamTag tag,
+                                                ContainerInfo base)
+    {
+        return new ParamTagInfo(tag.name(), tag.kind(), tag.text(),
+                              tag.isTypeParameter(), tag.parameterComment(),
+                              tag.parameterName(),
+                              base,
+                              Converter.convertSourcePosition(tag.position()));
+    }
+
+    private static SeeTagInfo convertSeeTag(SeeTag tag, ContainerInfo base)
+    {
+        return new SeeTagInfo(tag.name(), tag.kind(), tag.text(), base,
+                              Converter.convertSourcePosition(tag.position()));
+    }
+
+    private static SourcePositionInfo convertSourcePosition(SourcePosition sp)
+    {
+        if (sp == null) {
+            return null;
+        }
+        return new SourcePositionInfo(sp.file().toString(), sp.line(),
+                                        sp.column());
+    }
+
+    public static TagInfo[] convertTags(Tag[] tags, ContainerInfo base)
+    {
+        int len = tags.length;
+        TagInfo[] out = new TagInfo[len];
+        for (int i=0; i<len; i++) {
+            Tag t = tags[i];
+            /*
+            System.out.println("Tag name='" + t.name() + "' kind='"
+                    + t.kind() + "'");
+            */
+            if (t instanceof SeeTag) {
+                out[i] = Converter.convertSeeTag((SeeTag)t, base);
+            }
+            else if (t instanceof ThrowsTag) {
+                out[i] = Converter.convertThrowsTag((ThrowsTag)t, base);
+            }
+            else if (t instanceof ParamTag) {
+                out[i] = Converter.convertParamTag((ParamTag)t, base);
+            }
+            else {
+                out[i] = Converter.convertTag(t);
+            }
+        }
+        return out;
+    }
+
+    public static ClassInfo[] convertClasses(ClassDoc[] classes)
+    {
+        if (classes == null) return null;
+        int N = classes.length;
+        ClassInfo[] result = new ClassInfo[N];
+        for (int i=0; i<N; i++) {
+            result[i] = Converter.obtainClass(classes[i]);
+        }
+        return result;
+    }
+
+    private static ParameterInfo convertParameter(Parameter p, SourcePosition pos)
+    {
+        if (p == null) return null;
+        ParameterInfo pi = new ParameterInfo(p.name(), p.typeName(),
+                Converter.obtainType(p.type()),
+                Converter.convertSourcePosition(pos));
+        return pi;
+    }
+
+    private static ParameterInfo[] convertParameters(Parameter[] p, MemberDoc m)
+    {
+        SourcePosition pos = m.position();
+        int len = p.length;
+        ParameterInfo[] q = new ParameterInfo[len];
+        for (int i=0; i<len; i++) {
+            q[i] = Converter.convertParameter(p[i], pos);
+        }
+        return q;
+    }
+
+    private static TypeInfo[] convertTypes(Type[] p)
+    {
+        if (p == null) return null;
+        int len = p.length;
+        TypeInfo[] q = new TypeInfo[len];
+        for (int i=0; i<len; i++) {
+            q[i] = Converter.obtainType(p[i]);
+        }
+        return q;
+    }
+
+    private Converter()
+    {
+    }
+
+    private static class ClassNeedingInit
+    {
+        ClassNeedingInit(ClassDoc c, ClassInfo cl)
+        {
+            this.c = c;
+            this.cl = cl;
+        }
+        ClassDoc c;
+        ClassInfo cl;
+    };
+    private static ArrayList<ClassNeedingInit> mClassesNeedingInit
+                                            = new ArrayList<ClassNeedingInit>();
+
+    static ClassInfo obtainClass(ClassDoc o)
+    {
+        return (ClassInfo)mClasses.obtain(o);
+    }
+    private static Cache mClasses = new Cache()
+    {
+        protected Object make(Object o)
+        {
+            ClassDoc c = (ClassDoc)o;
+            ClassInfo cl = new ClassInfo(
+                    c,
+                    c.getRawCommentText(),
+                    Converter.convertSourcePosition(c.position()),
+                    c.isPublic(),
+                    c.isProtected(),
+                    c.isPackagePrivate(),
+                    c.isPrivate(),
+                    c.isStatic(),
+                    c.isInterface(),
+                    c.isAbstract(),
+                    c.isOrdinaryClass(),
+                    c.isException(),
+                    c.isError(),
+                    c.isEnum(),
+                    (c instanceof AnnotationTypeDoc),
+                    c.isFinal(),
+                    c.isIncluded(),
+                    c.name(),
+                    c.qualifiedName(),
+                    c.qualifiedTypeName(),
+                    c.isPrimitive());
+            if (mClassesNeedingInit != null) {
+                mClassesNeedingInit.add(new ClassNeedingInit(c, cl));
+            }
+            return cl;
+        }
+        protected void made(Object o, Object r)
+        {
+            if (mClassesNeedingInit == null) {
+                initClass((ClassDoc)o, (ClassInfo)r);
+                ((ClassInfo)r).init2();
+            }
+        } 
+        ClassInfo[] all()
+        {
+            return (ClassInfo[])mCache.values().toArray(new ClassInfo[mCache.size()]);
+        }
+    };
+    
+    private static MethodInfo[] getHiddenMethods(MethodDoc[] methods){
+      if (methods == null) return null;
+      ArrayList<MethodInfo> out = new ArrayList<MethodInfo>();
+      int N = methods.length;
+      for (int i=0; i<N; i++) {
+          MethodInfo m = Converter.obtainMethod(methods[i]);
+          //System.out.println(m.toString() + ": ");
+          //for (TypeInfo ti : m.getTypeParameters()){
+            //  if (ti.asClassInfo() != null){
+                //System.out.println(" " +ti.asClassInfo().toString());
+              //} else {
+                //System.out.println(" null");
+              //}
+            //}
+          if (m.isHidden()) {
+              out.add(m);
+          }
+      }
+      return out.toArray(new MethodInfo[out.size()]);
+    }
+
+    /**
+     * Convert MethodDoc[] into MethodInfo[].  Also filters according
+     * to the -private, -public option, because the filtering doesn't seem
+     * to be working in the ClassDoc.constructors(boolean) call.
+     */
+    private static MethodInfo[] convertMethods(MethodDoc[] methods)
+    {
+        if (methods == null) return null;
+        ArrayList<MethodInfo> out = new ArrayList<MethodInfo>();
+        int N = methods.length;
+        for (int i=0; i<N; i++) {
+            MethodInfo m = Converter.obtainMethod(methods[i]);
+            //System.out.println(m.toString() + ": ");
+            //for (TypeInfo ti : m.getTypeParameters()){
+              //  if (ti.asClassInfo() != null){
+                  //System.out.println(" " +ti.asClassInfo().toString());
+                //} else {
+                  //System.out.println(" null");
+                //}
+              //}
+            if (m.checkLevel()) {
+                out.add(m);
+            }
+        }
+        return out.toArray(new MethodInfo[out.size()]);
+    }
+
+    private static MethodInfo[] convertMethods(ConstructorDoc[] methods)
+    {
+        if (methods == null) return null;
+        ArrayList<MethodInfo> out = new ArrayList<MethodInfo>();
+        int N = methods.length;
+        for (int i=0; i<N; i++) {
+            MethodInfo m = Converter.obtainMethod(methods[i]);
+            if (m.checkLevel()) {
+                out.add(m);
+            }
+        }
+        return out.toArray(new MethodInfo[out.size()]);
+    }
+    
+    private static MethodInfo[] convertNonWrittenConstructors(ConstructorDoc[] methods)
+    {
+        if (methods == null) return null;
+        ArrayList<MethodInfo> out = new ArrayList<MethodInfo>();
+        int N = methods.length;
+        for (int i=0; i<N; i++) {
+            MethodInfo m = Converter.obtainMethod(methods[i]);
+            if (!m.checkLevel()) {
+                out.add(m);
+            }
+        }
+        return out.toArray(new MethodInfo[out.size()]);
+    }
+
+    private static MethodInfo obtainMethod(MethodDoc o)
+    {
+        return (MethodInfo)mMethods.obtain(o);
+    }
+    private static MethodInfo obtainMethod(ConstructorDoc o)
+    {
+        return (MethodInfo)mMethods.obtain(o);
+    }
+    private static Cache mMethods = new Cache()
+    {
+        protected Object make(Object o)
+        {
+            if (o instanceof AnnotationTypeElementDoc) {
+                AnnotationTypeElementDoc m = (AnnotationTypeElementDoc)o;
+                MethodInfo result = new MethodInfo(
+                                m.getRawCommentText(),
+                                Converter.convertTypes(m.typeParameters()),
+                                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()),
+                                Converter.obtainType(m.returnType()),
+                                Converter.convertParameters(m.parameters(), m),
+                                Converter.convertClasses(m.thrownExceptions()),
+                                Converter.convertSourcePosition(m.position()),
+                                Converter.convertAnnotationInstances(m.annotations())
+                            );
+                result.setVarargs(m.isVarArgs());
+                result.init(Converter.obtainAnnotationValue(m.defaultValue(), result));
+                return result;
+            }
+            else if (o instanceof MethodDoc) {
+                MethodDoc m = (MethodDoc)o;
+                MethodInfo result = new MethodInfo(
+                                m.getRawCommentText(),
+                                Converter.convertTypes(m.typeParameters()),
+                                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(), false,
+                                "method",
+                                m.flatSignature(),
+                                Converter.obtainMethod(m.overriddenMethod()),
+                                Converter.obtainType(m.returnType()),
+                                Converter.convertParameters(m.parameters(), m),
+                                Converter.convertClasses(m.thrownExceptions()),
+                                Converter.convertSourcePosition(m.position()),
+                                Converter.convertAnnotationInstances(m.annotations())
+                           );
+                result.setVarargs(m.isVarArgs());
+                result.init(null);
+                return result;
+            }
+            else {
+                ConstructorDoc m = (ConstructorDoc)o;
+                MethodInfo result = new MethodInfo(
+                                m.getRawCommentText(),
+                                Converter.convertTypes(m.typeParameters()),
+                                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(),
+                                false, m.isSynchronized(), m.isNative(), false,
+                                "constructor",
+                                m.flatSignature(),
+                                null,
+                                null,
+                                Converter.convertParameters(m.parameters(), m),
+                                Converter.convertClasses(m.thrownExceptions()),
+                                Converter.convertSourcePosition(m.position()),
+                                Converter.convertAnnotationInstances(m.annotations())
+                            );
+                result.setVarargs(m.isVarArgs());
+                result.init(null);
+                return result;
+            }
+        }
+    };
+
+
+    private static FieldInfo[] convertFields(FieldDoc[] fields)
+    {
+        if (fields == null) return null;
+        ArrayList<FieldInfo> out = new ArrayList<FieldInfo>();
+        int N = fields.length;
+        for (int i=0; i<N; i++) {
+            FieldInfo f = Converter.obtainField(fields[i]);
+            if (f.checkLevel()) {
+                out.add(f);
+            }
+        }
+        return out.toArray(new FieldInfo[out.size()]);
+    }
+
+    private static FieldInfo obtainField(FieldDoc o)
+    {
+        return (FieldInfo)mFields.obtain(o);
+    }
+    private static FieldInfo obtainField(ConstructorDoc o)
+    {
+        return (FieldInfo)mFields.obtain(o);
+    }
+    private static Cache mFields = new Cache()
+    {
+        protected Object make(Object o)
+        {
+            FieldDoc f = (FieldDoc)o;
+            return new FieldInfo(f.name(),
+                            Converter.obtainClass(f.containingClass()),
+                            Converter.obtainClass(f.containingClass()),
+                            f.isPublic(), f.isProtected(),
+                            f.isPackagePrivate(), f.isPrivate(),
+                            f.isFinal(), f.isStatic(), f.isTransient(), f.isVolatile(),
+                            f.isSynthetic(),
+                            Converter.obtainType(f.type()),
+                            f.getRawCommentText(), f.constantValue(),
+                            Converter.convertSourcePosition(f.position()),
+                            Converter.convertAnnotationInstances(f.annotations())
+                        );
+        }
+    };
+
+    private static PackageInfo obtainPackage(PackageDoc o)
+    {
+        return (PackageInfo)mPackagees.obtain(o);
+    }
+    private static Cache mPackagees = new Cache()
+    {
+        protected Object make(Object o)
+        {
+            PackageDoc p = (PackageDoc)o;
+            return new PackageInfo(p, p.name(),
+                    Converter.convertSourcePosition(p.position()));
+        }
+    };
+
+    private static TypeInfo obtainType(Type o)
+    {
+        return (TypeInfo)mTypes.obtain(o);
+    }
+    private static Cache mTypes = new Cache()
+    {
+       protected Object make(Object o)
+       {
+           Type t = (Type)o;
+           String simpleTypeName;
+           if (t instanceof ClassDoc) {
+               simpleTypeName = ((ClassDoc)t).name();
+           } else {
+               simpleTypeName = t.simpleTypeName();
+           }
+           TypeInfo ti = new TypeInfo(t.isPrimitive(), t.dimension(),
+                   simpleTypeName, t.qualifiedTypeName(),
+                   Converter.obtainClass(t.asClassDoc()));
+           return ti;
+       }
+        protected void made(Object o, Object r)
+        {
+            Type t = (Type)o;
+            TypeInfo ti = (TypeInfo)r;
+            if (t.asParameterizedType() != null) {
+                ti.setTypeArguments(Converter.convertTypes(
+                            t.asParameterizedType().typeArguments()));
+            }
+            else if (t instanceof ClassDoc) {
+                ti.setTypeArguments(Converter.convertTypes(((ClassDoc)t).typeParameters()));
+            }
+            else if (t.asTypeVariable() != null) {
+                ti.setBounds(null, Converter.convertTypes((t.asTypeVariable().bounds())));
+                ti.setIsTypeVariable(true);
+            }
+            else if (t.asWildcardType() != null) {
+                ti.setIsWildcard(true);
+                ti.setBounds(Converter.convertTypes(t.asWildcardType().superBounds()),
+                             Converter.convertTypes(t.asWildcardType().extendsBounds()));
+            }
+        }
+        protected Object keyFor(Object o)
+        {  
+            Type t = (Type)o;
+            String keyString = o.getClass().getName() + "/" + o.toString() + "/";
+            if (t.asParameterizedType() != null){
+              keyString += t.asParameterizedType().toString() +"/";
+              if (t.asParameterizedType().typeArguments() != null){
+              for(Type ty : t.asParameterizedType().typeArguments()){
+                keyString += ty.toString() + "/";
+              }
+              }
+            }else{
+              keyString += "NoParameterizedType//";
+            }
+            if (t.asTypeVariable() != null){
+              keyString += t.asTypeVariable().toString() +"/";
+              if (t.asTypeVariable().bounds() != null){
+              for(Type ty : t.asTypeVariable().bounds()){
+                keyString += ty.toString() + "/";
+              }
+              }
+            }else{
+              keyString += "NoTypeVariable//";
+            }
+            if (t.asWildcardType() != null){
+              keyString += t.asWildcardType().toString() +"/";
+              if (t.asWildcardType().superBounds() != null){
+              for(Type ty : t.asWildcardType().superBounds()){
+                keyString += ty.toString() + "/";
+              }
+              }
+              if (t.asWildcardType().extendsBounds() != null){
+                for(Type ty : t.asWildcardType().extendsBounds()){
+                  keyString += ty.toString() + "/";
+                }
+                }
+            }else{
+              keyString += "NoWildCardType//";
+            }
+            
+            
+            
+            return keyString;
+        }
+    };
+    
+
+
+    private static MemberInfo obtainMember(MemberDoc o)
+    {
+        return (MemberInfo)mMembers.obtain(o);
+    }
+    private static Cache mMembers = new Cache()
+    {
+        protected Object make(Object o)
+        {
+            if (o instanceof MethodDoc) {
+                return Converter.obtainMethod((MethodDoc)o);
+            }
+            else if (o instanceof ConstructorDoc) {
+                return Converter.obtainMethod((ConstructorDoc)o);
+            }
+            else if (o instanceof FieldDoc) {
+                return Converter.obtainField((FieldDoc)o);
+            }
+            else {
+                return null;
+            }
+        }
+    };
+
+    private static AnnotationInstanceInfo[] convertAnnotationInstances(AnnotationDesc[] orig)
+    {
+        int len = orig.length;
+        AnnotationInstanceInfo[] out = new AnnotationInstanceInfo[len];
+        for (int i=0; i<len; i++) {
+            out[i] = Converter.obtainAnnotationInstance(orig[i]);
+        }
+        return out;
+    }
+
+
+    private static AnnotationInstanceInfo obtainAnnotationInstance(AnnotationDesc o)
+    {
+        return (AnnotationInstanceInfo)mAnnotationInstances.obtain(o);
+    }
+    private static Cache mAnnotationInstances = new Cache()
+    {
+        protected Object make(Object o)
+        {
+            AnnotationDesc a = (AnnotationDesc)o;
+            ClassInfo annotationType = Converter.obtainClass(a.annotationType());
+            AnnotationDesc.ElementValuePair[] ev = a.elementValues();
+            AnnotationValueInfo[] elementValues = new AnnotationValueInfo[ev.length];
+            for (int i=0; i<ev.length; i++) {
+                elementValues[i] = obtainAnnotationValue(ev[i].value(),
+                                            Converter.obtainMethod(ev[i].element()));
+            }
+            return new AnnotationInstanceInfo(annotationType, elementValues);
+        }
+    };
+
+
+    private abstract static class Cache
+    {
+        void put(Object key, Object value)
+        {
+            mCache.put(key, value);
+        }
+        Object obtain(Object o)
+        {
+            if (o == null ) {
+                return null;
+            }
+            Object k = keyFor(o);
+            Object r = mCache.get(k);
+            if (r == null) {
+                r = make(o);
+                mCache.put(k, r);
+                made(o, r);
+            }
+            return r;
+        }
+        protected HashMap<Object,Object> mCache = new HashMap<Object,Object>();
+        protected abstract Object make(Object o);
+        protected void made(Object o, Object r)
+        {
+        }
+        protected Object keyFor(Object o) { return o; }
+        Object[] all() { return null; }
+    }
+
+    // annotation values
+    private static HashMap<AnnotationValue,AnnotationValueInfo> mAnnotationValues = new HashMap();
+    private static HashSet<AnnotationValue> mAnnotationValuesNeedingInit = new HashSet();
+
+    private static AnnotationValueInfo obtainAnnotationValue(AnnotationValue o, MethodInfo element)
+    {
+        if (o == null) {
+            return null;
+        }
+        AnnotationValueInfo v = mAnnotationValues.get(o);
+        if (v != null) return v;
+        v = new AnnotationValueInfo(element);
+        mAnnotationValues.put(o, v);
+        if (mAnnotationValuesNeedingInit != null) {
+            mAnnotationValuesNeedingInit.add(o);
+        } else {
+            initAnnotationValue(o, v);
+        }
+        return v;
+    }
+
+    private static void initAnnotationValue(AnnotationValue o, AnnotationValueInfo v) {
+        Object orig = o.value();
+        Object converted;
+        if (orig instanceof Type) {
+            // class literal
+            converted = Converter.obtainType((Type)orig);
+        }
+        else if (orig instanceof FieldDoc) {
+            // enum constant
+            converted = Converter.obtainField((FieldDoc)orig);
+        }
+        else if (orig instanceof AnnotationDesc) {
+            // annotation instance
+            converted = Converter.obtainAnnotationInstance((AnnotationDesc)orig);
+        }
+        else if (orig instanceof AnnotationValue[]) {
+            AnnotationValue[] old = (AnnotationValue[])orig;
+            AnnotationValueInfo[] array = new AnnotationValueInfo[old.length];
+            for (int i=0; i<array.length; i++) {
+                array[i] = Converter.obtainAnnotationValue(old[i], null);
+            }
+            converted = array;
+        }
+        else {
+            converted = orig;
+        }
+        v.init(converted);
+    }
+
+    private static void finishAnnotationValueInit()
+    {
+        int depth = 0;
+        while (mAnnotationValuesNeedingInit.size() > 0) {
+            HashSet<AnnotationValue> set = mAnnotationValuesNeedingInit;
+            mAnnotationValuesNeedingInit = new HashSet();
+            for (AnnotationValue o: set) {
+                AnnotationValueInfo v = mAnnotationValues.get(o);
+                initAnnotationValue(o, v);
+            }
+            depth++;
+        }
+        mAnnotationValuesNeedingInit = null;
+    }
+}
diff --git a/tools/droiddoc/src/DocFile.java b/tools/droiddoc/src/DocFile.java
new file mode 100644
index 0000000..f53a35c
--- /dev/null
+++ b/tools/droiddoc/src/DocFile.java
@@ -0,0 +1,137 @@
+/*
+ * 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 org.clearsilver.CS;
+import java.util.*;
+import java.io.*;
+import java.util.regex.Pattern;
+import java.util.regex.Matcher;
+
+
+public class DocFile
+{
+    private static final Pattern LINE = Pattern.compile("(.*)[\r]?\n",
+                                                        Pattern.MULTILINE);
+    private static final Pattern PROP = Pattern.compile("([^=]+)=(.*)");
+
+    public static String readFile(String filename)
+    {
+        try {
+            File f = new File(filename);
+            int length = (int)f.length();
+            FileReader reader = new FileReader(f);
+            char[] buf = new char[length];
+            int index = 0;
+            int amt;
+            while (true) {
+                amt = reader.read(buf, index, length-index);
+
+                if (amt < 1) {
+                    break;
+                }
+
+                index += amt;
+            }
+            return new String(buf, 0, index);
+        }
+        catch (IOException e) {
+            return null;
+        }
+    }
+
+    public static void writePage(String docfile, String relative,
+                                    String outfile)
+    {
+        HDF hdf = DroidDoc.makeHDF();
+
+        /*
+        System.out.println("docfile='" + docfile
+                            + "' relative='" + relative + "'"
+                            + "' outfile='" + outfile + "'");
+        */
+
+        String filedata = readFile(docfile);
+
+        // The document is properties up until the line "@jd:body".
+        // Any blank lines are ignored.
+        int start = -1;
+        int lineno = 1;
+        Matcher lines = LINE.matcher(filedata);
+        String line = null;
+        while (lines.find()) {
+            line = lines.group(1);
+            if (line.length() > 0) {
+                if (line.equals("@jd:body")) {
+                    start = lines.end();
+                    break;
+                }
+                Matcher prop = PROP.matcher(line);
+                if (prop.matches()) {
+                    String key = prop.group(1);
+                    String value = prop.group(2);
+                    hdf.setValue(key, value);
+                } else {
+                    break;
+                }
+            }
+            lineno++;
+        }
+        if (start < 0) {
+            System.err.println(docfile + ":" + lineno + ": error parsing docfile");
+            if (line != null) {
+                System.err.println(docfile + ":" + lineno + ":" + line);
+            }
+            System.exit(1);
+        }
+
+        // if they asked to only be for a certain template, maybe skip it
+        String fromTemplate = hdf.getValue("template.which", "");
+        String fromPage = hdf.getValue("page.onlyfortemplate", "");
+        if (!"".equals(fromPage) && !fromTemplate.equals(fromPage)) {
+            return;
+        }
+
+        // and the actual text after that
+        String commentText = filedata.substring(start);
+
+        Comment comment = new Comment(commentText, null,
+                                    new SourcePositionInfo(docfile, lineno, 1));
+        TagInfo[] tags = comment.tags();
+
+        TagInfo.makeHDF(hdf, "root.descr", tags);
+
+        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 {
+                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);
+        }
+    }
+
+}
diff --git a/tools/droiddoc/src/DocInfo.java b/tools/droiddoc/src/DocInfo.java
new file mode 100644
index 0000000..2530dc2
--- /dev/null
+++ b/tools/droiddoc/src/DocInfo.java
@@ -0,0 +1,58 @@
+/*
+ * 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.
+ */
+
+public abstract class DocInfo
+{
+    public DocInfo(String rawCommentText, SourcePositionInfo sp)
+    {
+        mRawCommentText = rawCommentText;
+        mPosition = sp;
+    }
+
+    public boolean isHidden()
+    {
+        return comment().isHidden();
+    }
+
+    public boolean isDocOnly() {
+        return comment().isDocOnly();
+    }
+    
+    public String getRawCommentText()
+    {
+        return mRawCommentText;
+    }
+
+    public Comment comment()
+    {
+        if (mComment == null) {
+            mComment = new Comment(mRawCommentText, parent(), mPosition);
+        }
+        return mComment;
+    }
+
+    public SourcePositionInfo position()
+    {
+        return mPosition;
+    }
+
+    public abstract ContainerInfo parent();
+
+    private String mRawCommentText;
+    Comment mComment;
+    SourcePositionInfo mPosition;
+}
+
diff --git a/tools/droiddoc/src/DroidDoc.java b/tools/droiddoc/src/DroidDoc.java
new file mode 100644
index 0000000..f664c41
--- /dev/null
+++ b/tools/droiddoc/src/DroidDoc.java
@@ -0,0 +1,1342 @@
+/*
+ * 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 com.sun.javadoc.*;
+
+import org.clearsilver.HDF;
+
+import java.util.*;
+import java.io.*;
+import java.lang.reflect.Proxy;
+import java.lang.reflect.Array;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+public class DroidDoc
+{
+    private static final String SDK_CONSTANT_ANNOTATION = "android.annotation.SdkConstant";
+    private static final String SDK_CONSTANT_TYPE_ACTIVITY_ACTION = "android.annotation.SdkConstant.SdkConstantType.ACTIVITY_INTENT_ACTION";
+    private static final String SDK_CONSTANT_TYPE_BROADCAST_ACTION = "android.annotation.SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION";
+    private static final String SDK_CONSTANT_TYPE_SERVICE_ACTION = "android.annotation.SdkConstant.SdkConstantType.SERVICE_INTENT_ACTION";
+    private static final String SDK_CONSTANT_TYPE_CATEGORY = "android.annotation.SdkConstant.SdkConstantType.INTENT_CATEGORY";
+    private static final String SDK_WIDGET_ANNOTATION = "android.annotation.Widget";
+    private static final String SDK_LAYOUT_ANNOTATION = "android.annotation.Layout";
+
+    private static final int TYPE_NONE = 0;
+    private static final int TYPE_WIDGET = 1;
+    private static final int TYPE_LAYOUT = 2;
+    private static final int TYPE_LAYOUT_PARAM = 3;
+    
+    public static final int SHOW_PUBLIC = 0x00000001;
+    public static final int SHOW_PROTECTED = 0x00000003;
+    public static final int SHOW_PACKAGE = 0x00000007;
+    public static final int SHOW_PRIVATE = 0x0000000f;
+    public static final int SHOW_HIDDEN = 0x0000001f;
+
+    public static int showLevel = SHOW_PROTECTED;
+
+    public static final String javadocDir = "reference/";
+    public static String htmlExtension;
+
+    public static RootDoc root;
+    public static ArrayList<String[]> mHDFData = new ArrayList<String[]>();
+    public static Map<Character,String> escapeChars = new HashMap<Character,String>();
+    public static String title = "";
+
+    public static boolean checkLevel(int level)
+    {
+        return (showLevel & level) == level;
+    }
+
+    public static boolean checkLevel(boolean pub, boolean prot, boolean pkgp,
+            boolean priv, boolean hidden)
+    {
+        int level = 0;
+        if (hidden && !checkLevel(SHOW_HIDDEN)) {
+            return false;
+        }
+        if (pub && checkLevel(SHOW_PUBLIC)) {
+            return true;
+        }
+        if (prot && checkLevel(SHOW_PROTECTED)) {
+            return true;
+        }
+        if (pkgp && checkLevel(SHOW_PACKAGE)) {
+            return true;
+        }
+        if (priv && checkLevel(SHOW_PRIVATE)) {
+            return true;
+        }
+        return false;
+    }
+    
+    public static boolean start(RootDoc r)
+    {
+        String keepListFile = null;
+        String proofreadFile = null;
+        String todoFile = null;
+        String sdkValuePath = null;
+        ArrayList<SampleCode> sampleCodes = new ArrayList<SampleCode>();
+        String stubsDir = null;
+        //Create the dependency graph for the stubs directory
+        boolean apiXML = false;
+        String apiFile = null;
+        String debugStubsFile = "";
+        HashSet<String> stubPackages = null;
+
+        root = r;
+
+        String[][] options = r.options();
+        for (String[] a: options) {
+            if (a[0].equals("-d")) {
+                ClearPage.outputDir = a[1];
+            }
+            else if (a[0].equals("-templatedir")) {
+                ClearPage.addTemplateDir(a[1]);
+            }
+            else if (a[0].equals("-hdf")) {
+                mHDFData.add(new String[] {a[1], a[2]});
+            }
+            else if (a[0].equals("-toroot")) {
+                ClearPage.toroot = a[1];
+            }
+            else if (a[0].equals("-samplecode")) {
+                sampleCodes.add(new SampleCode(a[1], a[2], a[3]));
+            }
+            else if (a[0].equals("-htmldir")) {
+                ClearPage.htmlDir = a[1];
+            }
+            else if (a[0].equals("-title")) {
+                DroidDoc.title = a[1];
+            }
+            else if (a[0].equals("-werror")) {
+                Errors.setWarningsAreErrors(true);
+            }
+            else if (a[0].equals("-error") || a[0].equals("-warning")
+                    || a[0].equals("-hide")) {
+                try {
+                    int level = -1;
+                    if (a[0].equals("-error")) {
+                        level = Errors.ERROR;
+                    }
+                    else if (a[0].equals("-warning")) {
+                        level = Errors.WARNING;
+                    }
+                    else if (a[0].equals("-hide")) {
+                        level = Errors.HIDDEN;
+                    }
+                    Errors.setErrorLevel(Integer.parseInt(a[1]), level);
+                }
+                catch (NumberFormatException e) {
+                    // already printed below
+                    return false;
+                }
+            }
+            else if (a[0].equals("-keeplist")) {
+                keepListFile = a[1];
+            }
+            else if (a[0].equals("-proofread")) {
+                proofreadFile = a[1];
+            }
+            else if (a[0].equals("-todo")) {
+                todoFile = a[1];
+            }
+            else if (a[0].equals("-public")) {
+                showLevel = SHOW_PUBLIC;
+            }
+            else if (a[0].equals("-protected")) {
+                showLevel = SHOW_PROTECTED;
+            }
+            else if (a[0].equals("-package")) {
+                showLevel = SHOW_PACKAGE;
+            }
+            else if (a[0].equals("-private")) {
+                showLevel = SHOW_PRIVATE;
+            }
+            else if (a[0].equals("-hidden")) {
+                showLevel = SHOW_HIDDEN;
+            }
+            else if (a[0].equals("-stubs")) {
+                stubsDir = a[1];
+            }
+            else if (a[0].equals("-stubpackages")) {
+                stubPackages = new HashSet();
+                for (String pkg: a[1].split(":")) {
+                    stubPackages.add(pkg);
+                }
+            }
+            else if (a[0].equals("-sdkvalues")) {
+                sdkValuePath = a[1];
+            }
+            else if (a[0].equals("-apixml")) {
+                apiXML = true;
+                apiFile = a[1];
+            }
+        }
+
+        // read some prefs from the template
+        if (!readTemplateSettings()) {
+            return false;
+        }
+
+        // Set up the data structures
+        Converter.makeInfo(r);
+
+        // Files for proofreading
+        if (proofreadFile != null) {
+            Proofread.initProofread(proofreadFile);
+        }
+        if (todoFile != null) {
+            TodoFile.writeTodoFile(todoFile);
+        }
+
+        // HTML Pages
+        if (ClearPage.htmlDir != null) {
+            writeHTMLPages();
+        }
+
+        // Navigation tree
+        NavTree.writeNavTree(javadocDir);
+
+        // Packages Pages
+        writePackages(javadocDir
+                        + (ClearPage.htmlDir!=null
+                            ? "packages" + htmlExtension
+                            : "index" + htmlExtension));
+
+        // Classes
+        writeClassLists();
+        writeClasses();
+        writeHierarchy();
+ //      writeKeywords();
+
+        // Lists for JavaScript
+        writeLists();
+        if (keepListFile != null) {
+            writeKeepList(keepListFile);
+        }
+
+        // Sample Code
+        for (SampleCode sc: sampleCodes) {
+            sc.write();
+        }
+
+        // Index page
+        writeIndex();
+
+        Proofread.finishProofread(proofreadFile);
+
+        // Stubs
+        if (stubsDir != null) {
+            Stubs.writeStubs(stubsDir, apiXML, apiFile, stubPackages);
+        }
+        
+        if (sdkValuePath != null) {
+            writeSdkValues(sdkValuePath);
+        }
+
+        Errors.printErrors();
+        return !Errors.hadError;
+    }
+
+    private static void writeIndex() {
+        HDF data = makeHDF();
+        ClearPage.write(data, "index.cs", javadocDir + "index" + htmlExtension);
+    }
+
+    private static boolean readTemplateSettings()
+    {
+        HDF data = makeHDF();
+        htmlExtension = data.getValue("template.extension", ".html");
+        int i=0;
+        while (true) {
+            String k = data.getValue("template.escape." + i + ".key", "");
+            String v = data.getValue("template.escape." + i + ".value", "");
+            if ("".equals(k)) {
+                break;
+            }
+            if (k.length() != 1) {
+                System.err.println("template.escape." + i + ".key must have a length of 1: " + k);
+                return false;
+            }
+            escapeChars.put(k.charAt(0), v);
+            i++;
+        }
+        return true;
+    }
+
+    public static String escape(String s) {
+        if (escapeChars.size() == 0) {
+            return s;
+        }
+        StringBuffer b = null;
+        int begin = 0;
+        final int N = s.length();
+        for (int i=0; i<N; i++) {
+            char c = s.charAt(i);
+            String mapped = escapeChars.get(c);
+            if (mapped != null) {
+                if (b == null) {
+                    b = new StringBuffer(s.length() + mapped.length());
+                }
+                if (begin != i) {
+                    b.append(s.substring(begin, i));
+                }
+                b.append(mapped);
+                begin = i+1;
+            }
+        }
+        if (b != null) {
+            if (begin != N) {
+                b.append(s.substring(begin, N));
+            }
+            return b.toString();
+        }
+        return s;
+    }
+
+    public static void setPageTitle(HDF data, String title)
+    {
+        String s = title;
+        if (DroidDoc.title.length() > 0) {
+            s += " - " + DroidDoc.title;
+        }
+        data.setValue("page.title", s);
+    }
+
+    public static LanguageVersion languageVersion()
+    {
+        return LanguageVersion.JAVA_1_5;
+    }
+
+    public static int optionLength(String option)
+    {
+        if (option.equals("-d")) {
+            return 2;
+        }
+        if (option.equals("-templatedir")) {
+            return 2;
+        }
+        if (option.equals("-hdf")) {
+            return 3;
+        }
+        if (option.equals("-toroot")) {
+            return 2;
+        }
+        if (option.equals("-samplecode")) {
+            return 4;
+        }
+        if (option.equals("-htmldir")) {
+            return 2;
+        }
+        if (option.equals("-title")) {
+            return 2;
+        }
+        if (option.equals("-werror")) {
+            return 1;
+        }
+        if (option.equals("-hide")) {
+            return 2;
+        }
+        if (option.equals("-warning")) {
+            return 2;
+        }
+        if (option.equals("-error")) {
+            return 2;
+        }
+        if (option.equals("-keeplist")) {
+            return 2;
+        }
+        if (option.equals("-proofread")) {
+            return 2;
+        }
+        if (option.equals("-todo")) {
+            return 2;
+        }
+        if (option.equals("-public")) {
+            return 1;
+        }
+        if (option.equals("-protected")) {
+            return 1;
+        }
+        if (option.equals("-package")) {
+            return 1;
+        }
+        if (option.equals("-private")) {
+            return 1;
+        }
+        if (option.equals("-hidden")) {
+            return 1;
+        }
+        if (option.equals("-stubs")) {
+            return 2;
+        }
+        if (option.equals("-stubpackages")) {
+            return 2;
+        }
+        if (option.equals("-sdkvalues")) {
+            return 2;
+        }
+        if (option.equals("-apixml")) {
+            return 2;
+        }
+        return 0;
+    }
+    
+    public static boolean validOptions(String[][] options, DocErrorReporter r)
+    {
+        for (String[] a: options) {
+            if (a[0].equals("-error") || a[0].equals("-warning")
+                    || a[0].equals("-hide")) {
+                try {
+                    Integer.parseInt(a[1]);
+                }
+                catch (NumberFormatException e) {
+                    r.printError("bad -" + a[0] + " value must be a number: "
+                            + a[1]);
+                    return false;
+                }
+            }
+        }
+
+        return true;
+    }
+
+    public static HDF makeHDF()
+    {
+        HDF data = new HDF();
+
+        for (String[] p: mHDFData) {
+            data.setValue(p[0], p[1]);
+        }
+
+        try {
+            for (String p: ClearPage.hdfFiles) {
+                data.readFile(p);
+            }
+        }
+        catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+
+        return data;
+    }
+
+    public static HDF makePackageHDF()
+    {
+        HDF data = makeHDF();
+        ClassInfo[] classes = Converter.rootClasses();
+
+        SortedMap<String, PackageInfo> sorted = new TreeMap<String, PackageInfo>();
+        for (ClassInfo cl: classes) {
+            PackageInfo pkg = cl.containingPackage();
+            String name;
+            if (pkg == null) {
+                name = "";
+            } else {
+                name = pkg.name();
+            }
+            sorted.put(name, pkg);
+        }
+
+        int i = 0;
+        for (String s: sorted.keySet()) {
+            PackageInfo pkg = sorted.get(s);
+
+            if (pkg.isHidden()) {
+                continue;
+            }
+            Boolean allHidden = true;
+            int pass = 0;
+            ClassInfo[] classesToCheck = null;
+            while (pass < 5 ) {
+                switch(pass) {
+                case 0:
+                    classesToCheck = pkg.ordinaryClasses();
+                    break;
+                case 1:
+                    classesToCheck = pkg.enums();
+                    break;
+                case 2:
+                    classesToCheck = pkg.errors();
+                    break;
+                case 3:
+                    classesToCheck = pkg.exceptions();
+                    break;
+                case 4:
+                    classesToCheck = pkg.interfaces();
+                    break;
+                default:
+                    System.err.println("Error reading package: " + pkg.name());
+                    break;
+                }
+                for (ClassInfo cl : classesToCheck) {
+                    if (!cl.isHidden()) {
+                        allHidden = false;
+                        break;
+                    }
+                }
+                if (!allHidden) {
+                    break;
+                }
+                pass++;
+            }
+            if (allHidden) {
+                continue;
+            }
+
+            data.setValue("reference", "true");
+            data.setValue("docs.packages." + i + ".name", s);
+            data.setValue("docs.packages." + i + ".link", pkg.htmlPage());
+            TagInfo.makeHDF(data, "docs.packages." + i + ".shortDescr",
+                                               pkg.firstSentenceTags());
+            i++;
+        }
+
+        return data;
+    }
+
+    public static void writeDirectory(File dir, String relative)
+    {
+        File[] files = dir.listFiles();
+        int i, count = files.length;
+        for (i=0; i<count; i++) {
+            File f = files[i];
+            if (f.isFile()) {
+                String templ = relative + f.getName();
+                int len = templ.length();
+                if (len > 3 && ".cs".equals(templ.substring(len-3))) {
+                    HDF data = makeHDF();
+                    String filename = templ.substring(0,len-3) + htmlExtension;
+                    ClearPage.write(data, templ, filename);
+                }
+                else if (len > 3 && ".jd".equals(templ.substring(len-3))) {
+                    String filename = templ.substring(0,len-3) + htmlExtension;
+                    DocFile.writePage(f.getAbsolutePath(), relative, filename);
+                }
+                else {
+                    ClearPage.copyFile(f, templ);
+                }
+            }
+            else if (f.isDirectory()) {
+                writeDirectory(f, relative + f.getName() + "/");
+            }
+        }
+    }
+
+    public static void writeHTMLPages()
+    {
+        File f = new File(ClearPage.htmlDir);
+        if (!f.isDirectory()) {
+            System.err.println("htmlDir not a directory: " + ClearPage.htmlDir);
+        }
+        writeDirectory(f, "");
+    }
+
+    public static void writeLists()
+    {
+        HDF data = makeHDF();
+
+        ClassInfo[] classes = Converter.rootClasses();
+
+        SortedMap<String, Object> sorted = new TreeMap<String, Object>();
+        for (ClassInfo cl: classes) {
+            if (cl.isHidden()) {
+                continue;
+            }
+            sorted.put(cl.qualifiedName(), cl);
+            PackageInfo pkg = cl.containingPackage();
+            String name;
+            if (pkg == null) {
+                name = "";
+            } else {
+                name = pkg.name();
+            }
+            sorted.put(name, pkg);
+        }
+
+        int i = 0;
+        for (String s: sorted.keySet()) {
+            data.setValue("docs.pages." + i + ".id" , ""+i);
+            data.setValue("docs.pages." + i + ".label" , s);
+
+            Object o = sorted.get(s);
+            if (o instanceof PackageInfo) {
+                PackageInfo pkg = (PackageInfo)o;
+                data.setValue("docs.pages." + i + ".link" , pkg.htmlPage());
+                data.setValue("docs.pages." + i + ".type" , "package");
+            }
+            else if (o instanceof ClassInfo) {
+                ClassInfo cl = (ClassInfo)o;
+                data.setValue("docs.pages." + i + ".link" , cl.htmlPage());
+                data.setValue("docs.pages." + i + ".type" , "class");
+            }
+            i++;
+        }
+
+        ClearPage.write(data, "lists.cs", javadocDir + "lists.js");
+    }
+
+    public static void cantStripThis(ClassInfo cl, HashSet<ClassInfo> notStrippable) {
+        if (!notStrippable.add(cl)) {
+            // slight optimization: if it already contains cl, it already contains
+            // all of cl's parents
+            return;
+        }
+        ClassInfo supr = cl.superclass();
+        if (supr != null) {
+            cantStripThis(supr, notStrippable);
+        }
+        for (ClassInfo iface: cl.interfaces()) {
+            cantStripThis(iface, notStrippable);
+        }
+    }
+
+    private static String getPrintableName(ClassInfo cl) {
+        ClassInfo containingClass = cl.containingClass();
+        if (containingClass != null) {
+            // This is an inner class.
+            String baseName = cl.name();
+            baseName = baseName.substring(baseName.lastIndexOf('.') + 1);
+            return getPrintableName(containingClass) + '$' + baseName;
+        }
+        return cl.qualifiedName();
+    }
+
+    /**
+     * Writes the list of classes that must be present in order to
+     * provide the non-hidden APIs known to javadoc.
+     *
+     * @param filename the path to the file to write the list to
+     */
+    public static void writeKeepList(String filename) {
+        HashSet<ClassInfo> notStrippable = new HashSet<ClassInfo>();
+        ClassInfo[] all = Converter.allClasses();
+        Arrays.sort(all); // just to make the file a little more readable
+
+        // If a class is public and not hidden, then it and everything it derives
+        // from cannot be stripped.  Otherwise we can strip it.
+        for (ClassInfo cl: all) {
+            if (cl.isPublic() && !cl.isHidden()) {
+                cantStripThis(cl, notStrippable);
+            }
+        }
+        PrintStream stream = null;
+        try {
+            stream = new PrintStream(filename);
+            for (ClassInfo cl: notStrippable) {
+                stream.println(getPrintableName(cl));
+            }
+        }
+        catch (FileNotFoundException e) {
+            System.err.println("error writing file: " + filename);
+        }
+        finally {
+            if (stream != null) {
+                stream.close();
+            }
+        }
+    }
+
+    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();
+            String name;
+            if (pkg == null) {
+                name = "";
+            } else {
+                name = pkg.name();
+            }
+            sorted.put(name, pkg);
+        }
+
+        ArrayList<PackageInfo> result = new ArrayList();
+
+        for (String s: sorted.keySet()) {
+            PackageInfo pkg = sorted.get(s);
+
+            if (pkg.isHidden()) {
+                continue;
+            }
+            Boolean allHidden = true;
+            int pass = 0;
+            ClassInfo[] classesToCheck = null;
+            while (pass < 5 ) {
+                switch(pass) {
+                case 0:
+                    classesToCheck = pkg.ordinaryClasses();
+                    break;
+                case 1:
+                    classesToCheck = pkg.enums();
+                    break;
+                case 2:
+                    classesToCheck = pkg.errors();
+                    break;
+                case 3:
+                    classesToCheck = pkg.exceptions();
+                    break;
+                case 4:
+                    classesToCheck = pkg.interfaces();
+                    break;
+                default:
+                    System.err.println("Error reading package: " + pkg.name());
+                    break;
+                }
+                for (ClassInfo cl : classesToCheck) {
+                    if (!cl.isHidden()) {
+                        allHidden = false;
+                        break;
+                    }
+                }
+                if (!allHidden) {
+                    break;
+                }
+                pass++;
+            }
+            if (allHidden) {
+                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++;
+        }
+
+        setPageTitle(data, "Package Index");
+
+        TagInfo.makeHDF(data, "root.descr",
+                Converter.convertTags(root.inlineTags(), null));
+
+        ClearPage.write(data, "packages.cs", filename);
+        ClearPage.write(data, "package-list.cs", javadocDir + "package-list");
+
+        Proofread.writePackages(filename,
+                Converter.convertTags(root.inlineTags(), null));
+    }
+
+    public static void writePackage(PackageInfo pkg)
+    {
+        // these this and the description are in the same directory,
+        // so it's okay
+        HDF data = makePackageHDF();
+
+        String name = pkg.name();
+
+        data.setValue("package.name", name);
+        data.setValue("package.descr", "...description...");
+
+        makeClassListHDF(data, "package.interfaces", 
+                         ClassInfo.sortByName(pkg.interfaces()));
+        makeClassListHDF(data, "package.classes",
+                         ClassInfo.sortByName(pkg.ordinaryClasses()));
+        makeClassListHDF(data, "package.enums",
+                         ClassInfo.sortByName(pkg.enums()));
+        makeClassListHDF(data, "package.exceptions",
+                         ClassInfo.sortByName(pkg.exceptions()));
+        makeClassListHDF(data, "package.errors",
+                         ClassInfo.sortByName(pkg.errors()));
+        TagInfo.makeHDF(data, "package.shortDescr",
+                         pkg.firstSentenceTags());
+        TagInfo.makeHDF(data, "package.descr", pkg.inlineTags());
+
+        String filename = pkg.htmlPage();
+        setPageTitle(data, name);
+        ClearPage.write(data, "package.cs", filename);
+
+        filename = pkg.fullDescriptionHtmlPage();
+        setPageTitle(data, name + " Details");
+        ClearPage.write(data, "package-descr.cs", filename);
+
+        Proofread.writePackage(filename, pkg.inlineTags());
+    }
+
+    public static void writeClassLists()
+    {
+        int i;
+        HDF data = makePackageHDF();
+
+        ClassInfo[] classes = PackageInfo.filterHidden(
+                                    Converter.convertClasses(root.classes()));
+        if (classes.length == 0) {
+            return ;
+        }
+
+        Sorter[] sorted = new Sorter[classes.length];
+        for (i=0; i<sorted.length; i++) {
+            ClassInfo cl = classes[i];
+            String name = cl.name();
+            sorted[i] = new Sorter(name, cl);
+        }
+
+        Arrays.sort(sorted);
+
+        // make a pass and resolve ones that have the same name
+        int firstMatch = 0;
+        String lastName = sorted[0].label;
+        for (i=1; i<sorted.length; i++) {
+            String s = sorted[i].label;
+            if (!lastName.equals(s)) {
+                if (firstMatch != i-1) {
+                    // there were duplicates
+                    for (int j=firstMatch; j<i; j++) {
+                        PackageInfo pkg = ((ClassInfo)sorted[j].data).containingPackage();
+                        if (pkg != null) {
+                            sorted[j].label = sorted[j].label + " (" + pkg.name() + ")";
+                        }
+                    }
+                }
+                firstMatch = i;
+                lastName = s;
+            }
+        }
+
+        // and sort again
+        Arrays.sort(sorted);
+
+        for (i=0; i<sorted.length; i++) {
+            String s = sorted[i].label;
+            ClassInfo cl = (ClassInfo)sorted[i].data;
+            char first = Character.toUpperCase(s.charAt(0));
+            cl.makeShortDescrHDF(data, "docs.classes." + first + '.' + i);
+        }
+
+        setPageTitle(data, "Class Index");
+        ClearPage.write(data, "classes.cs", javadocDir + "classes" + htmlExtension);
+    }
+
+    // we use the word keywords because "index" means something else in html land
+    // the user only ever sees the word index
+/*    public static void writeKeywords()
+    {
+        ArrayList<KeywordEntry> keywords = new ArrayList<KeywordEntry>();
+
+        ClassInfo[] classes = PackageInfo.filterHidden(Converter.convertClasses(root.classes()));
+
+        for (ClassInfo cl: classes) {
+            cl.makeKeywordEntries(keywords);
+        }
+
+        HDF data = makeHDF();
+
+        Collections.sort(keywords);
+        
+        int i=0;
+        for (KeywordEntry entry: keywords) {
+            String base = "keywords." + entry.firstChar() + "." + i;
+            entry.makeHDF(data, base);
+            i++;
+        }
+
+        setPageTitle(data, "Index");
+        ClearPage.write(data, "keywords.cs", javadocDir + "keywords" + htmlExtension);
+    } */
+
+    public static void writeHierarchy()
+    {
+        ClassInfo[] classes = Converter.rootClasses();
+        ArrayList<ClassInfo> info = new ArrayList<ClassInfo>();
+        for (ClassInfo cl: classes) {
+            if (!cl.isHidden()) {
+                info.add(cl);
+            }
+        }
+        HDF data = makePackageHDF();
+        Hierarchy.makeHierarchy(data, info.toArray(new ClassInfo[info.size()]));
+        setPageTitle(data, "Class Hierarchy");
+        ClearPage.write(data, "hierarchy.cs", javadocDir + "hierarchy" + htmlExtension);
+    }
+
+    public static void writeClasses()
+    {
+        ClassInfo[] classes = Converter.rootClasses();
+
+        for (ClassInfo cl: classes) {
+            HDF data = makePackageHDF();
+            if (!cl.isHidden()) {
+                writeClass(cl, data);
+            }
+        }
+    }
+
+    public static void writeClass(ClassInfo cl, HDF data)
+    {
+        cl.makeHDF(data);
+
+        setPageTitle(data, cl.name());
+        ClearPage.write(data, "class.cs", cl.htmlPage());
+
+        Proofread.writeClass(cl.htmlPage(), cl);
+    }
+
+    public static void makeClassListHDF(HDF data, String base,
+            ClassInfo[] classes)
+    {
+        for (int i=0; i<classes.length; i++) {
+            ClassInfo cl = classes[i];
+            if (!cl.isHidden()) {
+                cl.makeShortDescrHDF(data, base + "." + i);
+            }
+        }
+    }
+
+    public static String linkTarget(String source, String target)
+    {
+        String[] src = source.split("/");
+        String[] tgt = target.split("/");
+
+        int srclen = src.length;
+        int tgtlen = tgt.length;
+
+        int same = 0;
+        while (same < (srclen-1)
+                && same < (tgtlen-1)
+                && (src[same].equals(tgt[same]))) {
+            same++;
+        }
+
+        String s = "";
+
+        int up = srclen-same-1;
+        for (int i=0; i<up; i++) {
+            s += "../";
+        }
+
+
+        int N = tgtlen-1;
+        for (int i=same; i<N; i++) {
+            s += tgt[i] + '/';
+        }
+        s += tgt[tgtlen-1];
+
+        return s;
+    }
+
+    /**
+     * Returns true if the given element has an @hide annotation.
+     */
+    private static boolean hasHideAnnotation(Doc doc) {
+        return doc.getRawCommentText().indexOf("@hide") != -1;
+    }
+
+    /**
+     * Returns true if the given element is hidden.
+     */
+    private static boolean isHidden(Doc doc) {
+        // Methods, fields, constructors.
+        if (doc instanceof MemberDoc) {
+            return hasHideAnnotation(doc);
+        }
+
+        // Classes, interfaces, enums, annotation types.
+        if (doc instanceof ClassDoc) {
+            ClassDoc classDoc = (ClassDoc) doc;
+
+            // Check the containing package.
+            if (hasHideAnnotation(classDoc.containingPackage())) {
+                return true;
+            }
+
+            // Check the class doc and containing class docs if this is a
+            // nested class.
+            ClassDoc current = classDoc;
+            do {
+                if (hasHideAnnotation(current)) {
+                    return true;
+                }
+
+                current = current.containingClass();
+            } while (current != null);
+        }
+
+        return false;
+    }
+
+    /**
+     * Filters out hidden elements.
+     */
+    private static Object filterHidden(Object o, Class<?> expected) {
+        if (o == null) {
+            return null;
+        }
+
+        Class type = o.getClass();
+        if (type.getName().startsWith("com.sun.")) {
+            // TODO: Implement interfaces from superclasses, too.
+            return Proxy.newProxyInstance(type.getClassLoader(),
+                    type.getInterfaces(), new HideHandler(o));
+        } else if (o instanceof Object[]) {
+            Class<?> componentType = expected.getComponentType();
+            Object[] array = (Object[]) o;
+            List<Object> list = new ArrayList<Object>(array.length);
+            for (Object entry : array) {
+                if ((entry instanceof Doc) && isHidden((Doc) entry)) {
+                    continue;
+                }
+                list.add(filterHidden(entry, componentType));
+            }
+            return list.toArray(
+                    (Object[]) Array.newInstance(componentType, list.size()));
+        } else {
+            return o;
+        }
+    }
+
+    /**
+     * Filters hidden elements out of method return values.
+     */
+    private static class HideHandler implements InvocationHandler {
+
+        private final Object target;
+
+        public HideHandler(Object target) {
+            this.target = target;
+        }
+
+        public Object invoke(Object proxy, Method method, Object[] args)
+                throws Throwable {
+            String methodName = method.getName();
+            if (args != null) {
+                if (methodName.equals("compareTo") ||
+                    methodName.equals("equals") ||
+                    methodName.equals("overrides") ||
+                    methodName.equals("subclassOf")) {
+                    args[0] = unwrap(args[0]);
+                }
+            }
+
+            if (methodName.equals("getRawCommentText")) {
+                return filterComment((String) method.invoke(target, args));
+            }
+            
+            // escape "&" in disjunctive types.
+            if (proxy instanceof Type && methodName.equals("toString")) {
+                return ((String) method.invoke(target, args))
+                        .replace("&", "&amp;");
+            }
+
+            try {
+                return filterHidden(method.invoke(target, args),
+                        method.getReturnType());
+            } catch (InvocationTargetException e) {
+                throw e.getTargetException();
+            }
+        }
+
+        private String filterComment(String s) {
+            if (s == null) {
+                return null;
+            }
+
+            s = s.trim();
+
+            // Work around off by one error
+            while (s.length() >= 5
+                    && s.charAt(s.length() - 5) == '{') {
+                s += "&nbsp;";
+            }
+
+            return s;
+        }
+
+        private static Object unwrap(Object proxy) {
+            if (proxy instanceof Proxy)
+                return ((HideHandler)Proxy.getInvocationHandler(proxy)).target;
+            return proxy;
+        }
+    }
+
+    public static String scope(Scoped scoped) {
+        if (scoped.isPublic()) {
+            return "public";
+        }
+        else if (scoped.isProtected()) {
+            return "protected";
+        }
+        else if (scoped.isPackagePrivate()) {
+            return "";
+        }
+        else if (scoped.isPrivate()) {
+            return "private";
+        }
+        else {
+            throw new RuntimeException("invalid scope for object " + scoped);
+        }
+    }
+    
+    /**
+     * Collect the values used by the Dev tools and write them in files packaged with the SDK
+     * @param output the ouput directory for the files.
+     */
+    private static void writeSdkValues(String output) {
+        ArrayList<String> activityActions = new ArrayList<String>();
+        ArrayList<String> broadcastActions = new ArrayList<String>();
+        ArrayList<String> serviceActions = new ArrayList<String>();
+        ArrayList<String> categories = new ArrayList<String>();
+        
+        ArrayList<ClassInfo> layouts = new ArrayList<ClassInfo>();
+        ArrayList<ClassInfo> widgets = new ArrayList<ClassInfo>();
+        ArrayList<ClassInfo> layoutParams = new ArrayList<ClassInfo>();
+        
+        ClassInfo[] classes = Converter.allClasses();
+
+        // Go through all the fields of all the classes, looking SDK stuff.
+        for (ClassInfo clazz : classes) {
+            
+            // first check constant fields for the SdkConstant annotation.
+            FieldInfo[] fields = clazz.allSelfFields();
+            for (FieldInfo field : fields) {
+                Object cValue = field.constantValue();
+                if (cValue != null) {
+                    AnnotationInstanceInfo[] annotations = field.annotations();
+                    if (annotations.length > 0) {
+                        for (AnnotationInstanceInfo annotation : annotations) {
+                            if (SDK_CONSTANT_ANNOTATION.equals(annotation.type().qualifiedName())) {
+                                AnnotationValueInfo[] values = annotation.elementValues();
+                                if (values.length > 0) {
+                                    String type = values[0].valueString();
+                                    if (SDK_CONSTANT_TYPE_ACTIVITY_ACTION.equals(type)) {
+                                        activityActions.add(cValue.toString());
+                                    } else if (SDK_CONSTANT_TYPE_BROADCAST_ACTION.equals(type)) {
+                                        broadcastActions.add(cValue.toString());
+                                    } else if (SDK_CONSTANT_TYPE_SERVICE_ACTION.equals(type)) {
+                                        serviceActions.add(cValue.toString());
+                                    } else if (SDK_CONSTANT_TYPE_CATEGORY.equals(type)) {
+                                        categories.add(cValue.toString());
+                                    }
+                                }
+                                break;
+                            }
+                        }
+                    }
+                }
+            }
+            
+            // Now check the class for @Widget or if its in the android.widget package
+            // (unless the class is hidden or abstract, or non public)
+            if (clazz.isHidden() == false && clazz.isPublic() && clazz.isAbstract() == false) {
+                boolean annotated = false;
+                AnnotationInstanceInfo[] annotations = clazz.annotations();
+                if (annotations.length > 0) {
+                    for (AnnotationInstanceInfo annotation : annotations) {
+                        if (SDK_WIDGET_ANNOTATION.equals(annotation.type().qualifiedName())) {
+                            widgets.add(clazz);
+                            annotated = true;
+                            break;
+                        } else if (SDK_LAYOUT_ANNOTATION.equals(annotation.type().qualifiedName())) {
+                            layouts.add(clazz);
+                            annotated = true;
+                            break;
+                        }
+                    }
+                }
+                
+                if (annotated == false) {
+                    // lets check if this is inside android.widget
+                    PackageInfo pckg = clazz.containingPackage();
+                    String packageName = pckg.name();
+                    if ("android.widget".equals(packageName) ||
+                            "android.view".equals(packageName)) {
+                        // now we check what this class inherits either from android.view.ViewGroup
+                        // or android.view.View, or android.view.ViewGroup.LayoutParams
+                        int type = checkInheritance(clazz);
+                        switch (type) {
+                            case TYPE_WIDGET:
+                                widgets.add(clazz);
+                                break;
+                            case TYPE_LAYOUT:
+                                layouts.add(clazz);
+                                break;
+                            case TYPE_LAYOUT_PARAM:
+                                layoutParams.add(clazz);
+                                break;
+                        }
+                    }
+                }
+            }
+        }
+
+        // now write the files, whether or not the list are empty.
+        // the SDK built requires those files to be present.
+
+        Collections.sort(activityActions);
+        writeValues(output + "/activity_actions.txt", activityActions);
+
+        Collections.sort(broadcastActions);
+        writeValues(output + "/broadcast_actions.txt", broadcastActions);
+
+        Collections.sort(serviceActions);
+        writeValues(output + "/service_actions.txt", serviceActions);
+
+        Collections.sort(categories);
+        writeValues(output + "/categories.txt", categories);
+        
+        // before writing the list of classes, we do some checks, to make sure the layout params
+        // are enclosed by a layout class (and not one that has been declared as a widget)
+        for (int i = 0 ; i < layoutParams.size();) {
+            ClassInfo layoutParamClass = layoutParams.get(i);
+            ClassInfo containingClass = layoutParamClass.containingClass();
+            if (containingClass == null || layouts.indexOf(containingClass) == -1) {
+                layoutParams.remove(i);
+            } else {
+                i++;
+            }
+        }
+        
+        writeClasses(output + "/widgets.txt", widgets, layouts, layoutParams);
+    }
+    
+    /**
+     * Writes a list of values into a text files.
+     * @param pathname the absolute os path of the output file.
+     * @param values the list of values to write.
+     */
+    private static void writeValues(String pathname, ArrayList<String> values) {
+        FileWriter fw = null;
+        BufferedWriter bw = null;
+        try {
+            fw = new FileWriter(pathname, false);
+            bw = new BufferedWriter(fw);
+            
+            for (String value : values) {
+                bw.append(value).append('\n');
+            }
+        } catch (IOException e) {
+            // pass for now
+        } finally {
+            try {
+                if (bw != null) bw.close();
+            } catch (IOException e) {
+                // pass for now
+            }
+            try {
+                if (fw != null) fw.close();
+            } catch (IOException e) {
+                // pass for now
+            }
+        }
+    }
+
+    /**
+     * Writes the widget/layout/layout param classes into a text files.
+     * @param pathname the absolute os path of the output file.
+     * @param widgets the list of widget classes to write.
+     * @param layouts the list of layout classes to write.
+     * @param layoutParams the list of layout param classes to write.
+     */
+    private static void writeClasses(String pathname, ArrayList<ClassInfo> widgets,
+            ArrayList<ClassInfo> layouts, ArrayList<ClassInfo> layoutParams) {
+        FileWriter fw = null;
+        BufferedWriter bw = null;
+        try {
+            fw = new FileWriter(pathname, false);
+            bw = new BufferedWriter(fw);
+            
+            // write the 3 types of classes.
+            for (ClassInfo clazz : widgets) {
+                writeClass(bw, clazz, 'W');
+            }
+            for (ClassInfo clazz : layoutParams) {
+                writeClass(bw, clazz, 'P');
+            }
+            for (ClassInfo clazz : layouts) {
+                writeClass(bw, clazz, 'L');
+            }
+        } catch (IOException e) {
+            // pass for now
+        } finally {
+            try {
+                if (bw != null) bw.close();
+            } catch (IOException e) {
+                // pass for now
+            }
+            try {
+                if (fw != null) fw.close();
+            } catch (IOException e) {
+                // pass for now
+            }
+        }
+    }
+
+    /**
+     * Writes a class name and its super class names into a {@link BufferedWriter}.
+     * @param writer the BufferedWriter to write into
+     * @param clazz the class to write
+     * @param prefix the prefix to put at the beginning of the line.
+     * @throws IOException
+     */
+    private static void writeClass(BufferedWriter writer, ClassInfo clazz, char prefix)
+            throws IOException {
+        writer.append(prefix).append(clazz.qualifiedName());
+        ClassInfo superClass = clazz;
+        while ((superClass = superClass.superclass()) != null) {
+            writer.append(' ').append(superClass.qualifiedName());
+        }
+        writer.append('\n');
+    }
+    
+    /**
+     * Checks the inheritance of {@link ClassInfo} objects. This method return
+     * <ul>
+     * <li>{@link #TYPE_LAYOUT}: if the class extends <code>android.view.ViewGroup</code></li>
+     * <li>{@link #TYPE_WIDGET}: if the class extends <code>android.view.View</code></li>
+     * <li>{@link #TYPE_LAYOUT_PARAM}: if the class extends <code>android.view.ViewGroup$LayoutParams</code></li>
+     * <li>{@link #TYPE_NONE}: in all other cases</li>
+     * </ul> 
+     * @param clazz the {@link ClassInfo} to check.
+     */
+    private static int checkInheritance(ClassInfo clazz) {
+        if ("android.view.ViewGroup".equals(clazz.qualifiedName())) {
+            return TYPE_LAYOUT;
+        } else if ("android.view.View".equals(clazz.qualifiedName())) {
+            return TYPE_WIDGET;
+        } else if ("android.view.ViewGroup.LayoutParams".equals(clazz.qualifiedName())) {
+            return TYPE_LAYOUT_PARAM;
+        }
+        
+        ClassInfo parent = clazz.superclass();
+        if (parent != null) {
+            return checkInheritance(parent);
+        }
+        
+        return TYPE_NONE;
+    }
+}
diff --git a/tools/droiddoc/src/Errors.java b/tools/droiddoc/src/Errors.java
new file mode 100644
index 0000000..dfeac88
--- /dev/null
+++ b/tools/droiddoc/src/Errors.java
@@ -0,0 +1,143 @@
+/*
+ * 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 java.util.SortedSet;
+import java.util.TreeSet;
+
+public class Errors
+{
+    public static boolean hadError = false;
+    private static boolean warningsAreErrors = false;
+    private static TreeSet<Message> allErrors = new TreeSet<Message>();
+
+    private static class Message implements Comparable {
+        SourcePositionInfo pos;
+        int level;
+        String msg;
+
+        Message(SourcePositionInfo p, int l, String m) {
+            pos = p;
+            level = l;
+            msg = m;
+        }
+
+        public int compareTo(Object o) {
+            Message that = (Message)o;
+            int r = this.pos.compareTo(that.pos);
+            if (r != 0) return r;
+            return this.msg.compareTo(that.msg);
+        }
+
+        public String toString() {
+            String whereText = this.pos == null ? "unknown: " : this.pos.toString() + ':';
+            return whereText + this.msg;
+        }
+    }
+
+    public static void error(Error error, SourcePositionInfo where, String text) {
+        if (error.level == HIDDEN) {
+            return;
+        }
+
+        int level = (!warningsAreErrors && error.level == WARNING) ? WARNING : ERROR;
+        String which = level == WARNING ? " warning " : " error ";
+        String message = which + error.code + ": " + text;
+
+        if (where == null) {
+            where = new SourcePositionInfo("unknown", 0, 0);
+        }
+
+        allErrors.add(new Message(where, level, message));
+
+        if (error.level == ERROR || (warningsAreErrors && error.level == WARNING)) {
+            hadError = true;
+        }
+    }
+
+    public static void printErrors() {
+        for (Message m: allErrors) {
+            if (m.level == WARNING) {
+                System.err.println(m.toString());
+            }
+        }
+        for (Message m: allErrors) {
+            if (m.level == ERROR) {
+                System.err.println(m.toString());
+            }
+        }
+    }
+
+    public static int HIDDEN = 0;
+    public static int WARNING = 1;
+    public static int ERROR = 2;
+
+    public static void setWarningsAreErrors(boolean val) {
+        warningsAreErrors = val;
+    }
+
+    public static class Error {
+        public int code;
+        public int level;
+
+        public Error(int code, int level)
+        {
+            this.code = code;
+            this.level = level;
+        }
+    }
+
+    public static Error UNRESOLVED_LINK = new Error(1, WARNING);
+    public static Error BAD_INCLUDE_TAG = new Error(2, WARNING);
+    public static Error UNKNOWN_TAG = new Error(3, WARNING);
+    public static Error UNKNOWN_PARAM_TAG_NAME = new Error(4, WARNING);
+    public static Error UNDOCUMENTED_PARAMETER = new Error(5, HIDDEN);
+    public static Error BAD_ATTR_TAG = new Error(6, ERROR);
+    public static Error BAD_INHERITDOC = new Error(7, HIDDEN);
+    public static Error HIDDEN_LINK = new Error(8, WARNING);
+    public static Error HIDDEN_CONSTRUCTOR = new Error(9, WARNING);
+    public static Error UNAVAILABLE_SYMBOL = new Error(10, ERROR);
+    public static Error HIDDEN_SUPERCLASS = new Error(11, WARNING);
+    public static Error DEPRECATED = new Error(12, HIDDEN);
+    public static Error DEPRECATION_MISMATCH = new Error(13, WARNING);
+    public static Error MISSING_COMMENT = new Error(14, WARNING);
+    public static Error IO_ERROR = new Error(15, HIDDEN);
+
+    public static Error[] ERRORS = {
+            UNRESOLVED_LINK,
+            BAD_INCLUDE_TAG,
+            UNKNOWN_TAG,
+            UNKNOWN_PARAM_TAG_NAME,
+            UNDOCUMENTED_PARAMETER,
+            BAD_ATTR_TAG,
+            BAD_INHERITDOC,
+            HIDDEN_LINK,
+            HIDDEN_CONSTRUCTOR,
+            UNAVAILABLE_SYMBOL,
+            HIDDEN_SUPERCLASS,
+            DEPRECATED,
+            IO_ERROR,
+        };
+
+    public static boolean setErrorLevel(int code, int level) {
+        for (Error e: ERRORS) {
+            if (e.code == code) {
+                e.level = level;
+                return true;
+            }
+        }
+        return false;
+    }
+}
diff --git a/tools/droiddoc/src/FieldInfo.java b/tools/droiddoc/src/FieldInfo.java
new file mode 100644
index 0000000..536d798
--- /dev/null
+++ b/tools/droiddoc/src/FieldInfo.java
@@ -0,0 +1,315 @@
+/*
+ * 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 org.clearsilver.CS;
+
+import java.util.Comparator;
+
+public class FieldInfo extends MemberInfo
+{
+    public static final Comparator<FieldInfo> comparator = new Comparator<FieldInfo>() {
+        public int compare(FieldInfo a, FieldInfo b) {
+            return a.name().compareTo(b.name());
+        }
+    };
+    
+    public FieldInfo(String name, ClassInfo containingClass, ClassInfo realContainingClass,
+                        boolean isPublic, boolean isProtected,
+                        boolean isPackagePrivate, boolean isPrivate,
+                        boolean isFinal, boolean isStatic, boolean isTransient, boolean isVolatile,
+                        boolean isSynthetic, TypeInfo type, String rawCommentText,
+                        Object constantValue,
+                        SourcePositionInfo position,
+                        AnnotationInstanceInfo[] annotations)
+    {
+        super(rawCommentText, name, null, containingClass, realContainingClass,
+                isPublic, isProtected, isPackagePrivate, isPrivate,
+                isFinal, isStatic, isSynthetic, chooseKind(isFinal, isStatic), position,
+                annotations);
+        mIsTransient = isTransient;
+        mIsVolatile = isVolatile;
+        mType = type;
+        mConstantValue = constantValue;
+    }
+
+    public FieldInfo cloneForClass(ClassInfo newContainingClass) {
+        return new FieldInfo(name(), newContainingClass, realContainingClass(),
+                isPublic(), isProtected(), isPackagePrivate(),
+                isPrivate(), isFinal(), isStatic(), isTransient(), isVolatile(),
+                isSynthetic(), mType, getRawCommentText(), mConstantValue, position(),
+                annotations());
+    }
+
+    static String chooseKind(boolean isFinal, boolean isStatic)
+    {
+        if (isStatic && isFinal) {
+            return "constant";
+        } else {
+            return "field";
+        }
+    }
+
+    public TypeInfo type()
+    {
+        return mType;
+    }
+
+    public boolean isConstant()
+    {
+        return isStatic() && isFinal();
+    }
+
+    public TagInfo[] firstSentenceTags()
+    {
+        return comment().briefTags();
+    }
+
+    public TagInfo[] inlineTags()
+    {
+        return comment().tags();
+    }
+
+    public Object constantValue()
+    {
+        return mConstantValue;
+    }
+
+    public String constantLiteralValue()
+    {
+        return constantLiteralValue(mConstantValue);
+    }
+    
+    public boolean isDeprecated() {
+        boolean deprecated = false;
+        if (!mDeprecatedKnown) {
+            boolean commentDeprecated = (comment().deprecatedTags().length > 0);
+            boolean annotationDeprecated = false;
+            for (AnnotationInstanceInfo annotation : annotations()) {
+                if (annotation.type().qualifiedName().equals("java.lang.Deprecated")) {
+                    annotationDeprecated = true;
+                    break;
+                }
+            }
+
+            if (commentDeprecated != annotationDeprecated) {
+                Errors.error(Errors.DEPRECATION_MISMATCH, position(),
+                        "Field " + mContainingClass.qualifiedName() + "." + name()
+                        + ": @Deprecated annotation and @deprecated comment do not match");
+            }
+
+            mIsDeprecated = commentDeprecated | annotationDeprecated;
+            mDeprecatedKnown = true;
+        }
+        return mIsDeprecated;
+    }
+
+    public static String constantLiteralValue(Object val)
+    {
+        String str = null;
+        if (val != null) {
+            if (val instanceof Boolean
+                    || val instanceof Byte
+                    || val instanceof Short
+                    || val instanceof Integer) 
+            {
+                str = val.toString();
+            }
+            //catch all special values
+            else if (val instanceof Double){
+                Double dbl = (Double) val;
+                    if (dbl.toString().equals("Infinity")){
+                        str = "(1.0 / 0.0)";
+                    } else if (dbl.toString().equals("-Infinity")) {
+                        str = "(-1.0 / 0.0)";
+                    } else if (dbl.isNaN()) {
+                        str = "(0.0 / 0.0)";
+                    } else {
+                        str = dbl.toString();
+                    }
+            }
+            else if (val instanceof Long) {
+                str = val.toString() + "L";
+            }
+            else if (val instanceof Float) {
+                Float fl = (Float) val;
+                if (fl.toString().equals("Infinity")) {
+                    str = "(1.0f / 0.0f)";
+                } else if (fl.toString().equals("-Infinity")) {
+                    str = "(-1.0f / 0.0f)";
+                } else if (fl.isNaN()) {
+                    str = "(0.0f / 0.0f)";
+                } else {
+                    str = val.toString() + "f";
+                }
+            }
+            else if (val instanceof Character) {
+                str = String.format("\'\\u%04x\'", val);
+            }
+            else if (val instanceof String) {
+                str = "\"" + javaEscapeString((String)val) + "\"";
+            }
+            else {
+                str = "<<<<" +val.toString() + ">>>>";
+            }
+        }
+        if (str == null) {
+            str = "null";
+        }
+        return str;
+    }
+
+    public static String javaEscapeString(String str) {
+        String result = "";
+        final int N = str.length();
+        for (int i=0; i<N; i++) {
+            char c = str.charAt(i);
+            if (c == '\\') {
+                result += "\\\\";
+            }
+            else if (c == '\t') {
+                result += "\\t";
+            }
+            else if (c == '\b') {
+                result += "\\b";
+            }
+            else if (c == '\r') {
+                result += "\\r";
+            }
+            else if (c == '\n') {
+                result += "\\n";
+            }
+            else if (c == '\f') {
+                result += "\\f";
+            }
+            else if (c == '\'') {
+                result += "\\'";
+            }
+            else if (c == '\"') {
+                result += "\\\"";
+            }
+            else if (c >= ' ' && c <= '~') {
+                result += c;
+            }
+            else {
+                result += String.format("\\u%04x", new Integer((int)c));
+            }
+        }
+        return result;
+    }
+
+
+    public void makeHDF(HDF data, String base)
+    {
+        data.setValue(base + ".kind", kind());
+        type().makeHDF(data, base + ".type");
+        data.setValue(base + ".name", name());
+        data.setValue(base + ".href", htmlPage());
+        data.setValue(base + ".anchor", anchor());
+        TagInfo.makeHDF(data, base + ".shortDescr", firstSentenceTags());
+        TagInfo.makeHDF(data, base + ".descr", inlineTags());
+        TagInfo.makeHDF(data, base + ".deprecated", comment().deprecatedTags());
+        TagInfo.makeHDF(data, base + ".seeAlso", comment().seeTags());
+        data.setValue(base + ".final", isFinal() ? "final" : "");
+        data.setValue(base + ".static", isStatic() ? "static" : "");
+        if (isPublic()) {
+            data.setValue(base + ".scope", "public");
+        }
+        else if (isProtected()) {
+            data.setValue(base + ".scope", "protected");
+        }
+        else if (isPackagePrivate()) {
+            data.setValue(base + ".scope", "");
+        }
+        else if (isPrivate()) {
+            data.setValue(base + ".scope", "private");
+        }
+        Object val = mConstantValue;
+        if (val != null) {
+            String dec = null;
+            String hex = null;
+            String str = null;
+
+            if (val instanceof Boolean) {
+                str = ((Boolean)val).toString();
+            }
+            else if (val instanceof Byte) {
+                dec = String.format("%d", val);
+                hex = String.format("0x%02x", val);
+            }
+            else if (val instanceof Character) {
+                dec = String.format("\'%c\'", val);
+                hex = String.format("0x%04x", val);
+            }
+            else if (val instanceof Double) {
+                str = ((Double)val).toString();
+            }
+            else if (val instanceof Float) {
+                str = ((Float)val).toString();
+            }
+            else if (val instanceof Integer) {
+                dec = String.format("%d", val);
+                hex = String.format("0x%08x", val);
+            }
+            else if (val instanceof Long) {
+                dec = String.format("%d", val);
+                hex = String.format("0x%016x", val);
+            }
+            else if (val instanceof Short) {
+                dec = String.format("%d", val);
+                hex = String.format("0x%04x", val);
+            }
+            else if (val instanceof String) {
+                str = "\"" + ((String)val) + "\"";
+            }
+            else {
+                str = "";
+            }
+
+            if (dec != null && hex != null) {
+                data.setValue(base + ".constantValue.dec", DroidDoc.escape(dec));
+                data.setValue(base + ".constantValue.hex", DroidDoc.escape(hex));
+            }
+            else {
+                data.setValue(base + ".constantValue.str", DroidDoc.escape(str));
+                data.setValue(base + ".constantValue.isString", "1");
+            }
+        }
+    }
+
+    public boolean isExecutable()
+    {
+        return false;
+    }
+
+    public boolean isTransient()
+    {
+        return mIsTransient;
+    }
+
+    public boolean isVolatile()
+    {
+        return mIsVolatile;
+    }
+
+    boolean mIsTransient;
+    boolean mIsVolatile;
+    boolean mDeprecatedKnown;
+    boolean mIsDeprecated;
+    TypeInfo mType;
+    Object mConstantValue;
+}
+
diff --git a/tools/droiddoc/src/Hierarchy.java b/tools/droiddoc/src/Hierarchy.java
new file mode 100755
index 0000000..ac5e1dc
--- /dev/null
+++ b/tools/droiddoc/src/Hierarchy.java
@@ -0,0 +1,155 @@
+/*
+ * 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 java.util.HashMap;
+import java.util.TreeSet;
+import java.util.Set;
+import org.clearsilver.HDF;
+
+public class Hierarchy
+{
+    public static void makeHierarchy(HDF hdf, ClassInfo[] classes)
+    {
+        HashMap<String,TreeSet<String>> nodes
+                                    = new HashMap<String,TreeSet<String>>();
+
+        for (ClassInfo cl: classes) {
+            String name = cl.qualifiedName();
+
+            TreeSet<String> me = nodes.get(name);
+            if (me == null) {
+                me = new TreeSet<String>();
+                nodes.put(name, me);
+            }
+
+            ClassInfo superclass = cl.superclass();
+            String sname = superclass != null
+                                    ? superclass.qualifiedName() : null;
+            if (sname != null) {
+                TreeSet<String> s = nodes.get(sname);
+                if (s == null) {
+                    s = new TreeSet<String>();
+                    nodes.put(sname, s);
+                }
+                s.add(name);
+            }
+        }
+
+        /*
+        Set<String> keys = nodes.keySet();
+        for (String n: keys) {
+            System.out.println("class: " + n);
+
+            TreeSet<String> values = nodes.get(n);
+            for (String v: values) {
+                System.out.println("       - " + v);
+            }
+        }
+        */
+
+        int depth = depth(nodes, "java.lang.Object");
+
+        hdf.setValue("classes.0", "");
+        hdf.setValue("colspan", "" + depth);
+
+        recurse(nodes, "java.lang.Object", hdf.getObj("classes.0"),depth,depth);
+
+        if (false) {
+            Set<String> keys = nodes.keySet();
+            if (keys.size() > 0) {
+                System.err.println("The following classes are hidden but"
+                        + " are superclasses of not-hidden classes");
+                for (String n: keys) {
+                    System.err.println("  " + n);
+                }
+            }
+        }
+    }
+
+    private static int depth(HashMap<String,TreeSet<String>> nodes,
+                                String name)
+    {
+        int d = 0;
+        TreeSet<String> derived = nodes.get(name);
+        if (derived != null && derived.size() > 0) {
+            for (String s: derived) {
+                int n = depth(nodes, s);
+                if (n > d) {
+                    d = n;
+                }
+            }
+        }
+        return d + 1;
+    }
+
+    private static boolean exists(ClassInfo cl)
+    {
+        return cl != null && !cl.isHidden() && cl.isIncluded();
+    }
+
+    private static void recurse(HashMap<String,TreeSet<String>> nodes,
+                                String name, HDF hdf, 
+                                int totalDepth, int remainingDepth)
+    {
+        int i;
+
+        hdf.setValue("indent", "" + (totalDepth-remainingDepth-1));
+        hdf.setValue("colspan", "" + remainingDepth);
+
+        ClassInfo cl = Converter.obtainClass(name);
+
+        hdf.setValue("class.label", cl.name());
+        hdf.setValue("class.qualified", cl.qualifiedName());
+        if (cl.checkLevel()) {
+            hdf.setValue("class.link", cl.htmlPage());
+        }
+
+        if (exists(cl)) {
+            hdf.setValue("exists", "1");
+        }
+
+        i = 0;
+        for (ClassInfo iface: cl.interfaces()) {
+            hdf.setValue("interfaces." + i + ".class.label", iface.name());
+            hdf.setValue("interfaces." + i + ".class.qualified", iface.qualifiedName());
+            if (iface.checkLevel()) {
+                hdf.setValue("interfaces." + i + ".class.link", iface.htmlPage());
+            }
+            if (exists(cl)) {
+                hdf.setValue("interfaces." + i + ".exists", "1");
+            }
+            i++;
+        }
+
+        TreeSet<String> derived = nodes.get(name);
+        if (derived != null && derived.size() > 0) {
+            hdf.setValue("derived", "");
+            HDF children = hdf.getObj("derived");
+            i = 0;
+            remainingDepth--;
+            for (String s: derived) {
+                String index = "" + i;
+                children.setValue(index, "");
+                recurse(nodes, s, children.getObj(index), totalDepth,
+                        remainingDepth);
+                i++;
+            }
+        }
+
+        nodes.remove(name);
+    }
+}
+
diff --git a/tools/droiddoc/src/InheritedTags.java b/tools/droiddoc/src/InheritedTags.java
new file mode 100644
index 0000000..242170c
--- /dev/null
+++ b/tools/droiddoc/src/InheritedTags.java
@@ -0,0 +1,27 @@
+/*
+ * 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 org.clearsilver.CS;
+import java.util.*;
+import java.io.*;
+
+public interface InheritedTags
+{
+    TagInfo[] tags();
+    InheritedTags inherited();
+}
+
diff --git a/tools/droiddoc/src/KeywordEntry.java b/tools/droiddoc/src/KeywordEntry.java
new file mode 100644
index 0000000..7e5e357
--- /dev/null
+++ b/tools/droiddoc/src/KeywordEntry.java
@@ -0,0 +1,51 @@
+/*
+ * 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 org.clearsilver.CS;
+
+class KeywordEntry implements Comparable
+{
+    KeywordEntry(String label, String href, String comment)
+    {
+        this.label = label;
+        this.href = href;
+        this.comment = comment;
+    }
+
+    public void makeHDF(HDF data, String base)
+    {
+        data.setValue(base + ".label", this.label);
+        data.setValue(base + ".href", this.href);
+        data.setValue(base + ".comment", this.comment);
+    }
+
+    public char firstChar()
+    {
+        return Character.toUpperCase(this.label.charAt(0));
+    }
+
+    public int compareTo(Object that)
+    {
+        return this.label.compareToIgnoreCase(((KeywordEntry)that).label);
+    }
+
+    private String label;
+    private String href;
+    private String comment;
+}
+
+
diff --git a/tools/droiddoc/src/LinkReference.java b/tools/droiddoc/src/LinkReference.java
new file mode 100644
index 0000000..bbcd4db
--- /dev/null
+++ b/tools/droiddoc/src/LinkReference.java
@@ -0,0 +1,440 @@
+/*
+ * 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 java.util.regex.Pattern;
+import java.util.regex.Matcher;
+import java.util.ArrayList;
+
+/**
+ * Class that represents what you see in an link or see tag.  This is
+ * factored out of SeeTagInfo so it can be used elsewhere (like AttrTagInfo).
+ */
+public class LinkReference {
+
+    /** The original text. */
+    public String text;
+    
+    /** The kind of this tag, if we have a new suggestion after parsing. */
+    public String kind;
+
+    /** The user visible text. */
+    public String label;
+
+    /** The link. */
+    public String href;
+
+    /** The {@link PackageInfo} if any. */
+    public PackageInfo packageInfo;
+
+    /** The {@link ClassInfo} if any. */
+    public ClassInfo classInfo;
+
+    /** The {@link MemberInfo} if any. */
+    public MemberInfo memberInfo;
+
+    /** The name of the referenced member PackageInfo} if any. */
+    public String referencedMemberName;
+
+    /** Set to true if everything is a-ok */
+    public boolean good;
+
+    /**
+     * regex pattern to use when matching explicit "<a href" reference text
+     */
+    private static final Pattern HREF_PATTERN
+            = Pattern.compile("^<a href=\"([^\"]*)\">([^<]*)</a>[ \n\r\t]*$",
+                              Pattern.CASE_INSENSITIVE);
+
+    /**
+     * Parse and resolve a link string.
+     *
+     * @param text the original text
+     * @param base the class or whatever that this link is on
+     * @param pos the original position in the source document
+     * @return a new link reference.  It always returns something.  If there was an
+     *         error, it logs it and fills in href and label with error text.
+     */
+    public static LinkReference parse(String text, ContainerInfo base, SourcePositionInfo pos,
+                                        boolean printOnErrors) {
+        LinkReference result = new LinkReference();
+        result.text = text;
+
+        int index;
+        int len = text.length();
+        int pairs = 0;
+        int pound = -1;
+        // split the string
+        done: {
+            for (index=0; index<len; index++) {
+                char c = text.charAt(index);
+                switch (c)
+                {
+                    case '(':
+                        pairs++;
+                        break;
+                    case '[':
+                        pairs++;
+                        break;
+                    case ')':
+                        pairs--;
+                        break;
+                    case ']':
+                        pairs--;
+                        break;
+                    case ' ':
+                    case '\t':
+                    case '\r':
+                    case '\n':
+                        if (pairs == 0) {
+                            break done;
+                        }
+                        break;
+                    case '#':
+                        if (pound < 0) {
+                            pound = index;
+                        }
+                        break;
+                }
+            }
+        }
+        if (index == len && pairs != 0) {
+            Errors.error(Errors.UNRESOLVED_LINK, pos,
+                        "unable to parse link/see tag: " + text.trim());
+            return result;
+        }
+
+        int linkend = index;
+
+        for (; index<len; index++) {
+            char c = text.charAt(index);
+            if (!(c == ' ' || c == '\t' || c == '\r' || c == '\n')) {
+                break;
+            }
+        }
+
+        result.label = text.substring(index);
+
+        String ref;
+        String mem;
+        if (pound == 0) {
+            ref = null;
+            mem = text.substring(1, linkend);
+        }
+        else if (pound > 0) {
+            ref = text.substring(0, pound);
+            mem = text.substring(pound+1, linkend);
+        }
+        else {
+            ref = text.substring(0, linkend);
+            mem = null;
+        }
+
+        // parse parameters, if any
+        String[] params = null;
+        String[] paramDimensions = null;
+        if (mem != null) {
+            index = mem.indexOf('(');
+            if (index > 0) {
+                ArrayList<String> paramList = new ArrayList<String>();
+                ArrayList<String> paramDimensionList = new ArrayList<String>();
+                len = mem.length();
+                int start = index+1;
+                final int START = 0;
+                final int TYPE = 1;
+                final int NAME = 2;
+                int dimension = 0;
+                int arraypair = 0;
+                int state = START;
+                int typestart = 0;
+                int typeend = -1;
+                for (int i=start; i<len; i++) {
+                    char c = mem.charAt(i);
+                    switch (state)
+                    {
+                        case START:
+                            if (c!=' ' && c!='\t' && c!='\r' && c!='\n') {
+                                state = TYPE;
+                                typestart = i;
+                            }
+                            break;
+                        case TYPE:
+                            if (c == '[') {
+                                if (typeend < 0) {
+                                    typeend = i;
+                                }
+                                dimension++;
+                                arraypair++;
+                            }
+                            else if (c == ']') {
+                                arraypair--;
+                            }
+                            else if (c==' ' || c=='\t' || c=='\r' || c=='\n') {
+                                if (typeend < 0) {
+                                    typeend = i;
+                                }
+                            }
+                            else {
+                                if (typeend >= 0 || c == ')' || c == ',') {
+                                    if (typeend < 0) {
+                                        typeend = i;
+                                    }
+                                    String s = mem.substring(typestart, typeend);
+                                    paramList.add(s);
+                                    s = "";
+                                    for (int j=0; j<dimension; j++) {
+                                        s += "[]";
+                                    }
+                                    paramDimensionList.add(s);
+                                    state = START;
+                                    typeend = -1;
+                                    dimension = 0;
+                                    if (c == ',' || c == ')') {
+                                        state = START;
+                                    } else {
+                                        state = NAME;
+                                    }
+                                }
+                            }
+                            break;
+                        case NAME:
+                            if (c == ',' || c == ')') {
+                                state = START;
+                            }
+                            break;
+                    }
+
+                }
+                params = paramList.toArray(new String[paramList.size()]);
+                paramDimensions = paramDimensionList.toArray(new String[paramList.size()]);
+                mem = mem.substring(0, index);
+            }
+        }
+
+        ClassInfo cl = null;
+        if (base instanceof ClassInfo) {
+            cl = (ClassInfo)base;
+        }
+
+        if (ref == null) {
+            // no class or package was provided, assume it's this class
+            if (cl != null) {
+                result.classInfo = cl;
+            }
+        } else {
+            // they provided something, maybe it's a class or a package
+            if (cl != null) {
+                result.classInfo = cl.extendedFindClass(ref);
+                if (result.classInfo == null) {
+                    result.classInfo = cl.findClass(ref);
+                }
+                if (result.classInfo == null) {
+                    result.classInfo = cl.findInnerClass(ref);
+                }
+            }
+            if (result.classInfo == null) {
+                result.classInfo = Converter.obtainClass(ref);
+            }
+            if (result.classInfo == null) {
+                result.packageInfo = Converter.obtainPackage(ref);
+            }
+        }
+
+        if (result.classInfo != null && mem != null) {
+            // it's either a field or a method, prefer a field
+            if (params == null) {
+                FieldInfo field = result.classInfo.findField(mem);
+                // findField looks in containing classes, so it might actually
+                // be somewhere else; link to where it really is, not what they
+                // typed.
+                if (field != null) {
+                    result.classInfo = field.containingClass();
+                    result.memberInfo = field;
+                }
+            }
+            if (result.memberInfo == null) {
+                MethodInfo method = result.classInfo.findMethod(mem, params, paramDimensions);
+                if (method != null) {
+                    result.classInfo = method.containingClass();
+                    result.memberInfo = method;
+                }
+            }
+        }
+
+        result.referencedMemberName = mem;
+        if (params != null) {
+            result.referencedMemberName = result.referencedMemberName + '(';
+            len = params.length;
+            if (len > 0) {
+                len--;
+                for (int i=0; i<len; i++) {
+                    result.referencedMemberName = result.referencedMemberName + params[i]
+                            + paramDimensions[i] + ", ";
+                }
+                result.referencedMemberName = result.referencedMemberName + params[len]
+                        + paramDimensions[len];
+            }
+            result.referencedMemberName = result.referencedMemberName + ")";
+        }
+
+        // debugging spew
+        if (false) {
+            result.label = result.label + "/" + ref + "/" + mem + '/';
+            if (params != null) {
+                for (int i=0; i<params.length; i++) {
+                    result.label += params[i] + "|";
+                }
+            }
+
+            FieldInfo f = (result.memberInfo instanceof FieldInfo)
+                        ? (FieldInfo)result.memberInfo
+                        : null;
+            MethodInfo m = (result.memberInfo instanceof MethodInfo)
+                        ? (MethodInfo)result.memberInfo
+                        : null;
+            result.label = result.label
+                        + "/package=" + (result.packageInfo!=null?result.packageInfo.name():"")
+                        + "/class=" + (result.classInfo!=null?result.classInfo.qualifiedName():"")
+                        + "/field=" + (f!=null?f.name():"")
+                        + "/method=" + (m!=null?m.name():"");
+            
+        }
+
+        MethodInfo method = null;
+        boolean skipHref = false;
+
+        if (result.memberInfo != null && result.memberInfo.isExecutable()) {
+           method = (MethodInfo)result.memberInfo;
+        }
+
+        if (text.startsWith("\"")) {
+            // literal quoted reference (e.g., a book title)
+            result.label = text.substring(1);
+            skipHref = true;
+            if (!result.label.endsWith("\"")) {
+                Errors.error(Errors.UNRESOLVED_LINK, pos,
+                        "unbalanced quoted link/see tag: " + text.trim());
+                result.makeError();
+                return result;
+            }
+            result.label = result.label.substring(0, result.label.length() - 1);
+            result.kind = "@seeJustLabel";
+        }
+        else if (text.startsWith("<")) {
+            // explicit "<a href" form
+            Matcher matcher = HREF_PATTERN.matcher(text);
+            if (! matcher.matches()) {
+                Errors.error(Errors.UNRESOLVED_LINK, pos,
+                        "invalid <a> link/see tag: " + text.trim());
+                result.makeError();
+                return result;
+            }
+            result.href = matcher.group(1);
+            result.label = matcher.group(2);
+            result.kind = "@seeHref";
+        }
+        else if (result.packageInfo != null) {
+            result.href = result.packageInfo.htmlPage();
+            if (result.label.length() == 0) {
+                result.href = result.packageInfo.htmlPage();
+                result.label = result.packageInfo.name();
+            }
+        }
+        else if (result.classInfo != null && result.referencedMemberName == null) {
+            // class reference
+            if (result.label.length() == 0) {
+                result.label = result.classInfo.name();
+            }
+            result.href = result.classInfo.htmlPage();
+        }
+        else if (result.memberInfo != null) {
+            // member reference
+            ClassInfo containing = result.memberInfo.containingClass();
+            if (result.memberInfo.isExecutable()) {
+                if (result.referencedMemberName.indexOf('(') < 0) {
+                    result.referencedMemberName += method.flatSignature();
+                }
+            } 
+            if (result.label.length() == 0) {
+                result.label = result.referencedMemberName;
+            }
+            result.href = containing.htmlPage() + '#' + result.memberInfo.anchor();
+        }
+
+        if (result.href == null && !skipHref) {
+            if (printOnErrors && (base == null || base.checkLevel())) {
+                Errors.error(Errors.UNRESOLVED_LINK, pos,
+                        "Unresolved link/see tag \"" + text.trim()
+                        + "\" in " + ((base != null) ? base.qualifiedName() : "[null]"));
+            }
+            result.makeError();
+        }
+        else if (result.memberInfo != null && !result.memberInfo.checkLevel()) {
+            if (printOnErrors && (base == null || base.checkLevel())) {
+                Errors.error(Errors.HIDDEN_LINK, pos,
+                        "Link to hidden member: " + text.trim());
+                result.href = null;
+            }
+            result.kind = "@seeJustLabel";
+        }
+        else if (result.classInfo != null && !result.classInfo.checkLevel()) {
+            if (printOnErrors && (base == null || base.checkLevel())) {
+                Errors.error(Errors.HIDDEN_LINK, pos,
+                        "Link to hidden class: " + text.trim() + " label=" + result.label);
+                result.href = null;
+            }
+            result.kind = "@seeJustLabel";
+        }
+        else if (result.packageInfo != null && !result.packageInfo.checkLevel()) {
+            if (printOnErrors && (base == null || base.checkLevel())) {
+                Errors.error(Errors.HIDDEN_LINK, pos,
+                        "Link to hidden package: " + text.trim());
+                result.href = null;
+            }
+            result.kind = "@seeJustLabel";
+        }
+
+        result.good = true;
+
+        return result;
+    }
+
+    public boolean checkLevel() {
+        if (memberInfo != null) {
+            return memberInfo.checkLevel();
+        }
+        if (classInfo != null) {
+            return classInfo.checkLevel();
+        }
+        if (packageInfo != null) {
+            return packageInfo.checkLevel();
+        }
+        return false;
+    }
+
+    /** turn this LinkReference into one with an error message */
+    private void makeError() {
+        //this.href = "ERROR(" + this.text.trim() + ")";
+        this.href = null;
+        if (this.label == null) {
+            this.label = "";
+        }
+        this.label = "ERROR(" + this.label + "/" + text.trim() + ")";
+    }
+
+    /** private. **/
+    private LinkReference() {
+    }
+}
diff --git a/tools/droiddoc/src/LiteralTagInfo.java b/tools/droiddoc/src/LiteralTagInfo.java
new file mode 100644
index 0000000..b39490d
--- /dev/null
+++ b/tools/droiddoc/src/LiteralTagInfo.java
@@ -0,0 +1,31 @@
+/*
+ * 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.
+ */
+
+public class LiteralTagInfo extends TagInfo
+{
+    private static String encode(String t)
+    {
+        t = t.replace("&", "&amp;");
+        t = t.replace("<", "&lt;");
+        t = t.replace(">", "&gt;");
+        return t;
+    }
+
+    public LiteralTagInfo(String n, String k, String t, SourcePositionInfo sp)
+    {
+        super("Text", "Text", encode(t), sp);
+    }
+}
diff --git a/tools/droiddoc/src/MemberInfo.java b/tools/droiddoc/src/MemberInfo.java
new file mode 100644
index 0000000..2a2572a
--- /dev/null
+++ b/tools/droiddoc/src/MemberInfo.java
@@ -0,0 +1,154 @@
+/*
+ * 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.
+ */
+
+public abstract class MemberInfo extends DocInfo implements Comparable, Scoped
+{
+    public MemberInfo(String rawCommentText, String name, String signature,
+                        ClassInfo containingClass, ClassInfo realContainingClass,
+                        boolean isPublic, boolean isProtected,
+                        boolean isPackagePrivate, boolean isPrivate,
+                        boolean isFinal, boolean isStatic, boolean isSynthetic,
+                        String kind,
+                        SourcePositionInfo position,
+                        AnnotationInstanceInfo[] annotations)
+    {
+        super(rawCommentText, position);
+        mName = name;
+        mSignature = signature;
+        mContainingClass = containingClass;
+        mRealContainingClass = realContainingClass;
+        mIsPublic = isPublic;
+        mIsProtected = isProtected;
+        mIsPackagePrivate = isPackagePrivate;
+        mIsPrivate = isPrivate;
+        mIsFinal = isFinal;
+        mIsStatic = isStatic;
+        mIsSynthetic = isSynthetic;
+        mKind = kind;
+        mAnnotations = annotations;
+    }
+
+    public abstract boolean isExecutable();
+
+    public String anchor()
+    {
+        if (mSignature != null) {
+            return mName + mSignature;
+        } else {
+            return mName;
+        }
+    }
+
+    public String htmlPage() {
+        return mContainingClass.htmlPage() + "#" + anchor();
+    }
+
+    public int compareTo(Object that) {
+        return this.htmlPage().compareTo(((MemberInfo)that).htmlPage());
+    }
+
+    public String name()
+    {
+        return mName;
+    }
+
+    public String signature()
+    {
+        return mSignature;
+    }
+
+    public ClassInfo realContainingClass()
+    {
+        return mRealContainingClass;
+    }
+
+    public ClassInfo containingClass()
+    {
+        return mContainingClass;
+    }
+
+    public boolean isPublic()
+    {
+        return mIsPublic;
+    }
+
+    public boolean isProtected()
+    {
+        return mIsProtected;
+    }
+
+    public boolean isPackagePrivate()
+    {
+        return mIsPackagePrivate;
+    }
+
+    public boolean isPrivate()
+    {
+        return mIsPrivate;
+    }
+
+    public boolean isStatic()
+    {
+        return mIsStatic;
+    }
+
+    public boolean isFinal()
+    {
+        return mIsFinal;
+    }
+
+    public boolean isSynthetic()
+    {
+        return mIsSynthetic;
+    }
+
+    public ContainerInfo parent()
+    {
+        return mContainingClass;
+    }
+
+    public boolean checkLevel()
+    {
+        return DroidDoc.checkLevel(mIsPublic, mIsProtected,
+                mIsPackagePrivate, mIsPrivate, isHidden());
+    }
+
+    public String kind()
+    {
+        return mKind;
+    }
+    
+    public AnnotationInstanceInfo[] annotations()
+    {
+        return mAnnotations;
+    }
+
+    ClassInfo mContainingClass;
+    ClassInfo mRealContainingClass;
+    String mName;
+    String mSignature;
+    boolean mIsPublic;
+    boolean mIsProtected;
+    boolean mIsPackagePrivate;
+    boolean mIsPrivate;
+    boolean mIsFinal;
+    boolean mIsStatic;
+    boolean mIsSynthetic;
+    String mKind;
+    private AnnotationInstanceInfo[] mAnnotations;
+
+}
+
diff --git a/tools/droiddoc/src/MethodInfo.java b/tools/droiddoc/src/MethodInfo.java
new file mode 100644
index 0000000..ca30665
--- /dev/null
+++ b/tools/droiddoc/src/MethodInfo.java
@@ -0,0 +1,645 @@
+/*
+ * 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 org.clearsilver.CS;
+import java.util.*;
+import java.io.*;
+
+public class MethodInfo extends MemberInfo
+{
+    public static final Comparator<MethodInfo> comparator = new Comparator<MethodInfo>() {
+        public int compare(MethodInfo a, MethodInfo b) {
+            return a.name().compareTo(b.name());
+        }
+    };
+    
+    private class InlineTags implements InheritedTags
+    { 
+        public TagInfo[] tags()
+        {
+            return comment().tags();
+        }
+        public InheritedTags inherited()
+        {
+            MethodInfo m = findOverriddenMethod(name(), signature());
+            if (m != null) {
+                return m.inlineTags();
+            } else {
+                return null;
+            }
+        }
+    }
+    
+    private static void addInterfaces(ClassInfo[] ifaces, ArrayList<ClassInfo> queue)
+    {
+        for (ClassInfo i: ifaces) {
+            queue.add(i);
+        }
+        for (ClassInfo i: ifaces) {
+            addInterfaces(i.interfaces(), queue);
+        }
+    }
+
+    // first looks for a superclass, and then does a breadth first search to
+    // find the least far away match
+    public MethodInfo findOverriddenMethod(String name, String signature)
+    {
+        if (mReturnType == null) {
+            // ctor
+            return null;
+        }
+        if (mOverriddenMethod != null) {
+            return mOverriddenMethod;
+        }
+
+        ArrayList<ClassInfo> queue = new ArrayList<ClassInfo>();
+        addInterfaces(containingClass().interfaces(), queue);
+        for (ClassInfo iface: queue) {
+            for (MethodInfo me: iface.methods()) {
+                if (me.name().equals(name)
+                        && me.signature().equals(signature)
+                        && me.inlineTags().tags() != null
+                        && me.inlineTags().tags().length > 0) {
+                    return me;
+                }
+            }
+        }
+        return null;
+    }
+    
+    private static void addRealInterfaces(ClassInfo[] ifaces, ArrayList<ClassInfo> queue)
+    {
+        for (ClassInfo i: ifaces) {
+            queue.add(i);
+            if (i.realSuperclass() != null &&  i.realSuperclass().isAbstract()) {
+                queue.add(i.superclass());
+            }
+        }
+        for (ClassInfo i: ifaces) {
+            addInterfaces(i.realInterfaces(), queue);
+        }
+    }
+    
+    public MethodInfo findRealOverriddenMethod(String name, String signature, HashSet notStrippable) {
+        if (mReturnType == null) {
+        // ctor
+        return null;
+        }
+        if (mOverriddenMethod != null) {
+            return mOverriddenMethod;
+        }
+
+        ArrayList<ClassInfo> queue = new ArrayList<ClassInfo>();
+        if (containingClass().realSuperclass() != null && 
+            containingClass().realSuperclass().isAbstract()) {
+            queue.add(containingClass());
+        }
+        addInterfaces(containingClass().realInterfaces(), queue);
+        for (ClassInfo iface: queue) {
+            for (MethodInfo me: iface.methods()) {
+                if (me.name().equals(name)
+                    && me.signature().equals(signature)
+                    && me.inlineTags().tags() != null
+                    && me.inlineTags().tags().length > 0
+                    && notStrippable.contains(me.containingClass())) {
+                return me;
+                }
+            }
+        }
+        return null;
+    }
+    
+    public MethodInfo findSuperclassImplementation(HashSet notStrippable) {
+        if (mReturnType == null) {
+            // ctor
+            return null;
+        }
+        if (mOverriddenMethod != null) {
+            // Even if we're told outright that this was the overridden method, we want to
+            // be conservative and ignore mismatches of parameter types -- they arise from
+            // extending generic specializations, and we want to consider the derived-class
+            // method to be a non-override.
+            if (this.signature().equals(mOverriddenMethod.signature())) {
+                return mOverriddenMethod;
+            }
+        }
+
+        ArrayList<ClassInfo> queue = new ArrayList<ClassInfo>();
+        if (containingClass().realSuperclass() != null && 
+                containingClass().realSuperclass().isAbstract()) {
+            queue.add(containingClass());
+        }
+        addInterfaces(containingClass().realInterfaces(), queue);
+        for (ClassInfo iface: queue) {
+            for (MethodInfo me: iface.methods()) {
+                if (me.name().equals(this.name())
+                        && me.signature().equals(this.signature())
+                        && notStrippable.contains(me.containingClass())) {
+                    return me;
+                }
+            }
+        }
+        return null;
+    }
+    
+    public ClassInfo findRealOverriddenClass(String name, String signature) {
+        if (mReturnType == null) {
+        // ctor
+        return null;
+        }
+        if (mOverriddenMethod != null) {
+            return mOverriddenMethod.mRealContainingClass;
+        }
+
+        ArrayList<ClassInfo> queue = new ArrayList<ClassInfo>();
+        if (containingClass().realSuperclass() != null && 
+            containingClass().realSuperclass().isAbstract()) {
+            queue.add(containingClass());
+        }
+        addInterfaces(containingClass().realInterfaces(), queue);
+        for (ClassInfo iface: queue) {
+            for (MethodInfo me: iface.methods()) {
+                if (me.name().equals(name)
+                    && me.signature().equals(signature)
+                    && me.inlineTags().tags() != null
+                    && me.inlineTags().tags().length > 0) {
+                return iface;
+                }
+            }
+        }
+        return null;
+    }
+
+    private class FirstSentenceTags implements InheritedTags
+    {
+        public TagInfo[] tags()
+        {
+            return comment().briefTags();
+        }
+        public InheritedTags inherited()
+        {
+            MethodInfo m = findOverriddenMethod(name(), signature());
+            if (m != null) {
+                return m.firstSentenceTags();
+            } else {
+                return null;
+            }
+        }
+    }
+    
+    private class ReturnTags implements InheritedTags {
+        public TagInfo[] tags() {
+            return comment().returnTags();
+        }
+        public InheritedTags inherited() {
+            MethodInfo m = findOverriddenMethod(name(), signature());
+            if (m != null) {
+                return m.returnTags();
+            } else {
+                return null;
+            }
+        }
+    }
+    
+    public boolean isDeprecated() {
+        boolean deprecated = false;
+        if (!mDeprecatedKnown) {
+            boolean commentDeprecated = (comment().deprecatedTags().length > 0);
+            boolean annotationDeprecated = false;
+            for (AnnotationInstanceInfo annotation : annotations()) {
+                if (annotation.type().qualifiedName().equals("java.lang.Deprecated")) {
+                    annotationDeprecated = true;
+                    break;
+                }
+            }
+
+            if (commentDeprecated != annotationDeprecated) {
+                Errors.error(Errors.DEPRECATION_MISMATCH, position(),
+                        "Method " + mContainingClass.qualifiedName() + "." + name()
+                        + ": @Deprecated annotation and @deprecated doc tag do not match");
+            }
+
+            mIsDeprecated = commentDeprecated | annotationDeprecated;
+            mDeprecatedKnown = true;
+        }
+        return mIsDeprecated;
+    }
+    
+    public TypeInfo[] getTypeParameters(){
+        return mTypeParameters;
+    }
+
+    public MethodInfo cloneForClass(ClassInfo newContainingClass) {
+        MethodInfo result =  new MethodInfo(getRawCommentText(), mTypeParameters,
+                name(), signature(), newContainingClass, realContainingClass(),
+                isPublic(), isProtected(), isPackagePrivate(), isPrivate(), isFinal(), isStatic(),
+                isSynthetic(), mIsAbstract, mIsSynchronized, mIsNative, mIsAnnotationElement,
+                kind(), mFlatSignature, mOverriddenMethod,
+                mReturnType, mParameters, mThrownExceptions, position(), annotations());
+        result.init(mDefaultAnnotationElementValue);
+        return result;
+    }
+
+    public MethodInfo(String rawCommentText, TypeInfo[] typeParameters, String name,
+                        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, ParameterInfo[] parameters,
+                        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,
+                ((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;
+        mFlatSignature = flatSignature;
+        mOverriddenMethod = overriddenMethod;
+        mReturnType = returnType;
+        mParameters = parameters;
+        mThrownExceptions = thrownExceptions;
+    }
+
+    public void init(AnnotationValueInfo defaultAnnotationElementValue)
+    {
+        mDefaultAnnotationElementValue = defaultAnnotationElementValue;
+    }
+
+    public boolean isAbstract()
+    {
+        return mIsAbstract;
+    }
+
+    public boolean isSynchronized()
+    {
+        return mIsSynchronized;
+    }
+
+    public boolean isNative()
+    {
+        return mIsNative;
+    }
+
+    public String flatSignature()
+    {
+        return mFlatSignature;
+    }
+
+    public InheritedTags inlineTags()
+    {
+        return new InlineTags();
+    }
+
+    public InheritedTags firstSentenceTags()
+    {
+        return new FirstSentenceTags();
+    }
+
+    public InheritedTags returnTags() {
+        return new ReturnTags();
+    }
+
+    public TypeInfo returnType()
+    {
+        return mReturnType;
+    }
+
+    public String prettySignature()
+    {
+        String s = "(";
+        int N = mParameters.length;
+        for (int i=0; i<N; i++) {
+            ParameterInfo p = mParameters[i];
+            TypeInfo t = p.type();
+            if (t.isPrimitive()) {
+                s += t.simpleTypeName();
+            } else {
+                s += t.asClassInfo().name();
+            }
+            if (i != N-1) {
+                s += ',';
+            }
+        }
+        s += ')';
+        return s;
+    }
+
+    private boolean inList(ClassInfo item, ThrowsTagInfo[] list)
+    {
+        int len = list.length;
+        String qn = item.qualifiedName();
+        for (int i=0; i<len; i++) {
+            ClassInfo ex = list[i].exception();
+            if (ex != null && ex.qualifiedName().equals(qn)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public ThrowsTagInfo[] throwsTags()
+    {
+        if (mThrowsTags == null) {
+            ThrowsTagInfo[] documented = comment().throwsTags();
+            ArrayList<ThrowsTagInfo> rv = new ArrayList<ThrowsTagInfo>();
+
+            int len = documented.length;
+            for (int i=0; i<len; i++) {
+                rv.add(documented[i]);
+            }
+
+            ClassInfo[] all = mThrownExceptions;
+            len = all.length;
+            for (int i=0; i<len; i++) {
+                ClassInfo cl = all[i];
+                if (documented == null || !inList(cl, documented)) {
+                    rv.add(new ThrowsTagInfo("@throws", "@throws",
+                                        cl.qualifiedName(), cl, "",
+                                        containingClass(), position()));
+                }
+            }
+            mThrowsTags = rv.toArray(new ThrowsTagInfo[rv.size()]);
+        }
+        return mThrowsTags;
+    }
+
+    private static int indexOfParam(String name, String[] list)
+    {
+        final int N = list.length;
+        for (int i=0; i<N; i++) {
+            if (name.equals(list[i])) {
+                return i;
+            }
+        }
+        return -1;
+    }
+
+    public ParamTagInfo[] paramTags()
+    {
+        if (mParamTags == null) {
+            final int N = mParameters.length;
+
+            String[] names = new String[N];
+            String[] comments = new String[N];
+            SourcePositionInfo[] positions = new SourcePositionInfo[N];
+
+            // get the right names so we can handle our names being different from
+            // our parent's names.
+            for (int i=0; i<N; i++) {
+                names[i] = mParameters[i].name();
+                comments[i] = "";
+                positions[i] = mParameters[i].position();
+            }
+
+            // gather our comments, and complain about misnamed @param tags
+            for (ParamTagInfo tag: comment().paramTags()) {
+                int index = indexOfParam(tag.parameterName(), names);
+                if (index >= 0) {
+                    comments[index] = tag.parameterComment();
+                    positions[index] = tag.position();
+                } else {
+                    Errors.error(Errors.UNKNOWN_PARAM_TAG_NAME, tag.position(),
+                            "@param tag with name that doesn't match the parameter list: '"
+                            + tag.parameterName() + "'");
+                }
+            }
+             
+            // get our parent's tags to fill in the blanks
+            MethodInfo overridden = this.findOverriddenMethod(name(), signature());
+            if (overridden != null) {
+                ParamTagInfo[] maternal = overridden.paramTags();
+                for (int i=0; i<N; i++) {
+                    if (comments[i].equals("")) {
+                        comments[i] = maternal[i].parameterComment();
+                        positions[i] = maternal[i].position();
+                    }
+                }
+            }
+
+            // construct the results, and cache them for next time
+            mParamTags = new ParamTagInfo[N];
+            for (int i=0; i<N; i++) {
+                mParamTags[i] = new ParamTagInfo("@param", "@param", names[i] + " " + comments[i],
+                        parent(), positions[i]);
+
+                // while we're here, if we find any parameters that are still undocumented at this
+                // point, complain. (this warning is off by default, because it's really, really
+                // common; but, it's good to be able to enforce it)
+                if (comments[i].equals("")) {
+                    Errors.error(Errors.UNDOCUMENTED_PARAMETER, positions[i],
+                            "Undocumented parameter '" + names[i] + "' on method '"
+                            + name() + "'");
+                }
+            }
+        }
+        return mParamTags;
+    }
+
+    public SeeTagInfo[] seeTags()
+    {
+        SeeTagInfo[] result = comment().seeTags();
+        if (result == null) {
+            if (mOverriddenMethod != null) {
+                result = mOverriddenMethod.seeTags();
+            }
+        }
+        return result;
+    }
+
+    public TagInfo[] deprecatedTags()
+    {
+        TagInfo[] result = comment().deprecatedTags();
+        if (result.length == 0) {
+            if (comment().undeprecateTags().length == 0) {
+                if (mOverriddenMethod != null) {
+                    result = mOverriddenMethod.deprecatedTags();
+                }
+            }
+        }
+        return result;
+    }
+
+    public ParameterInfo[] parameters()
+    {
+        return mParameters;
+    }
+    
+
+    public boolean matchesParams(String[] params, String[] dimensions)
+    {
+        if (mParamStrings == null) {
+            ParameterInfo[] mine = mParameters;
+            int len = mine.length;
+            if (len != params.length) {
+                return false;
+            }
+            for (int i=0; i<len; i++) {
+                TypeInfo t = mine[i].type();
+                if (!t.dimension().equals(dimensions[i])) {
+                    return false;
+                }
+                String qn = t.qualifiedTypeName();
+                String s = params[i];
+                int slen = s.length();
+                int qnlen = qn.length();
+                if (!(qn.equals(s) ||
+                        ((slen+1)<qnlen && qn.charAt(qnlen-slen-1)=='.'
+                         && qn.endsWith(s)))) {
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
+
+    public void makeHDF(HDF data, String base)
+    {
+        data.setValue(base + ".kind", kind());
+        data.setValue(base + ".name", name());
+        data.setValue(base + ".href", htmlPage());
+        data.setValue(base + ".anchor", anchor());
+
+        if (mReturnType != null) {
+            returnType().makeHDF(data, base + ".returnType", false, typeVariables());
+            data.setValue(base + ".abstract", mIsAbstract ? "abstract" : "");
+        }
+
+        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 + ".deprecated", deprecatedTags());
+        TagInfo.makeHDF(data, base + ".seeAlso", seeTags());
+        ParamTagInfo.makeHDF(data, base + ".paramTags", paramTags());
+        AttrTagInfo.makeReferenceHDF(data, base + ".attrRefs", comment().attrTags());
+        ThrowsTagInfo.makeHDF(data, base + ".throws", throwsTags());
+        ParameterInfo.makeHDF(data, base + ".params", parameters(), isVarArgs(), typeVariables());
+        if (isProtected()) {
+            data.setValue(base + ".scope", "protected");
+        }
+        else if (isPublic()) {
+            data.setValue(base + ".scope", "public");
+        }
+        TagInfo.makeHDF(data, base + ".returns", returnTags());
+
+        if (mTypeParameters != null) {
+            TypeInfo.makeHDF(data, base + ".generic.typeArguments", mTypeParameters, false);
+        }
+    }
+
+    public HashSet<String> typeVariables()
+    {
+        HashSet<String> result = TypeInfo.typeVariables(mTypeParameters);
+        ClassInfo cl = containingClass();
+        while (cl != null) {
+            TypeInfo[] types = cl.asTypeInfo().typeArguments();
+            if (types != null) {
+                TypeInfo.typeVariables(types, result);
+            }
+            cl = cl.containingClass();
+        }
+        return result;
+    }
+
+    public boolean isExecutable()
+    {
+        return true;
+    }
+
+    public ClassInfo[] thrownExceptions()
+    {
+        return mThrownExceptions;
+    }
+
+    public String typeArgumentsName(HashSet<String> typeVars)
+    {
+        if (mTypeParameters == null || mTypeParameters.length == 0) {
+            return "";
+        } else {
+            return TypeInfo.typeArgumentsName(mTypeParameters, typeVars);
+        }
+    }
+
+    public boolean isAnnotationElement()
+    {
+        return mIsAnnotationElement;
+    }
+
+    public AnnotationValueInfo defaultAnnotationElementValue()
+    {
+        return mDefaultAnnotationElementValue;
+    }
+    
+    public void setVarargs(boolean set){
+        mIsVarargs = set;
+    }
+    public boolean isVarArgs(){
+      return mIsVarargs;
+    }
+    public String toString(){
+      return this.name();
+    }
+    
+    public void setReason(String reason) {
+        mReasonOpened = reason;
+    }
+    
+    public String getReason() {
+        return mReasonOpened;
+    }
+
+    private String mFlatSignature;
+    private MethodInfo mOverriddenMethod;
+    private TypeInfo mReturnType;
+    private boolean mIsAnnotationElement;
+    private boolean mIsAbstract;
+    private boolean mIsSynchronized;
+    private boolean mIsNative;
+    private boolean mIsVarargs;
+    private boolean mDeprecatedKnown;
+    private boolean mIsDeprecated;
+    private ParameterInfo[] mParameters;
+    private ClassInfo[] mThrownExceptions;
+    private String[] mParamStrings;
+    ThrowsTagInfo[] mThrowsTags;
+    private ParamTagInfo[] mParamTags;
+    private TypeInfo[] mTypeParameters;
+    private AnnotationValueInfo mDefaultAnnotationElementValue;
+    private String mReasonOpened;
+}
+
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
new file mode 100644
index 0000000..aac0def
--- /dev/null
+++ b/tools/droiddoc/src/PackageInfo.java
@@ -0,0 +1,185 @@
+/*
+ * 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 com.sun.javadoc.*;
+import com.sun.tools.doclets.*;
+import org.clearsilver.HDF;
+import org.clearsilver.CS;
+import java.util.*;
+import java.io.*;
+
+public class PackageInfo extends DocInfo implements ContainerInfo
+{
+    public static final Comparator<PackageInfo> comparator = new Comparator<PackageInfo>() {
+        public int compare(PackageInfo a, PackageInfo b) {
+            return a.name().compareTo(b.name());
+        }
+    };
+
+    public PackageInfo(PackageDoc pkg, String name, SourcePositionInfo position)
+    {
+        super(pkg.getRawCommentText(), position);
+        mName = name;
+
+        if (pkg == null) {
+            throw new RuntimeException("pkg is null");
+        }
+        mPackage = pkg;
+    }
+
+    public String htmlPage()
+    {
+        String s = mName;
+        s = s.replace('.', '/');
+        s += "/package-summary.html";
+        s = DroidDoc.javadocDir + s;
+        return s;
+    }
+
+    public String fullDescriptionHtmlPage() {
+        String s = mName;
+        s = s.replace('.', '/');
+        s += "/package-descr.html";
+        s = DroidDoc.javadocDir + s;
+        return s;
+    }
+
+    public ContainerInfo parent()
+    {
+        return null;
+    }
+
+    public boolean isHidden()
+    {
+        return comment().isHidden();
+    }
+
+    public boolean checkLevel() {
+        // TODO should return false if all classes are hidden but the package isn't.
+        // We don't have this so I'm not doing it now.
+        return !isHidden();
+    }
+
+    public String name()
+    {
+        return mName;
+    }
+
+    public String qualifiedName()
+    {
+        return mName;
+    }
+
+    public TagInfo[] inlineTags()
+    {
+        return comment().tags();
+    }
+
+    public TagInfo[] firstSentenceTags()
+    {
+        return comment().briefTags();
+    }
+
+    public static ClassInfo[] filterHidden(ClassInfo[] classes)
+    {
+        ArrayList<ClassInfo> out = new ArrayList<ClassInfo>();
+
+        for (ClassInfo cl: classes) {
+            if (!cl.isHidden()) {
+                out.add(cl);
+            }
+        }
+
+        return out.toArray(new ClassInfo[0]);
+    }
+
+    public void makeLink(HDF data, String base)
+    {
+        if (checkLevel()) {
+            data.setValue(base + ".link", htmlPage());
+        }
+        data.setValue(base + ".name", name());
+    }
+
+    public void makeClassLinkListHDF(HDF data, String base)
+    {
+        makeLink(data, base);
+        ClassInfo.makeLinkListHDF(data, base + ".interfaces", interfaces());
+        ClassInfo.makeLinkListHDF(data, base + ".classes", ordinaryClasses());
+        ClassInfo.makeLinkListHDF(data, base + ".enums", enums());
+        ClassInfo.makeLinkListHDF(data, base + ".exceptions", exceptions());
+        ClassInfo.makeLinkListHDF(data, base + ".errors", errors());
+    }
+
+    public ClassInfo[] interfaces()
+    {
+        if (mInterfaces == null) {
+            mInterfaces = ClassInfo.sortByName(filterHidden(Converter.convertClasses(
+                            mPackage.interfaces())));
+        }
+        return mInterfaces;
+    }
+
+    public ClassInfo[] ordinaryClasses()
+    {
+        if (mOrdinaryClasses == null) {
+            mOrdinaryClasses = ClassInfo.sortByName(filterHidden(Converter.convertClasses(
+                            mPackage.ordinaryClasses())));
+        }
+        return mOrdinaryClasses;
+    }
+
+    public ClassInfo[] enums()
+    {
+        if (mEnums == null) {
+            mEnums = ClassInfo.sortByName(filterHidden(Converter.convertClasses(mPackage.enums())));
+        }
+        return mEnums;
+    }
+
+    public ClassInfo[] exceptions()
+    {
+        if (mExceptions == null) {
+            mExceptions = ClassInfo.sortByName(filterHidden(Converter.convertClasses(
+                        mPackage.exceptions())));
+        }
+        return mExceptions;
+    }
+
+    public ClassInfo[] errors()
+    {
+        if (mErrors == null) {
+            mErrors = ClassInfo.sortByName(filterHidden(Converter.convertClasses(
+                        mPackage.errors())));
+        }
+        return mErrors;
+    }
+
+    // in hashed containers, treat the name as the key
+    @Override
+    public int hashCode() {
+        return mName.hashCode();
+    }
+
+    private String mName;
+    private PackageDoc mPackage;
+    private ClassInfo[] mInterfaces;
+    private ClassInfo[] mOrdinaryClasses;
+    private ClassInfo[] mEnums;
+    private ClassInfo[] mExceptions;
+    private ClassInfo[] mErrors;
+}
+
diff --git a/tools/droiddoc/src/ParamTagInfo.java b/tools/droiddoc/src/ParamTagInfo.java
new file mode 100644
index 0000000..c21ecd5
--- /dev/null
+++ b/tools/droiddoc/src/ParamTagInfo.java
@@ -0,0 +1,95 @@
+/*
+ * 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 java.util.regex.Pattern;
+import java.util.regex.Matcher;
+import org.clearsilver.HDF;
+import org.clearsilver.CS;
+
+public class ParamTagInfo extends ParsedTagInfo
+{
+    static final Pattern PATTERN = Pattern.compile(
+                                "([^ \t\r\n]+)[ \t\r\n]+(.*)",
+                                Pattern.DOTALL);
+
+    private boolean mIsTypeParameter;
+    private String mParameterComment;
+    private String mParameterName;
+
+    ParamTagInfo(String name, String kind, String text, ContainerInfo base,
+            SourcePositionInfo sp)
+    {
+        super(name, kind, text, base, sp);
+
+        Matcher m = PATTERN.matcher(text);
+        if (m.matches()) {
+            mParameterName = m.group(1);
+            mParameterComment = m.group(2);
+            int len = mParameterName.length();
+            mIsTypeParameter = len > 2
+                                && mParameterName.charAt(0) == '<'
+                                && mParameterName.charAt(len-1) == '>';
+        } else {
+            mParameterName = text.trim();
+            mParameterComment = "";
+            mIsTypeParameter = false;
+        }
+        setCommentText(mParameterComment);
+    }
+
+    ParamTagInfo(String name, String kind, String text,
+                            boolean isTypeParameter, String parameterComment,
+                            String parameterName, ContainerInfo base,
+                            SourcePositionInfo sp)
+    {
+        super(name, kind, text, base, sp);
+        mIsTypeParameter = isTypeParameter;
+        mParameterComment = parameterComment;
+        mParameterName = parameterName;
+    }
+
+    public boolean isTypeParameter()
+    {
+        return mIsTypeParameter;
+    }
+
+    public String parameterComment()
+    {
+        return mParameterComment;
+    }
+
+    public String parameterName()
+    {
+        return mParameterName;
+    }
+
+    public void makeHDF(HDF data, String base)
+    {
+        data.setValue(base + ".name", parameterName());
+        data.setValue(base + ".isTypeParameter", isTypeParameter() ? "1" : "0");
+        TagInfo.makeHDF(data, base + ".comment", commentTags());
+    }
+
+    public static void makeHDF(HDF data, String base, ParamTagInfo[] tags)
+    {
+        for (int i=0; i<tags.length; i++) {
+            // don't output if the comment is ""
+            if (!"".equals(tags[i].parameterComment())) {
+                tags[i].makeHDF(data, base + "." + i);
+            }
+        }
+    }
+}
diff --git a/tools/droiddoc/src/ParameterInfo.java b/tools/droiddoc/src/ParameterInfo.java
new file mode 100644
index 0000000..44608be
--- /dev/null
+++ b/tools/droiddoc/src/ParameterInfo.java
@@ -0,0 +1,72 @@
+/*
+ * 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 org.clearsilver.CS;
+import java.util.HashSet;
+
+public class ParameterInfo
+{
+    ParameterInfo(String name, String typeName, TypeInfo type, SourcePositionInfo position)
+    {
+        mName = name;
+        mTypeName = typeName;
+        mType = type;
+        mPosition = position;
+    }
+
+    TypeInfo type()
+    {
+        return mType;
+    }
+
+    String name()
+    {
+        return mName;
+    }
+
+    String typeName()
+    {
+        return mTypeName;
+    }
+
+    SourcePositionInfo position()
+    {
+        return mPosition;
+    }
+
+    public void makeHDF(HDF data, String base, boolean isLastVararg,
+            HashSet<String> typeVariables)
+    {
+        data.setValue(base + ".name", this.name());
+        type().makeHDF(data, base + ".type", isLastVararg, typeVariables);
+    }
+
+    public static void makeHDF(HDF data, String base, ParameterInfo[] params,
+            boolean isVararg, HashSet<String> typeVariables)
+    {
+        for (int i=0; i<params.length; i++) {
+            params[i].makeHDF(data, base + "." + i,
+                    isVararg && (i == params.length - 1), typeVariables);
+        }
+    }
+    
+    String mName;
+    String mTypeName;
+    TypeInfo mType;
+    SourcePositionInfo mPosition;
+}
+
diff --git a/tools/droiddoc/src/ParsedTagInfo.java b/tools/droiddoc/src/ParsedTagInfo.java
new file mode 100755
index 0000000..c2e4806
--- /dev/null
+++ b/tools/droiddoc/src/ParsedTagInfo.java
@@ -0,0 +1,61 @@
+/*
+ * 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 org.clearsilver.CS;
+import java.util.ArrayList;
+
+public class ParsedTagInfo extends TagInfo
+{
+    private ContainerInfo mContainer;
+    private String mCommentText;
+    private Comment mComment;
+
+    ParsedTagInfo(String name, String kind, String text, ContainerInfo base, SourcePositionInfo sp)
+    {
+        super(name, kind, text, SourcePositionInfo.findBeginning(sp, text));
+        mContainer = base;
+        mCommentText = text;
+    }
+
+    public TagInfo[] commentTags()
+    {
+        if (mComment == null) {
+            mComment = new Comment(mCommentText, mContainer, position());
+        }
+        return mComment.tags();
+    }
+
+    protected void setCommentText(String comment)
+    {
+        mCommentText = comment;
+    }
+
+    public static <T extends ParsedTagInfo> TagInfo[]
+    joinTags(T[] tags)
+    {
+        ArrayList<TagInfo> list = new ArrayList<TagInfo>();
+        final int N = tags.length;
+        for (int i=0; i<N; i++) {
+            TagInfo[] t = tags[i].commentTags();
+            final int M = t.length;
+            for (int j=0; j<M; j++) {
+                list.add(t[j]);
+            }
+        }
+        return list.toArray(new TagInfo[list.size()]);
+    }
+}
diff --git a/tools/droiddoc/src/Proofread.java b/tools/droiddoc/src/Proofread.java
new file mode 100644
index 0000000..ec9f523
--- /dev/null
+++ b/tools/droiddoc/src/Proofread.java
@@ -0,0 +1,178 @@
+/*
+ * 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 java.io.IOException;
+import java.io.FileWriter;
+import java.util.regex.Pattern;
+import java.util.regex.Matcher;
+
+public class Proofread
+{
+    static FileWriter out = null;
+    static final Pattern WHITESPACE = Pattern.compile("\\r?\\n");
+    static final String INDENT = "        ";
+    static final String NEWLINE = "\n" + INDENT;
+
+    public static void initProofread(String filename)
+    {
+        try {
+            out = new FileWriter(filename);
+            out.write("javadoc proofread file: " + filename + "\n");
+        }
+        catch (IOException e) {
+            if (out != null) {
+                try {
+                    out.close();
+                }
+                catch (IOException ex) {
+                }
+                out = null;
+            }
+            System.err.println("error opening file: " + filename);
+        }
+    }
+
+    public static void finishProofread(String filename)
+    {
+        if (out == null) {
+            return;
+        }
+
+        try {
+            out.close();
+        }
+        catch (IOException e) {
+        }
+    }
+
+    public static void write(String s)
+    {
+        if (out == null) {
+            return ;
+        }
+        try {
+            out.write(s);
+        }
+        catch (IOException e) {
+        }
+    }
+
+    public static void writeIndented(String s)
+    {
+        s = s.trim();
+        Matcher m = WHITESPACE.matcher(s);
+        s = m.replaceAll(NEWLINE);
+        write(INDENT);
+        write(s);
+        write("\n");
+    }
+
+    public static void writeFileHeader(String filename)
+    {
+        write("\n\n=== ");
+        write(filename);
+        write(" ===\n");
+    }
+
+    public static void writeTagList(TagInfo[] tags)
+    {
+        if (out == null) {
+            return;
+        }
+
+        for (TagInfo t: tags) {
+            String k = t.kind();
+            if ("Text".equals(t.name())) {
+                writeIndented(t.text());
+            }
+            else if ("@more".equals(k)) {
+                writeIndented("");
+            }
+            else if ("@see".equals(k)) {
+                SeeTagInfo see = (SeeTagInfo)t;
+                String label = see.label();
+                if (label == null) {
+                    label = "";
+                }
+                writeIndented("{" + see.name() + " ... " + label + "}");
+            }
+            else if ("@code".equals(k)) {
+                writeIndented(t.text());
+            }
+            else if ("@samplecode".equals(k)) {
+                writeIndented(t.text());
+            }
+            else {
+                writeIndented("{" + (t.name() != null ? t.name() : "") + "/" +
+                        t.text() + "}");
+            }
+        }
+    }
+
+    public static void writePackages(String filename, TagInfo[] tags)
+    {
+        if (out == null) {
+            return;
+        }
+
+        writeFileHeader(filename);
+        writeTagList(tags);
+    }
+
+    public static void writePackage(String filename, TagInfo[] tags)
+    {
+        if (out == null) {
+            return;
+        }
+
+        writeFileHeader(filename);
+        writeTagList(tags);
+    }
+
+    public static void writeClass(String filename, ClassInfo cl)
+    {
+        if (out == null) {
+            return;
+        }
+
+        writeFileHeader(filename);
+        writeTagList(cl.inlineTags());
+
+        // enum constants
+        for (FieldInfo f: cl.enumConstants()) {
+            write("ENUM: " + f.name() + "\n");
+            writeTagList(f.inlineTags());
+        }
+
+        // fields
+        for (FieldInfo f: cl.selfFields()) {
+            write("FIELD: " + f.name() + "\n");
+            writeTagList(f.inlineTags());
+        }
+
+        // constructors
+        for (MethodInfo m: cl.constructors()) {
+            write("CONSTRUCTOR: " + m.name() + "\n");
+            writeTagList(m.inlineTags().tags());
+        }
+
+        // methods
+        for (MethodInfo m: cl.selfMethods()) {
+            write("METHOD: " + m.name() + "\n");
+            writeTagList(m.inlineTags().tags());
+        }
+    }
+}
diff --git a/tools/droiddoc/src/SampleCode.java b/tools/droiddoc/src/SampleCode.java
new file mode 100644
index 0000000..e2283bd
--- /dev/null
+++ b/tools/droiddoc/src/SampleCode.java
@@ -0,0 +1,161 @@
+/*
+ * 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 org.clearsilver.CS;
+import java.util.*;
+import java.io.*;
+import java.util.regex.Pattern;
+import java.util.regex.Matcher;
+
+
+public class SampleCode {
+    String mSource;
+    String mDest;
+    String mTitle;
+
+    public SampleCode(String source, String dest, String title) {
+        mSource = source;
+        mTitle = title;
+        int len = dest.length();
+        if (len > 1 && dest.charAt(len-1) != '/') {
+            mDest = dest + '/';
+        } else {
+            mDest = dest;
+        }
+    }
+
+    public void write() {
+        File f = new File(mSource);
+        if (!f.isDirectory()) {
+            System.out.println("-samplecode not a directory: " + mSource);
+            return;
+        }
+        writeDirectory(f, mDest);
+    }
+
+    public static String convertExtension(String s, String ext) {
+        return s.substring(0, s.lastIndexOf('.')) + ext;
+    }
+
+    public static String[] IMAGES = { ".png", ".jpg", ".gif" };
+    public static String[] TEMPLATED = { ".java", ".xml" };
+
+    public static boolean inList(String s, String[] list) {
+        for (String t: list) {
+            if (s.endsWith(t)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public void writeDirectory(File dir, String relative) {
+        TreeSet<String> dirs = new TreeSet<String>();
+        TreeSet<String> files = new TreeSet<String>();
+
+        String subdir = relative; //.substring(mDest.length());
+
+        for (File f: dir.listFiles()) {
+            String name = f.getName();
+            if (name.startsWith(".") || name.startsWith("_")) {
+                continue;
+            }
+            if (f.isFile()) {
+                String out = relative + name;
+
+                if (inList(out, IMAGES)) {
+                    // copied directly
+                    ClearPage.copyFile(f, out);
+                    writeImagePage(f, convertExtension(out, DroidDoc.htmlExtension), subdir);
+                    files.add(name);
+                }
+                if (inList(out, TEMPLATED)) {
+                    // copied and goes through the template
+                    ClearPage.copyFile(f, out);
+                    writePage(f, convertExtension(out, DroidDoc.htmlExtension), subdir);
+                    files.add(name);
+                }
+                // else ignored
+            }
+            else if (f.isDirectory()) {
+                writeDirectory(f, relative + name + "/");
+                dirs.add(name);
+            }
+        }
+
+        // write the index page
+        int i;
+        HDF hdf = DroidDoc.makeHDF();
+
+        hdf.setValue("page.title", dir.getName() + " - " + mTitle);
+        hdf.setValue("projectTitle", mTitle);
+        hdf.setValue("subdir", subdir);
+        i=0;
+        for (String d: dirs) {
+            hdf.setValue("subdirs." + i + ".name", d);
+            i++;
+        }
+        i=0;
+        for (String f: files) {
+            hdf.setValue("files." + i + ".name", f);
+            hdf.setValue("files." + i + ".href", convertExtension(f, ".html"));
+            i++;
+        }
+        String filename = dir.getPath() + "/_index.html";
+        String summary = SampleTagInfo.readFile(new SourcePositionInfo(filename, -1,-1), filename,
+                                                "sample code", true, false, true);
+        if (summary == null) {
+            summary = "";
+        }
+        hdf.setValue("summary", summary);
+        
+        ClearPage.write(hdf, "sampleindex.cs", relative + "/index" + DroidDoc.htmlExtension);
+    }
+
+    public void writePage(File f, String out, String subdir) {
+        String name = f.getName();
+
+        String filename = f.getPath();
+        String data = SampleTagInfo.readFile(new SourcePositionInfo(filename, -1,-1), filename,
+                                                "sample code", true, true, true);
+        data = DroidDoc.escape(data);
+        
+        HDF hdf = DroidDoc.makeHDF();
+
+        hdf.setValue("page.title", name);
+        hdf.setValue("subdir", subdir);
+        hdf.setValue("realFile", name);
+        hdf.setValue("fileContents", data);
+
+        ClearPage.write(hdf, "sample.cs", out);
+    }
+
+    public void writeImagePage(File f, String out, String subdir) {
+        String name = f.getName();
+
+        String data = "<img src=\"" + name + "\" title=\"" + name + "\" />";
+        
+        HDF hdf = DroidDoc.makeHDF();
+
+        hdf.setValue("page.title", name);
+        hdf.setValue("subdir", subdir);
+        hdf.setValue("realFile", name);
+        hdf.setValue("fileContents", data);
+
+        ClearPage.write(hdf, "sample.cs", out);
+    }
+}
diff --git a/tools/droiddoc/src/SampleTagInfo.java b/tools/droiddoc/src/SampleTagInfo.java
new file mode 100644
index 0000000..c80083b
--- /dev/null
+++ b/tools/droiddoc/src/SampleTagInfo.java
@@ -0,0 +1,288 @@
+/*
+ * 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 org.clearsilver.CS;
+
+import java.io.Reader;
+import java.io.IOException;
+import java.io.FileReader;
+import java.io.LineNumberReader;
+import java.util.regex.Pattern;
+import java.util.regex.Matcher;
+
+/*
+ * SampleTagInfo copies text from a given file into the javadoc comment.
+ *
+ * The @include tag copies the text verbatim from the given file.
+ *
+ * The @sample tag copies the text from the given file, stripping leading and
+ * trailing whitespace, and reducing the indent level of the text to the indent
+ * level of the first non-whitespace line.
+ *
+ * Both tags accept either a filename and an id or just a filename.  If no id
+ * is provided, the entire file is copied.  If an id is provided, the lines
+ * in the given file between the first two lines containing BEGIN_INCLUDE(id)
+ * and END_INCLUDE(id), for the given id, are copied.  The id may be only 
+ * letters, numbers and underscore (_).
+ *
+ * Four examples:
+ * {@include samples/ApiDemos/src/com/google/app/Notification1.java}
+ * {@sample samples/ApiDemos/src/com/google/app/Notification1.java}
+ * {@include samples/ApiDemos/src/com/google/app/Notification1.java Bleh}
+ * {@sample samples/ApiDemos/src/com/google/app/Notification1.java Bleh}
+ *
+ */
+public class SampleTagInfo extends TagInfo
+{
+    static final int STATE_BEGIN = 0;
+    static final int STATE_MATCHING = 1;
+
+    static final Pattern TEXT = Pattern.compile(
+                "[\r\n \t]*([^\r\n \t]*)[\r\n \t]*([0-9A-Za-z_]*)[\r\n \t]*",
+                Pattern.DOTALL);
+
+    private static final String BEGIN_INCLUDE = "BEGIN_INCLUDE";
+    private static final String END_INCLUDE = "END_INCLUDE";
+
+    private ContainerInfo mBase;
+    private String mIncluded;
+
+    public static String escapeHtml(String str) {
+        return str.replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;");
+    }
+
+    private static boolean isIncludeLine(String str) {
+        return str.indexOf(BEGIN_INCLUDE)>=0 || str.indexOf(END_INCLUDE)>=0;
+    }
+
+    SampleTagInfo(String name, String kind, String text, ContainerInfo base,
+            SourcePositionInfo position)
+    {
+        super(name, kind, text, position);
+        mBase = base;
+
+        Matcher m = TEXT.matcher(text);
+        if (!m.matches()) {
+            Errors.error(Errors.BAD_INCLUDE_TAG, position, "Bad @include tag: "
+                    + text);
+            return;
+        }
+        String filename = m.group(1);
+        String id = m.group(2);
+        boolean trim = "@sample".equals(name);
+
+        if (id == null || "".equals(id)) {
+            mIncluded = readFile(position, filename, id, trim, true, false);
+        } else {
+            mIncluded = loadInclude(position, filename, id, trim);
+        }
+
+        if (mIncluded == null) {
+            Errors.error(Errors.BAD_INCLUDE_TAG, position, "include tag '" + id
+                    + "' not found in file: " + filename);
+        }
+    }
+
+    static String getTrimString(String line)
+    {
+        int i = 0;
+        int len = line.length();
+        for (; i<len; i++) {
+            char c = line.charAt(i);
+            if (c != ' ' && c != '\t') {
+                break;
+            }
+        }
+        if (i == len) {
+            return null;
+        } else {
+            return line.substring(0, i);
+        }
+    }
+
+    static String loadInclude(SourcePositionInfo pos, String filename,
+                                String id, boolean trim)
+    {
+        Reader input = null;
+        StringBuilder result = new StringBuilder();
+
+        String begin = BEGIN_INCLUDE + "(" + id + ")";
+        String end = END_INCLUDE + "(" + id + ")";
+
+        try {
+            input = new FileReader(filename);
+            LineNumberReader lines = new LineNumberReader(input);
+
+            int state = STATE_BEGIN;
+
+            int trimLength = -1;
+            String trimString = null;
+            int trailing = 0;
+
+            while (true) {
+                String line = lines.readLine();
+                if (line == null) {
+                    return null;
+                }
+                switch (state) {
+                case STATE_BEGIN:
+                    if (line.indexOf(begin) >= 0) {
+                        state = STATE_MATCHING;
+                    }
+                    break;
+                case STATE_MATCHING:
+                    if (line.indexOf(end) >= 0) {
+                        return result.substring(0);
+                    } else {
+                        boolean empty = "".equals(line.trim());
+                        if (trim) {
+                            if (isIncludeLine(line)) {
+                                continue;
+                            }
+                            if (trimLength < 0 && !empty) {
+                                trimString = getTrimString(line);
+                                if (trimString != null) {
+                                    trimLength = trimString.length();
+                                }
+                            }
+                            if (trimLength >= 0 && line.length() > trimLength) {
+                                boolean trimThisLine = true;
+                                for (int i=0; i<trimLength; i++) {
+                                    if (line.charAt(i) != trimString.charAt(i)){
+                                        trimThisLine = false;
+                                        break;
+                                    }
+                                }
+                                if (trimThisLine) {
+                                    line = line.substring(trimLength);
+                                }
+                            }
+                            if (trimLength >= 0) {
+                                if (!empty) {
+                                    for (int i=0; i<trailing; i++) {
+                                        result.append('\n');
+                                    }
+                                    line = escapeHtml(line);
+                                    result.append(line);
+                                    trailing = 1;  // add \n next time, maybe
+                                } else {
+                                    trailing++;
+                                }
+                            }
+                        } else {
+                            result.append(line);
+                            result.append('\n');
+                        }
+                    }
+                    break;
+                }
+            }
+        }
+        catch (IOException e) {
+            Errors.error(Errors.BAD_INCLUDE_TAG, pos, "Error reading file for"
+                    + " include \"" + id + "\" " + filename);
+        }
+        finally {
+            if (input != null) {
+                try {
+                    input.close();
+                }
+                catch (IOException ex) {
+                }
+            }
+        }
+        Errors.error(Errors.BAD_INCLUDE_TAG, pos, "Did not find " + end
+                + " in file " + filename);
+        return null;
+    }
+
+    static String readFile(SourcePositionInfo pos, String filename,
+                                String id, boolean trim, boolean escape,
+                                boolean errorOk)
+    {
+        Reader input = null;
+        StringBuilder result = new StringBuilder();
+        int trailing = 0;
+        boolean started = false;
+        try {
+            input = new FileReader(filename);
+            LineNumberReader lines = new LineNumberReader(input);
+
+            while (true) {
+                String line = lines.readLine();
+                if (line == null) {
+                    break;
+                }
+                if (trim) {
+                    if (isIncludeLine(line)) {
+                        continue;
+                    }
+                    if (!"".equals(line.trim())) {
+                        if (started) {
+                            for (int i=0; i<trailing; i++) {
+                                result.append('\n');
+                            }
+                        }
+                        if (escape) {
+                            line = escapeHtml(line);
+                        }
+                        result.append(line);
+                        trailing = 1;  // add \n next time, maybe
+                        started = true;
+                    } else {
+                        if (started) {
+                            trailing++;
+                        }
+                    }
+                } else {
+                    result.append(line);
+                    result.append('\n');
+                }
+            }
+        }
+        catch (IOException e) {
+            if (errorOk) {
+                return null;
+            } else {
+                Errors.error(Errors.BAD_INCLUDE_TAG, pos, "Error reading file for"
+                        + " include \"" + id + "\" " + filename);
+            }
+        }
+        finally {
+            if (input != null) {
+                try {
+                    input.close();
+                }
+                catch (IOException ex) {
+                }
+            }
+        }
+        return result.substring(0);
+    }
+
+    public void makeHDF(HDF data, String base)
+    {
+        data.setValue(base + ".name", name());
+        data.setValue(base + ".kind", kind());
+        if (mIncluded != null) {
+            data.setValue(base + ".text", mIncluded);
+        } else {
+            data.setValue(base + ".text", "INCLUDE_ERROR");
+        }
+    }
+}
+
diff --git a/tools/droiddoc/src/Scoped.java b/tools/droiddoc/src/Scoped.java
new file mode 100644
index 0000000..cca61ed
--- /dev/null
+++ b/tools/droiddoc/src/Scoped.java
@@ -0,0 +1,23 @@
+/*
+ * 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.
+ */
+
+public interface Scoped {
+    boolean isPublic();
+    boolean isProtected();
+    boolean isPackagePrivate();
+    boolean isPrivate();
+    boolean isHidden();
+}
diff --git a/tools/droiddoc/src/SeeTagInfo.java b/tools/droiddoc/src/SeeTagInfo.java
new file mode 100644
index 0000000..94863b5
--- /dev/null
+++ b/tools/droiddoc/src/SeeTagInfo.java
@@ -0,0 +1,79 @@
+/*
+ * 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 java.util.regex.Pattern;
+import java.util.regex.Matcher;
+import org.clearsilver.HDF;
+import org.clearsilver.CS;
+import java.util.ArrayList;
+
+public class SeeTagInfo extends TagInfo
+{
+    private ContainerInfo mBase;
+    LinkReference mLink;
+
+    SeeTagInfo(String name, String kind, String text, ContainerInfo base,
+            SourcePositionInfo position)
+    {
+        super(name, kind, text, position);
+        mBase = base;
+    }
+
+    protected LinkReference linkReference() {
+        if (mLink == null) {
+            mLink = LinkReference.parse(text(), mBase, position(),
+                           (!"@see".equals(name())) && (mBase != null ? mBase.checkLevel() : true));
+        }
+        return mLink;
+    }
+
+    public String label()
+    {
+        return linkReference().label;
+    }
+
+    public void makeHDF(HDF data, String base)
+    {
+        LinkReference linkRef = linkReference();
+        if (linkRef.kind != null) {
+            // if they have a better suggestion about "kind" use that.
+            // do this before super.makeHDF() so it picks it up
+            setKind(linkRef.kind);
+        }
+
+        super.makeHDF(data, base);
+
+        data.setValue(base + ".label", linkRef.label);
+        if (linkRef.href != null) {
+            data.setValue(base + ".href", linkRef.href);
+        }
+    }
+
+    public boolean checkLevel() {
+        return linkReference().checkLevel();
+    }
+
+    public static void makeHDF(HDF data, String base, SeeTagInfo[] tags)
+    {
+        int j=0;
+        for (SeeTagInfo tag: tags) {
+            if (tag.mBase.checkLevel() && tag.checkLevel()) {
+                tag.makeHDF(data, base + "." + j);
+                j++;
+            }
+        }
+    }
+}
diff --git a/tools/droiddoc/src/Sorter.java b/tools/droiddoc/src/Sorter.java
new file mode 100644
index 0000000..92039d4
--- /dev/null
+++ b/tools/droiddoc/src/Sorter.java
@@ -0,0 +1,32 @@
+/*
+ * 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.
+ */
+
+public class Sorter implements Comparable
+{
+    public String label;
+    public Object data;
+
+    public Sorter(String l, Object d)
+    {
+        label = l;
+        data = d;
+    }
+
+    public int compareTo(Object other)
+    {
+        return label.compareToIgnoreCase(((Sorter)other).label);
+    }
+}
diff --git a/tools/droiddoc/src/SourcePositionInfo.java b/tools/droiddoc/src/SourcePositionInfo.java
new file mode 100644
index 0000000..6244803
--- /dev/null
+++ b/tools/droiddoc/src/SourcePositionInfo.java
@@ -0,0 +1,94 @@
+/*
+ * 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.
+ */
+
+public class SourcePositionInfo implements Comparable
+{
+    public SourcePositionInfo() {
+        this.file = "<unknown>";
+        this.line = 0;
+        this.column = 0;
+    }
+
+    public SourcePositionInfo(String file, int line, int column)
+    {
+        this.file = file;
+        this.line = line;
+        this.column = column;
+    }
+
+    public SourcePositionInfo(SourcePositionInfo that)
+    {
+        this.file = that.file;
+        this.line = that.line;
+        this.column = that.column;
+    }
+
+    /**
+     * Given this position and str which occurs at that position, as well as str an index into str,
+     * find the SourcePositionInfo.
+     *
+     * @throw StringIndexOutOfBoundsException if index &gt; str.length()
+     */
+    public static SourcePositionInfo add(SourcePositionInfo that, String str, int index)
+    {
+        if (that == null) {
+            return null;
+        }
+        int line = that.line;
+        char prev = 0;
+        for (int i=0; i<index; i++) {
+            char c = str.charAt(i);
+            if (c == '\r' || (c == '\n' && prev != '\r')) {
+                line++;
+            }
+            prev = c;
+        }
+        return new SourcePositionInfo(that.file, line, 0);
+    }
+
+    public static SourcePositionInfo findBeginning(SourcePositionInfo that, String str)
+    {
+        if (that == null) {
+            return null;
+        }
+        int line = that.line-1; // -1 because, well, it seems to work
+        int prev = 0;
+        for (int i=str.length()-1; i>=0; i--) {
+            char c = str.charAt(i);
+            if ((c == '\r' && prev != '\n') || (c == '\n')) {
+                line--;
+            }
+            prev = c;
+        }
+        return new SourcePositionInfo(that.file, line, 0);
+    }
+
+    public String toString()
+    {
+        return file + ':' + line;
+    }
+
+    public int compareTo(Object o) {
+        SourcePositionInfo that = (SourcePositionInfo)o;
+        int r = this.file.compareTo(that.file);
+        if (r != 0) return r;
+        return this.line - that.line;
+    }
+
+    public String file;
+    public int line;
+    public int column;
+}
diff --git a/tools/droiddoc/src/Stubs.java b/tools/droiddoc/src/Stubs.java
new file mode 100644
index 0000000..e1ec76a
--- /dev/null
+++ b/tools/droiddoc/src/Stubs.java
@@ -0,0 +1,988 @@
+/*
+ * 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 java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Set;
+import java.util.Comparator;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.PrintStream;
+
+public class Stubs {
+    private static HashSet<ClassInfo> notStrippable;
+    public static void writeStubs(String stubsDir, Boolean writeXML, String xmlFile,
+            HashSet<String> stubPackages) {
+        // figure out which classes we need
+        notStrippable = new HashSet();
+        ClassInfo[] all = Converter.allClasses();
+        File  xml = new File(xmlFile);
+        xml.getParentFile().mkdirs();
+        PrintStream xmlWriter = null;
+        if (writeXML) {
+            try {
+                xmlWriter = new PrintStream(xml);
+            } catch (FileNotFoundException e) {
+                Errors.error(Errors.IO_ERROR, new SourcePositionInfo(xmlFile, 0, 0),
+                        "Cannot open file for write.");
+            }
+        }
+        // If a class is public or protected, not hidden, and marked as included,
+        // then we can't strip it
+        for (ClassInfo cl: all) {
+            if (cl.checkLevel() && cl.isIncluded()) {
+                cantStripThis(cl, notStrippable, "0:0");
+            }
+        }
+
+        // complain about anything that looks includeable but is not supposed to
+        // be written, e.g. hidden things
+        for (ClassInfo cl: notStrippable) {
+            if (!cl.isHidden()) {
+                MethodInfo[] methods = cl.selfMethods();
+                for (MethodInfo m: methods) {
+                    if (m.isHidden()) {
+                        Errors.error(Errors.UNAVAILABLE_SYMBOL,
+                                m.position(), "Reference to hidden method "
+                                + m.name());
+                    } else if (m.isDeprecated()) {
+                        // don't bother reporting deprecated methods
+                        // unless they are public
+                        Errors.error(Errors.DEPRECATED,
+                                m.position(), "Method "
+                                + cl.qualifiedName() + "." + m.name()
+                                + " is deprecated");
+                    }
+
+                    ClassInfo returnClass = m.returnType().asClassInfo();
+                    if (returnClass != null && returnClass.isHidden()) {
+                        Errors.error(Errors.UNAVAILABLE_SYMBOL, m.position(),
+                                "Method " + cl.qualifiedName() + "." + m.name()
+                                + " returns unavailable type " + returnClass.name());
+                    }
+
+                    ParameterInfo[] params = m.parameters();
+                    for (ParameterInfo p: params) {
+                        TypeInfo t = p.type();
+                        if (!t.isPrimitive()) {
+                            if (t.asClassInfo().isHidden()) {
+                                Errors.error(Errors.UNAVAILABLE_SYMBOL,
+                                        m.position(), "Parameter of hidden type "
+                                        + t.fullName() + " in "
+                                        + cl.qualifiedName() + "." + m.name() + "()");
+                            }
+                        }
+                    }
+                }
+
+                // annotations are handled like methods
+                methods = cl.annotationElements();
+                for (MethodInfo m: methods) {
+                    if (m.isHidden()) {
+                        Errors.error(Errors.UNAVAILABLE_SYMBOL,
+                                m.position(), "Reference to hidden annotation "
+                                + m.name());
+                    }
+
+                    ClassInfo returnClass = m.returnType().asClassInfo();
+                    if (returnClass != null && returnClass.isHidden()) {
+                        Errors.error(Errors.UNAVAILABLE_SYMBOL,
+                                m.position(), "Annotation '" + m.name()
+                                + "' returns unavailable type " + returnClass.name());
+                    }
+
+                    ParameterInfo[] params = m.parameters();
+                    for (ParameterInfo p: params) {
+                        TypeInfo t = p.type();
+                        if (!t.isPrimitive()) {
+                            if (t.asClassInfo().isHidden()) {
+                                Errors.error(Errors.UNAVAILABLE_SYMBOL,
+                                        p.position(), "Reference to unavailable annotation class "
+                                        + t.fullName());
+                            }
+                        }
+                    }
+                }
+            } else if (cl.isDeprecated()) {
+                // not hidden, but deprecated
+                Errors.error(Errors.DEPRECATED,
+                        cl.position(), "Class " + cl.qualifiedName()
+                        + " is deprecated");
+            }
+        }
+
+        // write out the stubs
+        HashMap<PackageInfo, List<ClassInfo>> packages = new HashMap<PackageInfo, List<ClassInfo>>();
+        for (ClassInfo cl: notStrippable) {
+            if (!cl.isDocOnly()) {
+                if (stubPackages == null || stubPackages.contains(cl.containingPackage().name())) {
+                    writeClassFile(stubsDir, cl);
+                    if (packages.containsKey(cl.containingPackage())) {
+                        packages.get(cl.containingPackage()).add(cl);
+                    } else {
+                        ArrayList<ClassInfo> classes = new ArrayList<ClassInfo>();
+                        classes.add(cl);
+                        packages.put(cl.containingPackage(), classes);
+                    }
+                }
+            }
+        }
+
+        // write out the XML
+        if (writeXML && xmlWriter != null) {
+            writeXML(xmlWriter, packages, notStrippable);
+        }
+
+        if (xmlWriter != null) {
+            xmlWriter.close();
+        }
+
+    }
+
+    public static void cantStripThis(ClassInfo cl, HashSet<ClassInfo> notStrippable, String why) {
+
+      if (!notStrippable.add(cl)) {
+        // slight optimization: if it already contains cl, it already contains
+        // all of cl's parents
+          return;
+      }
+      cl.setReasonIncluded(why);
+
+      // cant strip annotations
+      /*if (cl.annotations() != null){
+          for (AnnotationInstanceInfo ai : cl.annotations()){
+              if (ai.type() != null){
+                  cantStripThis(ai.type(), notStrippable, "1:" + cl.qualifiedName());
+              }
+          }
+      }*/
+      // cant strip any public fields or their generics
+      if (cl.allSelfFields() != null){
+          for (FieldInfo fInfo : cl.allSelfFields()){
+              if (fInfo.type() != null){
+                  if (fInfo.type().asClassInfo() != null){
+                      cantStripThis(fInfo.type().asClassInfo(), notStrippable,
+                          "2:" + cl.qualifiedName());
+                  }
+                  if (fInfo.type().typeArguments() != null){
+                      for (TypeInfo tTypeInfo : fInfo.type().typeArguments()){
+                          if (tTypeInfo.asClassInfo() != null){
+                              cantStripThis(tTypeInfo.asClassInfo(), notStrippable,
+                                  "3:" + cl.qualifiedName());
+                          }
+                      }
+                  }
+              }
+          }
+      }
+      //cant strip any of the type's generics
+      if (cl.asTypeInfo() != null){
+          if (cl.asTypeInfo().typeArguments() != null){
+              for (TypeInfo tInfo : cl.asTypeInfo().typeArguments()){
+                  if (tInfo.asClassInfo() != null){
+                      cantStripThis(tInfo.asClassInfo(), notStrippable, "4:" + cl.qualifiedName());
+                  }
+              }
+          }
+      }
+      //cant strip any of the annotation elements
+      //cantStripThis(cl.annotationElements(), notStrippable);
+      // take care of methods
+      cantStripThis(cl.allSelfMethods(), notStrippable);
+      cantStripThis(cl.allConstructors(), notStrippable);
+      // blow the outer class open if this is an inner class
+      if(cl.containingClass() != null){
+          cantStripThis(cl.containingClass(), notStrippable, "5:" + cl.qualifiedName());
+      }
+      // blow open super class and interfaces
+     ClassInfo supr = cl.realSuperclass();
+      if (supr != null) {
+          if (supr.isHidden()) {
+              // cl is a public class declared as extending a hidden superclass.
+              // this is not a desired practice but it's happened, so we deal
+              // with it by stripping off the superclass relation for purposes of
+              // generating the doc & stub information, and proceeding normally.
+              cl.init(cl.asTypeInfo(), cl.realInterfaces(), cl.realInterfaceTypes(),
+                      cl.innerClasses(), cl.allConstructors(), cl.allSelfMethods(),
+                      cl.annotationElements(), cl.allSelfFields(), cl.enumConstants(),
+                      cl.containingPackage(), cl.containingClass(),
+                      null, null, cl.annotations());
+              Errors.error(Errors.HIDDEN_SUPERCLASS,
+                      cl.position(), "Public class " + cl.qualifiedName()
+                      + " stripped of unavailable superclass "
+                      + supr.qualifiedName());
+          } else {
+              cantStripThis(supr, notStrippable, "6:" + cl.realSuperclass().name()
+                      + cl.qualifiedName());
+          }
+      }
+    }
+
+    private static void cantStripThis(MethodInfo[] mInfos , HashSet<ClassInfo> notStrippable) {
+      //for each method, blow open the parameters, throws and return types.  also blow open their generics
+      if (mInfos != null){
+          for (MethodInfo mInfo : mInfos){
+              if (mInfo.getTypeParameters() != null){
+                  for (TypeInfo tInfo : mInfo.getTypeParameters()){
+                      if (tInfo.asClassInfo() != null){
+                          cantStripThis(tInfo.asClassInfo(), notStrippable, "8:" +
+                                        mInfo.realContainingClass().qualifiedName() + ":" +
+                                        mInfo.name());
+                      }
+                  }
+              }
+              if (mInfo.parameters() != null){
+                  for (ParameterInfo pInfo : mInfo.parameters()){
+                      if (pInfo.type() != null && pInfo.type().asClassInfo() != null){
+                          cantStripThis(pInfo.type().asClassInfo(), notStrippable,
+                                        "9:"+  mInfo.realContainingClass().qualifiedName()
+                                        + ":" + mInfo.name());
+                          if (pInfo.type().typeArguments() != null){
+                              for (TypeInfo tInfoType : pInfo.type().typeArguments()){
+                                  if (tInfoType.asClassInfo() != null){
+                                      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());
+                                      }
+                                  }
+                              }
+                          }
+                      }
+                  }
+              }
+              for (ClassInfo thrown : mInfo.thrownExceptions()){
+                  cantStripThis(thrown, notStrippable, "11:" +
+                                mInfo.realContainingClass().qualifiedName()
+                                +":" + mInfo.name());
+              }
+              if (mInfo.returnType() != null && mInfo.returnType().asClassInfo() != null){
+                  cantStripThis(mInfo.returnType().asClassInfo(), notStrippable,
+                                "12:" + mInfo.realContainingClass().qualifiedName() +
+                                ":" + mInfo.name());
+                  if (mInfo.returnType().typeArguments() != null){
+                      for (TypeInfo tyInfo: mInfo.returnType().typeArguments() ){
+                          if (tyInfo.asClassInfo() != null){
+                              cantStripThis(tyInfo.asClassInfo(), notStrippable,
+                                            "13:" +
+                                            mInfo.realContainingClass().qualifiedName()
+                                            + ":" + mInfo.name());
+                          }
+                      }
+                  }
+              }
+          }
+      }
+    }
+
+    static String javaFileName(ClassInfo cl) {
+        String dir = "";
+        PackageInfo pkg = cl.containingPackage();
+        if (pkg != null) {
+            dir = pkg.name();
+            dir = dir.replace('.', '/') + '/';
+        }
+        return dir + cl.name() + ".java";
+    }
+
+    static void writeClassFile(String stubsDir, ClassInfo cl) {
+        // inner classes are written by their containing class
+        if (cl.containingClass() != null) {
+            return;
+        }
+
+        String filename = stubsDir + '/' + javaFileName(cl);
+        File file = new File(filename);
+        ClearPage.ensureDirectory(file);
+
+        PrintStream stream = null;
+        try {
+            stream = new PrintStream(file);
+            writeClassFile(stream, cl);
+        }
+        catch (FileNotFoundException e) {
+            System.err.println("error writing file: " + filename);
+        }
+        finally {
+            if (stream != null) {
+                stream.close();
+            }
+        }
+    }
+
+    static void writeClassFile(PrintStream stream, ClassInfo cl) {
+        PackageInfo pkg = cl.containingPackage();
+        if (pkg != null) {
+            stream.println("package " + pkg.name() + ";");
+        }
+        writeClass(stream, cl);
+    }
+
+    static void writeClass(PrintStream stream, ClassInfo cl) {
+        writeAnnotations(stream, cl.annotations());
+
+        stream.print(DroidDoc.scope(cl) + " ");
+        if (cl.isAbstract() && !cl.isAnnotation() && !cl.isInterface()) {
+            stream.print("abstract ");
+        }
+        if (cl.isStatic()){
+            stream.print("static ");
+        }
+        if (cl.isFinal() && !cl.isEnum()) {
+            stream.print("final ");
+        }
+        if (false) {
+            stream.print("strictfp ");
+        }
+
+        HashSet<String> classDeclTypeVars = new HashSet();
+        String leafName = cl.asTypeInfo().fullName(classDeclTypeVars);
+        int bracket = leafName.indexOf('<');
+        if (bracket < 0) bracket = leafName.length() - 1;
+        int period = leafName.lastIndexOf('.', bracket);
+        if (period < 0) period = -1;
+        leafName = leafName.substring(period+1);
+
+        String kind = cl.kind();
+        stream.println(kind + " " + leafName);
+
+        TypeInfo base = cl.superclassType();
+
+        if (!"enum".equals(kind)) {
+            if (base != null && !"java.lang.Object".equals(base.qualifiedTypeName())) {
+                stream.println("  extends " + base.fullName(classDeclTypeVars));
+            }
+        }
+
+        TypeInfo[] interfaces = cl.realInterfaceTypes();
+        List<TypeInfo> usedInterfaces = new ArrayList<TypeInfo>();
+        for (TypeInfo iface : interfaces) {
+            if (notStrippable.contains(iface.asClassInfo())
+                    && !iface.asClassInfo().isDocOnly()) {
+                usedInterfaces.add(iface);
+            }
+        }
+        if (usedInterfaces.size() > 0 && !cl.isAnnotation()) {
+            // can java annotations extend other ones?
+            if (cl.isInterface() || cl.isAnnotation()) {
+                stream.print("  extends ");
+            } else {
+                stream.print("  implements ");
+            }
+            String comma = "";
+            for (TypeInfo iface: usedInterfaces) {
+                stream.print(comma + iface.fullName(classDeclTypeVars));
+                comma = ", ";
+            }
+            stream.println();
+        }
+
+        stream.println("{");
+
+        FieldInfo[] enumConstants = cl.enumConstants();
+        int N = enumConstants.length;
+        for (int i=0; i<N; i++) {
+            FieldInfo field = enumConstants[i];
+            if (!field.constantLiteralValue().equals("null")){
+            stream.println(field.name() + "(" + field.constantLiteralValue()
+                    + (i==N-1 ? ");" : "),"));
+            }else{
+              stream.println(field.name() + "(" + (i==N-1 ? ");" : "),"));
+            }
+        }
+
+        for (ClassInfo inner: cl.getRealInnerClasses()) {
+            if (notStrippable.contains(inner)
+                    && !inner.isDocOnly()){
+                writeClass(stream, inner);
+            }
+        }
+
+
+        for (MethodInfo method: cl.constructors()) {
+            if (!method.isDocOnly()) {
+                writeMethod(stream, method, true);
+            }
+        }
+
+        boolean fieldNeedsInitialization = false;
+        boolean staticFieldNeedsInitialization = false;
+        for (FieldInfo field: cl.allSelfFields()) {
+            if (!field.isDocOnly()) {
+                if (!field.isStatic() && field.isFinal() && !fieldIsInitialized(field)) {
+                    fieldNeedsInitialization = true;
+                }
+                if (field.isStatic() && field.isFinal() && !fieldIsInitialized(field)) {
+                    staticFieldNeedsInitialization = true;
+                }
+            }
+        }
+
+        // The compiler includes a default public constructor that calls the super classes
+        // default constructor in the case where there are no written constructors.
+        // So, if we hide all the constructors, java may put in a constructor
+        // that calls a nonexistent super class constructor.  So, if there are no constructors,
+        // and the super class doesn't have a default constructor, write in a private constructor
+        // that works.  TODO -- we generate this as protected, but we really should generate
+        // it as private unless it also exists in the real code.
+        if ((cl.constructors().length == 0 && (cl.getNonWrittenConstructors().length != 0
+                    || fieldNeedsInitialization))
+                && !cl.isAnnotation()
+                && !cl.isInterface()
+                && !cl.isEnum() ) {
+            //Errors.error(Errors.HIDDEN_CONSTRUCTOR,
+            //             cl.position(), "No constructors " +
+            //            "found and superclass has no parameterless constructor.  A constructor " +
+            //            "that calls an appropriate superclass constructor " +
+            //            "was automatically written to stubs.\n");
+            stream.println(cl.leafName()
+                    + "() { " + superCtorCall(cl,null)
+                    + "throw new" + " RuntimeException(\"Stub!\"); }");
+        }
+
+        for (MethodInfo method: cl.allSelfMethods()) {
+            if (cl.isEnum()) {
+                if (("values".equals(method.name())
+                            && "()".equals(method.signature()))
+                    || ("valueOf".equals(method.name())
+                            && "(java.lang.String)".equals(method.signature()))) {
+                    // skip these two methods on enums, because they're synthetic,
+                    // although for some reason javadoc doesn't mark them as synthetic,
+                    // maybe because they still want them documented
+                    continue;
+                }
+            }
+            if (!method.isDocOnly()) {
+                writeMethod(stream, method, false);
+            }
+        }
+        //Write all methods that are hidden, but override abstract methods or interface methods.
+        //These can't be hidden.
+        for (MethodInfo method : cl.getHiddenMethods()){
+            MethodInfo overriddenMethod = method.findRealOverriddenMethod(method.name(), method.signature(), notStrippable);
+            ClassInfo classContainingMethod = method.findRealOverriddenClass(method.name(),
+                                                                             method.signature());
+            if (overriddenMethod != null && !overriddenMethod.isHidden()
+                && !overriddenMethod.isDocOnly() &&
+                (overriddenMethod.isAbstract() ||
+                overriddenMethod.containingClass().isInterface())) {
+                method.setReason("1:" + classContainingMethod.qualifiedName());
+                cl.addMethod(method);
+                writeMethod(stream, method, false);
+            }
+        }
+
+        for (MethodInfo element: cl.annotationElements()) {
+            if (!element.isDocOnly()) {
+                writeAnnotationElement(stream, element);
+            }
+        }
+
+        for (FieldInfo field: cl.allSelfFields()) {
+            if (!field.isDocOnly()) {
+                writeField(stream, field);
+            }
+        }
+
+        if (staticFieldNeedsInitialization) {
+            stream.print("static { ");
+            for (FieldInfo field: cl.allSelfFields()) {
+                if (!field.isDocOnly() && field.isStatic() && field.isFinal()
+                        && !fieldIsInitialized(field) && field.constantValue() == null) {
+                    stream.print(field.name() + " = " + field.type().defaultValue()
+                            + "; ");
+                }
+            }
+            stream.println("}");
+        }
+
+        stream.println("}");
+    }
+
+
+    static void writeMethod(PrintStream stream, MethodInfo method, boolean isConstructor) {
+        String comma;
+
+        stream.print(DroidDoc.scope(method) + " ");
+        if (method.isStatic()) {
+            stream.print("static ");
+        }
+        if (method.isFinal()) {
+            stream.print("final ");
+        }
+        if (method.isAbstract()) {
+            stream.print("abstract ");
+        }
+        if (method.isSynchronized()) {
+            stream.print("synchronized ");
+        }
+        if (method.isNative()) {
+            stream.print("native ");
+        }
+        if (false /*method.isStictFP()*/) {
+            stream.print("strictfp ");
+        }
+
+        stream.print(method.typeArgumentsName(new HashSet()) + " ");
+
+        if (!isConstructor) {
+            stream.print(method.returnType().fullName(method.typeVariables()) + " ");
+        }
+        String n = method.name();
+        int pos = n.lastIndexOf('.');
+        if (pos >= 0) {
+            n = n.substring(pos + 1);
+        }
+        stream.print(n + "(");
+        comma = "";
+        int count = 1;
+        int size = method.parameters().length;
+        for (ParameterInfo param: method.parameters()) {
+            stream.print(comma + fullParameterTypeName(method, param.type(), count == size)
+                    + " " + param.name());
+            comma = ", ";
+            count++;
+        }
+        stream.print(")");
+
+        comma = "";
+        if (method.thrownExceptions().length > 0) {
+            stream.print(" throws ");
+            for (ClassInfo thrown: method.thrownExceptions()) {
+                stream.print(comma + thrown.qualifiedName());
+                comma = ", ";
+            }
+        }
+        if (method.isAbstract() || method.isNative() || method.containingClass().isInterface()) {
+            stream.println(";");
+        } else {
+            stream.print(" { ");
+            if (isConstructor) {
+                stream.print(superCtorCall(method.containingClass(), method.thrownExceptions()));
+            }
+            stream.println("throw new RuntimeException(\"Stub!\"); }");
+        }
+    }
+
+    static void writeField(PrintStream stream, FieldInfo field) {
+        stream.print(DroidDoc.scope(field) + " ");
+        if (field.isStatic()) {
+            stream.print("static ");
+        }
+        if (field.isFinal()) {
+            stream.print("final ");
+        }
+        if (field.isTransient()) {
+            stream.print("transient ");
+        }
+        if (field.isVolatile()) {
+            stream.print("volatile ");
+        }
+
+        stream.print(field.type().fullName());
+        stream.print(" ");
+        stream.print(field.name());
+
+        if (fieldIsInitialized(field)) {
+            stream.print(" = " + field.constantLiteralValue());
+        }
+
+        stream.println(";");
+    }
+
+    static boolean fieldIsInitialized(FieldInfo field) {
+        return (field.isFinal() && field.constantValue() != null)
+                || !field.type().dimension().equals("")
+                || field.containingClass().isInterface();
+    }
+
+    // Returns 'true' if the method is an @Override of a visible parent
+    // method implementation, and thus does not affect the API.
+    static boolean methodIsOverride(MethodInfo mi) {
+        // Abstract/static/final methods are always listed in the API description
+        if (mi.isAbstract() || mi.isStatic() || mi.isFinal()) {
+            return false;
+        }
+
+        // Find any relevant ancestor declaration and inspect it
+        MethodInfo om = mi.findSuperclassImplementation(notStrippable);
+        if (om != null) {
+            // Visibility mismatch is an API change, so check for it
+            if (mi.mIsPrivate == om.mIsPrivate
+                    && mi.mIsPublic == om.mIsPublic
+                    && mi.mIsProtected == om.mIsProtected) {
+                // Look only for overrides of an ancestor class implementation,
+                // not of e.g. an abstract or interface method declaration
+                if (!om.isAbstract()) {
+                    // If the parent is hidden, we can't rely on it to provide
+                    // the API
+                    if (!om.isHidden()) {
+                        // If the only "override" turns out to be in our own class
+                        // (which sometimes happens in concrete subclasses of
+                        // abstract base classes), it's not really an override
+                        if (!mi.mContainingClass.equals(om.mContainingClass)) {
+                            return true;
+                        }
+                    }
+                }
+            }
+        }
+        return false;
+    }
+
+    static boolean canCallMethod(ClassInfo from, MethodInfo m) {
+        if (m.isPublic() || m.isProtected()) {
+            return true;
+        }
+        if (m.isPackagePrivate()) {
+            String fromPkg = from.containingPackage().name();
+            String pkg = m.containingClass().containingPackage().name();
+            if (fromPkg.equals(pkg)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    // call a constructor, any constructor on this class's superclass.
+    static String superCtorCall(ClassInfo cl, ClassInfo[] thrownExceptions) {
+        ClassInfo base = cl.realSuperclass();
+        if (base == null) {
+            return "";
+        }
+        HashSet<String> exceptionNames = new HashSet<String>();
+        if (thrownExceptions != null ){
+            for (ClassInfo thrown : thrownExceptions){
+              exceptionNames.add(thrown.name());
+            }
+        }
+        MethodInfo[] ctors = base.constructors();
+        MethodInfo ctor = null;
+        //bad exception indicates that the exceptions thrown by the super constructor
+        //are incompatible with the constructor we're using for the sub class.
+        Boolean badException = false;
+        for (MethodInfo m: ctors) {
+            if (canCallMethod(cl, m)) {
+                if (m.thrownExceptions() != null){
+                    for (ClassInfo thrown : m.thrownExceptions()){
+                        if (!exceptionNames.contains(thrown.name())){
+                            badException = true;
+                        }
+                    }
+                }
+                if (badException){
+                  badException = false;
+                  continue;
+                }
+                // if it has no args, we're done
+                if (m.parameters().length == 0) {
+                    return "";
+                }
+                ctor = m;
+            }
+        }
+        if (ctor != null) {
+            String result = "";
+            result+= "super(";
+            ParameterInfo[] params = ctor.parameters();
+            int N = params.length;
+            for (int i=0; i<N; i++) {
+                TypeInfo t = params[i].type();
+                if (t.isPrimitive() && t.dimension().equals("")) {
+                    String n = t.simpleTypeName();
+                    if (("byte".equals(n)
+                            || "short".equals(n)
+                            || "int".equals(n)
+                            || "long".equals(n)
+                            || "float".equals(n)
+                            || "double".equals(n)) && t.dimension().equals("")) {
+                        result += "0";
+                    }
+                    else if ("char".equals(n)) {
+                        result += "'\\0'";
+                    }
+                    else if ("boolean".equals(n)) {
+                        result += "false";
+                    }
+                    else {
+                        result += "<<unknown-" + n + ">>";
+                    }
+                } else {
+                    //put null in each super class method.  Cast null to the correct type
+                    //to avoid collisions with other constructors.  If the type is generic
+                    //don't cast it
+                    result += (!t.isTypeVariable() ? "(" + t.qualifiedTypeName() + t.dimension() +
+                              ")" : "") + "null";
+                }
+                if (i != N-1) {
+                    result += ",";
+                }
+            }
+            result += "); ";
+            return result;
+        } else {
+            return "";
+        }
+    }
+
+    static void writeAnnotations(PrintStream stream, AnnotationInstanceInfo[] annotations) {
+        for (AnnotationInstanceInfo ann: annotations) {
+            if (!ann.type().isHidden()) {
+                stream.println(ann.toString());
+            }
+        }
+    }
+
+    static void writeAnnotationElement(PrintStream stream, MethodInfo ann) {
+        stream.print(ann.returnType().fullName());
+        stream.print(" ");
+        stream.print(ann.name());
+        stream.print("()");
+        AnnotationValueInfo def = ann.defaultAnnotationElementValue();
+        if (def != null) {
+            stream.print(" default ");
+            stream.print(def.valueString());
+        }
+        stream.println(";");
+    }
+
+    static void writeXML(PrintStream xmlWriter, HashMap<PackageInfo, List<ClassInfo>> allClasses,
+                         HashSet notStrippable) {
+        // extract the set of packages, sort them by name, and write them out in that order
+        Set<PackageInfo> allClassKeys = allClasses.keySet();
+        PackageInfo[] allPackages = allClassKeys.toArray(new PackageInfo[allClassKeys.size()]);
+        Arrays.sort(allPackages, PackageInfo.comparator);
+
+        xmlWriter.println("<api>");
+        for (PackageInfo pack : allPackages) {
+            writePackageXML(xmlWriter, pack, allClasses.get(pack), notStrippable);
+        }
+        xmlWriter.println("</api>");
+    }
+
+    static void writePackageXML(PrintStream xmlWriter, PackageInfo pack, List<ClassInfo> classList,
+                                HashSet notStrippable) {
+        ClassInfo[] classes = classList.toArray(new ClassInfo[classList.size()]);
+        Arrays.sort(classes, ClassInfo.comparator);
+        xmlWriter.println("<package name=\"" + pack.name() + "\"\n"
+                //+ " source=\"" + pack.position() + "\"\n"
+                + ">");
+        for (ClassInfo cl : classes) {
+            writeClassXML(xmlWriter, cl, notStrippable);
+        }
+        xmlWriter.println("</package>");
+
+
+    }
+
+    static void writeClassXML(PrintStream xmlWriter, ClassInfo cl, HashSet notStrippable) {
+        String scope = DroidDoc.scope(cl);
+        String deprecatedString = "";
+        String declString = (cl.isInterface()) ? "interface" : "class";
+        if (cl.isDeprecated()) {
+            deprecatedString = "deprecated";
+        } else {
+            deprecatedString = "not deprecated";
+        }
+        xmlWriter.println("<" + declString + " name=\"" + cl.name() + "\"");
+        if (!cl.isInterface() && !cl.qualifiedName().equals("java.lang.Object")) {
+            xmlWriter.println(" extends=\"" + ((cl.realSuperclass() == null)
+                            ? "java.lang.Object"
+                            : cl.realSuperclass().qualifiedName()) + "\"");
+        }
+        xmlWriter.println(" abstract=\"" + cl.isAbstract() + "\"\n"
+                + " static=\"" + cl.isStatic() + "\"\n"
+                + " final=\"" + cl.isFinal() + "\"\n"
+                + " deprecated=\"" + deprecatedString + "\"\n"
+                + " visibility=\"" + scope + "\"\n"
+                //+ " source=\"" + cl.position() + "\"\n"
+                + ">");
+
+        ClassInfo[] interfaces = cl.realInterfaces();
+        Arrays.sort(interfaces, ClassInfo.comparator);
+        for (ClassInfo iface : interfaces) {
+            if (notStrippable.contains(iface)) {
+                xmlWriter.println("<implements name=\"" + iface.qualifiedName() + "\">");
+                xmlWriter.println("</implements>");
+            }
+        }
+
+        MethodInfo[] constructors = cl.constructors();
+        Arrays.sort(constructors, MethodInfo.comparator);
+        for (MethodInfo mi : constructors) {
+            writeConstructorXML(xmlWriter, mi);
+        }
+
+        MethodInfo[] methods = cl.allSelfMethods();
+        Arrays.sort(methods, MethodInfo.comparator);
+        for (MethodInfo mi : methods) {
+            if (!methodIsOverride(mi)) {
+                writeMethodXML(xmlWriter, mi);
+            }
+        }
+
+        FieldInfo[] fields = cl.allSelfFields();
+        Arrays.sort(fields, FieldInfo.comparator);
+        for (FieldInfo fi : fields) {
+            writeFieldXML(xmlWriter, fi);
+        }
+        xmlWriter.println("</" + declString + ">");
+
+    }
+
+    static void writeMethodXML(PrintStream xmlWriter, MethodInfo mi) {
+        String scope = DroidDoc.scope(mi);
+
+        String deprecatedString = "";
+        if (mi.isDeprecated()) {
+            deprecatedString = "deprecated";
+        } else {
+            deprecatedString = "not deprecated";
+        }
+        xmlWriter.println("<method name=\"" + mi.name() + "\"\n"
+                + ((mi.returnType() != null)
+                        ? " return=\"" + makeXMLcompliant(fullParameterTypeName(mi, mi.returnType(), false)) + "\"\n"
+                        : "")
+                + " abstract=\"" + mi.isAbstract() + "\"\n"
+                + " native=\"" + mi.isNative() + "\"\n"
+                + " synchronized=\"" + mi.isSynchronized() + "\"\n"
+                + " static=\"" + mi.isStatic() + "\"\n"
+                + " final=\"" + mi.isFinal() + "\"\n"
+                + " deprecated=\""+ deprecatedString + "\"\n"
+                + " visibility=\"" + scope + "\"\n"
+                //+ " source=\"" + mi.position() + "\"\n"
+                + ">");
+
+        // write parameters in declaration order
+        int numParameters = mi.parameters().length;
+        int count = 0;
+        for (ParameterInfo pi : mi.parameters()) {
+            count++;
+            writeParameterXML(xmlWriter, mi, pi, count == numParameters);
+        }
+
+        // but write exceptions in canonicalized order
+        ClassInfo[] exceptions = mi.thrownExceptions();
+        Arrays.sort(exceptions, ClassInfo.comparator);
+        for (ClassInfo pi : exceptions) {
+          xmlWriter.println("<exception name=\"" + pi.name() +"\" type=\"" + pi.qualifiedName()
+                            + "\">");
+          xmlWriter.println("</exception>");
+        }
+        xmlWriter.println("</method>");
+    }
+
+    static void writeConstructorXML(PrintStream xmlWriter, MethodInfo mi) {
+        String scope = DroidDoc.scope(mi);
+        String deprecatedString = "";
+        if (mi.isDeprecated()) {
+            deprecatedString = "deprecated";
+        } else {
+            deprecatedString = "not deprecated";
+        }
+        xmlWriter.println("<constructor name=\"" + mi.name() + "\"\n"
+                + " type=\"" + mi.containingClass().qualifiedName() + "\"\n"
+                + " static=\"" + mi.isStatic() + "\"\n"
+                + " final=\"" + mi.isFinal() + "\"\n"
+                + " deprecated=\"" + deprecatedString + "\"\n"
+                + " visibility=\"" + scope +"\"\n"
+                //+ " 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) {
+            xmlWriter.println("<exception name=\"" + pi.name() +"\" type=\"" + pi.qualifiedName()
+                              + "\">");
+            xmlWriter.println("</exception>");
+        }
+        xmlWriter.println("</constructor>");
+  }
+
+    static void writeParameterXML(PrintStream xmlWriter, MethodInfo method,
+            ParameterInfo pi, boolean isLast) {
+        xmlWriter.println("<parameter name=\"" + pi.name() + "\" type=\"" +
+                makeXMLcompliant(fullParameterTypeName(method, pi.type(), isLast)) + "\">");
+        xmlWriter.println("</parameter>");
+    }
+
+    static void writeFieldXML(PrintStream xmlWriter, FieldInfo fi) {
+        String scope = DroidDoc.scope(fi);
+        String deprecatedString = "";
+        if (fi.isDeprecated()) {
+            deprecatedString = "deprecated";
+        } else {
+            deprecatedString = "not deprecated";
+        }
+        //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=\"" + fullTypeName + "\"\n"
+                          + " transient=\"" + fi.isTransient() + "\"\n"
+                          + " volatile=\"" + fi.isVolatile() + "\"\n"
+                          + (fieldIsInitialized(fi) ? " value=\"" + value + "\"\n" : "")
+                          + " static=\"" + fi.isStatic() + "\"\n"
+                          + " final=\"" + fi.isFinal() + "\"\n"
+                          + " deprecated=\"" + deprecatedString + "\"\n"
+                          + " visibility=\"" + scope + "\"\n"
+                          //+ " source=\"" + fi.position() + "\"\n"
+                          + ">");
+        xmlWriter.println("</field>");
+    }
+
+    static String makeXMLcompliant(String s) {
+        String returnString = "";
+        returnString = s.replaceAll("&", "&amp;");
+        returnString = returnString.replaceAll("<", "&lt;");
+        returnString = returnString.replaceAll(">", "&gt;");
+        returnString = returnString.replaceAll("\"", "&quot;");
+        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.fullNameNoDimension(method.typeVariables()) + "...";
+        }
+        return fullTypeName;
+    }
+}
diff --git a/tools/droiddoc/src/TagInfo.java b/tools/droiddoc/src/TagInfo.java
new file mode 100644
index 0000000..d25c500
--- /dev/null
+++ b/tools/droiddoc/src/TagInfo.java
@@ -0,0 +1,100 @@
+/*
+ * 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 org.clearsilver.CS;
+
+public class TagInfo
+{
+    private String mName;
+    private String mText;
+    private String mKind;
+    private SourcePositionInfo mPosition;
+
+    TagInfo(String n, String k, String t, SourcePositionInfo sp)
+    {
+        mName = n;
+        mText = t;
+        mKind = k;
+        mPosition = sp;
+    }
+
+    String name()
+    {
+        return mName;
+    }
+
+    String text()
+    {
+        return mText;
+    }
+
+    String kind()
+    {
+        return mKind;
+    }
+    
+    SourcePositionInfo position() {
+        return mPosition;
+    }
+
+    void setKind(String kind) {
+        mKind = kind;
+    }
+
+    public void makeHDF(HDF data, String base)
+    {
+        data.setValue(base + ".name", name());
+        data.setValue(base + ".text", text());
+        data.setValue(base + ".kind", kind());
+    }
+
+    public static void makeHDF(HDF data, String base, TagInfo[] tags)
+    {
+        makeHDF(data, base, tags, null, 0, 0);
+    }
+
+    public static void makeHDF(HDF data, String base, InheritedTags tags)
+    {
+        makeHDF(data, base, tags.tags(), tags.inherited(), 0, 0);
+    }
+
+    private static int makeHDF(HDF data, String base, TagInfo[] tags,
+                                InheritedTags inherited, int j, int depth)
+    {
+        int i;
+        int len = tags.length;
+        if (len == 0 && inherited != null) {
+            j = makeHDF(data, base, inherited.tags(), inherited.inherited(), j, depth+1);
+        } else {
+            for (i=0; i<len; i++, j++) {
+                TagInfo t = tags[i];
+                if (inherited != null && t.name().equals("@inheritDoc")) {
+                    j = makeHDF(data, base, inherited.tags(),
+                                    inherited.inherited(), j, depth+1);
+                } else {
+                    if (t.name().equals("@inheritDoc")) {
+                        Errors.error(Errors.BAD_INHERITDOC, t.mPosition,
+                                "@inheritDoc on class/method that is not inherited");
+                    }
+                    t.makeHDF(data, base + "." + j);
+                }
+            }
+        }
+        return j;
+    }
+}
+
diff --git a/tools/droiddoc/src/TextTagInfo.java b/tools/droiddoc/src/TextTagInfo.java
new file mode 100644
index 0000000..dcdfdd9
--- /dev/null
+++ b/tools/droiddoc/src/TextTagInfo.java
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+
+public class TextTagInfo extends TagInfo {
+    TextTagInfo(String n, String k, String t, SourcePositionInfo p) {
+        super(n, k, DroidDoc.escape(t), p);
+    }
+}
diff --git a/tools/droiddoc/src/ThrowsTagInfo.java b/tools/droiddoc/src/ThrowsTagInfo.java
new file mode 100644
index 0000000..318a57d
--- /dev/null
+++ b/tools/droiddoc/src/ThrowsTagInfo.java
@@ -0,0 +1,83 @@
+/*
+ * 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 org.clearsilver.CS;
+import java.util.regex.Pattern;
+import java.util.regex.Matcher;
+
+public class ThrowsTagInfo extends ParsedTagInfo
+{
+    static final Pattern PATTERN = Pattern.compile(
+                                "(\\S+)\\s+(.*)",
+                                Pattern.DOTALL);
+    private ClassInfo mException;
+
+    public ThrowsTagInfo(String name, String kind, String text,
+            ContainerInfo base, SourcePositionInfo sp)
+    {
+        super(name, kind, text, base, sp);
+
+        Matcher m = PATTERN.matcher(text);
+        if (m.matches()) {
+            setCommentText(m.group(2));
+            String className = m.group(1);
+            if (base instanceof ClassInfo) {
+                mException = ((ClassInfo)base).findClass(className);
+            }
+            if (mException == null) {
+                mException = Converter.obtainClass(className);
+            }
+        }
+    }
+
+    public ThrowsTagInfo(String name, String kind, String text,
+                            ClassInfo exception, String exceptionComment,
+                            ContainerInfo base, SourcePositionInfo sp)
+    {
+        super(name, kind, text, base, sp);
+        mException = exception;
+        setCommentText(exceptionComment);
+    }
+
+    public ClassInfo exception()
+    {
+        return mException;
+    }
+
+    public TypeInfo exceptionType()
+    {
+        if (mException != null) {
+            return mException.asTypeInfo();
+        } else {
+            return null;
+        }
+    }
+
+    public static void makeHDF(HDF data, String base, ThrowsTagInfo[] tags)
+    {
+        for (int i=0; i<tags.length; i++) {
+            TagInfo.makeHDF(data, base + '.' + i + ".comment",
+                    tags[i].commentTags());
+            if (tags[i].exceptionType() != null) {
+                tags[i].exceptionType().makeHDF(data, base + "." + i + ".type");
+            }
+        }
+    }
+
+    
+}
+
diff --git a/tools/droiddoc/src/TodoFile.java b/tools/droiddoc/src/TodoFile.java
new file mode 100644
index 0000000..ebef27e
--- /dev/null
+++ b/tools/droiddoc/src/TodoFile.java
@@ -0,0 +1,195 @@
+/*
+ * 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 org.clearsilver.CS;
+import java.util.*;
+import java.io.*;
+
+public class TodoFile {
+
+    public static final String MISSING = "No description text";
+
+    public static boolean areTagsUseful(InheritedTags tags) {
+        while (tags != null) {
+            if (areTagsUseful(tags.tags())) {
+                return true;
+            }
+            tags = tags.inherited();
+        }
+        return false;
+    }
+
+    public static boolean areTagsUseful(TagInfo[] tags) {
+        for (TagInfo t: tags) {
+            if ("Text".equals(t.name()) && t.text().trim().length() != 0) {
+                return true;
+            }
+            if ("@inheritDoc".equals(t.name())) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public static void setHDF(HDF data, String base, SourcePositionInfo pos, String name,
+            String descr) {
+        data.setValue(base + ".pos", pos.toString());
+        data.setValue(base + ".name", name);
+        data.setValue(base + ".descr", descr);
+    }
+
+    static class PackageStats {
+        String name;
+        public int total;
+        public int errors;
+    }
+
+    public static String percent(int a, int b) {
+        return ""+Math.round((((b-a)/(float)b))*100) + "%";
+    }
+
+    public static void writeTodoFile(String filename) {
+        HDF data = DroidDoc.makeHDF();
+        DroidDoc.setPageTitle(data, "Missing Documentation");
+        TreeMap<String,PackageStats> packageStats = new TreeMap<String,PackageStats>();
+
+        ClassInfo[] classes = Converter.rootClasses();
+        Arrays.sort(classes);
+
+        int classIndex = 0;
+        
+        for (ClassInfo cl: classes) {
+            if (cl.isHidden()) {
+                continue;
+            }
+
+            String classBase = "classes." + classIndex;
+
+            String base = classBase + ".errors.";
+            int errors = 0;
+            int total = 1;
+
+            if (!areTagsUseful(cl.inlineTags())) {
+                setHDF(data, base + errors, cl.position(), "&lt;class comment&gt;", MISSING);
+                errors++;
+            }
+
+
+            for (MethodInfo m: cl.constructors()) {
+                boolean good = true;
+                total++;
+                if (m.checkLevel()) {
+                    if (!areTagsUseful(m.inlineTags())) {
+                        setHDF(data, base + errors, m.position(), m.name() + m.prettySignature(),
+                                MISSING);
+                        good = false;
+                    }
+                }
+                if (!good) {
+                    errors++;
+                }
+            }
+            
+            for (MethodInfo m: cl.selfMethods()) {
+                boolean good = true;
+                total++;
+                if (m.checkLevel()) {
+                    if (!areTagsUseful(m.inlineTags())) {
+                        setHDF(data, base + errors, m.position(), m.name() + m.prettySignature(),
+                                MISSING);
+                        good = false;
+                    }
+                }
+                if (!good) {
+                    errors++;
+                }
+            }
+            
+
+            for (FieldInfo f: cl.enumConstants()) {
+                boolean good = true;
+                total++;
+                if (f.checkLevel()) {
+                    if (!areTagsUseful(f.inlineTags())) {
+                        setHDF(data, base + errors, f.position(), f.name(), MISSING);
+                        good = false;
+                    }
+                }
+                if (!good) {
+                    errors++;
+                }
+            }
+            
+            for (FieldInfo f: cl.selfFields()) {
+                boolean good = true;
+                total++;
+                if (f.checkLevel()) {
+                    if (!areTagsUseful(f.inlineTags())) {
+                        setHDF(data, base + errors, f.position(), f.name(), MISSING);
+                        good = false;
+                    }
+                }
+                if (!good) {
+                    errors++;
+                }
+            }
+
+            if (errors > 0) {
+                data.setValue(classBase + ".qualified", cl.qualifiedName());
+                data.setValue(classBase + ".errorCount", ""+errors);
+                data.setValue(classBase + ".totalCount", ""+total);
+                data.setValue(classBase + ".percentGood", percent(errors, total));
+            }
+
+            PackageInfo pkg = cl.containingPackage();
+            String pkgName = pkg != null ? pkg.name() : "";
+            PackageStats ps = packageStats.get(pkgName);
+            if (ps == null) {
+                ps = new PackageStats();
+                ps.name = pkgName;
+                packageStats.put(pkgName, ps);
+            }
+            ps.total += total;
+            ps.errors += errors;
+
+            classIndex++;
+        }
+
+        int allTotal = 0;
+        int allErrors = 0;
+
+        int i = 0;
+        for (PackageStats ps: packageStats.values()) {
+            data.setValue("packages." + i + ".name", ""+ps.name);
+            data.setValue("packages." + i + ".errorCount", ""+ps.errors);
+            data.setValue("packages." + i + ".totalCount", ""+ps.total);
+            data.setValue("packages." + i + ".percentGood", percent(ps.errors, ps.total));
+
+            allTotal += ps.total;
+            allErrors += ps.errors;
+
+            i++;
+        }
+
+        data.setValue("all.errorCount", ""+allErrors);
+        data.setValue("all.totalCount", ""+allTotal);
+        data.setValue("all.percentGood", percent(allErrors, allTotal));
+
+        ClearPage.write(data, "todo.cs", filename, true);
+    }
+}
+
diff --git a/tools/droiddoc/src/TypeInfo.java b/tools/droiddoc/src/TypeInfo.java
new file mode 100644
index 0000000..c1119de
--- /dev/null
+++ b/tools/droiddoc/src/TypeInfo.java
@@ -0,0 +1,289 @@
+/*
+ * 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 org.clearsilver.CS;
+import java.util.*;
+import java.io.*;
+
+public class TypeInfo
+{
+    public TypeInfo(boolean isPrimitive, String dimension,
+            String simpleTypeName, String qualifiedTypeName,
+            ClassInfo cl)
+    {
+        mIsPrimitive = isPrimitive;
+        mDimension = dimension;
+        mSimpleTypeName = simpleTypeName;
+        mQualifiedTypeName = qualifiedTypeName;
+        mClass = cl;
+    }
+
+    public ClassInfo asClassInfo()
+    {
+        return mClass;
+    }
+
+    public boolean isPrimitive()
+    {
+        return mIsPrimitive;
+    }
+
+    public String dimension()
+    {
+        return mDimension;
+    }
+
+    public String simpleTypeName()
+    {
+        return mSimpleTypeName;
+    }
+
+    public String qualifiedTypeName()
+    {
+        return mQualifiedTypeName;
+    }
+
+    public String fullName()
+    {
+        if (mFullName != null) {
+            return mFullName;
+        } else {
+            return fullName(new HashSet());
+        }
+    }
+
+    public static String typeArgumentsName(TypeInfo[] args, HashSet<String> typeVars)
+    {
+        String result = "<";
+        for (int i=0; i<args.length; i++) {
+            result += args[i].fullName(typeVars);
+            if (i != args.length-1) {
+                result += ", ";
+            }
+        }
+        result += ">";
+        return result;
+    }
+
+    public String fullName(HashSet<String> typeVars)
+    {
+        mFullName = fullNameNoDimension(typeVars) + mDimension;
+        return mFullName;
+    }
+
+    public String fullNameNoDimension(HashSet<String> typeVars)
+    {
+        String fullName = null;
+        if (mIsTypeVariable) {
+            if (typeVars.contains(mQualifiedTypeName)) {
+                // don't recurse forever with the parameters.  This handles
+                // Enum<K extends Enum<K>>
+                return mQualifiedTypeName;
+            }
+            typeVars.add(mQualifiedTypeName);
+        }
+/*
+        if (fullName != null) {
+            return fullName;
+        }
+*/
+        fullName = mQualifiedTypeName;
+        if (mTypeArguments != null && mTypeArguments.length > 0) {
+            fullName += typeArgumentsName(mTypeArguments, typeVars);
+        }
+        else if (mSuperBounds != null && mSuperBounds.length > 0) {
+            fullName += " super " + mSuperBounds[0].fullName(typeVars);
+            for (int i=1; i<mSuperBounds.length; i++) {
+                fullName += " & " + mSuperBounds[i].fullName(typeVars);
+            }
+        }
+        else if (mExtendsBounds != null && mExtendsBounds.length > 0) {
+            fullName += " extends " + mExtendsBounds[0].fullName(typeVars);
+            for (int i=1; i<mExtendsBounds.length; i++) {
+                fullName += " & " + mExtendsBounds[i].fullName(typeVars);
+            }
+        }
+        return fullName;
+    }
+
+    public TypeInfo[] typeArguments()
+    {
+        return mTypeArguments;
+    }
+
+    public void makeHDF(HDF data, String base)
+    {
+        makeHDFRecursive(data, base, false, false, new HashSet<String>());
+    }
+
+    public void makeQualifiedHDF(HDF data, String base)
+    {
+        makeHDFRecursive(data, base, true, false, new HashSet<String>());
+    }
+
+    public void makeHDF(HDF data, String base, boolean isLastVararg,
+            HashSet<String> typeVariables)
+    {
+        makeHDFRecursive(data, base, false, isLastVararg, typeVariables);
+    }
+
+    public void makeQualifiedHDF(HDF data, String base, HashSet<String> typeVariables)
+    {
+        makeHDFRecursive(data, base, true, false, typeVariables);
+    }
+
+    private void makeHDFRecursive(HDF data, String base, boolean qualified,
+            boolean isLastVararg, HashSet<String> typeVars)
+    {
+        String label = qualified ? qualifiedTypeName() : simpleTypeName();
+        label += (isLastVararg) ? "..." : dimension();
+        data.setValue(base + ".label", label);
+        ClassInfo cl = asClassInfo();
+        if (mIsTypeVariable || mIsWildcard) {
+            // could link to an @param tag on the class to describe this
+            // but for now, just don't make it a link
+        }
+        else if (!isPrimitive() && cl != null && cl.isIncluded()) {
+            data.setValue(base + ".link", cl.htmlPage());
+        }
+
+        if (mIsTypeVariable) {
+            if (typeVars.contains(qualifiedTypeName())) {
+                // don't recurse forever with the parameters.  This handles
+                // Enum<K extends Enum<K>>
+                return;
+            }
+            typeVars.add(qualifiedTypeName());
+        }
+        if (mTypeArguments != null) {
+            TypeInfo.makeHDF(data, base + ".typeArguments", mTypeArguments, qualified, typeVars);
+        }
+        if (mSuperBounds != null) {
+            TypeInfo.makeHDF(data, base + ".superBounds", mSuperBounds, qualified, typeVars);
+        }
+        if (mExtendsBounds != null) {
+            TypeInfo.makeHDF(data, base + ".extendsBounds", mExtendsBounds, qualified, typeVars);
+        }
+    }
+
+    public static void makeHDF(HDF data, String base, TypeInfo[] types, boolean qualified,
+            HashSet<String> typeVariables)
+    {
+        final int N = types.length;
+        for (int i=0; i<N; i++) {
+            types[i].makeHDFRecursive(data, base + "." + i, qualified, false, typeVariables);
+        }
+    }
+
+    public static void makeHDF(HDF data, String base, TypeInfo[] types, boolean qualified)
+    {
+        makeHDF(data, base, types, qualified, new HashSet<String>());
+    }
+
+    void setTypeArguments(TypeInfo[] args)
+    {
+        mTypeArguments = args;
+    }
+
+    void setBounds(TypeInfo[] superBounds, TypeInfo[] extendsBounds)
+    {
+        mSuperBounds = superBounds;
+        mExtendsBounds = extendsBounds;
+    }
+
+    void setIsTypeVariable(boolean b)
+    {
+        mIsTypeVariable = b;
+    }
+
+    void setIsWildcard(boolean b)
+    {
+        mIsWildcard = b;
+    }
+
+    static HashSet<String> typeVariables(TypeInfo[] params)
+    {
+        return typeVariables(params, new HashSet());
+    }
+
+    static HashSet<String> typeVariables(TypeInfo[] params, HashSet<String> result)
+    {
+        for (TypeInfo t: params) {
+            if (t.mIsTypeVariable) {
+                result.add(t.mQualifiedTypeName);
+            }
+        }
+        return result;
+    }
+
+
+    public boolean isTypeVariable()
+    {
+        return mIsTypeVariable;
+    }
+
+    public String defaultValue() {
+        if (mIsPrimitive) {
+            if ("boolean".equals(mSimpleTypeName)) {
+                return "false";
+            } else {
+                return "0";
+            }
+        } else {
+            return "null";
+        }
+    }
+
+    public String toString(){
+      String returnString = "";
+      returnString += "Primitive?: " + mIsPrimitive + " TypeVariable?: " +
+      mIsTypeVariable + " Wildcard?: " + mIsWildcard + " Dimension: " + mDimension
+      + " QualifedTypeName: " + mQualifiedTypeName;
+
+      if (mTypeArguments != null){
+        returnString += "\nTypeArguments: ";
+        for (TypeInfo tA : mTypeArguments){
+          returnString += tA.qualifiedTypeName() + "(" + tA + ") ";
+        }
+      }
+      if (mSuperBounds != null){
+        returnString += "\nSuperBounds: ";
+        for (TypeInfo tA : mSuperBounds){
+          returnString += tA.qualifiedTypeName() + "(" + tA + ") ";
+        }
+      }
+      if (mExtendsBounds != null){
+        returnString += "\nExtendsBounds: ";
+        for (TypeInfo tA : mExtendsBounds){
+          returnString += tA.qualifiedTypeName() + "(" + tA + ") ";
+        }
+      }
+      return returnString;
+    }
+
+    private boolean mIsPrimitive;
+    private boolean mIsTypeVariable;
+    private boolean mIsWildcard;
+    private String mDimension;
+    private String mSimpleTypeName;
+    private String mQualifiedTypeName;
+    private ClassInfo mClass;
+    private TypeInfo[] mTypeArguments;
+    private TypeInfo[] mSuperBounds;
+    private TypeInfo[] mExtendsBounds;
+    private String mFullName;
+}
diff --git a/tools/droiddoc/templates-sdk/assets-sdk/placeholder b/tools/droiddoc/templates-sdk/assets-sdk/placeholder
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tools/droiddoc/templates-sdk/assets-sdk/placeholder
diff --git a/tools/droiddoc/templates-sdk/customization.cs b/tools/droiddoc/templates-sdk/customization.cs
new file mode 100644
index 0000000..4a1dde6
--- /dev/null
+++ b/tools/droiddoc/templates-sdk/customization.cs
@@ -0,0 +1,121 @@
+<?cs # This default template file is meant to be replaced. ?>
+<?cs # Use the -tempatedir arg to javadoc to set your own directory with a replacement for this file in it. ?>
+
+<?cs 
+def:custom_masthead() ?>
+  <div id="header">
+      <div id="headerLeft">
+          <a href="<?cs var:toroot ?>index.html" tabindex="-1"><img
+              src="<?cs var:toroot ?>assets/images/bg_logo.png" alt="Android Developers" /></a>
+          <ul class="<?cs 
+                  if:reference ?>reference<?cs
+                  elif:guide ?>guide<?cs
+                  elif:sdk ?>sdk<?cs
+                  elif:home ?>home<?cs
+                  elif:community ?>community<?cs
+                  elif:publish ?>publish<?cs
+                  elif:about ?>about<?cs /if ?>">
+              <li id="home-link"><a href="<?cs var:toroot ?><?cs 
+                  if:android.whichdoc != "online" ?>offline.html<?cs 
+                  else ?>index.html<?cs /if ?>">
+                  <span>Home</span></a></li>
+              <li id="sdk-link"><a href="<?cs var:toroot ?>sdk/1.1_r1/index.html"><span>SDK</span></a></li>
+              <li id="guide-link"><a href="<?cs var:toroot ?>guide/index.html"
+                                  onClick="return loadLast('guide')"><span>Dev Guide</span></a></li>
+              <li id="reference-link"><a href="<?cs var:toroot ?>reference/packages.html" 
+                                  onClick="return loadLast('reference')"><span>Reference</span></a></li>
+              <li><a href="http://android-developers.blogspot.com"><span>Blog</span></a></li>
+              <li id="community-link"><a href="<?cs var:toroot ?>community/index.html"><span>Community</span></a></li>
+          </ul>
+      </div>
+      <div id="headerRight">
+          <div id="headerLinks">
+            <!-- <img src="<?cs var:toroot ?>assets/images/icon_world.jpg" alt="" /> -->
+            <span class="text">
+              <!-- &nbsp;<a href="#">English</a> | -->
+              <a href="http://www.android.com">Android.com</a>
+            </span>
+          </div><?cs 
+          call:default_search_box() ?>
+      </div><!-- headerRight -->
+  </div><!-- header --><?cs 
+/def ?><?cs # custom_masthead ?>
+
+<?cs 
+def:sdk_nav() ?>
+  <div class="g-section g-tpl-240" id="body-content">
+    <div class="g-unit g-first not-resizable" id="side-nav">
+      <div id="devdoc-nav"><?cs 
+        include:"../../../../frameworks/base/docs/html/sdk/sdk_toc.cs" ?>
+      </div>
+    </div> <!-- end side-nav -->
+<?cs /def ?>
+
+<?cs 
+def:guide_nav() ?>
+  <div class="g-section g-tpl-240" id="body-content">
+    <div class="g-unit g-first side-nav-resizable" id="side-nav">
+      <div id="devdoc-nav"><?cs 
+        include:"../../../../frameworks/base/docs/html/guide/guide_toc.cs" ?>
+      </div>
+    </div> <!-- end side-nav -->
+    <script>
+      addLoadEvent(function() {
+        scrollIntoView("devdoc-nav");
+        });
+    </script>
+<?cs /def ?>
+
+<?cs 
+def:publish_nav() ?>
+  <div class="g-section g-tpl-180" id="body-content">
+    <div class="g-unit g-first" id="side-nav">
+      <div id="devdoc-nav"><?cs 
+        include:"../../../../frameworks/base/docs/html/publish/publish_toc.cs" ?>
+      </div>
+    </div> <!-- end side-nav -->
+<?cs /def ?>
+
+<?cs 
+def:custom_left_nav() ?><?cs 
+  if:guide ?><?cs 
+    call:guide_nav() ?><?cs 
+  elif:publish ?><?cs 
+    call:publish_nav() ?><?cs 
+  elif:sdk ?><?cs 
+    call:sdk_nav() ?><?cs 
+  else ?><?cs 
+    call:default_left_nav() ?><?cs 
+  /if ?><?cs 
+/def ?>
+
+<?cs # appears at the bottom of every page ?><?cs 
+def:custom_cc_copyright() ?>
+  Except as noted, this content is 
+  licensed under <a href="http://creativecommons.org/licenses/by/2.5/">
+  Creative Commons Attribution 2.5</a>. For details and 
+  restrictions, see the <a href="<?cs var:toroot ?>license.html">Content 
+  License</a>.<?cs 
+/def ?>
+
+<?cs 
+def:custom_copyright() ?>
+  Except as noted, this content is licensed under <a
+  href="http://www.apache.org/licenses/LICENSE-2.0">Apache 2.0</a>. 
+  For details and restrictions, see the <a href="<?cs var:toroot ?>license.html">
+  Content License</a>.<?cs 
+/def ?>
+
+<?cs 
+def:custom_footerlinks() ?>
+  <p>
+    <a href="http://www.android.com/terms.html">Site Terms of Service</a> -
+    <a href="http://www.android.com/privacy.html">Privacy Policy</a> -
+    <a href="http://www.android.com/branding.html">Brand Guidelines</a>
+  </p><?cs 
+/def ?>
+
+<?cs # appears on the right side of the blue bar at the bottom of every page ?><?cs 
+def:custom_buildinfo() ?>
+  Android 1.1 r1 - <?cs var:page.now ?><?cs 
+/def ?>
diff --git a/tools/droiddoc/templates-sdk/data.hdf b/tools/droiddoc/templates-sdk/data.hdf
new file mode 100644
index 0000000..9411b78
--- /dev/null
+++ b/tools/droiddoc/templates-sdk/data.hdf
@@ -0,0 +1,4 @@
+template {
+    which = normal
+}
+
diff --git a/tools/droiddoc/templates-sdk/devdoc-nav.cs b/tools/droiddoc/templates-sdk/devdoc-nav.cs
new file mode 100644
index 0000000..a69c175
--- /dev/null
+++ b/tools/droiddoc/templates-sdk/devdoc-nav.cs
@@ -0,0 +1,66 @@
+<ul>
+  <li><div><a href="<?cs var:toroot ?>index.html">Home</a></div></li>
+  <li><div><a href="<?cs var:toroot ?>what-is-android.html">What is Android?</a></div></li>
+  <li><div><a href="<?cs var:toroot ?>intro/index.html">Getting Started</a></div>
+    <ul>
+      <li><div><a href="<?cs var:toroot ?>intro/installing.html">Installing the SDK</a></div></li>
+      <li><div><a href="<?cs var:toroot ?>intro/upgrading.html">Upgrading the SDK</a></div></li>
+      <li><div><a href="<?cs var:toroot ?>intro/develop-and-debug.html">Developing/Debugging</a></div></li>
+      <li><div><a href="<?cs var:toroot ?>intro/hello-android.html">Hello Android</a></div></li>
+      <li><div><a href="<?cs var:toroot ?>intro/anatomy.html">Anatomy of an App</a></div></li>
+      <li><div><a href="<?cs var:toroot ?>intro/tutorial.html">Notepad Tutorial</a></div></li>
+      <li><div><a href="<?cs var:toroot ?>intro/tools.html">Development Tools</a></div></li>
+      <li><div><a href="<?cs var:toroot ?>intro/appmodel.html">Application Model</a></div></li>
+      <li><div><a href="<?cs var:toroot ?>intro/lifecycle.html">Application Life Cycle</a></div></li>
+    </ul>
+  </li>
+  <li><div><div><a href="<?cs var:toroot ?>devel/index.html">Developing Applications</a></div>
+    <ul>
+      <li><div><a href="<?cs var:toroot ?>devel/implementing-ui.html">Implementing a UI</a></div></li>
+      <li><div><a href="<?cs var:toroot ?>devel/building-blocks.html">Building Blocks</a></div></li>
+      <li><div><a href="<?cs var:toroot ?>devel/data.html">Data Storage and Retrieval</a></div></li>
+      <li><div><a href="<?cs var:toroot ?>devel/security.html">Security Model</a></div></li>
+      <li><div><a href="<?cs var:toroot ?>devel/resources-i18n.html">Resources and i18n</a></div></li>
+    </ul>
+  </li>
+  <li><div><a href="<?cs var:toroot ?>toolbox/index.html">Developer Toolbox</a></div>
+    <ul>
+      <li><div><a href="<?cs var:toroot ?>toolbox/philosophy.html">Design Philosophy</a></div></li>
+      <li><div><a href="<?cs var:toroot ?>toolbox/custom-components.html">Building Custom Components</a></div></li>
+      <li><div><a href="<?cs var:toroot ?>toolbox/optional-apis.html">Optional APIs</a></div></li>
+    </ul>
+  </li>
+  <li><div><a href="<?cs var:toroot ?>samples/index.html">Sample Code</a></div>
+    <ul>
+      <li><div><a href="<?cs var:toroot ?>samples/ApiDemos/index.html">API Demos</a></div></li>
+      <li><div><a href="<?cs var:toroot ?>samples/LunarLander/index.html">Lunar Lander</a></div></li>
+      <li><div><a href="<?cs var:toroot ?>samples/NotePad/index.html">Note Pad</a></div></li>
+    </ul>
+  </li>
+  <li> <a href="<?cs var:toroot ?>reference/index.html"><strong>Reference Information</strong></a>
+    <ul>
+      <li><a href="<?cs var:toroot ?>reference/packages.html">Package Index</a></li>
+      <li><a href="<?cs var:toroot ?>reference/classes.html">Class Index</a></li>
+      <li><a href="<?cs var:toroot ?>reference/hierarchy.html">Class Hierarchy</a></li>
+      <li><a href="<?cs var:toroot ?>reference/view-gallery.html">List of Views</a></li>
+      <li><a href="<?cs var:toroot ?>reference/available-intents.html">List of Intents</a></li>
+      <li><a href="<?cs var:toroot ?>reference/android/Manifest.permission.html">List of Permissions</a></li>
+      <li><a href="<?cs var:toroot ?>reference/available-resources.html">List of Resource Types</a></li>
+      <li><a href="<?cs var:toroot ?>reference/aidl.html">Android IDL</a></li>
+      <li><a href="<?cs var:toroot ?>reference/glossary.html">Glossary</a></li>
+      <li><a href="<?cs var:toroot ?>reference/keywords.html">Index</a></li>
+    </ul>
+  </li>
+  <li><div><a href="<?cs var:toroot ?>kb/index.html">FAQs</a></div>
+    <ul>
+      <li><div><a href="<?cs var:toroot ?>kb/general.html">General</a></div></li>
+      <li><div><a href="<?cs var:toroot ?>kb/commontasks.html">Common Tasks</a></div></li>
+      <li><div><a href="<?cs var:toroot ?>kb/troubleshooting.html">Troubleshooting</a></div></li>
+      <li><div><a href="<?cs var:toroot ?>kb/licensingandoss.html">Open Source Licensing</a></div></li>
+      <li><div><a href="<?cs var:toroot ?>kb/framework.html">Application Framework</a></div></li>
+      <li><div><a href="<?cs var:toroot ?>kb/security.html">Security</a></div></li>
+    </ul>
+  </li>
+  <li><div><a href="<?cs var:toroot ?>roadmap.html">Roadmap</a></div></li>
+  <li><div><a href="<?cs var:toroot ?>goodies/index.html">Goodies</a></div></li>
+</ul>
\ No newline at end of file
diff --git a/tools/droiddoc/templates-sdk/sdkpage.cs b/tools/droiddoc/templates-sdk/sdkpage.cs
new file mode 100644
index 0000000..b425da0
--- /dev/null
+++ b/tools/droiddoc/templates-sdk/sdkpage.cs
@@ -0,0 +1,108 @@
+<?cs include:"doctype.cs" ?>
+<?cs include:"macros.cs" ?>
+<html>
+<?cs if:sdk.redirect ?>
+  <head>
+    <title>Redirecting...</title>
+    <meta http-equiv="refresh" content="0;url=<?cs var:toroot ?>sdk/<?cs var:sdk.redirect ?>/index.html">
+    <link href="<?cs var:toroot ?>assets/android-developer-docs.css" rel="stylesheet" type="text/css" />
+  </head>
+<?cs else ?>
+  <?cs include:"head_tag.cs" ?>
+<?cs /if ?>
+<body class="gc-documentation">
+<a name="top"></a>
+<?cs call:custom_masthead() ?>
+
+<?cs call:sdk_nav() ?>
+
+  
+<div class="g-unit" id="doc-content" >
+
+<?cs if:sdk.redirect ?>
+  Redirecting to 
+  <a href="<?cs var:toroot ?>sdk/<?cs var:sdk.redirect ?>/index.html">
+  <?cs var:toroot ?>sdk/<?cs var:sdk.redirect ?>/index.html
+  </a>...
+<?cs else ?>
+  
+  <div id="jd-header" class="guide-header" >
+    <span class="crumb">&nbsp;</span>
+    <h1><?cs if:android.whichdoc == "online" ?>Download <?cs /if ?><?cs var:page.title ?></h1>
+  </div>
+
+
+<div id="jd-content">
+
+    <p><em>
+    <?cs var:sdk.date ?>
+    </em></p>
+
+<?cs if:sdk.not_latest_version ?>
+  <div class="special">
+    <p><strong>This is NOT the current Android SDK release.</strong></p>
+    <p>Use the links under <strong>Current SDK Release</strong>, on the left, to be directed to the current SDK.</p>
+  </div>
+<?cs /if ?>
+  
+  
+<?cs if:android.whichdoc != "online" ?>
+
+<p>The sections below provide an overview of the SDK package. </p>
+
+<?cs else ?>
+
+<p>Before downloading, please read the <a href="<?cs var:toroot ?>sdk/<?cs var:sdk.version ?>/requirements.html">
+System Requirements</a> document. As you start the download, you will also need to review and agree to 
+the Terms and Conditions that govern the use of the Android SDK. </p>
+  
+  <table class="download">
+    <tr>
+      <th>Platform</th>
+      <th>Package</th>
+      <th>Size</th>
+      <th>MD5 Checksum</th>
+  </tr>
+  <tr>
+    <td>Windows</td>
+    <td>
+  <a href="<?cs var:toroot ?>sdk/download.html?v=<?cs var:sdk.win_download ?>"><?cs var:sdk.win_download ?></a>
+    </td>
+    <td><?cs var:sdk.win_bytes ?> bytes</td>
+    <td><?cs var:sdk.win_checksum ?></td>
+  </tr>
+  <tr class="alt-color">
+    <td>Mac OS X (intel)</td>
+    <td>
+  <a href="<?cs var:toroot ?>sdk/download.html?v=<?cs var:sdk.mac_download ?>"><?cs var:sdk.mac_download ?></a>
+    </td>
+    <td><?cs var:sdk.mac_bytes ?> bytes</td>
+    <td><?cs var:sdk.mac_checksum ?></td>
+  </tr>
+  <tr>
+    <td>Linux (i386)</td>
+    <td>
+  <a href="<?cs var:toroot ?>sdk/download.html?v=<?cs var:sdk.linux_download ?>"><?cs var:sdk.linux_download ?></a>
+    </td>
+    <td><?cs var:sdk.linux_bytes ?> bytes</td>
+    <td><?cs var:sdk.linux_checksum ?></td>
+  </tr>
+  </table>
+
+<?cs /if ?>
+
+      <?cs call:tag_list(root.descr) ?>
+
+<?cs /if ?>
+</div><!-- end jd-content -->
+
+<?cs include:"footer.cs" ?>
+</div><!-- end doc-content -->
+
+<?cs include:"trailer.cs" ?>
+
+</body>
+</html>
+
+
+
diff --git a/tools/droiddoc/templates/assets/android-developer-core.css b/tools/droiddoc/templates/assets/android-developer-core.css
new file mode 100644
index 0000000..eaa1176
--- /dev/null
+++ b/tools/droiddoc/templates/assets/android-developer-core.css
@@ -0,0 +1,909 @@
+/* file: android-developer-core.css
+   author: smain
+   date: september 2008
+   info: core developer styles (developer.android.com)
+*/
+
+
+/* RESET STYLES */
+
+html,body,div,h1,h2,h3,h4,h5,h6,p,img,
+dl,dt,dd,ol,ul,li,table,caption,tbody,
+tfoot,thead,tr,th,td,form,fieldset,
+embed,object,applet {
+  margin: 0;
+  padding: 0;
+  border: 0;
+}
+
+/* BASICS */
+
+html, body {
+  overflow:hidden; /* keeps scrollbar off IE */
+  background-color:#fff;
+}
+
+body {
+  font-family:arial,sans-serif;
+  color:#000;
+  font-size:13px;
+  color:#333;
+  background-image:url(images/bg_fade.jpg); 
+  background-repeat:repeat-x;
+}
+
+a, a code { 
+  color:#006699;
+} 
+
+
+a:active,
+a:active code { 
+  color:#f00;
+} 
+
+a:visited,
+a:visited code { 
+  color:#006699;
+}
+
+input, select,
+textarea, option {
+  font-family:inherit;
+  font-size:inherit;
+}
+
+p {
+  padding:0;
+  margin:0 0 1em;
+}
+
+code, pre {
+  color:#007000;
+  font-family:monospace;
+  line-height:1em;
+}
+
+var {
+  color:#007000;
+  font-style:italic;
+}
+
+pre {
+  border:1px solid #ccc;
+  background-color:#fafafa;
+  padding:10px;
+  margin:0 0 1em 1em;
+  overflow:auto;
+}
+
+h1,h2,h3,h4,h5 {
+  margin:1em 0;
+  padding:0;
+}
+
+p,ul,ol,dl,dd,dt,li {
+  line-height:1.3em;
+}
+
+ul,ol {
+  margin:0 0 .8em;
+  padding:0 0 0 2em;
+}
+
+li {
+  padding:0 0 .5em;
+}
+
+dl {
+  margin:0 0 1em 0;
+  padding:0;
+}
+
+dt {  
+  margin:0;
+  padding:0;
+}
+
+dd {
+  margin:0 0 1em;
+  padding:0 0 0 2em;
+}
+
+li p, dd p {
+  margin:1em 0 0;
+}
+
+li pre, li table, li img,
+dd pre, dd table, dd img {
+  margin:1em 0 0 1em;
+}
+
+li ul,
+li ol {
+  margin:.5em 0 0 0;
+  padding: 0 0 0 2em;
+}
+
+dl li {
+  padding:.5em 0 0 0;
+}
+
+dl dl,
+ol dl,
+ul dl {
+  margin:0 0 1em;
+  padding:0;
+}
+
+table {
+  font-size:1em;
+  margin:0 0 1em;
+  padding:0;
+  border-collapse:collapse;
+  border-width:0;
+  empty-cells:show;
+}
+
+td,th {
+  border:1px solid #ccc;
+  padding:6px 12px;
+  text-align:left;
+  vertical-align:top;
+  background-color:inherit;
+}
+
+th {
+  background-color:#dee8f1;
+}
+
+hr.blue {
+  background-color:#DDF0F2;
+  border:none;
+  height:5px;
+  margin:20px 0 10px;
+}
+
+/* LAYOUT */
+#body-content {
+  margin:0;
+  position:relative;
+  width:100%;
+  background: url('images/preliminary.png');
+}
+
+#header {
+  height: 114px;
+  position:relative;
+  z-index:100;
+  min-width:576px;
+  padding:0 10px;
+  border-bottom:3px solid #94b922;
+}
+
+#headerLeft{
+  padding: 25px 0 0;
+}
+
+#headerRight {
+  position:absolute;
+  right:0;
+  top:0;
+  text-align:right;
+}
+
+/* Tabs in the header */
+#header ul {
+  list-style: none;
+  margin: 7px 0 0;  
+  padding: 0;
+  height: 29px;
+}
+
+#header li {
+  float: left;
+  margin: 0px 2px 0px 0px;
+  padding:0;
+}
+
+#header li a {
+  text-decoration: none;
+  display: block;
+  background-image: url(images/bg_images_sprite.png);
+  background-position: 0 -58px;
+  background-repeat: no-repeat;
+  color: #666;
+  font-size: 13px;
+  font-weight: bold;
+  width: 94px;
+  height: 29px;
+  text-align: center;
+  margin: 0px;
+}
+
+#header li a:hover {
+  background-image: url(images/bg_images_sprite.png);
+  background-position: 0 -29px;
+  background-repeat: no-repeat;
+}
+
+#header li a span {
+  position:relative;
+  top:7px;
+}
+
+/* TAB HIGHLIGHTING */
+.home #home-link a,
+.publish #publish-link a,
+.guide #guide-link a,
+.reference #reference-link a,
+.sdk #sdk-link a,
+.community #community-link a,
+.about #about-link a {
+  background-image: url(images/bg_images_sprite.png);
+  background-position: 0 0;
+  background-repeat: no-repeat;
+  color: #fff;
+  font-weight: bold;
+  cursor:default;
+}
+
+.home #home-link a:hover,
+.publish #publish-link a:hover,
+.guide #guide-link a:hover,
+.reference #reference-link a:hover,
+.sdk #sdk-link a:hover,
+.community #community-link a:hover,
+.about #about-link  a:hover {
+  background-image: url(images/bg_images_sprite.png);
+  background-position: 0 0;
+}
+
+#headerLinks {
+  margin:10px 10px 0 0;
+  height:13px;
+}
+
+#headerLinks .text {
+  text-decoration: none;
+  color: #7FA9B5;
+  font-size: 11px;
+  vertical-align: top;
+}
+
+#headerLinks a {
+  text-decoration: underline;
+  color: #7FA9B5;
+  font-size: 11px;
+  vertical-align: top;
+}
+
+#search {
+  height:45px;
+  margin:15px 10px 0 0;
+}
+
+/* main */
+
+#mainBodyFluid {
+  margin: 20px 10px;
+  color:#333;
+}
+
+#mainBodyFixed {
+  margin: 20px 10px;
+  color: #333;
+  width:930px;
+  position:relative;
+}
+
+#mainBodyFixed h3,
+#mainBodyFluid h3 {
+  color:#336666;
+  font-size:1.25em;
+  margin: 0em 0em 0em 0em;
+  padding-bottom:.5em;
+}
+
+#mainBodyFixed h2,
+#mainBodyFluid h2 { 
+  color:#336666;
+  font-size:1.25em;
+  margin: 0;
+  padding-bottom:.5em;
+}
+
+#mainBodyFixed h1,
+#mainBodyFluid h1 { 
+  color:#435A6E;
+  font-size:1.7em;
+  margin: 1em 0;
+}
+
+#mainBodyFixed .green,
+#mainBodyFluid .green,
+#jd-content .green { 
+  color:#7BB026;
+  background-color:none;
+}
+
+#mainBodyLeft {
+  float: left;
+  width: 600px;
+  margin-right: 20px;  
+  color: #333;
+  position:relative;
+}
+
+div.indent {
+  margin-left: 40px;  
+  margin-right: 70px;
+}
+
+#mainBodyLeft p {
+  color: #333;
+  font-size: 13px;
+}
+
+#mainBodyLeft p.blue {
+  color: #669999;
+}
+
+#mainBodyLeft #communityDiv {
+  float: left;
+  background-image:url(images/bg_community_leftDiv.jpg);
+  background-repeat: no-repeat;
+  width: 581px;
+  height: 347px;
+  padding: 20px 0px 0px 20px;
+}
+
+#mainBodyRight {
+  float: left;
+  width: 300px;
+  color: #333;
+}
+
+#mainBodyRight p {
+  padding-right: 50px;
+  color: #333;
+}
+
+#mainBodyRight table {
+  width: 100%;
+}
+
+#mainBodyRight td {
+  border:0px solid #666;
+  padding:0px 5px;
+  text-align:left;
+}
+
+#mainBodyRight .blueBorderBox {
+  border:5px solid #ddf0f2;
+  padding:18px 18px 18px 18px;
+  text-align:left;
+}
+
+#mainBodyFixed .seperator {
+  background-image:url(images/hr_gray_side.jpg);
+  background-repeat:no-repeat;
+  width: 100%;
+  float: left;
+  clear: both;
+}
+
+#mainBodyBottom {
+  float: left;
+  width: 100%;
+  clear:both;
+  color: #333;
+}
+
+#mainBodyBottom .seperator {
+  background-image:url(images/hr_gray_main.jpg);
+  background-repeat:no-repeat;
+  width: 100%;
+  float: left;
+  clear: both;
+}
+
+/* Footer */
+#footer {
+  float: left;
+  width:90%;
+  margin: 20px;
+  color: #aaa;
+  font-size: 11px;
+}
+
+#footer a {
+  color: #aaa;
+  font-size: 11px;
+}
+
+#footer a:hover {
+  text-decoration: underline;
+  color:#aaa;
+}
+
+#footerlinks {
+  margin-top:2px;
+}
+
+#footerlinks a,
+#footerlinks a:visited {
+  color:#006699;
+}
+
+#homeBottom td {
+  border:0px solid #666;
+  padding: 8px 18px 8px 18px;
+}
+
+#homeBottom table {
+  width: 100%;
+}
+
+
+#homeBottom {
+  padding: 0px 0px 0px 0px;
+  float: left;
+  width: 585px;
+  height: 165px;
+  background-image:url(images/home/bg_home_bottom.jpg);
+  background-repeat: no-repeat;
+}
+
+.groupTable {
+  width: 100%;
+}
+
+.groupTable th {
+  padding: 10px;
+  color: #ffffff;
+  background-color: #6D8293;
+  border: 2px solid #fff;
+}
+
+.groupTable td {
+  padding: 10px;
+  color: #333333;
+  background-color: #d9d9d9;
+  border: 2px solid #fff;
+}
+
+.groupTable .evenRow td {  
+  background-color: #ededed;
+}
+
+span.BigBlue {
+  color:#336666;
+  font-size:1.25em;
+  margin: 0em 0em 0em 0em;
+  padding-bottom:.5em;
+  font-weight: bold;
+}
+
+span.emBlue {
+  color: #336666;
+  font-style:italic;
+}
+
+.pageTable {
+  width: 95%;
+  border: none;
+}
+
+.pageTable img {
+vertical-align: bottom;
+}
+
+.pageTable td {
+  border: none;
+}
+
+.pageTable td.leftNav {
+  width: 100px;
+}
+
+.greenBox {
+  margin: 10px 30px 10px 30px;
+  padding: 10px 20px 10px 20px;
+  background-color: #EBF3DB;
+  width: 75%;
+}
+
+.blueBox {
+  margin: 10px 30px 10px 30px;
+  padding: 10px 20px 10px 20px;
+  background-color: #DDF0F2;
+  width: 75%;
+}
+
+.blueHR {
+  margin: 10px 30px 10px 30px;
+  height: 5px;
+  background-color: #DDF0F2;
+  width: 75%;
+}
+
+/* SEARCH FILTER */
+#search_autocomplete {
+  color:#aaa;
+}
+
+#search-button {
+  display:inline;
+}
+
+#search_filtered_div {
+  position:absolute;
+  margin-top:-1px;
+  z-index:101;
+  border:1px solid #BCCDF0;
+  background-color:#fff;
+}
+
+#search_filtered {
+  min-width:100%;
+}
+#search_filtered td{
+  background-color:#fff;
+  border-bottom: 1px solid #669999;
+  line-height:1.5em;
+}
+
+#search_filtered .jd-selected {
+  background-color: #94b922;
+  cursor:pointer;
+}
+#search_filtered .jd-selected,
+#search_filtered .jd-selected a {
+  color:#fff;
+}
+
+.no-display {
+  display: none;
+}
+
+.jd-autocomplete {
+  font-family: Arial, sans-serif;
+  padding-left: 6px;
+  padding-right: 6px;
+  padding-top: 1px;
+  padding-bottom: 1px;
+  font-size: .8em;
+  border: none;
+  margin: 0;
+  line-height: 1.05em;
+}
+
+.show-row {
+  display: table-row;
+}
+.hide-row {
+  display: hidden;
+}
+
+/* SEARCH */
+
+/* restrict global search form width */
+#searchForm {
+  width:350px;
+}
+
+#searchTxt {
+  width:200px;
+}
+
+/* disable twiddle and size selectors for left column */
+#leftSearchControl div {
+  width: 100%;
+}
+
+#leftSearchControl .gsc-twiddle {
+  background-image : none;
+}
+
+#leftSearchControl td, #searchForm td {
+  border: 0px solid #000;
+}
+
+#leftSearchControl .gsc-resultsHeader .gsc-title {
+  padding-left : 0px;
+  font-weight : bold;
+  font-size : 13px;
+  color:#006699;
+  display : none;
+}
+
+#leftSearchControl .gsc-resultsHeader div.gsc-results-selector {
+  display : none;
+}
+
+#leftSearchControl .gsc-resultsRoot {
+  padding-top : 6px;
+}
+
+#leftSearchControl div.gs-visibleUrl-long {
+  display : block;
+  color:#006699;
+}
+
+.gsc-webResult div.gs-visibleUrl-short,
+table.gsc-branding,
+.gsc-clear-button {
+  display : none;
+}
+
+.gsc-cursor-box .gsc-cursor div.gsc-cursor-page,
+.gsc-cursor-box .gsc-trailing-more-results a.gsc-trailing-more-results,
+#leftSearchControl a, 
+#leftSearchControl a b {
+  color:#006699;
+}
+
+.gsc-resultsHeader {
+  display: none;
+}
+
+/* Disable built in search forms */
+.gsc-control form.gsc-search-box {
+  display : none;
+}
+table.gsc-search-box {
+  margin:6px 0 0 0;
+  border-collapse:collapse;
+}
+
+td.gsc-input {
+  padding:0 2px;
+  width:100%;
+  vertical-align:middle;
+}
+
+input.gsc-input {
+  border:1px solid #BCCDF0;
+  width:99%;
+  padding-left:2px;
+  font-size:.95em;
+}
+
+td.gsc-search-button {
+  text-align: right;
+  padding:0;
+  vertical-align:top;
+}
+
+#search-button {
+  margin:0 0 0 2px;
+  font-size:11px;
+  height:1.8em;
+}
+
+/* search result tabs */
+
+#doc-content .gsc-control {
+  position:relative;
+}
+
+#doc-content .gsc-tabsArea {
+  position:relative;
+}
+
+#doc-content .gsc-tabHeader {
+  padding: 3px 6px;
+  position:relative;
+}
+
+#doc-content .gsc-tabHeader.gsc-tabhActive {
+  border-top: 2px solid #94B922;
+}
+
+#doc-content h2#searchTitle {
+  padding:0;
+}
+
+#doc-content .gsc-resultsbox-visible {
+  padding:1em 0 0 6px;
+}
+
+/* CAROUSEL */
+
+#homeMiddle {
+  padding: 0px 0px 0px 0px;
+  float: left;
+  width: 584px;
+  height: 580px;
+  background:url(images/home/bg_home_middle.png) no-repeat 0 0;
+  position:relative;
+}
+
+#homeTitle {
+  margin:15px 15px 0;
+  height:30px;
+  background:url(images/hr_gray_side.jpg) no-repeat 0 29px;
+}
+
+#homeTitle h2 {
+  padding:0;
+}
+
+#announcement-block {
+  margin:15px 15px 0;
+  height:125px;
+}
+
+#announcement-block img {
+  float:left;
+  margin:0 30px 0 0;
+}
+
+#announcement {
+  float:left;
+  margin:0;
+}
+
+.clearer { clear:both; }
+
+#arrow-left, #arrow-right {
+  float:left;
+  width:42px;
+  height:42px;
+  background-image:url(images/home/carousel_buttons_sprite.png);
+  background-repeat:no-repeat;
+}
+#arrow-left {
+  margin:35px 3px 0 10px;
+}
+#arrow-right {
+  margin:35px 10px 0 0;
+}
+.arrow-left-off,
+#arrow-left.arrow-left-off:hover { 
+  background-position:0 0;
+}
+.arrow-right-off, 
+#arrow-right.arrow-right-off:hover { 
+  background-position:-42px 0;
+}
+#arrow-left:hover { 
+  background-position:0 -42px;
+}
+#arrow-right:hover { 
+  background-position:-42px -42px;
+}
+.arrow-left-on {
+  background-position:0 0;
+}
+.arrow-right-on {
+  background-position:-42px 0;
+}
+.arrow-right-off,
+.arrow-left-off {
+  cursor:default;
+}
+
+.app-list-container {
+  margin:37px 20px 0;
+  _margin-top:33px;
+  position:relative;
+  width:100%;
+}
+
+div#list-clip { 
+  height:110px; 
+  width:438px;
+  overflow:hidden; 
+  position:relative; 
+  float:left; 
+}
+
+div#app-list { 
+  left:0; 
+  z-index:1; 
+  position:absolute;
+  margin:11px 0 0;
+  _margin-top:13px;
+  width:1000%;
+}
+
+#app-list a {
+  display:block;
+  float:left;
+  height:90px;
+  width:90px;
+  margin:0 24px 0;
+  padding:3px;
+  background:#99cccc;
+  -webkit-border-radius:7px;
+  -moz-border-radius:7px;
+  border-radius:7px;
+  text-decoration:none;
+  text-align:center;
+  font-size:11px;
+}
+
+#app-list img {  
+  width:90px;
+  height:70px;
+  margin:0;
+}
+
+#app-list a.selected, 
+#app-list a:active.selected, 
+#app-list a:hover.selected {
+  background:#A4C639;
+  color:#fff;
+  cursor:default;
+  text-decoration:none;
+}
+
+#app-list a:hover, 
+#app-list a:active {
+  background:#ff9900;
+}
+
+#app-list a:hover span, 
+#app-list a:active span {
+  text-decoration:underline;
+}
+
+#droid-name {
+  padding-top:.5em;
+  color:#666;
+  padding-bottom:.25em;
+}
+
+#carouselMain {
+  margin: 25px 21px 0;
+  height:185px;
+  background-position:top;
+  background-repeat:no-repeat;
+  overflow:hidden;
+}
+
+#carouselMain img {
+  margin:0;
+}
+
+/*carousel bulletin layouts*/
+/*460px width*/
+/*185px height*/
+.img-left {
+  float:left;
+  width:230px;
+  height:165px;
+  overflow:hidden;
+  margin:8px 0 8px 8px;
+}
+.desc-right {
+  float:left;
+  width:270px;
+  margin:10px;
+}
+.img-right {
+  float:right;
+  width:220px;
+  height:165px;
+  overflow:hidden;
+  margin:8px 8px 8px 0;
+}
+.desc-left {
+  float:right;
+  width:280px;
+  margin:10px;
+  text-align:right;
+}
+.img-top {
+  height:80px;
+  text-align:center;
+}
+.desc-bottom {
+  height:100px;
+  margin:10px;
+}
+
+
+
diff --git a/tools/droiddoc/templates/assets/android-developer-docs-devguide.css b/tools/droiddoc/templates/assets/android-developer-docs-devguide.css
new file mode 100644
index 0000000..d8bd3b3
--- /dev/null
+++ b/tools/droiddoc/templates/assets/android-developer-docs-devguide.css
@@ -0,0 +1,19 @@
+
+@import url("android-developer-docs.css");
+
+/* Page title */
+
+#jd-header h1 {
+  padding: 8px 0 0 0;
+}
+
+/* Page content container */
+
+#jd-header table {
+margin: 0 0 1em 1em;
+}
+
+#jd-content table table,
+#jd-content table img {
+  margin:1em 0;
+}
\ No newline at end of file
diff --git a/tools/droiddoc/templates/assets/android-developer-docs.css b/tools/droiddoc/templates/assets/android-developer-docs.css
new file mode 100644
index 0000000..daa839f
--- /dev/null
+++ b/tools/droiddoc/templates/assets/android-developer-docs.css
@@ -0,0 +1,1083 @@
+/* file: android-developer-docs.css
+   author: smain
+   date: september 2008
+   info: developer doc styles (developer.android.com)
+*/
+
+@import url("android-developer-core.css");
+
+#title {
+  border-bottom: 4px solid #ccc;
+  display:none;
+}
+
+#title h1 {
+  color:#336666;
+  margin:0;
+  padding: 5px 10px;
+  font-size: 1em;
+  line-height: 15px;
+}
+
+#title h1 .small{
+  color:#000;
+  margin:0;
+  font-size: 13px;
+  padding:0 0 0 15px;
+}
+
+/* SIDE NAVIGATION */
+
+#side-nav {
+  padding:0 6px 0 0;
+  background-color: #fff;
+  font-size:12px;
+}
+
+#side-nav.not-resizable {
+  background:url('images/sidenav-rule.png') no-repeat 243px 0;
+}
+
+#resize-packages-nav {
+/* keeps the resize handle below the h-scroll handle */
+  height:270px;
+  overflow:hidden;
+  max-height:100%;
+}
+
+#packages-nav {
+  height:270px;
+  max-height:inherit;
+  position:relative;
+  overflow:auto;
+}
+
+#classes-nav,
+#devdoc-nav {
+  overflow:auto;
+  position:relative;
+}
+
+#side-nav ul {
+  list-style: none;
+  margin: 0;
+  padding:5px 0;
+}
+
+#side-nav ul ul {
+  margin: .35em 0 0 0;
+  padding: 0;
+}
+
+#side-nav li {
+  padding:0;
+  line-height:16px;
+  white-space:nowrap;
+  zoom:1;
+}
+
+#side-nav li h2 {
+  font-size:12px;
+  font-weight: bold;
+  margin:.5em 0 0 0;
+  padding: 3px 0 1px 9px;
+}
+
+#side-nav li a {
+  text-decoration:none;
+  padding: 0 0 0 18px;
+  zoom:1;
+}
+
+#side-nav li a:hover {
+  text-decoration:underline;
+}
+
+#side-nav li a+a {
+  padding: 0;
+}
+
+#side-nav li li li a { 
+/*sdk lists*/
+  padding: 0 0 0 28px;
+} 
+
+#side-nav .selected {
+  background-color: #435a6e;
+  color: #fff;
+  font-weight:bold;
+}
+
+#side-nav .selected a {
+  color: #fff;
+  text-decoration:none;
+}
+
+#side-nav strong {
+  display:block;
+}
+
+#side-nav .toggle-img {
+  margin:0;
+  padding:0;
+  position:absolute;
+  top:0;
+  left:0;
+  height:16px;
+  width:15px;
+  outline-style:none;
+}
+
+#side-nav .closed .toggle-img {
+  background:url('images/triangle-closed-small.png') 7px 4px no-repeat;
+}
+#side-nav .open .toggle-img {
+  background:url('images/triangle-opened-small.png') 7px 4px no-repeat;
+}
+
+#side-nav .toggle-list {
+  position:relative;
+}
+
+#side-nav .toggle-list ul {
+  margin:0;
+  display:none;
+}
+
+#side-nav .toggle-list div {
+  display:block;
+}
+
+#index-links .selected {
+  background-color: #fff;
+  color: #000;
+  font-weight:normal;
+  text-decoration:none;
+}
+
+#index-links {
+  padding:7px 0 4px 10px;
+}
+
+/* nav tree */
+
+#nav-tree ul {
+  padding:5px 0 1.5em;
+}
+
+#side-nav #nav-tree ul li a,
+#side-nav #nav-tree ul li span.no-children {
+  padding: 0 0 0 0;
+  margin: 0;
+}
+
+#nav-tree .plus {
+  margin: 0 3px 0 0;
+}
+
+#nav-tree ul ul {
+  list-style: none;
+  margin: 0;
+  padding: 0 0 0 0;
+}
+
+#nav-tree ul li {
+  margin: 0;
+  padding: 0 0 0 0;
+  white-space: nowrap;
+}
+
+#nav-tree .children_ul {
+  margin:0;
+}
+
+#nav-tree a.nolink {
+  color: black;
+  text-decoration: none;
+}
+
+#nav-tree span.label {
+  width: 100%;
+}
+
+#nav-tree {
+  overflow-x: auto;
+  overflow-y: scroll;
+}
+
+#nav-swap {
+  font-size:10px;
+  line-height:10px;
+  margin-left:1em;
+  text-decoration:none;
+  display:block;
+}
+
+#tree-link {
+
+}
+
+/* DOCUMENT BODY */
+
+#doc-content {
+  overflow:auto;
+}
+	
+#jd-header {
+  background-color: #E2E2E2;
+  padding: 7px 15px;
+}
+
+#jd-header h1 {
+  margin: 0 0 10px;
+  font-size:1.7em;
+}
+
+#jd-header .crumb {
+  font-size:.9em;
+  line-height:1em;
+  color:#777;
+}
+
+#jd-header .crumb a,
+#jd-header .crumb a:visited {
+  text-decoration:none;
+  color:#777;
+}
+
+#jd-header .crumb a:hover {
+  text-decoration:underline;
+}
+
+#jd-header table {
+  margin:0;
+  padding:0;
+}
+
+#jd-header td {
+  border:none;
+  padding:0;
+  vertical-align:top;
+}
+
+#jd-header.guide-header {
+  background-color:#fff;
+  color:#435a6e;
+  height:50px;
+}
+
+#jd-descr {
+  position:relative;
+}
+
+/* summary tables for reference pages */
+.jd-sumtable {
+margin: .5em 1em 1em 1em;
+width:99%;
+font-size:.9em;
+}
+
+.jd-sumtable a {
+  text-decoration:none;
+}
+
+.jd-sumtable a:hover {
+  text-decoration:underline;
+}
+
+/* the link inside a sumtable for "Show All/Hide All" */
+.toggle-all {
+  display:block;
+  float:right;
+  font-weight:normal;
+  font-size:0.9em;
+}
+
+/* adjustments for in/direct subclasses tables */
+.jd-sumtable-subclasses {
+  margin: 1em 0 0 0;
+  max-width:968px;
+}
+
+/* extra space between end of method name and open-paren */
+.sympad {
+  margin-right: 2px;
+}
+
+/* right alignment for the return type in sumtable */
+.jd-sumtable .jd-typecol {
+  text-align:right;
+}
+
+/* adjustments for the expando table-in-table */
+.jd-sumtable-expando {
+  margin:.5em 0;
+  padding:0;
+}
+
+/* a div that holds a short description */
+.jd-descrdiv {
+  width:100%; 
+  padding:3px 1em 0 1em;
+  margin:0;
+  border:0;
+}
+
+/* page-top-right container for reference pages (holds
+links to summary tables) */
+#api-info-block {
+  font-size:.8em;
+  margin:0;
+  padding:6px;
+  font-weight:normal;
+  float:right;
+  text-align:right;
+  color:#999;
+  max-width:70%;
+}
+
+/* applies to a div containing links to summary tables */
+.sum-details-links {
+  margin:0 .5em;
+  padding:0;
+  font-weight:normal;
+}
+
+.sum-details-links a {
+  text-decoration:none;
+}
+
+.sum-details-links a:hover {
+  text-decoration:underline;
+}
+
+
+/* inheritance table */
+.jd-inheritance-table {
+  border-spacing:0;
+  margin:0;
+  padding:0;
+  font-size:.9em;
+}
+.jd-inheritance-table td {
+  border: none;
+  margin: 0;
+  padding: 0;
+}
+.jd-inheritance-table .jd-inheritance-space {
+  font-weight:bold;
+  width:1em;
+}
+.jd-inheritance-table .jd-inheritance-interface-cell {
+  padding-left: 17px;
+}
+
+#jd-content {
+  padding: 18px 15px;
+}
+
+hr {
+  background-color:#ccc;
+}
+
+/* DOC CLASSES */
+
+#jd-content h1 {
+/*sdk page*/
+  font-size:1.6em;
+  color:#336666;
+  margin:0 0 .5em;
+}
+
+#jd-content h2 {
+  font-size:1.45em;
+  color:#111;
+  border-top:2px solid #ccc;
+  padding: .5em 0 0;
+  margin: 1.5em 0 1em 0;
+  max-width:968px;
+}
+
+#jd-content h3 {
+  font-size:1.2em;
+  color:#222;
+  padding: .75em 0 .65em 0;
+  margin:0;
+}
+
+#jd-content h4 {
+  font-size:1.1em;
+  margin-bottom:.5em;
+  color:#222;
+}
+
+#jd-content .small-header {
+  font-size:1em;
+  color:#000;
+  font-weight:bold;
+  border:none;
+  padding:0;
+  margin:1em 0 .5em;
+  position:inherit;
+}
+
+#jd-content img {
+  margin: 0 0 1em 1em;
+}
+
+#jd-content li img,
+#jd-content dd img {
+  margin:.5em 0 0 1em;
+}
+
+.nolist {
+  list-style:none;
+  padding:0;
+  margin:0 0 0 1em;
+}
+
+.nolist li {
+  padding:0;
+  margin:0;
+}
+
+h4 .normal {
+  font-size:.9em;
+  font-weight:normal;
+}
+
+.jd-details {
+/*  border:1px solid #669999;
+  padding:4px; */
+  margin:0 0 1em;
+}
+
+/* API reference: a container for the
+.tagdata blocks that make up the detailed
+description */
+.jd-details-descr {
+  padding:0;
+  margin:.5em .25em;
+}
+
+/* API reference: a block containing 
+a detailed description, a params table,
+seealso list, etc */
+.jd-tagdata {
+  margin:.5em 1em;
+}
+
+/* API reference: adjustments to
+the detailed description block */
+.jd-tagdescr {
+  margin:.25em 0 .75em 0;
+  line-height:1em;
+}
+
+.jd-tagdescr p {
+  margin:.5em 0;
+  padding:0;
+
+}
+
+.jd-tagdescr ol,
+.jd-tagdescr ul {
+  margin:0 2.5em;
+  padding:0;
+}
+
+.jd-tagdescr table,
+.jd-tagdescr img {
+  margin:.25em 1em;
+}
+
+.jd-tagdescr li {
+margin:0 0 .25em 0;
+padding:0;
+}
+
+/* API reference: heading marking
+the details section for constants,
+attrs, methods, etc. */
+h4.jd-details-title {
+  font-size:1.15em;
+  background-color: #E2E2E2;
+  margin:1.5em 0 .6em;
+  padding:3px;
+}
+
+h4.jd-tagtitle {
+  margin:0;
+}
+
+/* API reference: heading for "Parameters", "See Also", etc.,
+in details sections */
+h5.jd-tagtitle {
+  margin:0 0 .25em 0;
+  font-size:1em;
+}
+
+.jd-tagtable {
+  margin:0;
+}
+
+.jd-tagtable td,
+.jd-tagtable th {
+  border:none;
+  background-color:#fff;
+  vertical-align:top;
+  font-weight:normal;
+  padding:2px 10px;
+}
+
+.jd-tagtable th {
+  font-style:italic;
+}
+
+#jd-content table h2 {
+  background-color: #d6d6d6;
+  font-size: 1.1em;
+  margin:0 0 10px;
+  padding:5px;
+  left:0;
+  width:auto;
+}
+
+div.special {
+  padding: .5em 1em 1em 1em;
+  margin: 0 0 1em;
+  background-color: #ddf0f2;
+}
+
+div.special p {
+  margin: .5em 0 0 0;
+}
+
+div.special ol {
+  margin: 0;
+}
+
+div.special ol li {
+  margin: 0;
+  padding: 0;
+}
+
+#jd-content div.special h2,
+#jd-content div.special h3 {
+  color:#669999;
+  font-size:1.2em;
+  border:none;
+  margin:0 0 .5em;
+  padding:0;
+}
+  
+/* old p.note, p.caution, p.warning {
+  margin:0 0 1em;
+  padding: 4px 10px;
+  background-color: #efefef;
+  border-top: 1px solid;  
+  border-bottom: 1px solid;
+}
+*/
+p.note, p.caution, p.warning {
+  margin: 1em;
+  padding: 0 0 0 .5em;
+  border-left: 4px solid;
+}
+
+p.special-note {
+  background-color:#EBF3DB;
+  padding:10px 20px;
+  margin:0 0 1em;
+}
+
+p.note {
+ border-color: #99aacc;
+}
+    
+p.caution {
+  border-color: #ffcc33;
+}
+
+p.warning {
+  border-color: #aa0033;
+}
+  
+p.warning b, p.warning em, p.warning strong {
+  color: #aa0033;
+  font-weight: bold;
+}
+
+li p.note, li p.warning, li p.caution {
+  margin: .5em 0 0 0;  
+  padding: .2em .5em .2em .9em;
+}
+
+dl.xml dt {
+  font-variant:small-caps;
+  font-size:1.2em;
+}
+
+dl.xml dl {
+  padding:0;
+}
+
+dl.xml dl dt {
+  font-variant:normal;
+  font-size:1em;
+}
+
+.listhead li {
+  font-weight: bold;
+}
+  
+.listhead li *, /*ie*/.listhead li li {
+  font-weight: normal;
+}
+
+ol.no-style,
+ul.no-style {
+  list-style:none;
+  padding-left:1em;
+}
+
+.new {
+  font-size: .78em;
+  font-weight: bold;
+  color: red;
+  text-decoration: none;
+}
+
+pre.classic {
+  background-color:transparent;
+  border:none;
+  padding:0;
+}
+
+
+/* BEGIN quickview sidebar element styles */
+
+#qv-wrapper {
+  float: right;
+  width:310px;
+  background-color:#fff;
+  margin:-48px 0 2px 0;
+  padding:0 0 20px 35px;
+}
+
+#qv {
+  background-color:#fff;
+  border:4px solid #dee8f1;
+  margin:0;
+  padding:0 6px 6px;
+  width:270px;
+  font-size:.9em;
+}
+
+#qv ol {
+  list-style:none;
+  padding: 0;
+}
+
+#qv ol ol{
+  list-style:none;
+  padding: 0 0 3px 12px;
+  margin:0;
+}
+
+#qv ul {
+  padding: 0 10px 0 2em;
+}
+
+#qv li {
+  padding: 0 10px;
+  margin: 2 0 0;
+  line-height: 1.2em;
+}
+
+#qv ul li {
+  padding: 0 10px 0 0;
+}
+
+#qv li.selected a {
+  color:#555;
+  text-decoration:none;
+}
+
+#qv a {
+  color:#cc6600;
+}
+
+#qv p {
+  margin:8px 0 0;
+  padding:0 10px;
+}
+
+#qv-extra #rule {
+  padding: 0 10px;
+  margin: 0;
+}
+
+#qv-sub-rule {
+  padding: 6px 20px;
+  margin: 0;
+}
+
+#qv-sub-rule p {
+  margin: 0;
+}
+
+#jd-content #qv h2 {
+  font-size:1.05em;
+  font-weight:bold;
+  margin:12px 0 .25em 0;
+  padding:0 10px;
+  background-color:transparent;
+  color:#7BB026;
+  border:none;
+  left:0;
+  z-index:1;
+}
+
+/* END quickview sidebar element styles */
+
+/* Begin sidebox sidebar element styles */
+
+.sidebox-wrapper {
+  float: right;
+  width:280px;
+  background-color:#fff;
+  margin: 0;
+  padding: 20px 0 20px 20px;
+}
+
+.sidebox-inner {
+  border-left:1px solid #dee8f1;
+  background-color:#ffffee;
+  padding:5px 8px 5px 12px;
+  font-size:90%;
+  width:260px;
+}
+
+.sidebox {
+  float: right;
+  width:260px;
+  background-color:#ffffee;
+  border-left:1px solid #dee8f1;
+  margin: 12px 0 0 15px;
+  padding:5px 8px 0 12px;
+  font-size:90%;
+}	
+
+.sidebox p,
+.sidebox-inner p {
+  margin-bottom: .25em;
+}
+
+.sidebox ul,
+.sidebox-inner ul {
+  padding: 0 0 0 1.5em;
+}
+
+.sidebox li ul,
+.sidebox-inner li ul {
+  margin-top:0;
+  margin-bottom:.1em;
+}
+
+.sidebox li,
+.sidebox-inner li {
+padding:0 0 0 0em;
+}
+
+#jd-content .sidebox h2,
+#jd-content .sidebox h3,
+#jd-content .sidebox-inner h2,
+#jd-content .sidebox-inner h3 {
+  border:none;
+  font-size:1em;
+  margin:0;
+  padding:4px 0 4px;
+  left:0;
+  z-index:0;
+}
+
+.sidebox hr,
+.sidebox-inner hr {
+  background-color:#ccc;
+  border:none;
+}
+
+/* End sidebox sidebar element styles */
+
+/* table of contents */
+
+ol.toc {
+  margin: 0 0 1em 0;
+  padding: 0;
+  list-style: none;
+  font-size:95%;
+}
+
+ol.toc li {
+  font-weight: bold;
+  margin: 0 0 .5em 1em;
+  padding: 0;
+}
+
+ol.toc li p {
+  font-weight: normal;
+}
+
+ol.toc li ol {
+  margin: 0;
+  padding: 0;
+}
+  
+ol.toc li li {
+  padding: 0;
+  margin: 0 0 0 1em;
+  font-weight: normal;
+  list-style: none;
+}
+
+table ol.toc {
+  margin-left: 0;
+}
+
+.columns td {
+  padding:0 5px;
+  border:none;
+}
+
+/* link table */
+.jd-linktable {
+  margin: 0 0 1em;
+  border-bottom: 1px solid #888;
+}
+.jd-linktable th,
+.jd-linktable td {
+  padding: 3px 5px;
+  vertical-align: top;
+  text-align: left;
+  border:none;
+}
+.jd-linktable tr {
+  background-color: #fff;
+}
+.jd-linktable td {
+  border-top: 1px solid #888;
+  background-color: inherit;
+}
+.jd-linktable td  p {
+  padding: 0 0 5px;
+}
+.jd-linktable .jd-linkcol {
+}
+.jd-linktable .jd-descrcol {
+}
+.jd-linktable .jd-typecol {
+  text-align:right;
+}
+.jd-linktable .jd-valcol {
+}
+.jd-linktable .jd-commentrow {
+  border-top:none;
+  padding-left:25px;
+}
+.jd-deprecated-warning {
+  margin-top: 0;
+  margin-bottom: 10px;
+}
+
+tr.alt-color {
+  background-color: #f6f6f6;
+}
+
+/* expando trigger */
+#jd-content .jd-expando-trigger-img {
+  margin:0;
+}
+
+/* jd-expando */
+.jd-inheritedlinks {
+  padding:0 0 0 13px
+}
+
+/* SDK PAGE */
+table.download tr {
+  background-color:#d9d9d9;
+}
+
+table.download tr.alt-color {
+  background-color:#ededed;
+}
+
+table.download td,
+table.download th {
+  border:2px solid #fff;
+  padding:10px 5px;
+}
+
+table.download th {
+  background-color:#6d8293;
+  color:#fff;
+}
+
+/* INLAY 180 COPY and 240PX EXTENSION */
+/* modified to 43px so that all browsers eliminate the package panel h-scroll */
+.g-tpl-240 .g-unit, 
+.g-unit .g-tpl-240 .g-unit, 
+.g-unit .g-unit .g-tpl-240 .g-unit {
+  display: block;
+  margin: 0 0 0 243px;
+  width: auto;
+  float: none;
+}
+.g-unit .g-unit .g-tpl-240 .g-first,
+.g-unit .g-tpl-240 .g-first,
+.g-tpl-240 .g-first {
+  display: block;
+  margin: 0;
+  width: 243px;
+  float: left;
+}
+/* 240px alt */
+.g-tpl-240-alt .g-unit, 
+.g-unit .g-tpl-240-alt .g-unit, 
+.g-unit .g-unit .g-tpl-240-alt .g-unit {
+  display: block;
+  margin: 0 243px 0 0;
+  width: auto;
+  float: none;
+}
+.g-unit .g-unit .g-tpl-240-alt .g-first,
+.g-unit .g-tpl-240-alt .g-first,
+.g-tpl-240-alt .g-first {
+  display: block;
+  margin: 0;
+  width: 243px;
+  float: right;
+}
+
+/* 180px */
+.g-tpl-180 .g-unit, 
+.g-unit .g-tpl-180 .g-unit, 
+.g-unit .g-unit .g-tpl-180 .g-unit {
+  display: block;
+  margin: 0 0 0 180px;
+  width: auto;
+  float: none;
+}
+.g-unit .g-unit .g-tpl-180 .g-first,
+.g-unit .g-tpl-180 .g-first,
+.g-tpl-180 .g-first {
+  display: block;
+  margin: 0;
+  width: 180px;
+  float: left;
+}
+/* 180px alt */
+.g-tpl-180-alt .g-unit, 
+.g-unit .g-tpl-180-alt .g-unit, 
+.g-unit .g-unit .g-tpl-180-alt .g-unit {
+  display: block;
+  margin: 0 180px 0 0;
+  width: auto;
+  float: none;
+}
+.g-unit .g-unit .g-tpl-180-alt .g-first,
+.g-unit .g-tpl-180-alt .g-first,
+.g-tpl-180-alt .g-first {
+  display: block;
+  margin: 0;
+  width: 180px;
+  float: right;
+}
+
+  
+/* JQUERY RESIZABLE STYLES */
+.ui-resizable { position: relative; }
+.ui-resizable-handle { position: absolute; display: none; font-size: 0.1px; z-index:1; }
+.ui-resizable .ui-resizable-handle { display: block; }
+body .ui-resizable-disabled .ui-resizable-handle { display: none; } /* use 'body' to make it more specific (css order) */
+body .ui-resizable-autohide .ui-resizable-handle { display: none; } /* use 'body' to make it more specific (css order) */
+.ui-resizable-s { cursor: s-resize; height: 6px; width: 100%; bottom: 0px; left: 0px; background: transparent url("images/resizable-s2.gif") repeat scroll center top; }
+.ui-resizable-e { cursor: e-resize; width: 6px; right: 0px; top: 0px; height: 100%; background: transparent url("images/resizable-e2.gif") repeat scroll right center; }
+
+@media print {
+
+  body {
+    overflow:visible;
+  }
+
+  #header {
+    height:60px;
+  }
+
+  #headerLeft {
+    margin:0;
+  }
+  
+  #headerRight {
+    display:none;
+  }
+
+  #body-content {
+    position:inherit;
+  }
+
+  #side-nav {
+    display:none;
+  }
+  
+  #doc-content {
+    margin-left:0 !important;
+    height:auto !important;
+    width:auto !important;
+    overflow:inherit;
+    display:inline;
+  }
+
+  #jd-header {
+    padding:10px 0;
+  }
+
+  #jd-content {
+    padding:15px 0 0;
+  }
+
+  #footer {
+    float:none;
+    margin:2em 0 0;
+  }
+
+  h4.jd-details-title {
+    border-bottom:1px solid #666;
+  }
+
+  pre {
+    /* these allow lines to break (if there's a white space) */
+    overflow: visible;
+    text-wrap: unrestricted;
+    white-space: -moz-pre-wrap; /* Moz */
+    white-space: -pre-wrap; /* Opera 4-6 */
+    white-space: -o-pre-wrap; /* Opera 7 */
+    white-space: pre-wrap; /* CSS3  */
+    word-wrap: break-word; /* IE 5.5+ */
+  }
+
+  h1, h2, h3, h4, h5, h6 { 
+    page-break-after: avoid;
+  }
+
+  table, img {
+    page-break-inside: avoid;
+  }
+
+  #qv,
+  #qv-wrapper {
+    display:none;
+  }
+
+}
diff --git a/tools/droiddoc/templates/assets/android-developer-docs.js b/tools/droiddoc/templates/assets/android-developer-docs.js
new file mode 100644
index 0000000..a84d5a6
--- /dev/null
+++ b/tools/droiddoc/templates/assets/android-developer-docs.js
@@ -0,0 +1,330 @@
+var resizePackagesNav;
+var classesNav;
+var devdocNav;
+var sidenav;
+var content;
+var HEADER_HEIGHT = 117;
+var cookie_style = 'android_developer';
+var NAV_PREF_TREE = "tree";
+var NAV_PREF_PANELS = "panels";
+var nav_pref;
+var toRoot;
+
+
+function addLoadEvent(newfun) {
+  var current = window.onload;
+  if (typeof window.onload != 'function') {
+    window.onload = newfun;
+  } else {
+    window.onload = function() {
+      current();
+      newfun();
+    }
+  }
+}
+
+window.onresize = resizeAll;
+
+function setToRoot(root) {
+  toRoot = root;
+  // note: toRoot also used by carousel.js
+}
+
+function restoreWidth(navWidth) {
+  var windowWidth = $(window).width() + "px";
+  content.css({marginLeft:parseInt(navWidth) + 6 + "px", //account for 6px-wide handle-bar
+               width:parseInt(windowWidth) - parseInt(navWidth) - 6 + "px"});
+  sidenav.css({width:navWidth});
+  resizePackagesNav.css({width:navWidth});
+  classesNav.css({width:navWidth});
+  $("#packages-nav").css({width:navWidth});
+}
+
+function restoreHeight(packageHeight) {
+  var windowHeight = ($(window).height() - HEADER_HEIGHT);
+  var swapperHeight = windowHeight - 13;
+  $("#swapper").css({height:swapperHeight + "px"});
+  sidenav.css({height:windowHeight + "px"});
+  content.css({height:windowHeight + "px"});
+  resizePackagesNav.css({maxHeight:swapperHeight + "px", height:packageHeight});
+  classesNav.css({height:swapperHeight - parseInt(packageHeight) + "px"});
+  $("#packages-nav").css({height:parseInt(packageHeight) - 6 + "px"}); //move 6px to give space for the resize handle
+  devdocNav.css({height:sidenav.css("height")});
+  $("#nav-tree").css({height:swapperHeight + "px"});
+}
+
+function getCookie(cookie) {
+  var myCookie = cookie_style+"_"+cookie+"=";
+  if (document.cookie) {
+    var index = document.cookie.indexOf(myCookie);
+    if (index != -1) {
+      var valStart = index + myCookie.length;
+      var valEnd = document.cookie.indexOf(";", valStart);
+      if (valEnd == -1) {
+        valEnd = document.cookie.length;
+      }
+      var val = document.cookie.substring(valStart, valEnd);
+      return val;
+    }
+  }
+  return 0;
+}
+
+function writeCookie(cookie, val, path, expiration) {
+  if (!val) return;  
+  var date = new Date();
+  date.setTime(date.getTime()+(10*365*24*60*60*1000)); // default expiration is one week
+  expiration = expiration ? expiration : date.toGMTString();
+  if (location.href.indexOf("/reference/") != -1) {
+    document.cookie = cookie_style+'_reference_'+cookie+'='+val+'; expires='+expiration+'; path='+'/'+path;
+  } else if (location.href.indexOf("/guide/") != -1) {
+    document.cookie = cookie_style+'_guide_'+cookie+'='+val+'; expires='+expiration+'; path='+'/'+path;
+  }
+} 
+
+function init() {
+  $("#resize-packages-nav").resizable({handles: "s", resize: function(e, ui) { resizeHeight(); } });
+  $(".side-nav-resizable").resizable({handles: "e", resize: function(e, ui) { resizeWidth(); } });
+
+  $("#side-nav").css({position:"absolute",left:0});
+  content = $("#doc-content");
+  resizePackagesNav = $("#resize-packages-nav");
+  classesNav = $("#classes-nav");
+  sidenav = $("#side-nav");
+  devdocNav = $("#devdoc-nav");
+
+  if (location.href.indexOf("/reference/") != -1) {
+    var cookiePath = "reference_";
+  } else if (location.href.indexOf("/guide/") != -1) {
+    var cookiePath = "guide_";
+  }
+  var cookieWidth = getCookie(cookiePath+'width');
+  var cookieHeight = getCookie(cookiePath+'height');
+  if (cookieWidth) {
+    restoreWidth(cookieWidth);
+  } else if ($(".side-nav-resizable").length) {
+    resizeWidth();
+  }
+  if (cookieHeight) {
+    restoreHeight(cookieHeight);
+  } else {
+    resizeHeight();
+  }
+
+  if (devdocNav.length) { // only dev guide and sdk 
+    highlightNav(location.href); 
+  }
+}
+
+function highlightNav(fullPageName) {
+  var lastSlashPos = fullPageName.lastIndexOf("/");
+  var firstSlashPos = (fullPageName.indexOf("/guide/") != -1) ?
+                       fullPageName.indexOf("/guide/") : 
+                       fullPageName.indexOf("/sdk/"); // first slash after /guide or /sdk
+  if (lastSlashPos == (fullPageName.length - 1)) { // if the url ends in slash (add 'index.html')
+    fullPageName = fullPageName + "index.html";
+  }
+  var htmlPos = fullPageName.lastIndexOf(".html", fullPageName.length);
+  var pathPageName = fullPageName.slice(firstSlashPos, htmlPos + 5);
+  var link = $("#devdoc-nav a[href$='"+ pathPageName+"']");
+  if ((link.length == 0) && ((fullPageName.indexOf("/guide/") != -1) || (fullPageName.indexOf("/sdk/") != -1))) { 
+// if there's no match, then let's backstep through the directory until we find an index.html page that matches our ancestor directories (only for dev guide and sdk)
+    lastBackstep = pathPageName.lastIndexOf("/");
+    while (link.length == 0) {
+      backstepDirectory = pathPageName.lastIndexOf("/", lastBackstep);
+      link = $("#devdoc-nav a[href$='"+ pathPageName.slice(0, backstepDirectory + 1)+"index.html']");
+      lastBackstep = pathPageName.lastIndexOf("/", lastBackstep - 1);
+      if (lastBackstep == 0) break;
+    }
+  }
+  link.parent().addClass('selected');
+  if (link.parent().parent().is(':hidden')) {
+    toggle(link.parent().parent().parent(), false);
+  } else if (link.parent().parent().hasClass('toggle-list')) {
+    toggle(link.parent().parent(), false);
+  }
+}
+
+function resizeHeight() {
+  var windowHeight = ($(window).height() - HEADER_HEIGHT);
+  var swapperHeight = windowHeight - 13;
+  $("#swapper").css({height:swapperHeight + "px"});
+  sidenav.css({height:windowHeight + "px"});
+  content.css({height:windowHeight + "px"});
+  resizePackagesNav.css({maxHeight:swapperHeight + "px"});
+  classesNav.css({height:swapperHeight - parseInt(resizePackagesNav.css("height")) + "px"});
+  $("#packages-nav").css({height:parseInt(resizePackagesNav.css("height")) - 6 + "px"}); //move 6px for handle
+  devdocNav.css({height:sidenav.css("height")});
+  $("#nav-tree").css({height:swapperHeight + "px"});
+  writeCookie("height", resizePackagesNav.css("height"), "", null);
+}
+
+function resizeWidth() {
+  var windowWidth = $(window).width() + "px";
+  if (sidenav.length) {
+    var sidenavWidth = sidenav.css("width");
+  } else {
+    var sidenavWidth = 0;
+  }
+  content.css({marginLeft:parseInt(sidenavWidth) + 6 + "px", //account for 6px-wide handle-bar
+               width:parseInt(windowWidth) - parseInt(sidenavWidth) - 6 + "px"});
+  resizePackagesNav.css({width:sidenavWidth});
+  classesNav.css({width:sidenavWidth});
+  $("#packages-nav").css({width:sidenavWidth});
+  writeCookie("width", sidenavWidth, "", null);
+}
+
+function resizeAll() {
+  resizeHeight();
+  if ($(".side-nav-resizable").length) {
+    resizeWidth();
+  }
+}
+
+function loadLast(cookiePath) {
+  var location = window.location.href;
+  if (location.indexOf("/"+cookiePath+"/") != -1) {
+    return true;
+  }
+  var lastPage = getCookie(cookiePath + "_lastpage");
+  if (lastPage) {
+    window.location = lastPage;
+    return false;
+  }
+  return true;
+}
+
+$(window).unload(function(){
+  var href = location.href;
+  if (href.indexOf("/reference/") != -1) {
+    writeCookie("lastpage", href, "", null);
+  } else if (href.indexOf("/guide/") != -1) {
+    writeCookie("lastpage", href, "", null);
+  }
+});
+
+
+
+function toggle(obj, slide) {
+  var ul = $("ul", obj);
+  var li = ul.parent();
+  if (li.hasClass("closed")) {
+    if (slide) {
+      ul.slideDown("fast");
+    } else {
+      ul.show();
+    }
+    li.removeClass("closed");
+    li.addClass("open");
+    $(".toggle-img", li).attr("title", "hide pages");
+  } else {
+    ul.slideUp("fast");
+    li.removeClass("open");
+    li.addClass("closed");
+    $(".toggle-img", li).attr("title", "show pages");
+  }
+}
+
+
+
+function buildToggleLists() {
+  $(".toggle-list").each(
+    function(i) {
+      $("div", this).append("<a class='toggle-img' href='#' title='show pages' onClick='toggle(this.parentNode.parentNode, true); return false;'></a>");
+      $(this).addClass("closed");
+    });
+}
+
+function getNavPref() {
+  var v = getCookie('reference_nav');
+  if (v != NAV_PREF_TREE) {
+    v = NAV_PREF_PANELS;
+  }
+  return v;
+}
+
+function chooseDefaultNav() {
+  nav_pref = getNavPref();
+  if (nav_pref == NAV_PREF_TREE) {
+    $("#nav-panels").toggle();
+    $("#panel-link").toggle();
+    $("#nav-tree").toggle();
+    $("#tree-link").toggle();
+  }
+}
+
+function swapNav() {
+  if (nav_pref == NAV_PREF_TREE) {
+    nav_pref = NAV_PREF_PANELS;
+  } else {
+    nav_pref = NAV_PREF_TREE;
+    init_navtree("nav-tree", toRoot, NAVTREE_DATA);
+  }
+  var date = new Date();
+  date.setTime(date.getTime()+(10*365*24*60*60*1000)); // keep this for 10 years
+  writeCookie("nav", nav_pref, "", date.toGMTString());
+
+  $("#nav-panels").toggle();
+  $("#panel-link").toggle();
+  $("#nav-tree").toggle();
+  $("#tree-link").toggle();
+
+  if ($("#nav-tree").is(':visible')) scrollIntoView("nav-tree");
+  else {
+    scrollIntoView("packages-nav");
+    scrollIntoView("classes-nav");
+  }
+}
+
+function scrollIntoView(nav) {
+  var navObj = $("#"+nav);
+  if (navObj.is(':visible')) {
+    var selected = $(".selected", navObj);
+    if (selected.length == 0) return;
+    if (selected.is("div")) selected = selected.parent();
+
+    var scrolling = document.getElementById(nav);
+    var navHeight = navObj.height();
+    var offsetTop = selected.position().top;
+    if (selected.parent().parent().is(".toggle-list")) offsetTop += selected.parent().parent().position().top;
+    if(offsetTop > navHeight - 92) {
+      scrolling.scrollTop = offsetTop - navHeight + 92;
+    }
+  }
+}
+
+function toggleAllInherited(linkObj, expand) {
+  var a = $(linkObj);
+  var table = $(a.parent().parent().parent());
+  var expandos = $(".jd-expando-trigger", table);
+  if ( (expand == null && a.text() == "[Expand]") || expand ) {
+    expandos.each(function(i) {
+      toggleInherited(this, true);
+    });
+    a.text("[Collapse]");
+  } else if ( (expand == null && a.text() == "[Collapse]") || (expand == false) ) {
+    expandos.each(function(i) {
+      toggleInherited(this, false);
+    });
+    a.text("[Expand]");
+  }
+  return false;
+}
+
+function toggleAllSummaryInherited(linkObj) {
+  var a = $(linkObj);
+  var content = $(a.parent().parent().parent());
+  var toggles = $(".toggle-all", content);
+  if (a.text() == "[Expand All]") {
+    toggles.each(function(i) {
+      toggleAllInherited(this, true);
+    });
+    a.text("[Collapse All]");
+  } else {
+    toggles.each(function(i) {
+      toggleAllInherited(this, false);
+    });
+    a.text("[Expand All]");
+  }
+  return false;
+}
diff --git a/tools/droiddoc/templates/assets/carousel.js b/tools/droiddoc/templates/assets/carousel.js
new file mode 100644
index 0000000..4eebd89
--- /dev/null
+++ b/tools/droiddoc/templates/assets/carousel.js
@@ -0,0 +1,295 @@
+/* file: carousel.js
+   date: oct 2008
+   author: jeremydw,smain
+   info: operates the carousel widget for announcements on 
+         the android developers home page. modified from the
+         original market.js from jeremydw. */
+
+/* -- video switcher -- */
+
+var oldVid = "multi"; // set the default video
+var nowPlayingString = "Now playing:";
+var assetsRoot = "assets/";
+
+
+/* -- app thumbnail switcher -- */
+
+var currentDroid;
+var oldDroid;
+
+// shows a random application
+function randomDroid(){
+
+	// count the total number of apps
+	var droidListLength = 0;
+	for (var k in droidList)
+		droidListLength++;
+		
+	// pick a random app and show it
+  var j = 0;
+  var i = Math.floor(droidListLength*Math.random());
+  for (var x in droidList) {
+    if(j++ == i){
+    	currentDroid = x;
+    	showPreview(x);
+    	centerSlide(x);
+    }
+  }
+
+}
+
+// shows a bulletin, swaps the carousel highlighting
+function droid(appName){
+
+  oldDroid = $("#droidlink-"+currentDroid);
+  currentDroid = appName;
+
+  var droid = droidList[appName];
+  var layout = droid.layout;
+  var imgDiv = document.getElementById("bulletinImg");
+  var descDiv = document.getElementById("bulletinDesc");
+
+  if (layout == "imgLeft") {
+    imgDiv.className = "img-left";
+    descDiv.className = "desc-right";
+  } else if (layout == "imgTop") {
+    imgDiv.className = "img-top";
+    descDiv.className = "desc-bottom";
+  } else if (layout == "imgRight") {
+    imgDiv.className = "img-right";
+    descDiv.className = "desc-left";
+  }
+
+  imgDiv.innerHTML = "<img src='" + toRoot + assetsRoot + "images/home/" + droid.img + "'>";
+  descDiv.innerHTML = (droid.title != "") ? "<h3>" + droid.title + "</h3>" + droid.desc : droid.desc;
+
+  if(oldDroid)
+    oldDroid.removeClass("selected");
+
+  $("#droidlink-"+appName).addClass("selected");
+}
+
+
+// -- * build the carousel based on the droidList * -- //
+function buildCarousel() {
+  var appList = document.getElementById("app-list");
+  for (var x in droidList) {
+    var droid = droidList[x];
+    var icon = droid.icon;
+    var name = droid.name;
+    var a = document.createElement("a");
+    var img = document.createElement("img");
+    var br = document.createElement("br");
+    var span = document.createElement("span");
+    var text = document.createTextNode(droid.name);
+
+    a.setAttribute("id", "droidlink-" + x);
+    a.className = x;
+    a.setAttribute("href", "#");
+    a.onclick = function() { showPreview(this.className); return false; }
+    img.setAttribute("src", toRoot + assetsRoot + "images/home/" + droid.icon);
+    img.setAttribute("alt", "");
+
+    span.appendChild(text);
+    a.appendChild(img);
+    a.appendChild(br);
+    a.appendChild(span);
+    appList.appendChild(a);
+  }
+}
+
+// -- * slider * -- //
+
+// -- dependencies:
+//    (1) div containing slides, (2) a "clip" div to hide the scroller
+//    (3) control arrows
+
+// -- * config below * -- //
+
+var slideCode = droidList; // the dictionary of slides
+var slideList = 'app-list'; // the div containing the slides
+var arrowRight = 'arrow-right'; // the right control arrow
+var arrowLeft = 'arrow-left'; // the left control arrow
+
+
+function showPreview(slideName) {
+  centerSlide(slideName);
+  if (slideName.indexOf('selected') != -1) {
+    return false;
+  }
+  droid(slideName); // do this function when slide is clicked
+}
+
+var thumblist = document.getElementById(slideList);// the div containing the slides
+
+var slideWidth = 144; // width of a slide including all margins, etc.
+var slidesAtOnce = 3; // no. of slides to appear at once (requires odd number to have a centered slide)
+
+// -- * no editing should be needed below * -- //
+
+var originPosition = {};
+var is_animating = 0;
+var currentStripPosition = 0;
+var centeringPoint = 0;
+var rightScrollLimit = 0;
+
+// makeSlideStrip()
+// - figures out how many slides there are
+// - determines the centering point of the slide strip
+function makeSlideStrip() {
+  var slideTotal = 0;
+  centeringPoint = Math.ceil(slidesAtOnce/2);
+  for (var x in slideCode) {
+    slideTotal++;
+  }
+  var i = 0;
+  for (var code in slideCode) {
+    if (i <= centeringPoint-1) {
+      originPosition[code] = 0;
+    } else {
+      if (i >= slideTotal-centeringPoint+1)  {
+        originPosition[code] = (slideTotal-slidesAtOnce)*slideWidth;
+      } else {
+        originPosition[code] = (i-centeringPoint+1)*slideWidth;
+      }
+    }
+    i++;
+  }
+  rightScrollLimit = -1*(slideTotal-slidesAtOnce)*slideWidth;
+}
+
+// slides with acceleration
+function slide(goal, id, go_left, cp) {
+  var div = document.getElementById(id);
+  var animation = {};
+  animation.time = 0.5;  // in seconds
+  animation.fps = 60;
+  animation.goal = goal;
+  origin = 0.0;
+  animation.origin = Math.abs(origin);  
+  animation.frames = (animation.time * animation.fps) - 1.0;
+  var current_frame = 0;
+  var motions = Math.abs(animation.goal - animation.origin);
+  function animate() {
+    var ease_right = function (t) { return (1 - Math.cos(t * Math.PI))/2.0; };
+    var ease = ease_right;
+    if (go_left == 1) {
+      ease = function(t) { return 1.0 - ease_right(t); };
+    }
+    var left = (ease(current_frame/animation.frames) * Math.abs(animation.goal - animation.origin)) - cp; 
+    if(left < 0) {
+      left = 0;
+    }
+    if(!isNaN(left)) {
+      div.style.left = '-' + Math.round(left) + 'px';
+    }
+    current_frame += 1;
+    if (current_frame == animation.frames) {
+      is_animating = 0;
+      window.clearInterval(timeoutId)
+    }
+  }
+  var timeoutId = window.setInterval(animate, animation.time/animation.fps * 1000);
+}
+
+//Get style property
+function getStyle(element, cssProperty){
+  var elem = document.getElementById(element);
+  if(elem.currentStyle){
+    return elem.currentStyle[cssProperty]; //IE
+  } else{
+    var style =  document.defaultView.getComputedStyle(elem, null); //firefox, Opera
+    return style.getPropertyValue(cssProperty);
+  }
+}
+
+// Left and right arrows
+function page_left() {
+  var amount = slideWidth;
+  animateSlide(amount, 'left');
+}
+
+function page_right() { 
+  var amount = slideWidth;
+  animateSlide(amount, 'right');
+}
+
+
+// animates the strip
+// - sets arrows to on or off
+function animateSlide(amount,dir) {
+  var currentStripPosition = parseInt(getStyle(slideList,'left'));
+  var motionDistance;
+  if (amount == slideWidth ) {
+    motionDistance = slideWidth;
+  } else {
+    motionDistance = amount;
+  }
+  
+  var rightarrow = document.getElementById(arrowRight);
+  var leftarrow = document.getElementById(arrowLeft);
+  
+  function aToggle(state,aDir) {
+    if (state == 'on') {
+      if (aDir =='right') {
+        rightarrow.className = 'arrow-right-on';
+        rightarrow.href = "javascript:page_right()";
+      } else {
+        leftarrow.className = 'arrow-left-on';
+        leftarrow.href = "javascript:page_left()";
+      }
+    } else {
+      if (aDir =='right') {
+        rightarrow.href = "javascript:{}";
+        rightarrow.className = 'arrow-right-off'; 
+      } else {
+        leftarrow.href = "javascript:{}";
+        leftarrow.className = 'arrow-left-off';
+      }
+    }
+  }
+  
+  function arrowChange(rP) {
+    if (rP >= rightScrollLimit) {
+      aToggle('on','right');
+    }
+    if (rP <= rightScrollLimit) {
+      aToggle('off','right');
+    }
+    if (rP <= slideWidth) {
+      aToggle('on','left');
+    }
+    if (rP >= 0) {
+      aToggle('off','left');
+    }
+  }
+
+  if (dir == 'right' && is_animating == 0) {
+    arrowChange(currentStripPosition-motionDistance);
+    is_animating = 1;
+    slide(motionDistance, slideList, 0, currentStripPosition);
+  } else if (dir == 'left' && is_animating == 0) {
+    arrowChange(currentStripPosition+motionDistance);
+    is_animating = 1;
+    rightStripPosition = currentStripPosition + motionDistance;
+    slide(motionDistance, slideList, 1, rightStripPosition);
+  }
+}
+
+function centerSlide(slideName) {
+  var currentStripPosition = parseInt(getStyle(slideList,'left'));
+  var dir = 'left';
+  var originpoint = Math.abs(currentStripPosition);
+  if (originpoint <= originPosition[slideName]) {
+    dir = 'right';
+  }
+  var motionValue = Math.abs(originPosition[slideName]-originpoint);
+  animateSlide(motionValue,dir);
+}
+
+
+function initCarousel(def) {
+  buildCarousel();
+  showPreview(def);
+  makeSlideStrip();
+}
diff --git a/tools/droiddoc/templates/assets/images/android-developers-logo.png b/tools/droiddoc/templates/assets/images/android-developers-logo.png
new file mode 100644
index 0000000..30a8f62
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/android-developers-logo.png
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/android_wrench.png b/tools/droiddoc/templates/assets/images/android_wrench.png
new file mode 100644
index 0000000..6390a2d
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/android_wrench.png
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/arrow_left_off.jpg b/tools/droiddoc/templates/assets/images/arrow_left_off.jpg
new file mode 100755
index 0000000..fd32a64
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/arrow_left_off.jpg
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/arrow_left_on.jpg b/tools/droiddoc/templates/assets/images/arrow_left_on.jpg
new file mode 100755
index 0000000..143184b
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/arrow_left_on.jpg
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/arrow_right_off.jpg b/tools/droiddoc/templates/assets/images/arrow_right_off.jpg
new file mode 100755
index 0000000..17d2efe
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/arrow_right_off.jpg
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/arrow_right_on.jpg b/tools/droiddoc/templates/assets/images/arrow_right_on.jpg
new file mode 100755
index 0000000..baa2af1
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/arrow_right_on.jpg
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/bg_community_leftDiv.jpg b/tools/droiddoc/templates/assets/images/bg_community_leftDiv.jpg
new file mode 100755
index 0000000..a6d6f0e
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/bg_community_leftDiv.jpg
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/bg_fade.jpg b/tools/droiddoc/templates/assets/images/bg_fade.jpg
new file mode 100755
index 0000000..c6c70b6
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/bg_fade.jpg
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/bg_images_sprite.png b/tools/droiddoc/templates/assets/images/bg_images_sprite.png
new file mode 100755
index 0000000..84437e7
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/bg_images_sprite.png
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/bg_logo.png b/tools/droiddoc/templates/assets/images/bg_logo.png
new file mode 100755
index 0000000..8c57fc4
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/bg_logo.png
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/body-gradient-tab.png b/tools/droiddoc/templates/assets/images/body-gradient-tab.png
new file mode 100644
index 0000000..5223ac3
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/body-gradient-tab.png
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/body-gradient.png b/tools/droiddoc/templates/assets/images/body-gradient.png
new file mode 100755
index 0000000..9d59855
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/body-gradient.png
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/developers-logo.png b/tools/droiddoc/templates/assets/images/developers-logo.png
new file mode 100755
index 0000000..08122ee
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/developers-logo.png
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/grad-rule-qv.png b/tools/droiddoc/templates/assets/images/grad-rule-qv.png
new file mode 100644
index 0000000..bae2d18
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/grad-rule-qv.png
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/home/IO-logo.png b/tools/droiddoc/templates/assets/images/home/IO-logo.png
new file mode 100644
index 0000000..65334c8
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/home/IO-logo.png
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/home/bg_home_bottom.jpg b/tools/droiddoc/templates/assets/images/home/bg_home_bottom.jpg
new file mode 100755
index 0000000..dacd401
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/home/bg_home_bottom.jpg
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/home/bg_home_middle.png b/tools/droiddoc/templates/assets/images/home/bg_home_middle.png
new file mode 100644
index 0000000..4221e96
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/home/bg_home_middle.png
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/home/carousel_buttons_sprite.png b/tools/droiddoc/templates/assets/images/home/carousel_buttons_sprite.png
new file mode 100755
index 0000000..e98c942
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/home/carousel_buttons_sprite.png
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/home/devphone-large.png b/tools/droiddoc/templates/assets/images/home/devphone-large.png
new file mode 100755
index 0000000..cbf94c8
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/home/devphone-large.png
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/home/devphone-small.png b/tools/droiddoc/templates/assets/images/home/devphone-small.png
new file mode 100755
index 0000000..b8487f5
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/home/devphone-small.png
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/home/maps-large.png b/tools/droiddoc/templates/assets/images/home/maps-large.png
new file mode 100644
index 0000000..b26f65a
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/home/maps-large.png
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/home/maps-small.png b/tools/droiddoc/templates/assets/images/home/maps-small.png
new file mode 100644
index 0000000..cc5f1fa
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/home/maps-small.png
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/home/market-large.png b/tools/droiddoc/templates/assets/images/home/market-large.png
new file mode 100644
index 0000000..069fee7
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/home/market-large.png
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/home/market-small.png b/tools/droiddoc/templates/assets/images/home/market-small.png
new file mode 100644
index 0000000..fa1201c
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/home/market-small.png
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/home/sdk-large.png b/tools/droiddoc/templates/assets/images/home/sdk-large.png
new file mode 100644
index 0000000..315a1bf
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/home/sdk-large.png
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/home/sdk-small.png b/tools/droiddoc/templates/assets/images/home/sdk-small.png
new file mode 100644
index 0000000..0f1670d
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/home/sdk-small.png
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/hr_gray_main.jpg b/tools/droiddoc/templates/assets/images/hr_gray_main.jpg
new file mode 100755
index 0000000..f7a0a2f
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/hr_gray_main.jpg
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/hr_gray_side.jpg b/tools/droiddoc/templates/assets/images/hr_gray_side.jpg
new file mode 100755
index 0000000..6667476
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/hr_gray_side.jpg
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/icon_contribute.jpg b/tools/droiddoc/templates/assets/images/icon_contribute.jpg
new file mode 100755
index 0000000..1aa12b6
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/icon_contribute.jpg
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/icon_download.jpg b/tools/droiddoc/templates/assets/images/icon_download.jpg
new file mode 100755
index 0000000..f8c1165
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/icon_download.jpg
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/icon_download2.jpg b/tools/droiddoc/templates/assets/images/icon_download2.jpg
new file mode 100755
index 0000000..c0af7a2
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/icon_download2.jpg
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/icon_market.jpg b/tools/droiddoc/templates/assets/images/icon_market.jpg
new file mode 100755
index 0000000..225e727
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/icon_market.jpg
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/icon_robot.jpg b/tools/droiddoc/templates/assets/images/icon_robot.jpg
new file mode 100755
index 0000000..ca0fd39
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/icon_robot.jpg
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/icon_world.jpg b/tools/droiddoc/templates/assets/images/icon_world.jpg
new file mode 100755
index 0000000..65b8fa6
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/icon_world.jpg
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/left_off.jpg b/tools/droiddoc/templates/assets/images/left_off.jpg
new file mode 100755
index 0000000..fd32a64
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/left_off.jpg
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/left_on.jpg b/tools/droiddoc/templates/assets/images/left_on.jpg
new file mode 100755
index 0000000..143184b
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/left_on.jpg
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/logo_breadcrumbz.jpg b/tools/droiddoc/templates/assets/images/logo_breadcrumbz.jpg
new file mode 100755
index 0000000..e743f86
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/logo_breadcrumbz.jpg
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/preliminary.png b/tools/droiddoc/templates/assets/images/preliminary.png
new file mode 100755
index 0000000..aad1644
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/preliminary.png
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/resizable-e.gif b/tools/droiddoc/templates/assets/images/resizable-e.gif
new file mode 100755
index 0000000..f748097
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/resizable-e.gif
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/resizable-e2.gif b/tools/droiddoc/templates/assets/images/resizable-e2.gif
new file mode 100755
index 0000000..e45d0c5
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/resizable-e2.gif
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/resizable-eg.gif b/tools/droiddoc/templates/assets/images/resizable-eg.gif
new file mode 100755
index 0000000..6196616
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/resizable-eg.gif
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/resizable-s.gif b/tools/droiddoc/templates/assets/images/resizable-s.gif
new file mode 100755
index 0000000..7f6a4eb
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/resizable-s.gif
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/resizable-s2.gif b/tools/droiddoc/templates/assets/images/resizable-s2.gif
new file mode 100755
index 0000000..99e869c
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/resizable-s2.gif
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/resizable-sg.gif b/tools/droiddoc/templates/assets/images/resizable-sg.gif
new file mode 100755
index 0000000..b4bea10
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/resizable-sg.gif
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/right_off.jpg b/tools/droiddoc/templates/assets/images/right_off.jpg
new file mode 100755
index 0000000..17d2efe
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/right_off.jpg
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/right_on.jpg b/tools/droiddoc/templates/assets/images/right_on.jpg
new file mode 100755
index 0000000..baa2af1
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/right_on.jpg
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/sidenav-rule.png b/tools/droiddoc/templates/assets/images/sidenav-rule.png
new file mode 100644
index 0000000..eab9920
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/sidenav-rule.png
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/slide_1.jpg b/tools/droiddoc/templates/assets/images/slide_1.jpg
new file mode 100755
index 0000000..6d75be1
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/slide_1.jpg
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/slide_2.jpg b/tools/droiddoc/templates/assets/images/slide_2.jpg
new file mode 100755
index 0000000..aa994c2
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/slide_2.jpg
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/slide_3.jpg b/tools/droiddoc/templates/assets/images/slide_3.jpg
new file mode 100755
index 0000000..b04deb3
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/slide_3.jpg
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/slide_large_1.jpg b/tools/droiddoc/templates/assets/images/slide_large_1.jpg
new file mode 100755
index 0000000..a992e92
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/slide_large_1.jpg
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/slide_large_2.jpg b/tools/droiddoc/templates/assets/images/slide_large_2.jpg
new file mode 100755
index 0000000..9af63f4
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/slide_large_2.jpg
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/slide_large_3.jpg b/tools/droiddoc/templates/assets/images/slide_large_3.jpg
new file mode 100755
index 0000000..fcf236c
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/slide_large_3.jpg
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/slide_off.jpg b/tools/droiddoc/templates/assets/images/slide_off.jpg
new file mode 100755
index 0000000..5971227
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/slide_off.jpg
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/slide_on.jpg b/tools/droiddoc/templates/assets/images/slide_on.jpg
new file mode 100755
index 0000000..7ca3577
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/slide_on.jpg
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/spacer.gif b/tools/droiddoc/templates/assets/images/spacer.gif
new file mode 100755
index 0000000..f96b355
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/spacer.gif
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/triangle-closed-small.png b/tools/droiddoc/templates/assets/images/triangle-closed-small.png
new file mode 100644
index 0000000..002364a
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/triangle-closed-small.png
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/triangle-closed.png b/tools/droiddoc/templates/assets/images/triangle-closed.png
new file mode 100644
index 0000000..a34a055
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/triangle-closed.png
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/triangle-opened-small.png b/tools/droiddoc/templates/assets/images/triangle-opened-small.png
new file mode 100644
index 0000000..e1eb784
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/triangle-opened-small.png
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/triangle-opened.png b/tools/droiddoc/templates/assets/images/triangle-opened.png
new file mode 100644
index 0000000..a709604
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/triangle-opened.png
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/video-droid.png b/tools/droiddoc/templates/assets/images/video-droid.png
new file mode 100644
index 0000000..25163b6
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/video-droid.png
Binary files differ
diff --git a/tools/droiddoc/templates/assets/jdiff_logo.gif b/tools/droiddoc/templates/assets/jdiff_logo.gif
new file mode 100644
index 0000000..6970087
--- /dev/null
+++ b/tools/droiddoc/templates/assets/jdiff_logo.gif
Binary files differ
diff --git a/tools/droiddoc/templates/assets/jquery-history.js b/tools/droiddoc/templates/assets/jquery-history.js
new file mode 100644
index 0000000..ef96ec3
--- /dev/null
+++ b/tools/droiddoc/templates/assets/jquery-history.js
@@ -0,0 +1,78 @@
+/**
+ * jQuery history event v0.1
+ * Copyright (c) 2008 Tom Rodenberg <tarodenberg gmail com>
+ * Licensed under the GPL (http://www.gnu.org/licenses/gpl.html) license.
+ */
+(function($) {
+    var currentHash, previousNav, timer, hashTrim = /^.*#/;
+
+    var msie = {
+        iframe: null,
+        getDoc: function() {
+            return msie.iframe.contentWindow.document;
+        },
+        getHash: function() {
+            return msie.getDoc().location.hash;
+        },
+        setHash: function(hash) {
+            var d = msie.getDoc();
+            d.open();
+            d.close();
+            d.location.hash = hash;
+        }
+    };
+
+    var historycheck = function() {
+        var hash = msie.iframe ? msie.getHash() : location.hash;
+        if (hash != currentHash) {
+            currentHash = hash;
+            if (msie.iframe) {
+                location.hash = currentHash;
+            }
+            var current = $.history.getCurrent();
+            $.event.trigger('history', [current, previousNav]);
+            previousNav = current;
+        }
+    };
+
+    $.history = {
+        add: function(hash) {
+            hash = '#' + hash.replace(hashTrim, '');
+            if (currentHash != hash) {
+                var previous = $.history.getCurrent();
+                location.hash = currentHash = hash;
+                if (msie.iframe) {
+                    msie.setHash(currentHash);
+                }
+                $.event.trigger('historyadd', [$.history.getCurrent(), previous]);
+            }
+            if (!timer) {
+                timer = setInterval(historycheck, 100);
+            }
+        },
+        getCurrent: function() {
+            if (currentHash) {
+              return currentHash.replace(hashTrim, '');
+            } else { 
+              return ""; 
+            }
+        }
+    };
+
+    $.fn.history = function(fn) {
+        $(this).bind('history', fn);
+    };
+
+    $.fn.historyadd = function(fn) {
+        $(this).bind('historyadd', fn);
+    };
+
+    $(function() {
+        currentHash = location.hash;
+        if ($.browser.msie) {
+            msie.iframe = $('<iframe style="display:none" src="javascript:false;"></iframe>').prependTo('body')[0];
+            msie.setHash(currentHash);
+            currentHash = msie.getHash();
+        }
+    });
+})(jQuery);
diff --git a/tools/droiddoc/templates/assets/jquery-resizable.min.js b/tools/droiddoc/templates/assets/jquery-resizable.min.js
new file mode 100755
index 0000000..b3b4aed
--- /dev/null
+++ b/tools/droiddoc/templates/assets/jquery-resizable.min.js
@@ -0,0 +1,94 @@
+/*
+ * jQuery 1.2.6 - New Wave Javascript
+ *
+ * Copyright (c) 2008 John Resig (jquery.com)
+ * Dual licensed under the MIT (MIT-LICENSE.txt)
+ * and GPL (GPL-LICENSE.txt) licenses.
+ *
+ * $Date: 2008-05-24 14:22:17 -0400 (Sat, 24 May 2008) $
+ * $Rev: 5685 $
+ */
+/* Includes jQuery UI core and resizable */ 
+(function(){var _jQuery=window.jQuery,_$=window.$;var jQuery=window.jQuery=window.$=function(selector,context){return new jQuery.fn.init(selector,context);};var quickExpr=/^[^<]*(<(.|\s)+>)[^>]*$|^#(\w+)$/,isSimple=/^.[^:#\[\.]*$/,undefined;jQuery.fn=jQuery.prototype={init:function(selector,context){selector=selector||document;if(selector.nodeType){this[0]=selector;this.length=1;return this;}if(typeof selector=="string"){var match=quickExpr.exec(selector);if(match&&(match[1]||!context)){if(match[1])selector=jQuery.clean([match[1]],context);else{var elem=document.getElementById(match[3]);if(elem){if(elem.id!=match[3])return jQuery().find(selector);return jQuery(elem);}selector=[];}}else
+return jQuery(context).find(selector);}else if(jQuery.isFunction(selector))return jQuery(document)[jQuery.fn.ready?"ready":"load"](selector);return this.setArray(jQuery.makeArray(selector));},jquery:"1.2.6",size:function(){return this.length;},length:0,get:function(num){return num==undefined?jQuery.makeArray(this):this[num];},pushStack:function(elems){var ret=jQuery(elems);ret.prevObject=this;return ret;},setArray:function(elems){this.length=0;Array.prototype.push.apply(this,elems);return this;},each:function(callback,args){return jQuery.each(this,callback,args);},index:function(elem){var ret=-1;return jQuery.inArray(elem&&elem.jquery?elem[0]:elem,this);},attr:function(name,value,type){var options=name;if(name.constructor==String)if(value===undefined)return this[0]&&jQuery[type||"attr"](this[0],name);else{options={};options[name]=value;}return this.each(function(i){for(name in options)jQuery.attr(type?this.style:this,name,jQuery.prop(this,options[name],type,i,name));});},css:function(key,value){if((key=='width'||key=='height')&&parseFloat(value)<0)value=undefined;return this.attr(key,value,"curCSS");},text:function(text){if(typeof text!="object"&&text!=null)return this.empty().append((this[0]&&this[0].ownerDocument||document).createTextNode(text));var ret="";jQuery.each(text||this,function(){jQuery.each(this.childNodes,function(){if(this.nodeType!=8)ret+=this.nodeType!=1?this.nodeValue:jQuery.fn.text([this]);});});return ret;},wrapAll:function(html){if(this[0])jQuery(html,this[0].ownerDocument).clone().insertBefore(this[0]).map(function(){var elem=this;while(elem.firstChild)elem=elem.firstChild;return elem;}).append(this);return this;},wrapInner:function(html){return this.each(function(){jQuery(this).contents().wrapAll(html);});},wrap:function(html){return this.each(function(){jQuery(this).wrapAll(html);});},append:function(){return this.domManip(arguments,true,false,function(elem){if(this.nodeType==1)this.appendChild(elem);});},prepend:function(){return this.domManip(arguments,true,true,function(elem){if(this.nodeType==1)this.insertBefore(elem,this.firstChild);});},before:function(){return this.domManip(arguments,false,false,function(elem){this.parentNode.insertBefore(elem,this);});},after:function(){return this.domManip(arguments,false,true,function(elem){this.parentNode.insertBefore(elem,this.nextSibling);});},end:function(){return this.prevObject||jQuery([]);},find:function(selector){var elems=jQuery.map(this,function(elem){return jQuery.find(selector,elem);});return this.pushStack(/[^+>] [^+>]/.test(selector)||selector.indexOf("..")>-1?jQuery.unique(elems):elems);},clone:function(events){var ret=this.map(function(){if(jQuery.browser.msie&&!jQuery.isXMLDoc(this)){var clone=this.cloneNode(true),container=document.createElement("div");container.appendChild(clone);return jQuery.clean([container.innerHTML])[0];}else
+return this.cloneNode(true);});var clone=ret.find("*").andSelf().each(function(){if(this[expando]!=undefined)this[expando]=null;});if(events===true)this.find("*").andSelf().each(function(i){if(this.nodeType==3)return;var events=jQuery.data(this,"events");for(var type in events)for(var handler in events[type])jQuery.event.add(clone[i],type,events[type][handler],events[type][handler].data);});return ret;},filter:function(selector){return this.pushStack(jQuery.isFunction(selector)&&jQuery.grep(this,function(elem,i){return selector.call(elem,i);})||jQuery.multiFilter(selector,this));},not:function(selector){if(selector.constructor==String)if(isSimple.test(selector))return this.pushStack(jQuery.multiFilter(selector,this,true));else
+selector=jQuery.multiFilter(selector,this);var isArrayLike=selector.length&&selector[selector.length-1]!==undefined&&!selector.nodeType;return this.filter(function(){return isArrayLike?jQuery.inArray(this,selector)<0:this!=selector;});},add:function(selector){return this.pushStack(jQuery.unique(jQuery.merge(this.get(),typeof selector=='string'?jQuery(selector):jQuery.makeArray(selector))));},is:function(selector){return!!selector&&jQuery.multiFilter(selector,this).length>0;},hasClass:function(selector){return this.is("."+selector);},val:function(value){if(value==undefined){if(this.length){var elem=this[0];if(jQuery.nodeName(elem,"select")){var index=elem.selectedIndex,values=[],options=elem.options,one=elem.type=="select-one";if(index<0)return null;for(var i=one?index:0,max=one?index+1:options.length;i<max;i++){var option=options[i];if(option.selected){value=jQuery.browser.msie&&!option.attributes.value.specified?option.text:option.value;if(one)return value;values.push(value);}}return values;}else
+return(this[0].value||"").replace(/\r/g,"");}return undefined;}if(value.constructor==Number)value+='';return this.each(function(){if(this.nodeType!=1)return;if(value.constructor==Array&&/radio|checkbox/.test(this.type))this.checked=(jQuery.inArray(this.value,value)>=0||jQuery.inArray(this.name,value)>=0);else if(jQuery.nodeName(this,"select")){var values=jQuery.makeArray(value);jQuery("option",this).each(function(){this.selected=(jQuery.inArray(this.value,values)>=0||jQuery.inArray(this.text,values)>=0);});if(!values.length)this.selectedIndex=-1;}else
+this.value=value;});},html:function(value){return value==undefined?(this[0]?this[0].innerHTML:null):this.empty().append(value);},replaceWith:function(value){return this.after(value).remove();},eq:function(i){return this.slice(i,i+1);},slice:function(){return this.pushStack(Array.prototype.slice.apply(this,arguments));},map:function(callback){return this.pushStack(jQuery.map(this,function(elem,i){return callback.call(elem,i,elem);}));},andSelf:function(){return this.add(this.prevObject);},data:function(key,value){var parts=key.split(".");parts[1]=parts[1]?"."+parts[1]:"";if(value===undefined){var data=this.triggerHandler("getData"+parts[1]+"!",[parts[0]]);if(data===undefined&&this.length)data=jQuery.data(this[0],key);return data===undefined&&parts[1]?this.data(parts[0]):data;}else
+return this.trigger("setData"+parts[1]+"!",[parts[0],value]).each(function(){jQuery.data(this,key,value);});},removeData:function(key){return this.each(function(){jQuery.removeData(this,key);});},domManip:function(args,table,reverse,callback){var clone=this.length>1,elems;return this.each(function(){if(!elems){elems=jQuery.clean(args,this.ownerDocument);if(reverse)elems.reverse();}var obj=this;if(table&&jQuery.nodeName(this,"table")&&jQuery.nodeName(elems[0],"tr"))obj=this.getElementsByTagName("tbody")[0]||this.appendChild(this.ownerDocument.createElement("tbody"));var scripts=jQuery([]);jQuery.each(elems,function(){var elem=clone?jQuery(this).clone(true)[0]:this;if(jQuery.nodeName(elem,"script"))scripts=scripts.add(elem);else{if(elem.nodeType==1)scripts=scripts.add(jQuery("script",elem).remove());callback.call(obj,elem);}});scripts.each(evalScript);});}};jQuery.fn.init.prototype=jQuery.fn;function evalScript(i,elem){if(elem.src)jQuery.ajax({url:elem.src,async:false,dataType:"script"});else
+jQuery.globalEval(elem.text||elem.textContent||elem.innerHTML||"");if(elem.parentNode)elem.parentNode.removeChild(elem);}function now(){return+new Date;}jQuery.extend=jQuery.fn.extend=function(){var target=arguments[0]||{},i=1,length=arguments.length,deep=false,options;if(target.constructor==Boolean){deep=target;target=arguments[1]||{};i=2;}if(typeof target!="object"&&typeof target!="function")target={};if(length==i){target=this;--i;}for(;i<length;i++)if((options=arguments[i])!=null)for(var name in options){var src=target[name],copy=options[name];if(target===copy)continue;if(deep&&copy&&typeof copy=="object"&&!copy.nodeType)target[name]=jQuery.extend(deep,src||(copy.length!=null?[]:{}),copy);else if(copy!==undefined)target[name]=copy;}return target;};var expando="jQuery"+now(),uuid=0,windowData={},exclude=/z-?index|font-?weight|opacity|zoom|line-?height/i,defaultView=document.defaultView||{};jQuery.extend({noConflict:function(deep){window.$=_$;if(deep)window.jQuery=_jQuery;return jQuery;},isFunction:function(fn){return!!fn&&typeof fn!="string"&&!fn.nodeName&&fn.constructor!=Array&&/^[\s[]?function/.test(fn+"");},isXMLDoc:function(elem){return elem.documentElement&&!elem.body||elem.tagName&&elem.ownerDocument&&!elem.ownerDocument.body;},globalEval:function(data){data=jQuery.trim(data);if(data){var head=document.getElementsByTagName("head")[0]||document.documentElement,script=document.createElement("script");script.type="text/javascript";if(jQuery.browser.msie)script.text=data;else
+script.appendChild(document.createTextNode(data));head.insertBefore(script,head.firstChild);head.removeChild(script);}},nodeName:function(elem,name){return elem.nodeName&&elem.nodeName.toUpperCase()==name.toUpperCase();},cache:{},data:function(elem,name,data){elem=elem==window?windowData:elem;var id=elem[expando];if(!id)id=elem[expando]=++uuid;if(name&&!jQuery.cache[id])jQuery.cache[id]={};if(data!==undefined)jQuery.cache[id][name]=data;return name?jQuery.cache[id][name]:id;},removeData:function(elem,name){elem=elem==window?windowData:elem;var id=elem[expando];if(name){if(jQuery.cache[id]){delete jQuery.cache[id][name];name="";for(name in jQuery.cache[id])break;if(!name)jQuery.removeData(elem);}}else{try{delete elem[expando];}catch(e){if(elem.removeAttribute)elem.removeAttribute(expando);}delete jQuery.cache[id];}},each:function(object,callback,args){var name,i=0,length=object.length;if(args){if(length==undefined){for(name in object)if(callback.apply(object[name],args)===false)break;}else
+for(;i<length;)if(callback.apply(object[i++],args)===false)break;}else{if(length==undefined){for(name in object)if(callback.call(object[name],name,object[name])===false)break;}else
+for(var value=object[0];i<length&&callback.call(value,i,value)!==false;value=object[++i]){}}return object;},prop:function(elem,value,type,i,name){if(jQuery.isFunction(value))value=value.call(elem,i);return value&&value.constructor==Number&&type=="curCSS"&&!exclude.test(name)?value+"px":value;},className:{add:function(elem,classNames){jQuery.each((classNames||"").split(/\s+/),function(i,className){if(elem.nodeType==1&&!jQuery.className.has(elem.className,className))elem.className+=(elem.className?" ":"")+className;});},remove:function(elem,classNames){if(elem.nodeType==1)elem.className=classNames!=undefined?jQuery.grep(elem.className.split(/\s+/),function(className){return!jQuery.className.has(classNames,className);}).join(" "):"";},has:function(elem,className){return jQuery.inArray(className,(elem.className||elem).toString().split(/\s+/))>-1;}},swap:function(elem,options,callback){var old={};for(var name in options){old[name]=elem.style[name];elem.style[name]=options[name];}callback.call(elem);for(var name in options)elem.style[name]=old[name];},css:function(elem,name,force){if(name=="width"||name=="height"){var val,props={position:"absolute",visibility:"hidden",display:"block"},which=name=="width"?["Left","Right"]:["Top","Bottom"];function getWH(){val=name=="width"?elem.offsetWidth:elem.offsetHeight;var padding=0,border=0;jQuery.each(which,function(){padding+=parseFloat(jQuery.curCSS(elem,"padding"+this,true))||0;border+=parseFloat(jQuery.curCSS(elem,"border"+this+"Width",true))||0;});val-=Math.round(padding+border);}if(jQuery(elem).is(":visible"))getWH();else
+jQuery.swap(elem,props,getWH);return Math.max(0,val);}return jQuery.curCSS(elem,name,force);},curCSS:function(elem,name,force){var ret,style=elem.style;function color(elem){if(!jQuery.browser.safari)return false;var ret=defaultView.getComputedStyle(elem,null);return!ret||ret.getPropertyValue("color")=="";}if(name=="opacity"&&jQuery.browser.msie){ret=jQuery.attr(style,"opacity");return ret==""?"1":ret;}if(jQuery.browser.opera&&name=="display"){var save=style.outline;style.outline="0 solid black";style.outline=save;}if(name.match(/float/i))name=styleFloat;if(!force&&style&&style[name])ret=style[name];else if(defaultView.getComputedStyle){if(name.match(/float/i))name="float";name=name.replace(/([A-Z])/g,"-$1").toLowerCase();var computedStyle=defaultView.getComputedStyle(elem,null);if(computedStyle&&!color(elem))ret=computedStyle.getPropertyValue(name);else{var swap=[],stack=[],a=elem,i=0;for(;a&&color(a);a=a.parentNode)stack.unshift(a);for(;i<stack.length;i++)if(color(stack[i])){swap[i]=stack[i].style.display;stack[i].style.display="block";}ret=name=="display"&&swap[stack.length-1]!=null?"none":(computedStyle&&computedStyle.getPropertyValue(name))||"";for(i=0;i<swap.length;i++)if(swap[i]!=null)stack[i].style.display=swap[i];}if(name=="opacity"&&ret=="")ret="1";}else if(elem.currentStyle){var camelCase=name.replace(/\-(\w)/g,function(all,letter){return letter.toUpperCase();});ret=elem.currentStyle[name]||elem.currentStyle[camelCase];if(!/^\d+(px)?$/i.test(ret)&&/^\d/.test(ret)){var left=style.left,rsLeft=elem.runtimeStyle.left;elem.runtimeStyle.left=elem.currentStyle.left;style.left=ret||0;ret=style.pixelLeft+"px";style.left=left;elem.runtimeStyle.left=rsLeft;}}return ret;},clean:function(elems,context){var ret=[];context=context||document;if(typeof context.createElement=='undefined')context=context.ownerDocument||context[0]&&context[0].ownerDocument||document;jQuery.each(elems,function(i,elem){if(!elem)return;if(elem.constructor==Number)elem+='';if(typeof elem=="string"){elem=elem.replace(/(<(\w+)[^>]*?)\/>/g,function(all,front,tag){return tag.match(/^(abbr|br|col|img|input|link|meta|param|hr|area|embed)$/i)?all:front+"></"+tag+">";});var tags=jQuery.trim(elem).toLowerCase(),div=context.createElement("div");var wrap=!tags.indexOf("<opt")&&[1,"<select multiple='multiple'>","</select>"]||!tags.indexOf("<leg")&&[1,"<fieldset>","</fieldset>"]||tags.match(/^<(thead|tbody|tfoot|colg|cap)/)&&[1,"<table>","</table>"]||!tags.indexOf("<tr")&&[2,"<table><tbody>","</tbody></table>"]||(!tags.indexOf("<td")||!tags.indexOf("<th"))&&[3,"<table><tbody><tr>","</tr></tbody></table>"]||!tags.indexOf("<col")&&[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"]||jQuery.browser.msie&&[1,"div<div>","</div>"]||[0,"",""];div.innerHTML=wrap[1]+elem+wrap[2];while(wrap[0]--)div=div.lastChild;if(jQuery.browser.msie){var tbody=!tags.indexOf("<table")&&tags.indexOf("<tbody")<0?div.firstChild&&div.firstChild.childNodes:wrap[1]=="<table>"&&tags.indexOf("<tbody")<0?div.childNodes:[];for(var j=tbody.length-1;j>=0;--j)if(jQuery.nodeName(tbody[j],"tbody")&&!tbody[j].childNodes.length)tbody[j].parentNode.removeChild(tbody[j]);if(/^\s/.test(elem))div.insertBefore(context.createTextNode(elem.match(/^\s*/)[0]),div.firstChild);}elem=jQuery.makeArray(div.childNodes);}if(elem.length===0&&(!jQuery.nodeName(elem,"form")&&!jQuery.nodeName(elem,"select")))return;if(elem[0]==undefined||jQuery.nodeName(elem,"form")||elem.options)ret.push(elem);else
+ret=jQuery.merge(ret,elem);});return ret;},attr:function(elem,name,value){if(!elem||elem.nodeType==3||elem.nodeType==8)return undefined;var notxml=!jQuery.isXMLDoc(elem),set=value!==undefined,msie=jQuery.browser.msie;name=notxml&&jQuery.props[name]||name;if(elem.tagName){var special=/href|src|style/.test(name);if(name=="selected"&&jQuery.browser.safari)elem.parentNode.selectedIndex;if(name in elem&&notxml&&!special){if(set){if(name=="type"&&jQuery.nodeName(elem,"input")&&elem.parentNode)throw"type property can't be changed";elem[name]=value;}if(jQuery.nodeName(elem,"form")&&elem.getAttributeNode(name))return elem.getAttributeNode(name).nodeValue;return elem[name];}if(msie&&notxml&&name=="style")return jQuery.attr(elem.style,"cssText",value);if(set)elem.setAttribute(name,""+value);var attr=msie&&notxml&&special?elem.getAttribute(name,2):elem.getAttribute(name);return attr===null?undefined:attr;}if(msie&&name=="opacity"){if(set){elem.zoom=1;elem.filter=(elem.filter||"").replace(/alpha\([^)]*\)/,"")+(parseInt(value)+''=="NaN"?"":"alpha(opacity="+value*100+")");}return elem.filter&&elem.filter.indexOf("opacity=")>=0?(parseFloat(elem.filter.match(/opacity=([^)]*)/)[1])/100)+'':"";}name=name.replace(/-([a-z])/ig,function(all,letter){return letter.toUpperCase();});if(set)elem[name]=value;return elem[name];},trim:function(text){return(text||"").replace(/^\s+|\s+$/g,"");},makeArray:function(array){var ret=[];if(array!=null){var i=array.length;if(i==null||array.split||array.setInterval||array.call)ret[0]=array;else
+while(i)ret[--i]=array[i];}return ret;},inArray:function(elem,array){for(var i=0,length=array.length;i<length;i++)if(array[i]===elem)return i;return-1;},merge:function(first,second){var i=0,elem,pos=first.length;if(jQuery.browser.msie){while(elem=second[i++])if(elem.nodeType!=8)first[pos++]=elem;}else
+while(elem=second[i++])first[pos++]=elem;return first;},unique:function(array){var ret=[],done={};try{for(var i=0,length=array.length;i<length;i++){var id=jQuery.data(array[i]);if(!done[id]){done[id]=true;ret.push(array[i]);}}}catch(e){ret=array;}return ret;},grep:function(elems,callback,inv){var ret=[];for(var i=0,length=elems.length;i<length;i++)if(!inv!=!callback(elems[i],i))ret.push(elems[i]);return ret;},map:function(elems,callback){var ret=[];for(var i=0,length=elems.length;i<length;i++){var value=callback(elems[i],i);if(value!=null)ret[ret.length]=value;}return ret.concat.apply([],ret);}});var userAgent=navigator.userAgent.toLowerCase();jQuery.browser={version:(userAgent.match(/.+(?:rv|it|ra|ie)[\/: ]([\d.]+)/)||[])[1],safari:/webkit/.test(userAgent),opera:/opera/.test(userAgent),msie:/msie/.test(userAgent)&&!/opera/.test(userAgent),mozilla:/mozilla/.test(userAgent)&&!/(compatible|webkit)/.test(userAgent)};var styleFloat=jQuery.browser.msie?"styleFloat":"cssFloat";jQuery.extend({boxModel:!jQuery.browser.msie||document.compatMode=="CSS1Compat",props:{"for":"htmlFor","class":"className","float":styleFloat,cssFloat:styleFloat,styleFloat:styleFloat,readonly:"readOnly",maxlength:"maxLength",cellspacing:"cellSpacing"}});jQuery.each({parent:function(elem){return elem.parentNode;},parents:function(elem){return jQuery.dir(elem,"parentNode");},next:function(elem){return jQuery.nth(elem,2,"nextSibling");},prev:function(elem){return jQuery.nth(elem,2,"previousSibling");},nextAll:function(elem){return jQuery.dir(elem,"nextSibling");},prevAll:function(elem){return jQuery.dir(elem,"previousSibling");},siblings:function(elem){return jQuery.sibling(elem.parentNode.firstChild,elem);},children:function(elem){return jQuery.sibling(elem.firstChild);},contents:function(elem){return jQuery.nodeName(elem,"iframe")?elem.contentDocument||elem.contentWindow.document:jQuery.makeArray(elem.childNodes);}},function(name,fn){jQuery.fn[name]=function(selector){var ret=jQuery.map(this,fn);if(selector&&typeof selector=="string")ret=jQuery.multiFilter(selector,ret);return this.pushStack(jQuery.unique(ret));};});jQuery.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(name,original){jQuery.fn[name]=function(){var args=arguments;return this.each(function(){for(var i=0,length=args.length;i<length;i++)jQuery(args[i])[original](this);});};});jQuery.each({removeAttr:function(name){jQuery.attr(this,name,"");if(this.nodeType==1)this.removeAttribute(name);},addClass:function(classNames){jQuery.className.add(this,classNames);},removeClass:function(classNames){jQuery.className.remove(this,classNames);},toggleClass:function(classNames){jQuery.className[jQuery.className.has(this,classNames)?"remove":"add"](this,classNames);},remove:function(selector){if(!selector||jQuery.filter(selector,[this]).r.length){jQuery("*",this).add(this).each(function(){jQuery.event.remove(this);jQuery.removeData(this);});if(this.parentNode)this.parentNode.removeChild(this);}},empty:function(){jQuery(">*",this).remove();while(this.firstChild)this.removeChild(this.firstChild);}},function(name,fn){jQuery.fn[name]=function(){return this.each(fn,arguments);};});jQuery.each(["Height","Width"],function(i,name){var type=name.toLowerCase();jQuery.fn[type]=function(size){return this[0]==window?jQuery.browser.opera&&document.body["client"+name]||jQuery.browser.safari&&window["inner"+name]||document.compatMode=="CSS1Compat"&&document.documentElement["client"+name]||document.body["client"+name]:this[0]==document?Math.max(Math.max(document.body["scroll"+name],document.documentElement["scroll"+name]),Math.max(document.body["offset"+name],document.documentElement["offset"+name])):size==undefined?(this.length?jQuery.css(this[0],type):null):this.css(type,size.constructor==String?size:size+"px");};});function num(elem,prop){return elem[0]&&parseInt(jQuery.curCSS(elem[0],prop,true),10)||0;}var chars=jQuery.browser.safari&&parseInt(jQuery.browser.version)<417?"(?:[\\w*_-]|\\\\.)":"(?:[\\w\u0128-\uFFFF*_-]|\\\\.)",quickChild=new RegExp("^>\\s*("+chars+"+)"),quickID=new RegExp("^("+chars+"+)(#)("+chars+"+)"),quickClass=new RegExp("^([#.]?)("+chars+"*)");jQuery.extend({expr:{"":function(a,i,m){return m[2]=="*"||jQuery.nodeName(a,m[2]);},"#":function(a,i,m){return a.getAttribute("id")==m[2];},":":{lt:function(a,i,m){return i<m[3]-0;},gt:function(a,i,m){return i>m[3]-0;},nth:function(a,i,m){return m[3]-0==i;},eq:function(a,i,m){return m[3]-0==i;},first:function(a,i){return i==0;},last:function(a,i,m,r){return i==r.length-1;},even:function(a,i){return i%2==0;},odd:function(a,i){return i%2;},"first-child":function(a){return a.parentNode.getElementsByTagName("*")[0]==a;},"last-child":function(a){return jQuery.nth(a.parentNode.lastChild,1,"previousSibling")==a;},"only-child":function(a){return!jQuery.nth(a.parentNode.lastChild,2,"previousSibling");},parent:function(a){return a.firstChild;},empty:function(a){return!a.firstChild;},contains:function(a,i,m){return(a.textContent||a.innerText||jQuery(a).text()||"").indexOf(m[3])>=0;},visible:function(a){return"hidden"!=a.type&&jQuery.css(a,"display")!="none"&&jQuery.css(a,"visibility")!="hidden";},hidden:function(a){return"hidden"==a.type||jQuery.css(a,"display")=="none"||jQuery.css(a,"visibility")=="hidden";},enabled:function(a){return!a.disabled;},disabled:function(a){return a.disabled;},checked:function(a){return a.checked;},selected:function(a){return a.selected||jQuery.attr(a,"selected");},text:function(a){return"text"==a.type;},radio:function(a){return"radio"==a.type;},checkbox:function(a){return"checkbox"==a.type;},file:function(a){return"file"==a.type;},password:function(a){return"password"==a.type;},submit:function(a){return"submit"==a.type;},image:function(a){return"image"==a.type;},reset:function(a){return"reset"==a.type;},button:function(a){return"button"==a.type||jQuery.nodeName(a,"button");},input:function(a){return/input|select|textarea|button/i.test(a.nodeName);},has:function(a,i,m){return jQuery.find(m[3],a).length;},header:function(a){return/h\d/i.test(a.nodeName);},animated:function(a){return jQuery.grep(jQuery.timers,function(fn){return a==fn.elem;}).length;}}},parse:[/^(\[) *@?([\w-]+) *([!*$^~=]*) *('?"?)(.*?)\4 *\]/,/^(:)([\w-]+)\("?'?(.*?(\(.*?\))?[^(]*?)"?'?\)/,new RegExp("^([:.#]*)("+chars+"+)")],multiFilter:function(expr,elems,not){var old,cur=[];while(expr&&expr!=old){old=expr;var f=jQuery.filter(expr,elems,not);expr=f.t.replace(/^\s*,\s*/,"");cur=not?elems=f.r:jQuery.merge(cur,f.r);}return cur;},find:function(t,context){if(typeof t!="string")return[t];if(context&&context.nodeType!=1&&context.nodeType!=9)return[];context=context||document;var ret=[context],done=[],last,nodeName;while(t&&last!=t){var r=[];last=t;t=jQuery.trim(t);var foundToken=false,re=quickChild,m=re.exec(t);if(m){nodeName=m[1].toUpperCase();for(var i=0;ret[i];i++)for(var c=ret[i].firstChild;c;c=c.nextSibling)if(c.nodeType==1&&(nodeName=="*"||c.nodeName.toUpperCase()==nodeName))r.push(c);ret=r;t=t.replace(re,"");if(t.indexOf(" ")==0)continue;foundToken=true;}else{re=/^([>+~])\s*(\w*)/i;if((m=re.exec(t))!=null){r=[];var merge={};nodeName=m[2].toUpperCase();m=m[1];for(var j=0,rl=ret.length;j<rl;j++){var n=m=="~"||m=="+"?ret[j].nextSibling:ret[j].firstChild;for(;n;n=n.nextSibling)if(n.nodeType==1){var id=jQuery.data(n);if(m=="~"&&merge[id])break;if(!nodeName||n.nodeName.toUpperCase()==nodeName){if(m=="~")merge[id]=true;r.push(n);}if(m=="+")break;}}ret=r;t=jQuery.trim(t.replace(re,""));foundToken=true;}}if(t&&!foundToken){if(!t.indexOf(",")){if(context==ret[0])ret.shift();done=jQuery.merge(done,ret);r=ret=[context];t=" "+t.substr(1,t.length);}else{var re2=quickID;var m=re2.exec(t);if(m){m=[0,m[2],m[3],m[1]];}else{re2=quickClass;m=re2.exec(t);}m[2]=m[2].replace(/\\/g,"");var elem=ret[ret.length-1];if(m[1]=="#"&&elem&&elem.getElementById&&!jQuery.isXMLDoc(elem)){var oid=elem.getElementById(m[2]);if((jQuery.browser.msie||jQuery.browser.opera)&&oid&&typeof oid.id=="string"&&oid.id!=m[2])oid=jQuery('[@id="'+m[2]+'"]',elem)[0];ret=r=oid&&(!m[3]||jQuery.nodeName(oid,m[3]))?[oid]:[];}else{for(var i=0;ret[i];i++){var tag=m[1]=="#"&&m[3]?m[3]:m[1]!=""||m[0]==""?"*":m[2];if(tag=="*"&&ret[i].nodeName.toLowerCase()=="object")tag="param";r=jQuery.merge(r,ret[i].getElementsByTagName(tag));}if(m[1]==".")r=jQuery.classFilter(r,m[2]);if(m[1]=="#"){var tmp=[];for(var i=0;r[i];i++)if(r[i].getAttribute("id")==m[2]){tmp=[r[i]];break;}r=tmp;}ret=r;}t=t.replace(re2,"");}}if(t){var val=jQuery.filter(t,r);ret=r=val.r;t=jQuery.trim(val.t);}}if(t)ret=[];if(ret&&context==ret[0])ret.shift();done=jQuery.merge(done,ret);return done;},classFilter:function(r,m,not){m=" "+m+" ";var tmp=[];for(var i=0;r[i];i++){var pass=(" "+r[i].className+" ").indexOf(m)>=0;if(!not&&pass||not&&!pass)tmp.push(r[i]);}return tmp;},filter:function(t,r,not){var last;while(t&&t!=last){last=t;var p=jQuery.parse,m;for(var i=0;p[i];i++){m=p[i].exec(t);if(m){t=t.substring(m[0].length);m[2]=m[2].replace(/\\/g,"");break;}}if(!m)break;if(m[1]==":"&&m[2]=="not")r=isSimple.test(m[3])?jQuery.filter(m[3],r,true).r:jQuery(r).not(m[3]);else if(m[1]==".")r=jQuery.classFilter(r,m[2],not);else if(m[1]=="["){var tmp=[],type=m[3];for(var i=0,rl=r.length;i<rl;i++){var a=r[i],z=a[jQuery.props[m[2]]||m[2]];if(z==null||/href|src|selected/.test(m[2]))z=jQuery.attr(a,m[2])||'';if((type==""&&!!z||type=="="&&z==m[5]||type=="!="&&z!=m[5]||type=="^="&&z&&!z.indexOf(m[5])||type=="$="&&z.substr(z.length-m[5].length)==m[5]||(type=="*="||type=="~=")&&z.indexOf(m[5])>=0)^not)tmp.push(a);}r=tmp;}else if(m[1]==":"&&m[2]=="nth-child"){var merge={},tmp=[],test=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(m[3]=="even"&&"2n"||m[3]=="odd"&&"2n+1"||!/\D/.test(m[3])&&"0n+"+m[3]||m[3]),first=(test[1]+(test[2]||1))-0,last=test[3]-0;for(var i=0,rl=r.length;i<rl;i++){var node=r[i],parentNode=node.parentNode,id=jQuery.data(parentNode);if(!merge[id]){var c=1;for(var n=parentNode.firstChild;n;n=n.nextSibling)if(n.nodeType==1)n.nodeIndex=c++;merge[id]=true;}var add=false;if(first==0){if(node.nodeIndex==last)add=true;}else if((node.nodeIndex-last)%first==0&&(node.nodeIndex-last)/first>=0)add=true;if(add^not)tmp.push(node);}r=tmp;}else{var fn=jQuery.expr[m[1]];if(typeof fn=="object")fn=fn[m[2]];if(typeof fn=="string")fn=eval("false||function(a,i){return "+fn+";}");r=jQuery.grep(r,function(elem,i){return fn(elem,i,m,r);},not);}}return{r:r,t:t};},dir:function(elem,dir){var matched=[],cur=elem[dir];while(cur&&cur!=document){if(cur.nodeType==1)matched.push(cur);cur=cur[dir];}return matched;},nth:function(cur,result,dir,elem){result=result||1;var num=0;for(;cur;cur=cur[dir])if(cur.nodeType==1&&++num==result)break;return cur;},sibling:function(n,elem){var r=[];for(;n;n=n.nextSibling){if(n.nodeType==1&&n!=elem)r.push(n);}return r;}});jQuery.event={add:function(elem,types,handler,data){if(elem.nodeType==3||elem.nodeType==8)return;if(jQuery.browser.msie&&elem.setInterval)elem=window;if(!handler.guid)handler.guid=this.guid++;if(data!=undefined){var fn=handler;handler=this.proxy(fn,function(){return fn.apply(this,arguments);});handler.data=data;}var events=jQuery.data(elem,"events")||jQuery.data(elem,"events",{}),handle=jQuery.data(elem,"handle")||jQuery.data(elem,"handle",function(){if(typeof jQuery!="undefined"&&!jQuery.event.triggered)return jQuery.event.handle.apply(arguments.callee.elem,arguments);});handle.elem=elem;jQuery.each(types.split(/\s+/),function(index,type){var parts=type.split(".");type=parts[0];handler.type=parts[1];var handlers=events[type];if(!handlers){handlers=events[type]={};if(!jQuery.event.special[type]||jQuery.event.special[type].setup.call(elem)===false){if(elem.addEventListener)elem.addEventListener(type,handle,false);else if(elem.attachEvent)elem.attachEvent("on"+type,handle);}}handlers[handler.guid]=handler;jQuery.event.global[type]=true;});elem=null;},guid:1,global:{},remove:function(elem,types,handler){if(elem.nodeType==3||elem.nodeType==8)return;var events=jQuery.data(elem,"events"),ret,index;if(events){if(types==undefined||(typeof types=="string"&&types.charAt(0)=="."))for(var type in events)this.remove(elem,type+(types||""));else{if(types.type){handler=types.handler;types=types.type;}jQuery.each(types.split(/\s+/),function(index,type){var parts=type.split(".");type=parts[0];if(events[type]){if(handler)delete events[type][handler.guid];else
+for(handler in events[type])if(!parts[1]||events[type][handler].type==parts[1])delete events[type][handler];for(ret in events[type])break;if(!ret){if(!jQuery.event.special[type]||jQuery.event.special[type].teardown.call(elem)===false){if(elem.removeEventListener)elem.removeEventListener(type,jQuery.data(elem,"handle"),false);else if(elem.detachEvent)elem.detachEvent("on"+type,jQuery.data(elem,"handle"));}ret=null;delete events[type];}}});}for(ret in events)break;if(!ret){var handle=jQuery.data(elem,"handle");if(handle)handle.elem=null;jQuery.removeData(elem,"events");jQuery.removeData(elem,"handle");}}},trigger:function(type,data,elem,donative,extra){data=jQuery.makeArray(data);if(type.indexOf("!")>=0){type=type.slice(0,-1);var exclusive=true;}if(!elem){if(this.global[type])jQuery("*").add([window,document]).trigger(type,data);}else{if(elem.nodeType==3||elem.nodeType==8)return undefined;var val,ret,fn=jQuery.isFunction(elem[type]||null),event=!data[0]||!data[0].preventDefault;if(event){data.unshift({type:type,target:elem,preventDefault:function(){},stopPropagation:function(){},timeStamp:now()});data[0][expando]=true;}data[0].type=type;if(exclusive)data[0].exclusive=true;var handle=jQuery.data(elem,"handle");if(handle)val=handle.apply(elem,data);if((!fn||(jQuery.nodeName(elem,'a')&&type=="click"))&&elem["on"+type]&&elem["on"+type].apply(elem,data)===false)val=false;if(event)data.shift();if(extra&&jQuery.isFunction(extra)){ret=extra.apply(elem,val==null?data:data.concat(val));if(ret!==undefined)val=ret;}if(fn&&donative!==false&&val!==false&&!(jQuery.nodeName(elem,'a')&&type=="click")){this.triggered=true;try{elem[type]();}catch(e){}}this.triggered=false;}return val;},handle:function(event){var val,ret,namespace,all,handlers;event=arguments[0]=jQuery.event.fix(event||window.event);namespace=event.type.split(".");event.type=namespace[0];namespace=namespace[1];all=!namespace&&!event.exclusive;handlers=(jQuery.data(this,"events")||{})[event.type];for(var j in handlers){var handler=handlers[j];if(all||handler.type==namespace){event.handler=handler;event.data=handler.data;ret=handler.apply(this,arguments);if(val!==false)val=ret;if(ret===false){event.preventDefault();event.stopPropagation();}}}return val;},fix:function(event){if(event[expando]==true)return event;var originalEvent=event;event={originalEvent:originalEvent};var props="altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode metaKey newValue originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target timeStamp toElement type view wheelDelta which".split(" ");for(var i=props.length;i;i--)event[props[i]]=originalEvent[props[i]];event[expando]=true;event.preventDefault=function(){if(originalEvent.preventDefault)originalEvent.preventDefault();originalEvent.returnValue=false;};event.stopPropagation=function(){if(originalEvent.stopPropagation)originalEvent.stopPropagation();originalEvent.cancelBubble=true;};event.timeStamp=event.timeStamp||now();if(!event.target)event.target=event.srcElement||document;if(event.target.nodeType==3)event.target=event.target.parentNode;if(!event.relatedTarget&&event.fromElement)event.relatedTarget=event.fromElement==event.target?event.toElement:event.fromElement;if(event.pageX==null&&event.clientX!=null){var doc=document.documentElement,body=document.body;event.pageX=event.clientX+(doc&&doc.scrollLeft||body&&body.scrollLeft||0)-(doc.clientLeft||0);event.pageY=event.clientY+(doc&&doc.scrollTop||body&&body.scrollTop||0)-(doc.clientTop||0);}if(!event.which&&((event.charCode||event.charCode===0)?event.charCode:event.keyCode))event.which=event.charCode||event.keyCode;if(!event.metaKey&&event.ctrlKey)event.metaKey=event.ctrlKey;if(!event.which&&event.button)event.which=(event.button&1?1:(event.button&2?3:(event.button&4?2:0)));return event;},proxy:function(fn,proxy){proxy.guid=fn.guid=fn.guid||proxy.guid||this.guid++;return proxy;},special:{ready:{setup:function(){bindReady();return;},teardown:function(){return;}},mouseenter:{setup:function(){if(jQuery.browser.msie)return false;jQuery(this).bind("mouseover",jQuery.event.special.mouseenter.handler);return true;},teardown:function(){if(jQuery.browser.msie)return false;jQuery(this).unbind("mouseover",jQuery.event.special.mouseenter.handler);return true;},handler:function(event){if(withinElement(event,this))return true;event.type="mouseenter";return jQuery.event.handle.apply(this,arguments);}},mouseleave:{setup:function(){if(jQuery.browser.msie)return false;jQuery(this).bind("mouseout",jQuery.event.special.mouseleave.handler);return true;},teardown:function(){if(jQuery.browser.msie)return false;jQuery(this).unbind("mouseout",jQuery.event.special.mouseleave.handler);return true;},handler:function(event){if(withinElement(event,this))return true;event.type="mouseleave";return jQuery.event.handle.apply(this,arguments);}}}};jQuery.fn.extend({bind:function(type,data,fn){return type=="unload"?this.one(type,data,fn):this.each(function(){jQuery.event.add(this,type,fn||data,fn&&data);});},one:function(type,data,fn){var one=jQuery.event.proxy(fn||data,function(event){jQuery(this).unbind(event,one);return(fn||data).apply(this,arguments);});return this.each(function(){jQuery.event.add(this,type,one,fn&&data);});},unbind:function(type,fn){return this.each(function(){jQuery.event.remove(this,type,fn);});},trigger:function(type,data,fn){return this.each(function(){jQuery.event.trigger(type,data,this,true,fn);});},triggerHandler:function(type,data,fn){return this[0]&&jQuery.event.trigger(type,data,this[0],false,fn);},toggle:function(fn){var args=arguments,i=1;while(i<args.length)jQuery.event.proxy(fn,args[i++]);return this.click(jQuery.event.proxy(fn,function(event){this.lastToggle=(this.lastToggle||0)%i;event.preventDefault();return args[this.lastToggle++].apply(this,arguments)||false;}));},hover:function(fnOver,fnOut){return this.bind('mouseenter',fnOver).bind('mouseleave',fnOut);},ready:function(fn){bindReady();if(jQuery.isReady)fn.call(document,jQuery);else
+jQuery.readyList.push(function(){return fn.call(this,jQuery);});return this;}});jQuery.extend({isReady:false,readyList:[],ready:function(){if(!jQuery.isReady){jQuery.isReady=true;if(jQuery.readyList){jQuery.each(jQuery.readyList,function(){this.call(document);});jQuery.readyList=null;}jQuery(document).triggerHandler("ready");}}});var readyBound=false;function bindReady(){if(readyBound)return;readyBound=true;if(document.addEventListener&&!jQuery.browser.opera)document.addEventListener("DOMContentLoaded",jQuery.ready,false);if(jQuery.browser.msie&&window==top)(function(){if(jQuery.isReady)return;try{document.documentElement.doScroll("left");}catch(error){setTimeout(arguments.callee,0);return;}jQuery.ready();})();if(jQuery.browser.opera)document.addEventListener("DOMContentLoaded",function(){if(jQuery.isReady)return;for(var i=0;i<document.styleSheets.length;i++)if(document.styleSheets[i].disabled){setTimeout(arguments.callee,0);return;}jQuery.ready();},false);if(jQuery.browser.safari){var numStyles;(function(){if(jQuery.isReady)return;if(document.readyState!="loaded"&&document.readyState!="complete"){setTimeout(arguments.callee,0);return;}if(numStyles===undefined)numStyles=jQuery("style, link[rel=stylesheet]").length;if(document.styleSheets.length!=numStyles){setTimeout(arguments.callee,0);return;}jQuery.ready();})();}jQuery.event.add(window,"load",jQuery.ready);}jQuery.each(("blur,focus,load,resize,scroll,unload,click,dblclick,"+"mousedown,mouseup,mousemove,mouseover,mouseout,change,select,"+"submit,keydown,keypress,keyup,error").split(","),function(i,name){jQuery.fn[name]=function(fn){return fn?this.bind(name,fn):this.trigger(name);};});var withinElement=function(event,elem){var parent=event.relatedTarget;while(parent&&parent!=elem)try{parent=parent.parentNode;}catch(error){parent=elem;}return parent==elem;};jQuery(window).bind("unload",function(){jQuery("*").add(document).unbind();});jQuery.fn.extend({_load:jQuery.fn.load,load:function(url,params,callback){if(typeof url!='string')return this._load(url);var off=url.indexOf(" ");if(off>=0){var selector=url.slice(off,url.length);url=url.slice(0,off);}callback=callback||function(){};var type="GET";if(params)if(jQuery.isFunction(params)){callback=params;params=null;}else{params=jQuery.param(params);type="POST";}var self=this;jQuery.ajax({url:url,type:type,dataType:"html",data:params,complete:function(res,status){if(status=="success"||status=="notmodified")self.html(selector?jQuery("<div/>").append(res.responseText.replace(/<script(.|\s)*?\/script>/g,"")).find(selector):res.responseText);self.each(callback,[res.responseText,status,res]);}});return this;},serialize:function(){return jQuery.param(this.serializeArray());},serializeArray:function(){return this.map(function(){return jQuery.nodeName(this,"form")?jQuery.makeArray(this.elements):this;}).filter(function(){return this.name&&!this.disabled&&(this.checked||/select|textarea/i.test(this.nodeName)||/text|hidden|password/i.test(this.type));}).map(function(i,elem){var val=jQuery(this).val();return val==null?null:val.constructor==Array?jQuery.map(val,function(val,i){return{name:elem.name,value:val};}):{name:elem.name,value:val};}).get();}});jQuery.each("ajaxStart,ajaxStop,ajaxComplete,ajaxError,ajaxSuccess,ajaxSend".split(","),function(i,o){jQuery.fn[o]=function(f){return this.bind(o,f);};});var jsc=now();jQuery.extend({get:function(url,data,callback,type){if(jQuery.isFunction(data)){callback=data;data=null;}return jQuery.ajax({type:"GET",url:url,data:data,success:callback,dataType:type});},getScript:function(url,callback){return jQuery.get(url,null,callback,"script");},getJSON:function(url,data,callback){return jQuery.get(url,data,callback,"json");},post:function(url,data,callback,type){if(jQuery.isFunction(data)){callback=data;data={};}return jQuery.ajax({type:"POST",url:url,data:data,success:callback,dataType:type});},ajaxSetup:function(settings){jQuery.extend(jQuery.ajaxSettings,settings);},ajaxSettings:{url:location.href,global:true,type:"GET",timeout:0,contentType:"application/x-www-form-urlencoded",processData:true,async:true,data:null,username:null,password:null,accepts:{xml:"application/xml, text/xml",html:"text/html",script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},lastModified:{},ajax:function(s){s=jQuery.extend(true,s,jQuery.extend(true,{},jQuery.ajaxSettings,s));var jsonp,jsre=/=\?(&|$)/g,status,data,type=s.type.toUpperCase();if(s.data&&s.processData&&typeof s.data!="string")s.data=jQuery.param(s.data);if(s.dataType=="jsonp"){if(type=="GET"){if(!s.url.match(jsre))s.url+=(s.url.match(/\?/)?"&":"?")+(s.jsonp||"callback")+"=?";}else if(!s.data||!s.data.match(jsre))s.data=(s.data?s.data+"&":"")+(s.jsonp||"callback")+"=?";s.dataType="json";}if(s.dataType=="json"&&(s.data&&s.data.match(jsre)||s.url.match(jsre))){jsonp="jsonp"+jsc++;if(s.data)s.data=(s.data+"").replace(jsre,"="+jsonp+"$1");s.url=s.url.replace(jsre,"="+jsonp+"$1");s.dataType="script";window[jsonp]=function(tmp){data=tmp;success();complete();window[jsonp]=undefined;try{delete window[jsonp];}catch(e){}if(head)head.removeChild(script);};}if(s.dataType=="script"&&s.cache==null)s.cache=false;if(s.cache===false&&type=="GET"){var ts=now();var ret=s.url.replace(/(\?|&)_=.*?(&|$)/,"$1_="+ts+"$2");s.url=ret+((ret==s.url)?(s.url.match(/\?/)?"&":"?")+"_="+ts:"");}if(s.data&&type=="GET"){s.url+=(s.url.match(/\?/)?"&":"?")+s.data;s.data=null;}if(s.global&&!jQuery.active++)jQuery.event.trigger("ajaxStart");var remote=/^(?:\w+:)?\/\/([^\/?#]+)/;if(s.dataType=="script"&&type=="GET"&&remote.test(s.url)&&remote.exec(s.url)[1]!=location.host){var head=document.getElementsByTagName("head")[0];var script=document.createElement("script");script.src=s.url;if(s.scriptCharset)script.charset=s.scriptCharset;if(!jsonp){var done=false;script.onload=script.onreadystatechange=function(){if(!done&&(!this.readyState||this.readyState=="loaded"||this.readyState=="complete")){done=true;success();complete();head.removeChild(script);}};}head.appendChild(script);return undefined;}var requestDone=false;var xhr=window.ActiveXObject?new ActiveXObject("Microsoft.XMLHTTP"):new XMLHttpRequest();if(s.username)xhr.open(type,s.url,s.async,s.username,s.password);else
+xhr.open(type,s.url,s.async);try{if(s.data)xhr.setRequestHeader("Content-Type",s.contentType);if(s.ifModified)xhr.setRequestHeader("If-Modified-Since",jQuery.lastModified[s.url]||"Thu, 01 Jan 1970 00:00:00 GMT");xhr.setRequestHeader("X-Requested-With","XMLHttpRequest");xhr.setRequestHeader("Accept",s.dataType&&s.accepts[s.dataType]?s.accepts[s.dataType]+", */*":s.accepts._default);}catch(e){}if(s.beforeSend&&s.beforeSend(xhr,s)===false){s.global&&jQuery.active--;xhr.abort();return false;}if(s.global)jQuery.event.trigger("ajaxSend",[xhr,s]);var onreadystatechange=function(isTimeout){if(!requestDone&&xhr&&(xhr.readyState==4||isTimeout=="timeout")){requestDone=true;if(ival){clearInterval(ival);ival=null;}status=isTimeout=="timeout"&&"timeout"||!jQuery.httpSuccess(xhr)&&"error"||s.ifModified&&jQuery.httpNotModified(xhr,s.url)&&"notmodified"||"success";if(status=="success"){try{data=jQuery.httpData(xhr,s.dataType,s.dataFilter);}catch(e){status="parsererror";}}if(status=="success"){var modRes;try{modRes=xhr.getResponseHeader("Last-Modified");}catch(e){}if(s.ifModified&&modRes)jQuery.lastModified[s.url]=modRes;if(!jsonp)success();}else
+jQuery.handleError(s,xhr,status);complete();if(s.async)xhr=null;}};if(s.async){var ival=setInterval(onreadystatechange,13);if(s.timeout>0)setTimeout(function(){if(xhr){xhr.abort();if(!requestDone)onreadystatechange("timeout");}},s.timeout);}try{xhr.send(s.data);}catch(e){jQuery.handleError(s,xhr,null,e);}if(!s.async)onreadystatechange();function success(){if(s.success)s.success(data,status);if(s.global)jQuery.event.trigger("ajaxSuccess",[xhr,s]);}function complete(){if(s.complete)s.complete(xhr,status);if(s.global)jQuery.event.trigger("ajaxComplete",[xhr,s]);if(s.global&&!--jQuery.active)jQuery.event.trigger("ajaxStop");}return xhr;},handleError:function(s,xhr,status,e){if(s.error)s.error(xhr,status,e);if(s.global)jQuery.event.trigger("ajaxError",[xhr,s,e]);},active:0,httpSuccess:function(xhr){try{return!xhr.status&&location.protocol=="file:"||(xhr.status>=200&&xhr.status<300)||xhr.status==304||xhr.status==1223||jQuery.browser.safari&&xhr.status==undefined;}catch(e){}return false;},httpNotModified:function(xhr,url){try{var xhrRes=xhr.getResponseHeader("Last-Modified");return xhr.status==304||xhrRes==jQuery.lastModified[url]||jQuery.browser.safari&&xhr.status==undefined;}catch(e){}return false;},httpData:function(xhr,type,filter){var ct=xhr.getResponseHeader("content-type"),xml=type=="xml"||!type&&ct&&ct.indexOf("xml")>=0,data=xml?xhr.responseXML:xhr.responseText;if(xml&&data.documentElement.tagName=="parsererror")throw"parsererror";if(filter)data=filter(data,type);if(type=="script")jQuery.globalEval(data);if(type=="json")data=eval("("+data+")");return data;},param:function(a){var s=[];if(a.constructor==Array||a.jquery)jQuery.each(a,function(){s.push(encodeURIComponent(this.name)+"="+encodeURIComponent(this.value));});else
+for(var j in a)if(a[j]&&a[j].constructor==Array)jQuery.each(a[j],function(){s.push(encodeURIComponent(j)+"="+encodeURIComponent(this));});else
+s.push(encodeURIComponent(j)+"="+encodeURIComponent(jQuery.isFunction(a[j])?a[j]():a[j]));return s.join("&").replace(/%20/g,"+");}});jQuery.fn.extend({show:function(speed,callback){return speed?this.animate({height:"show",width:"show",opacity:"show"},speed,callback):this.filter(":hidden").each(function(){this.style.display=this.oldblock||"";if(jQuery.css(this,"display")=="none"){var elem=jQuery("<"+this.tagName+" />").appendTo("body");this.style.display=elem.css("display");if(this.style.display=="none")this.style.display="block";elem.remove();}}).end();},hide:function(speed,callback){return speed?this.animate({height:"hide",width:"hide",opacity:"hide"},speed,callback):this.filter(":visible").each(function(){this.oldblock=this.oldblock||jQuery.css(this,"display");this.style.display="none";}).end();},_toggle:jQuery.fn.toggle,toggle:function(fn,fn2){return jQuery.isFunction(fn)&&jQuery.isFunction(fn2)?this._toggle.apply(this,arguments):fn?this.animate({height:"toggle",width:"toggle",opacity:"toggle"},fn,fn2):this.each(function(){jQuery(this)[jQuery(this).is(":hidden")?"show":"hide"]();});},slideDown:function(speed,callback){return this.animate({height:"show"},speed,callback);},slideUp:function(speed,callback){return this.animate({height:"hide"},speed,callback);},slideToggle:function(speed,callback){return this.animate({height:"toggle"},speed,callback);},fadeIn:function(speed,callback){return this.animate({opacity:"show"},speed,callback);},fadeOut:function(speed,callback){return this.animate({opacity:"hide"},speed,callback);},fadeTo:function(speed,to,callback){return this.animate({opacity:to},speed,callback);},animate:function(prop,speed,easing,callback){var optall=jQuery.speed(speed,easing,callback);return this[optall.queue===false?"each":"queue"](function(){if(this.nodeType!=1)return false;var opt=jQuery.extend({},optall),p,hidden=jQuery(this).is(":hidden"),self=this;for(p in prop){if(prop[p]=="hide"&&hidden||prop[p]=="show"&&!hidden)return opt.complete.call(this);if(p=="height"||p=="width"){opt.display=jQuery.css(this,"display");opt.overflow=this.style.overflow;}}if(opt.overflow!=null)this.style.overflow="hidden";opt.curAnim=jQuery.extend({},prop);jQuery.each(prop,function(name,val){var e=new jQuery.fx(self,opt,name);if(/toggle|show|hide/.test(val))e[val=="toggle"?hidden?"show":"hide":val](prop);else{var parts=val.toString().match(/^([+-]=)?([\d+-.]+)(.*)$/),start=e.cur(true)||0;if(parts){var end=parseFloat(parts[2]),unit=parts[3]||"px";if(unit!="px"){self.style[name]=(end||1)+unit;start=((end||1)/e.cur(true))*start;self.style[name]=start+unit;}if(parts[1])end=((parts[1]=="-="?-1:1)*end)+start;e.custom(start,end,unit);}else
+e.custom(start,val,"");}});return true;});},queue:function(type,fn){if(jQuery.isFunction(type)||(type&&type.constructor==Array)){fn=type;type="fx";}if(!type||(typeof type=="string"&&!fn))return queue(this[0],type);return this.each(function(){if(fn.constructor==Array)queue(this,type,fn);else{queue(this,type).push(fn);if(queue(this,type).length==1)fn.call(this);}});},stop:function(clearQueue,gotoEnd){var timers=jQuery.timers;if(clearQueue)this.queue([]);this.each(function(){for(var i=timers.length-1;i>=0;i--)if(timers[i].elem==this){if(gotoEnd)timers[i](true);timers.splice(i,1);}});if(!gotoEnd)this.dequeue();return this;}});var queue=function(elem,type,array){if(elem){type=type||"fx";var q=jQuery.data(elem,type+"queue");if(!q||array)q=jQuery.data(elem,type+"queue",jQuery.makeArray(array));}return q;};jQuery.fn.dequeue=function(type){type=type||"fx";return this.each(function(){var q=queue(this,type);q.shift();if(q.length)q[0].call(this);});};jQuery.extend({speed:function(speed,easing,fn){var opt=speed&&speed.constructor==Object?speed:{complete:fn||!fn&&easing||jQuery.isFunction(speed)&&speed,duration:speed,easing:fn&&easing||easing&&easing.constructor!=Function&&easing};opt.duration=(opt.duration&&opt.duration.constructor==Number?opt.duration:jQuery.fx.speeds[opt.duration])||jQuery.fx.speeds.def;opt.old=opt.complete;opt.complete=function(){if(opt.queue!==false)jQuery(this).dequeue();if(jQuery.isFunction(opt.old))opt.old.call(this);};return opt;},easing:{linear:function(p,n,firstNum,diff){return firstNum+diff*p;},swing:function(p,n,firstNum,diff){return((-Math.cos(p*Math.PI)/2)+0.5)*diff+firstNum;}},timers:[],timerId:null,fx:function(elem,options,prop){this.options=options;this.elem=elem;this.prop=prop;if(!options.orig)options.orig={};}});jQuery.fx.prototype={update:function(){if(this.options.step)this.options.step.call(this.elem,this.now,this);(jQuery.fx.step[this.prop]||jQuery.fx.step._default)(this);if(this.prop=="height"||this.prop=="width")this.elem.style.display="block";},cur:function(force){if(this.elem[this.prop]!=null&&this.elem.style[this.prop]==null)return this.elem[this.prop];var r=parseFloat(jQuery.css(this.elem,this.prop,force));return r&&r>-10000?r:parseFloat(jQuery.curCSS(this.elem,this.prop))||0;},custom:function(from,to,unit){this.startTime=now();this.start=from;this.end=to;this.unit=unit||this.unit||"px";this.now=this.start;this.pos=this.state=0;this.update();var self=this;function t(gotoEnd){return self.step(gotoEnd);}t.elem=this.elem;jQuery.timers.push(t);if(jQuery.timerId==null){jQuery.timerId=setInterval(function(){var timers=jQuery.timers;for(var i=0;i<timers.length;i++)if(!timers[i]())timers.splice(i--,1);if(!timers.length){clearInterval(jQuery.timerId);jQuery.timerId=null;}},13);}},show:function(){this.options.orig[this.prop]=jQuery.attr(this.elem.style,this.prop);this.options.show=true;this.custom(0,this.cur());if(this.prop=="width"||this.prop=="height")this.elem.style[this.prop]="1px";jQuery(this.elem).show();},hide:function(){this.options.orig[this.prop]=jQuery.attr(this.elem.style,this.prop);this.options.hide=true;this.custom(this.cur(),0);},step:function(gotoEnd){var t=now();if(gotoEnd||t>this.options.duration+this.startTime){this.now=this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;var done=true;for(var i in this.options.curAnim)if(this.options.curAnim[i]!==true)done=false;if(done){if(this.options.display!=null){this.elem.style.overflow=this.options.overflow;this.elem.style.display=this.options.display;if(jQuery.css(this.elem,"display")=="none")this.elem.style.display="block";}if(this.options.hide)this.elem.style.display="none";if(this.options.hide||this.options.show)for(var p in this.options.curAnim)jQuery.attr(this.elem.style,p,this.options.orig[p]);}if(done)this.options.complete.call(this.elem);return false;}else{var n=t-this.startTime;this.state=n/this.options.duration;this.pos=jQuery.easing[this.options.easing||(jQuery.easing.swing?"swing":"linear")](this.state,n,0,1,this.options.duration);this.now=this.start+((this.end-this.start)*this.pos);this.update();}return true;}};jQuery.extend(jQuery.fx,{speeds:{slow:600,fast:200,def:400},step:{scrollLeft:function(fx){fx.elem.scrollLeft=fx.now;},scrollTop:function(fx){fx.elem.scrollTop=fx.now;},opacity:function(fx){jQuery.attr(fx.elem.style,"opacity",fx.now);},_default:function(fx){fx.elem.style[fx.prop]=fx.now+fx.unit;}}});jQuery.fn.offset=function(){var left=0,top=0,elem=this[0],results;if(elem)with(jQuery.browser){var parent=elem.parentNode,offsetChild=elem,offsetParent=elem.offsetParent,doc=elem.ownerDocument,safari2=safari&&parseInt(version)<522&&!/adobeair/i.test(userAgent),css=jQuery.curCSS,fixed=css(elem,"position")=="fixed";if(elem.getBoundingClientRect){var box=elem.getBoundingClientRect();add(box.left+Math.max(doc.documentElement.scrollLeft,doc.body.scrollLeft),box.top+Math.max(doc.documentElement.scrollTop,doc.body.scrollTop));add(-doc.documentElement.clientLeft,-doc.documentElement.clientTop);}else{add(elem.offsetLeft,elem.offsetTop);while(offsetParent){add(offsetParent.offsetLeft,offsetParent.offsetTop);if(mozilla&&!/^t(able|d|h)$/i.test(offsetParent.tagName)||safari&&!safari2)border(offsetParent);if(!fixed&&css(offsetParent,"position")=="fixed")fixed=true;offsetChild=/^body$/i.test(offsetParent.tagName)?offsetChild:offsetParent;offsetParent=offsetParent.offsetParent;}while(parent&&parent.tagName&&!/^body|html$/i.test(parent.tagName)){if(!/^inline|table.*$/i.test(css(parent,"display")))add(-parent.scrollLeft,-parent.scrollTop);if(mozilla&&css(parent,"overflow")!="visible")border(parent);parent=parent.parentNode;}if((safari2&&(fixed||css(offsetChild,"position")=="absolute"))||(mozilla&&css(offsetChild,"position")!="absolute"))add(-doc.body.offsetLeft,-doc.body.offsetTop);if(fixed)add(Math.max(doc.documentElement.scrollLeft,doc.body.scrollLeft),Math.max(doc.documentElement.scrollTop,doc.body.scrollTop));}results={top:top,left:left};}function border(elem){add(jQuery.curCSS(elem,"borderLeftWidth",true),jQuery.curCSS(elem,"borderTopWidth",true));}function add(l,t){left+=parseInt(l,10)||0;top+=parseInt(t,10)||0;}return results;};jQuery.fn.extend({position:function(){var left=0,top=0,results;if(this[0]){var offsetParent=this.offsetParent(),offset=this.offset(),parentOffset=/^body|html$/i.test(offsetParent[0].tagName)?{top:0,left:0}:offsetParent.offset();offset.top-=num(this,'marginTop');offset.left-=num(this,'marginLeft');parentOffset.top+=num(offsetParent,'borderTopWidth');parentOffset.left+=num(offsetParent,'borderLeftWidth');results={top:offset.top-parentOffset.top,left:offset.left-parentOffset.left};}return results;},offsetParent:function(){var offsetParent=this[0].offsetParent;while(offsetParent&&(!/^body|html$/i.test(offsetParent.tagName)&&jQuery.css(offsetParent,'position')=='static'))offsetParent=offsetParent.offsetParent;return jQuery(offsetParent);}});jQuery.each(['Left','Top'],function(i,name){var method='scroll'+name;jQuery.fn[method]=function(val){if(!this[0])return;return val!=undefined?this.each(function(){this==window||this==document?window.scrollTo(!i?val:jQuery(window).scrollLeft(),i?val:jQuery(window).scrollTop()):this[method]=val;}):this[0]==window||this[0]==document?self[i?'pageYOffset':'pageXOffset']||jQuery.boxModel&&document.documentElement[method]||document.body[method]:this[0][method];};});jQuery.each(["Height","Width"],function(i,name){var tl=i?"Left":"Top",br=i?"Right":"Bottom";jQuery.fn["inner"+name]=function(){return this[name.toLowerCase()]()+num(this,"padding"+tl)+num(this,"padding"+br);};jQuery.fn["outer"+name]=function(margin){return this["inner"+name]()+num(this,"border"+tl+"Width")+num(this,"border"+br+"Width")+(margin?num(this,"margin"+tl)+num(this,"margin"+br):0);};});})();
+
+;(function($){var _remove=$.fn.remove;$.fn.remove=function(){$("*",this).add(this).triggerHandler("remove");return _remove.apply(this,arguments);};function isVisible(element){function checkStyles(element){var style=element.style;return(style.display!='none'&&style.visibility!='hidden');}
+var visible=checkStyles(element);(visible&&$.each($.dir(element,'parentNode'),function(){return(visible=checkStyles(this));}));return visible;}
+$.extend($.expr[':'],{data:function(a,i,m){return $.data(a,m[3]);},tabbable:function(a,i,m){var nodeName=a.nodeName.toLowerCase();return(a.tabIndex>=0&&(('a'==nodeName&&a.href)||(/input|select|textarea|button/.test(nodeName)&&'hidden'!=a.type&&!a.disabled))&&isVisible(a));}});$.keyCode={BACKSPACE:8,CAPS_LOCK:20,COMMA:188,CONTROL:17,DELETE:46,DOWN:40,END:35,ENTER:13,ESCAPE:27,HOME:36,INSERT:45,LEFT:37,NUMPAD_ADD:107,NUMPAD_DECIMAL:110,NUMPAD_DIVIDE:111,NUMPAD_ENTER:108,NUMPAD_MULTIPLY:106,NUMPAD_SUBTRACT:109,PAGE_DOWN:34,PAGE_UP:33,PERIOD:190,RIGHT:39,SHIFT:16,SPACE:32,TAB:9,UP:38};function getter(namespace,plugin,method,args){function getMethods(type){var methods=$[namespace][plugin][type]||[];return(typeof methods=='string'?methods.split(/,?\s+/):methods);}
+var methods=getMethods('getter');if(args.length==1&&typeof args[0]=='string'){methods=methods.concat(getMethods('getterSetter'));}
+return($.inArray(method,methods)!=-1);}
+$.widget=function(name,prototype){var namespace=name.split(".")[0];name=name.split(".")[1];$.fn[name]=function(options){var isMethodCall=(typeof options=='string'),args=Array.prototype.slice.call(arguments,1);if(isMethodCall&&options.substring(0,1)=='_'){return this;}
+if(isMethodCall&&getter(namespace,name,options,args)){var instance=$.data(this[0],name);return(instance?instance[options].apply(instance,args):undefined);}
+return this.each(function(){var instance=$.data(this,name);(!instance&&!isMethodCall&&$.data(this,name,new $[namespace][name](this,options)));(instance&&isMethodCall&&$.isFunction(instance[options])&&instance[options].apply(instance,args));});};$[namespace][name]=function(element,options){var self=this;this.widgetName=name;this.widgetEventPrefix=$[namespace][name].eventPrefix||name;this.widgetBaseClass=namespace+'-'+name;this.options=$.extend({},$.widget.defaults,$[namespace][name].defaults,$.metadata&&$.metadata.get(element)[name],options);this.element=$(element).bind('setData.'+name,function(e,key,value){return self._setData(key,value);}).bind('getData.'+name,function(e,key){return self._getData(key);}).bind('remove',function(){return self.destroy();});this._init();};$[namespace][name].prototype=$.extend({},$.widget.prototype,prototype);$[namespace][name].getterSetter='option';};$.widget.prototype={_init:function(){},destroy:function(){this.element.removeData(this.widgetName);},option:function(key,value){var options=key,self=this;if(typeof key=="string"){if(value===undefined){return this._getData(key);}
+options={};options[key]=value;}
+$.each(options,function(key,value){self._setData(key,value);});},_getData:function(key){return this.options[key];},_setData:function(key,value){this.options[key]=value;if(key=='disabled'){this.element[value?'addClass':'removeClass'](this.widgetBaseClass+'-disabled');}},enable:function(){this._setData('disabled',false);},disable:function(){this._setData('disabled',true);},_trigger:function(type,e,data){var eventName=(type==this.widgetEventPrefix?type:this.widgetEventPrefix+type);e=e||$.event.fix({type:eventName,target:this.element[0]});return this.element.triggerHandler(eventName,[e,data],this.options[type]);}};$.widget.defaults={disabled:false};$.ui={plugin:{add:function(module,option,set){var proto=$.ui[module].prototype;for(var i in set){proto.plugins[i]=proto.plugins[i]||[];proto.plugins[i].push([option,set[i]]);}},call:function(instance,name,args){var set=instance.plugins[name];if(!set){return;}
+for(var i=0;i<set.length;i++){if(instance.options[set[i][0]]){set[i][1].apply(instance.element,args);}}}},cssCache:{},css:function(name){if($.ui.cssCache[name]){return $.ui.cssCache[name];}
+var tmp=$('<div class="ui-gen">').addClass(name).css({position:'absolute',top:'-5000px',left:'-5000px',display:'block'}).appendTo('body');$.ui.cssCache[name]=!!((!(/auto|default/).test(tmp.css('cursor'))||(/^[1-9]/).test(tmp.css('height'))||(/^[1-9]/).test(tmp.css('width'))||!(/none/).test(tmp.css('backgroundImage'))||!(/transparent|rgba\(0, 0, 0, 0\)/).test(tmp.css('backgroundColor'))));try{$('body').get(0).removeChild(tmp.get(0));}catch(e){}
+return $.ui.cssCache[name];},disableSelection:function(el){return $(el).attr('unselectable','on').css('MozUserSelect','none').bind('selectstart.ui',function(){return false;});},enableSelection:function(el){return $(el).attr('unselectable','off').css('MozUserSelect','').unbind('selectstart.ui');},hasScroll:function(e,a){if($(e).css('overflow')=='hidden'){return false;}
+var scroll=(a&&a=='left')?'scrollLeft':'scrollTop',has=false;if(e[scroll]>0){return true;}
+e[scroll]=1;has=(e[scroll]>0);e[scroll]=0;return has;}};$.ui.mouse={_mouseInit:function(){var self=this;this.element.bind('mousedown.'+this.widgetName,function(e){return self._mouseDown(e);});if($.browser.msie){this._mouseUnselectable=this.element.attr('unselectable');this.element.attr('unselectable','on');}
+this.started=false;},_mouseDestroy:function(){this.element.unbind('.'+this.widgetName);($.browser.msie&&this.element.attr('unselectable',this._mouseUnselectable));},_mouseDown:function(e){(this._mouseStarted&&this._mouseUp(e));this._mouseDownEvent=e;var self=this,btnIsLeft=(e.which==1),elIsCancel=(typeof this.options.cancel=="string"?$(e.target).parents().add(e.target).filter(this.options.cancel).length:false);if(!btnIsLeft||elIsCancel||!this._mouseCapture(e)){return true;}
+this.mouseDelayMet=!this.options.delay;if(!this.mouseDelayMet){this._mouseDelayTimer=setTimeout(function(){self.mouseDelayMet=true;},this.options.delay);}
+if(this._mouseDistanceMet(e)&&this._mouseDelayMet(e)){this._mouseStarted=(this._mouseStart(e)!==false);if(!this._mouseStarted){e.preventDefault();return true;}}
+this._mouseMoveDelegate=function(e){return self._mouseMove(e);};this._mouseUpDelegate=function(e){return self._mouseUp(e);};$(document).bind('mousemove.'+this.widgetName,this._mouseMoveDelegate).bind('mouseup.'+this.widgetName,this._mouseUpDelegate);return false;},_mouseMove:function(e){if($.browser.msie&&!e.button){return this._mouseUp(e);}
+if(this._mouseStarted){this._mouseDrag(e);return false;}
+if(this._mouseDistanceMet(e)&&this._mouseDelayMet(e)){this._mouseStarted=(this._mouseStart(this._mouseDownEvent,e)!==false);(this._mouseStarted?this._mouseDrag(e):this._mouseUp(e));}
+return!this._mouseStarted;},_mouseUp:function(e){$(document).unbind('mousemove.'+this.widgetName,this._mouseMoveDelegate).unbind('mouseup.'+this.widgetName,this._mouseUpDelegate);if(this._mouseStarted){this._mouseStarted=false;this._mouseStop(e);}
+return false;},_mouseDistanceMet:function(e){return(Math.max(Math.abs(this._mouseDownEvent.pageX-e.pageX),Math.abs(this._mouseDownEvent.pageY-e.pageY))>=this.options.distance);},_mouseDelayMet:function(e){return this.mouseDelayMet;},_mouseStart:function(e){},_mouseDrag:function(e){},_mouseStop:function(e){},_mouseCapture:function(e){return true;}};$.ui.mouse.defaults={cancel:null,distance:1,delay:0};})(jQuery);(function($){$.widget("ui.resizable",$.extend({},$.ui.mouse,{_init:function(){var self=this,o=this.options;var elpos=this.element.css('position');this.originalElement=this.element;this.element.addClass("ui-resizable").css({position:/static/.test(elpos)?'relative':elpos});$.extend(o,{_aspectRatio:!!(o.aspectRatio),helper:o.helper||o.ghost||o.animate?o.helper||'proxy':null,knobHandles:o.knobHandles===true?'ui-resizable-knob-handle':o.knobHandles});var aBorder='1px solid #DEDEDE';o.defaultTheme={'ui-resizable':{display:'block'},'ui-resizable-handle':{position:'absolute',background:'#F2F2F2',fontSize:'0.1px'},'ui-resizable-n':{cursor:'n-resize',height:'4px',left:'0px',right:'0px',borderTop:aBorder},'ui-resizable-s':{cursor:'s-resize',height:'4px',left:'0px',right:'0px',borderBottom:aBorder},'ui-resizable-e':{cursor:'e-resize',width:'4px',top:'0px',bottom:'0px',borderRight:aBorder},'ui-resizable-w':{cursor:'w-resize',width:'4px',top:'0px',bottom:'0px',borderLeft:aBorder},'ui-resizable-se':{cursor:'se-resize',width:'4px',height:'4px',borderRight:aBorder,borderBottom:aBorder},'ui-resizable-sw':{cursor:'sw-resize',width:'4px',height:'4px',borderBottom:aBorder,borderLeft:aBorder},'ui-resizable-ne':{cursor:'ne-resize',width:'4px',height:'4px',borderRight:aBorder,borderTop:aBorder},'ui-resizable-nw':{cursor:'nw-resize',width:'4px',height:'4px',borderLeft:aBorder,borderTop:aBorder}};o.knobTheme={'ui-resizable-handle':{background:'#F2F2F2',border:'1px solid #808080',height:'8px',width:'8px'},'ui-resizable-n':{cursor:'n-resize',top:'0px',left:'45%'},'ui-resizable-s':{cursor:'s-resize',bottom:'0px',left:'45%'},'ui-resizable-e':{cursor:'e-resize',right:'0px',top:'45%'},'ui-resizable-w':{cursor:'w-resize',left:'0px',top:'45%'},'ui-resizable-se':{cursor:'se-resize',right:'0px',bottom:'0px'},'ui-resizable-sw':{cursor:'sw-resize',left:'0px',bottom:'0px'},'ui-resizable-nw':{cursor:'nw-resize',left:'0px',top:'0px'},'ui-resizable-ne':{cursor:'ne-resize',right:'0px',top:'0px'}};o._nodeName=this.element[0].nodeName;if(o._nodeName.match(/canvas|textarea|input|select|button|img/i)){var el=this.element;if(/relative/.test(el.css('position'))&&$.browser.opera)
+el.css({position:'relative',top:'auto',left:'auto'});el.wrap($('<div class="ui-wrapper" style="overflow: hidden;"></div>').css({position:el.css('position'),width:el.outerWidth(),height:el.outerHeight(),top:el.css('top'),left:el.css('left')}));var oel=this.element;this.element=this.element.parent();this.element.data('resizable',this);this.element.css({marginLeft:oel.css("marginLeft"),marginTop:oel.css("marginTop"),marginRight:oel.css("marginRight"),marginBottom:oel.css("marginBottom")});oel.css({marginLeft:0,marginTop:0,marginRight:0,marginBottom:0});if($.browser.safari&&o.preventDefault)oel.css('resize','none');o.proportionallyResize=oel.css({position:'static',zoom:1,display:'block'});this.element.css({margin:oel.css('margin')});this._proportionallyResize();}
+if(!o.handles)o.handles=!$('.ui-resizable-handle',this.element).length?"e,s,se":{n:'.ui-resizable-n',e:'.ui-resizable-e',s:'.ui-resizable-s',w:'.ui-resizable-w',se:'.ui-resizable-se',sw:'.ui-resizable-sw',ne:'.ui-resizable-ne',nw:'.ui-resizable-nw'};if(o.handles.constructor==String){o.zIndex=o.zIndex||1000;if(o.handles=='all')o.handles='n,e,s,w,se,sw,ne,nw';var n=o.handles.split(",");o.handles={};var insertionsDefault={handle:'position: absolute; display: none; overflow:hidden;',n:'top: 0pt; width:100%;',e:'right: 0pt; height:100%;',s:'bottom: 0pt; width:100%;',w:'left: 0pt; height:100%;',se:'bottom: 0pt; right: 0px;',sw:'bottom: 0pt; left: 0px;',ne:'top: 0pt; right: 0px;',nw:'top: 0pt; left: 0px;'};for(var i=0;i<n.length;i++){var handle=$.trim(n[i]),dt=o.defaultTheme,hname='ui-resizable-'+handle,loadDefault=!$.ui.css(hname)&&!o.knobHandles,userKnobClass=$.ui.css('ui-resizable-knob-handle'),allDefTheme=$.extend(dt[hname],dt['ui-resizable-handle']),allKnobTheme=$.extend(o.knobTheme[hname],!userKnobClass?o.knobTheme['ui-resizable-handle']:{});var applyZIndex=/sw|se|ne|nw/.test(handle)?{zIndex:++o.zIndex}:{};var defCss=(loadDefault?insertionsDefault[handle]:''),axis=$(['<div class="ui-resizable-handle ',hname,'" style="',defCss,insertionsDefault.handle,'"></div>'].join('')).css(applyZIndex);o.handles[handle]='.ui-resizable-'+handle;this.element.append(axis.css(loadDefault?allDefTheme:{}).css(o.knobHandles?allKnobTheme:{}).addClass(o.knobHandles?'ui-resizable-knob-handle':'').addClass(o.knobHandles));}
+if(o.knobHandles)this.element.addClass('ui-resizable-knob').css(!$.ui.css('ui-resizable-knob')?{}:{});}
+this._renderAxis=function(target){target=target||this.element;for(var i in o.handles){if(o.handles[i].constructor==String)
+o.handles[i]=$(o.handles[i],this.element).show();if(o.transparent)
+o.handles[i].css({opacity:0});if(this.element.is('.ui-wrapper')&&o._nodeName.match(/textarea|input|select|button/i)){var axis=$(o.handles[i],this.element),padWrapper=0;padWrapper=/sw|ne|nw|se|n|s/.test(i)?axis.outerHeight():axis.outerWidth();var padPos=['padding',/ne|nw|n/.test(i)?'Top':/se|sw|s/.test(i)?'Bottom':/^e$/.test(i)?'Right':'Left'].join("");if(!o.transparent)
+target.css(padPos,padWrapper);this._proportionallyResize();}
+if(!$(o.handles[i]).length)continue;}};this._renderAxis(this.element);o._handles=$('.ui-resizable-handle',self.element);if(o.disableSelection)
+o._handles.each(function(i,e){$.ui.disableSelection(e);});o._handles.mouseover(function(){if(!o.resizing){if(this.className)
+var axis=this.className.match(/ui-resizable-(se|sw|ne|nw|n|e|s|w)/i);self.axis=o.axis=axis&&axis[1]?axis[1]:'se';}});if(o.autoHide){o._handles.hide();$(self.element).addClass("ui-resizable-autohide").hover(function(){$(this).removeClass("ui-resizable-autohide");o._handles.show();},function(){if(!o.resizing){$(this).addClass("ui-resizable-autohide");o._handles.hide();}});}
+this._mouseInit();},plugins:{},ui:function(){return{originalElement:this.originalElement,element:this.element,helper:this.helper,position:this.position,size:this.size,options:this.options,originalSize:this.originalSize,originalPosition:this.originalPosition};},_propagate:function(n,e){$.ui.plugin.call(this,n,[e,this.ui()]);if(n!="resize")this.element.triggerHandler(["resize",n].join(""),[e,this.ui()],this.options[n]);},destroy:function(){var el=this.element,wrapped=el.children(".ui-resizable").get(0);this._mouseDestroy();var _destroy=function(exp){$(exp).removeClass("ui-resizable ui-resizable-disabled").removeData("resizable").unbind(".resizable").find('.ui-resizable-handle').remove();};_destroy(el);if(el.is('.ui-wrapper')&&wrapped){el.parent().append($(wrapped).css({position:el.css('position'),width:el.outerWidth(),height:el.outerHeight(),top:el.css('top'),left:el.css('left')})).end().remove();_destroy(wrapped);}},_mouseCapture:function(e){if(this.options.disabled)return false;var handle=false;for(var i in this.options.handles){if($(this.options.handles[i])[0]==e.target)handle=true;}
+if(!handle)return false;return true;},_mouseStart:function(e){var o=this.options,iniPos=this.element.position(),el=this.element,num=function(v){return parseInt(v,10)||0;},ie6=$.browser.msie&&$.browser.version<7;o.resizing=true;o.documentScroll={top:$(document).scrollTop(),left:$(document).scrollLeft()};if(el.is('.ui-draggable')||(/absolute/).test(el.css('position'))){var sOffset=$.browser.msie&&!o.containment&&(/absolute/).test(el.css('position'))&&!(/relative/).test(el.parent().css('position'));var dscrollt=sOffset?o.documentScroll.top:0,dscrolll=sOffset?o.documentScroll.left:0;el.css({position:'absolute',top:(iniPos.top+dscrollt),left:(iniPos.left+dscrolll)});}
+if($.browser.opera&&/relative/.test(el.css('position')))
+el.css({position:'relative',top:'auto',left:'auto'});this._renderProxy();var curleft=num(this.helper.css('left')),curtop=num(this.helper.css('top'));if(o.containment){curleft+=$(o.containment).scrollLeft()||0;curtop+=$(o.containment).scrollTop()||0;}
+this.offset=this.helper.offset();this.position={left:curleft,top:curtop};this.size=o.helper||ie6?{width:el.outerWidth(),height:el.outerHeight()}:{width:el.width(),height:el.height()};this.originalSize=o.helper||ie6?{width:el.outerWidth(),height:el.outerHeight()}:{width:el.width(),height:el.height()};this.originalPosition={left:curleft,top:curtop};this.sizeDiff={width:el.outerWidth()-el.width(),height:el.outerHeight()-el.height()};this.originalMousePosition={left:e.pageX,top:e.pageY};o.aspectRatio=(typeof o.aspectRatio=='number')?o.aspectRatio:((this.originalSize.width/this.originalSize.height)||1);if(o.preserveCursor)
+$('body').css('cursor',this.axis+'-resize');this._propagate("start",e);return true;},_mouseDrag:function(e){var el=this.helper,o=this.options,props={},self=this,smp=this.originalMousePosition,a=this.axis;var dx=(e.pageX-smp.left)||0,dy=(e.pageY-smp.top)||0;var trigger=this._change[a];if(!trigger)return false;var data=trigger.apply(this,[e,dx,dy]),ie6=$.browser.msie&&$.browser.version<7,csdif=this.sizeDiff;if(o._aspectRatio||e.shiftKey)
+data=this._updateRatio(data,e);data=this._respectSize(data,e);this._propagate("resize",e);el.css({top:this.position.top+"px",left:this.position.left+"px",width:this.size.width+"px",height:this.size.height+"px"});if(!o.helper&&o.proportionallyResize)
+this._proportionallyResize();this._updateCache(data);this.element.triggerHandler("resize",[e,this.ui()],this.options["resize"]);return false;},_mouseStop:function(e){this.options.resizing=false;var o=this.options,num=function(v){return parseInt(v,10)||0;},self=this;if(o.helper){var pr=o.proportionallyResize,ista=pr&&(/textarea/i).test(pr.get(0).nodeName),soffseth=ista&&$.ui.hasScroll(pr.get(0),'left')?0:self.sizeDiff.height,soffsetw=ista?0:self.sizeDiff.width;var s={width:(self.size.width-soffsetw),height:(self.size.height-soffseth)},left=(parseInt(self.element.css('left'),10)+(self.position.left-self.originalPosition.left))||null,top=(parseInt(self.element.css('top'),10)+(self.position.top-self.originalPosition.top))||null;if(!o.animate)
+this.element.css($.extend(s,{top:top,left:left}));if(o.helper&&!o.animate)this._proportionallyResize();}
+if(o.preserveCursor)
+$('body').css('cursor','auto');this._propagate("stop",e);if(o.helper)this.helper.remove();return false;},_updateCache:function(data){var o=this.options;this.offset=this.helper.offset();if(data.left)this.position.left=data.left;if(data.top)this.position.top=data.top;if(data.height)this.size.height=data.height;if(data.width)this.size.width=data.width;},_updateRatio:function(data,e){var o=this.options,cpos=this.position,csize=this.size,a=this.axis;if(data.height)data.width=(csize.height*o.aspectRatio);else if(data.width)data.height=(csize.width/o.aspectRatio);if(a=='sw'){data.left=cpos.left+(csize.width-data.width);data.top=null;}
+if(a=='nw'){data.top=cpos.top+(csize.height-data.height);data.left=cpos.left+(csize.width-data.width);}
+return data;},_respectSize:function(data,e){var el=this.helper,o=this.options,pRatio=o._aspectRatio||e.shiftKey,a=this.axis,ismaxw=data.width&&o.maxWidth&&o.maxWidth<data.width,ismaxh=data.height&&o.maxHeight&&o.maxHeight<data.height,isminw=data.width&&o.minWidth&&o.minWidth>data.width,isminh=data.height&&o.minHeight&&o.minHeight>data.height;if(isminw)data.width=o.minWidth;if(isminh)data.height=o.minHeight;if(ismaxw)data.width=o.maxWidth;if(ismaxh)data.height=o.maxHeight;var dw=this.originalPosition.left+this.originalSize.width,dh=this.position.top+this.size.height;var cw=/sw|nw|w/.test(a),ch=/nw|ne|n/.test(a);if(isminw&&cw)data.left=dw-o.minWidth;if(ismaxw&&cw)data.left=dw-o.maxWidth;if(isminh&&ch)data.top=dh-o.minHeight;if(ismaxh&&ch)data.top=dh-o.maxHeight;var isNotwh=!data.width&&!data.height;if(isNotwh&&!data.left&&data.top)data.top=null;else if(isNotwh&&!data.top&&data.left)data.left=null;return data;},_proportionallyResize:function(){var o=this.options;if(!o.proportionallyResize)return;var prel=o.proportionallyResize,el=this.helper||this.element;if(!o.borderDif){var b=[prel.css('borderTopWidth'),prel.css('borderRightWidth'),prel.css('borderBottomWidth'),prel.css('borderLeftWidth')],p=[prel.css('paddingTop'),prel.css('paddingRight'),prel.css('paddingBottom'),prel.css('paddingLeft')];o.borderDif=$.map(b,function(v,i){var border=parseInt(v,10)||0,padding=parseInt(p[i],10)||0;return border+padding;});}
+prel.css({height:(el.height()-o.borderDif[0]-o.borderDif[2])+"px",width:(el.width()-o.borderDif[1]-o.borderDif[3])+"px"});},_renderProxy:function(){var el=this.element,o=this.options;this.elementOffset=el.offset();if(o.helper){this.helper=this.helper||$('<div style="overflow:hidden;"></div>');var ie6=$.browser.msie&&$.browser.version<7,ie6offset=(ie6?1:0),pxyoffset=(ie6?2:-1);this.helper.addClass(o.helper).css({width:el.outerWidth()+pxyoffset,height:el.outerHeight()+pxyoffset,position:'absolute',left:this.elementOffset.left-ie6offset+'px',top:this.elementOffset.top-ie6offset+'px',zIndex:++o.zIndex});this.helper.appendTo("body");if(o.disableSelection)
+$.ui.disableSelection(this.helper.get(0));}else{this.helper=el;}},_change:{e:function(e,dx,dy){return{width:this.originalSize.width+dx};},w:function(e,dx,dy){var o=this.options,cs=this.originalSize,sp=this.originalPosition;return{left:sp.left+dx,width:cs.width-dx};},n:function(e,dx,dy){var o=this.options,cs=this.originalSize,sp=this.originalPosition;return{top:sp.top+dy,height:cs.height-dy};},s:function(e,dx,dy){return{height:this.originalSize.height+dy};},se:function(e,dx,dy){return $.extend(this._change.s.apply(this,arguments),this._change.e.apply(this,[e,dx,dy]));},sw:function(e,dx,dy){return $.extend(this._change.s.apply(this,arguments),this._change.w.apply(this,[e,dx,dy]));},ne:function(e,dx,dy){return $.extend(this._change.n.apply(this,arguments),this._change.e.apply(this,[e,dx,dy]));},nw:function(e,dx,dy){return $.extend(this._change.n.apply(this,arguments),this._change.w.apply(this,[e,dx,dy]));}}}));$.extend($.ui.resizable,{defaults:{cancel:":input",distance:1,delay:0,preventDefault:true,transparent:false,minWidth:10,minHeight:10,aspectRatio:false,disableSelection:true,preserveCursor:true,autoHide:false,knobHandles:false}});$.ui.plugin.add("resizable","containment",{start:function(e,ui){var o=ui.options,self=$(this).data("resizable"),el=self.element;var oc=o.containment,ce=(oc instanceof $)?oc.get(0):(/parent/.test(oc))?el.parent().get(0):oc;if(!ce)return;self.containerElement=$(ce);if(/document/.test(oc)||oc==document){self.containerOffset={left:0,top:0};self.containerPosition={left:0,top:0};self.parentData={element:$(document),left:0,top:0,width:$(document).width(),height:$(document).height()||document.body.parentNode.scrollHeight};}
+else{self.containerOffset=$(ce).offset();self.containerPosition=$(ce).position();self.containerSize={height:$(ce).innerHeight(),width:$(ce).innerWidth()};var co=self.containerOffset,ch=self.containerSize.height,cw=self.containerSize.width,width=($.ui.hasScroll(ce,"left")?ce.scrollWidth:cw),height=($.ui.hasScroll(ce)?ce.scrollHeight:ch);self.parentData={element:ce,left:co.left,top:co.top,width:width,height:height};}},resize:function(e,ui){var o=ui.options,self=$(this).data("resizable"),ps=self.containerSize,co=self.containerOffset,cs=self.size,cp=self.position,pRatio=o._aspectRatio||e.shiftKey,cop={top:0,left:0},ce=self.containerElement;if(ce[0]!=document&&/static/.test(ce.css('position')))
+cop=self.containerPosition;if(cp.left<(o.helper?co.left:cop.left)){self.size.width=self.size.width+(o.helper?(self.position.left-co.left):(self.position.left-cop.left));if(pRatio)self.size.height=self.size.width/o.aspectRatio;self.position.left=o.helper?co.left:cop.left;}
+if(cp.top<(o.helper?co.top:0)){self.size.height=self.size.height+(o.helper?(self.position.top-co.top):self.position.top);if(pRatio)self.size.width=self.size.height*o.aspectRatio;self.position.top=o.helper?co.top:0;}
+var woset=(o.helper?self.offset.left-co.left:(self.position.left-cop.left))+self.sizeDiff.width,hoset=(o.helper?self.offset.top-co.top:self.position.top)+self.sizeDiff.height;if(woset+self.size.width>=self.parentData.width){self.size.width=self.parentData.width-woset;if(pRatio)self.size.height=self.size.width/o.aspectRatio;}
+if(hoset+self.size.height>=self.parentData.height){self.size.height=self.parentData.height-hoset;if(pRatio)self.size.width=self.size.height*o.aspectRatio;}},stop:function(e,ui){var o=ui.options,self=$(this).data("resizable"),cp=self.position,co=self.containerOffset,cop=self.containerPosition,ce=self.containerElement;var helper=$(self.helper),ho=helper.offset(),w=helper.innerWidth(),h=helper.innerHeight();if(o.helper&&!o.animate&&/relative/.test(ce.css('position')))
+$(this).css({left:(ho.left-co.left),top:(ho.top-co.top),width:w,height:h});if(o.helper&&!o.animate&&/static/.test(ce.css('position')))
+$(this).css({left:cop.left+(ho.left-co.left),top:cop.top+(ho.top-co.top),width:w,height:h});}});$.ui.plugin.add("resizable","grid",{resize:function(e,ui){var o=ui.options,self=$(this).data("resizable"),cs=self.size,os=self.originalSize,op=self.originalPosition,a=self.axis,ratio=o._aspectRatio||e.shiftKey;o.grid=typeof o.grid=="number"?[o.grid,o.grid]:o.grid;var ox=Math.round((cs.width-os.width)/(o.grid[0]||1))*(o.grid[0]||1),oy=Math.round((cs.height-os.height)/(o.grid[1]||1))*(o.grid[1]||1);if(/^(se|s|e)$/.test(a)){self.size.width=os.width+ox;self.size.height=os.height+oy;}
+else if(/^(ne)$/.test(a)){self.size.width=os.width+ox;self.size.height=os.height+oy;self.position.top=op.top-oy;}
+else if(/^(sw)$/.test(a)){self.size.width=os.width+ox;self.size.height=os.height+oy;self.position.left=op.left-ox;}
+else{self.size.width=os.width+ox;self.size.height=os.height+oy;self.position.top=op.top-oy;self.position.left=op.left-ox;}}});$.ui.plugin.add("resizable","animate",{stop:function(e,ui){var o=ui.options,self=$(this).data("resizable");var pr=o.proportionallyResize,ista=pr&&(/textarea/i).test(pr.get(0).nodeName),soffseth=ista&&$.ui.hasScroll(pr.get(0),'left')?0:self.sizeDiff.height,soffsetw=ista?0:self.sizeDiff.width;var style={width:(self.size.width-soffsetw),height:(self.size.height-soffseth)},left=(parseInt(self.element.css('left'),10)+(self.position.left-self.originalPosition.left))||null,top=(parseInt(self.element.css('top'),10)+(self.position.top-self.originalPosition.top))||null;self.element.animate($.extend(style,top&&left?{top:top,left:left}:{}),{duration:o.animateDuration||"slow",easing:o.animateEasing||"swing",step:function(){var data={width:parseInt(self.element.css('width'),10),height:parseInt(self.element.css('height'),10),top:parseInt(self.element.css('top'),10),left:parseInt(self.element.css('left'),10)};if(pr)pr.css({width:data.width,height:data.height});self._updateCache(data);self._propagate("animate",e);}});}});$.ui.plugin.add("resizable","ghost",{start:function(e,ui){var o=ui.options,self=$(this).data("resizable"),pr=o.proportionallyResize,cs=self.size;if(!pr)self.ghost=self.element.clone();else self.ghost=pr.clone();self.ghost.css({opacity:.25,display:'block',position:'relative',height:cs.height,width:cs.width,margin:0,left:0,top:0}).addClass('ui-resizable-ghost').addClass(typeof o.ghost=='string'?o.ghost:'');self.ghost.appendTo(self.helper);},resize:function(e,ui){var o=ui.options,self=$(this).data("resizable"),pr=o.proportionallyResize;if(self.ghost)self.ghost.css({position:'relative',height:self.size.height,width:self.size.width});},stop:function(e,ui){var o=ui.options,self=$(this).data("resizable"),pr=o.proportionallyResize;if(self.ghost&&self.helper)self.helper.get(0).removeChild(self.ghost.get(0));}});$.ui.plugin.add("resizable","alsoResize",{start:function(e,ui){var o=ui.options,self=$(this).data("resizable"),_store=function(exp){$(exp).each(function(){$(this).data("resizable-alsoresize",{width:parseInt($(this).width(),10),height:parseInt($(this).height(),10),left:parseInt($(this).css('left'),10),top:parseInt($(this).css('top'),10)});});};if(typeof(o.alsoResize)=='object'){if(o.alsoResize.length){o.alsoResize=o.alsoResize[0];_store(o.alsoResize);}
+else{$.each(o.alsoResize,function(exp,c){_store(exp);});}}else{_store(o.alsoResize);}},resize:function(e,ui){var o=ui.options,self=$(this).data("resizable"),os=self.originalSize,op=self.originalPosition;var delta={height:(self.size.height-os.height)||0,width:(self.size.width-os.width)||0,top:(self.position.top-op.top)||0,left:(self.position.left-op.left)||0},_alsoResize=function(exp,c){$(exp).each(function(){var start=$(this).data("resizable-alsoresize"),style={},css=c&&c.length?c:['width','height','top','left'];$.each(css||['width','height','top','left'],function(i,prop){var sum=(start[prop]||0)+(delta[prop]||0);if(sum&&sum>=0)
+style[prop]=sum||null;});$(this).css(style);});};if(typeof(o.alsoResize)=='object'){$.each(o.alsoResize,function(exp,c){_alsoResize(exp,c);});}else{_alsoResize(o.alsoResize);}},stop:function(e,ui){$(this).removeData("resizable-alsoresize-start");}});})(jQuery);
\ No newline at end of file
diff --git a/tools/droiddoc/templates/assets/navtree.js b/tools/droiddoc/templates/assets/navtree.js
new file mode 100644
index 0000000..f48e1dc
--- /dev/null
+++ b/tools/droiddoc/templates/assets/navtree.js
@@ -0,0 +1,179 @@
+
+function new_node(me, mom, text, link, children_data)
+{
+  var node = new Object();
+  node.children = Array();
+  node.children_data = children_data;
+  node.depth = mom.depth + 1;
+
+  node.li = document.createElement("li");
+  mom.get_children_ul().appendChild(node.li);
+
+  node.label_div = document.createElement("div");
+  node.li.appendChild(node.label_div);
+  node.label_div.style.paddingLeft = 10*node.depth + "px";
+  node.label_div.className = "label";
+
+  if (children_data == null) {
+    // 12 is the width of the triangle and padding extra space
+    node.label_div.style.paddingLeft = ((10*node.depth)+12) + "px";
+  } else {
+    node.label_div.style.paddingLeft = 10*node.depth + "px";
+    node.expand_toggle = document.createElement("a");
+    node.expand_toggle.href = "javascript:void(0)";
+    node.expand_toggle.onclick = function() {
+          if (node.expanded) {
+            $(node.get_children_ul()).slideUp("fast");
+            node.plus_img.src = me.toroot + "assets/images/triangle-closed-small.png";
+            node.expanded = false;
+          } else {
+            expand_node(me, node);
+          }
+       };
+    node.label_div.appendChild(node.expand_toggle);
+
+    node.plus_img = document.createElement("img");
+    node.plus_img.src = me.toroot + "assets/images/triangle-closed-small.png";
+    node.plus_img.className = "plus";
+    node.plus_img.border = "0";
+    node.expand_toggle.appendChild(node.plus_img);
+
+    node.expanded = false;
+  }
+
+  var a = document.createElement("a");
+  node.label_div.appendChild(a);
+  node.label = document.createTextNode(text);
+  a.appendChild(node.label);
+  if (link) {
+    a.href = me.toroot + link;
+  } else {
+    if (children_data != null) {
+      a.className = "nolink";
+      a.href = "javascript:void(0)";
+      a.onclick = node.expand_toggle.onclick;
+      // This next line shouldn't be necessary.  I'll buy a beer for the first
+      // person who figures out how to remove this line and have the link
+      // toggle shut on the first try. --joeo@android.com
+      node.expanded = false;
+    }
+  }
+  
+
+  node.children_ul = null;
+  node.get_children_ul = function() {
+      if (!node.children_ul) {
+        node.children_ul = document.createElement("ul");
+        node.children_ul.className = "children_ul";
+        node.children_ul.style.display = "none";
+        node.li.appendChild(node.children_ul);
+      }
+      return node.children_ul;
+    };
+
+  return node;
+}
+
+function expand_node(me, node)
+{
+  if (node.children_data && !node.expanded) {
+    if (node.children_visited) {
+      $(node.get_children_ul()).slideDown("fast");
+    } else {
+      get_node(me, node);
+      $(node.get_children_ul()).slideDown("fast");
+    }
+    node.plus_img.src = me.toroot + "assets/images/triangle-opened-small.png";
+    node.expanded = true;
+  }
+}
+
+function get_node(me, mom)
+{
+  mom.children_visited = true;
+  for (var i in mom.children_data) {
+    var node_data = mom.children_data[i];
+    mom.children[i] = new_node(me, mom, node_data[0], node_data[1],
+        node_data[2]);
+  }
+}
+
+function this_page_relative(toroot)
+{
+  var full = document.location.pathname;
+  var file = "";
+  if (toroot.substr(0, 1) == "/") {
+    if (full.substr(0, toroot.length) == toroot) {
+      return full.substr(toroot.length);
+    } else {
+      // the file isn't under toroot.  Fail.
+      return null;
+    }
+  } else {
+    if (toroot != "./") {
+      toroot = "./" + toroot;
+    }
+    do {
+      if (toroot.substr(toroot.length-3, 3) == "../" || toroot == "./") {
+        var pos = full.lastIndexOf("/");
+        file = full.substr(pos) + file;
+        full = full.substr(0, pos);
+        toroot = toroot.substr(0, toroot.length-3);
+      }
+    } while (toroot != "" && toroot != "/");
+    return file.substr(1);
+  }
+}
+
+function find_page(url, data)
+{
+  var nodes = data;
+  var result = null;
+  for (var i in nodes) {
+    var d = nodes[i];
+    if (d[1] == url) {
+      return new Array(i);
+    }
+    else if (d[2] != null) {
+      result = find_page(url, d[2]);
+      if (result != null) {
+        return (new Array(i).concat(result));
+      }
+    }
+  }
+  return null;
+}
+
+function init_navtree(navtree_id, toroot, root_nodes)
+{
+  var me = new Object();
+  me.toroot = toroot;
+  me.node = new Object();
+
+  me.node.li = document.getElementById(navtree_id);
+  me.node.children_data = root_nodes;
+  me.node.children = new Array();
+  me.node.children_ul = document.createElement("ul");
+  me.node.get_children_ul = function() { return me.node.children_ul; };
+  //me.node.children_ul.className = "children_ul";
+  me.node.li.appendChild(me.node.children_ul);
+  me.node.depth = 0;
+
+  get_node(me, me.node);
+
+  me.this_page = this_page_relative(toroot);
+  me.breadcrumbs = find_page(me.this_page, root_nodes);
+  if (me.breadcrumbs != null && me.breadcrumbs.length != 0) {
+    var mom = me.node;
+    for (var i in me.breadcrumbs) {
+      var j = me.breadcrumbs[i];
+      mom = mom.children[j];
+      expand_node(me, mom);
+    }
+    mom.label_div.className = mom.label_div.className + " selected";
+    addLoadEvent(function() {
+      scrollIntoView("nav-tree");
+      });
+  }
+}
+
diff --git a/tools/droiddoc/templates/assets/search_autocomplete.js b/tools/droiddoc/templates/assets/search_autocomplete.js
new file mode 100644
index 0000000..2e12e0f
--- /dev/null
+++ b/tools/droiddoc/templates/assets/search_autocomplete.js
@@ -0,0 +1,173 @@
+var gSelectedIndex = -1;
+var gSelectedID = -1;
+var gMatches = new Array();
+var gLastText = "";
+var ROW_COUNT = 30;
+var gInitialized = false;
+var DEFAULT_TEXT = "search developer docs";
+
+function set_row_selected(row, selected)
+{
+    var c1 = row.cells[0];
+  //  var c2 = row.cells[1];
+    if (selected) {
+        c1.className = "jd-autocomplete jd-selected";
+  //      c2.className = "jd-autocomplete jd-selected jd-linktype";
+    } else {
+        c1.className = "jd-autocomplete";
+  //      c2.className = "jd-autocomplete jd-linktype";
+    }
+}
+
+function set_row_values(toroot, row, match)
+{
+    var link = row.cells[0].childNodes[0];
+    link.innerHTML = match.label;
+    link.href = toroot + match.link
+  //  row.cells[1].innerHTML = match.type;
+}
+
+function sync_selection_table(toroot)
+{
+    var filtered = document.getElementById("search_filtered");
+    var r; //TR DOM object
+    var i; //TR iterator
+    gSelectedID = -1;
+
+    filtered.onmouseover = function() { 
+        if(gSelectedIndex >= 0) {
+          set_row_selected(this.rows[gSelectedIndex], false);
+          gSelectedIndex = -1;
+        }
+    }
+
+    //initialize the table; draw it for the first time (but not visible).
+    if (!gInitialized) {
+        for (i=0; i<ROW_COUNT; i++) {
+            var r = filtered.insertRow(-1);
+            var c1 = r.insertCell(-1);
+        //    var c2 = r.insertCell(-1);
+            c1.className = "jd-autocomplete";
+         //   c2.className = "jd-autocomplete jd-linktype";
+            var link = document.createElement("a");
+            c1.onmousedown = function() {
+                window.location = this.firstChild.getAttribute("href");
+            }
+            c1.onmouseover = function() {
+                this.className = this.className + " jd-selected";
+            }
+            c1.onmouseout = function() {
+                this.className = "jd-autocomplete";
+            }
+            c1.appendChild(link);
+        }
+  /*      var r = filtered.insertRow(-1);
+        var c1 = r.insertCell(-1);
+        c1.className = "jd-autocomplete jd-linktype";
+        c1.colSpan = 2; */
+        gInitialized = true;
+    }
+
+    //if we have results, make the table visible and initialize result info
+    if (gMatches.length > 0) {
+        document.getElementById("search_filtered_div").className = "showing";
+        var N = gMatches.length < ROW_COUNT ? gMatches.length : ROW_COUNT;
+        for (i=0; i<N; i++) {
+            r = filtered.rows[i];
+            r.className = "show-row";
+            set_row_values(toroot, r, gMatches[i]);
+            set_row_selected(r, i == gSelectedIndex);
+            if (i == gSelectedIndex) {
+                gSelectedID = gMatches[i].id;
+            }
+        }
+        //start hiding rows that are no longer matches
+        for (; i<ROW_COUNT; i++) {
+            r = filtered.rows[i];
+            r.className = "no-display";
+        }
+        //if there are more results we're not showing, so say so.
+/*      if (gMatches.length > ROW_COUNT) {
+            r = filtered.rows[ROW_COUNT];
+            r.className = "show-row";
+            c1 = r.cells[0];
+            c1.innerHTML = "plus " + (gMatches.length-ROW_COUNT) + " more"; 
+        } else {
+            filtered.rows[ROW_COUNT].className = "hide-row";
+        }*/
+    //if we have no results, hide the table
+    } else {
+        document.getElementById("search_filtered_div").className = "no-display";
+    }
+}
+
+function search_changed(e, kd, toroot)
+{
+    var search = document.getElementById("search_autocomplete");
+    var text = search.value;
+
+    // 13 = enter
+    if (!kd && (e.keyCode == 13)) {
+        document.getElementById("search_filtered_div").className = "no-display";
+        if (gSelectedIndex >= 0) {
+            window.location = toroot + gMatches[gSelectedIndex].link;
+            return false;
+        }
+    }
+    // 38 -- arrow up
+    else if (kd && (e.keyCode == 38)) {
+        if (gSelectedIndex >= 0) {
+            gSelectedIndex--;
+        }
+        sync_selection_table(toroot);
+        return false;
+    }
+    // 40 -- arrow down
+    else if (kd && (e.keyCode == 40)) {
+        if (gSelectedIndex < gMatches.length-1
+                        && gSelectedIndex < ROW_COUNT-1) {
+            gSelectedIndex++;
+        }
+        sync_selection_table(toroot);
+        return false;
+    }
+    else if (!kd) {
+        gMatches = new Array();
+        matchedCount = 0;
+        gSelectedIndex = -1;
+        for (i=0; i<DATA.length; i++) {
+            var s = DATA[i];
+            if (text.length != 0 && s.label.indexOf(text) != -1) {
+                gMatches[matchedCount] = s;
+                if (gSelectedID == s.id) {
+                    gSelectedIndex = matchedCount;
+                }
+                matchedCount++;
+            }
+        }
+        sync_selection_table(toroot);
+        return true; // allow the event to bubble up to the search api
+    }
+}
+
+function search_focus_changed(obj, focused)
+{
+    if (focused) {
+        if(obj.value == DEFAULT_TEXT){
+            obj.value = "";
+            obj.style.color="#000000";
+        }
+    } else {
+        if(obj.value == ""){
+          obj.value = DEFAULT_TEXT;
+          obj.style.color="#aaaaaa";
+        }
+        document.getElementById("search_filtered_div").className = "no-display";
+    }
+}
+
+function submit_search() {
+  var query = document.getElementById('search_autocomplete').value;
+  document.location = '/search.html#q=' + query; 
+  return false;
+}
diff --git a/tools/droiddoc/templates/assets/style.css b/tools/droiddoc/templates/assets/style.css
new file mode 100644
index 0000000..5ad1118
--- /dev/null
+++ b/tools/droiddoc/templates/assets/style.css
@@ -0,0 +1,316 @@
+.jd-toptitle {
+    padding-left: 6px;
+    margin-bottom: 30px;
+    font-size: 160%;
+    font-weight: bold;
+}
+
+div#jd-content table {
+    border: none;
+}
+
+div#jd-content td, div#jd-content th {
+    font-size: small;
+}
+
+div#jd-content table.jd-linktable {
+    margin-top: 3px;
+    border-spacing: 0;
+}
+
+div#jd-content p.jd-deprecated-warning {
+    margin-top: 0;
+    margin-bottom: 10px;
+}
+
+div#jd-content table.jd-linktable th {
+    vertical-align: top;
+    text-align: left;
+    padding-top: 2px;
+    padding-bottom: 2px;
+    padding-left: 7px;
+    padding-right: 7px;
+    border: none;
+    border-top: 1px solid #d2d7d0;
+    background-color: #F7FCF4;
+}
+
+div#jd-content table.jd-linktable td {
+    border: none;
+}
+
+div#jd-content table.jd-linktable td  p {
+    padding: 0;
+    margin: 0;
+    line-height: 110%;
+}
+
+div#jd-content table.jd-linktable .jd-linkcol {
+    vertical-align: top;
+    padding-top: 3px;
+    padding-bottom: 0;
+    padding-left: 7px;
+    padding-right: 7px;
+    border-top: 1px solid #d2d7d0;
+    background-color: #E5F1E0;
+    line-height: 110%;
+}
+
+div#jd-content table.jd-linktable .jd-descrcol {
+    vertical-align: top;
+    padding-top: 3px;
+    padding-bottom: 0;
+    padding-left: 7px;
+    padding-right: 7px;
+    border-top: 1px solid #d2d7d0;
+    background-color: #F7FCF4;
+    line-height: 110%;
+}
+
+div#jd-content table.jd-linktable .jd-descrcol p {
+    padding: 0;
+    margin: 0;
+    line-height: 110%;
+}
+
+div#jd-content table.jd-linktable .jd-valcol {
+    vertical-align: top;
+    padding-top: 3px;
+    padding-bottom: 0;
+    padding-left: 7px;
+    padding-right: 7px;
+    border-top: 1px solid #d2d7d0;
+    background-color: #E5F1E0;
+    line-height: 110%;
+}
+
+div#jd-content table.jd-linktable .jd-commentrow {
+    vertical-align: top;
+    padding-top: 3px;
+    padding-bottom: 4px;
+    padding-left: 7px;
+    padding-right: 7px;
+    background-color: #F7FCF4;
+    line-height: 110%;
+}
+
+div#jd-content div.jd-inheritedlinks {
+    vertical-align: top;
+    margin-top: 9px;
+    padding-left: 7px;
+    padding-right: 7px;
+    background-color: #F7FCF4;
+    line-height: 110%;
+}
+
+div#jd-content .jd-page_title-prefix {
+    padding-top: 2em;
+    margin-bottom: -14pt;
+}
+
+div#jd-content {
+    margin-left: 0;
+    margin-right: 10px;
+    margin-bottom: 0;
+}
+
+div#jd-content h1 {
+    padding-left: 10px;
+}
+
+div#jd-content h2 {
+    padding-left: 10px;
+}
+
+div#jd-content h4 {
+    margin-top: 9px;
+    margin-bottom: 1px;
+}
+
+div#jd-content .jd-descr h5 {
+    margin-bottom: 8px;
+}
+
+div#jd-content .sidebox h3 {
+    margin: 1em 0 0 0;
+}
+
+div#jd-content .jd-letterlist {
+    margin-top: 20px;
+    margin-bottom: 0;
+}
+
+div#jd-content .jd-lettertable {
+    margin-top: 15px;
+    margin-right: 10px;
+}
+div#jd-content .jd-letterentries {
+	list-style: none;
+	margin-left: 0;
+}
+div#jd-content .jd-letterentrycomments {
+    color: gray;
+}
+
+div#jd-content table.jd-inheritance-table {
+    margin-top: 0;
+    margin-left: 10px;
+    margin-right: 10px;
+    border-spacing: 0;
+}
+
+div#jd-content table.jd-inheritance-table td {
+    border: none;
+    margin: 0;
+    padding: 0;
+    background-color: white;
+}
+
+div#jd-content table.jd-inheritance-table .jd-inheritance-space {
+    width: 10px;
+}
+
+div#jd-content table.jd-inheritance-table .jd-inheritance-interface-cell {
+    padding-left: 17px;
+}
+
+div#jd-content h4.jd-details-title {
+    margin: 0;
+    background-color: #E5F1E0;
+    padding: 2px;
+    padding-left: 10px;
+    padding-right: 10px;
+    margin-top: 15px;
+}
+
+div#jd-content .jd-details {
+    margin-top: 0;
+    margin-left: -10px;
+}
+
+div#jd-content .jd-details-descr {
+    line-height: 120%;
+    padding-left: 10px;
+    padding-top: 10px;
+    padding-right: 20px;
+}
+
+div#jd-content .jd-descr h5,
+div#jd-content .jd-details h5 {
+    font-style: normal;
+    text-decoration: none;
+    font-size: 120%;
+}
+
+div#jd-content .jd-more {
+}
+
+div#jd-content .jd-descr {
+    padding-top: 0;
+}
+
+div#jd-content .jd-tagdata {
+    margin-top: 6px;
+    margin-bottom: 6px;
+}
+
+div#jd-content .jd-tagtitle {
+    margin-top: 0px;
+}
+
+div#jd-content .jd-tagtable {
+    margin-top: 10px;
+    border-spacing: 0;
+}
+
+div#jd-content .jd-tagtable th {
+    background: white;
+    padding-left: 10px;
+    padding-right: 10px;
+line-height: 120%;
+}
+
+div#jd-content .jd-tagtable th,
+div#jd-content .jd-tagtable td {
+line-height: 120%;
+    border: none;
+    margin: 0;
+    text-align: left;
+    padding-top: 0px;
+    padding-bottom: 5px;
+}
+
+div#jd-content .Code,code,pre,samp,var {
+    color: #004000;
+}
+
+div#jd-content pre.Code {
+    padding-left: 20px;
+}
+
+/* XXX I would really like to apply font-size: 9pt only if var/samp
+   is NOT inside of a .jd-descr div. */
+div#jd-content .jd-descr code,var,samp {
+    padding-left: 0px;
+}
+
+#search_autocomplete {
+    font-size: 80%;
+}
+
+div#jd-searchbox table.jd-autocomplete-table-hidden {
+    display: none;
+}
+
+div#jd-searchbox table.jd-autocomplete-table-showing {
+    z-index: 10;
+    border: 1px solid #3366cc;
+    position: relative;
+    top: -14px;
+    left: 5px;
+    background-color: white;
+}
+
+div#jd-searchbox td.jd-autocomplete {
+    font-family: Arial, sans-serif;
+    padding-left: 6px;
+    padding-right: 6px;
+    padding-top: 1px;
+    padding-bottom: 1px;
+    font-size: 80%;
+    border: none;
+    margin: 0;
+    line-height: 105%;
+}
+
+div#jd-searchbox td.jd-selected {
+    background-color: #E5F1E0;
+}
+
+div#jd-searchbox td.jd-linktype {
+    color: #999999;
+}
+
+div#jd-content .jd-expando-trigger {
+    margin-left: -8px;
+    margin-right: 0px;
+    border: none;
+}
+
+div#jd-build-id {
+    color: #666;
+    width: 100%;
+    text-align: right;
+    padding-right: 5px;
+    padding-bottom: 3px;
+}
+
+@media print {
+    #jd-searchbox, .jd-nav {
+        display: none;
+    }
+    div#jd-content {
+        margin-top: 0px;
+    }
+}
+
diff --git a/tools/droiddoc/templates/assets/triangle-none.gif b/tools/droiddoc/templates/assets/triangle-none.gif
new file mode 100644
index 0000000..0c7b469
--- /dev/null
+++ b/tools/droiddoc/templates/assets/triangle-none.gif
Binary files differ
diff --git a/tools/droiddoc/templates/class.cs b/tools/droiddoc/templates/class.cs
new file mode 100644
index 0000000..1077886
--- /dev/null
+++ b/tools/droiddoc/templates/class.cs
@@ -0,0 +1,624 @@
+<?cs include:"doctype.cs" ?>
+<?cs include:"macros.cs" ?>
+<html>
+<?cs include:"head_tag.cs" ?>
+<body>
+<script type="text/javascript">
+function toggleInherited(linkObj, expand) {
+    var base = linkObj.getAttribute("id");
+    var list = document.getElementById(base + "-list");
+    var summary = document.getElementById(base + "-summary");
+    var trigger = document.getElementById(base + "-trigger");
+    var a = $(linkObj);
+    if ( (expand == null && a.hasClass("closed")) || expand ) {
+        list.style.display = "none";
+        summary.style.display = "block";
+        trigger.src = "<?cs var:toroot ?>assets/images/triangle-opened.png";
+        a.removeClass("closed");
+        a.addClass("opened");
+    } else if ( (expand == null && a.hasClass("opened")) || (expand == false) ) {
+        list.style.display = "block";
+        summary.style.display = "none";
+        trigger.src = "<?cs var:toroot ?>assets/images/triangle-closed.png";
+        a.removeClass("opened");
+        a.addClass("closed");
+    }
+    return false;
+}
+</script>
+
+<?cs include:"header.cs" ?>
+
+<div class="g-unit" id="doc-content">
+
+<div id="api-info-block">
+
+<?cs # are there inherited members ?>
+<?cs each:cl=class.inherited ?>
+  <?cs if:subcount(cl.methods) ?>
+   <?cs set:inhmethods = #1 ?>
+  <?cs /if ?>
+  <?cs if:subcount(cl.constants) ?>
+   <?cs set:inhconstants = #1 ?>
+  <?cs /if ?>
+  <?cs if:subcount(cl.fields) ?>
+   <?cs set:inhfields = #1 ?>
+  <?cs /if ?>
+  <?cs if:subcount(cl.attrs) ?>
+   <?cs set:inhattrs = #1 ?>
+  <?cs /if ?>
+<?cs /each ?>
+
+<div class="sum-details-links">
+Summary:
+<?cs if:subcount(class.inners) ?>
+  <a href="#nestedclasses">Nested Classes</a>
+  <?cs set:linkcount = #1 ?>
+<?cs /if ?>
+<?cs if:subcount(class.attrs) ?>
+  <?cs if:linkcount ?>&#124; <?cs /if ?><a href="#lattrs">XML Attrs</a>
+  <?cs set:linkcount = #1 ?>
+<?cs /if ?>
+<?cs if:inhattrs ?>
+  <?cs if:linkcount ?>&#124; <?cs /if ?><a href="#inhattrs">Inherited XML Attrs</a>
+  <?cs set:linkcount = #1 ?>
+<?cs /if ?>
+<?cs if:subcount(class.enumConstants) ?>
+  <?cs if:linkcount ?>&#124; <?cs /if ?><a href="#enumconstants">Enums</a>
+  <?cs set:linkcount = #1 ?>
+<?cs /if ?>
+<?cs if:subcount(class.constants) ?>
+  <?cs if:linkcount ?>&#124; <?cs /if ?><a href="#constants">Constants</a>
+  <?cs set:linkcount = #1 ?>
+<?cs /if ?>
+<?cs if:inhconstants ?>
+  <?cs if:linkcount ?>&#124; <?cs /if ?><a href="#inhconstants">Inherited Constants</a>
+  <?cs set:linkcount = #1 ?>
+<?cs /if ?>
+<?cs if:subcount(class.fields) ?>
+  <?cs if:linkcount ?>&#124; <?cs /if ?><a href="#lfields">Fields</a>
+  <?cs set:linkcount = #1 ?>
+<?cs /if ?>
+<?cs if:inhfields ?>
+  <?cs if:linkcount ?>&#124; <?cs /if ?><a href="#inhfields">Inherited Fields</a>
+  <?cs set:linkcount = #1 ?>
+<?cs /if ?>
+<?cs if:subcount(class.ctors.public) ?>
+  <?cs if:linkcount ?>&#124; <?cs /if ?><a href="#pubctors">Ctors</a>
+  <?cs set:linkcount = #1 ?>
+<?cs /if ?>
+<?cs if:subcount(class.ctors.protected) ?>
+  <?cs if:linkcount ?>&#124; <?cs /if ?><a href="#proctors">Protected Ctors</a>
+  <?cs set:linkcount = #1 ?>
+<?cs /if ?>
+<?cs if:subcount(class.methods.public) ?>
+  <?cs if:linkcount ?>&#124; <?cs /if ?><a href="#pubmethods">Methods</a>
+  <?cs set:linkcount = #1 ?>
+<?cs /if ?>
+<?cs if:subcount(class.methods.protected) ?>
+  <?cs if:linkcount ?>&#124; <?cs /if ?><a href="#promethods">Protected Methods</a>
+  <?cs set:linkcount = #1 ?>
+<?cs /if ?>
+<?cs if:inhmethods ?>
+  <?cs if:linkcount ?>&#124; <?cs /if ?><a href="#inhmethods">Inherited Methods</a>
+<?cs /if ?>
+</nobr>
+<?cs if:inhattrs || inhconstants || inhfields || inhmethods || subcount(class.subclasses.direct) || subcount(class.subclasses.indirect) ?>
+&#124; <a href="#" onclick="return toggleAllSummaryInherited(this)">[Expand All]</a>
+<?cs /if ?>
+</div>
+</div>
+
+<?cs # this next line must be exactly like this to be parsed by eclipse ?>
+<!-- ======== START OF CLASS DATA ======== -->
+
+<div id="jd-header">
+    <?cs var:class.scope ?>
+    <?cs var:class.static ?> 
+    <?cs var:class.final ?> 
+    <?cs var:class.abstract ?>
+    <?cs var:class.kind ?>
+<h1><?cs var:class.name ?></h1>
+
+<?cs set:colspan = subcount(class.inheritance) ?>
+<?cs each:supr = class.inheritance ?>
+  <?cs if:colspan == 2 ?>
+    extends <?cs call:type_link(supr.short_class) ?><br/>
+  <?cs /if ?>
+  <?cs if:last(supr) && subcount(supr.interfaces) ?>
+      implements 
+      <?cs each:t=supr.interfaces ?>
+        <?cs call:type_link(t) ?> 
+      <?cs /each ?>
+  <?cs /if ?>
+  <?cs set:colspan = colspan-1 ?>
+<?cs /each ?>
+
+</div><!-- end header -->
+
+
+<div id="jd-content">
+<table class="jd-inheritance-table">
+<?cs set:colspan = subcount(class.inheritance) ?>
+<?cs each:supr = class.inheritance ?>
+    <tr>
+        <?cs loop:i = 1, (subcount(class.inheritance)-colspan), 1 ?>
+            <td class="jd-inheritance-space">&nbsp;<?cs if:(subcount(class.inheritance)-colspan) == i ?>&nbsp;&nbsp;&#x21b3;<?cs /if ?></td>
+        <?cs /loop ?> 	
+        <td colspan="<?cs var:colspan ?>" class="jd-inheritance-class-cell"><?cs
+            if:colspan == 1
+                ?><?cs call:class_name(class.qualifiedType) ?><?cs 
+            else 
+                ?><?cs call:type_link(supr.class) ?><?cs
+            /if ?></td>
+    </tr>
+    <?cs set:colspan = colspan-1 ?>
+<?cs /each ?>
+</table>
+
+<?cs # this next line must be exactly like this to be parsed by eclipse ?>
+
+<?cs if:subcount(class.subclasses.direct) ?>
+<table class="jd-sumtable jd-sumtable-subclasses"><tr><td colspan="12" style="border:none;margin:0;padding:0;">
+<?cs call:expando_trigger("subclasses-direct", "closed") ?>Known Direct Subclasses
+<?cs call:expandable_class_list("subclasses-direct", class.subclasses.direct, "list") ?>
+</td></tr></table>
+<?cs /if ?>
+
+<?cs if:subcount(class.subclasses.indirect) ?>
+<table class="jd-sumtable jd-sumtable-subclasses"><tr><td colspan="12" style="border:none;margin:0;padding:0;">
+<?cs call:expando_trigger("subclasses-indirect", "closed") ?>Known Indirect Subclasses
+<?cs call:expandable_class_list("subclasses-indirect", class.subclasses.indirect, "list") ?>
+</td></tr></table>
+<?cs /if ?>
+
+<div class="jd-descr">
+<?cs call:deprecated_warning(class) ?>
+<?cs if:subcount(class.descr) ?>
+<h2>Class Overview</h2>
+<p><?cs call:tag_list(class.descr) ?></p>
+<?cs /if ?>
+
+<?cs call:see_also_tags(class.seeAlso) ?>
+
+</div><!-- jd-descr -->
+
+
+<?cs # summary macros ?>
+
+<?cs def:write_method_summary(methods) ?>
+<?cs set:count = #1 ?>
+<?cs each:method = methods ?>
+    <tr <?cs if:count % #2 ?>class="alt-color"<?cs /if ?> >
+        <td class="jd-typecol"><nobr>
+            <?cs var:method.abstract ?>
+            <?cs var:method.synchronized ?>
+            <?cs var:method.final ?>
+            <?cs var:method.static ?>
+            <?cs call:type_link(method.generic) ?>
+            <?cs call:type_link(method.returnType) ?></nobr>
+        </td>
+        <td class="jd-linkcol" width="100%"><nobr>
+        <span class="sympad"><a href="<?cs var:toroot ?><?cs var:method.href ?>">
+        <?cs var:method.name ?></a></span>(<?cs call:parameter_list(method.params) ?>)</nobr>
+        <?cs if:subcount(method.shortDescr) || subcount(method.deprecated) ?>
+        <div class="jd-descrdiv"><?cs call:short_descr(method) ?></div>
+  <?cs /if ?>
+  </td></tr>
+<?cs set:count = count + #1 ?>
+<?cs /each ?>
+<?cs /def ?>
+
+<?cs def:write_field_summary(fields) ?>
+<?cs set:count = #1 ?>
+    <?cs each:field=fields ?>
+      <tr <?cs if:count % #2 ?>class="alt-color"<?cs /if ?> >
+          <td class="jd-typecol"><nobr>
+          <?cs var:field.scope ?>
+          <?cs var:field.static ?>
+          <?cs var:field.final ?>
+          <?cs call:type_link(field.type) ?></nobr></td>
+          <td class="jd-linkcol"><a href="<?cs var:toroot ?><?cs var:field.href ?>"><?cs var:field.name ?></a></td>
+          <td class="jd-descrcol" width="100%"><?cs call:short_descr(field) ?></td>
+      </tr>
+      <?cs set:count = count + #1 ?>
+    <?cs /each ?>
+<?cs /def ?>
+
+<?cs def:write_constant_summary(fields) ?>
+<?cs set:count = #1 ?>
+    <?cs each:field=fields ?>
+    <tr <?cs if:count % #2 ?>class="alt-color"<?cs /if ?> >
+        <td class="jd-typecol"><?cs call:type_link(field.type) ?></td>
+        <td class="jd-linkcol"><a href="<?cs var:toroot ?><?cs var:field.href ?>"><?cs var:field.name ?></a></td>
+        <td class="jd-descrcol" width="100%"><?cs call:short_descr(field) ?></td>
+    </tr>
+    <?cs set:count = count + #1 ?>
+    <?cs /each ?>
+<?cs /def ?>
+
+<?cs def:write_attr_summary(attrs) ?>
+<?cs set:count = #1 ?>
+    <tr>
+        <td><nobr><em>Attribute Name</em></nobr></td>
+        <td><nobr><em>Related Method</em></nobr></td>
+        <td><nobr><em>Description</em></nobr></td>
+    </tr>
+    <?cs each:attr=attrs ?>
+    <tr <?cs if:count % #2 ?>class="alt-color"<?cs /if ?> >
+        <td class="jd-linkcol"><a href="<?cs var:toroot ?><?cs var:attr.href ?>"><?cs var:attr.name ?></a></td>
+        <td class="jd-linkcol"><?cs each:m=attr.methods ?>
+            <a href="<?cs var:toroot ?><?cs var:m.href ?>"><?cs var:m.name ?></a>
+            <?cs /each ?>
+        </td>
+        <td class="jd-descrcol" width="100%"><?cs call:short_descr(attr) ?>&nbsp;</td>
+    </tr>
+    <?cs set:count = count + #1 ?>
+    <?cs /each ?>
+<?cs /def ?>
+
+<?cs def:write_inners_summary(classes) ?>
+<?cs set:count = #1 ?>
+  <?cs each:cl=class.inners ?>
+    <tr <?cs if:count % #2 ?>class="alt-color"<?cs /if ?> >
+      <td class="jd-typecol"><nobr>
+        <?cs var:class.scope ?>
+        <?cs var:class.static ?> 
+        <?cs var:class.final ?> 
+        <?cs var:class.abstract ?>
+        <?cs var:class.kind ?></nobr></td>
+      <td class="jd-linkcol"><?cs call:type_link(cl.type) ?></td>
+      <td class="jd-descrcol" width="100%"><?cs call:short_descr(cl) ?>&nbsp;</td>
+    </tr>
+    <?cs set:count = count + #1 ?>
+    <?cs /each ?>
+<?cs /def ?>
+
+<?cs # end macros ?>
+
+<div class="jd-descr">
+<h2>Summary</h2>
+
+<?cs if:subcount(class.inners) ?>
+<?cs # this next line must be exactly like this to be parsed by eclipse ?>
+<!-- ======== NESTED CLASS SUMMARY ======== -->
+<table id="nestedclasses" class="jd-sumtable"><tr><th colspan="12">Nested Classes</th></tr>
+<?cs call:write_inners_summary(class.inners) ?>
+<?cs /if ?>
+
+<?cs # this next line must be exactly like this to be parsed by eclipse ?>
+<?cs if:subcount(class.attrs) ?>
+<!-- =========== FIELD SUMMARY =========== -->
+<table id="lattrs" class="jd-sumtable"><tr><th colspan="12">XML Attributes</th></tr>
+<?cs call:write_attr_summary(class.attrs) ?>
+<?cs /if ?>
+
+<?cs # if there are inherited attrs, write the table ?>
+<?cs if:inhattrs ?>
+<?cs # this next line must be exactly like this to be parsed by eclipse ?>
+<!-- =========== FIELD SUMMARY =========== -->
+<table id="inhattrs" class="jd-sumtable"><tr><th>
+  <a href="#" class="toggle-all" onclick="return toggleAllInherited(this, null)">[Expand]</a>
+  <div style="clear:left;">Inherited XML Attributes</div></th></tr>
+<?cs each:cl=class.inherited ?>
+<?cs if:subcount(cl.attrs) ?>
+<tr><td colspan="12">
+<?cs call:expando_trigger("inherited-attrs-"+cl.qualified, "closed") ?>From <?cs var:cl.kind ?>
+<a href="<?cs var:toroot ?><?cs var:cl.link ?>"><?cs var:cl.qualified ?></a>
+<div id="inherited-attrs-<?cs var:cl.qualified ?>">
+  <div id="inherited-attrs-<?cs var:cl.qualified ?>-list"
+        class="jd-inheritedlinks">
+  </div>
+  <div id="inherited-attrs-<?cs var:cl.qualified ?>-summary" style="display: none;">
+    <table class="jd-sumtable-expando">
+    <?cs call:write_attr_summary(cl.attrs) ?></table>
+  </div>
+</div>
+</td></tr>
+<?cs /if ?>
+<?cs /each ?>
+</table>
+<?cs /if ?>
+
+<?cs if:subcount(class.enumConstants) ?>
+<?cs # this next line must be exactly like this to be parsed by eclipse ?>
+<!-- =========== ENUM CONSTANT SUMMARY =========== -->
+<table id="enumconstants" class="jd-sumtable"><tr><th colspan="12">Enum Values</th></tr>
+<?cs set:count = #1 ?>
+    <?cs each:field=class.enumConstants ?>
+    <tr <?cs if:count % #2 ?>class="alt-color"<?cs /if ?> >
+        <td class="jd-descrcol"><?cs call:type_link(field.type) ?>&nbsp;</td>
+        <td class="jd-linkcol"><a href="<?cs var:toroot ?><?cs var:field.href ?>"><?cs var:field.name ?></a>&nbsp;</td>
+        <td class="jd-descrcol" width="100%"><?cs call:short_descr(field) ?>&nbsp;</td>
+    </tr>
+    <?cs set:count = count + #1 ?>
+    <?cs /each ?>
+<?cs /if ?>
+
+<?cs if:subcount(class.constants) ?>
+<?cs # this next line must be exactly like this to be parsed by eclipse ?>
+<!-- =========== ENUM CONSTANT SUMMARY =========== -->
+<table id="constants" class="jd-sumtable"><tr><th colspan="12">Constants</th></tr>
+<?cs call:write_constant_summary(class.constants) ?>
+</table>
+<?cs /if ?>
+
+<?cs # if there are inherited constants, write the table ?>
+<?cs if:inhconstants ?>
+<?cs # this next line must be exactly like this to be parsed by eclipse ?>
+<!-- =========== ENUM CONSTANT SUMMARY =========== -->
+<table id="inhconstants" class="jd-sumtable"><tr><th>
+  <a href="#" class="toggle-all" onclick="return toggleAllInherited(this, null)">[Expand]</a>
+  <div style="clear:left;">Inherited Constants</div></th></tr>
+<?cs each:cl=class.inherited ?>
+<?cs if:subcount(cl.constants) ?>
+<tr><td colspan="12">
+<?cs call:expando_trigger("inherited-constants-"+cl.qualified, "closed") ?>From <?cs var:cl.kind ?>
+<a href="<?cs var:toroot ?><?cs var:cl.link ?>"><?cs var:cl.qualified ?></a>
+<div id="inherited-constants-<?cs var:cl.qualified ?>">
+  <div id="inherited-constants-<?cs var:cl.qualified ?>-list"
+        class="jd-inheritedlinks">
+  </div>
+  <div id="inherited-constants-<?cs var:cl.qualified ?>-summary" style="display: none;">
+    <table class="jd-sumtable-expando">
+    <?cs call:write_constant_summary(cl.constants) ?></table>
+  </div>
+</div>
+</td></tr>
+<?cs /if ?>
+<?cs /each ?>
+</table>
+<?cs /if ?>
+
+<?cs if:subcount(class.fields) ?>
+<?cs # this next line must be exactly like this to be parsed by eclipse ?>
+<!-- =========== FIELD SUMMARY =========== -->
+<table id="lfields" class="jd-sumtable"><tr><th colspan="12">Fields</th></tr>
+<?cs call:write_field_summary(class.fields) ?>
+</table>
+<?cs /if ?>
+
+<?cs # if there are inherited fields, write the table ?>
+<?cs if:inhfields ?>
+<?cs # this next line must be exactly like this to be parsed by eclipse ?>
+<!-- =========== FIELD SUMMARY =========== -->
+<table id="inhfields" class="jd-sumtable"><tr><th>
+  <a href="#" class="toggle-all" onclick="return toggleAllInherited(this, null)">[Expand]</a>
+  <div style="clear:left;">Inherited Fields</div></th></tr>
+<?cs each:cl=class.inherited ?>
+<?cs if:subcount(cl.fields) ?>
+<tr><td colspan="12">
+<?cs call:expando_trigger("inherited-fields-"+cl.qualified, "closed") ?>From <?cs var:cl.kind ?>
+<a href="<?cs var:toroot ?><?cs var:cl.link ?>"><?cs var:cl.qualified ?></a>
+<div id="inherited-fields-<?cs var:cl.qualified ?>">
+  <div id="inherited-fields-<?cs var:cl.qualified ?>-list"
+        class="jd-inheritedlinks">
+  </div>
+  <div id="inherited-fields-<?cs var:cl.qualified ?>-summary" style="display: none;">
+    <table class="jd-sumtable-expando">
+    <?cs call:write_field_summary(cl.fields) ?></table>
+  </div>
+</div>
+</td></tr>
+<?cs /if ?>
+<?cs /each ?>
+</table>
+<?cs /if ?>
+
+<?cs if:subcount(class.ctors.public) ?>
+<?cs # this next line must be exactly like this to be parsed by eclipse ?>
+<!-- ======== CONSTRUCTOR SUMMARY ======== -->
+<table id="pubctors" class="jd-sumtable"><tr><th colspan="12">Public Constructors</th></tr>
+<?cs call:write_method_summary(class.ctors.public) ?>
+</table>
+<?cs /if ?>
+
+<?cs if:subcount(class.ctors.protected) ?>
+<?cs # this next line must be exactly like this to be parsed by eclipse ?>
+<!-- ======== CONSTRUCTOR SUMMARY ======== -->
+<table id="proctors" class="jd-sumtable"><tr><th colspan="12">Protected Constructors</th></tr>
+<?cs call:write_method_summary(class.ctors.protected) ?>
+</table>
+<?cs /if ?>
+
+<?cs if:subcount(class.methods.public) ?>
+<?cs # this next line must be exactly like this to be parsed by eclipse ?>
+<!-- ========== METHOD SUMMARY =========== -->
+<table id="pubmethods" class="jd-sumtable"><tr><th colspan="12">Public Methods</th></tr>
+<?cs call:write_method_summary(class.methods.public) ?>
+</table>
+<?cs /if ?>
+
+<?cs if:subcount(class.methods.protected) ?>
+<?cs # this next line must be exactly like this to be parsed by eclipse ?>
+<!-- ========== METHOD SUMMARY =========== -->
+<table id="promethods" class="jd-sumtable"><tr><th colspan="12">Protected Methods</th></tr>
+<?cs call:write_method_summary(class.methods.protected) ?>
+</table>
+<?cs /if ?>
+
+<?cs # if there are inherited methods, write the table ?>
+<?cs if:inhmethods ?>
+<?cs # this next line must be exactly like this to be parsed by eclipse ?>
+<!-- ========== METHOD SUMMARY =========== -->
+<table id="inhmethods" class="jd-sumtable"><tr><th>
+  <a href="#" class="toggle-all" onclick="return toggleAllInherited(this, null)">[Expand]</a>
+  <div style="clear:left;">Inherited Methods</div></th></tr>
+<?cs each:cl=class.inherited ?>
+<?cs if:subcount(cl.methods) ?>
+<tr><td colspan="12"><?cs call:expando_trigger("inherited-methods-"+cl.qualified, "closed") ?>
+From <?cs var:cl.kind ?> <a href="<?cs var:toroot ?><?cs var:cl.link ?>"><?cs var:cl.qualified ?></a>
+<div id="inherited-methods-<?cs var:cl.qualified ?>">
+  <div id="inherited-methods-<?cs var:cl.qualified ?>-list"
+        class="jd-inheritedlinks">
+  </div>
+  <div id="inherited-methods-<?cs var:cl.qualified ?>-summary" style="display: none;">
+    <table class="jd-sumtable-expando">
+    <?cs call:write_method_summary(cl.methods) ?></table>
+  </div>
+</div>
+</td></tr>
+<?cs /if ?>
+<?cs /each ?>
+</table>
+<?cs /if ?>
+
+</div><!-- jd-descr (summary) -->
+
+<!-- Details -->
+
+<?cs def:write_field_details(fields) ?>
+<?cs each:field=fields ?>
+<?cs # this next line must be exactly like this to be parsed by eclipse ?>
+<?cs # the A tag in the next line must remain where it is, so that Eclipse can parse the docs ?>
+<A NAME="<?cs var:field.anchor ?>"></A>
+<div class="jd-details"> 
+    <h4 class="jd-details-title">
+      <span class="normal">
+        <?cs var:field.scope ?> 
+        <?cs var:field.static ?> 
+        <?cs var:field.final ?> 
+        <?cs call:type_link(field.type) ?>
+      </span>
+        <?cs var:field.name ?>
+    </h4>
+    <div class="jd-details-descr"><?cs call:description(field) ?>
+    <?cs if:subcount(field.constantValue) ?>
+        <div class="jd-tagdata">
+        <span class="jd-tagtitle">Constant Value: </span>
+        <span>
+            <?cs if:field.constantValue.isString ?>
+                <?cs var:field.constantValue.str ?>
+            <?cs else ?>
+                <?cs var:field.constantValue.dec ?>
+                (<?cs var:field.constantValue.hex ?>)
+            <?cs /if ?>
+        </span>
+        </div>
+    <?cs /if ?>
+    </div>
+</div>
+<?cs /each ?>
+<?cs /def ?>
+
+<?cs def:write_method_details(methods) ?>
+<?cs each:method=methods ?>
+<?cs # the A tag in the next line must remain where it is, so that Eclipse can parse the docs ?>
+<A NAME="<?cs var:method.anchor ?>"></A>
+<div class="jd-details"> 
+    <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.synchronized ?> 
+        <?cs call:type_link(method.returnType) ?>
+      </span>
+      <span class="sympad"><?cs var:method.name ?></span>
+      <span class="normal">(<?cs call:parameter_list(method.params) ?>)</span>
+    </h4>
+    <div class="jd-details-descr"><?cs call:description(method) ?></div>
+</div>
+<?cs /each ?>
+<?cs /def ?>
+
+<?cs def:write_attr_details(attrs) ?>
+<?cs each:attr=attrs ?>
+<?cs # the A tag in the next line must remain where it is, so that Eclipse can parse the docs ?>
+<A NAME="<?cs var:attr.anchor ?>"></A>
+<div class="jd-details">
+    <h4 class="jd-details-title"><?cs var:attr.name ?></h4>
+    <div class="jd-details-descr">
+        <?cs call:description(attr) ?>
+
+        <div class="jd-tagdata">
+            <h5 class="jd-tagtitle">Related Methods</h5>
+            <ul class="nolist">
+            <?cs each:m=attr.methods ?>
+                <li><a href="<?cs var:toroot ?><?cs var:m.href ?>"><?cs var:m.name ?></a></li>
+            <?cs /each ?>
+            </ul>
+        </div>
+    </div>
+</div>
+<?cs /each ?>
+<?cs /def ?>
+
+
+<!-- XML Attributes -->
+<?cs if:subcount(class.attrs) ?>
+<?cs # this next line must be exactly like this to be parsed by eclipse ?>
+<!-- ========= FIELD DETAIL ======== -->
+<h2>XML Attributes</h2>
+<?cs call:write_attr_details(class.attrs) ?>
+<?cs /if ?>
+
+<!-- Enum Values -->
+<?cs if:subcount(class.enumConstants) ?>
+<?cs # this next line must be exactly like this to be parsed by eclipse ?>
+<!-- ========= ENUM CONSTANTS DETAIL ======== -->
+<h2>Enum Values</h2>
+<?cs call:write_field_details(class.enumConstants) ?>
+<?cs /if ?>
+
+<!-- Constants -->
+<?cs if:subcount(class.constants) ?>
+<?cs # this next line must be exactly like this to be parsed by eclipse ?>
+<!-- ========= ENUM CONSTANTS DETAIL ======== -->
+<h2>Constants</h2>
+<?cs call:write_field_details(class.constants) ?>
+<?cs /if ?>
+
+<!-- Fields -->
+<?cs if:subcount(class.fields) ?>
+<?cs # this next line must be exactly like this to be parsed by eclipse ?>
+<!-- ========= FIELD DETAIL ======== -->
+<h2>Fields</h2>
+<?cs call:write_field_details(class.fields) ?>
+<?cs /if ?>
+
+<!-- Public ctors -->
+<?cs if:subcount(class.ctors.public) ?>
+<?cs # this next line must be exactly like this to be parsed by eclipse ?>
+<!-- ========= CONSTRUCTOR DETAIL ======== -->
+<h2>Public Constructors</h2>
+<?cs call:write_method_details(class.ctors.public) ?>
+<?cs /if ?>
+
+<?cs # this next line must be exactly like this to be parsed by eclipse ?>
+<!-- ========= CONSTRUCTOR DETAIL ======== -->
+<!-- Protected ctors -->
+<?cs if:subcount(class.ctors.protected) ?>
+<h2>Protected Constructors</h2>
+<?cs call:write_method_details(class.ctors.protected) ?>
+<?cs /if ?>
+
+<?cs # this next line must be exactly like this to be parsed by eclipse ?>
+<!-- ========= METHOD DETAIL ======== -->
+<!-- Public methdos -->
+<?cs if:subcount(class.methods.public) ?>
+<h2>Public Methods</h2>
+<?cs call:write_method_details(class.methods.public) ?>
+<?cs /if ?>
+
+<?cs # this next line must be exactly like this to be parsed by eclipse ?>
+<!-- ========= METHOD DETAIL ======== -->
+<?cs if:subcount(class.methods.protected) ?>
+<h2>Protected Methods</h2>
+<?cs call:write_method_details(class.methods.protected) ?>
+<?cs /if ?>
+
+<?cs # the next two lines must be exactly like this to be parsed by eclipse ?>
+<!-- ========= END OF CLASS DATA ========= -->
+<A NAME="navbar_top"></A>
+
+<?cs include:"footer.cs" ?>
+</div> <!-- jd-content -->
+
+</div><!-- end doc-content -->
+
+<?cs include:"trailer.cs" ?>
+
+</body>
+</html>
diff --git a/tools/droiddoc/templates/classes.cs b/tools/droiddoc/templates/classes.cs
new file mode 100644
index 0000000..abe8e4e
--- /dev/null
+++ b/tools/droiddoc/templates/classes.cs
@@ -0,0 +1,41 @@
+<?cs include:"doctype.cs" ?>
+<?cs include:"macros.cs" ?>
+<html>
+<?cs include:"head_tag.cs" ?>
+<?cs include:"header.cs" ?>
+
+<div class="g-unit" id="doc-content">
+
+<div id="jd-header">
+<h1><?cs var:page.title ?></h1>
+</div>
+
+<div id="jd-content">
+
+<div class="jd-letterlist"><?cs each:letter=docs.classes ?>
+    <a href="#letter_<?cs name:letter ?>"><?cs name:letter ?></a><?cs /each?>
+</div>
+
+<?cs each:letter=docs.classes ?>
+<?cs set:count = #1 ?>
+<h2 id="letter_<?cs name:letter ?>"><?cs name:letter ?></h2>
+<table class="jd-sumtable">
+    <?cs set:cur_row = #0 ?>
+    <?cs each:cl = letter ?>
+        <tr <?cs if:count % #2 ?>class="alt-color"<?cs /if ?> >
+            <td class="jd-linkcol"><?cs call:type_link(cl.type) ?></td>
+            <td class="jd-descrcol" width="100%"><?cs call:short_descr(cl) ?>&nbsp;</td>
+        </tr>
+    <?cs set:count = count + #1 ?>
+    <?cs /each ?>
+</table>
+<?cs /each ?>
+
+<?cs include:"footer.cs" ?>
+</div><!-- end jd-content -->
+</div><!-- end doc-content -->
+
+<?cs include:"trailer.cs" ?>
+
+</body>
+</html>
\ No newline at end of file
diff --git a/tools/droiddoc/templates/customization.cs b/tools/droiddoc/templates/customization.cs
new file mode 100644
index 0000000..3646495
--- /dev/null
+++ b/tools/droiddoc/templates/customization.cs
@@ -0,0 +1,24 @@
+<?cs # This default template file is meant to be replaced.                      ?>
+<?cs # Use the -templatedir arg to javadoc to set your own directory with a     ?>
+<?cs # replacement for this file in it. ?>
+
+<?cs # appears at the top of every page ?><?cs 
+def:custom_masthead() ?>
+  <div id="header">
+      <div id="headerLeft">
+          <a href="<?cs var:toroot ?>index.html" tabindex="-1"><?cs var:page.title ?></a>
+      </div>
+      <div id="headerRight">
+          <?cs call:default_search_box() ?>
+      </div><!-- headerRight -->
+  </div><!-- header --><?cs 
+/def ?>
+
+<?cs # appear at the bottom of every page ?>
+<?cs def:custom_copyright() ?><?cs /def ?>
+<?cs def:custom_cc_copyright() ?><?cs /def ?>
+<?cs def:custom_footerlinks() ?><?cs /def ?>
+<?cs def:custom_buildinfo() ?>Build <?cs var:page.build ?> - <?cs var:page.now ?><?cs /def ?>
+
+<?cs # appears on the side of the page ?>
+<?cs def:custom_left_nav() ?><?cs call:default_left_nav() ?><?cs /def ?>
diff --git a/tools/droiddoc/templates/docpage.cs b/tools/droiddoc/templates/docpage.cs
new file mode 100644
index 0000000..06b3f35
--- /dev/null
+++ b/tools/droiddoc/templates/docpage.cs
@@ -0,0 +1,42 @@
+<?cs include:"doctype.cs" ?>
+<?cs include:"macros.cs" ?>
+<html>
+<?cs include:"head_tag.cs" ?>
+<body class="gc-documentation">
+<?cs include:"header.cs" ?>
+
+<div class="g-unit" id="doc-content"><a name="top"></a>
+
+<div id="jd-header" class="guide-header">
+  <span class="crumb">
+    <?cs if:parent.link ?>
+      <a href="<?cs var:parent.link ?>"><?cs var:parent.title ?></a> >
+    <?cs else ?>&nbsp;
+    <?cs /if ?>
+  </span>
+<h1><?cs var:page.title ?></h1>
+</div>
+
+  <div id="jd-content">
+
+
+    <div class="jd-descr">
+    <?cs call:tag_list(root.descr) ?>
+    </div>
+
+  <a href="#top" style="float:right">&uarr; Go to top</a>
+  <?cs if:parent.link ?>
+    <p><a href="<?cs var:parent.link ?>">&larr; Back to <?cs var:parent.title ?></a></p>
+  <?cs /if ?>
+  </div>
+
+<?cs include:"footer.cs" ?>
+</div><!-- end doc-content -->
+
+<?cs include:"trailer.cs" ?>
+
+</body>
+</html>
+
+
+
diff --git a/tools/droiddoc/templates/doctype.cs b/tools/droiddoc/templates/doctype.cs
new file mode 100644
index 0000000..643f992
--- /dev/null
+++ b/tools/droiddoc/templates/doctype.cs
@@ -0,0 +1 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
\ No newline at end of file
diff --git a/tools/droiddoc/templates/footer.cs b/tools/droiddoc/templates/footer.cs
new file mode 100644
index 0000000..bb82c8d
--- /dev/null
+++ b/tools/droiddoc/templates/footer.cs
@@ -0,0 +1,19 @@
+<div id="footer">
+
+<?cs if:reference||guide ?>
+  <div id="copyright">
+    <?cs call:custom_copyright() ?>
+  </div>
+  <div id="build_info">
+    <?cs call:custom_buildinfo() ?>
+  </div>
+<?cs elif:!hide_license_footer ?>
+  <div id="copyright">
+    <?cs call:custom_cc_copyright() ?>
+  </div>
+<?cs /if ?>
+  <div id="footerlinks">
+    <?cs call:custom_footerlinks() ?>
+  </div>
+
+</div> <!-- end footer -->
diff --git a/tools/droiddoc/templates/head_tag.cs b/tools/droiddoc/templates/head_tag.cs
new file mode 100644
index 0000000..55d0225
--- /dev/null
+++ b/tools/droiddoc/templates/head_tag.cs
@@ -0,0 +1,37 @@
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+<link rel="shortcut icon" type="image/x-icon" href="<?cs var:toroot ?>favicon.ico" />
+<title><?cs 
+  if:page.title ?><?cs 
+    var:page.title ?><?cs
+    if:sdk.version ?> (<?cs
+      var:sdk.version ?>)<?cs
+    /if ?> | <?cs
+  /if ?>Android Developers</title><?cs 
+if:guide||sdk ?>
+<link href="<?cs var:toroot ?>assets/android-developer-docs-devguide.css" rel="stylesheet" type="text/css" /><?cs 
+else ?>
+<link href="<?cs var:toroot ?>assets/android-developer-docs.css" rel="stylesheet" type="text/css" /><?cs 
+/if ?>
+<script src="<?cs var:toroot ?>assets/search_autocomplete.js" type="text/javascript"></script>
+<script src="<?cs var:toroot ?>reference/lists.js" type="text/javascript"></script>
+<script src="<?cs var:toroot ?>assets/jquery-resizable.min.js" type="text/javascript"></script>
+<script src="<?cs var:toroot ?>assets/android-developer-docs.js" type="text/javascript"></script>
+<script type="text/javascript">
+  setToRoot("<?cs var:toroot ?>");
+</script><?cs 
+if:reference ?>
+<script src="<?cs var:toroot ?>navtree_data.js" type="text/javascript"></script>
+<script src="<?cs var:toroot ?>assets/navtree.js" type="text/javascript"></script><?cs 
+/if ?>
+<noscript>
+  <style type="text/css">
+    body{overflow:auto;}
+    #body-content{position:relative; top:0;}
+    #doc-content{overflow:visible;border-left:3px solid #666;}
+    #side-nav{padding:0;}
+    #side-nav .toggle-list ul {display:block;}
+    #resize-packages-nav{border-bottom:3px solid #666;}
+  </style>
+</noscript>
+</head>
diff --git a/tools/droiddoc/templates/header.cs b/tools/droiddoc/templates/header.cs
new file mode 100644
index 0000000..e8301be
--- /dev/null
+++ b/tools/droiddoc/templates/header.cs
@@ -0,0 +1,3 @@
+<?cs call:custom_masthead() ?>
+<?cs call:custom_left_nav() ?>
+
diff --git a/tools/droiddoc/templates/hierarchy.cs b/tools/droiddoc/templates/hierarchy.cs
new file mode 100644
index 0000000..a607ffd
--- /dev/null
+++ b/tools/droiddoc/templates/hierarchy.cs
@@ -0,0 +1,68 @@
+<?cs include:"macros.cs" ?>
+<html>
+<style>
+    .jd-hierarchy-spacer {
+        width: 15px;
+    }
+    .jd-hierarchy-data {
+        text-align: left;
+        vertical-align: top;
+    }
+</style>
+<?cs include:"head_tag.cs" ?>
+<?cs include:"header.cs" ?>
+
+<div class="g-unit" id="doc-content">
+
+<div id="jd-header">
+<h1><?cs var:page.title ?></h1>
+</div>
+
+<div id="jd-content">
+
+<div style="margin-left: 20px; margin-right: 20px;">
+
+<?cs def:hierarchy_list(classes) ?>
+<?cs each:cl = classes ?>
+<tr>
+    <?cs loop:x=#0,cl.indent,#1 ?><td class="jd-hierarchy-spacer"></td><?cs /loop ?>
+    <td class="jd-hierarchy-data" colspan="<?cs var:cl.colspan ?>">
+    <?cs if:cl.exists ?>
+        <?cs call:type_link(cl.class) ?>
+    <?cs else ?>
+        <?cs var:cl.value ?>
+    <?cs /if ?>
+    </td>
+    <td class="jd-hierarchy-data">
+    <?cs each:iface = cl.interfaces ?>
+        <?cs if:iface.exists ?>
+            <?cs call:type_link(iface.class) ?>
+        <?cs else ?>
+            <?cs var:iface.value ?>
+        <?cs /if ?> &nbsp;&nbsp;
+    <?cs /each ?>
+    &nbsp;
+    </td>
+</tr>
+<?cs call:hierarchy_list(cl.derived) ?>
+<?cs /each ?>
+<?cs /def ?>
+
+
+<table border="0" cellpadding="0" cellspacing="1">
+<th class="jd-hierarchy-data" colspan="<?cs var:colspan ?>">Class</th>
+<th class="jd-hierarchy-data">Interfaces</th>
+<?cs call:hierarchy_list(classes) ?>
+</table>
+
+</div>
+
+<?cs include:"footer.cs" ?>
+</div><!-- end jd-content -->
+</div><!-- end doc-content -->
+
+<?cs include:"trailer.cs" ?>
+
+</body>
+</html>
+
diff --git a/tools/droiddoc/templates/index.cs b/tools/droiddoc/templates/index.cs
new file mode 100644
index 0000000..15a6a59
--- /dev/null
+++ b/tools/droiddoc/templates/index.cs
@@ -0,0 +1,8 @@
+<html>
+<head>
+<meta http-equiv="refresh" content="0;url=packages.html">
+</head>
+<body>
+<?cs include:"analytics.cs" ?>
+</body>
+</html>
\ No newline at end of file
diff --git a/tools/droiddoc/templates/keywords.cs b/tools/droiddoc/templates/keywords.cs
new file mode 100644
index 0000000..0c8d4e3
--- /dev/null
+++ b/tools/droiddoc/templates/keywords.cs
@@ -0,0 +1,37 @@
+<?cs include:"macros.cs" ?>
+<html>
+<?cs include:"head_tag.cs" ?>
+<?cs include:"header.cs" ?>
+
+<div class="g-unit" id="doc-content">
+
+<div id="jd-header">
+<h1><?cs var:page.title ?></h1>
+</div>
+
+<div id="jd-content">
+
+<div class="jd-letterlist"><?cs each:letter=keywords ?>
+    <a href="#letter_<?cs name:letter ?>"><?cs name:letter ?></a><?cs /each?>
+</div>
+
+<?cs each:letter=keywords ?>
+<a name="letter_<?cs name:letter ?>"></a>
+<h2><?cs name:letter ?></h2>
+<ul class="jd-letterentries">
+<?cs each:entry=letter
+?>  <li><a href="<?cs var:toroot ?><?cs var:entry.href ?>"><?cs var:entry.label
+        ?></a>&nbsp;<font class="jd-letterentrycomments">(<?cs var:entry.comment ?>)</font></li>
+<?cs /each
+?></ul>
+
+<?cs /each ?>
+
+<?cs include:"footer.cs" ?>
+</div><!-- end jd-content -->
+</div><!-- end doc-content -->
+
+<?cs include:"trailer.cs" ?>
+
+</body>
+</html>
diff --git a/tools/droiddoc/templates/lists.cs b/tools/droiddoc/templates/lists.cs
new file mode 100644
index 0000000..0af32b2
--- /dev/null
+++ b/tools/droiddoc/templates/lists.cs
@@ -0,0 +1,5 @@
+var DATA = [
+<?cs each:page = docs.pages
+?>      { id:<?cs var: page.id ?>, label:"<?cs var:page.label ?>", link:"<?cs var:page.link ?>", type:"<?cs var:page.type ?>" }<?cs if:!last(page) ?>,<?cs /if ?>
+<?cs /each ?>
+    ];
diff --git a/tools/droiddoc/templates/macros.cs b/tools/droiddoc/templates/macros.cs
new file mode 100644
index 0000000..fe89045
--- /dev/null
+++ b/tools/droiddoc/templates/macros.cs
@@ -0,0 +1,333 @@
+<?cs # A link to a package ?><?cs 
+def:package_link(pkg)) ?>
+  <a href="<?cs var:toroot ?><?cs var:pkg.link ?>"><?cs var:pkg.name ?></a><?cs 
+/def ?>
+
+<?cs # A link to a type, or not if it's a primitive type
+        link: whether to create a link at the top level, always creates links in
+              recursive invocations.
+        Expects the following fields:
+            .name
+            .link
+            .isPrimitive
+            .superBounds.N.(more links)   (... super ... & ...)
+            .extendsBounds.N.(more links) (... extends ... & ...)
+            .typeArguments.N.(more links) (< ... >)
+?><?cs 
+def:type_link_impl(type, link) ?><?cs
+  if:type.link && link=="true" ?><a href="<?cs var:toroot ?><?cs var:type.link ?>"><?cs /if
+      ?><?cs var:type.label ?><?cs if:type.link && link=="true" ?></a><?cs /if ?><?cs
+  if:subcount(type.extendsBounds) ?><?cs
+      each:t=type.extendsBounds ?><?cs
+          if:first(t) ?>&nbsp;extends&nbsp;<?cs else ?>&nbsp;&amp;&nbsp;<?cs /if ?><?cs
+          call:type_link_impl(t, "true") ?><?cs
+      /each ?><?cs
+  /if ?><?cs
+  if:subcount(type.superBounds) ?><?cs
+      each:t=type.superBounds ?><?cs
+          if:first(t) ?>&nbsp;super&nbsp;<?cs else ?>&nbsp;&amp;&nbsp;<?cs /if ?><?cs
+          call:type_link_impl(t, "true") ?><?cs
+      /each ?><?cs
+  /if ?><?cs
+  if:subcount(type.typeArguments)
+      ?>&lt;<?cs each:t=type.typeArguments ?><?cs call:type_link_impl(t, "true") ?><?cs
+          if:!last(t) ?>,&nbsp;<?cs /if ?><?cs
+      /each ?>&gt;<?cs
+  /if ?><?cs
+/def ?>
+
+<?cs def:class_name(type) ?><?cs call:type_link_impl(type, "false") ?><?cs /def ?>
+<?cs def:type_link(type) ?><?cs call:type_link_impl(type, "true") ?><?cs /def ?>
+
+<?cs # A comma separated parameter list ?><?cs 
+def:parameter_list(params) ?><?cs
+  each:param = params ?><?cs
+      call:type_link(param.type)?> <?cs
+      var:param.name ?><?cs
+      if: name(param)!=subcount(params)-1?>, <?cs /if ?><?cs
+  /each ?><?cs
+/def ?>
+
+<?cs # Print a list of tags (e.g. description text ?><?cs 
+def:tag_list(tags) ?><?cs
+  each:tag = tags ?><?cs
+      if:tag.name == "Text" ?><?cs var:tag.text?><?cs
+      elif:tag.kind == "@more" ?><p><?cs
+      elif:tag.kind == "@see" ?><a href="<?cs var:toroot ?><?cs var:tag.href ?>"><?cs var:tag.label ?></a><?cs
+      elif:tag.kind == "@seeHref" ?><a href="<?cs var:tag.href ?>"><?cs var:tag.label ?></a><?cs
+      elif:tag.kind == "@seeJustLabel" ?><?cs var:tag.label ?><?cs
+      elif:tag.kind == "@code" ?><code class="Code prettyprint"><?cs var:tag.text ?></code><?cs
+      elif:tag.kind == "@samplecode" ?><pre class="Code prettyprint"><?cs var:tag.text ?></pre><?cs
+      elif:tag.name == "@sample" ?><pre class="Code prettyprint"><?cs var:tag.text ?></pre><?cs
+      elif:tag.name == "@include" ?><?cs var:tag.text ?><?cs
+      elif:tag.kind == "@docRoot" ?><?cs var:toroot ?><?cs
+      elif:tag.kind == "@inheritDoc" ?><?cs # This is the case when @inheritDoc is in something
+                                              that doesn't inherit from anything?><?cs
+      elif:tag.kind == "@attr" ?><?cs
+      else ?>{<?cs var:tag.name?> <?cs var:tag.text ?>}<?cs
+      /if ?><?cs
+  /each ?><?cs
+/def ?>
+
+<?cs # The message about This xxx is deprecated. ?><?cs 
+def:deprecated_text(kind) ?>
+  This <?cs var:kind ?> is deprecated.<?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 call:deprecated_text(obj.kind) ?>
+      <?cs call:tag_list(obj.deprecated) ?></em><?cs
+  else ?><?cs call:tag_list(obj.shortDescr) ?><?cs
+  /if ?><?cs
+/def ?>
+
+<?cs # Show the red box with the deprecated warning ?><?cs 
+def:deprecated_warning(obj) ?><?cs 
+  if:subcount(obj.deprecated) ?><p>
+  <p class="warning jd-deprecated-warning">
+      <strong><?cs call:deprecated_text(obj.kind) ?></strong><?cs 
+      call:tag_list(obj.deprecated) ?>
+  </p><?cs 
+  /if ?><?cs 
+/def ?>
+
+<?cs # print the See Also: section ?><?cs 
+def:see_also_tags(also) ?><?cs 
+  if:subcount(also) ?>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">See Also</h5>
+      <ul class="nolist"><?cs 
+        each:tag=also ?><li><?cs
+            if:tag.kind == "@see" ?><a href="<?cs var:toroot ?><?cs var:tag.href ?>"><?cs
+                    var:tag.label ?></a><?cs
+            elif:tag.kind == "@seeHref" ?><a href="<?cs var:tag.href ?>"><?cs var:tag.label ?></a><?cs
+            elif:tag.kind == "@seeJustLabel" ?><?cs var:tag.label ?><?cs
+            else ?>[ERROR: Unknown @see kind]<?cs
+            /if ?></li><?cs 
+        /each ?>
+      </ul>
+  </div><?cs 
+  /if ?>
+<?cs /def ?>
+
+
+<?cs # Print the long-form description for something.
+       Uses the following fields: deprecated descr seeAlso ?><?cs 
+def:description(obj) ?><?cs 
+  call:deprecated_warning(obj) ?>
+  <div class="jd-tagdata jd-tagdescr"><p><?cs call:tag_list(obj.descr) ?></p></div><?cs 
+  if:subcount(obj.attrRefs) ?>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Related XML Attributes</h5>
+      <ul class="nolist"><?cs 
+        each:attr=obj.attrRefs ?>
+            <li><a href="<?cs var:toroot ?><?cs var:attr.href ?>"><?cs var:attr.name ?></a></li><?cs 
+        /each ?>
+      </ul>
+  </div><?cs 
+  /if ?><?cs 
+  if:subcount(obj.paramTags) ?>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Parameters</h5>
+      <table class="jd-tagtable"><?cs 
+      each:tag=obj.paramTags ?>
+        <tr>
+          <th><?cs if:tag.isTypeParameter ?>&lt;<?cs /if ?><?cs var:tag.name
+                  ?><?cs if:tag.isTypeParameter ?>&gt;<?cs /if ?></td>
+          <td><?cs call:tag_list(tag.comment) ?></td>
+        </tr><?cs 
+      /each ?>
+      </table>
+  </div><?cs 
+  /if ?><?cs 
+  if:subcount(obj.returns) ?>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Returns</h5>
+      <ul class="nolist"><li><?cs call:tag_list(obj.returns) ?></li></ul>
+  </div><?cs 
+  /if ?><?cs 
+  if:subcount(obj.throws) ?>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Throws</h5>
+      <table class="jd-tagtable"><?cs 
+      each:tag=obj.throws ?>  
+        <tr>
+            <th><?cs call:type_link(tag.type) ?></td>
+            <td><?cs call:tag_list(tag.comment) ?></td>
+        </tr><?cs 
+      /each ?>
+      </table>
+  </div><?cs 
+  /if ?><?cs 
+  call:see_also_tags(obj.seeAlso) ?><?cs 
+/def ?>
+
+<?cs # A table of links to classes with descriptions, as in a package file or the nested classes ?><?cs
+def:class_link_table(classes) ?><?cs 
+  set:count = #1 ?>
+  <table class="jd-sumtable-expando"><?cs
+      each:cl=classes ?>
+        <tr <?cs if:count % #2 ?>class="alt-color"<?cs /if ?> >
+              <td class="jd-linkcol"><?cs call:type_link(cl.type) ?></td>
+              <td class="jd-descrcol" width="100%"><?cs call:short_descr(cl) ?>&nbsp;</td>
+          </tr><?cs set:count = count + #1 ?><?cs
+      /each ?>
+  </table><?cs 
+/def ?>
+
+<?cs # A list of links to classes, for use in the side navigation of packages ?><?cs 
+def:class_link_list(label, classes) ?><?cs 
+  if:subcount(classes) ?>
+    <li><h2><?cs var:label ?></h2>
+      <ul><?cs 
+      each:cl=classes ?>
+        <li><?cs call:type_link(cl.type) ?></li><?cs 
+      /each ?>
+      </ul>
+    </li><?cs 
+  /if ?><?cs 
+/def ?>
+
+<?cs # A list of links to classes, for use in the side navigation of classes ?><?cs 
+def:list(label, classes) ?><?cs 
+  if:subcount(classes) ?>
+    <li><h2><?cs var:label ?></h2>
+      <ul><?cs 
+      each:cl=classes ?>
+          <li <?cs if:class.name == cl.label?>class="selected"<?cs /if ?>><?cs call:type_link(cl) ?></li><?cs 
+      /each ?>
+      </ul>
+    </li><?cs 
+  /if ?><?cs 
+/def ?>
+
+<?cs # An expando trigger ?><?cs 
+def:expando_trigger(id, default) ?>
+  <a href="#" onclick="return toggleInherited(this, null)" id="<?cs var:id ?>" class="jd-expando-trigger closed"
+          ><img id="<?cs var:id ?>-trigger"
+          src="<?cs var:toroot ?>assets/images/triangle-<?cs var:default ?>.png"
+          class="jd-expando-trigger-img" /></a><?cs 
+/def ?>
+
+<?cs # An expandable list of classes ?><?cs 
+def:expandable_class_list(id, classes, default) ?>
+  <div id="<?cs var:id ?>">
+      <div id="<?cs var:id ?>-list"
+              class="jd-inheritedlinks"
+              <?cs if:default != "list" ?>style="display: none;"<?cs /if ?>
+              ><?cs 
+          each:cl=classes ?>
+              <?cs call:type_link(cl.type) ?><?cs if:!last(cl) ?>,<?cs /if ?><?cs 
+          /each ?>
+      </div>
+      <div id="<?cs var:id ?>-summary"
+              <?cs if:default != "summary" ?>style="display: none;"<?cs /if ?>
+              ><?cs 
+          call:class_link_table(classes) ?>
+      </div>
+  </div><?cs 
+/def ?>
+
+<?cs # The default side navigation for the reference docs ?><?cs 
+def:default_left_nav() ?>
+  <div class="g-section g-tpl-240" id="body-content">
+    <div class="g-unit g-first side-nav-resizable" id="side-nav">
+      <div id="swapper">
+        <div id="nav-panels">
+          <div id="resize-packages-nav">
+            <div id="packages-nav">
+              <div id="index-links"><nobr>
+                <a href="<?cs var:toroot ?>reference/packages.html" <?cs if:(page.title == "Package Index") ?>class="selected"<?cs /if ?> >Package Index</a> | 
+                <a href="<?cs var:toroot ?>reference/classes.html" <?cs if:(page.title == "Class Index") ?>class="selected"<?cs /if ?>>Class Index</a></nobr>
+              </div>
+              <ul><?cs 
+              each:pkg=docs.packages ?>
+                <li <?cs if:(class.package.name == pkg.name) || (package.name == pkg.name)?>class="selected"<?cs /if ?>><?cs call:package_link(pkg) ?></li><?cs 
+              /each ?>
+              </ul><br/>
+            </div> <!-- end packages -->
+          </div> <!-- end resize-packages -->
+          <div id="classes-nav"><?cs 
+            if:subcount(class.package) ?>
+            <ul>
+              <?cs call:list("Interfaces", class.package.interfaces) ?>
+              <?cs call:list("Classes", class.package.classes) ?>
+              <?cs call:list("Enums", class.package.enums) ?>
+              <?cs call:list("Exceptions", class.package.exceptions) ?>
+              <?cs call:list("Errors", class.package.errors) ?>
+            </ul><?cs 
+            elif:subcount(package) ?>
+            <ul>
+              <?cs call:class_link_list("Interfaces", package.interfaces) ?>
+              <?cs call:class_link_list("Classes", package.classes) ?>
+              <?cs call:class_link_list("Enums", package.enums) ?>
+              <?cs call:class_link_list("Exceptions", package.exceptions) ?>
+              <?cs call:class_link_list("Errors", package.errors) ?>
+            </ul><?cs 
+            else ?>
+              <script>
+                /*addLoadEvent(maxPackageHeight);*/
+              </script>
+              <p style="padding:10px">Select a package to view its members</p><?cs 
+            /if ?><br/>
+          </div><!-- end classes -->
+        </div><!-- end nav-panels -->
+        <div id="nav-tree" style="display:none">
+          <div id="index-links"><nobr>
+            <a href="<?cs var:toroot ?>reference/packages.html" <?cs if:(page.title == "Package Index") ?>class="selected"<?cs /if ?> >Package Index</a> | 
+            <a href="<?cs var:toroot ?>reference/classes.html" <?cs if:(page.title == "Class Index") ?>class="selected"<?cs /if ?>>Class Index</a></nobr>
+          </div>
+        </div><!-- end nav-tree -->
+      </div><!-- end swapper -->
+    </div> <!-- end side-nav -->
+    <script>
+      $("<a href='#' id='nav-swap' onclick='swapNav();return false;' style='font-size:10px;line-height:9px;margin-left:1em;text-decoration:none;'><span id='tree-link'>Use Tree Navigation</span><span id='panel-link' style='display:none'>Use Panel Navigation</span></a>").appendTo("#side-nav");
+      chooseDefaultNav();
+      if ($("#nav-tree").is(':visible')) init_navtree("nav-tree", "<?cs var:toroot ?>", NAVTREE_DATA);
+      else {
+        addLoadEvent(function() {
+          scrollIntoView("packages-nav");
+          scrollIntoView("classes-nav");
+        });
+      }
+      $("#swapper").css({borderBottom:"2px solid #aaa"});
+    </script><?cs 
+/def ?>
+
+<?cs # The default search box that goes in the header ?><?cs 
+def:default_search_box() ?>
+  <div id="search" >
+      <div id="searchForm">
+          <form accept-charset="utf-8" class="gsc-search-box" 
+                onsubmit="return submit_search()">
+            <table class="gsc-search-box" cellpadding="0" cellspacing="0"><tbody>
+                <tr>
+                  <td class="gsc-input">
+                    <input id="search_autocomplete" class="gsc-input" type="text" size="33" autocomplete="off" 
+                      title="search developer docs" name="q"
+                      value="search developer docs" 
+                      onFocus="search_focus_changed(this, true)" 
+                      onBlur="search_focus_changed(this, false)" 
+                      onkeydown="return search_changed(event, true, '<?cs var:toroot?>')" 
+                      onkeyup="return search_changed(event, false, '<?cs var:toroot?>')" />
+                  <div id="search_filtered_div" class="no-display">
+                      <table id="search_filtered" cellspacing=0>
+                      </table>
+                  </div>
+                  </td>
+                  <td class="gsc-search-button">
+                    <input type="submit" value="Search" title="search" id="search-button" class="gsc-search-button" />
+                  </td>
+                  <td class="gsc-clear-button">
+                    <div title="clear results" class="gsc-clear-button">&nbsp;</div>
+                  </td>
+                </tr></tbody>
+              </table>
+          </form>
+      </div><!-- searchForm -->
+  </div><!-- search --><?cs 
+/def ?>
+
+<?cs include:"customization.cs" ?>
diff --git a/tools/droiddoc/templates/navtree_data.cs b/tools/droiddoc/templates/navtree_data.cs
new file mode 100644
index 0000000..c707232
--- /dev/null
+++ b/tools/droiddoc/templates/navtree_data.cs
@@ -0,0 +1,4 @@
+var NAVTREE_DATA =
+<?cs var:reference_tree ?>
+;
+
diff --git a/tools/droiddoc/templates/nosidenavpage.cs b/tools/droiddoc/templates/nosidenavpage.cs
new file mode 100644
index 0000000..1dec41e
--- /dev/null
+++ b/tools/droiddoc/templates/nosidenavpage.cs
@@ -0,0 +1,23 @@
+<?cs include:"doctype.cs" ?>
+<?cs include:"macros.cs" ?>
+<html>
+<?cs include:"head_tag.cs" ?>
+<body class="gc-documentation">
+<a name="top"></a>
+<?cs call:custom_masthead() ?>
+
+<div id="body-content">
+<div id="doc-content" style="position:relative;">
+
+<?cs call:tag_list(root.descr) ?>
+
+<?cs include:"footer.cs" ?>
+</div><!-- end doc-content -->
+
+<?cs include:"trailer.cs" ?>
+
+</body>
+</html>
+
+
+
diff --git a/tools/droiddoc/templates/package-descr.cs b/tools/droiddoc/templates/package-descr.cs
new file mode 100644
index 0000000..385ce23
--- /dev/null
+++ b/tools/droiddoc/templates/package-descr.cs
@@ -0,0 +1,32 @@
+<?cs include:"doctype.cs" ?>
+<?cs include:"macros.cs" ?>
+<html>
+<?cs include:"head_tag.cs" ?>
+<?cs include:"header.cs" ?>
+
+<div class="g-unit" id="doc-content">
+
+<div id="jd-header">
+  <strong>
+    <div class="jd-page_title-prefix">package</div>
+  </strong>
+  <h1><?cs var:package.name ?></b></h1>
+  <div class="jd-nav">
+      <a class="jd-navlink" href="package-summary.html">Classes</a> |
+      Description
+  </div>
+</div><!-- end header -->
+
+<div id="jd-content">
+<div class="jd-descr">
+<p><?cs call:tag_list(package.descr) ?></p>
+</div>
+
+<?cs include:"footer.cs" ?>
+</div><!-- end jd-content -->
+</div> <!-- end doc-content -->
+
+<?cs include:"trailer.cs" ?>
+
+</body>
+</html>
diff --git a/tools/droiddoc/templates/package-list.cs b/tools/droiddoc/templates/package-list.cs
new file mode 100644
index 0000000..7f0f889
--- /dev/null
+++ b/tools/droiddoc/templates/package-list.cs
@@ -0,0 +1,2 @@
+<?cs each:pkg=docs.packages ?><?cs var: pkg.name ?>
+<?cs /each ?>
diff --git a/tools/droiddoc/templates/package.cs b/tools/droiddoc/templates/package.cs
new file mode 100644
index 0000000..7d1936d
--- /dev/null
+++ b/tools/droiddoc/templates/package.cs
@@ -0,0 +1,52 @@
+<?cs include:"doctype.cs" ?>
+<?cs include:"macros.cs" ?>
+<html>
+<?cs include:"head_tag.cs" ?>
+<?cs include:"header.cs" ?>
+
+<div class="g-unit" id="doc-content">
+
+<div id="jd-header">
+  package
+  <h1><?cs var:package.name ?></h1>
+
+  <div class="jd-nav">
+      <?cs if:subcount(package.shortDescr) ?>
+      Classes |
+      <a class="jd-navlink" href="package-descr.html">Description</a>
+      <?cs /if ?>
+  </div>
+</div>
+
+<div id="jd-content">
+
+<?cs if:subcount(package.shortDescr) ?>
+  <div class="jd-descr">
+  <p><?cs call:tag_list(package.shortDescr) ?>
+  <span class="jd-more"><a href="package-descr.html">more...</a></span></p>
+  </div>
+<?cs /if ?>
+
+<?cs def:class_table(label, classes) ?>
+  <?cs if:subcount(classes) ?>
+    <h3><?cs var:label ?></h3>
+    <div class="jd-sumtable">
+    <?cs call:class_link_table(classes) ?>
+    </div>
+  <?cs /if ?>
+<?cs /def ?>
+
+<?cs call:class_table("Interfaces", package.interfaces) ?>
+<?cs call:class_table("Classes", package.classes) ?>
+<?cs call:class_table("Enums", package.enums) ?>
+<?cs call:class_table("Exceptions", package.exceptions) ?>
+<?cs call:class_table("Errors", package.errors) ?>
+
+<?cs include:"footer.cs" ?>
+</div><!-- end jd-content -->
+</div><!-- doc-content -->
+
+<?cs include:"trailer.cs" ?>
+
+</body>
+</html>
diff --git a/tools/droiddoc/templates/packages.cs b/tools/droiddoc/templates/packages.cs
new file mode 100644
index 0000000..a358dca
--- /dev/null
+++ b/tools/droiddoc/templates/packages.cs
@@ -0,0 +1,38 @@
+<?cs include:"doctype.cs" ?>
+<?cs include:"macros.cs" ?>
+<html>
+<?cs include:"head_tag.cs" ?>
+<body class="gc-documentation">
+<?cs include:"header.cs" ?>
+
+<div class="g-unit" id="doc-content">
+
+<div id="jd-header">
+<h1><?cs var:page.title ?></h1>
+</div>
+
+<div id="jd-content">
+
+<div class="jd-descr">
+<p><?cs call:tag_list(root.descr) ?></p>
+</div>
+
+<?cs set:count = #1 ?>
+<table class="jd-sumtable">
+<?cs each:pkg = docs.packages ?>
+    <tr <?cs if:count % #2 ?>class="alt-color"<?cs /if ?> >
+        <td class="jd-linkcol"><?cs call:package_link(pkg) ?></td>
+        <td class="jd-descrcol" width="100%"><?cs call:tag_list(pkg.shortDescr) ?>&nbsp;</td>
+    </tr>
+<?cs set:count = count + #1 ?>
+<?cs /each ?>
+</table>
+
+<?cs include:"footer.cs" ?>
+</div><!-- end jd-content -->
+</div> <!-- end doc-content -->
+
+<?cs include:"trailer.cs" ?>
+
+</body>
+</html>
diff --git a/tools/droiddoc/templates/sample.cs b/tools/droiddoc/templates/sample.cs
new file mode 100644
index 0000000..7ab5dc9
--- /dev/null
+++ b/tools/droiddoc/templates/sample.cs
@@ -0,0 +1,34 @@
+<?cs include:"doctype.cs" ?>
+<?cs include:"macros.cs" ?>
+<?cs set:guide="true" ?>
+<html>
+<?cs include:"head_tag.cs" ?>
+<?cs include:"header.cs" ?>
+
+<div class="g-unit" id="doc-content"><a name="top"></a>
+
+<div id="jd-header" class="guide-header">
+
+  <span class="crumb">
+    <a href="<?cs var:toroot ?>guide/samples/index.html">Sample Code &gt;</a>
+    
+  </span>
+<h1><?cs var:page.title ?></h1>
+</div>
+
+<div id="jd-content">
+
+<p><a href="<?cs var:realFile ?>">Original <?cs var:realFile ?></a></p>
+
+<!-- begin file contents -->
+<pre class="Code prettyprint"><?cs var:fileContents ?></pre>
+<!-- end file contents -->
+
+<?cs include:"footer.cs" ?>
+</div><!-- end jd-content -->
+</div> <!-- end doc-content -->
+
+<?cs include:"trailer.cs" ?>
+
+</body>
+</html>
diff --git a/tools/droiddoc/templates/sampleindex.cs b/tools/droiddoc/templates/sampleindex.cs
new file mode 100644
index 0000000..6e57cfd
--- /dev/null
+++ b/tools/droiddoc/templates/sampleindex.cs
@@ -0,0 +1,48 @@
+<?cs include:"doctype.cs" ?>
+<?cs include:"macros.cs" ?>
+<?cs set:guide="true" ?>
+<html>
+<?cs include:"head_tag.cs" ?>
+<?cs include:"header.cs" ?>
+
+<div class="g-unit" id="doc-content"><a name="top"></a>
+
+<div id="jd-header" class="guide-header">
+
+  <span class="crumb">
+    <a href="<?cs var:toroot ?>guide/samples/index.html">Sample Code &gt;</a>
+    
+  </span>
+<h1><?cs var:page.title ?></h1>
+</div>
+
+<div id="jd-content">
+
+<?cs var:summary ?>
+
+<?cs if:subcount(subdirs) ?>
+    <h2>Subdirectories</h2>
+    <ul class="nolist">
+    <?cs each:dir=subdirs ?>
+      <li><a href="<?cs var:dir.name ?>/index.html"><?cs var:dir.name ?>/</a></li>
+    <?cs /each ?>
+    </ul>
+<?cs /if ?>
+
+<?cs if:subcount(files) ?>
+    <h2>Files</h2>
+    <ul class="nolist">
+    <?cs each:file=files ?>
+      <li><a href="<?cs var:file.href ?>"><?cs var:file.name ?></a></li>
+    <?cs /each ?>
+    </ul>
+<?cs /if ?>
+
+<?cs include:"footer.cs" ?>
+</div><!-- end jd-content -->
+</div><!-- end doc-content -->
+
+<?cs include:"trailer.cs" ?>
+
+</body>
+</html>
diff --git a/tools/droiddoc/templates/todo.cs b/tools/droiddoc/templates/todo.cs
new file mode 100644
index 0000000..e9f7237
--- /dev/null
+++ b/tools/droiddoc/templates/todo.cs
@@ -0,0 +1,99 @@
+<html>
+<head>
+    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+    <title><?cs var:page.title ?></title>
+    <style type="text/css">
+    table {
+        border-width: 1px 1px 1px 1px;
+        border-spacing: 0px;
+        border-style: solid solid solid solid;
+        border-color: black black black black;
+        border-collapse: collapse;
+        background-color: white;
+    }
+    table th {
+        border-width: 1px 1px 1px 1px;
+        padding: 1px 4px 1px 3px;
+        border-style: inset inset inset inset;
+        border-color: gray gray gray gray;
+        background-color: white;
+    }
+    table td {
+        border-width: 1px 1px 1px 1px;
+        padding: 1px 4px 1px 3px;
+        border-style: inset inset inset inset;
+        border-color: gray gray gray gray;
+        background-color: white;
+    }
+    </style>
+</head>
+<body>
+<h1><?cs var:page.title ?></h1>
+
+<h2>Overall</h2>
+<table>
+<tr><th>Errors</th><td><?cs var:all.errorCount ?></td></tr>
+<tr><th>Percent Good</th><td><?cs var:all.percentGood ?></td></tr>
+<tr><th>Total Comments</th><td><?cs var:all.totalCount ?></td></tr>
+</table>
+
+<h2>Package Summary</h2>
+
+<table>
+<tr>
+    <th>Package</th>
+    <th>Errors</th>
+    <th>Percent Good</th>
+    <th>Total</th>
+</tr>
+<?cs each:pkg=packages ?>
+<tr>
+    <td><?cs var:pkg.name ?></td>
+    <td><?cs var:pkg.errorCount ?></td>
+    <td><?cs var:pkg.percentGood ?></td>
+    <td><?cs var:pkg.totalCount ?></td>
+</tr>
+<?cs /each ?>
+</table>
+
+
+<h2>Class Summary</h3>
+
+<table>
+<tr>
+    <th>Class</th>
+    <th>Errors</th>
+    <th>Percent Good</th>
+    <th>Total</th>
+</tr>
+<?cs each:cl=classes ?>
+<tr>
+    <td><a href="#class_<?cs var:cl.qualified ?>"><?cs var:cl.qualified ?></a></td>
+    <td><?cs var:cl.errorCount ?></td>
+    <td><?cs var:cl.percentGood ?></td>
+    <td><?cs var:cl.totalCount ?></td>
+</tr>
+<?cs /each ?>
+</table>
+
+<h2>Detail</h2>
+
+<?cs each:cl=classes ?>
+<h3><a name="class_<?cs var:cl.qualified ?>"><?cs var:cl.qualified ?></a></h3>
+<p>Errors: <?cs var:cl.errorCount ?><br/>
+Total: <?cs var:cl.totalCount ?><br/>
+Percent Good: <?cs var:cl.percentGood ?></p>
+<table>
+<?cs each:err=cl.errors ?>
+<tr>
+    <td><?cs var:err.pos ?></td>
+    <td><?cs var:err.name ?></td>
+    <td><?cs var:err.descr ?></td>
+</tr>
+<?cs /each ?>
+</table>
+
+<?cs /each ?>
+
+</body>
+</html>
diff --git a/tools/droiddoc/templates/trailer.cs b/tools/droiddoc/templates/trailer.cs
new file mode 100644
index 0000000..155ba58
--- /dev/null
+++ b/tools/droiddoc/templates/trailer.cs
@@ -0,0 +1,11 @@
+</div> <!-- end body-content --> <?cs # normally opened by header.cs ?>
+
+<script type="text/javascript">
+init(); /* initialize android-developer-docs.js */
+var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
+document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
+</script>
+<script type="text/javascript">
+var pageTracker = _gat._getTracker("UA-5831155-1");
+pageTracker._trackPageview();
+</script>
\ No newline at end of file
diff --git a/tools/droiddoc/test/generics/Android.mk b/tools/droiddoc/test/generics/Android.mk
new file mode 100644
index 0000000..0c808fd
--- /dev/null
+++ b/tools/droiddoc/test/generics/Android.mk
@@ -0,0 +1,28 @@
+# 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.
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:=$(call all-subdir-java-files)
+
+LOCAL_MODULE:=test_generics
+LOCAL_DROIDDOC_OPTIONS:=\
+        -stubs __test_generics__
+
+LOCAL_DROIDDOC_CUSTOM_TEMPLATE_DIR:=tools/droiddoc/templates-google
+LOCAL_DROIDDOC_CUSTOM_ASSET_DIR:=assets-google
+LOCAL_MODULE_CLASS := JAVA_LIBRARIES
+
+include $(BUILD_DROIDDOC)
diff --git a/tools/droiddoc/test/generics/src/com/android/generics/AbsListView.java b/tools/droiddoc/test/generics/src/com/android/generics/AbsListView.java
new file mode 100644
index 0000000..6bef812
--- /dev/null
+++ b/tools/droiddoc/test/generics/src/com/android/generics/AbsListView.java
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+
+package com.android.generics;
+
+public class AbsListView implements AdapterView<ListAdapter> {
+}
diff --git a/tools/droiddoc/test/generics/src/com/android/generics/Adapter.java b/tools/droiddoc/test/generics/src/com/android/generics/Adapter.java
new file mode 100644
index 0000000..c041dd1
--- /dev/null
+++ b/tools/droiddoc/test/generics/src/com/android/generics/Adapter.java
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+
+package com.android.generics;
+
+public class Adapter {
+}
diff --git a/tools/droiddoc/test/generics/src/com/android/generics/AdapterView.java b/tools/droiddoc/test/generics/src/com/android/generics/AdapterView.java
new file mode 100644
index 0000000..de4f8f1
--- /dev/null
+++ b/tools/droiddoc/test/generics/src/com/android/generics/AdapterView.java
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+
+package com.android.generics;
+
+public interface AdapterView<T extends Adapter> {
+}
+
diff --git a/tools/droiddoc/test/generics/src/com/android/generics/Bar.java b/tools/droiddoc/test/generics/src/com/android/generics/Bar.java
new file mode 100644
index 0000000..bd9fcbc
--- /dev/null
+++ b/tools/droiddoc/test/generics/src/com/android/generics/Bar.java
@@ -0,0 +1,22 @@
+/*
+ * 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.
+ */
+
+package com.android.generics;
+
+public interface Bar<K> {
+    public K bar(K arg);
+}
+
diff --git a/tools/droiddoc/test/generics/src/com/android/generics/Foo.java b/tools/droiddoc/test/generics/src/com/android/generics/Foo.java
new file mode 100644
index 0000000..d5d07eb
--- /dev/null
+++ b/tools/droiddoc/test/generics/src/com/android/generics/Foo.java
@@ -0,0 +1,28 @@
+/*
+ * 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.
+ */
+
+package com.android.generics;
+
+public class Foo<V> {
+    public Foo(V v) {
+    }
+
+    public V foo(V arg) {
+        return null;
+    }
+}
+
+
diff --git a/tools/droiddoc/test/generics/src/com/android/generics/FooBar.java b/tools/droiddoc/test/generics/src/com/android/generics/FooBar.java
new file mode 100644
index 0000000..7ea3567
--- /dev/null
+++ b/tools/droiddoc/test/generics/src/com/android/generics/FooBar.java
@@ -0,0 +1,51 @@
+/*
+ * 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.
+ */
+
+package com.android.generics;
+
+public class FooBar<K,V,L> extends Foo<V> implements Bar<K> {
+    public class C
+    {
+    }
+
+    public class CI extends C implements Iface
+    {
+    }
+
+    public FooBar(K k) {
+        super(null);
+        throw new RuntimeException("!");
+    }
+
+    public K bar(K arg) {
+        return null;
+    }
+    
+    public FooBar<K,? extends Foo,L> a(K arg) {
+        return null;
+    }
+
+    public FooBar<V,K,L> b(Bar<? extends K> arg) {
+        return null;
+    }
+
+    public <L extends C & Iface> void f(L arg) {
+    }
+
+    public V v;
+}
+
+
diff --git a/tools/droiddoc/test/generics/src/com/android/generics/Iface.java b/tools/droiddoc/test/generics/src/com/android/generics/Iface.java
new file mode 100644
index 0000000..03aa2dd
--- /dev/null
+++ b/tools/droiddoc/test/generics/src/com/android/generics/Iface.java
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+
+package com.android.generics;
+
+public interface Iface {
+}
diff --git a/tools/droiddoc/test/generics/src/com/android/generics/ListAdapter.java b/tools/droiddoc/test/generics/src/com/android/generics/ListAdapter.java
new file mode 100644
index 0000000..5f88a56
--- /dev/null
+++ b/tools/droiddoc/test/generics/src/com/android/generics/ListAdapter.java
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+
+package com.android.generics;
+
+public class ListAdapter extends Adapter {
+}
diff --git a/tools/droiddoc/test/generics/src/com/android/generics/TestComparable.java b/tools/droiddoc/test/generics/src/com/android/generics/TestComparable.java
new file mode 100644
index 0000000..9d394ae
--- /dev/null
+++ b/tools/droiddoc/test/generics/src/com/android/generics/TestComparable.java
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+
+package com.android.generics;
+
+public interface TestComparable<T> {
+}
diff --git a/tools/droiddoc/test/generics/src/com/android/generics/TestEnum.java b/tools/droiddoc/test/generics/src/com/android/generics/TestEnum.java
new file mode 100644
index 0000000..efb1d18
--- /dev/null
+++ b/tools/droiddoc/test/generics/src/com/android/generics/TestEnum.java
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+
+package com.android.generics;
+
+public class TestEnum<E extends TestEnum<E>> implements TestComparable<E> {
+}
+
diff --git a/tools/droiddoc/test/stubs/Android.mk b/tools/droiddoc/test/stubs/Android.mk
new file mode 100644
index 0000000..fc971e1
--- /dev/null
+++ b/tools/droiddoc/test/stubs/Android.mk
@@ -0,0 +1,29 @@
+# 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.
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:=$(call all-java-files-under,src)
+
+LOCAL_MODULE:=test_stubs
+LOCAL_DROIDDOC_OPTIONS:=\
+        -stubs $(OUT_DIR)/__test_stubs__
+
+LOCAL_DROIDDOC_CUSTOM_TEMPLATE_DIR:=tools/droiddoc/templates-google
+LOCAL_DROIDDOC_CUSTOM_ASSET_DIR:=assets-google
+LOCAL_MODULE_CLASS := JAVA_LIBRARIES
+
+include $(BUILD_DROIDDOC)
+
diff --git a/tools/droiddoc/test/stubs/expected/com/android/stubs/Annot.java b/tools/droiddoc/test/stubs/expected/com/android/stubs/Annot.java
new file mode 100644
index 0000000..b6144b6
--- /dev/null
+++ b/tools/droiddoc/test/stubs/expected/com/android/stubs/Annot.java
@@ -0,0 +1,8 @@
+package com.android.stubs;
+@java.lang.annotation.Documented()
+@java.lang.annotation.Retention(value=java.lang.annotation.RetentionPolicy.RUNTIME)
+@java.lang.annotation.Target(value={java.lang.annotation.ElementType.TYPE})
+public @interface Annot
+{
+java.lang.String value() default "yo\u1234";
+}
diff --git a/tools/droiddoc/test/stubs/expected/com/android/stubs/InterfaceEnum.java b/tools/droiddoc/test/stubs/expected/com/android/stubs/InterfaceEnum.java
new file mode 100644
index 0000000..b0c87e7
--- /dev/null
+++ b/tools/droiddoc/test/stubs/expected/com/android/stubs/InterfaceEnum.java
@@ -0,0 +1,9 @@
+package com.android.stubs;
+public enum InterfaceEnum
+  implements com.android.stubs.Parent.Interface
+{
+VAL();
+public  void method() { throw new RuntimeException("Stub!"); }
+public static final java.lang.Object OBJECT;
+static { OBJECT = null; }
+}
diff --git a/tools/droiddoc/test/stubs/expected/com/android/stubs/Parent.java b/tools/droiddoc/test/stubs/expected/com/android/stubs/Parent.java
new file mode 100644
index 0000000..132d566
--- /dev/null
+++ b/tools/droiddoc/test/stubs/expected/com/android/stubs/Parent.java
@@ -0,0 +1,27 @@
+package com.android.stubs;
+@com.android.stubs.Annot(value="asdf")
+public class Parent
+{
+public static interface Interface
+{
+public  void method();
+}
+public  Parent() { throw new RuntimeException("Stub!"); }
+public  java.lang.String methodString() { throw new RuntimeException("Stub!"); }
+public  int method(boolean b, char c, int i, long l, float f, double d) { throw new RuntimeException("Stub!"); }
+protected  void protectedMethod() { throw new RuntimeException("Stub!"); }
+public static final byte public_static_final_byte = 42;
+public static final short public_static_final_short = 43;
+public static final int public_static_final_int = 44;
+public static final long public_static_final_long = 45L;
+public static final char public_static_final_char = 4660;
+public static final float public_static_final_float = 42.1f;
+public static final double public_static_final_double = 42.2;
+public static int public_static_int;
+public static final java.lang.String public_static_final_String = "ps\u1234fS";
+public static java.lang.String public_static_String;
+public static com.android.stubs.Parent public_static_Parent;
+public static final com.android.stubs.Parent public_static_final_Parent;
+public static final com.android.stubs.Parent public_static_final_Parent_null;
+static { public_static_final_Parent = null; public_static_final_Parent_null = null; }
+}
diff --git a/tools/droiddoc/test/stubs/expected/com/android/stubs/SomeEnum.java b/tools/droiddoc/test/stubs/expected/com/android/stubs/SomeEnum.java
new file mode 100644
index 0000000..ecfd9d4
--- /dev/null
+++ b/tools/droiddoc/test/stubs/expected/com/android/stubs/SomeEnum.java
@@ -0,0 +1,7 @@
+package com.android.stubs;
+public enum SomeEnum
+{
+A(),
+B(),
+C();
+}
diff --git a/tools/droiddoc/test/stubs/expected/com/android/stubs/Types.java b/tools/droiddoc/test/stubs/expected/com/android/stubs/Types.java
new file mode 100644
index 0000000..3f5a791
--- /dev/null
+++ b/tools/droiddoc/test/stubs/expected/com/android/stubs/Types.java
@@ -0,0 +1,33 @@
+package com.android.stubs;
+public class Types
+{
+public static interface Interface
+{
+public static final boolean public_static_final_boolean = false;
+public static final char public_static_final_char = 0;
+public static final short public_static_final_short = 0;
+public static final int public_static_final_int = 0;
+public static final long public_static_final_long = 0L;
+public static final float public_static_final_float = 0.0f;
+public static final double public_static_final_double = 0.0;
+public static final java.lang.Object public_static_final_Object = null;
+}
+protected  Types() { throw new RuntimeException("Stub!"); }
+public final boolean public_final_boolean;
+public final char public_final_char;
+public final short public_final_short;
+public final int public_final_int;
+public final long public_final_long;
+public final float public_final_float;
+public final double public_final_double;
+public final java.lang.Object public_final_Object;
+public static final boolean public_static_final_boolean;
+public static final char public_static_final_char;
+public static final short public_static_final_short;
+public static final int public_static_final_int;
+public static final long public_static_final_long;
+public static final float public_static_final_float;
+public static final double public_static_final_double;
+public static final java.lang.Object public_static_final_Object;
+static { public_static_final_boolean = false; public_static_final_char = 0; public_static_final_short = 0; public_static_final_int = 0; public_static_final_long = 0; public_static_final_float = 0; public_static_final_double = 0; public_static_final_Object = null; }
+}
diff --git a/tools/droiddoc/test/stubs/expected/com/android/stubs/a/A.java b/tools/droiddoc/test/stubs/expected/com/android/stubs/a/A.java
new file mode 100644
index 0000000..f3cb888
--- /dev/null
+++ b/tools/droiddoc/test/stubs/expected/com/android/stubs/a/A.java
@@ -0,0 +1,14 @@
+package com.android.stubs.a;
+public abstract class A
+  extends com.android.stubs.Parent
+  implements com.android.stubs.Parent.Interface, com.android.stubs.a.SomeInterface
+{
+public class Inner
+{
+public  Inner() { throw new RuntimeException("Stub!"); }
+}
+protected  A(int a) { throw new RuntimeException("Stub!"); }
+public  com.android.stubs.a.A varargs(com.android.stubs.Parent[]... args) { throw new RuntimeException("Stub!"); }
+public  void method() { throw new RuntimeException("Stub!"); }
+public abstract  java.lang.String[] stringArrayMethod() throws java.io.IOException;
+}
diff --git a/tools/droiddoc/test/stubs/expected/com/android/stubs/a/SomeInterface.java b/tools/droiddoc/test/stubs/expected/com/android/stubs/a/SomeInterface.java
new file mode 100644
index 0000000..c24981b
--- /dev/null
+++ b/tools/droiddoc/test/stubs/expected/com/android/stubs/a/SomeInterface.java
@@ -0,0 +1,4 @@
+package com.android.stubs.a;
+public interface SomeInterface
+{
+}
diff --git a/tools/droiddoc/test/stubs/expected/com/android/stubs/b/B.java b/tools/droiddoc/test/stubs/expected/com/android/stubs/b/B.java
new file mode 100644
index 0000000..5db2fce
--- /dev/null
+++ b/tools/droiddoc/test/stubs/expected/com/android/stubs/b/B.java
@@ -0,0 +1,5 @@
+package com.android.stubs.b;
+public class B
+{
+public  B() { throw new RuntimeException("Stub!"); }
+}
diff --git a/tools/droiddoc/test/stubs/func.sh b/tools/droiddoc/test/stubs/func.sh
new file mode 100644
index 0000000..1ad4bd5
--- /dev/null
+++ b/tools/droiddoc/test/stubs/func.sh
@@ -0,0 +1,68 @@
+#!/bin/sh
+#
+# 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.
+
+export A_STUBS=out/stubs/a/stubs
+export B_STUBS=out/stubs/b/stubs
+export EXPECTED_STUBS=out/stubs/expected/stubs
+export EXPECTED=$DIR/expected
+
+function build_stubs()
+{
+    ID=$1
+    SRC_DIR=$2
+    STUBS_DIR=$3
+
+    OBJ_DIR=out/stubs/$ID
+
+    rm -rf $OBJ_DIR &> /dev/null
+    mkdir -p $OBJ_DIR
+
+    find $SRC_DIR -name '*.java' > $OBJ_DIR/javadoc-src-list
+    ( \
+        LD_LIBRARY_PATH=out/host/darwin-x86/lib \
+        javadoc \
+            \@$OBJ_DIR/javadoc-src-list \
+            -J-Xmx512m \
+            -J-Djava.library.path=out/host/darwin-x86/lib \
+             \
+            -quiet \
+            -doclet DroidDoc \
+            -docletpath out/host/darwin-x86/framework/clearsilver.jar:out/host/darwin-x86/framework/droiddoc.jar \
+            -templatedir tools/droiddoc/templates \
+            -classpath out/target/common/obj/JAVA_LIBRARIES/core_intermediates/classes.jar:out/target/common/obj/JAVA_LIBRARIES/ext_intermediates/classes.jar:out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/classes.jar \
+            -sourcepath $SRC_DIR:out/target/common/obj/JAVA_LIBRARIES/core_intermediates/classes.jar:out/target/common/obj/JAVA_LIBRARIES/ext_intermediates/classes.jar:out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/classes.jar \
+            -d $OBJ_DIR/docs \
+            -hdf page.build MAIN-eng.joeo.20080710.121320 -hdf page.now "10 Jul 2008 12:13" \
+            -stubs $STUBS_DIR \
+            -stubpackages com.android.stubs:com.android.stubs.a:com.android.stubs.b:com.android.stubs.hidden \
+        && rm -rf $OBJ_DIR/docs/assets \
+        && mkdir -p $OBJ_DIR/docs/assets \
+        && cp -fr tools/droiddoc/templates/assets/* $OBJ_DIR/docs/assets/ \
+    )# || (rm -rf $OBJ_DIR; exit 45)
+}
+
+function compile_stubs()
+{
+    ID=$1
+    STUBS_DIR=$2
+
+    OBJ_DIR=out/stubs/$ID
+    CLASS_DIR=$OBJ_DIR/class
+    mkdir -p $CLASS_DIR
+
+    find $STUBS_DIR -name "*.java" > $OBJ_DIR/java-src-list
+    javac @$OBJ_DIR/java-src-list -d $CLASS_DIR
+}
diff --git a/tools/droiddoc/test/stubs/run.sh b/tools/droiddoc/test/stubs/run.sh
new file mode 100755
index 0000000..f237a7d
--- /dev/null
+++ b/tools/droiddoc/test/stubs/run.sh
@@ -0,0 +1,39 @@
+#!/bin/sh
+#
+# 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.
+
+DIR=tools/droiddoc/test/stubs
+
+pushd $TOP
+
+. $TOP/$DIR/func.sh
+
+mkdir -p out/stubs_compiled
+find $DIR/src -name "*.java" | xargs javac -d out/stubs_compiled
+
+build_stubs a $DIR/src $A_STUBS
+build_stubs b $A_STUBS $B_STUBS
+
+compile_stubs a $A_STUBS
+
+echo EXPECTED
+diff -r $DIR/expected $A_STUBS
+echo TWICE STUBBED
+diff -r $A_STUBS $B_STUBS
+
+popd &> /dev/null
+
+
+
diff --git a/tools/droiddoc/test/stubs/src/com/android/stubs/Annot.java b/tools/droiddoc/test/stubs/src/com/android/stubs/Annot.java
new file mode 100644
index 0000000..fe9226f
--- /dev/null
+++ b/tools/droiddoc/test/stubs/src/com/android/stubs/Annot.java
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+
+package com.android.stubs;
+
+import java.lang.annotation.*;
+
+/**
+ * poop
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE)
+public @interface Annot {
+    String value() default "yo\u1234";
+}
+
diff --git a/tools/droiddoc/test/stubs/src/com/android/stubs/InterfaceEnum.java b/tools/droiddoc/test/stubs/src/com/android/stubs/InterfaceEnum.java
new file mode 100644
index 0000000..1e64f57
--- /dev/null
+++ b/tools/droiddoc/test/stubs/src/com/android/stubs/InterfaceEnum.java
@@ -0,0 +1,23 @@
+/*
+ * 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.
+ */
+
+package com.android.stubs;
+
+public enum InterfaceEnum implements Parent.Interface {
+    VAL;
+    public static final Object OBJECT = new Object();
+    public void method() { }
+}
diff --git a/tools/droiddoc/test/stubs/src/com/android/stubs/Parent.java b/tools/droiddoc/test/stubs/src/com/android/stubs/Parent.java
new file mode 100644
index 0000000..577db38
--- /dev/null
+++ b/tools/droiddoc/test/stubs/src/com/android/stubs/Parent.java
@@ -0,0 +1,60 @@
+/*
+ * 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.
+ */
+
+package com.android.stubs;
+
+@Annot("asdf")
+public class Parent {
+    public static final byte public_static_final_byte = 42;
+    public static final short public_static_final_short = 43;
+    public static final int public_static_final_int = 44;
+    public static final long public_static_final_long = 45;
+    public static final char public_static_final_char = '\u1234';
+    public static final float public_static_final_float = 42.1f;
+    public static final double public_static_final_double = 42.2;
+    public static int public_static_int = 1;
+    public static final String public_static_final_String = "ps\u1234fS";
+    public static String public_static_String = "psS";
+    public static Parent public_static_Parent = new Parent();
+    public static final Parent public_static_final_Parent = new Parent();
+    public static final Parent public_static_final_Parent_null = null;
+
+    public interface Interface {
+        void method();
+    }
+
+    public Parent() {
+    }
+
+    public String methodString() {
+        return "yo";
+    }
+
+    public int method(boolean b, char c, int i, long l, float f, double d) {
+        return 1;
+    }
+
+    protected void protectedMethod() {
+    }
+
+    void packagePrivateMethod() {
+    }
+
+    /** @hide */
+    public void hiddenMethod() {
+    }
+}
+
diff --git a/tools/droiddoc/test/stubs/src/com/android/stubs/SomeEnum.java b/tools/droiddoc/test/stubs/src/com/android/stubs/SomeEnum.java
new file mode 100644
index 0000000..51c5000
--- /dev/null
+++ b/tools/droiddoc/test/stubs/src/com/android/stubs/SomeEnum.java
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+
+package com.android.stubs;
+
+public enum SomeEnum {
+    A, B, C
+}
diff --git a/tools/droiddoc/test/stubs/src/com/android/stubs/Types.java b/tools/droiddoc/test/stubs/src/com/android/stubs/Types.java
new file mode 100644
index 0000000..5e24a10
--- /dev/null
+++ b/tools/droiddoc/test/stubs/src/com/android/stubs/Types.java
@@ -0,0 +1,72 @@
+/*
+ * 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.
+ */
+
+package com.android.stubs;
+
+public class Types {
+    public final boolean public_final_boolean;
+    public final char public_final_char;
+    public final short public_final_short;
+    public final int public_final_int;
+    public final long public_final_long;
+    public final float public_final_float;
+    public final double public_final_double;
+    public final Object public_final_Object;
+
+    public static final boolean public_static_final_boolean;
+    public static final char public_static_final_char;
+    public static final short public_static_final_short;
+    public static final int public_static_final_int;
+    public static final long public_static_final_long;
+    public static final float public_static_final_float;
+    public static final double public_static_final_double;
+    public static final Object public_static_final_Object;
+
+    /** @hide */
+    public Types() {
+        public_final_boolean = false;
+        public_final_char = 0;
+        public_final_short = 0;
+        public_final_int = 0;
+        public_final_long = 0;
+        public_final_float = 0;
+        public_final_double = 0;
+        public_final_Object = null;
+    }
+
+    static {
+        public_static_final_boolean = false;
+        public_static_final_char = 0;
+        public_static_final_short = 0;
+        public_static_final_int = 0;
+        public_static_final_long = 0;
+        public_static_final_float = 0;
+        public_static_final_double = 0;
+        public_static_final_Object = null;
+    }
+
+    public interface Interface {
+        public static final boolean public_static_final_boolean = false;
+        public static final char public_static_final_char = 0;
+        public static final short public_static_final_short = 0;
+        public static final int public_static_final_int = 0;
+        public static final long public_static_final_long = 0;
+        public static final float public_static_final_float = 0;
+        public static final double public_static_final_double = 0;
+        public static final Object public_static_final_Object = null;
+    }
+}
+
diff --git a/tools/droiddoc/test/stubs/src/com/android/stubs/a/A.java b/tools/droiddoc/test/stubs/src/com/android/stubs/a/A.java
new file mode 100644
index 0000000..cebeaf1
--- /dev/null
+++ b/tools/droiddoc/test/stubs/src/com/android/stubs/a/A.java
@@ -0,0 +1,41 @@
+/*
+ * 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.
+ */
+
+package com.android.stubs.a;
+
+import com.android.stubs.Parent;
+
+public abstract class A extends Parent implements Parent.Interface, SomeInterface {
+    protected A(int a) {
+        super();
+    }
+
+    public A varargs(Parent... args) {
+        return null;
+    }
+
+    public void method() {
+    }
+    public abstract String[] stringArrayMethod() throws java.io.IOException;
+
+    public class Inner {
+        int method() {
+            return 1;
+        }
+        int field;
+    }
+}
+
diff --git a/tools/droiddoc/test/stubs/src/com/android/stubs/a/SomeInterface.java b/tools/droiddoc/test/stubs/src/com/android/stubs/a/SomeInterface.java
new file mode 100644
index 0000000..6f5c3e0
--- /dev/null
+++ b/tools/droiddoc/test/stubs/src/com/android/stubs/a/SomeInterface.java
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+
+package com.android.stubs.a;
+
+public interface SomeInterface {
+}
+
diff --git a/tools/droiddoc/test/stubs/src/com/android/stubs/b/B.java b/tools/droiddoc/test/stubs/src/com/android/stubs/b/B.java
new file mode 100644
index 0000000..7febe33
--- /dev/null
+++ b/tools/droiddoc/test/stubs/src/com/android/stubs/b/B.java
@@ -0,0 +1,26 @@
+/*
+ * 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.
+ */
+
+package com.android.stubs.b;
+
+import com.android.stubs.Parent;
+import com.android.stubs.a.A;
+
+public class B {
+    Parent method(Parent p) {
+        return null;
+    }
+}
diff --git a/tools/droiddoc/test/stubs/src/com/android/stubs/hidden/Hidden.java b/tools/droiddoc/test/stubs/src/com/android/stubs/hidden/Hidden.java
new file mode 100644
index 0000000..39ece6e
--- /dev/null
+++ b/tools/droiddoc/test/stubs/src/com/android/stubs/hidden/Hidden.java
@@ -0,0 +1,23 @@
+/*
+ * 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.
+ */
+
+package com.android.stubs.c;
+
+/** @hide */
+public class Hidden {
+
+}
+
diff --git a/tools/droiddoc/test/stubs/src/com/android/stubs/hidden/HiddenOuter.java b/tools/droiddoc/test/stubs/src/com/android/stubs/hidden/HiddenOuter.java
new file mode 100644
index 0000000..0380f43
--- /dev/null
+++ b/tools/droiddoc/test/stubs/src/com/android/stubs/hidden/HiddenOuter.java
@@ -0,0 +1,25 @@
+/*
+ * 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.
+ */
+
+package com.android.stubs.c;
+
+/** @hide */
+public class HiddenOuter {
+
+    public class NotHiddenInner {
+    }
+}
+
diff --git a/tools/droiddoc/test/stubs/src/com/android/stubs/hidden/PackagePrivate.java b/tools/droiddoc/test/stubs/src/com/android/stubs/hidden/PackagePrivate.java
new file mode 100644
index 0000000..165735c
--- /dev/null
+++ b/tools/droiddoc/test/stubs/src/com/android/stubs/hidden/PackagePrivate.java
@@ -0,0 +1,22 @@
+/*
+ * 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.
+ */
+
+package com.android.stubs.c;
+
+class PackagePrivate {
+
+}
+