• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 }