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