KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > enhydra > xml > xmlc > dom > generic > DOMBuilderGenerator


1 /*
2  * Enhydra Java Application Server Project
3  *
4  * The contents of this file are subject to the Enhydra Public License
5  * Version 1.1 (the "License"); you may not use this file except in
6  * compliance with the License. You may obtain a copy of the License on
7  * the Enhydra web site ( http://www.enhydra.org/ ).
8  *
9  * Software distributed under the License is distributed on an "AS IS"
10  * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
11  * the License for the specific terms governing rights and limitations
12  * under the License.
13  *
14  * The Initial Developer of the Enhydra Application Server is Lutris
15  * Technologies, Inc. The Enhydra Application Server and portions created
16  * by Lutris Technologies, Inc. are Copyright Lutris Technologies, Inc.
17  * All Rights Reserved.
18  *
19  * Contributor(s):
20  *
21  * $Id: DOMBuilderGenerator.java,v 1.2 2005/01/26 08:29:24 jkjome Exp $
22  */

23
24 package org.enhydra.xml.xmlc.dom.generic;
25
26 import org.enhydra.xml.xmlc.XMLCError;
27 import org.enhydra.xml.xmlc.XMLCException;
28 import org.enhydra.xml.xmlc.codegen.JavaClass;
29 import org.enhydra.xml.xmlc.codegen.JavaCode;
30 import org.enhydra.xml.xmlc.codegen.JavaMethod;
31 import org.enhydra.xml.xmlc.codegen.JavaModifiers;
32 import org.enhydra.xml.xmlc.codegen.JavaParameter;
33 import org.enhydra.xml.xmlc.codegen.VarNames;
34 import org.enhydra.xml.xmlc.compiler.ElementInfo;
35 import org.enhydra.xml.xmlc.compiler.ElementTable;
36 import org.enhydra.xml.xmlc.dom.AccessorGenerator;
37 import org.w3c.dom.Attr JavaDoc;
38 import org.w3c.dom.Document JavaDoc;
39 import org.w3c.dom.DocumentType JavaDoc;
40 import org.w3c.dom.Element JavaDoc;
41 import org.w3c.dom.Entity JavaDoc;
42 import org.w3c.dom.NamedNodeMap JavaDoc;
43 import org.w3c.dom.Node JavaDoc;
44 import org.w3c.dom.Notation JavaDoc;
45
46 // FIXME: this code is still too ugly
47

48 /**
49  * Class to generate code to build the document tree using only the W3C DOM
50  * interface. The document building code is generated in several functions to
51  * avoid the JVM limit on maximum method size. Since it is not possible to
52  * determine the amount of bytecode generate, a metric is defined called
53  * <em>create-cost</em>. A unit of create-cost is approximately the cost
54  * to create a node and append it to it's parent. The limit on create-cost
55  * is determined experimentally.
56  *
57  */

58 public class DOMBuilderGenerator {
59     /** Add comments to generated code with node cost info */
60     private static final boolean COST_DEBUG = false;
61
62     /** Name of argument to method containing parent node. */
63     protected static final String JavaDoc PARENT_NODE_ARG = "parentNode";
64
65     /*
66      * Name of argument to method containing document. Also used for local
67      * variable name.
68      */

69     protected static final String JavaDoc DOCUMENT_ARG = "document";
70
71     /* Name of variable containing document type */
72     protected static final String JavaDoc DOCUMENT_TYPE_VAR = "docType";
73
74     /** Maximum creation-cost that a single build method can handle. */
75     private int fMaxCreateCostPerBuildMethod;
76
77     /**
78      * Type of document argument of build methods. Allows derived
79      * DOMs to use a more specific argument type to avoid casting.
80      */

81     private String JavaDoc fDocumentArgClassName;
82
83     /** Name of method to create. */
84     private String JavaDoc fMethodName;
85
86     /** Source document. */
87     private Document JavaDoc fDocument;
88
89     /** Object used to create DOM nodes. */
90     private NodeCreateGenerator fNodeCreator;
91
92     /** Table of element information. */
93     private ElementTable fElementTable;
94
95     /**
96      * Object that determines where new methods will be created to
97      * prevent method overflow.
98      */

99     private BuildMethodMappings fBuildMethodMappings;
100
101     /** Class being created. */
102     private JavaClass fDocClass;
103
104     /**
105      * Generator of access methods, needed to generate access method
106      * initialization
107      */

108     private AccessorGenerator fAccessorGenerator;
109
110     /**
111      * Should the methods be static.
112      */

113     private boolean fStaticMethods;
114
115     /**
116      * Class that represents a single subdocument building method.
117      * <P>
118      * A build method build either a subtree or a set of nodes with the
119      * same parent. This is required because the stack for building the tree
120      * is contained in variables in the build methods. The same-parent
121      * is used when all of the immediate children of a node will not fit
122      * under the limit, these objects are refered to as being chained to
123      * the object that created the parent.
124      * <P>
125      * The method that is generated that is associated with the
126      * Document has the signature in the form:
127      * <PRE>
128      * Document buildIt();
129      * </PRE>
130      * Others have
131      * <PRE>
132      * void buildItSub1(Document doc, Node parentNode);
133      * </PRE>
134      * With <CODE>Document</CODE> being the specific type for the document.
135      */

136     private class BuildMethodGenerator {
137         /** Table of tmp node variable names. */
138         private VarNames fNodeVarNames = new VarNames("Node", "$node");
139
140         /* Table of tmp Element node variable names. */
141         private VarNames fElementVarNames = new VarNames("Element", "$elem");
142
143         /* Table of tmp attribute variable names. */
144         private VarNames fAttrVarNames = new VarNames("Attr", "$attr");
145
146         /* Has document element variable been referenced? */
147         private boolean fReferencedDocElement;
148
149         /* The java code for the build metho */
150         private JavaMethod fMethod;
151
152         /** Body of current method being generated. */
153         private JavaCode fBody;
154
155         /** Next number to for generating submethod names. */
156         private int fNextSubMethodId;
157
158         /** If this is a chained entry, this is the chain level. */
159         private int fChainLevel;
160
161         /** The cost of this method. */
162         private int fMethodCost;
163
164         /**
165          * Constructor. Create a method to generate a subtree and its
166          * children.
167          *
168          * @param methodName The name for the build method. A unique
169          * suffix is append to the name for each sub-method generated.
170          */

171         public BuildMethodGenerator(String JavaDoc methodName,
172                                     Node JavaDoc root) throws XMLCException {
173             // Initialize java method, with differente signiture for doc
174
String JavaDoc type;
175             JavaParameter[] params;
176             if (root.getNodeType() == Node.DOCUMENT_NODE) {
177                 type = root.getClass().getName();
178                 params = new JavaParameter[0];
179             } else {
180                 type = "void";
181                 JavaParameter docArg
182                     = new JavaParameter(DOCUMENT_ARG,
183                                         fDocumentArgClassName,
184                                         (String JavaDoc)null);
185                 JavaParameter parentArg
186                     = new JavaParameter(PARENT_NODE_ARG,
187                                         Node JavaDoc.class.getName(),
188                                         (String JavaDoc)null);
189                 params = new JavaParameter[]{docArg, parentArg};
190             }
191             fMethod = new JavaMethod(methodName, type,
192                                      (JavaModifiers.PRIVATE | (fStaticMethods ? JavaModifiers.STATIC : 0)),
193                                      params, new String JavaDoc[] {"Create a subtree of the document."});
194             fDocClass.addMethod(fMethod);
195             fBody = fMethod.getCode();
196         }
197
198         /**
199          * Create a call to the method represented by this object. Not used
200          * for top level.
201          */

202         public void createMethodCall(String JavaDoc docVar,
203                                      String JavaDoc parentVar,
204                                      JavaCode body) {
205             body.addln(fMethod.getName() + "(" + docVar + ", "
206                        + parentVar + ");");
207         }
208
209         /**
210          * Generate a method to create DOM. This is the document-level
211          * entry point,
212          * @param doc The document
213          * @param body Append a call to this build method here, if not null.
214          */

215         public void createMethod(Document JavaDoc document) throws XMLCException {
216             if (COST_DEBUG) {
217                 fBody.addln("// " +fBuildMethodMappings.toString(document));
218             }
219
220             // This creates the document and document type, also a local
221
// variables holding the document and document type.
222
fNodeCreator.genDocumentCreate(document, DOCUMENT_ARG,
223                                            DOCUMENT_TYPE_VAR, fBody);
224             fMethodCost += fBuildMethodMappings.getNodeTypeCost(document);
225             
226             // Build whole main tree
227
processChildren(DOCUMENT_ARG, document, 1);
228
229             // Build document type
230
DocumentType JavaDoc docType = document.getDoctype();
231             if (docType != null) {
232                 processDocumentType(DOCUMENT_TYPE_VAR, docType, 1);
233             }
234
235             fBody.addln("return " + DOCUMENT_ARG + ";");
236             createVariables();
237
238             if (fMethodCost > fMaxCreateCostPerBuildMethod) {
239                 throw new XMLCError("BUG: maximum number of nodes per method exceeded");
240             }
241         }
242         
243         /**
244          * Generate a method to create DOM nodes. This is the non-document
245          * entry point, it may recursively create more methods.
246          * @param root The root of the subtree.
247          * @param docVar The variable containing the document, if not null.
248          */

249         public void createMethod(Node JavaDoc parent,
250                                  Node JavaDoc root) throws XMLCException {
251             processNode(PARENT_NODE_ARG, parent, root, 0);
252             createVariables();
253
254             if (fMethodCost > fMaxCreateCostPerBuildMethod) {
255                 throw new XMLCError("BUG: maximum number of nodes per method exceeded");
256             }
257         }
258         
259         /**
260          * Create children of a node using chaining. This is the entry when
261          * creating more chained nodes.
262          * @param children the enumeration should point to the next child
263          * to add.
264          */

265         private void createChainedChildrenMethod(int chainLevel,
266                                                  String JavaDoc parentVar,
267                                                  Node JavaDoc parent,
268                                                  Node JavaDoc nextChild,
269                                                  String JavaDoc docVar,
270                                                  JavaCode body) throws XMLCException {
271             fChainLevel = chainLevel;
272             body.addln(fMethod.getName() + "(" + docVar + ", " + parentVar + ");");
273             processChildrenChaining(PARENT_NODE_ARG, parent, nextChild, 0);
274             createVariables();
275         }
276
277         /**
278          * Get temporary variable name for a node. Special handling for
279          * Elements; allow for attribute setting without requiring (slower)
280          * type casting in generate code.
281          */

282         private String JavaDoc getNodeVarName(Node JavaDoc node,
283                                       int level) {
284             if (node instanceof Element JavaDoc) {
285                 return fElementVarNames.getVarName(level);
286             } else {
287                 return fNodeVarNames.getVarName(level);
288             }
289         }
290
291         /**
292          * Get the name of the next subtree method to build.
293          */

294         private String JavaDoc getNextSubTreeMethodName() {
295             return fMethod.getName() + "_" + fNextSubMethodId++;
296         }
297
298         /**
299          * Determine how much space is left in the method, in create-cost units.
300          */

301         private int spaceLeft() {
302             int left = (fMaxCreateCostPerBuildMethod - fMethodCost);
303             return (left < 0) ? 0 : left;
304         }
305
306         /**
307          * Create a new build method for a subtree.
308          */

309         private void createBuildMethod(String JavaDoc parentVar,
310                                        Node JavaDoc parent,
311                                        Node JavaDoc node) throws XMLCException {
312             BuildMethodGenerator buildMethod
313                 = new BuildMethodGenerator(getNextSubTreeMethodName(), node);
314             buildMethod.createMethodCall(DOCUMENT_ARG, parentVar, fBody);
315             buildMethod.createMethod(parent, node);
316             fMethodCost += BuildMethodMappings.BUILD_METHOD_CALL_CREATE_COST;
317         }
318         
319         /**
320          * Generate code to to initialize an attribute.
321          */

322         private void processAttr(String JavaDoc elementVar,
323                                  Element JavaDoc element,
324                                  Attr JavaDoc attr,
325                                  int level) throws XMLCException {
326             String JavaDoc attrVar = fAttrVarNames.getVarName(level);
327             if (attr.getSpecified()) {
328                 // Create attribute node
329
fNodeCreator.genNodeCreate(DOCUMENT_ARG, attrVar, attr, fBody);
330                 fNodeCreator.genAddAttribute(elementVar, attrVar, fBody);
331                 fMethodCost += fBuildMethodMappings.getNodeTypeCost(attr);
332
333                 // Create attribute children.
334
processChildren(attrVar, attr, level+1);
335             }
336         }
337
338         /**
339          * Generate code to initialize an element's attributes.
340          */

341         private void processAttrs(String JavaDoc elementVar,
342                                   Element JavaDoc element,
343                                   int level) throws XMLCException {
344             NamedNodeMap JavaDoc attrList = element.getAttributes();
345             if (attrList != null) {
346                 for (int idx = 0; idx < attrList.getLength(); idx++) {
347                     processAttr(elementVar, element,
348                                 (Attr JavaDoc)attrList.item(idx), level);
349                 }
350             }
351         }
352
353         /**
354          * Generate code to initialize element-specific constructs and create
355          * its attributes.
356          */

357         private void processElement(String JavaDoc elementVar,
358                                     Element JavaDoc element,
359                                     int level) throws XMLCException {
360             processAttrs(elementVar, element, level);
361             ElementInfo elementInfo = fElementTable.getElementInfo(element);
362             if (elementInfo.getNumAccessMethods() > 0) {
363                 fAccessorGenerator.createAccessMethodInit(elementInfo, elementVar,
364                                                           fBody);
365             }
366         }
367
368         /**
369          * Generate code to initialize an entity in a DocumentType.
370          */

371         private void processEntity(String JavaDoc docTypeVar,
372                                    Entity JavaDoc entity,
373                                    int level) throws XMLCException {
374             String JavaDoc entityVar = getNodeVarName(entity, level);
375             fNodeCreator.genNodeCreate(DOCUMENT_ARG, entityVar, entity, fBody);
376             fNodeCreator.genAddEntity(docTypeVar, entityVar, entity, fBody);
377             fMethodCost += fBuildMethodMappings.getNodeTypeCost(entity);
378             processChildren(entityVar, entity, level+1);
379         }
380
381         /**
382          * Generate code to initialize an notation in a DocumentType.
383          */

384         private void processNotation(String JavaDoc docTypeVar,
385                                      Notation JavaDoc notation,
386                                      int level) throws XMLCException {
387             String JavaDoc notationVar = getNodeVarName(notation, level);
388             fNodeCreator.genNodeCreate(DOCUMENT_ARG, notationVar, notation, fBody);
389             fNodeCreator.genAddNotation(docTypeVar, notationVar, notation, fBody);
390             fMethodCost += fBuildMethodMappings.getNodeTypeCost(notation);
391         }
392
393         /**
394          * Generate code to initialize DocumentType contained nodes.
395          */

396         private void processDocumentType(String JavaDoc docTypeVar,
397                                          DocumentType JavaDoc docType,
398                                          int level) throws XMLCException {
399             NamedNodeMap JavaDoc entities = docType.getEntities();
400             if (entities != null) {
401                 for (int idx = 0; idx < entities.getLength(); idx++) {
402                     processEntity(docTypeVar, (Entity JavaDoc)entities.item(idx),
403                                   level);
404                 }
405             }
406             NamedNodeMap JavaDoc notations = docType.getNotations();
407             if (notations != null) {
408                 for (int idx = 0; idx < notations.getLength(); idx++) {
409                     processNotation(docTypeVar, (Notation JavaDoc)notations.item(idx),
410                                     level);
411                 }
412             }
413         }
414
415         /**
416          * Generate code to create a node of most types. This doesn't
417          * handle Document or attributes.
418          * @return The generated variable name that will contain the node.
419          */

420         private String JavaDoc createNode(String JavaDoc parentVar,
421                                   Node JavaDoc parent,
422                                   Node JavaDoc node,
423                                   int level) throws XMLCException {
424             String JavaDoc nodeVar = getNodeVarName(node, level);
425             fNodeCreator.genNodeCreate(DOCUMENT_ARG, nodeVar, node, fBody);
426             fNodeCreator.genAppendChild(parentVar, nodeVar, node, fBody);
427             fMethodCost += fBuildMethodMappings.getNodeTypeCost(node);
428             fBody.addln();
429             return nodeVar;
430         }
431
432         /**
433          * Process a node, generating code to recreate it.
434          */

435         private void processNode(String JavaDoc parentVar,
436                                  Node JavaDoc parent,
437                                  Node JavaDoc node,
438                                  int level) throws XMLCException {
439             if (node instanceof DocumentType JavaDoc) {
440                 return; // already handled
441
}
442             if ((level != 0) && fBuildMethodMappings.isMethodRoot(node)) {
443                 // Make this element root of a method
444
createBuildMethod(parentVar, parent, node);
445             } else {
446                 if (COST_DEBUG) {
447                     fBody.addln("// " + fBuildMethodMappings.toString(node));
448                 }
449                 String JavaDoc nodeVar = createNode(parentVar, parent, node, level);
450                 if (node instanceof Element JavaDoc) {
451                     processElement(nodeVar, (Element JavaDoc)node, level);
452                 }
453                 processChildren(nodeVar, node, level+1);
454             }
455         }
456
457         /**
458          * Process nodes contained in parent node while chaining build methods
459          * together. This fills in the current build method and then chains
460          * to another.
461          */

462         private void processChildrenChaining(String JavaDoc parentVar,
463                                              Node JavaDoc parent,
464                                              Node JavaDoc nextChild,
465                                              int level) throws XMLCException {
466             // Fit as many as we an into this method. At least fit one, to
467
// handle the case of the calculated cost exceeding the max cost
468
// of a method.
469
boolean isFirst = true;
470             while ((nextChild != null)
471                    && ((fBuildMethodMappings.getCreateCost(nextChild) < spaceLeft())
472                        || isFirst)) {
473                 processNode(parentVar, parent, nextChild, level);
474                 nextChild = nextChild.getNextSibling();
475                 isFirst = false;
476             }
477             
478             // If there are any left, create another method.
479
if (nextChild != null) {
480                 BuildMethodGenerator nextBuildMethod
481                     = new BuildMethodGenerator(fMethod.getName() + "c",
482                                                nextChild);
483                 nextBuildMethod.createChainedChildrenMethod(fChainLevel+1,
484                                                             parentVar, parent,
485                                                             nextChild,
486                                                             DOCUMENT_ARG,
487                                                             fBody);
488                 fMethodCost += BuildMethodMappings.BUILD_METHOD_CALL_CREATE_COST;
489             }
490         }
491
492         /**
493          * Create children of the node.
494          */

495         private void processChildren(String JavaDoc parentVar,
496                                      Node JavaDoc parent,
497                                      int level) throws XMLCException {
498             if (fBuildMethodMappings.useChainedChildrenMethods(parent)) {
499                 processChildrenChaining(parentVar, parent,
500                                         parent.getFirstChild(), level);
501             } else {
502                 for (Node JavaDoc child = parent.getFirstChild();
503                      child != null; child = child.getNextSibling()) {
504                     processNode(parentVar, parent, child, level);
505                 }
506             }
507         }
508
509         /**
510          * Fill in the variables for this method.
511          */

512         private void createVariables() {
513             fNodeVarNames.insertVarDefs(fBody);
514             fElementVarNames.insertVarDefs(fBody);
515             fAttrVarNames.insertVarDefs(fBody);
516             fBody.addVars(""); // blank line
517
}
518     }
519
520     /**
521      * Constructor. Build the methods.
522      */

523     public DOMBuilderGenerator(String JavaDoc methodName,
524                                Document JavaDoc document,
525                                String JavaDoc documentArgClassName,
526                                NodeCreateGenerator nodeCreator,
527                                AccessorGenerator accessorGenerator,
528                                ElementTable elementTable,
529                                JavaClass docClass,
530                                int maxCreateCostPerBuildMethod,
531                                boolean staticMethods) throws XMLCException {
532         fMethodName = methodName;
533         fDocument = document;
534         fDocumentArgClassName = documentArgClassName;
535         fNodeCreator = nodeCreator;
536         fAccessorGenerator = accessorGenerator;
537         fElementTable = elementTable;
538         fDocClass = docClass;
539         fMaxCreateCostPerBuildMethod = maxCreateCostPerBuildMethod;
540         fStaticMethods = staticMethods;
541
542         fBuildMethodMappings = new BuildMethodMappings(fMaxCreateCostPerBuildMethod,
543                                                        document);
544
545         BuildMethodGenerator buildMethod
546             = new BuildMethodGenerator(fMethodName, document);
547         buildMethod.createMethod(document);
548     }
549
550     /**
551      * Create a call to the top level method that was generated.
552      */

553     public void createMethodCall(JavaCode body) {
554         body.addln(fMethodName + "();");
555     }
556 }
557
Popular Tags