001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041:
042: package org.netbeans.nbbuild;
043:
044: import java.io.*;
045: import java.io.File;
046: import java.util.*;
047: import java.util.ArrayList;
048: import java.util.List;
049: import java.util.regex.*;
050:
051: import org.apache.tools.ant.BuildException;
052: import org.apache.tools.ant.DirectoryScanner;
053: import org.apache.tools.ant.Project;
054: import org.apache.tools.ant.Task;
055: import org.apache.tools.ant.types.FileSet;
056:
057: /** Generates a file with index of all files.
058: *
059: * @author Jaroslav Tulach
060: */
061: public class JavadocIndex extends Task {
062: private File target;
063: private FileSet set;
064: private Map<String, List<Clazz>> classes = new HashMap<String, List<Clazz>>(
065: 101);
066:
067: /** The file to generate the index to.
068: */
069: public void setTarget(File f) {
070: this .target = f;
071: }
072:
073: /** List of indexes to search in.
074: */
075: public void addPackagesList(FileSet set) throws BuildException {
076: if (this .set != null) {
077: throw new BuildException(
078: "Package list can be associated only once");
079: }
080: this .set = set;
081: }
082:
083: public void execute() throws BuildException {
084: if (target == null) {
085: throw new BuildException("Target must be set"); // NOI18N
086: }
087: if (set == null) {
088: throw new BuildException("Set of files must be provided: "
089: + set); // NOI18N
090: }
091:
092: DirectoryScanner scan = set.getDirectoryScanner(this
093: .getProject());
094: File bdir = scan.getBasedir();
095: for (String n : scan.getIncludedFiles()) {
096: File f = new File(bdir, n);
097: parseForClasses(f);
098: }
099:
100: try {
101: log("Generating list of all classes to " + target);
102: PrintStream ps = new PrintStream(new BufferedOutputStream(
103: new FileOutputStream(target)));
104: if (target.getName().endsWith(".xml")) {
105: printClassesAsXML(ps);
106: } else {
107: printClassesAsHtml(ps);
108: }
109: ps.close();
110: } catch (IOException ex) {
111: throw new BuildException(ex);
112: }
113: }
114:
115: /** Stores parsed info in classes variable */
116: private void parseForClasses(File f) throws BuildException {
117: log("Parsing file: " + f, Project.MSG_DEBUG);
118: try {
119: BufferedReader is = new BufferedReader(new FileReader(f));
120:
121: String urlPrefix;
122: try {
123: String fullDir = f.getParentFile().getCanonicalPath();
124: String fullTgz = target.getParentFile()
125: .getCanonicalPath();
126:
127: if (!fullDir.startsWith(fullTgz)) {
128: throw new BuildException(
129: "The directory of target file must be above all parsed files. Directory: "
130: + fullTgz + " the file dir: "
131: + fullDir);
132: }
133:
134: urlPrefix = fullDir.substring(fullTgz.length() + 1);
135: } catch (IOException ex) {
136: throw new BuildException(ex);
137: }
138:
139: // parse following string
140: // <A HREF="org/openide/xml/XMLUtil.html" title="class in org.openide.xml">XMLUtil</A
141: String mask = ".*<A HREF=\"([^\"]*)\" title=\"(class|interface) in ([^\"]*)\"[><I]*>([\\p{Alnum}\\.]*)</.*A>.*";
142: Pattern p = Pattern.compile(mask, Pattern.CASE_INSENSITIVE);
143: // group 1: relative URL to a class or interface
144: // group 2: interface or class string
145: // group 3: name of package
146: // group 4: name of class
147:
148: int matches = 0;
149: for (;;) {
150: String line = is.readLine();
151: if (line == null)
152: break;
153:
154: Matcher m = p.matcher(line);
155: if (m.matches()) {
156: matches++;
157: log("Accepted line: " + line, Project.MSG_DEBUG);
158:
159: if (m.groupCount() != 4) {
160: StringBuffer sb = new StringBuffer();
161: sb.append("Line " + line + " has "
162: + m.groupCount()
163: + " groups and not four");
164: for (int i = 0; i <= m.groupCount(); i++) {
165: sb.append("\n " + i + " grp: "
166: + m.group(i));
167: }
168: throw new BuildException(sb.toString());
169: }
170:
171: Clazz c = new Clazz(m.group(3), m.group(4),
172: "interface".equals(m.group(2)), urlPrefix
173: + "/" + m.group(1));
174: if (c.name == null)
175: throw new NullPointerException("Null name for "
176: + line + "\nclass: " + c);
177: if (c.name.length() == 0)
178: throw new IllegalStateException(
179: "Empty name for " + line + "\nclass: "
180: + c);
181:
182: log("Adding class: " + c, Project.MSG_DEBUG);
183:
184: List<Clazz> l = classes.get(c.pkg);
185: if (l == null) {
186: l = new ArrayList<Clazz>();
187: classes.put(c.pkg, l);
188: }
189: l.add(c);
190: } else {
191: log("Refused line: " + line, Project.MSG_DEBUG);
192: }
193: }
194:
195: if (matches == 0) {
196: throw new BuildException("No classes defined in file: "
197: + f);
198: }
199:
200: } catch (java.io.IOException ex) {
201: throw new BuildException(ex);
202: }
203: }
204:
205: private void printClassesAsHtml(PrintStream ps) {
206: ps
207: .println("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">");
208: ps
209: .println("<HTML>\n<HEAD><TITLE>List of All Classes</TITLE></HEAD>");
210: ps.println();
211: for (String pkg : new TreeSet<String>(classes.keySet())) {
212: ps.println("<H2>" + pkg + "</H2>");
213: for (Clazz c : new TreeSet<Clazz>(classes.get(pkg))) {
214: ps.print("<A HREF=\"" + c.url + "\">");
215: if (c.isInterface) {
216: ps.print("<I>");
217: }
218: ps.print(c.name);
219: if (c.isInterface) {
220: ps.print("</I>");
221: }
222: ps.println("</A>");
223: }
224: }
225: ps.println("</HTML>");
226: }
227:
228: private void printClassesAsXML(PrintStream ps) {
229: ps.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
230: ps.println("<classes>");
231: for (String pkg : new TreeSet<String>(classes.keySet())) {
232: for (Clazz c : new TreeSet<Clazz>(classes.get(pkg))) {
233: ps.print("<class name=\"");
234: ps.print(c.name);
235: ps.print("\"");
236: ps.print(" url=\"");
237: ps.print(c.url);
238: ps.print("\"");
239: ps.print(" interface=\"");
240: ps.print(c.isInterface);
241: ps.print("\"");
242: ps.print(" package=\"");
243: ps.print(c.pkg);
244: ps.print("\"");
245: ps.println(" />");
246: }
247: }
248: ps.println("</classes>");
249: }
250:
251: /** An information about one class in api */
252: private static final class Clazz extends Object implements
253: Comparable<Clazz> {
254: public final String pkg;
255: public final String name;
256: public final String url;
257: public final boolean isInterface;
258:
259: public Clazz(String pkg, String name, boolean isInterface,
260: String url) {
261: this .pkg = pkg;
262: this .name = name;
263: this .isInterface = isInterface;
264: this .url = url;
265: }
266:
267: /** Compares based on class names */
268: public int compareTo(Clazz o) {
269: return name.compareTo(o.name);
270: }
271:
272: public String toString() {
273: return "PKG: " + pkg + " NAME: " + name + " INTERFACE: "
274: + isInterface + " url: " + url;
275: }
276: } // end of Clazz
277: }
|