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 com.google.clearsilver.jsilver.data.Data; 20 21 import java.util.ArrayList; 22 import java.util.Collection; 23 import java.util.List; 24 import java.util.SortedMap; 25 import java.util.TreeMap; 26 27 public class NavTree { 28 29 /* @deprecated This method was used for an older version of DAC, circa 2012, retired May 2018 */ writeNavTree(String dir, String refPrefix)30 public static void writeNavTree(String dir, String refPrefix) { 31 List<Node> children = new ArrayList<Node>(); 32 for (PackageInfo pkg : Doclava.choosePackages()) { 33 children.add(makePackageNode(pkg)); 34 } 35 Node node = new Node("Reference", dir + refPrefix + "packages.html", children, null); 36 37 StringBuilder buf = new StringBuilder(); 38 if (false) { 39 // if you want a root node 40 buf.append("["); 41 node.render(buf); 42 buf.append("]"); 43 } else { 44 // if you don't want a root node 45 node.renderChildren(buf); 46 } 47 48 Data data = Doclava.makeHDF(); 49 data.setValue("reference_tree", buf.toString()); 50 if (refPrefix == "gms-"){ 51 ClearPage.write(data, "gms_navtree_data.cs", "gms_navtree_data.js"); 52 } else if (refPrefix == "gcm-"){ 53 ClearPage.write(data, "gcm_navtree_data.cs", "gcm_navtree_data.js"); 54 } else if (Doclava.USE_DEVSITE_LOCALE_OUTPUT_PATHS && (Doclava.libraryRoot != null)) { 55 ClearPage.write(data, "navtree_data.cs", dir + Doclava.libraryRoot 56 + "navtree_data.js"); 57 } else { 58 ClearPage.write(data, "navtree_data.cs", "navtree_data.js"); 59 } 60 } 61 62 /** 63 * Write the YAML formatted navigation tree. 64 * This is intended to replace writeYamlTree(), but for now requires an explicit opt-in via 65 * the yamlV2 flag in the doclava command. This version creates a yaml file with all classes, 66 * interface, exceptions, etc. separated into collapsible groups. 67 */ writeYamlTree2(String dir, String fileName)68 public static void writeYamlTree2(String dir, String fileName){ 69 List<Node> children = new ArrayList<Node>(); 70 for (PackageInfo pkg : Doclava.choosePackages()) { 71 children.add(makePackageNode(pkg)); 72 } 73 Node node = new Node("Reference", Doclava.ensureSlash(dir) + "packages.html", children, null); 74 StringBuilder buf = new StringBuilder(); 75 76 node.renderChildrenYaml(buf, 0); 77 78 Data data = Doclava.makeHDF(); 79 data.setValue("reference_tree", buf.toString()); 80 81 if (Doclava.USE_DEVSITE_LOCALE_OUTPUT_PATHS && (Doclava.libraryRoot != null)) { 82 dir = Doclava.ensureSlash(dir) + Doclava.libraryRoot; 83 } 84 85 data.setValue("docs.classes.link", Doclava.ensureSlash(dir) + "classes.html"); 86 data.setValue("docs.packages.link", Doclava.ensureSlash(dir) + "packages.html"); 87 88 ClearPage.write(data, "yaml_navtree2.cs", Doclava.ensureSlash(dir) + fileName); 89 90 } 91 92 93 /** 94 * Write the YAML formatted navigation tree (legacy version). 95 * This creates a yaml file with package names followed by all 96 * classes, interfaces, exceptions, etc. But they are not separated by classes, interfaces, etc. 97 * It also nests any nested classes under the parent class, instead of listing them as siblings. 98 * @see "http://yaml.org/" 99 */ writeYamlTree(String dir, String fileName)100 public static void writeYamlTree(String dir, String fileName){ 101 Data data = Doclava.makeHDF(); 102 Collection<ClassInfo> classes = Converter.rootClasses(); 103 104 SortedMap<String, Object> sorted = new TreeMap<String, Object>(); 105 for (ClassInfo cl : classes) { 106 if (cl.isHiddenOrRemoved()) { 107 continue; 108 } 109 sorted.put(cl.qualifiedName(), cl); 110 111 PackageInfo pkg = cl.containingPackage(); 112 String name; 113 if (pkg == null) { 114 name = ""; 115 } else { 116 name = pkg.name(); 117 } 118 sorted.put(name, pkg); 119 } 120 121 data = makeYamlHDF(sorted, "docs.pages", data); 122 123 if (Doclava.USE_DEVSITE_LOCALE_OUTPUT_PATHS && (Doclava.libraryRoot != null)) { 124 dir = Doclava.ensureSlash(dir) + Doclava.libraryRoot; 125 } 126 127 data.setValue("docs.classes.link", Doclava.ensureSlash(dir) + "classes.html"); 128 data.setValue("docs.packages.link", Doclava.ensureSlash(dir) + "packages.html"); 129 130 ClearPage.write(data, "yaml_navtree.cs", Doclava.ensureSlash(dir) + fileName); 131 } 132 makeYamlHDF(SortedMap<String, Object> sorted, String base, Data data)133 public static Data makeYamlHDF(SortedMap<String, Object> sorted, String base, Data data) { 134 135 String key = "docs.pages."; 136 int i = 0; 137 for (String s : sorted.keySet()) { 138 Object o = sorted.get(s); 139 140 if (o instanceof PackageInfo) { 141 PackageInfo pkg = (PackageInfo) o; 142 143 data.setValue("docs.pages." + i + ".id", "" + i); 144 data.setValue("docs.pages." + i + ".label", pkg.name()); 145 data.setValue("docs.pages." + i + ".shortname", "API"); 146 data.setValue("docs.pages." + i + ".apilevel", pkg.getSince()); 147 data.setValue("docs.pages." + i + ".link", pkg.htmlPage()); 148 data.setValue("docs.pages." + i + ".type", "package"); 149 } else if (o instanceof ClassInfo) { 150 ClassInfo cl = (ClassInfo) o; 151 152 // skip classes that are the child of another class, recursion will handle those. 153 if (cl.containingClass() == null) { 154 155 data.setValue("docs.pages." + i + ".id", "" + i); 156 data = makeYamlHDF(cl, "docs.pages."+i, data); 157 } 158 } 159 160 i++; 161 } 162 163 return data; 164 } 165 makeYamlHDF(ClassInfo cl, String base, Data data)166 public static Data makeYamlHDF(ClassInfo cl, String base, Data data) { 167 data.setValue(base + ".label", cl.name()); 168 data.setValue(base + ".shortname", cl.name().substring(cl.name().lastIndexOf(".")+1)); 169 data.setValue(base + ".link", cl.htmlPage()); 170 data.setValue(base + ".type", cl.kind()); 171 172 if (cl.innerClasses().size() > 0) { 173 int j = 0; 174 for (ClassInfo cl2 : cl.innerClasses()) { 175 if (cl2.isHiddenOrRemoved()) { 176 continue; 177 } 178 data = makeYamlHDF(cl2, base + ".children." + j, data); 179 j++; 180 } 181 } 182 183 return data; 184 } 185 makePackageNode(PackageInfo pkg)186 private static Node makePackageNode(PackageInfo pkg) { 187 List<Node> children = new ArrayList<Node>(); 188 189 addClassNodes(children, "Annotations", pkg.annotations()); 190 addClassNodes(children, "Interfaces", pkg.interfaces()); 191 addClassNodes(children, "Classes", pkg.ordinaryClasses()); 192 addClassNodes(children, "Enums", pkg.enums()); 193 addClassNodes(children, "Exceptions", pkg.exceptions()); 194 addClassNodes(children, "Errors", pkg.errors()); 195 196 return new Node(pkg.name(), pkg.htmlPage(), children, pkg.getSince()); 197 } 198 addClassNodes(List<Node> parent, String label, ClassInfo[] classes)199 private static void addClassNodes(List<Node> parent, String label, ClassInfo[] classes) { 200 List<Node> children = new ArrayList<Node>(); 201 202 for (ClassInfo cl : classes) { 203 if (cl.checkLevel()) { 204 children.add(new Node(cl.name(), cl.htmlPage(), null, cl.getSince(), cl.getArtifact())); 205 } 206 } 207 208 if (children.size() > 0) { 209 parent.add(new Node(label, null, children, null)); 210 } 211 } 212 213 private static class Node { 214 private String mLabel; 215 private String mLink; 216 List<Node> mChildren; 217 private String mSince; 218 private String mArtifact; 219 Node(String label, String link, List<Node> children, String since)220 Node(String label, String link, List<Node> children, String since) { 221 this(label, link, children, since, null); 222 } 223 Node(String label, String link, List<Node> children, String since, String artifact)224 Node(String label, String link, List<Node> children, String since, String artifact) { 225 mLabel = label; 226 mLink = link; 227 mChildren = children; 228 mSince = since; 229 mArtifact = artifact; 230 } 231 renderString(StringBuilder buf, String s)232 static void renderString(StringBuilder buf, String s) { 233 if (s == null) { 234 buf.append("null"); 235 } else { 236 buf.append('"'); 237 final int N = s.length(); 238 for (int i = 0; i < N; i++) { 239 char c = s.charAt(i); 240 if (c >= ' ' && c <= '~' && c != '"' && c != '\\') { 241 buf.append(c); 242 } else { 243 buf.append("\\u"); 244 for (int j = 0; i < 4; i++) { 245 char x = (char) (c & 0x000f); 246 if (x >= 10) { 247 x = (char) (x - 10 + 'a'); 248 } else { 249 x = (char) (x + '0'); 250 } 251 buf.append(x); 252 c >>= 4; 253 } 254 } 255 } 256 buf.append('"'); 257 } 258 } 259 renderChildren(StringBuilder buf)260 void renderChildren(StringBuilder buf) { 261 List<Node> list = mChildren; 262 if (list == null || list.size() == 0) { 263 // We output null for no children. That way empty lists here can just 264 // be a byproduct of how we generate the lists. 265 buf.append("null"); 266 } else { 267 buf.append("[ "); 268 final int N = list.size(); 269 for (int i = 0; i < N; i++) { 270 list.get(i).render(buf); 271 if (i != N - 1) { 272 buf.append(", "); 273 } 274 } 275 buf.append(" ]\n"); 276 } 277 } 278 render(StringBuilder buf)279 void render(StringBuilder buf) { 280 buf.append("[ "); 281 renderString(buf, mLabel); 282 buf.append(", "); 283 renderString(buf, mLink); 284 buf.append(", "); 285 renderChildren(buf); 286 buf.append(", "); 287 renderString(buf, mSince); 288 buf.append(", "); 289 renderString(buf, mArtifact); 290 buf.append(" ]"); 291 } 292 293 294 // YAML VERSION 295 296 renderStringYaml(StringBuilder buf, String s)297 static void renderStringYaml(StringBuilder buf, String s) { 298 if (s != null) { 299 final int N = s.length(); 300 for (int i = 0; i < N; i++) { 301 char c = s.charAt(i); 302 if (c >= ' ' && c <= '~' && c != '"' && c != '\\') { 303 buf.append(c); 304 } else { 305 buf.append("\\u"); 306 for (int j = 0; i < 4; i++) { 307 char x = (char) (c & 0x000f); 308 if (x >= 10) { 309 x = (char) (x - 10 + 'a'); 310 } else { 311 x = (char) (x + '0'); 312 } 313 buf.append(x); 314 c >>= 4; 315 } 316 } 317 } 318 } 319 } renderChildrenYaml(StringBuilder buf, int depth)320 void renderChildrenYaml(StringBuilder buf, int depth) { 321 List<Node> list = mChildren; 322 if (list != null && list.size() > 0) { 323 if (depth > 0) { 324 buf.append("\n\n" + getIndent(depth)); 325 buf.append("section:"); 326 } 327 final int N = list.size(); 328 for (int i = 0; i < N; i++) { 329 // get each child Node and render it 330 list.get(i).renderYaml(buf, depth); 331 } 332 // Extra line break after each "section" 333 buf.append("\n"); 334 } 335 } renderYaml(StringBuilder buf, int depth)336 void renderYaml(StringBuilder buf, int depth) { 337 buf.append("\n" + getIndent(depth)); 338 buf.append("- title: \""); 339 renderStringYaml(buf, mLabel); 340 buf.append("\""); 341 // Add link path, if it exists (the class/interface toggles don't have links) 342 if (mLink != null) { 343 buf.append("\n" + getIndent(depth)); 344 buf.append(" path: "); 345 renderStringYaml(buf, "/" + mLink); 346 // add the API level info only if we have it 347 if (mSince != null) { 348 buf.append("\n" + getIndent(depth)); 349 buf.append(" version_added: "); 350 renderStringYaml(buf, "'" + mSince + "'"); 351 } 352 } 353 // try rendering child Nodes 354 renderChildrenYaml(buf, depth + 1); 355 } getIndent(int depth)356 String getIndent(int depth) { 357 String spaces = ""; 358 for (int i = 0; i < depth; i++) { 359 spaces += " "; 360 } 361 return spaces; 362 } 363 } 364 }