doc change: Samples browsing: starter templates and navtree gen support.

Change-Id: I03f5eec1dcb71e96fed2ba0119a0b2c0bba56f49
diff --git a/src/com/google/doclava/DocFile.java b/src/com/google/doclava/DocFile.java
index 9e56235..f699f58 100644
--- a/src/com/google/doclava/DocFile.java
+++ b/src/com/google/doclava/DocFile.java
@@ -157,6 +157,8 @@
         hdf.setValue("more", "true");
       } else if (filename.indexOf("google") == 0) {
         hdf.setValue("google", "true");
+      } else if (filename.indexOf("samples") == 0) {
+        hdf.setValue("samples", "true");
       } else if (filename.indexOf("distribute") == 0) {
         hdf.setValue("distribute", "true");
       } else if (filename.indexOf("about") == 0) {
diff --git a/src/com/google/doclava/Doclava.java b/src/com/google/doclava/Doclava.java
index 3bcc689..1a77828 100644
--- a/src/com/google/doclava/Doclava.java
+++ b/src/com/google/doclava/Doclava.java
@@ -91,6 +91,7 @@
 
   private static boolean gmsRef = false;
   private static boolean gcmRef = false;
+  private static boolean samplesRef = false;
   private static boolean sac = false;
 
   public static boolean checkLevel(int level) {
@@ -322,8 +323,13 @@
 
       writeAssets();
 
+      // Sample code pages
+      if (samplesRef) {
+        writeSamples(offlineMode, sampleCodes);
+      }
+      
       // Navigation tree
-       String refPrefix = new String();
+      String refPrefix = new String();
       if(gmsRef){
         refPrefix = "gms-";
       } else if(gcmRef){
@@ -351,11 +357,6 @@
         writeKeepList(keepListFile);
       }
 
-      // Sample Code
-      for (SampleCode sc : sampleCodes) {
-        sc.write(offlineMode);
-      }
-
       // Index page
       writeIndex();
 
@@ -519,8 +520,12 @@
       return 2;
     }
     if (option.equals("-samplecode")) {
+      samplesRef = true;
       return 4;
     }
+    if (option.equals("-devsite")) {
+      return 1;
+    }
     if (option.equals("-htmldir")) {
       return 2;
     }
@@ -1645,4 +1650,20 @@
   static String ensureSlash(String path) {
     return path.endsWith("/") ? path : path + "/";
   }
+
+  /**
+  * Process samples dirs that are specified in Android.mk. Generate html
+  * wrapped pages, copy files to output dir, and generate a SampleCode NavTree.
+  */
+  public static void writeSamples(boolean offlineMode, ArrayList<SampleCode> sampleCodes) {
+    // Go through SCs processing files. Create a root list for SC nodes,
+    // pass it to SCs for their NavTree children and append them.
+    List<SampleCode.Node> samplesList = new ArrayList<SampleCode.Node>();
+    for (SampleCode sc : sampleCodes) {
+      samplesList.add(sc.write(offlineMode));
+    }
+    // Pass full samplesList to SC to render to js file.
+    SampleCode.writeSamplesNavTree(samplesList);
+  }
+
 }
diff --git a/src/com/google/doclava/SampleCode.java b/src/com/google/doclava/SampleCode.java
index 65d44b5..5294faf 100644
--- a/src/com/google/doclava/SampleCode.java
+++ b/src/com/google/doclava/SampleCode.java
@@ -30,22 +30,49 @@
   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;
     }
+
+    //System.out.println("SampleCode init: source: " + mSource);
+    //System.out.println("SampleCode init: dest: " + mDest);
+    //System.out.println("SampleCode init: title: " + mTitle);
+
   }
 
-  public void write(boolean offlineMode) {
+  public Node write(boolean offlineMode) {
+    List<Node> filelist = new ArrayList<Node>();
     File f = new File(mSource);
+    String name = f.getName();
+    String startname = name;
+    String subdir = mDest;
+    String mOut = subdir + name;
     if (!f.isDirectory()) {
       System.out.println("-samplecode not a directory: " + mSource);
-      return;
+      return null;
     }
-    
-    writeDirectory(f, mDest, offlineMode);
+
+    if (offlineMode)
+      writeIndexOnly(f, mDest, offlineMode);
+    else {
+      Data hdf = Doclava.makeHDF();
+      hdf.setValue("samples", "true");
+      writeProjectDirectory(filelist, f, mDest, false, hdf, "Files.");
+      // values for handling breadcrumb for project.html file
+      hdf.setValue("page.title", "Project Structure");
+      hdf.removeTree("parentdirs");
+      hdf.setValue("parentdirs.0.Name", name);
+      hdf.setValue("showProjectPaths","true");
+      hdf.setValue("samples", "true");
+      ClearPage.write(hdf, "sampleindex.cs", mDest + "project" + Doclava.htmlExtension); //write the project.html file
+      // return a root SC node for the sample with children appended
+      return new Node(mTitle, "samples/" + startname + "/index.html", filelist, null);
+    }
+    return null;
   }
 
   public static String convertExtension(String s, String ext) {
@@ -53,7 +80,7 @@
   }
 
   public static String[] IMAGES = {".png", ".jpg", ".gif"};
-  public static String[] TEMPLATED = {".java", ".xml", ".aidl", ".rs"};
+  public static String[] TEMPLATED = {".java", ".xml", ".aidl", ".rs",".txt", ".TXT"};
 
   public static boolean inList(String s, String[] list) {
     for (String t : list) {
@@ -63,9 +90,115 @@
     }
     return false;
   }
-  
-  public void writeDirectory(File dir, String relative) {
-    writeDirectory(dir, relative, false);
+
+  public static String mapTypes(String name) {
+    String type = name.substring(name.lastIndexOf('.') + 1, name.length());
+    if (type.equals("xml") || type.equals("java")) {
+      if (name.equals("AndroidManifest.xml")) type = "manifest";
+      return type;
+    } else {
+      return type = "file";
+    }
+  }
+
+  public void writeProjectDirectory(List<Node> parent, File dir, String relative, Boolean recursed, Data hdf, String newkey) {
+    TreeSet<String> dirs = new TreeSet<String>(); //dirs for project structure and breadcrumb
+    TreeSet<String> files = new TreeSet<String>(); //files for project structure and breadcrumb
+
+    String subdir = relative;
+    String name = "";
+    String label = "";
+    String link = "";
+    String type = "";
+    int i = 0;
+    String expansion = ".Sub.";
+    String key = newkey;
+
+    if (recursed) {
+      key = (key + expansion);
+    } else {
+      expansion = "";
+    }
+
+    for (File f: dir.listFiles()) {
+      name = f.getName();
+      // don't process certain types of files
+      if (name.startsWith(".") ||
+          name.startsWith("_") ||
+          name.equals("default.properties") ||
+          name.equals("build.properties") ||
+          name.endsWith(".ttf") ||
+          name.equals("Android.mk")) {
+         //System.out.println("Invalid File Type, bypassing: " + name);
+         continue;
+       }
+       if (f.isFile() && name.contains(".")){
+         String path = relative + name;
+         type = mapTypes(name);
+         link = convertExtension(path, ".html");
+         hdf.setValue("samples", "true");//dd needed?
+
+         if (inList(path, IMAGES)) {
+           // copy these files to output directly
+           type = "img";
+           ClearPage.copyFile(false, f, path);
+           writeImagePage(f, convertExtension(path, Doclava.htmlExtension), relative);
+           files.add(name);
+           hdf.setValue(key + i + ".Type", "img");
+           hdf.setValue(key + i + ".Name", name);
+           hdf.setValue(key + i + ".Href", link);
+         }
+         if (inList(path, TEMPLATED)) {
+           // copied and goes through the template
+           ClearPage.copyFile(false, f, path);
+           writePage(f, convertExtension(path, Doclava.htmlExtension), relative);
+           files.add(name);
+           hdf.setValue(key + i + ".Type", type);
+           hdf.setValue(key + i + ".Name", name);
+           hdf.setValue(key + i + ".Href", link);
+         }
+         // add file to the navtree
+         parent.add(new Node(name, link , null, type));
+         i++;
+       } else if (f.isDirectory()) {
+         List<Node> mchildren = new ArrayList<Node>();
+         type = "dir";
+         String dirpath = relative + name;
+         link = dirpath + "/index.html";
+         String hdfkeyName = (key + i + ".Name");
+         String hdfkeyType = (key + i + ".Type");
+         String hdfkeyHref = (key + i + ".Href");
+         hdf.setValue(hdfkeyName, name);
+         hdf.setValue(hdfkeyType, type);
+         hdf.setValue(hdfkeyHref, relative + name + "/" + "index.html");
+         //System.out.println("Found directory, recursing. Current key: " + hdfkeyName);
+         writeProjectDirectory(mchildren, f, relative + name + "/", true, hdf, (key + i));
+         if (mchildren.size() > 0) {
+           //dir is processed, now add it to the navtree
+           //don't link sidenav subdirs at this point (but cab use "link" to do so)
+          parent.add(new Node(name, null, mchildren, type));
+         }
+         //dirs.add(name); //dd not used?
+         i++;
+       }
+    }
+    //dd not working yet
+    getSummaryFromDir(hdf, dir, newkey);
+    hdf.setValue("resType", "Sample Code");
+    hdf.setValue("resTag", "sample");
+    hdf.setValue("showProjectPaths","false");
+    //If this is an index for the project root (assumed root if split length is 3 (development/samples/nn)),
+    //then remove the root dir so that it won't appear in the breadcrumb. Else just pass it through to
+    //setParentDirs as usual.
+    String mpath = dir + "";
+    String sdir[] = mpath.split("/");
+    if (sdir.length == 3 ) {
+      System.out.println("-----------------> this must be the root: [sdir len]" + sdir.length + "[dir]" + dir);
+      hdf.setValue("showProjectPaths","true");
+    }
+    setParentDirs(hdf, relative, name, false);
+    System.out.println("writing  sample index -- " + relative + "index" + Doclava.htmlExtension);
+    ClearPage.write(hdf, "sampleindex.cs", relative + "/index" + Doclava.htmlExtension);
   }
 
   public void writeDirectory(File dir, String relative, boolean offline) {
@@ -81,7 +214,6 @@
       }
       if (f.isFile()) {
         String out = relative + name;
-
         if (inList(out, IMAGES)) {
           // copied directly
           ClearPage.copyFile(false, f, out);
@@ -93,6 +225,7 @@
           ClearPage.copyFile(false, f, out);
           writePage(f, convertExtension(out, Doclava.htmlExtension), subdir);
           files.add(name);
+
         }
         // else ignored
       } else if (f.isDirectory()) {
@@ -108,13 +241,14 @@
     hdf.setValue("subdir", subdir);
     i = 0;
     for (String d : dirs) {
-      hdf.setValue("subdirs." + i + ".name", d);
+      hdf.setValue("subdirs." + i + ".Name", d);
+      hdf.setValue("files." + i + ".Href", convertExtension(d, ".html"));
       i++;
     }
     i = 0;
     for (String f : files) {
-      hdf.setValue("files." + i + ".name", f);
-      hdf.setValue("files." + i + ".href", convertExtension(f, ".html"));
+      hdf.setValue("files." + i + ".Name", f);
+      hdf.setValue("files." + i + ".Href", convertExtension(f, ".html"));
       i++;
     }
 
@@ -122,6 +256,14 @@
     ClearPage.write(hdf, "sampleindex.cs", relative + "index" + Doclava.htmlExtension);
   }
 
+  public void writeIndexOnly(File dir, String relative, Boolean offline) {
+    Data hdf = writeIndex(dir);
+    if (!offline) relative = "/" + relative;
+
+      System.out.println("writing indexonly at " + relative + "/index" + Doclava.htmlExtension);
+      ClearPage.write(hdf, "sampleindex.cs", relative + "index" + Doclava.htmlExtension);
+  }
+
   public Data writeIndex(File dir) {
     Data hdf = Doclava.makeHDF();
 
@@ -131,7 +273,7 @@
     String filename = dir.getPath() + "/_index.html";
     String summary =
         SampleTagInfo.readFile(new SourcePositionInfo(filename, -1, -1), filename, "sample code",
-            true, false, true);
+            true, false, false, true);
 
     if (summary == null) {
       summary = "";
@@ -141,21 +283,70 @@
     return hdf;
   }
 
+//dd START reformat this
+    public Boolean getSummaryFromDir(Data hdf, File dir, String key) {
+        hdf.setValue("page.title", dir.getName());
+        hdf.setValue("projectTitle", mTitle);
+
+        String filename = dir.getPath() + "/_index.html";
+        String summary = SampleTagInfo.readFile(new SourcePositionInfo(filename,
+                          -1,-1), filename, "sample code", true, false, false, true);
+
+        if (summary != null) {
+            hdf.setValue(key + "SummaryFlag", "true");
+            hdf.setValue("summary", summary);
+            //set the target for [info] link
+            hdf.setValue(key + "Href", dir + "/index.html");
+            return true;
+        }
+        else {
+            hdf.setValue("summary", "");
+            return false;
+        }
+    }
+
+    Data setParentDirs(Data hdf, String subdir, String name, Boolean isFile) {
+        isFile = false;
+        int iter;
+        hdf.removeTree("parentdirs");
+        //System.out.println("setParentDirs for " + subdir + name);
+        String s = subdir;
+        String urlParts[] = s.split("/");
+        int n, l = (isFile)?1:0;
+        for (iter=2; iter < urlParts.length - l; iter++) {
+            n = iter-2;
+         //System.out.println("parentdirs." + n + ".Name == " + urlParts[iter]);
+                hdf.setValue("parentdirs." + n + ".Name", urlParts[iter]);
+            }
+        return hdf;
+    }//dd END reformat this
+
+
+
+
+
+
+
+
+
   public void writePage(File f, String out, String subdir) {
     String name = f.getName();
-
-    String filename = f.getPath();
+    String path = f.getPath();
     String data =
-        SampleTagInfo.readFile(new SourcePositionInfo(filename, -1, -1), filename, "sample code",
-            true, true, true);
+        SampleTagInfo.readFile(new SourcePositionInfo(path, -1, -1), path, "sample code",
+            true, true, true, true);
     data = Doclava.escape(data);
 
     Data hdf = Doclava.makeHDF();
-
+    hdf.setValue("samples", "true");
+    setParentDirs(hdf, subdir, name, true);
+    hdf.setValue("projectTitle", mTitle);
     hdf.setValue("page.title", name);
     hdf.setValue("subdir", subdir);
     hdf.setValue("realFile", name);
     hdf.setValue("fileContents", data);
+    hdf.setValue("resTag", "sample");
+    hdf.setValue("resType", "Sample Code");
 
     ClearPage.write(hdf, "sample.cs", out);
   }
@@ -166,12 +357,114 @@
     String data = "<img src=\"" + name + "\" title=\"" + name + "\" />";
 
     Data hdf = Doclava.makeHDF();
-
+    hdf.setValue("samples", "true");
+    setParentDirs(hdf, subdir, name, true);
     hdf.setValue("page.title", name);
+    hdf.setValue("projectTitle", mTitle);
     hdf.setValue("subdir", subdir);
     hdf.setValue("realFile", name);
     hdf.setValue("fileContents", data);
-
+    hdf.setValue("resTag", "sample");
+    hdf.setValue("resType", "Sample Code");
     ClearPage.write(hdf, "sample.cs", out);
   }
+
+  /**
+  * Render a SC node to a navtree js file.
+  */
+  public static void writeSamplesNavTree(List<Node> tnode) {
+    Node node = new Node("Reference", "packages.html", tnode, null);
+
+    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);
+    }
+
+    Data data = Doclava.makeHDF();
+    data.setValue("reference_tree", buf.toString());
+    ClearPage.write(data, "samples_navtree_data.cs", "samples_navtree_data.js");
+  }
+
+  /**
+  * SampleCode variant of NavTree node.
+  */
+  public static class Node {
+    private String mLabel;
+    private String mLink;
+    List<Node> mChildren;
+    private String mType;
+
+    Node(String label, String link, List<Node> children, String type) {
+      mLabel = label;
+      mLink = link;
+      mChildren = children;
+      mType = type;
+    }
+
+    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) {
+      List<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(", ");
+      renderString(buf, mType);
+      buf.append(" ]");
+    }
+  }
+
 }
diff --git a/src/com/google/doclava/SampleTagInfo.java b/src/com/google/doclava/SampleTagInfo.java
index 9b9e40a..4e14b2b 100644
--- a/src/com/google/doclava/SampleTagInfo.java
+++ b/src/com/google/doclava/SampleTagInfo.java
@@ -79,7 +79,7 @@
     boolean trim = "@sample".equals(name);
 
     if (id == null || "".equals(id)) {
-      mIncluded = readFile(position, filename, id, trim, true, false);
+      mIncluded = readFile(position, filename, id, trim, true, false, false);
     } else {
       mIncluded = loadInclude(position, filename, id, trim);
     }
@@ -106,6 +106,14 @@
     }
   }
 
+  static String addLineNumber(String line, String num) {
+    StringBuilder numberedLine = new StringBuilder();
+    numberedLine.append("<li id=\"" + num + "\">");
+    numberedLine.append(line);
+    numberedLine.append("</li>");
+    return numberedLine.substring(0);
+  }
+
   static String loadInclude(SourcePositionInfo pos, String filename, String id, boolean trim) {
     Reader input = null;
     StringBuilder result = new StringBuilder();
@@ -196,20 +204,25 @@
   }
 
   static String readFile(SourcePositionInfo pos, String filename, String id, boolean trim,
-      boolean escape, boolean errorOk) {
+      boolean escape, boolean numberedLines, 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();
+        String lineNum = Integer.toString(lines.getLineNumber());
+
         if (line == null) {
           break;
         }
+
         if (trim) {
           if (isIncludeLine(line)) {
             continue;
@@ -223,15 +236,28 @@
             if (escape) {
               line = escapeHtml(line);
             }
+            if (numberedLines) {
+              line = addLineNumber(line, lineNum);
+            }
             result.append(line);
             trailing = 1; // add \n next time, maybe
             started = true;
           } else {
             if (started) {
-              trailing++;
+              if (numberedLines) {
+                result.append('\n');
+                line = line + " ";
+                line = addLineNumber(line, lineNum);
+                result.append(line);
+              } else {
+                trailing++;
+              }
             }
           }
         } else {
+            if (numberedLines) {
+              line = addLineNumber(line, lineNum);
+            }
           result.append(line);
           result.append('\n');
         }
diff --git a/src/com/google/doclava/SinceTagger.java b/src/com/google/doclava/SinceTagger.java
index a2797c1..858d98f 100644
--- a/src/com/google/doclava/SinceTagger.java
+++ b/src/com/google/doclava/SinceTagger.java
@@ -30,7 +30,7 @@
 
 
 /**
- * Applies version information to the DroidDoc class model from apicheck XML files. Sample usage:
+ * Applies version information to the Doclava class model from apicheck XML files. Sample usage:
  * 
  * <pre>
  *   ClassInfo[] classInfos = ...