blob: 57f1c5496fdb0621e66317b0bf8ecd9a2fea8283 [file] [log] [blame]
Ben Dodson920dbbb2010-08-04 15:21:06 -07001/*
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;
Ben Dodson920dbbb2010-08-04 15:21:06 -070018
Dirk Dougherty8a8f7912013-11-26 18:45:31 -080019import java.util.Arrays;
20import java.util.ArrayList;
21import java.util.Collections;
22import java.util.Comparator;
23import java.util.List;
Dirk Doughertye52f6d82013-09-04 17:27:29 -070024import java.util.regex.Pattern;
25import java.util.regex.Matcher;
Dirk Dougherty8a8f7912013-11-26 18:45:31 -080026import java.io.File;
Ben Dodson920dbbb2010-08-04 15:21:06 -070027
Dirk Dougherty8a8f7912013-11-26 18:45:31 -080028import 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*/
Ben Dodson920dbbb2010-08-04 15:21:06 -070034public class SampleCode {
35 String mSource;
36 String mDest;
37 String mTitle;
Dirk Doughertyc11a4672013-08-23 19:14:10 -070038 String mProjectDir;
Dirk Dougherty25586c02013-08-30 16:21:15 -070039 String mTags;
Ben Dodson920dbbb2010-08-04 15:21:06 -070040
Dirk Dougherty8a8f7912013-11-26 18:45:31 -080041 /** 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
Ben Dodson920dbbb2010-08-04 15:21:06 -070052 public SampleCode(String source, String dest, String title) {
53 mSource = source;
54 mTitle = title;
Dirk Dougherty25586c02013-08-30 16:21:15 -070055 mTags = null;
Dirk Dougherty1b45d1d2010-08-17 17:25:36 -070056
Dirk Dougherty815f9342013-09-12 00:30:28 -070057 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 }
Ben Dodson920dbbb2010-08-04 15:21:06 -070064 }
65 }
66
Dirk Dougherty8a8f7912013-11-26 18:45:31 -080067 /**
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) {
Dirk Dougherty1b45d1d2010-08-17 17:25:36 -070078 List<Node> filelist = new ArrayList<Node>();
Ben Dodson920dbbb2010-08-04 15:21:06 -070079 File f = new File(mSource);
Dirk Doughertyc11a4672013-08-23 19:14:10 -070080 mProjectDir = f.getName();
81 String name = mProjectDir;
Dirk Dougherty8a8f7912013-11-26 18:45:31 -080082 String mOut = mDest + name;
Ben Dodson920dbbb2010-08-04 15:21:06 -070083 if (!f.isDirectory()) {
84 System.out.println("-samplecode not a directory: " + mSource);
Dirk Dougherty1b45d1d2010-08-17 17:25:36 -070085 return null;
Ben Dodson920dbbb2010-08-04 15:21:06 -070086 }
Dirk Dougherty1b45d1d2010-08-17 17:25:36 -070087
Dirk Dougherty8a8f7912013-11-26 18:45:31 -080088 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);
Dirk Dougherty1b45d1d2010-08-17 17:25:36 -0700112 }
Dirk Dougherty8a8f7912013-11-26 18:45:31 -0800113
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);
Ben Dodson920dbbb2010-08-04 15:21:06 -0700129 }
130
Dirk Dougherty8a8f7912013-11-26 18:45:31 -0800131 /**
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 }
Ben Dodson920dbbb2010-08-04 15:21:06 -0700164 }
165 }
Ben Dodson920dbbb2010-08-04 15:21:06 -0700166 }
Dirk Dougherty1b45d1d2010-08-17 17:25:36 -0700167
Dirk Dougherty8a8f7912013-11-26 18:45:31 -0800168 /**
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) {
Dirk Dougherty1b45d1d2010-08-17 17:25:36 -0700182 String name = "";
Dirk Dougherty1b45d1d2010-08-17 17:25:36 -0700183 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
Dirk Doughertyc11a4672013-08-23 19:14:10 -0700195 File[] dirContents = dir.listFiles();
196 Arrays.sort(dirContents, byTypeAndName);
197 for (File f: dirContents) {
Dirk Dougherty1b45d1d2010-08-17 17:25:36 -0700198 name = f.getName();
Dirk Dougherty8a8f7912013-11-26 18:45:31 -0800199 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),
Dirk Dougherty35427702013-11-18 15:22:01 -0800211 relative, type, true);
Dirk Dougherty8a8f7912013-11-26 18:45:31 -0800212 } else {
213 writeImageVideoPage(f, convertExtension(path, Doclava.htmlExtension),
Dirk Dougherty35427702013-11-18 15:22:01 -0800214 relative, type, false);
Dirk Dougherty8a8f7912013-11-26 18:45:31 -0800215 }
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),
Dirk Dougherty35427702013-11-18 15:22:01 -0800225 relative, type, true);
Dirk Dougherty8a8f7912013-11-26 18:45:31 -0800226 } else {
227 writeImageVideoPage(f, convertExtension(path, Doclava.htmlExtension),
Dirk Dougherty35427702013-11-18 15:22:01 -0800228 relative, type, false);
Dirk Dougherty8a8f7912013-11-26 18:45:31 -0800229 }
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;
Dirk Dougherty1b45d1d2010-08-17 17:25:36 -0700247 String hdfkeyName = (key + i + ".Name");
248 String hdfkeyType = (key + i + ".Type");
249 String hdfkeyHref = (key + i + ".Href");
Dirk Dougherty8a8f7912013-11-26 18:45:31 -0800250 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 }
Dirk Dougherty1b45d1d2010-08-17 17:25:36 -0700256 }
Dirk Dougherty8a8f7912013-11-26 18:45:31 -0800257
Dirk Dougherty1b45d1d2010-08-17 17:25:36 -0700258 setParentDirs(hdf, relative, name, false);
Dirk Doughertye52f6d82013-09-04 17:27:29 -0700259 //Generate an index.html page for each dir being processed
Dirk Dougherty8a8f7912013-11-26 18:45:31 -0800260 if (FULL_TREE_NAVIGATION) {
261 ClearPage.write(hdf, "sampleindex.cs", relative + "/index" + Doclava.htmlExtension);
262 }
Ben Dodson920dbbb2010-08-04 15:21:06 -0700263 }
264
Dirk Doughertyc11a4672013-08-23 19:14:10 -0700265 /**
Dirk Doughertye52f6d82013-09-04 17:27:29 -0700266 * 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
Dirk Dougherty8a8f7912013-11-26 18:45:31 -0800269 * 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.
Dirk Doughertye52f6d82013-09-04 17:27:29 -0700271 *
Dirk Dougherty8a8f7912013-11-26 18:45:31 -0800272 * @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.
Dirk Doughertyc11a4672013-08-23 19:14:10 -0700278 */
Dirk Dougherty8a8f7912013-11-26 18:45:31 -0800279 public Node writeSampleIndexCs(Data hdf, File dir, Node tnode, boolean writeFiles) {
280
Dirk Doughertye52f6d82013-09-04 17:27:29 -0700281 String filename = dir.getAbsolutePath() + "/_index.jd";
Dirk Dougherty8a8f7912013-11-26 18:45:31 -0800282 String mGroup = "";
Dirk Doughertye52f6d82013-09-04 17:27:29 -0700283 File f = new File(filename);
284 String rel = dir.getPath();
Dirk Dougherty8a8f7912013-11-26 18:45:31 -0800285 if (writeFiles) {
Dirk Dougherty1b45d1d2010-08-17 17:25:36 -0700286
Dirk Dougherty8a8f7912013-11-26 18:45:31 -0800287 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);
Dirk Dougherty8a8f7912013-11-26 18:45:31 -0800299 }
300 } else if (f.isFile()) {
301 //gather metadata for toc and jd_lists_unified
302 DocFile.getPageMetadata(filename, hdf);
Dirk Doughertye52f6d82013-09-04 17:27:29 -0700303 mGroup = hdf.getValue("sample.group", "");
Dirk Dougherty8a8f7912013-11-26 18:45:31 -0800304 if (!"".equals(mGroup)) {
305 tnode.setGroup(hdf.getValue("sample.group", ""));
306 } else {
Dirk Doughertye52f6d82013-09-04 17:27:29 -0700307 //Errors.error(Errors.INVALID_SAMPLE_INDEX, null, "Sample " + mProjectDir
308 // + ": Root _index.jd must be present and must define sample.group"
309 // + " tag. Please see ... for details.");
Dirk Doughertye52f6d82013-09-04 17:27:29 -0700310 }
311 }
312 return tnode;
Dirk Doughertyc11a4672013-08-23 19:14:10 -0700313 }
Ben Dodson920dbbb2010-08-04 15:21:06 -0700314
Dirk Doughertyc11a4672013-08-23 19:14:10 -0700315 /**
Dirk Dougherty8a8f7912013-11-26 18:45:31 -0800316 * Sets metadata for managing html output and generates the project view page
317 * for a project.
318 *
319 * @param dir The project root dir.
320 * @param hdf The data to read/write for files in this project.
321 */
322 public void writeProjectStructure(String dir, Data hdf) {
323 hdf.setValue("projectStructure", "true");
324 hdf.setValue("projectDir", mProjectDir);
325 hdf.setValue("page.title", mProjectDir + " Structure");
326 hdf.setValue("projectTitle", mTitle);
327 ClearPage.write(hdf, "sampleindex.cs", mDest + "project" + Doclava.htmlExtension);
328 hdf.setValue("projectStructure", "");
329 }
330
331 /**
332 * Keeps track of each file's parent dirs. Used for generating path breadcrumbs in html.
333 *
334 * @param dir The data to read/write for this file.
335 * @param hdf The relative path for this file, from samples root.
336 * @param subdir The relative path for this file, from samples root.
337 * @param name The name of the file (minus extension).
338 * @param isFile Whether this is a file (not a dir).
Dirk Doughertyc11a4672013-08-23 19:14:10 -0700339 */
340 Data setParentDirs(Data hdf, String subdir, String name, Boolean isFile) {
Dirk Dougherty8a8f7912013-11-26 18:45:31 -0800341 if (FULL_TREE_NAVIGATION) {
342 hdf.setValue("linkfyPathCrumb", "");
343 }
Dirk Doughertyc11a4672013-08-23 19:14:10 -0700344 int iter;
345 hdf.removeTree("parentdirs");
Dirk Doughertyc11a4672013-08-23 19:14:10 -0700346 String s = subdir;
347 String urlParts[] = s.split("/");
Dirk Doughertye52f6d82013-09-04 17:27:29 -0700348 int n, l = 1;
Dirk Doughertye52f6d82013-09-04 17:27:29 -0700349 for (iter=1; iter < urlParts.length; iter++) {
350 n = iter-1;
Dirk Doughertyc11a4672013-08-23 19:14:10 -0700351 hdf.setValue("parentdirs." + n + ".Name", urlParts[iter]);
Dirk Doughertye52f6d82013-09-04 17:27:29 -0700352 hdf.setValue("parentdirs." + n + ".Link", subdir + "index" + Doclava.htmlExtension);
Ben Dodson920dbbb2010-08-04 15:21:06 -0700353 }
Ben Dodson920dbbb2010-08-04 15:21:06 -0700354 return hdf;
355 }
356
Dirk Doughertyc11a4672013-08-23 19:14:10 -0700357 /**
Dirk Dougherty8a8f7912013-11-26 18:45:31 -0800358 * Writes a templated source code file to out.
Dirk Doughertyc11a4672013-08-23 19:14:10 -0700359 */
Dirk Dougherty8a8f7912013-11-26 18:45:31 -0800360 public void writePage(File f, String out, String subdir, Data hdf) {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700361 String name = f.getName();
Dirk Dougherty1b45d1d2010-08-17 17:25:36 -0700362 String path = f.getPath();
Dirk Dougherty8a8f7912013-11-26 18:45:31 -0800363 String data = SampleTagInfo.readFile(new SourcePositionInfo(path, -1, -1), path,
364 "sample code", true, true, true, true);
Ben Dodson920dbbb2010-08-04 15:21:06 -0700365 data = Doclava.escape(data);
366
Dirk Dougherty25586c02013-08-30 16:21:15 -0700367 String relative = subdir.replaceFirst("samples/", "");
Dirk Dougherty1b45d1d2010-08-17 17:25:36 -0700368 setParentDirs(hdf, subdir, name, true);
369 hdf.setValue("projectTitle", mTitle);
Dirk Doughertyc11a4672013-08-23 19:14:10 -0700370 hdf.setValue("projectDir", mProjectDir);
Ben Dodson920dbbb2010-08-04 15:21:06 -0700371 hdf.setValue("page.title", name);
372 hdf.setValue("subdir", subdir);
Dirk Dougherty25586c02013-08-30 16:21:15 -0700373 hdf.setValue("relative", relative);
Ben Dodson920dbbb2010-08-04 15:21:06 -0700374 hdf.setValue("realFile", name);
375 hdf.setValue("fileContents", data);
Dirk Dougherty1b45d1d2010-08-17 17:25:36 -0700376 hdf.setValue("resTag", "sample");
Ben Dodson920dbbb2010-08-04 15:21:06 -0700377
378 ClearPage.write(hdf, "sample.cs", out);
379 }
380
Dirk Doughertyc11a4672013-08-23 19:14:10 -0700381 /**
Dirk Dougherty8a8f7912013-11-26 18:45:31 -0800382 * Writes a templated image or video file to out.
Dirk Doughertyc11a4672013-08-23 19:14:10 -0700383 */
Dirk Dougherty35427702013-11-18 15:22:01 -0800384 public void writeImageVideoPage(File f, String out, String subdir,
Dirk Dougherty8a8f7912013-11-26 18:45:31 -0800385 String resourceType, boolean browsable) {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700386 Data hdf = Doclava.makeHDF();
Dirk Dougherty8a8f7912013-11-26 18:45:31 -0800387 if (Doclava.samplesNavTree != null) {
388 hdf.setValue("samples_toc_tree", Doclava.samplesNavTree.getValue("samples_toc_tree", ""));
389 }
390 hdf.setValue("samples", "true");
391
Dirk Dougherty35427702013-11-18 15:22:01 -0800392 String name = f.getName();
393 if (!browsable) {
394 hdf.setValue("noDisplay", "true");
395 }
Dirk Dougherty1b45d1d2010-08-17 17:25:36 -0700396 setParentDirs(hdf, subdir, name, true);
Dirk Dougherty35427702013-11-18 15:22:01 -0800397 hdf.setValue("samples", "true");
Ben Dodson920dbbb2010-08-04 15:21:06 -0700398 hdf.setValue("page.title", name);
Dirk Dougherty1b45d1d2010-08-17 17:25:36 -0700399 hdf.setValue("projectTitle", mTitle);
Dirk Doughertyc11a4672013-08-23 19:14:10 -0700400 hdf.setValue("projectDir", mProjectDir);
Ben Dodson920dbbb2010-08-04 15:21:06 -0700401 hdf.setValue("subdir", subdir);
Dirk Dougherty35427702013-11-18 15:22:01 -0800402 hdf.setValue("resType", resourceType);
Ben Dodson920dbbb2010-08-04 15:21:06 -0700403 hdf.setValue("realFile", name);
Dirk Dougherty8a8f7912013-11-26 18:45:31 -0800404
Ben Dodson920dbbb2010-08-04 15:21:06 -0700405 ClearPage.write(hdf, "sample.cs", out);
406 }
Dirk Dougherty1b45d1d2010-08-17 17:25:36 -0700407
408 /**
Dirk Dougherty8a8f7912013-11-26 18:45:31 -0800409 * Given a node containing sample code projects and a node containing all valid
410 * group nodes, extract project nodes from tnode and append them to the group node
411 * that matches their sample.group metadata.
412 *
413 * @param tnode A list of nodes containing sample code projects.
414 * @param groupnodes A list of nodes that represent the valid sample groups.
415 * @return The groupnodes list with all projects appended properly to their
416 * associated sample groups.
Dirk Dougherty1b45d1d2010-08-17 17:25:36 -0700417 */
Dirk Dougherty815f9342013-09-12 00:30:28 -0700418 public static void writeSamplesNavTree(List<Node> tnode, List<Node> groupnodes) {
419
Dirk Dougherty8a8f7912013-11-26 18:45:31 -0800420 Node node = new Node.Builder().setLabel("Samples").setLink(ClearPage.toroot
421 + "samples/index" + Doclava.htmlExtension).setChildren(tnode).build();
Dirk Dougherty1b45d1d2010-08-17 17:25:36 -0700422
Dirk Dougherty815f9342013-09-12 00:30:28 -0700423 if (groupnodes != null) {
424 for (int i = 0; i < tnode.size(); i++) {
Dirk Dougherty187b9852013-11-23 12:05:36 -0800425 if (tnode.get(i) != null) {
426 groupnodes = appendNodeGroups(tnode.get(i), groupnodes);
427 }
Dirk Dougherty815f9342013-09-12 00:30:28 -0700428 }
Dirk Dougherty1d8fa8d2013-09-12 19:01:02 -0700429 for (int n = 0; n < groupnodes.size(); n++) {
430 if (groupnodes.get(n).getChildren() == null) {
431 groupnodes.remove(n);
432 n--;
Dirk Dougherty8a8f7912013-11-26 18:45:31 -0800433 } else {
434 Collections.sort(groupnodes.get(n).getChildren(), byLabel);
Dirk Dougherty1d8fa8d2013-09-12 19:01:02 -0700435 }
436 }
Dirk Dougherty815f9342013-09-12 00:30:28 -0700437 node.setChildren(groupnodes);
438 }
439
Dirk Dougherty1b45d1d2010-08-17 17:25:36 -0700440 StringBuilder buf = new StringBuilder();
Dirk Dougherty8a8f7912013-11-26 18:45:31 -0800441 node.renderGroupNodesTOC(buf);
442 if (Doclava.samplesNavTree != null) {
443 Doclava.samplesNavTree.setValue("samples_toc_tree", buf.toString());
Dirk Dougherty1b45d1d2010-08-17 17:25:36 -0700444 }
445
Dirk Dougherty1b45d1d2010-08-17 17:25:36 -0700446 }
447
Dirk Dougherty25586c02013-08-30 16:21:15 -0700448 /**
Dirk Dougherty1d8fa8d2013-09-12 19:01:02 -0700449 * For a given project root node, get the group and then iterate the list of valid
450 * groups looking for a match. If found, append the project to that group node.
Dirk Doughertyf9d9cb02013-09-13 19:45:17 -0700451 * Samples that reference a valid sample group tag are added to a list for that
Dirk Dougherty1d8fa8d2013-09-12 19:01:02 -0700452 * group. Samples declare a sample.group tag in their _index.jd files.
Dirk Dougherty815f9342013-09-12 00:30:28 -0700453 */
454 private static List<Node> appendNodeGroups(Node gNode, List<Node> groupnodes) {
455 List<Node> mgrouplist = new ArrayList<Node>();
Dirk Dougherty1d8fa8d2013-09-12 19:01:02 -0700456 for (int i = 0; i < groupnodes.size(); i++) {
Dirk Dougherty187b9852013-11-23 12:05:36 -0800457 if (gNode.getGroup().equals(groupnodes.get(i).getLabel())) {
Dirk Dougherty815f9342013-09-12 00:30:28 -0700458 if (groupnodes.get(i).getChildren() == null) {
Dirk Dougherty1d8fa8d2013-09-12 19:01:02 -0700459 mgrouplist.add(gNode);
Dirk Dougherty815f9342013-09-12 00:30:28 -0700460 groupnodes.get(i).setChildren(mgrouplist);
Dirk Dougherty1d8fa8d2013-09-12 19:01:02 -0700461 } else {
462 groupnodes.get(i).getChildren().add(gNode);
Dirk Dougherty815f9342013-09-12 00:30:28 -0700463 }
Dirk Dougherty815f9342013-09-12 00:30:28 -0700464 break;
Dirk Dougherty1d8fa8d2013-09-12 19:01:02 -0700465 }
Dirk Dougherty815f9342013-09-12 00:30:28 -0700466 }
467 return groupnodes;
468 }
469
470 /**
Dirk Dougherty8a8f7912013-11-26 18:45:31 -0800471 * Sorts an array of files by type and name (alpha), with manifest always at top.
Dirk Dougherty25586c02013-08-30 16:21:15 -0700472 */
Dirk Doughertyc11a4672013-08-23 19:14:10 -0700473 Comparator<File> byTypeAndName = new Comparator<File>() {
474 public int compare (File one, File other) {
475 if (one.isDirectory() && !other.isDirectory()) {
476 return 1;
477 } else if (!one.isDirectory() && other.isDirectory()) {
478 return -1;
Dirk Dougherty187b9852013-11-23 12:05:36 -0800479 } else if ("AndroidManifest.xml".equals(one.getName())) {
Dirk Dougherty25586c02013-08-30 16:21:15 -0700480 return -1;
Dirk Doughertyc11a4672013-08-23 19:14:10 -0700481 } else {
482 return one.compareTo(other);
483 }
484 }
485 };
486
Dirk Dougherty1b45d1d2010-08-17 17:25:36 -0700487 /**
Dirk Dougherty8a8f7912013-11-26 18:45:31 -0800488 * Sorts a list of Nodes by label.
489 */
490 public static Comparator<Node> byLabel = new Comparator<Node>() {
491 public int compare(Node one, Node other) {
492 return one.getLabel().compareTo(other.getLabel());
493 }
494 };
495
496 /**
497 * Concatenates dirs that only hold dirs, to simplify nav tree
Dirk Dougherty25586c02013-08-30 16:21:15 -0700498 */
Dirk Dougherty815f9342013-09-12 00:30:28 -0700499 public static List<Node> squashNodes(List<Node> tnode) {
Dirk Dougherty25586c02013-08-30 16:21:15 -0700500 List<Node> list = tnode;
501
502 for(int i = 0; i < list.size(); ++i) {
Dirk Dougherty187b9852013-11-23 12:05:36 -0800503 if (("dir".equals(list.get(i).getType())) &&
Dirk Dougherty25586c02013-08-30 16:21:15 -0700504 (list.size() == 1) &&
505 (list.get(i).getChildren().get(0).getChildren() != null)) {
506 String thisLabel = list.get(i).getLabel();
507 String childLabel = list.get(i).getChildren().get(0).getLabel();
508 String newLabel = thisLabel + "/" + childLabel;
Dirk Dougherty25586c02013-08-30 16:21:15 -0700509 list.get(i).setLabel(newLabel);
510 list.get(i).setChildren(list.get(i).getChildren().get(0).getChildren());
511 } else {
512 continue;
513 }
514 }
515 return list;
516 }
517
Dirk Dougherty8a8f7912013-11-26 18:45:31 -0800518 public static String convertExtension(String s, String ext) {
519 return s.substring(0, s.lastIndexOf('.')) + ext;
520 }
521
522 /**
523 * Whitelists of valid image/video and source code types.
524 */
525 public static String[] IMAGES = {".png", ".jpg", ".gif"};
526 public static String[] VIDEOS = {".mp4", ".ogv", ".webm"};
527 public static String[] TEMPLATED = {".java", ".xml", ".aidl", ".rs",".txt", ".TXT"};
528
529 public static boolean inList(String s, String[] list) {
530 for (String t : list) {
531 if (s.endsWith(t)) {
532 return true;
533 }
534 }
535 return false;
536 }
537
538 /**
539 * Maps filenames to a set of generic types. Used for displaying files/dirs
540 * in the project view page.
541 */
542 public static String mapTypes(String name) {
543 String type = name.substring(name.lastIndexOf('.') + 1, name.length());
544 if ("xml".equals(type) || "java".equals(type)) {
545 if ("AndroidManifest.xml".equals(name)) type = "manifest";
546 return type;
547 } else {
548 return type = "file";
549 }
550 }
551
552 /**
553 * Validates a source file from a project against restrictions to determine
554 * whether to include the file in the browsable project output.
555 */
556 public boolean isValidFiletype(String name) {
557 if (name.startsWith(".") ||
558 name.startsWith("_") ||
559 "default.properties".equals(name) ||
560 "build.properties".equals(name) ||
561 name.endsWith(".ttf") ||
562 name.endsWith(".gradle") ||
563 name.endsWith(".bat") ||
564 "Android.mk".equals(name)) {
565 return false;
566 } else {
567 return true;
568 }
569 }
570
Dirk Dougherty25586c02013-08-30 16:21:15 -0700571 /**
Dirk Dougherty1b45d1d2010-08-17 17:25:36 -0700572 * SampleCode variant of NavTree node.
573 */
574 public static class Node {
575 private String mLabel;
576 private String mLink;
Dirk Doughertye52f6d82013-09-04 17:27:29 -0700577 private String mGroup; // from sample.group in _index.jd
Dirk Dougherty25586c02013-08-30 16:21:15 -0700578 private List<Node> mChildren;
579 private String mType;
Dirk Dougherty1b45d1d2010-08-17 17:25:36 -0700580
Dirk Doughertyf9d9cb02013-09-13 19:45:17 -0700581 private Node(Builder builder) {
582 mLabel = builder.mLabel;
583 mLink = builder.mLink;
584 mGroup = builder.mGroup;
Dirk Doughertyf9d9cb02013-09-13 19:45:17 -0700585 mChildren = builder.mChildren;
586 mType = builder.mType;
Dirk Dougherty1b45d1d2010-08-17 17:25:36 -0700587 }
588
Dirk Doughertyf9d9cb02013-09-13 19:45:17 -0700589 public static class Builder {
590 private String mLabel, mLink, mGroup, mType;
Dirk Doughertyf9d9cb02013-09-13 19:45:17 -0700591 private List<Node> mChildren = null;
592 public Builder setLabel(String mLabel) { this.mLabel = mLabel; return this;}
593 public Builder setLink(String mLink) { this.mLink = mLink; return this;}
594 public Builder setGroup(String mGroup) { this.mGroup = mGroup; return this;}
Dirk Doughertyf9d9cb02013-09-13 19:45:17 -0700595 public Builder setChildren(List<Node> mChildren) { this.mChildren = mChildren; return this;}
596 public Builder setType(String mType) { this.mType = mType; return this;}
597 public Node build() {return new Node(this);}
598 }
599
Dirk Dougherty8a8f7912013-11-26 18:45:31 -0800600 /**
601 * Renders browsable sample groups and projects to an html list, starting
602 * from the group nodes and then rendering their project nodes and finally their
603 * child dirs and files.
604 */
605 void renderGroupNodesTOC(StringBuilder buf) {
Dirk Dougherty1b45d1d2010-08-17 17:25:36 -0700606 List<Node> list = mChildren;
607 if (list == null || list.size() == 0) {
Dirk Dougherty8a8f7912013-11-26 18:45:31 -0800608 return;
Dirk Dougherty1b45d1d2010-08-17 17:25:36 -0700609 } else {
Dirk Dougherty8a8f7912013-11-26 18:45:31 -0800610 final int n = list.size();
611 for (int i = 0; i < n; i++) {
612 if (list.get(i).getChildren() == null) {
613 continue;
614 } else {
615 buf.append("<li class=\"nav-section\">");
616 buf.append("<div class=\"nav-section-header\">");
617 buf.append("<a href=\"" + list.get(i).getLink() + "\" title=\""
618 + list.get(i).getLabel() + "\">"
619 + list.get(i).getLabel() + "</a>");
620 buf.append("</div>");
621 buf.append("<ul>");
622 list.get(i).renderProjectNodesTOC(buf);
Dirk Dougherty1b45d1d2010-08-17 17:25:36 -0700623 }
624 }
Dirk Dougherty8a8f7912013-11-26 18:45:31 -0800625 buf.append("</ul>");
626 buf.append("</li>");
Dirk Dougherty1b45d1d2010-08-17 17:25:36 -0700627 }
628 }
629
Dirk Dougherty8a8f7912013-11-26 18:45:31 -0800630 /**
631 * Renders a list of sample code projects associated with a group node.
632 */
633 void renderProjectNodesTOC(StringBuilder buf) {
634 List<Node> list = mChildren;
635 if (list == null || list.size() == 0) {
636 return;
637 } else {
638 final int n = list.size();
639 for (int i = 0; i < n; i++) {
640 if (list.get(i).getChildren() == null) {
641 continue;
642 } else {
643 buf.append("<li class=\"nav-section\">");
644 buf.append("<div class=\"nav-section-header\">");
645 buf.append("<a href=\"" + list.get(i).getLink() + "\" title=\""
646 + list.get(i).getLabel() + "\">"
647 + list.get(i).getLabel() + "</a>");
648 buf.append("</div>");
649 buf.append("<ul>");
650 list.get(i).renderChildrenToc(buf);
651 }
652 }
653 buf.append("</ul>");
654 buf.append("</li>");
655 }
656 }
657
658 /**
659 * Renders child dirs and files associated with a project node.
660 */
661 void renderChildrenToc(StringBuilder buf) {
662 List<Node> list = mChildren;
Dirk Doughertye52f6d82013-09-04 17:27:29 -0700663 if (list == null || list.size() == 0) {
664 buf.append("null");
665 } else {
Dirk Dougherty8a8f7912013-11-26 18:45:31 -0800666 final int n = list.size();
667 for (int i = 0; i < n; i++) {
668 if (list.get(i).getChildren() == null) {
669 buf.append("<li>");
670 buf.append("<a href=\"" + list.get(i).getLink() + "\" title=\""
671 + list.get(i).getLabel() + "\">"
672 + list.get(i).getLabel() + "</a>");
673 buf.append(" </li>");
674 } else {
675 buf.append("<li class=\"nav-section sticky\">");
676 buf.append("<div class=\"nav-section-header empty\">");
677 buf.append("<a href=\"#\" onclick=\"return false;\" title=\""
678 + list.get(i).getLabel() + "\">"
679 + list.get(i).getLabel() + "/</a>");
680 buf.append("</div>");
681 buf.append("<ul>");
682 list.get(i).renderChildrenToc(buf);
Dirk Doughertye52f6d82013-09-04 17:27:29 -0700683 }
Dirk Doughertye52f6d82013-09-04 17:27:29 -0700684 }
Dirk Dougherty8a8f7912013-11-26 18:45:31 -0800685 buf.append("</ul>");
686 buf.append("</li>");
Dirk Doughertye52f6d82013-09-04 17:27:29 -0700687 }
688 }
689
Dirk Dougherty8a8f7912013-11-26 18:45:31 -0800690 /**
691 * Node getters and setters
692 */
Dirk Dougherty25586c02013-08-30 16:21:15 -0700693 public String getLabel() {
694 return mLabel;
695 }
696
697 public void setLabel(String label) {
698 mLabel = label;
699 }
700
Dirk Dougherty8a8f7912013-11-26 18:45:31 -0800701 public String getLink() {
Dirk Dougherty25586c02013-08-30 16:21:15 -0700702 return mLink;
703 }
704
Dirk Dougherty8a8f7912013-11-26 18:45:31 -0800705 public void setLink(String ref) {
706 mLink = ref;
Dirk Dougherty25586c02013-08-30 16:21:15 -0700707 }
Dirk Doughertye52f6d82013-09-04 17:27:29 -0700708
Dirk Dougherty815f9342013-09-12 00:30:28 -0700709 public String getGroup() {
710 return mGroup;
Dirk Doughertye52f6d82013-09-04 17:27:29 -0700711 }
712
Dirk Dougherty815f9342013-09-12 00:30:28 -0700713 public void setGroup(String group) {
714 mGroup = group;
Dirk Doughertye52f6d82013-09-04 17:27:29 -0700715 }
716
Dirk Dougherty8a8f7912013-11-26 18:45:31 -0800717 public List<Node> getChildren() {
718 return mChildren;
Dirk Doughertyc11a4672013-08-23 19:14:10 -0700719 }
720
Dirk Dougherty8a8f7912013-11-26 18:45:31 -0800721 public void setChildren(List<Node> node) {
722 mChildren = node;
Dirk Doughertyc11a4672013-08-23 19:14:10 -0700723 }
724
Dirk Dougherty8a8f7912013-11-26 18:45:31 -0800725 public String getType() {
726 return mType;
Dirk Doughertyc11a4672013-08-23 19:14:10 -0700727 }
Dirk Doughertyc11a4672013-08-23 19:14:10 -0700728
Dirk Dougherty8a8f7912013-11-26 18:45:31 -0800729 public void setType(String type) {
730 mType = type;
731 }
Dirk Dougherty35427702013-11-18 15:22:01 -0800732 }
Ben Dodson920dbbb2010-08-04 15:21:06 -0700733}