/*
* sqlc 1
* SQL Compiler
* Copyright (C) 2003 Hammurapi Group
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* URL: http://www.hammurapi.biz/products/sqlc/index.html
* e-Mail: support@hammurapi.biz
*/
package biz.hammurapi.codegen;
import java.io.File;
import java.io.IOException;
import java.io.StringReader;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.FactoryConfigurationError;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerException;
import org.apache.bcel.classfile.JavaClass;
import org.apache.bcel.generic.ClassGen;
import org.w3c.dom.DOMException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import biz.hammurapi.codegen.JavaLexer;
import biz.hammurapi.codegen.JavaRecognizer;
import biz.hammurapi.codegen.JavaTokenTypes;
import antlr.RecognitionException;
import antlr.TokenStreamException;
import antlr.collections.AST;
import biz.hammurapi.xml.dom.DOMUtils;
/**
* Stores generated class files in a specified directory and creates HTML documenation.
* @author Pavel Vlasov
* @version $Revision: 1.4 $
*/
public class XmlDocConsumer implements DocumentingConsumer {
private File classDir;
private File docDir;
private Document indexDocument;
private Element indexRoot;
final Map documentMap=new HashMap();
/**
* Construcotor
* @param classDir Output directory for generated classes. Mandatory.
* @param docDir Output directory for HTML documentation. Optional (can be null).
* @param indexName Name of index file.
* @throws GenerationException
*/
public XmlDocConsumer(File classDir, File docDir, String indexName) throws GenerationException {
if (classDir==null) {
throw new GenerationException("Class directory is null");
}
if (!classDir.isDirectory()) {
throw new GenerationException(classDir.getAbsolutePath()+" is not a directory");
}
if (docDir!=null && !docDir.isDirectory()) {
throw new GenerationException(docDir.getAbsolutePath()+" is not a directory");
}
this.classDir=classDir;
this.docDir=docDir;
this.indexName=indexName;
if (docDir!=null) {
indexDocument=newDocument();
indexRoot=indexDocument.createElement("classes");
indexDocument.appendChild(indexRoot);
}
}
/**
* @throws GenerationException
*/
private Document newDocument() {
try {
return DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
} catch (ParserConfigurationException e) {
throw new DocumentingException("Cannot create index document: "+e, e);
} catch (FactoryConfigurationError e) {
throw new DocumentingException("Cannot create index document: "+e, e);
}
}
private GenerationListener listener;
private String indexName;
/**
* Writes file to disk and also adds it to documentation
* @param javaClass Class to save and document
* @throws GenerationException If generated file could not be saved
*/
public void consume(JavaClass javaClass) throws GenerationException {
try {
javaClass.dump(new File(classDir, javaClass.getClassName().replace('.', File.separatorChar)+".class"));
} catch (IOException e) {
throw new GenerationException("Cannot save generated file: "+e, e);
}
}
private void saveDocument(String className) {
Node classRoot = (Node) documentMap.get(className);
if (classRoot!=null) {
try {
Iterator it=documentMap.keySet().iterator();
while (it.hasNext()) {
Element ref=classRoot.getOwnerDocument().createElement("ref");
classRoot.appendChild(ref);
ref.appendChild(ref.getOwnerDocument().createTextNode((String) it.next()));
}
File file = new File(docDir, className.replace('.', File.separatorChar)+".xml");
file.getParentFile().mkdirs();
DOMUtils.serialize(classRoot.getOwnerDocument(), file);
indexRoot.appendChild(indexDocument.importNode(classRoot, true));
} catch (IOException e) {
throw new DocumentingException("Cannot save documentation for class "+className+": "+e, e);
} catch (TransformerException e) {
throw new DocumentingException("Cannot save documentation for class "+className+": "+e, e);
}
}
}
/**
* @return Generation listener which documents methods and classes
*/
public GenerationListener getListener() {
if (listener==null && docDir!=null) {
listener = new GenerationListener() {
public void onClass(ClassGen classGen, String description) {
String className=classGen.getClassName();
int idx=className.lastIndexOf('.');
String pkg = className.substring(0, idx);
Document classDocument=newDocument();
Element classRoot=classDocument.createElement("class");
classRoot.setAttribute("fcn", className);
classDocument.appendChild(classRoot);
if (idx==-1) {
classRoot.setAttribute("name", className);
} else {
classRoot.setAttribute("package", pkg);
StringBuffer sb=new StringBuffer("..");
for (int i=pkg.indexOf('.'); i!=-1; i=pkg.indexOf(".", i+1)) {
sb.append("/..");
}
classRoot.setAttribute("upPath", sb.toString());
classRoot.setAttribute("name", className.substring(idx+1));
}
if (description!=null) {
classRoot.setAttribute("description", description);
}
if (classGen.isInterface()) {
classRoot.setAttribute("interface", "yes");
}
String superclass = classGen.getSuperclassName();
if (superclass!=null) {
addSuper(classRoot, "extends", superclass);
}
String[] interfaces = classGen.getInterfaceNames();
for (int i=0; i<interfaces.length; i++) {
addSuper(classRoot, "implements", interfaces[i]);
}
documentMap.put(className, classRoot);
}
private void addSuper(Element classRoot, String elementName, String className) {
Element classElement=classRoot.getOwnerDocument().createElement(elementName);
classRoot.appendChild(classElement);
classElement.setAttribute("fcn", className);
int idx=className.lastIndexOf('.');
String pkg = className.substring(0, idx);
if (idx==-1) {
classElement.setAttribute("name", className);
} else {
classElement.setAttribute("package", pkg);
classElement.setAttribute("name", className.substring(idx+1));
}
}
public void onMethod(String className, String signature, String description, Properties attributes) {
//signature="public static java.lang.Object[] getObject(int a, java.lang.Object, BigDecimal[][] b) throws abc, java.lang.Pizdec";
Element classElement=selectClass(className);
Document ownerDocument = classElement.getOwnerDocument();
Element methodElement=ownerDocument.createElement("method");
classElement.appendChild(methodElement);
methodElement.setAttribute("signature", signature);
if (description!=null) {
methodElement.setAttribute("description", description);
}
try {
JavaLexer lexer=new JavaLexer(new StringReader(signature));
JavaRecognizer parser=new JavaRecognizer(lexer);
//System.out.println("Parsing: "+signature);
parser.signature();
AST ast=parser.getAST();
//AstUtil.dumpAll(ast, parser.getTokenNames());
if (ast.getType()==JavaTokenTypes.MODIFIERS) {
for (AST modifier=ast.getFirstChild(); modifier!=null; modifier=modifier.getNextSibling()) {
Element me=ownerDocument.createElement("modifier");
me.appendChild(ownerDocument.createTextNode(modifier.getText()));
methodElement.appendChild(me);
}
ast=ast.getNextSibling();
}
Element rte=ownerDocument.createElement("return");
methodElement.appendChild(rte);
type(ast, rte);
ast=ast.getNextSibling();
methodElement.setAttribute("name", ast.getText());
ast=ast.getNextSibling();
for (AST parameter=ast.getFirstChild(); parameter!=null; parameter=parameter.getNextSibling()) {
Element pe=ownerDocument.createElement("parameter");
methodElement.appendChild(pe);
Element pte=ownerDocument.createElement("type");
pe.appendChild(pte);
type(parameter.getFirstChild().getFirstChild(), pte);
AST nameNode = parameter.getFirstChild().getNextSibling();
if (nameNode!=null) {
pe.setAttribute("name", nameNode.getText());
}
}
ast=ast.getNextSibling();
if (ast!=null) {
for (AST tc=ast.getFirstChild(); tc!=null; tc=tc.getNextSibling()) {
Element te=ownerDocument.createElement("throws");
type(tc, te);
methodElement.appendChild(te);
}
}
} catch (TokenStreamException e) {
System.err.println("WARN: Signature could not be parsed: "+signature+" ("+e+")");
} catch (RecognitionException e) {
System.err.println("WARN: Signature could not be parsed: "+signature+" ("+e+")");
}
if (attributes!=null) {
Iterator it=attributes.keySet().iterator();
while (it.hasNext()) {
Element attributeElement=ownerDocument.createElement("attribute");
methodElement.appendChild(attributeElement);
String key=(String) it.next();
attributeElement.setAttribute("name", key);
attributeElement.appendChild(ownerDocument.createTextNode(attributes.getProperty(key)));
}
}
}
public void onField(String className, String declaration, String description, Properties attributes) {
Element classElement=selectClass(className);
Document ownerDocument = classElement.getOwnerDocument();
Element fieldElement=ownerDocument.createElement("field");
classElement.appendChild(fieldElement);
fieldElement.setAttribute("declaration", declaration);
if (description!=null) {
fieldElement.setAttribute("description", description);
}
try {
for (AST ast=ClassGeneratorBase.field(declaration); ast!=null; ast=ast.getNextSibling()) {
if (ast.getType()==JavaTokenTypes.VARIABLE_DEF) {
for (AST node=ast.getFirstChild(); node!=null; node=node.getNextSibling()) {
switch (node.getType()) {
case JavaTokenTypes.MODIFIERS:
for (AST child=node.getFirstChild(); child!=null; child=child.getNextSibling()) {
Element me=ownerDocument.createElement("modifier");
me.appendChild(ownerDocument.createTextNode(child.getText()));
fieldElement.appendChild(me);
}
break;
case JavaTokenTypes.IDENT:
fieldElement.setAttribute("name", node.getText());
break;
case JavaTokenTypes.TYPE:
fieldElement.setAttribute("type", ClassGeneratorBase.toString(node.getFirstChild()));
break;
default:
System.err.println("WARN: Bad field declaration '"+declaration+"', unexpected node: "+node);
return;
}
}
} else {
System.err.println("WARN: Invalid node type "+ast.getType()+" in declaration '"+declaration+"'");
}
}
if (attributes!=null) {
Iterator it=attributes.keySet().iterator();
while (it.hasNext()) {
Element attributeElement=ownerDocument.createElement("attribute");
fieldElement.appendChild(attributeElement);
String key=(String) it.next();
attributeElement.setAttribute("name", key);
attributeElement.appendChild(ownerDocument.createTextNode(attributes.getProperty(key)));
}
}
} catch (GenerationException e) {
System.err.println("WARN: Invalid field declaration: '"+declaration+"', exception: "+e);
} catch (DOMException e) {
System.err.println("WARN: Invalid field declaration: '"+declaration+"', exception: "+e);
}
}
private void type(AST ast, Element rte) {
while (ast.getType()==JavaTokenTypes.ARRAY_DECLARATOR) {
rte.setAttribute("dimensions", rte.getAttribute("dimensions")+"[]");
ast=ast.getFirstChild();
}
if (ast.getType()==JavaTokenTypes.DOT) {
String pkg = toString(ast.getFirstChild());
rte.setAttribute("package", pkg);
String name = ast.getFirstChild().getNextSibling().getText();
rte.setAttribute("name", name);
rte.setAttribute("fcn", pkg+"."+name);
} else {
rte.setAttribute("name", ast.getText());
rte.setAttribute("fcn", ast.getText());
}
}
private String toString(AST node) {
if (node.getType()==JavaTokenTypes.DOT) {
return toString(node.getFirstChild()) + "." + toString(node.getFirstChild().getNextSibling());
}
return node.getText();
}
private Element selectClass(String className) {
return (Element) documentMap.get(className);
}
};
}
return listener;
}
/**
* Closes all files.
*
*/
public void close() {
Iterator it=documentMap.keySet().iterator();
while (it.hasNext()) {
saveDocument((String) it.next());
}
if (indexDocument!=null) {
try {
DOMUtils.serialize(indexDocument, new File(docDir, indexName));
} catch (IOException e) {
throw new DocumentingException("Cannot close index.html: "+e, e);
} catch (TransformerException e) {
throw new DocumentingException("Cannot close index.html: "+e, e);
}
}
}
}
|