import java.util.ArrayList;
import java.util.List;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.Set;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.LineNumberReader;
import com.sun.javadoc.FieldDoc;
import com.sun.javadoc.MethodDoc;
import com.sun.javadoc.ClassDoc;
import com.sun.javadoc.RootDoc;
import com.sun.javadoc.DocErrorReporter;
import org.jdom.Attribute;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.input.SAXBuilder;
/**
* Doclet for generating the "Components" page. It searches for classes
* which have WOComponent as an ancestor and finds the resources associated
* with the component and pulls them together for the list.
*
* There is a test() method which, internally, checks the logic of the
* extractions. An new problemmatic comments are found, they can be added to
* that method.
*
* @author kiddyr
*/
public class PageGenerator {
static final String TABLE_TOP_START = "<table border=\"1\" width=\"100%\" cellpadding=\"3\" cellspacing=\"0\" summary=\"\">\n"+
"<tr bgcolor=\"#CCCCFF\" class=\"TableHeadingColor\">\n"+
"<th ALIGN=\"left\" colspan=\"2\"><font size=\"+2\">\n";
static final String TABLE_TOP_END = "</tr>\n";
static final String TABLE_END = "</table>\n";
static void dumpComps(HashMap<String,HashMap<String,Object>> comps) {
System.out.println("");
Iterator<String> keys = comps.keySet().iterator();
while (keys.hasNext()) {
String key = keys.next();
System.out.println("key: \""+key+"\" -> "+comps.get(key));
}
System.out.println("");
}
static void writeFileToWriter(String filename, FileWriter out) {
File src = new File(filename);
FileReader fRdr = null;
try {
fRdr = new FileReader(src);
} catch (java.io.FileNotFoundException fnfe) { throw new IllegalArgumentException(fnfe.getMessage()); }
LineNumberReader rdr = new LineNumberReader(fRdr);
String line = "";
while (line != null) {
try {
line = rdr.readLine();
} catch (java.io.IOException ioe) { line = null; }
try {
if (line != null) out.write(line+"\n");
} catch (java.io.IOException ioe) { throw new IllegalArgumentException(ioe.getMessage()); }
}
}
/**
* Find the classes for the page being generated and return a HashMap that uses the classname
* as a key which points to another HashMap that contains data about the class.
*/
static HashMap<String,HashMap<String,Object>> findSubClassesFromAvailable(ClassDoc[] classes, String classname) {
HashMap<String,HashMap<String,Object>> found = new HashMap<String,HashMap<String,Object>>();
for (int i = 0; i < classes.length; i++) {
ClassDoc aCD = classes[i];
if (!aCD.toString().startsWith("com.webobjects")) {
ClassDoc parent = aCD.superclass();
boolean done = false;
while (parent != null && !done) {
if (parent.toString().equals(classname)) {
// System.out.println("found component: "+aCD);
found.put(aCD.toString(), new HashMap<String,Object>());
done = true;
}
if (parent.toString().equals("java.lang.Object"))
done = true;
else
parent = parent.superclass();
}
}
}
return found;
}
static void findSourceFiles(HashMap<String,HashMap<String,Object>> comps, ArrayList<String> srcDirs) {
Iterator<String> keys = comps.keySet().iterator();
while (keys.hasNext()) {
String key = keys.next();
int lastDot = 0;
int currentDot = key.indexOf(".", lastDot);
while (currentDot > lastDot) {
lastDot = currentDot;
currentDot = key.indexOf(".", lastDot+1);
}
String leaf = key.substring(lastDot+1);
comps.get(key).put("componentName", leaf);
boolean found = false;
String srcDir = key.replaceAll("\\.", File.separator);
for (int jdx = 0; !found && jdx < srcDirs.size(); jdx++) {
String srcFilename = srcDirs.get(jdx)+File.separator+leaf+".java";
if ((new File(srcFilename)).exists())
comps.get(key).put("sourceFile", srcFilename);
}
}
}
static void findPackages(HashMap<String,HashMap<String,Object>> comps) {
Iterator<String> keys = comps.keySet().iterator();
while (keys.hasNext()) {
String key = keys.next();
String[] parts = key.split("\\.");
if (parts.length == 0)
comps.get(key).put("package", "NONE");
else {
comps.get(key).put("package", key.substring(0,key.length()-parts[parts.length-1].length()-1));
}
}
}
static void findApiFiles(HashMap<String,HashMap<String,Object>> comps, ArrayList<String> compDirs) {
Iterator keys = comps.keySet().iterator();
while (keys.hasNext()) {
String key = keys.next().toString();
int lastDot = 0;
int currentDot = key.indexOf(".", lastDot);
while (currentDot > lastDot) {
lastDot = currentDot;
currentDot = key.indexOf(".", lastDot+1);
}
String leaf = key.substring(lastDot+1);
comps.get(key).put("componentName", leaf);
boolean found = false;
for (int jdx = 0; !found && jdx < compDirs.size(); jdx++) {
String apiFilename = compDirs.get(jdx)+leaf+".api";
File apiFile = new File(apiFilename);
if (apiFile.exists())
comps.get(key).put("apiFile", apiFilename);
}
}
}
static void gatherAllComments(HashMap<String,HashMap<String,Object>> comps) {
Iterator<String> keys = comps.keySet().iterator();
while (keys.hasNext()) {
String key = keys.next().toString();
FileReader fRdr = null;
String srcFilename = (String)comps.get(key).get("sourceFile");
if (srcFilename != null) {
try {
fRdr = new FileReader(srcFilename);
} catch (java.io.FileNotFoundException fnfe) { System.out.println(fnfe.getMessage()); System.exit(1); }
LineNumberReader rdr = new LineNumberReader(fRdr);
boolean done = false;
boolean inComment = false;
ArrayList<String> comments = new ArrayList<String>();
String line = "";
while (line != null && !done) {
try {
line = rdr.readLine();
if (line != null) {
if (line.indexOf("/**") >= 0) { inComment = true; }
if (line.startsWith("public class")) { done = true; }
if (inComment && line.indexOf("*/") >= 0) { done = true; }
if (inComment) { comments.add(line); }
}
} catch (java.io.IOException ioe) { line = null; }
}
comps.get(key).put("allComments", comments);
} else
comps.get(key).put("allComments", new ArrayList<String>());
}
}
static TreeMap<String,TreeSet<String>> gatherClassesByPrefix(HashMap<String,HashMap<String,Object>> comps, String[] prefixes) {
// Gather the classnames by prefix.
//
TreeSet<String> classNames = new TreeSet<String>(comps.keySet());
Iterator<String> names = classNames.iterator();
TreeMap<String,TreeSet<String>> foundPrefixes = new TreeMap<String,TreeSet<String>>();
while (names.hasNext()) {
String current = names.next();
String[] parts = current.split("\\.");
String lastName = parts[parts.length-1];
String prefixFound = null;
for (int idx = 0; idx < prefixes.length && prefixFound == null; idx++) {
if (lastName.startsWith(prefixes[idx])) prefixFound = prefixes[idx];
}
if (prefixFound == null) prefixFound = "NONE";
// System.out.println("name: \""+current+"\", lastName = \""+lastName+"\", prefix = \""+prefixFound+"\"");
TreeSet<String> classesForPrefix = foundPrefixes.get(prefixFound);
if (classesForPrefix == null) {
foundPrefixes.put(prefixFound, new TreeSet<String>());
classesForPrefix = foundPrefixes.get(prefixFound);
}
classesForPrefix.add(current);
}
return foundPrefixes;
}
static String classDocURL(String className) {
return className.replace('.','/')+".html";
}
static void findClassDocURLs(HashMap<String, HashMap<String, Object>> comps) {
Iterator<String> keys = comps.keySet().iterator();
while (keys.hasNext()) {
String key = keys.next();
String path = key.replace('.', '/');
comps.get(key).put("classDocURL", path + ".html");
}
}
static String packageNameFromClassName(String className) {
return className.substring(0,className.lastIndexOf("."));
}
static void findClassComments(HashMap<String,HashMap<String,Object>> comps) {
Iterator<String> keys = comps.keySet().iterator();
while (keys.hasNext()) {
String key = keys.next();
String found = findClassComment((HashMap<String,Object>)comps.get(key));
//String found = findClassComment((List<String>)comps.get(key).get("allComments"));
if (found != null) comps.get(key).put("classComment", found);
}
}
/**
* Returns the first sentence of the javadoc for a component's
* class. It is a shame that we cannot leverage javadoc for this, but
* there are no hooks for this. We are looking for "end" strings from
* the ends array, below. So far, this contains ". ", ".<", and ".\" ".
*
* Because memory is a problem, we are going to remove the class comment
* from the allComments array as we locate it.
*/
@SuppressWarnings("unchecked")
static String findClassComment(HashMap<String,Object> comp) {
//List<String> comments) {
// System.out.println("start: comments: "+comments);
List<String> comments = (List<String>)comp.get("allComments");
if (comments == null || comments.size() == 0)
return "";
boolean done = false;
// Coalesce the lines of the comment into one string.
//
String str = comments.get(0).replaceFirst("/\\*\\*", "").trim();
for (int idx = 1; idx < comments.size(); idx++) {
String other = comments.get(idx).trim();
// this does not work the same as the while after...
//other.replace("^\\**", "");
while (other.startsWith("*")) { other = other.substring(1); }
str = (str.trim()+" "+other.trim()).trim();
}
if (str.equals("") || str.startsWith("@")) return "";
// Locate the end markers.
//
String[] ends = new String[] { ". ", ".<", ".\" ", " @", ".*/", ".*<" };
int end = str.length();
String endMarker = null;
for (int idx = 0; idx < ends.length; idx++) {
if (str.indexOf(ends[idx]) >= 0 && str.indexOf(ends[idx]) < end) {
end = str.indexOf(ends[idx]);
endMarker = ends[idx];
}
}
if (endMarker != null)
str = str.substring(0,end+endMarker.length()-1).trim();
// System.out.println("done: str: \""+str+"\"");
return str;
}
static String paddedNumber(int num, int width) {
String str = ""+num;
while (str.length() < width) str = "0"+str;
return str;
}
/**
* Take a list of comment strings, eg ("/**", "Something", "", " * @binding some", "\*\/"),
* and return a dictionary of binding names to binding comment structures.
*
* @param comps
* @param tag
*/
@SuppressWarnings("unchecked")
static void findTagComments(HashMap<String, HashMap<String, Object>> comps, String tag) {
// System.out.println("start: comments = "+comments);
Iterator<String> keys = comps.keySet().iterator();
while (keys.hasNext()) {
String key = keys.next();
ArrayList<String> comments = (ArrayList<String>)comps.get(key).get("allComments");
ArrayList<String> found = new ArrayList<String>();
boolean inTag = false;
// Start with the class comment block and turn this into an array of lines that
// all start with a tag.
//
// TODO: we do not get rid of tabs.... (or do we now, with trim())
//
for (int idx = 0; idx < comments.size(); idx++) {
String line = comments.get(idx);
line = line.trim();
if (line.startsWith("*") && !line.startsWith("*/")) line = line.substring(1);
line = line.trim();
if (line.startsWith(tag)) inTag = true;
if (line.indexOf("*/") >= 0) inTag = false;
if (line.length() == 0) inTag = false;
if (line.startsWith("@") && !line.startsWith(tag)) inTag = false;
if (inTag) found.add(line);
}
// System.out.println("found: "+found);
ArrayList<String> found2 = new ArrayList<String>();
// This will collapse multi-line comments to one line.
//
int jdx = -1;
for (int idx = 0; idx < found.size(); idx++) {
if (found.get(idx).startsWith("@")) { jdx++; found2.add(found.get(idx)); }
else { found2.set(jdx, found2.get(jdx)+" "+found.get(idx)); }
}
// System.out.println("found2: "+found2);
HashMap<String,HashMap<String,String>> finished = new HashMap<String,HashMap<String,String>>();
// Construct the HashMap that holds the bindings. Fix the key, if needed.
//
for (int idx = 0; idx < found2.size(); idx++) {
String line = found2.get(idx);
line = line.substring(tag.length());
line = line.trim();
String name;
if (line.indexOf(" ") > 0)
name = line.substring(0,line.indexOf(" "));
else
name = line;
// Character c = name.charAt(name.length()-1);
// while (!Character.isJavaIdentifierPart(c)) {
// name = name.substring(0,name.length()-1);
// c = name.charAt(name.length()-1);
// }
if (line.indexOf(" ") > 0)
line = line.substring(line.indexOf(" "));
else
line = "";
line = line.trim();
HashMap<String,String> data = new HashMap<String,String>();
data.put("name", name);
data.put("comment", line);
data.put("order", paddedNumber(idx, 3));
finished.put(name, data);
}
comps.get(key).put("comments", finished);
comps.get(key).remove("allComments");
}
}
}