001: /*
002: * sqlc 1
003: * SQL Compiler
004: * Copyright (C) 2003 Hammurapi Group
005: *
006: * This program is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU Lesser General Public
008: * License as published by the Free Software Foundation; either
009: * version 2 of the License, or (at your option) any later version.
010: *
011: * This program is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * Lesser General Public License for more details.
015: *
016: * You should have received a copy of the GNU Lesser General Public
017: * License along with this library; if not, write to the Free Software
018: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
019: *
020: * URL: http://www.hammurapi.biz/products/sqlc/index.html
021: * e-Mail: support@hammurapi.biz
022: */
023: package biz.hammurapi.codegen;
024:
025: import java.io.File;
026: import java.io.IOException;
027: import java.io.StringReader;
028: import java.util.HashMap;
029: import java.util.Iterator;
030: import java.util.Map;
031: import java.util.Properties;
032:
033: import javax.xml.parsers.DocumentBuilderFactory;
034: import javax.xml.parsers.FactoryConfigurationError;
035: import javax.xml.parsers.ParserConfigurationException;
036: import javax.xml.transform.TransformerException;
037:
038: import org.apache.bcel.classfile.JavaClass;
039: import org.apache.bcel.generic.ClassGen;
040: import org.w3c.dom.DOMException;
041: import org.w3c.dom.Document;
042: import org.w3c.dom.Element;
043: import org.w3c.dom.Node;
044:
045: import biz.hammurapi.codegen.JavaLexer;
046: import biz.hammurapi.codegen.JavaRecognizer;
047: import biz.hammurapi.codegen.JavaTokenTypes;
048:
049: import antlr.RecognitionException;
050: import antlr.TokenStreamException;
051: import antlr.collections.AST;
052: import biz.hammurapi.xml.dom.DOMUtils;
053:
054: /**
055: * Stores generated class files in a specified directory and creates HTML documenation.
056: * @author Pavel Vlasov
057: * @version $Revision: 1.4 $
058: */
059: public class XmlDocConsumer implements DocumentingConsumer {
060: private File classDir;
061: private File docDir;
062: private Document indexDocument;
063: private Element indexRoot;
064:
065: final Map documentMap = new HashMap();
066:
067: /**
068: * Construcotor
069: * @param classDir Output directory for generated classes. Mandatory.
070: * @param docDir Output directory for HTML documentation. Optional (can be null).
071: * @param indexName Name of index file.
072: * @throws GenerationException
073: */
074: public XmlDocConsumer(File classDir, File docDir, String indexName)
075: throws GenerationException {
076: if (classDir == null) {
077: throw new GenerationException("Class directory is null");
078: }
079: if (!classDir.isDirectory()) {
080: throw new GenerationException(classDir.getAbsolutePath()
081: + " is not a directory");
082: }
083: if (docDir != null && !docDir.isDirectory()) {
084: throw new GenerationException(docDir.getAbsolutePath()
085: + " is not a directory");
086: }
087: this .classDir = classDir;
088: this .docDir = docDir;
089: this .indexName = indexName;
090:
091: if (docDir != null) {
092: indexDocument = newDocument();
093: indexRoot = indexDocument.createElement("classes");
094: indexDocument.appendChild(indexRoot);
095: }
096: }
097:
098: /**
099: * @throws GenerationException
100: */
101: private Document newDocument() {
102: try {
103: return DocumentBuilderFactory.newInstance()
104: .newDocumentBuilder().newDocument();
105: } catch (ParserConfigurationException e) {
106: throw new DocumentingException(
107: "Cannot create index document: " + e, e);
108: } catch (FactoryConfigurationError e) {
109: throw new DocumentingException(
110: "Cannot create index document: " + e, e);
111: }
112: }
113:
114: private GenerationListener listener;
115: private String indexName;
116:
117: /**
118: * Writes file to disk and also adds it to documentation
119: * @param javaClass Class to save and document
120: * @throws GenerationException If generated file could not be saved
121: */
122: public void consume(JavaClass javaClass) throws GenerationException {
123: try {
124: javaClass.dump(new File(classDir, javaClass.getClassName()
125: .replace('.', File.separatorChar)
126: + ".class"));
127: } catch (IOException e) {
128: throw new GenerationException(
129: "Cannot save generated file: " + e, e);
130: }
131: }
132:
133: private void saveDocument(String className) {
134: Node classRoot = (Node) documentMap.get(className);
135: if (classRoot != null) {
136: try {
137: Iterator it = documentMap.keySet().iterator();
138: while (it.hasNext()) {
139: Element ref = classRoot.getOwnerDocument()
140: .createElement("ref");
141: classRoot.appendChild(ref);
142: ref.appendChild(ref.getOwnerDocument()
143: .createTextNode((String) it.next()));
144: }
145: File file = new File(docDir, className.replace('.',
146: File.separatorChar)
147: + ".xml");
148: file.getParentFile().mkdirs();
149: DOMUtils.serialize(classRoot.getOwnerDocument(), file);
150: indexRoot.appendChild(indexDocument.importNode(
151: classRoot, true));
152: } catch (IOException e) {
153: throw new DocumentingException(
154: "Cannot save documentation for class "
155: + className + ": " + e, e);
156: } catch (TransformerException e) {
157: throw new DocumentingException(
158: "Cannot save documentation for class "
159: + className + ": " + e, e);
160: }
161: }
162: }
163:
164: /**
165: * @return Generation listener which documents methods and classes
166: */
167: public GenerationListener getListener() {
168: if (listener == null && docDir != null) {
169: listener = new GenerationListener() {
170:
171: public void onClass(ClassGen classGen,
172: String description) {
173: String className = classGen.getClassName();
174: int idx = className.lastIndexOf('.');
175: String pkg = className.substring(0, idx);
176:
177: Document classDocument = newDocument();
178: Element classRoot = classDocument
179: .createElement("class");
180: classRoot.setAttribute("fcn", className);
181: classDocument.appendChild(classRoot);
182: if (idx == -1) {
183: classRoot.setAttribute("name", className);
184: } else {
185: classRoot.setAttribute("package", pkg);
186: StringBuffer sb = new StringBuffer("..");
187: for (int i = pkg.indexOf('.'); i != -1; i = pkg
188: .indexOf(".", i + 1)) {
189: sb.append("/..");
190: }
191: classRoot.setAttribute("upPath", sb.toString());
192: classRoot.setAttribute("name", className
193: .substring(idx + 1));
194: }
195: if (description != null) {
196: classRoot.setAttribute("description",
197: description);
198: }
199:
200: if (classGen.isInterface()) {
201: classRoot.setAttribute("interface", "yes");
202: }
203:
204: String super class = classGen.getSuperclassName();
205: if (super class != null) {
206: addSuper(classRoot, "extends", super class);
207: }
208:
209: String[] interfaces = classGen.getInterfaceNames();
210: for (int i = 0; i < interfaces.length; i++) {
211: addSuper(classRoot, "implements", interfaces[i]);
212: }
213:
214: documentMap.put(className, classRoot);
215: }
216:
217: private void addSuper(Element classRoot,
218: String elementName, String className) {
219: Element classElement = classRoot.getOwnerDocument()
220: .createElement(elementName);
221: classRoot.appendChild(classElement);
222: classElement.setAttribute("fcn", className);
223: int idx = className.lastIndexOf('.');
224: String pkg = className.substring(0, idx);
225: if (idx == -1) {
226: classElement.setAttribute("name", className);
227: } else {
228: classElement.setAttribute("package", pkg);
229: classElement.setAttribute("name", className
230: .substring(idx + 1));
231: }
232: }
233:
234: public void onMethod(String className,
235: String signature, String description,
236: Properties attributes) {
237: //signature="public static java.lang.Object[] getObject(int a, java.lang.Object, BigDecimal[][] b) throws abc, java.lang.Pizdec";
238: Element classElement = selectClass(className);
239: Document ownerDocument = classElement
240: .getOwnerDocument();
241: Element methodElement = ownerDocument
242: .createElement("method");
243: classElement.appendChild(methodElement);
244: methodElement.setAttribute("signature", signature);
245: if (description != null) {
246: methodElement.setAttribute("description",
247: description);
248: }
249:
250: try {
251: JavaLexer lexer = new JavaLexer(
252: new StringReader(signature));
253: JavaRecognizer parser = new JavaRecognizer(
254: lexer);
255: //System.out.println("Parsing: "+signature);
256: parser.signature();
257: AST ast = parser.getAST();
258: //AstUtil.dumpAll(ast, parser.getTokenNames());
259: if (ast.getType() == JavaTokenTypes.MODIFIERS) {
260: for (AST modifier = ast.getFirstChild(); modifier != null; modifier = modifier
261: .getNextSibling()) {
262: Element me = ownerDocument
263: .createElement("modifier");
264: me.appendChild(ownerDocument
265: .createTextNode(modifier
266: .getText()));
267: methodElement.appendChild(me);
268: }
269: ast = ast.getNextSibling();
270: }
271:
272: Element rte = ownerDocument
273: .createElement("return");
274: methodElement.appendChild(rte);
275: type(ast, rte);
276: ast = ast.getNextSibling();
277: methodElement.setAttribute("name", ast
278: .getText());
279: ast = ast.getNextSibling();
280: for (AST parameter = ast.getFirstChild(); parameter != null; parameter = parameter
281: .getNextSibling()) {
282: Element pe = ownerDocument
283: .createElement("parameter");
284: methodElement.appendChild(pe);
285: Element pte = ownerDocument
286: .createElement("type");
287: pe.appendChild(pte);
288: type(parameter.getFirstChild()
289: .getFirstChild(), pte);
290: AST nameNode = parameter.getFirstChild()
291: .getNextSibling();
292: if (nameNode != null) {
293: pe.setAttribute("name", nameNode
294: .getText());
295: }
296: }
297: ast = ast.getNextSibling();
298: if (ast != null) {
299: for (AST tc = ast.getFirstChild(); tc != null; tc = tc
300: .getNextSibling()) {
301: Element te = ownerDocument
302: .createElement("throws");
303: type(tc, te);
304: methodElement.appendChild(te);
305: }
306: }
307: } catch (TokenStreamException e) {
308: System.err
309: .println("WARN: Signature could not be parsed: "
310: + signature + " (" + e + ")");
311: } catch (RecognitionException e) {
312: System.err
313: .println("WARN: Signature could not be parsed: "
314: + signature + " (" + e + ")");
315: }
316:
317: if (attributes != null) {
318: Iterator it = attributes.keySet().iterator();
319: while (it.hasNext()) {
320: Element attributeElement = ownerDocument
321: .createElement("attribute");
322: methodElement.appendChild(attributeElement);
323: String key = (String) it.next();
324: attributeElement.setAttribute("name", key);
325: attributeElement.appendChild(ownerDocument
326: .createTextNode(attributes
327: .getProperty(key)));
328: }
329: }
330: }
331:
332: public void onField(String className,
333: String declaration, String description,
334: Properties attributes) {
335: Element classElement = selectClass(className);
336: Document ownerDocument = classElement
337: .getOwnerDocument();
338: Element fieldElement = ownerDocument
339: .createElement("field");
340: classElement.appendChild(fieldElement);
341: fieldElement.setAttribute("declaration",
342: declaration);
343: if (description != null) {
344: fieldElement.setAttribute("description",
345: description);
346: }
347:
348: try {
349: for (AST ast = ClassGeneratorBase
350: .field(declaration); ast != null; ast = ast
351: .getNextSibling()) {
352: if (ast.getType() == JavaTokenTypes.VARIABLE_DEF) {
353: for (AST node = ast.getFirstChild(); node != null; node = node
354: .getNextSibling()) {
355: switch (node.getType()) {
356: case JavaTokenTypes.MODIFIERS:
357: for (AST child = node
358: .getFirstChild(); child != null; child = child
359: .getNextSibling()) {
360: Element me = ownerDocument
361: .createElement("modifier");
362: me
363: .appendChild(ownerDocument
364: .createTextNode(child
365: .getText()));
366: fieldElement
367: .appendChild(me);
368: }
369: break;
370: case JavaTokenTypes.IDENT:
371: fieldElement.setAttribute(
372: "name", node.getText());
373: break;
374: case JavaTokenTypes.TYPE:
375: fieldElement
376: .setAttribute(
377: "type",
378: ClassGeneratorBase
379: .toString(node
380: .getFirstChild()));
381: break;
382: default:
383: System.err
384: .println("WARN: Bad field declaration '"
385: + declaration
386: + "', unexpected node: "
387: + node);
388: return;
389: }
390: }
391: } else {
392: System.err
393: .println("WARN: Invalid node type "
394: + ast.getType()
395: + " in declaration '"
396: + declaration + "'");
397: }
398: }
399:
400: if (attributes != null) {
401: Iterator it = attributes.keySet()
402: .iterator();
403: while (it.hasNext()) {
404: Element attributeElement = ownerDocument
405: .createElement("attribute");
406: fieldElement
407: .appendChild(attributeElement);
408: String key = (String) it.next();
409: attributeElement.setAttribute("name",
410: key);
411: attributeElement
412: .appendChild(ownerDocument
413: .createTextNode(attributes
414: .getProperty(key)));
415: }
416: }
417: } catch (GenerationException e) {
418: System.err
419: .println("WARN: Invalid field declaration: '"
420: + declaration
421: + "', exception: " + e);
422: } catch (DOMException e) {
423: System.err
424: .println("WARN: Invalid field declaration: '"
425: + declaration
426: + "', exception: " + e);
427: }
428: }
429:
430: private void type(AST ast, Element rte) {
431: while (ast.getType() == JavaTokenTypes.ARRAY_DECLARATOR) {
432: rte.setAttribute("dimensions", rte
433: .getAttribute("dimensions")
434: + "[]");
435: ast = ast.getFirstChild();
436: }
437:
438: if (ast.getType() == JavaTokenTypes.DOT) {
439: String pkg = toString(ast.getFirstChild());
440: rte.setAttribute("package", pkg);
441: String name = ast.getFirstChild()
442: .getNextSibling().getText();
443: rte.setAttribute("name", name);
444: rte.setAttribute("fcn", pkg + "." + name);
445: } else {
446: rte.setAttribute("name", ast.getText());
447: rte.setAttribute("fcn", ast.getText());
448: }
449: }
450:
451: private String toString(AST node) {
452: if (node.getType() == JavaTokenTypes.DOT) {
453: return toString(node.getFirstChild())
454: + "."
455: + toString(node.getFirstChild()
456: .getNextSibling());
457: }
458:
459: return node.getText();
460: }
461:
462: private Element selectClass(String className) {
463: return (Element) documentMap.get(className);
464: }
465: };
466: }
467: return listener;
468: }
469:
470: /**
471: * Closes all files.
472: *
473: */
474: public void close() {
475: Iterator it = documentMap.keySet().iterator();
476: while (it.hasNext()) {
477: saveDocument((String) it.next());
478: }
479:
480: if (indexDocument != null) {
481: try {
482: DOMUtils.serialize(indexDocument, new File(docDir,
483: indexName));
484: } catch (IOException e) {
485: throw new DocumentingException(
486: "Cannot close index.html: " + e, e);
487: } catch (TransformerException e) {
488: throw new DocumentingException(
489: "Cannot close index.html: " + e, e);
490: }
491: }
492: }
493:
494: }
|