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 com.google.clearsilver.jsilver.data.Data;
20
21import java.util.ArrayList;
22import java.util.List;
23import java.util.SortedMap;
24import java.util.TreeMap;
25
26public class NavTree {
27
28  public static void writeNavTree(String dir, String refPrefix) {
29    List<Node> children = new ArrayList<Node>();
30    for (PackageInfo pkg : Doclava.choosePackages()) {
31      children.add(makePackageNode(pkg));
32    }
33    Node node = new Node("Reference", dir + refPrefix + "packages.html", children, null);
34
35    StringBuilder buf = new StringBuilder();
36    if (false) {
37      // if you want a root node
38      buf.append("[");
39      node.render(buf);
40      buf.append("]");
41    } else {
42      // if you don't want a root node
43      node.renderChildren(buf);
44    }
45
46    Data data = Doclava.makeHDF();
47    data.setValue("reference_tree", buf.toString());
48    if (refPrefix == "gms-"){
49      ClearPage.write(data, "gms_navtree_data.cs", "gms_navtree_data.js");
50    } else if (refPrefix == "gcm-"){
51      ClearPage.write(data, "gcm_navtree_data.cs", "gcm_navtree_data.js");
52    } else if ((Doclava.USE_DEVSITE_LOCALE_OUTPUT_PATHS)
53          && (Doclava.testSupportRef)){
54        ClearPage.write(data, "navtree_data.cs", dir + Doclava.testSupportPath
55          + "navtree_data.js");
56    } else if ((Doclava.USE_DEVSITE_LOCALE_OUTPUT_PATHS)
57          && (Doclava.wearableSupportRef)){
58        ClearPage.write(data, "navtree_data.cs", dir + Doclava.wearableSupportPath
59          + "navtree_data.js");
60    } else {
61      ClearPage.write(data, "navtree_data.cs", "navtree_data.js");
62    }
63  }
64
65  /**
66   * Write the YAML formatted navigation tree.
67   * @see "http://yaml.org/"
68   */
69  public static void writeYamlTree(String dir, String fileName){
70    Data data = Doclava.makeHDF();
71    ClassInfo[] classes = Converter.rootClasses();
72
73    SortedMap<String, Object> sorted = new TreeMap<String, Object>();
74    for (ClassInfo cl : classes) {
75      if (cl.isHiddenOrRemoved()) {
76        continue;
77      }
78      sorted.put(cl.qualifiedName(), cl);
79
80      PackageInfo pkg = cl.containingPackage();
81      String name;
82      if (pkg == null) {
83        name = "";
84      } else {
85        name = pkg.name();
86      }
87      sorted.put(name, pkg);
88    }
89
90    data = makeYamlHDF(sorted, "docs.pages", data);
91    if ((Doclava.USE_DEVSITE_LOCALE_OUTPUT_PATHS) && (Doclava.testSupportRef)) {
92      dir = Doclava.ensureSlash(dir) + Doclava.testSupportPath;
93    } else if ((Doclava.USE_DEVSITE_LOCALE_OUTPUT_PATHS) && (Doclava.wearableSupportRef)) {
94      dir = Doclava.ensureSlash(dir) + Doclava.wearableSupportPath;
95    }
96    ClearPage.write(data, "yaml_navtree.cs", Doclava.ensureSlash(dir) + fileName);
97  }
98
99  public static Data makeYamlHDF(SortedMap<String, Object> sorted, String base, Data data) {
100
101    String key = "docs.pages.";
102    int i = 0;
103    for (String s : sorted.keySet()) {
104      Object o = sorted.get(s);
105
106      if (o instanceof PackageInfo) {
107        PackageInfo pkg = (PackageInfo) o;
108
109        data.setValue("docs.pages." + i + ".id", "" + i);
110        data.setValue("docs.pages." + i + ".label", pkg.name());
111        data.setValue("docs.pages." + i + ".shortname", "API");
112        data.setValue("docs.pages." + i + ".apilevel", pkg.getSince());
113        data.setValue("docs.pages." + i + ".link", pkg.htmlPage());
114        data.setValue("docs.pages." + i + ".type", "package");
115      } else if (o instanceof ClassInfo) {
116        ClassInfo cl = (ClassInfo) o;
117
118       // skip classes that are the child of another class, recursion will handle those.
119       if (cl.containingClass() == null){
120
121         data.setValue("docs.pages." + i + ".id", "" + i);
122         data = makeYamlHDF(cl, "docs.pages."+i, data);
123       }
124     }
125
126     i++;
127   }
128   return data;
129 }
130
131 public static Data makeYamlHDF(ClassInfo cl, String base, Data data) {
132   data.setValue(base + ".label", cl.name());
133   data.setValue(base + ".shortname", cl.name().substring(cl.name().lastIndexOf(".")+1));
134   data.setValue(base + ".link", cl.htmlPage());
135   data.setValue(base + ".type", cl.kind());
136
137   if (cl.innerClasses().size() > 0){
138     int j = 0;
139     for (ClassInfo cl2 : cl.innerClasses()){
140       data = makeYamlHDF(cl2, base + ".children." + j, data);
141       j++;
142     }
143   }
144
145    return data;
146  }
147  private static Node makePackageNode(PackageInfo pkg) {
148    List<Node> children = new ArrayList<Node>();
149
150    addClassNodes(children, "Annotations", pkg.annotations());
151    addClassNodes(children, "Interfaces", pkg.interfaces());
152    addClassNodes(children, "Classes", pkg.ordinaryClasses());
153    addClassNodes(children, "Enums", pkg.enums());
154    addClassNodes(children, "Exceptions", pkg.exceptions());
155    addClassNodes(children, "Errors", pkg.errors());
156
157    return new Node(pkg.name(), pkg.htmlPage(), children, pkg.getSince());
158  }
159
160  private static void addClassNodes(List<Node> parent, String label, ClassInfo[] classes) {
161    List<Node> children = new ArrayList<Node>();
162
163    for (ClassInfo cl : classes) {
164      if (cl.checkLevel()) {
165        children.add(new Node(cl.name(), cl.htmlPage(), null, cl.getSince()));
166      }
167    }
168
169    if (children.size() > 0) {
170      parent.add(new Node(label, null, children, null));
171    }
172  }
173
174  private static class Node {
175    private String mLabel;
176    private String mLink;
177    List<Node> mChildren;
178    private String mSince;
179
180    Node(String label, String link, List<Node> children, String since) {
181      mLabel = label;
182      mLink = link;
183      mChildren = children;
184      mSince = since;
185    }
186
187    static void renderString(StringBuilder buf, String s) {
188      if (s == null) {
189        buf.append("null");
190      } else {
191        buf.append('"');
192        final int N = s.length();
193        for (int i = 0; i < N; i++) {
194          char c = s.charAt(i);
195          if (c >= ' ' && c <= '~' && c != '"' && c != '\\') {
196            buf.append(c);
197          } else {
198            buf.append("\\u");
199            for (int j = 0; i < 4; i++) {
200              char x = (char) (c & 0x000f);
201              if (x >= 10) {
202                x = (char) (x - 10 + 'a');
203              } else {
204                x = (char) (x + '0');
205              }
206              buf.append(x);
207              c >>= 4;
208            }
209          }
210        }
211        buf.append('"');
212      }
213    }
214
215    void renderChildren(StringBuilder buf) {
216      List<Node> list = mChildren;
217      if (list == null || list.size() == 0) {
218        // We output null for no children. That way empty lists here can just
219        // be a byproduct of how we generate the lists.
220        buf.append("null");
221      } else {
222        buf.append("[ ");
223        final int N = list.size();
224        for (int i = 0; i < N; i++) {
225          list.get(i).render(buf);
226          if (i != N - 1) {
227            buf.append(", ");
228          }
229        }
230        buf.append(" ]\n");
231      }
232    }
233
234    void render(StringBuilder buf) {
235      buf.append("[ ");
236      renderString(buf, mLabel);
237      buf.append(", ");
238      renderString(buf, mLink);
239      buf.append(", ");
240      renderChildren(buf);
241      buf.append(", ");
242      renderString(buf, mSince);
243      buf.append(" ]");
244    }
245  }
246}
247