Doclava: add support for a second html dir and associated output. This is primarily for devsite online docs builds. Also move ca151a9bbea066b0efd0e423bcade8895a83db71 and related changes into production docs branch.

Change-Id: Idc95c4e396713eb592b8d1ffd60d93ee89472e97
diff --git a/src/com/google/doclava/ClassInfo.java b/src/com/google/doclava/ClassInfo.java
index f4f2853..927b240 100644
--- a/src/com/google/doclava/ClassInfo.java
+++ b/src/com/google/doclava/ClassInfo.java
@@ -44,13 +44,12 @@
       return a.qualifiedName().compareTo(b.qualifiedName());
     }
   };
-  
+
   /**
    * Constructs a stub representation of a class.
    */
   public ClassInfo(String qualifiedName) {
     super("", SourcePositionInfo.UNKNOWN);
-    
     mQualifiedName = qualifiedName;
     if (qualifiedName.lastIndexOf('.') != -1) {
       mName = qualifiedName.substring(qualifiedName.lastIndexOf('.') + 1);
@@ -1195,6 +1194,13 @@
       if (pkg != null && pkg.isHidden()) {
         return true;
       }
+      if (cl.annotations() != null) {
+        for (AnnotationInstanceInfo info : cl.annotations()) {
+          if (Doclava.showAnnotations.contains(info.type().qualifiedName())) {
+            return false;
+          }
+        }
+      }
       if (cl.comment().isHidden()) {
         return true;
       }
@@ -1244,7 +1250,7 @@
 
     return null;
   }
-  
+
   public boolean supportsMethod(MethodInfo method) {
     for (MethodInfo m : methods()) {
       if (m.getHashableName().equals(method.getHashableName())) {
@@ -1373,7 +1379,7 @@
     }
     return null;
   }
-  
+
   public String scope() {
     if (isPublic()) {
       return "public";
@@ -1467,7 +1473,7 @@
   private String mReasonIncluded;
   private ArrayList<MethodInfo> mNonWrittenConstructors;
   private boolean mIsDeprecated;
-  
+
   // TODO: Temporary members from apicheck migration.
   private HashMap<String, MethodInfo> mApiCheckConstructors = new HashMap<String, MethodInfo>();
   private HashMap<String, MethodInfo> mApiCheckMethods = new HashMap<String, MethodInfo>();
@@ -1476,7 +1482,7 @@
 
   // Resolutions
   private ArrayList<Resolution> mResolutions;
-  
+
   /**
    * Returns true if {@code cl} implements the interface {@code iface} either by either being that
    * interface, implementing that interface or extending a type that implements the interface.
@@ -1538,7 +1544,6 @@
    * Returns all methods defined directly in this class. For a list of all
    * methods supported by this class, see {@link #methods()}.
    */
-  
   public Map<String, MethodInfo> allMethods() {
     return mApiCheckMethods;
   }
@@ -1553,7 +1558,7 @@
     }
     return result;
   }
-  
+
   public String superclassName() {
     if (mSuperclass == null) {
       if (mQualifiedName.equals("java.lang.Object")) {
@@ -1563,11 +1568,11 @@
     }
     return mSuperclass.mQualifiedName;
   }
-  
+
   public void setAnnotations(ArrayList<AnnotationInstanceInfo> annotations) {
     mAnnotations = annotations;
   }
-  
+
   public boolean isConsistent(ClassInfo cl) {
     boolean consistent = true;
 
@@ -1727,7 +1732,7 @@
 
     return consistent;
   }
-  
+
   // Find a superclass implementation of the given method.
   public static MethodInfo overriddenMethod(MethodInfo candidate, ClassInfo newClassObj) {
     if (newClassObj == null) {
@@ -1758,7 +1763,7 @@
     }
     return ClassInfo.interfaceMethod(candidate, newClassObj.mSuperclass);
   }
-  
+
   public boolean hasConstructor(MethodInfo constructor) {
     String name = constructor.getHashableName();
     for (MethodInfo ctor : mApiCheckConstructors.values()) {
diff --git a/src/com/google/doclava/ClearPage.java b/src/com/google/doclava/ClearPage.java
index 3229c84..0df647d 100644
--- a/src/com/google/doclava/ClearPage.java
+++ b/src/com/google/doclava/ClearPage.java
@@ -132,7 +132,6 @@
     File file = new File(outputFilename(filename));
 
     ensureDirectory(file);
-
     Writer stream = null;
     try {
       stream = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), "UTF-8"));
@@ -177,6 +176,11 @@
       System.err.println(from.getAbsolutePath() + ": Error opening file");
       return;
     }
+    if ((Doclava.devsite) && (!isValidContentType(toPath, DROIDDOC_VALID_CONTENT_TYPES))) {
+        Errors.error(Errors.INVALID_CONTENT_TYPE, null, "Failed to process " + from
+                + ": Invalid file type. Please move the file to frameworks/base/docs/image_sources/... or docs/downloads/...");
+        return;
+    }
 
     long sizel = from.length();
     final int maxsize = 64 * 1024;
@@ -216,4 +220,14 @@
     }
   }
 
+  public static String[] DROIDDOC_VALID_CONTENT_TYPES = {".txt", ".css", ".js", ".html", ".ico", ".png", ".jpg", ".gif", ".svg", ".webm", ".ogv","mp4", ".java", ".xml", ".aidl", ".rs",".zip", ".yaml"};
+
+  public static boolean isValidContentType(String s, String[] list) {
+    for (String t : list) {
+      if (s.endsWith(t)) {
+        return true;
+      }
+    }
+    return false;
+  }
 }
diff --git a/src/com/google/doclava/DocFile.java b/src/com/google/doclava/DocFile.java
index d450224..da829a4 100644
--- a/src/com/google/doclava/DocFile.java
+++ b/src/com/google/doclava/DocFile.java
@@ -51,6 +51,27 @@
     }
   }
 
+  public static String[] DEVSITE_VALID_LANGS = {"en", "es","ja", "ko", "ru", "zh-cn"};
+
+  public static String getPathRoot(String filename) {
+    String[] stripStr = filename.split("\\/");
+    String outFrag = stripStr[0];
+    if (stripStr.length > 0) {
+      for (String t : DEVSITE_VALID_LANGS) {
+        if (stripStr[0].equals("intl")) {
+          if (stripStr[1].equals(t)) {
+            outFrag = stripStr[2];
+            break;
+          }
+        } else if (stripStr[0].equals(t)) {
+            outFrag = stripStr[1];
+            break;
+        }
+      }
+    }
+    return outFrag;
+  }
+  
   public static void writePage(String docfile, String relative, String outfile) {
     Data hdf = Doclava.makeHDF();
 
@@ -121,33 +142,30 @@
       ClearPage.write(hdf, "docpage.cs", outfile);
     } else {
       String filename = outfile;
-      // Check whether this is a localized page and remove the intl/*/ path from filename
-      if (filename.indexOf("intl/") == 0) {
-        filename = filename.substring(filename.indexOf("/", 5) + 1); // After intl/ to get 2nd /
-      }
-      if (filename.indexOf("design/") == 0) {
+      // Strip out the intl and lang id substr and get back just the 
+      // guide, design, distribute, etc. 
+      filename = getPathRoot(filename);
+      if (filename.indexOf("design") == 0) {
         hdf.setValue("design", "true");
-      } else if (filename.indexOf("develop/") == 0) {
+      } else if (filename.indexOf("develop") == 0) {
         hdf.setValue("develop", "true");
-      } else if (filename.indexOf("guide/") == 0) {
+      } else if (filename.indexOf("guide") == 0) {
         hdf.setValue("guide", "true");
-      } else if (filename.indexOf("training/") == 0) {
+      } else if (filename.indexOf("training") == 0) {
         hdf.setValue("training", "true");
-      } else if (filename.indexOf("more/") == 0) {
+      } else if (filename.indexOf("more") == 0) {
         hdf.setValue("more", "true");
-      } else if (filename.indexOf("google/") == 0) {
+      } else if (filename.indexOf("google") == 0) {
         hdf.setValue("google", "true");
-      } else if (filename.indexOf("distribute/") == 0) {
+      } else if (filename.indexOf("distribute") == 0) {
         hdf.setValue("distribute", "true");
-      } else if (filename.indexOf("about/") == 0) {
+      } else if (filename.indexOf("about") == 0) {
         hdf.setValue("about", "true");
-      } else if ((filename.indexOf("tools/") == 0) || (filename.indexOf("sdk/") == 0)) {
+      } else if ((filename.indexOf("tools") == 0) || (filename.indexOf("sdk") == 0)) {
         hdf.setValue("tools", "true");
+        fromTemplate = hdf.getValue("page.template", "");
       }
-
-      if ((filename.indexOf("tools/sdk/preview/index.html") == 0) ||
-          (filename.indexOf("sdk/index.html") == 0) ||
-          (filename.indexOf("tools/sdk/ndk/index.html") == 0)) {
+      if (fromTemplate.equals("sdk")) {
         ClearPage.write(hdf, "sdkpage.cs", outfile);
       } else {
         ClearPage.write(hdf, "docpage.cs", outfile);
diff --git a/src/com/google/doclava/Doclava.java b/src/com/google/doclava/Doclava.java
index 4bdf245..a93f5b9 100644
--- a/src/com/google/doclava/Doclava.java
+++ b/src/com/google/doclava/Doclava.java
@@ -64,7 +64,14 @@
 
   public static int showLevel = SHOW_PROTECTED;
 
-  public static final String javadocDir = "reference/";
+  public static String outputPathBase = "/";
+  public static ArrayList<String> inputPathHtmlDirs = new ArrayList<String>();
+  public static ArrayList<String> inputPathHtmlDir2 = new ArrayList<String>();
+  public static String outputPathHtmlDirs;
+  public static String outputPathHtmlDir2;
+  public static boolean devsite = false;
+  public static final String devsiteRoot = "en/";
+  public static String javadocDir = "reference/";
   public static String htmlExtension;
 
   public static RootDoc root;
@@ -74,6 +81,8 @@
   public static SinceTagger sinceTagger = new SinceTagger();
   public static HashSet<String> knownTags = new HashSet<String>();
   public static FederationTagger federationTagger = new FederationTagger();
+  public static Set<String> showAnnotations = new HashSet<String>();
+  public static boolean includeDefaultAssets = true;
   private static boolean generateDocs = true;
   private static boolean parseComments = false;
   private static String yamlNavFile = null;
@@ -141,7 +150,7 @@
     String[][] options = r.options();
     for (String[] a : options) {
       if (a[0].equals("-d")) {
-        ClearPage.outputDir = a[1];
+        outputPathBase = outputPathHtmlDirs = ClearPage.outputDir = a[1];
       } else if (a[0].equals("-templatedir")) {
         ClearPage.addTemplateDir(a[1]);
       } else if (a[0].equals("-hdf")) {
@@ -152,8 +161,18 @@
         ClearPage.toroot = a[1];
       } else if (a[0].equals("-samplecode")) {
         sampleCodes.add(new SampleCode(a[1], a[2], a[3]));
+      //the destination output path for main htmldir
       } else if (a[0].equals("-htmldir")) {
-        ClearPage.htmlDirs.add(a[1]);
+        inputPathHtmlDirs.add(a[1]);
+        ClearPage.htmlDirs = inputPathHtmlDirs;
+      //the destination output path for additional htmldir
+      } else if (a[0].equals("-htmldir2")) {
+          if (a[2].equals("default")) {
+          inputPathHtmlDirs.add(a[1]);
+        } else {
+          inputPathHtmlDir2.add(a[1]);
+          outputPathHtmlDir2 = a[2];
+        }
       } else if (a[0].equals("-title")) {
         Doclava.title = a[1];
       } else if (a[0].equals("-werror")) {
@@ -175,6 +194,8 @@
         }
       } else if (a[0].equals("-keeplist")) {
         keepListFile = a[1];
+      } else if (a[0].equals("-showAnnotation")) {
+        showAnnotations.add(a[1]);
       } else if (a[0].equals("-proguard")) {
         proguardFile = a[1];
       } else if (a[0].equals("-proofread")) {
@@ -225,6 +246,11 @@
         federationTagger.addSiteApi(name, file);
       } else if (a[0].equals("-yaml")) {
         yamlNavFile = a[1];
+      } else if (a[0].equals("-devsite")) {
+        devsite = true;
+        // Don't copy the doclava assets to devsite output (ie use proj assets only)
+        includeDefaultAssets = false;
+        outputPathHtmlDirs = outputPathHtmlDirs + "/" + devsiteRoot;
       }
     }
 
@@ -275,8 +301,20 @@
         TodoFile.writeTodoFile(todoFile);
       }
 
+      // HTML2 Pages -- Generate Pages from optional secondary dir
+      if (!inputPathHtmlDir2.isEmpty()) {
+        if (!outputPathHtmlDir2.isEmpty()) {
+          ClearPage.outputDir = outputPathBase + "/" + outputPathHtmlDir2;
+        }
+        ClearPage.htmlDirs = inputPathHtmlDir2;
+        writeHTMLPages();
+        ClearPage.htmlDirs = inputPathHtmlDirs;
+      }
+
       // HTML Pages
       if (!ClearPage.htmlDirs.isEmpty()) {
+        ClearPage.htmlDirs = inputPathHtmlDirs;
+        ClearPage.outputDir = outputPathHtmlDirs;
         writeHTMLPages();
       }
 
@@ -335,7 +373,7 @@
 
     long time = System.nanoTime() - startTime;
     System.out.println("DroidDoc took " + (time / 1000000000) + " sec. to write docs to "
-        + ClearPage.outputDir);
+        + outputPathBase );
 
     return !Errors.hadError;
   }
@@ -484,6 +522,9 @@
     if (option.equals("-htmldir")) {
       return 2;
     }
+    if (option.equals("-htmldir2")) {
+      return 3;
+    }
     if (option.equals("-title")) {
       return 2;
     }
@@ -502,6 +543,9 @@
     if (option.equals("-keeplist")) {
       return 2;
     }
+    if (option.equals("-showAnnotation")) {
+      return 2;
+    }
     if (option.equals("-proguard")) {
       return 2;
     }
@@ -559,6 +603,9 @@
     if (option.equals("-yaml")) {
       return 2;
     }
+    if (option.equals("-devsite")) {
+      return 1;
+    }
     if (option.equals("-gmsref")) {
       gmsRef = true;
       return 1;
@@ -715,7 +762,7 @@
 
   public static void writeAssets() {
     JarFile thisJar = JarUtils.jarForClass(Doclava.class, null);
-    if (thisJar != null) {
+    if ((thisJar != null) && (includeDefaultAssets)) {
       try {
         List<String> templateDirs = ClearPage.getBundledTemplateDirs();
         for (String templateDir : templateDirs) {
@@ -729,6 +776,7 @@
       }
     }
 
+    //write the project-specific assets
     List<String> templateDirs = ClearPage.getTemplateDirs();
     for (String templateDir : templateDirs) {
       File assets = new File(templateDir + "/assets");
@@ -736,6 +784,12 @@
         writeDirectory(assets, "assets/", null);
       }
     }
+
+    // Create the timestamp.js file based on .cs file
+    if (devsite) {
+      Data timedata = Doclava.makeHDF();
+      ClearPage.write(timedata, "timestamp.cs", "timestamp.js");
+    }
   }
 
   public static void writeLists() {
@@ -1064,10 +1118,9 @@
 
   public static void writeClass(ClassInfo cl, Data data) {
     cl.makeHDF(data);
-
     setPageTitle(data, cl.name());
-    ClearPage.write(data, "class.cs", cl.htmlPage());
-
+    String outfile = cl.htmlPage();
+    ClearPage.write(data, "class.cs", outfile);
     Proofread.writeClass(cl.htmlPage(), cl);
   }
 
diff --git a/src/com/google/doclava/Errors.java b/src/com/google/doclava/Errors.java
index ddcfafb..2fc0f0c 100644
--- a/src/com/google/doclava/Errors.java
+++ b/src/com/google/doclava/Errors.java
@@ -167,6 +167,7 @@
   public static final Error NO_SINCE_DATA = new Error(116, HIDDEN);
   public static final Error NO_FEDERATION_DATA = new Error(117, WARNING);
   public static final Error BROKEN_SINCE_FILE = new Error(118, ERROR);
+  public static final Error INVALID_CONTENT_TYPE = new Error(119, ERROR);
 
   public static final Error[] ERRORS =
       {UNRESOLVED_LINK, BAD_INCLUDE_TAG, UNKNOWN_TAG, UNKNOWN_PARAM_TAG_NAME,
@@ -177,7 +178,7 @@
           REMOVED_METHOD, REMOVED_FIELD, REMOVED_INTERFACE, CHANGED_STATIC, CHANGED_FINAL,
           CHANGED_TRANSIENT, CHANGED_VOLATILE, CHANGED_TYPE, CHANGED_VALUE, CHANGED_SUPERCLASS,
           CHANGED_SCOPE, CHANGED_ABSTRACT, CHANGED_THROWS, CHANGED_NATIVE, CHANGED_CLASS,
-          CHANGED_DEPRECATED, CHANGED_SYNCHRONIZED, BROKEN_SINCE_FILE};
+          CHANGED_DEPRECATED, CHANGED_SYNCHRONIZED, BROKEN_SINCE_FILE, INVALID_CONTENT_TYPE};
 
   public static boolean setErrorLevel(int code, int level) {
     for (Error e : ERRORS) {
diff --git a/src/com/google/doclava/MemberInfo.java b/src/com/google/doclava/MemberInfo.java
index 5600749..e5cc7a2 100644
--- a/src/com/google/doclava/MemberInfo.java
+++ b/src/com/google/doclava/MemberInfo.java
@@ -42,6 +42,18 @@
 
   public abstract boolean isExecutable();
 
+  @Override
+  public boolean isHidden() {
+    if (mAnnotations != null) {
+      for (AnnotationInstanceInfo info : mAnnotations) {
+        if (Doclava.showAnnotations.contains(info.type().qualifiedName())) {
+          return false;
+        }
+      }
+    }
+    return super.isHidden();
+  }
+
   public String anchor() {
     if (mSignature != null) {
       return mName + mSignature;