1 /* 2 * Copyright (C) 2010 Google Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.google.doclava; 18 19 import java.util.Arrays; 20 import java.util.ArrayList; 21 import java.util.Collections; 22 import java.util.Comparator; 23 import java.util.List; 24 import java.util.regex.Pattern; 25 import java.util.regex.Matcher; 26 import java.io.File; 27 28 import com.google.clearsilver.jsilver.data.Data; 29 30 /** 31 * Represents a browsable sample code project, with methods for managing 32 * metadata collection, file output, sorting, etc. 33 */ 34 public class SampleCode { 35 String mSource; 36 String mDest; 37 String mTitle; 38 String mProjectDir; 39 String mTags; 40 41 /** Max size for browseable images/video. If a source file exceeds this size, 42 * a file is generated with a generic placeholder and the original file is not 43 * copied to out. 44 */ 45 private static final double MAX_FILE_SIZE_BYTES = 2097152; 46 47 /** When full tree nav is enabled, generate an index for every dir 48 * and linkify the breadcrumb paths in all files. 49 */ 50 private static final boolean FULL_TREE_NAVIGATION = false; 51 SampleCode(String source, String dest, String title)52 public SampleCode(String source, String dest, String title) { 53 mSource = source; 54 mTitle = title; 55 mTags = null; 56 57 if (dest != null) { 58 int len = dest.length(); 59 if (len > 1 && dest.charAt(len - 1) != '/') { 60 mDest = dest + '/'; 61 } else { 62 mDest = dest; 63 } 64 } 65 } 66 67 /** 68 * Iterates a given sample code project gathering metadata for files and building 69 * a node tree that reflects the project's directory structure. After iterating 70 * the project, this method adds the project's metadata to jd_lists_unified, 71 * so that it is accessible for dynamic content and search suggestions. 72 * 73 * @param offlineMode Ignored -- offline-docs mode is not currently supported for 74 * browsable sample code projects. 75 * @return A root Node for the project containing its metadata and tree structure. 76 */ setSamplesTOC(boolean offlineMode)77 public Node setSamplesTOC(boolean offlineMode) { 78 List<Node> filelist = new ArrayList<Node>(); 79 File f = new File(mSource); 80 mProjectDir = f.getName(); 81 String name = mProjectDir; 82 String mOut = mDest + name; 83 if (!f.isDirectory()) { 84 System.out.println("-samplecode not a directory: " + mSource); 85 return null; 86 } 87 88 Data hdf = Doclava.makeHDF(); 89 setProjectStructure(filelist, f, mDest); 90 String link = ClearPage.toroot + "samples/" + name + "/index" + Doclava.htmlExtension; 91 Node rootNode = writeSampleIndexCs(hdf, f, 92 new Node.Builder().setLabel(mProjectDir).setLink(link).setChildren(filelist).build(),false); 93 return rootNode; 94 } 95 96 /** 97 * For a given sample code project dir, iterate through the project generating 98 * browsable html for all valid sample code files. After iterating the project 99 * generate a templated index file to the project output root. 100 * 101 * @param offlineMode Ignored -- offline-docs mode is not currently supported for 102 * browsable sample code projects. 103 */ writeSamplesFiles(boolean offlineMode)104 public void writeSamplesFiles(boolean offlineMode) { 105 List<Node> filelist = new ArrayList<Node>(); 106 File f = new File(mSource); 107 mProjectDir = f.getName(); 108 String name = mProjectDir; 109 String mOut = mDest + name; 110 if (!f.isDirectory()) { 111 System.out.println("-samplecode not a directory: " + mSource); 112 } 113 114 Data hdf = Doclava.makeHDF(); 115 if (Doclava.samplesNavTree != null) { 116 hdf.setValue("samples_toc_tree", Doclava.samplesNavTree.getValue("samples_toc_tree", "")); 117 } 118 hdf.setValue("samples", "true"); 119 hdf.setValue("projectDir", mProjectDir); 120 writeProjectDirectory(f, mDest, false, hdf, "Files."); 121 writeProjectStructure(name, hdf); 122 hdf.removeTree("parentdirs"); 123 hdf.setValue("parentdirs.0.Name", name); 124 boolean writeFiles = true; 125 String link = "samples/" + name + "/index" + Doclava.htmlExtension; 126 //Write root _index.jd to out and add metadata to Node. 127 writeSampleIndexCs(hdf, f, 128 new Node.Builder().setLabel(mProjectDir).setLink(link).build(), true); 129 } 130 131 /** 132 * Given the root Node for a sample code project, iterates through the project 133 * gathering metadata and project tree structure. Unsupported file types are 134 * filtered from the project output. The collected project Nodes are appended to 135 * the root project node. 136 * 137 * @param parent The root Node that represents this sample code project. 138 * @param dir The current dir being processed. 139 * @param relative Relative path for creating links to this file. 140 */ setProjectStructure(List<Node> parent, File dir, String relative)141 public void setProjectStructure(List<Node> parent, File dir, String relative) { 142 String name, link; 143 File[] dirContents = dir.listFiles(); 144 Arrays.sort(dirContents, byTypeAndName); 145 for (File f: dirContents) { 146 name = f.getName(); 147 if (!isValidFiletype(name)) { 148 continue; 149 } 150 if (f.isFile() && name.contains(".")) { 151 String path = relative + name; 152 link = convertExtension(path, Doclava.htmlExtension); 153 if (inList(path, IMAGES) || inList(path, VIDEOS) || inList(path, TEMPLATED)) { 154 parent.add(new Node.Builder().setLabel(name).setLink(ClearPage.toroot + link).build()); 155 } 156 } else if (f.isDirectory()) { 157 List<Node> mchildren = new ArrayList<Node>(); 158 String dirpath = relative + name + "/"; 159 setProjectStructure(mchildren, f, dirpath); 160 if (mchildren.size() > 0) { 161 parent.add(new Node.Builder().setLabel(name).setLink(ClearPage.toroot 162 + dirpath).setChildren(mchildren).build()); 163 } 164 } 165 } 166 } 167 168 /** 169 * Given a root sample code project path, iterates through the project 170 * setting page metadata to manage html output and writing/copying files to 171 * the output directory. Source files are templated and images are templated 172 * and linked to the original image. 173 * 174 * @param dir The current dir being processed. 175 * @param relative Relative path for creating links to this file. 176 * @param recursed Whether the method is being called recursively. 177 * @param hdf The data to read/write for files in this project. 178 * @param newKey Key passed in recursion for managing cs child trees. 179 */ writeProjectDirectory(File dir, String relative, Boolean recursed, Data hdf, String newkey)180 public void writeProjectDirectory(File dir, String relative, Boolean recursed, 181 Data hdf, String newkey) { 182 String name = ""; 183 String link = ""; 184 String type = ""; 185 int i = 0; 186 String expansion = ".Sub."; 187 String key = newkey; 188 String prefixRoot = Doclava.USE_DEVSITE_LOCALE_OUTPUT_PATHS ? "en/" : ""; 189 if (recursed) { 190 key = (key + expansion); 191 } else { 192 expansion = ""; 193 } 194 195 File[] dirContents = dir.listFiles(); 196 Arrays.sort(dirContents, byTypeAndName); 197 for (File f: dirContents) { 198 name = f.getName(); 199 if (!isValidFiletype(name)) { 200 continue; 201 } 202 if (f.isFile() && name.contains(".")) { 203 String baseRelative = relative; 204 if (Doclava.USE_DEVSITE_LOCALE_OUTPUT_PATHS) { 205 // don't nest root path 206 baseRelative = baseRelative.replaceFirst("^en/", ""); 207 } 208 String path = baseRelative + name; 209 type = mapTypes(name); 210 link = convertExtension(path, Doclava.htmlExtension); 211 if (inList(path, IMAGES)) { 212 type = "img"; 213 if (f.length() < MAX_FILE_SIZE_BYTES) { 214 ClearPage.copyFile(false, f, prefixRoot + path, false); 215 writeImageVideoPage(f, convertExtension(prefixRoot + path, Doclava.htmlExtension), 216 relative, type, true); 217 } else { 218 writeImageVideoPage(f, convertExtension(prefixRoot + path, Doclava.htmlExtension), 219 relative, type, false); 220 } 221 hdf.setValue(key + i + ".Type", "img"); 222 hdf.setValue(key + i + ".Name", name); 223 hdf.setValue(key + i + ".Href", link); 224 hdf.setValue(key + i + ".RelPath", relative); 225 } else if (inList(path, VIDEOS)) { 226 type = "video"; 227 if (f.length() < MAX_FILE_SIZE_BYTES) { 228 ClearPage.copyFile(false, f, prefixRoot + path, false); 229 writeImageVideoPage(f, convertExtension(prefixRoot + path, Doclava.htmlExtension), 230 relative, type, true); 231 } else { 232 writeImageVideoPage(f, convertExtension(prefixRoot + path, Doclava.htmlExtension), 233 relative, type, false); 234 } 235 hdf.setValue(key + i + ".Type", "video"); 236 hdf.setValue(key + i + ".Name", name); 237 hdf.setValue(key + i + ".Href", link); 238 hdf.setValue(key + i + ".RelPath", relative); 239 } else if (inList(path, TEMPLATED)) { 240 writePage(f, convertExtension(prefixRoot + path, Doclava.htmlExtension), relative, hdf); 241 hdf.setValue(key + i + ".Type", type); 242 hdf.setValue(key + i + ".Name", name); 243 hdf.setValue(key + i + ".Href", link); 244 hdf.setValue(key + i + ".RelPath", relative); 245 } 246 i++; 247 } else if (f.isDirectory()) { 248 List<Node> mchildren = new ArrayList<Node>(); 249 type = "dir"; 250 String dirpath = relative + name; 251 link = dirpath + "/index" + Doclava.htmlExtension; 252 String hdfkeyName = (key + i + ".Name"); 253 String hdfkeyType = (key + i + ".Type"); 254 String hdfkeyHref = (key + i + ".Href"); 255 hdf.setValue(hdfkeyName, name); 256 hdf.setValue(hdfkeyType, type); 257 hdf.setValue(hdfkeyHref, relative + name + "/" + "index" + Doclava.htmlExtension); 258 writeProjectDirectory(f, relative + name + "/", true, hdf, (key + i)); 259 i++; 260 } 261 } 262 263 setParentDirs(hdf, relative, name, false); 264 //Generate an index.html page for each dir being processed 265 if (FULL_TREE_NAVIGATION) { 266 ClearPage.write(hdf, "sampleindex.cs", prefixRoot + relative + "/index" + Doclava.htmlExtension); 267 } 268 } 269 270 /** 271 * Processes a templated project index page from _index.jd in a project root. 272 * Each sample project must have an index, and each index locally defines it's own 273 * page.tags and sample.group cs vars. This method takes a SC node on input, reads 274 * any local vars from the _index.jd, optionally generates an html file to out, 275 * then updates the SC node with the page vars and returns it to the caller. 276 * 277 * @param hdf The data source to read/write for this index file. 278 * @param dir The sample project root directory. 279 * @param tnode A Node to serve as the project's root node. 280 * @param writeFiles If true, generates output files only. If false, collects 281 * metadata only. 282 * @return The tnode root with any metadata/child Nodes appended. 283 */ writeSampleIndexCs(Data hdf, File dir, Node tnode, boolean writeFiles)284 public Node writeSampleIndexCs(Data hdf, File dir, Node tnode, boolean writeFiles) { 285 286 String filename = dir.getAbsolutePath() + "/_index.jd"; 287 String mGroup = ""; 288 File f = new File(filename); 289 String rel = dir.getPath(); 290 String prefixRoot = Doclava.USE_DEVSITE_LOCALE_OUTPUT_PATHS ? "en/" : ""; 291 if (writeFiles) { 292 293 hdf.setValue("samples", "true"); 294 //set any default page variables for root index 295 hdf.setValue("page.title", mProjectDir); 296 hdf.setValue("projectDir", mProjectDir); 297 hdf.setValue("projectTitle", mTitle); 298 //add the download/project links to the landing pages. 299 hdf.setValue("samplesProjectIndex", "true"); 300 if (!f.isFile()) { 301 //The directory didn't have an _index.jd, so create a stub. 302 ClearPage.write(hdf, "sampleindex.cs", prefixRoot + mDest + "index" + Doclava.htmlExtension); 303 } else { 304 DocFile.writePage(filename, rel, prefixRoot + mDest + "index" + Doclava.htmlExtension, hdf); 305 } 306 } else if (f.isFile()) { 307 //gather metadata for toc and jd_lists_unified 308 DocFile.getPageMetadata(filename, hdf); 309 mGroup = hdf.getValue("sample.group", ""); 310 if (!"".equals(mGroup)) { 311 tnode.setGroup(hdf.getValue("sample.group", "")); 312 } else { 313 //Errors.error(Errors.INVALID_SAMPLE_INDEX, null, "Sample " + mProjectDir 314 // + ": Root _index.jd must be present and must define sample.group" 315 // + " tag. Please see ... for details."); 316 } 317 } 318 return tnode; 319 } 320 321 /** 322 * Sets metadata for managing html output and generates the project view page 323 * for a project. 324 * 325 * @param dir The project root dir. 326 * @param hdf The data to read/write for files in this project. 327 */ writeProjectStructure(String dir, Data hdf)328 public void writeProjectStructure(String dir, Data hdf) { 329 String prefixRoot = Doclava.USE_DEVSITE_LOCALE_OUTPUT_PATHS ? "en/" : ""; 330 hdf.setValue("projectStructure", "true"); 331 hdf.setValue("projectDir", mProjectDir); 332 hdf.setValue("page.title", mProjectDir + " Structure"); 333 hdf.setValue("projectTitle", mTitle); 334 ClearPage.write(hdf, "sampleindex.cs", prefixRoot + mDest + "project" + Doclava.htmlExtension); 335 hdf.setValue("projectStructure", ""); 336 } 337 338 /** 339 * Keeps track of each file's parent dirs. Used for generating path breadcrumbs in html. 340 * 341 * @param dir The data to read/write for this file. 342 * @param hdf The relative path for this file, from samples root. 343 * @param subdir The relative path for this file, from samples root. 344 * @param name The name of the file (minus extension). 345 * @param isFile Whether this is a file (not a dir). 346 */ setParentDirs(Data hdf, String subdir, String name, Boolean isFile)347 Data setParentDirs(Data hdf, String subdir, String name, Boolean isFile) { 348 if (FULL_TREE_NAVIGATION) { 349 hdf.setValue("linkfyPathCrumb", ""); 350 } 351 int iter; 352 hdf.removeTree("parentdirs"); 353 String s = subdir; 354 String urlParts[] = s.split("/"); 355 int n, l = 1; 356 for (iter=1; iter < urlParts.length; iter++) { 357 n = iter-1; 358 hdf.setValue("parentdirs." + n + ".Name", urlParts[iter]); 359 hdf.setValue("parentdirs." + n + ".Link", subdir + "index" + Doclava.htmlExtension); 360 } 361 return hdf; 362 } 363 364 /** 365 * Writes a templated source code file to out. 366 */ writePage(File f, String out, String subdir, Data hdf)367 public void writePage(File f, String out, String subdir, Data hdf) { 368 String name = f.getName(); 369 String path = f.getPath(); 370 String data = SampleTagInfo.readFile(new SourcePositionInfo(path, -1, -1), path, 371 "sample code", true, true, true, true); 372 data = Doclava.escape(data); 373 374 String relative = subdir.replaceFirst("samples/", ""); 375 setParentDirs(hdf, subdir, name, true); 376 hdf.setValue("projectTitle", mTitle); 377 hdf.setValue("projectDir", mProjectDir); 378 hdf.setValue("page.title", name); 379 hdf.setValue("subdir", subdir); 380 hdf.setValue("relative", relative); 381 hdf.setValue("realFile", name); 382 hdf.setValue("fileContents", data); 383 hdf.setValue("resTag", "sample"); 384 385 ClearPage.write(hdf, "sample.cs", out); 386 } 387 388 /** 389 * Writes a templated image or video file to out. 390 */ writeImageVideoPage(File f, String out, String subdir, String resourceType, boolean browsable)391 public void writeImageVideoPage(File f, String out, String subdir, 392 String resourceType, boolean browsable) { 393 Data hdf = Doclava.makeHDF(); 394 if (Doclava.samplesNavTree != null) { 395 hdf.setValue("samples_toc_tree", Doclava.samplesNavTree.getValue("samples_toc_tree", "")); 396 } 397 hdf.setValue("samples", "true"); 398 399 String name = f.getName(); 400 if (!browsable) { 401 hdf.setValue("noDisplay", "true"); 402 } 403 setParentDirs(hdf, subdir, name, true); 404 hdf.setValue("samples", "true"); 405 hdf.setValue("page.title", name); 406 hdf.setValue("projectTitle", mTitle); 407 hdf.setValue("projectDir", mProjectDir); 408 hdf.setValue("subdir", subdir); 409 hdf.setValue("resType", resourceType); 410 hdf.setValue("realFile", name); 411 ClearPage.write(hdf, "sample.cs", out); 412 } 413 414 /** 415 * Given a node containing sample code projects and a node containing all valid 416 * group nodes, extract project nodes from tnode and append them to the group node 417 * that matches their sample.group metadata. 418 * 419 * @param tnode A list of nodes containing sample code projects. 420 * @param groupnodes A list of nodes that represent the valid sample groups. 421 * @return The groupnodes list with all projects appended properly to their 422 * associated sample groups. 423 */ writeSamplesNavTree(List<Node> tnode, List<Node> groupnodes)424 public static void writeSamplesNavTree(List<Node> tnode, List<Node> groupnodes) { 425 426 Node node = new Node.Builder().setLabel("Samples").setLink(ClearPage.toroot 427 + "samples/index" + Doclava.htmlExtension).setChildren(tnode).build(); 428 429 if (groupnodes != null) { 430 for (int i = 0; i < tnode.size(); i++) { 431 if (tnode.get(i) != null) { 432 groupnodes = appendNodeGroups(tnode.get(i), groupnodes); 433 } 434 } 435 for (int n = 0; n < groupnodes.size(); n++) { 436 if (groupnodes.get(n).getChildren() == null) { 437 groupnodes.remove(n); 438 n--; 439 } else { 440 Collections.sort(groupnodes.get(n).getChildren(), byLabel); 441 } 442 } 443 node.setChildren(groupnodes); 444 } 445 446 StringBuilder buf = new StringBuilder(); 447 if (Doclava.USE_DEVSITE_LOCALE_OUTPUT_PATHS) { 448 node.renderGroupNodesTOCYaml(buf, "", false); 449 } else { 450 node.renderGroupNodesTOC(buf); 451 } 452 if (Doclava.samplesNavTree != null) { 453 Doclava.samplesNavTree.setValue("samples_toc_tree", buf.toString()); 454 if (Doclava.USE_DEVSITE_LOCALE_OUTPUT_PATHS) { 455 ClearPage.write(Doclava.samplesNavTree, "samples_navtree_data.cs", "en/samples/_book.yaml"); 456 } 457 } 458 } 459 460 /** 461 * For a given project root node, get the group and then iterate the list of valid 462 * groups looking for a match. If found, append the project to that group node. 463 * Samples that reference a valid sample group tag are added to a list for that 464 * group. Samples declare a sample.group tag in their _index.jd files. 465 */ appendNodeGroups(Node gNode, List<Node> groupnodes)466 private static List<Node> appendNodeGroups(Node gNode, List<Node> groupnodes) { 467 List<Node> mgrouplist = new ArrayList<Node>(); 468 for (int i = 0; i < groupnodes.size(); i++) { 469 if (gNode.getGroup().equals(groupnodes.get(i).getLabel())) { 470 if (groupnodes.get(i).getChildren() == null) { 471 mgrouplist.add(gNode); 472 groupnodes.get(i).setChildren(mgrouplist); 473 } else { 474 groupnodes.get(i).getChildren().add(gNode); 475 } 476 break; 477 } 478 } 479 return groupnodes; 480 } 481 482 /** 483 * Sorts an array of files by type and name (alpha), with manifest always at top. 484 */ 485 Comparator<File> byTypeAndName = new Comparator<File>() { 486 public int compare (File one, File other) { 487 if (one.isDirectory() && !other.isDirectory()) { 488 return 1; 489 } else if (!one.isDirectory() && other.isDirectory()) { 490 return -1; 491 } else if ("AndroidManifest.xml".equals(one.getName())) { 492 return -1; 493 } else { 494 return one.compareTo(other); 495 } 496 } 497 }; 498 499 /** 500 * Sorts a list of Nodes by label. 501 */ 502 public static Comparator<Node> byLabel = new Comparator<Node>() { 503 public int compare(Node one, Node other) { 504 return one.getLabel().compareTo(other.getLabel()); 505 } 506 }; 507 508 /** 509 * Concatenates dirs that only hold dirs, to simplify nav tree 510 */ squashNodes(List<Node> tnode)511 public static List<Node> squashNodes(List<Node> tnode) { 512 List<Node> list = tnode; 513 514 for(int i = 0; i < list.size(); ++i) { 515 if (("dir".equals(list.get(i).getType())) && 516 (list.size() == 1) && 517 (list.get(i).getChildren().get(0).getChildren() != null)) { 518 String thisLabel = list.get(i).getLabel(); 519 String childLabel = list.get(i).getChildren().get(0).getLabel(); 520 String newLabel = thisLabel + "/" + childLabel; 521 list.get(i).setLabel(newLabel); 522 list.get(i).setChildren(list.get(i).getChildren().get(0).getChildren()); 523 } else { 524 continue; 525 } 526 } 527 return list; 528 } 529 convertExtension(String s, String ext)530 public static String convertExtension(String s, String ext) { 531 return s.substring(0, s.lastIndexOf('.')) + ext; 532 } 533 534 /** 535 * Whitelists of valid image/video and source code types. 536 */ 537 public static String[] IMAGES = {".png", ".jpg", ".gif"}; 538 public static String[] VIDEOS = {".mp4", ".ogv", ".webm"}; 539 public static String[] TEMPLATED = {".java", ".xml", ".aidl", ".rs",".txt", ".TXT"}; 540 inList(String s, String[] list)541 public static boolean inList(String s, String[] list) { 542 for (String t : list) { 543 if (s.endsWith(t)) { 544 return true; 545 } 546 } 547 return false; 548 } 549 550 /** 551 * Maps filenames to a set of generic types. Used for displaying files/dirs 552 * in the project view page. 553 */ mapTypes(String name)554 public static String mapTypes(String name) { 555 String type = name.substring(name.lastIndexOf('.') + 1, name.length()); 556 if ("xml".equals(type) || "java".equals(type)) { 557 if ("AndroidManifest.xml".equals(name)) type = "manifest"; 558 return type; 559 } else { 560 return type = "file"; 561 } 562 } 563 564 /** 565 * Validates a source file from a project against restrictions to determine 566 * whether to include the file in the browsable project output. 567 */ isValidFiletype(String name)568 public boolean isValidFiletype(String name) { 569 if (name.startsWith(".") || 570 name.startsWith("_") || 571 "default.properties".equals(name) || 572 "build.properties".equals(name) || 573 name.endsWith(".ttf") || 574 name.endsWith(".gradle") || 575 name.endsWith(".bat") || 576 "Android.mk".equals(name)) { 577 return false; 578 } else { 579 return true; 580 } 581 } 582 583 /** 584 * SampleCode variant of NavTree node. 585 */ 586 public static class Node { 587 private String mLabel; 588 private String mLink; 589 private String mGroup; // from sample.group in _index.jd 590 private List<Node> mChildren; 591 private String mType; 592 Node(Builder builder)593 private Node(Builder builder) { 594 mLabel = builder.mLabel; 595 mLink = builder.mLink; 596 mGroup = builder.mGroup; 597 mChildren = builder.mChildren; 598 mType = builder.mType; 599 } 600 601 public static class Builder { 602 private String mLabel, mLink, mGroup, mType; 603 private List<Node> mChildren = null; setLabel(String mLabel)604 public Builder setLabel(String mLabel) { this.mLabel = mLabel; return this;} setLink(String mLink)605 public Builder setLink(String mLink) { this.mLink = mLink; return this;} setGroup(String mGroup)606 public Builder setGroup(String mGroup) { this.mGroup = mGroup; return this;} setChildren(List<Node> mChildren)607 public Builder setChildren(List<Node> mChildren) { this.mChildren = mChildren; return this;} setType(String mType)608 public Builder setType(String mType) { this.mType = mType; return this;} build()609 public Node build() {return new Node(this);} 610 } 611 612 /** 613 * Renders browsable sample groups and projects to a _book.yaml file, starting 614 * from the group nodes and then rendering their project nodes and finally their 615 * child dirs and files. 616 */ renderGroupNodesTOCYaml(StringBuilder buf, String indent, Boolean isChild)617 void renderGroupNodesTOCYaml(StringBuilder buf, String indent, Boolean isChild) { 618 List<Node> list = mChildren; 619 if (list == null || list.size() == 0) { 620 return; 621 } else { 622 final int n = list.size(); 623 if (indent.length() > 0) { 624 buf.append(indent + "section:\n"); 625 } // else append 'toc:\n' if needed 626 for (int i = 0; i < n; i++) { 627 if (isChild == true && list.get(i).getChildren() != null) { 628 buf.append(indent + "- title: " + list.get(i).getLabel() + "/\n"); 629 if (list.get(i).getLink().indexOf(".html") > -1) { 630 buf.append(indent + " path: " + list.get(i).getLink() + "\n"); 631 buf.append(indent + " path_attributes:\n"); 632 buf.append(indent + " - name: title\n"); 633 buf.append(indent + " value: " + list.get(i).getLabel() + "\n"); 634 } else { 635 buf.append(indent + " path: \"#\"\n"); 636 buf.append(indent + " path_attributes:\n"); 637 buf.append(indent + " - name: onclick\n"); 638 buf.append(indent + " value: return false;\n"); 639 buf.append(indent + " - name: title\n"); 640 buf.append(indent + " value: " + list.get(i).getLabel() + "\n"); 641 } 642 } else { 643 String xmlToHtmlPath = list.get(i).getLink().replace(".xml", ".html"); 644 buf.append(indent + "- title: " + list.get(i).getLabel() + "\n"); 645 buf.append(indent + " path: " + xmlToHtmlPath + "\n"); 646 } 647 if (list.get(i).getChildren() != null) { 648 list.get(i).renderGroupNodesTOCYaml(buf, indent + " ", true); 649 } 650 } 651 } 652 } 653 654 /** 655 * Renders browsable sample groups and projects to an html list, starting 656 * from the group nodes and then rendering their project nodes and finally their 657 * child dirs and files. 658 */ renderGroupNodesTOC(StringBuilder buf)659 void renderGroupNodesTOC(StringBuilder buf) { 660 List<Node> list = mChildren; 661 if (list == null || list.size() == 0) { 662 return; 663 } else { 664 final int n = list.size(); 665 for (int i = 0; i < n; i++) { 666 if (list.get(i).getChildren() == null) { 667 continue; 668 } else { 669 buf.append("<li class=\"nav-section\">"); 670 buf.append("<div class=\"nav-section-header\">"); 671 buf.append("<a href=\"" + list.get(i).getLink() + "\" title=\"" 672 + list.get(i).getLabel() + "\">" 673 + list.get(i).getLabel() + "</a>"); 674 buf.append("</div>"); 675 buf.append("<ul>"); 676 list.get(i).renderProjectNodesTOC(buf); 677 } 678 } 679 buf.append("</ul>"); 680 buf.append("</li>"); 681 } 682 } 683 684 /** 685 * Renders a list of sample code projects associated with a group node. 686 */ renderProjectNodesTOC(StringBuilder buf)687 void renderProjectNodesTOC(StringBuilder buf) { 688 List<Node> list = mChildren; 689 if (list == null || list.size() == 0) { 690 return; 691 } else { 692 final int n = list.size(); 693 for (int i = 0; i < n; i++) { 694 if (list.get(i).getChildren() == null) { 695 continue; 696 } else { 697 buf.append("<li class=\"nav-section\">"); 698 buf.append("<div class=\"nav-section-header\">"); 699 buf.append("<a href=\"" + list.get(i).getLink() + "\" title=\"" 700 + list.get(i).getLabel() + "\">" 701 + list.get(i).getLabel() + "</a>"); 702 buf.append("</div>"); 703 buf.append("<ul>"); 704 list.get(i).renderChildrenToc(buf); 705 } 706 } 707 buf.append("</ul>"); 708 buf.append("</li>"); 709 } 710 } 711 712 /** 713 * Renders child dirs and files associated with a project node. 714 */ renderChildrenToc(StringBuilder buf)715 void renderChildrenToc(StringBuilder buf) { 716 List<Node> list = mChildren; 717 if (list == null || list.size() == 0) { 718 buf.append("null"); 719 } else { 720 final int n = list.size(); 721 for (int i = 0; i < n; i++) { 722 if (list.get(i).getChildren() == null) { 723 buf.append("<li>"); 724 buf.append("<a href=\"" + list.get(i).getLink() + "\" title=\"" 725 + list.get(i).getLabel() + "\">" 726 + list.get(i).getLabel() + "</a>"); 727 buf.append(" </li>"); 728 } else { 729 buf.append("<li class=\"nav-section sticky\">"); 730 buf.append("<div class=\"nav-section-header empty\">"); 731 buf.append("<a href=\"#\" onclick=\"return false;\" title=\"" 732 + list.get(i).getLabel() + "\">" 733 + list.get(i).getLabel() + "/</a>"); 734 buf.append("</div>"); 735 buf.append("<ul>"); 736 list.get(i).renderChildrenToc(buf); 737 } 738 } 739 buf.append("</ul>"); 740 buf.append("</li>"); 741 } 742 } 743 744 /** 745 * Node getters and setters 746 */ getLabel()747 public String getLabel() { 748 return mLabel; 749 } 750 setLabel(String label)751 public void setLabel(String label) { 752 mLabel = label; 753 } 754 getLink()755 public String getLink() { 756 return mLink; 757 } 758 setLink(String ref)759 public void setLink(String ref) { 760 mLink = ref; 761 } 762 getGroup()763 public String getGroup() { 764 return mGroup; 765 } 766 setGroup(String group)767 public void setGroup(String group) { 768 mGroup = group; 769 } 770 getChildren()771 public List<Node> getChildren() { 772 return mChildren; 773 } 774 setChildren(List<Node> node)775 public void setChildren(List<Node> node) { 776 mChildren = node; 777 } 778 getType()779 public String getType() { 780 return mType; 781 } 782 setType(String type)783 public void setType(String type) { 784 mType = type; 785 } 786 } 787 } 788