SampleCode.java revision e9ab1b94fc0414fe72646b90b355562abf98d86e
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 17package com.google.doclava; 18 19import java.util.Arrays; 20import java.util.ArrayList; 21import java.util.Collections; 22import java.util.Comparator; 23import java.util.List; 24import java.util.regex.Pattern; 25import java.util.regex.Matcher; 26import java.io.File; 27 28import 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*/ 34public 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 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 */ 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 */ 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 */ 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 */ 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 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 path = relative + name; 204 type = mapTypes(name); 205 link = convertExtension(path, Doclava.htmlExtension); 206 if (inList(path, IMAGES)) { 207 type = "img"; 208 if (f.length() < MAX_FILE_SIZE_BYTES) { 209 ClearPage.copyFile(false, f, path); 210 writeImageVideoPage(f, convertExtension(path, Doclava.htmlExtension), 211 relative, type, true); 212 } else { 213 writeImageVideoPage(f, convertExtension(path, Doclava.htmlExtension), 214 relative, type, false); 215 } 216 hdf.setValue(key + i + ".Type", "img"); 217 hdf.setValue(key + i + ".Name", name); 218 hdf.setValue(key + i + ".Href", link); 219 hdf.setValue(key + i + ".RelPath", relative); 220 } else if (inList(path, VIDEOS)) { 221 type = "video"; 222 if (f.length() < MAX_FILE_SIZE_BYTES) { 223 ClearPage.copyFile(false, f, path); 224 writeImageVideoPage(f, convertExtension(path, Doclava.htmlExtension), 225 relative, type, true); 226 } else { 227 writeImageVideoPage(f, convertExtension(path, Doclava.htmlExtension), 228 relative, type, false); 229 } 230 hdf.setValue(key + i + ".Type", "video"); 231 hdf.setValue(key + i + ".Name", name); 232 hdf.setValue(key + i + ".Href", link); 233 hdf.setValue(key + i + ".RelPath", relative); 234 } else if (inList(path, TEMPLATED)) { 235 writePage(f, convertExtension(path, Doclava.htmlExtension), relative, hdf); 236 hdf.setValue(key + i + ".Type", type); 237 hdf.setValue(key + i + ".Name", name); 238 hdf.setValue(key + i + ".Href", link); 239 hdf.setValue(key + i + ".RelPath", relative); 240 } 241 i++; 242 } else if (f.isDirectory()) { 243 List<Node> mchildren = new ArrayList<Node>(); 244 type = "dir"; 245 String dirpath = relative + name; 246 link = dirpath + "/index" + Doclava.htmlExtension; 247 String hdfkeyName = (key + i + ".Name"); 248 String hdfkeyType = (key + i + ".Type"); 249 String hdfkeyHref = (key + i + ".Href"); 250 hdf.setValue(hdfkeyName, name); 251 hdf.setValue(hdfkeyType, type); 252 hdf.setValue(hdfkeyHref, relative + name + "/" + "index" + Doclava.htmlExtension); 253 writeProjectDirectory(f, relative + name + "/", true, hdf, (key + i)); 254 i++; 255 } 256 } 257 258 setParentDirs(hdf, relative, name, false); 259 //Generate an index.html page for each dir being processed 260 if (FULL_TREE_NAVIGATION) { 261 ClearPage.write(hdf, "sampleindex.cs", relative + "/index" + Doclava.htmlExtension); 262 } 263 } 264 265 /** 266 * Processes a templated project index page from _index.jd in a project root. 267 * Each sample project must have an index, and each index locally defines it's own 268 * page.tags and sample.group cs vars. This method takes a SC node on input, reads 269 * any local vars from the _index.jd, optionally generates an html file to out, 270 * then updates the SC node with the page vars and returns it to the caller. 271 * 272 * @param hdf The data source to read/write for this index file. 273 * @param dir The sample project root directory. 274 * @param tnode A Node to serve as the project's root node. 275 * @param writeFiles If true, generates output files only. If false, collects 276 * metadata only. 277 * @return The tnode root with any metadata/child Nodes appended. 278 */ 279 public Node writeSampleIndexCs(Data hdf, File dir, Node tnode, boolean writeFiles) { 280 281 String filename = dir.getAbsolutePath() + "/_index.jd"; 282 String mGroup = ""; 283 File f = new File(filename); 284 String rel = dir.getPath(); 285 if (writeFiles) { 286 287 hdf.setValue("samples", "true"); 288 //set any default page variables for root index 289 hdf.setValue("page.title", mProjectDir); 290 hdf.setValue("projectDir", mProjectDir); 291 hdf.setValue("projectTitle", mTitle); 292 //add the download/project links to the landing pages. 293 hdf.setValue("samplesProjectIndex", "true"); 294 if (!f.isFile()) { 295 //The directory didn't have an _index.jd, so create a stub. 296 ClearPage.write(hdf, "sampleindex.cs", mDest + "index" + Doclava.htmlExtension); 297 } else { 298 DocFile.writePage(filename, rel, mDest + "index" + Doclava.htmlExtension, hdf); 299 PageMetadata.setPageMetadata(f, rel, mDest + "index" + Doclava.htmlExtension, 300 hdf, Doclava.sTaglist); 301 } 302 } else if (f.isFile()) { 303 //gather metadata for toc and jd_lists_unified 304 DocFile.getPageMetadata(filename, hdf); 305 mGroup = hdf.getValue("sample.group", ""); 306 if (!"".equals(mGroup)) { 307 tnode.setGroup(hdf.getValue("sample.group", "")); 308 } else { 309 //Errors.error(Errors.INVALID_SAMPLE_INDEX, null, "Sample " + mProjectDir 310 // + ": Root _index.jd must be present and must define sample.group" 311 // + " tag. Please see ... for details."); 312 } 313 } 314 return tnode; 315 } 316 317 /** 318 * Sets metadata for managing html output and generates the project view page 319 * for a project. 320 * 321 * @param dir The project root dir. 322 * @param hdf The data to read/write for files in this project. 323 */ 324 public void writeProjectStructure(String dir, Data hdf) { 325 hdf.setValue("projectStructure", "true"); 326 hdf.setValue("projectDir", mProjectDir); 327 hdf.setValue("page.title", mProjectDir + " Structure"); 328 hdf.setValue("projectTitle", mTitle); 329 ClearPage.write(hdf, "sampleindex.cs", mDest + "project" + Doclava.htmlExtension); 330 hdf.setValue("projectStructure", ""); 331 } 332 333 /** 334 * Keeps track of each file's parent dirs. Used for generating path breadcrumbs in html. 335 * 336 * @param dir The data to read/write for this file. 337 * @param hdf The relative path for this file, from samples root. 338 * @param subdir The relative path for this file, from samples root. 339 * @param name The name of the file (minus extension). 340 * @param isFile Whether this is a file (not a dir). 341 */ 342 Data setParentDirs(Data hdf, String subdir, String name, Boolean isFile) { 343 if (FULL_TREE_NAVIGATION) { 344 hdf.setValue("linkfyPathCrumb", ""); 345 } 346 int iter; 347 hdf.removeTree("parentdirs"); 348 String s = subdir; 349 String urlParts[] = s.split("/"); 350 int n, l = 1; 351 for (iter=1; iter < urlParts.length; iter++) { 352 n = iter-1; 353 hdf.setValue("parentdirs." + n + ".Name", urlParts[iter]); 354 hdf.setValue("parentdirs." + n + ".Link", subdir + "index" + Doclava.htmlExtension); 355 } 356 return hdf; 357 } 358 359 /** 360 * Writes a templated source code file to out. 361 */ 362 public void writePage(File f, String out, String subdir, Data hdf) { 363 String name = f.getName(); 364 String path = f.getPath(); 365 String data = SampleTagInfo.readFile(new SourcePositionInfo(path, -1, -1), path, 366 "sample code", true, true, true, true); 367 data = Doclava.escape(data); 368 369 String relative = subdir.replaceFirst("samples/", ""); 370 setParentDirs(hdf, subdir, name, true); 371 hdf.setValue("projectTitle", mTitle); 372 hdf.setValue("projectDir", mProjectDir); 373 hdf.setValue("page.title", name); 374 hdf.setValue("subdir", subdir); 375 hdf.setValue("relative", relative); 376 hdf.setValue("realFile", name); 377 hdf.setValue("fileContents", data); 378 hdf.setValue("resTag", "sample"); 379 380 ClearPage.write(hdf, "sample.cs", out); 381 } 382 383 /** 384 * Writes a templated image or video file to out. 385 */ 386 public void writeImageVideoPage(File f, String out, String subdir, 387 String resourceType, boolean browsable) { 388 Data hdf = Doclava.makeHDF(); 389 if (Doclava.samplesNavTree != null) { 390 hdf.setValue("samples_toc_tree", Doclava.samplesNavTree.getValue("samples_toc_tree", "")); 391 } 392 hdf.setValue("samples", "true"); 393 394 String name = f.getName(); 395 if (!browsable) { 396 hdf.setValue("noDisplay", "true"); 397 } 398 setParentDirs(hdf, subdir, name, true); 399 hdf.setValue("samples", "true"); 400 hdf.setValue("page.title", name); 401 hdf.setValue("projectTitle", mTitle); 402 hdf.setValue("projectDir", mProjectDir); 403 hdf.setValue("subdir", subdir); 404 hdf.setValue("resType", resourceType); 405 hdf.setValue("realFile", name); 406 407 ClearPage.write(hdf, "sample.cs", out); 408 } 409 410 /** 411 * Given a node containing sample code projects and a node containing all valid 412 * group nodes, extract project nodes from tnode and append them to the group node 413 * that matches their sample.group metadata. 414 * 415 * @param tnode A list of nodes containing sample code projects. 416 * @param groupnodes A list of nodes that represent the valid sample groups. 417 * @return The groupnodes list with all projects appended properly to their 418 * associated sample groups. 419 */ 420 public static void writeSamplesNavTree(List<Node> tnode, List<Node> groupnodes) { 421 422 Node node = new Node.Builder().setLabel("Samples").setLink(ClearPage.toroot 423 + "samples/index" + Doclava.htmlExtension).setChildren(tnode).build(); 424 425 if (groupnodes != null) { 426 for (int i = 0; i < tnode.size(); i++) { 427 if (tnode.get(i) != null) { 428 groupnodes = appendNodeGroups(tnode.get(i), groupnodes); 429 } 430 } 431 for (int n = 0; n < groupnodes.size(); n++) { 432 if (groupnodes.get(n).getChildren() == null) { 433 groupnodes.remove(n); 434 n--; 435 } else { 436 Collections.sort(groupnodes.get(n).getChildren(), byLabel); 437 } 438 } 439 node.setChildren(groupnodes); 440 } 441 442 StringBuilder buf = new StringBuilder(); 443 node.renderGroupNodesTOC(buf); 444 if (Doclava.samplesNavTree != null) { 445 Doclava.samplesNavTree.setValue("samples_toc_tree", buf.toString()); 446 } 447 448 } 449 450 /** 451 * For a given project root node, get the group and then iterate the list of valid 452 * groups looking for a match. If found, append the project to that group node. 453 * Samples that reference a valid sample group tag are added to a list for that 454 * group. Samples declare a sample.group tag in their _index.jd files. 455 */ 456 private static List<Node> appendNodeGroups(Node gNode, List<Node> groupnodes) { 457 List<Node> mgrouplist = new ArrayList<Node>(); 458 for (int i = 0; i < groupnodes.size(); i++) { 459 if (gNode.getGroup().equals(groupnodes.get(i).getLabel())) { 460 if (groupnodes.get(i).getChildren() == null) { 461 mgrouplist.add(gNode); 462 groupnodes.get(i).setChildren(mgrouplist); 463 } else { 464 groupnodes.get(i).getChildren().add(gNode); 465 } 466 break; 467 } 468 } 469 return groupnodes; 470 } 471 472 /** 473 * Sorts an array of files by type and name (alpha), with manifest always at top. 474 */ 475 Comparator<File> byTypeAndName = new Comparator<File>() { 476 public int compare (File one, File other) { 477 if (one.isDirectory() && !other.isDirectory()) { 478 return 1; 479 } else if (!one.isDirectory() && other.isDirectory()) { 480 return -1; 481 } else if ("AndroidManifest.xml".equals(one.getName())) { 482 return -1; 483 } else { 484 return one.compareTo(other); 485 } 486 } 487 }; 488 489 /** 490 * Sorts a list of Nodes by label. 491 */ 492 public static Comparator<Node> byLabel = new Comparator<Node>() { 493 public int compare(Node one, Node other) { 494 return one.getLabel().compareTo(other.getLabel()); 495 } 496 }; 497 498 /** 499 * Concatenates dirs that only hold dirs, to simplify nav tree 500 */ 501 public static List<Node> squashNodes(List<Node> tnode) { 502 List<Node> list = tnode; 503 504 for(int i = 0; i < list.size(); ++i) { 505 if (("dir".equals(list.get(i).getType())) && 506 (list.size() == 1) && 507 (list.get(i).getChildren().get(0).getChildren() != null)) { 508 String thisLabel = list.get(i).getLabel(); 509 String childLabel = list.get(i).getChildren().get(0).getLabel(); 510 String newLabel = thisLabel + "/" + childLabel; 511 list.get(i).setLabel(newLabel); 512 list.get(i).setChildren(list.get(i).getChildren().get(0).getChildren()); 513 } else { 514 continue; 515 } 516 } 517 return list; 518 } 519 520 public static String convertExtension(String s, String ext) { 521 return s.substring(0, s.lastIndexOf('.')) + ext; 522 } 523 524 /** 525 * Whitelists of valid image/video and source code types. 526 */ 527 public static String[] IMAGES = {".png", ".jpg", ".gif"}; 528 public static String[] VIDEOS = {".mp4", ".ogv", ".webm"}; 529 public static String[] TEMPLATED = {".java", ".xml", ".aidl", ".rs",".txt", ".TXT"}; 530 531 public static boolean inList(String s, String[] list) { 532 for (String t : list) { 533 if (s.endsWith(t)) { 534 return true; 535 } 536 } 537 return false; 538 } 539 540 /** 541 * Maps filenames to a set of generic types. Used for displaying files/dirs 542 * in the project view page. 543 */ 544 public static String mapTypes(String name) { 545 String type = name.substring(name.lastIndexOf('.') + 1, name.length()); 546 if ("xml".equals(type) || "java".equals(type)) { 547 if ("AndroidManifest.xml".equals(name)) type = "manifest"; 548 return type; 549 } else { 550 return type = "file"; 551 } 552 } 553 554 /** 555 * Validates a source file from a project against restrictions to determine 556 * whether to include the file in the browsable project output. 557 */ 558 public boolean isValidFiletype(String name) { 559 if (name.startsWith(".") || 560 name.startsWith("_") || 561 "default.properties".equals(name) || 562 "build.properties".equals(name) || 563 name.endsWith(".ttf") || 564 name.endsWith(".gradle") || 565 name.endsWith(".bat") || 566 "Android.mk".equals(name)) { 567 return false; 568 } else { 569 return true; 570 } 571 } 572 573 /** 574 * SampleCode variant of NavTree node. 575 */ 576 public static class Node { 577 private String mLabel; 578 private String mLink; 579 private String mGroup; // from sample.group in _index.jd 580 private List<Node> mChildren; 581 private String mType; 582 583 private Node(Builder builder) { 584 mLabel = builder.mLabel; 585 mLink = builder.mLink; 586 mGroup = builder.mGroup; 587 mChildren = builder.mChildren; 588 mType = builder.mType; 589 } 590 591 public static class Builder { 592 private String mLabel, mLink, mGroup, mType; 593 private List<Node> mChildren = null; 594 public Builder setLabel(String mLabel) { this.mLabel = mLabel; return this;} 595 public Builder setLink(String mLink) { this.mLink = mLink; return this;} 596 public Builder setGroup(String mGroup) { this.mGroup = mGroup; return this;} 597 public Builder setChildren(List<Node> mChildren) { this.mChildren = mChildren; return this;} 598 public Builder setType(String mType) { this.mType = mType; return this;} 599 public Node build() {return new Node(this);} 600 } 601 602 /** 603 * Renders browsable sample groups and projects to an html list, starting 604 * from the group nodes and then rendering their project nodes and finally their 605 * child dirs and files. 606 */ 607 void renderGroupNodesTOC(StringBuilder buf) { 608 List<Node> list = mChildren; 609 if (list == null || list.size() == 0) { 610 return; 611 } else { 612 final int n = list.size(); 613 for (int i = 0; i < n; i++) { 614 if (list.get(i).getChildren() == null) { 615 continue; 616 } else { 617 buf.append("<li class=\"nav-section\">"); 618 buf.append("<div class=\"nav-section-header\">"); 619 buf.append("<a href=\"" + list.get(i).getLink() + "\" title=\"" 620 + list.get(i).getLabel() + "\">" 621 + list.get(i).getLabel() + "</a>"); 622 buf.append("</div>"); 623 buf.append("<ul>"); 624 list.get(i).renderProjectNodesTOC(buf); 625 } 626 } 627 buf.append("</ul>"); 628 buf.append("</li>"); 629 } 630 } 631 632 /** 633 * Renders a list of sample code projects associated with a group node. 634 */ 635 void renderProjectNodesTOC(StringBuilder buf) { 636 List<Node> list = mChildren; 637 if (list == null || list.size() == 0) { 638 return; 639 } else { 640 final int n = list.size(); 641 for (int i = 0; i < n; i++) { 642 if (list.get(i).getChildren() == null) { 643 continue; 644 } else { 645 buf.append("<li class=\"nav-section\">"); 646 buf.append("<div class=\"nav-section-header\">"); 647 buf.append("<a href=\"" + list.get(i).getLink() + "\" title=\"" 648 + list.get(i).getLabel() + "\">" 649 + list.get(i).getLabel() + "</a>"); 650 buf.append("</div>"); 651 buf.append("<ul>"); 652 list.get(i).renderChildrenToc(buf); 653 } 654 } 655 buf.append("</ul>"); 656 buf.append("</li>"); 657 } 658 } 659 660 /** 661 * Renders child dirs and files associated with a project node. 662 */ 663 void renderChildrenToc(StringBuilder buf) { 664 List<Node> list = mChildren; 665 if (list == null || list.size() == 0) { 666 buf.append("null"); 667 } else { 668 final int n = list.size(); 669 for (int i = 0; i < n; i++) { 670 if (list.get(i).getChildren() == null) { 671 buf.append("<li>"); 672 buf.append("<a href=\"" + list.get(i).getLink() + "\" title=\"" 673 + list.get(i).getLabel() + "\">" 674 + list.get(i).getLabel() + "</a>"); 675 buf.append(" </li>"); 676 } else { 677 buf.append("<li class=\"nav-section sticky\">"); 678 buf.append("<div class=\"nav-section-header empty\">"); 679 buf.append("<a href=\"#\" onclick=\"return false;\" title=\"" 680 + list.get(i).getLabel() + "\">" 681 + list.get(i).getLabel() + "/</a>"); 682 buf.append("</div>"); 683 buf.append("<ul>"); 684 list.get(i).renderChildrenToc(buf); 685 } 686 } 687 buf.append("</ul>"); 688 buf.append("</li>"); 689 } 690 } 691 692 /** 693 * Node getters and setters 694 */ 695 public String getLabel() { 696 return mLabel; 697 } 698 699 public void setLabel(String label) { 700 mLabel = label; 701 } 702 703 public String getLink() { 704 return mLink; 705 } 706 707 public void setLink(String ref) { 708 mLink = ref; 709 } 710 711 public String getGroup() { 712 return mGroup; 713 } 714 715 public void setGroup(String group) { 716 mGroup = group; 717 } 718 719 public List<Node> getChildren() { 720 return mChildren; 721 } 722 723 public void setChildren(List<Node> node) { 724 mChildren = node; 725 } 726 727 public String getType() { 728 return mType; 729 } 730 731 public void setType(String type) { 732 mType = type; 733 } 734 } 735} 736