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.modules.schema2beansdev;
043:
044: import java.util.*;
045: import java.io.*;
046:
047: import org.netbeans.modules.schema2beans.*;
048:
049: //******************************************************************************
050: // BEGIN_NOI18N
051: // This class does not (and will not) cantain strings that need to be localized.
052: //******************************************************************************
053:
054: /**
055: *
056: * This class implements the Document Definition handler in order to build
057: * the internal tree representation of the DD DTD.
058: *
059: */
060: public class TreeBuilder implements DocDefHandler, TreeParser,
061: HasPrefixGuesser {
062: // root element of the DTD graph
063: GraphNode rootNode;
064: String docRoot;
065: GenBeans.Config config;
066:
067: // Current parsing pointers
068: private Stack curParentGroupStack = new Stack();
069: private GraphLink curParentGroup;
070:
071: // Global value of the type currently parsed (ELEMENT, ATTLIST, COMMENT)
072: private Stack curElementTypeStack = new Stack();
073: private int curElementType;
074:
075: // Current parsed attribute (<!ATTLIST element)
076: private Stack curAttrStack = new Stack();
077: private AttrProp curAttr;
078:
079: private PrefixGuesser prefixGuesser;
080:
081: void pushLevel() {
082: curParentGroupStack.push(curParentGroup);
083: curElementTypeStack.push(new Integer(curElementType));
084: curAttrStack.push(curAttr);
085: }
086:
087: void popLevel() {
088: curParentGroup = (GraphLink) curParentGroupStack.pop();
089: curElementType = ((Integer) curElementTypeStack.pop())
090: .intValue();
091: curAttr = (AttrProp) curAttrStack.pop();
092: }
093:
094: //
095: // Where the element nodes (GraphNode objects) are stored during
096: // the graph construction. We use this hash table to make sure that
097: // an element GraphNode is created only once (unicity on the name).
098: // This table is also useful to get the list of all the nodes.
099: //
100: Map nameHash; // Map<String, GraphNode>
101:
102: private String defaultNamespace = null;
103:
104: TreeBuilder(GenBeans.Config config) {
105: this .nameHash = new HashMap();
106: this .curAttr = null;
107: this .config = config;
108: }
109:
110: /**
111: * Called once, when the DTD is started to be parsed.
112: * Create the GraphNode root element.
113: *
114: * @param root root elemement name of the document (as the DOCTYPE
115: * specifies in the XML document)
116: */
117: public void startDocument(String root) {
118: if (DDLogFlags.debug) {
119: TraceLogger.put(TraceLogger.DEBUG, TraceLogger.SVC_DD,
120: DDLogFlags.DBG_DTD, 1, DDLogFlags.STARTDOC, root);
121:
122: config.messageOut
123: .println("Building the schema object graph.");
124: }
125: this .docRoot = root;
126: }
127:
128: /**
129: * Called when the DTD parsing is over.
130: *
131: * At this time, the DTD object graph is entirely built. The method
132: * checks the consitency of the built graph, and cleans things up a bit.
133: *
134: */
135: public void endDocument() {
136: if (DDLogFlags.debug) {
137: TraceLogger.put(TraceLogger.DEBUG, TraceLogger.SVC_DD,
138: DDLogFlags.DBG_DTD, 1, DDLogFlags.ENDDOC);
139:
140: config.messageOut.println("schema Object graph built.");
141: }
142:
143: // Remove the starter groupings
144: for (Iterator it = nameHash.values().iterator(); it.hasNext();) {
145: GraphNode node = (GraphNode) it.next();
146: GraphLink l = node.getGraphLink();
147: if (l == null || l.name != null || l.getSibling() != null
148: || l.isSequenceOr()
149: || l.getGroupInstance() != Common.TYPE_1)
150: continue;
151: GraphLink firstChild = l.getFirstChild();
152: if (firstChild != null && firstChild.getSibling() != null)
153: continue;
154: if (DDLogFlags.debug)
155: config.messageOut.println("Removing starter group: "
156: + l);
157: node.setGraphLink(firstChild);
158: }
159:
160: //
161: // We're done building the tree graph
162: // It's time now to generate the beans
163: //
164: try {
165: findRootNode();
166: } catch (Schema2BeansException e) {
167: throw new Schema2BeansRuntimeException(e);
168: }
169:
170: if (DDLogFlags.debug) {
171: config.messageOut.println(this .dump());
172: }
173: }
174:
175: /**
176: * Either create the GraphNode for the element named name, or
177: * get it from the hash table. This method can be called either
178: * by the startElement() method (an element definition has been
179: * found in the DTD) or by the element() method (an element is
180: * referenced by another one).
181: *
182: * @param name name of the element as the DTD parser reads it
183: * @param original true if the element definition has been
184: * read, false if we are just asking to reference the element.
185: * This parameter allows to check than an element is not
186: * defined twice and is at least defined once.
187: * @return the unique GraphNode object of the named element
188: */
189: private static final int CREATE = 1;
190: private static final int GET = 2;
191: private static final int REFERENCE = 3;
192:
193: private GraphNode getGraphNode(String uniqueName, String name,
194: int mode) throws Schema2BeansException {
195: //String uniqueName = name;
196: uniqueName = name;
197:
198: // Find out if we already know about it
199: GraphNode node = (GraphNode) this .nameHash.get(uniqueName);
200:
201: if (node != null) {
202: // We know about it
203: if (false && node.isCreated() && (mode == CREATE)) {
204: throw new Schema2BeansException(Common.getMessage(
205: "DuplicateElement_msg", uniqueName));
206: }
207: } else {
208: //
209: // First time we hear about this element. Create the GraphNode
210: // Object and its GraphLink link. The purpose of this GraphLink
211: // object is to hold the siblings/children links for this node.
212: // This GraphLink does _not_ reference the element.
213: // (graphLink.element = null).
214: //
215: node = new GraphNode(name, uniqueName);
216: node.setGraphLink(new GraphLink(null));
217: this .nameHash.put(uniqueName, node);
218: //System.out.println("Created new GraphNode: "+node);
219: }
220:
221: //
222: // Called to get the original: mark it. If we are later called again
223: // to get the original that means we have two element definition in
224: // the DTD and we can throw an exception (see above).
225: // Called to get a reference: increment the node usage. The root
226: // of the node is never referenced and will keep a refCount=0.
227: //
228: if (mode == CREATE)
229: node.setCreated(true);
230: else if (mode == REFERENCE)
231: node.incrRefCount();
232:
233: return node;
234: }
235:
236: /**
237: * Called each time a DTD <!element definition is read.
238: *
239: * @param name the name of the element
240: * @param typeName is the name to use for the attribute
241: * @param type the type (as a constant) of the element (for example
242: * ELEMENT or ATTLIST)
243: */
244: public void startElement(String uniqueName, String typeName,
245: int type) {
246:
247: if (DDLogFlags.debug) {
248: TraceLogger.put(TraceLogger.DEBUG, TraceLogger.SVC_DD,
249: DDLogFlags.DBG_DTD, 1, DDLogFlags.STARTELT,
250: uniqueName + " - " + typeName + " - "
251: + typeToString(type));
252: }
253: pushLevel();
254:
255: // Keep track of what we are building over all the method calls
256: this .curElementType = type;
257:
258: //
259: // For now, ignore comments
260: //
261: try {
262: if (type == Common.ELEMENT) {
263: // Get the node and start building beneath it
264: GraphNode node = getGraphNode(uniqueName, typeName,
265: CREATE);
266: curParentGroup = node.getGraphLink();
267: } else if (type == Common.ATTLIST) {
268: // Get the node and add this attribute
269: GraphNode node = getGraphNode(uniqueName, typeName, GET);
270: curAttr = new AttrProp(typeName);
271: node.addAttribute(curAttr);
272: }
273: } catch (Schema2BeansException e) {
274: throw new Schema2BeansRuntimeException(e);
275: }
276: }
277:
278: public boolean doesElementExist(String typeName) {
279: return nameHash.containsKey(typeName);
280: }
281:
282: /**
283: * Done with an element
284: */
285: public void endElement() {
286: if (DDLogFlags.debug) {
287: TraceLogger.put(TraceLogger.DEBUG, TraceLogger.SVC_DD,
288: DDLogFlags.DBG_DTD, 1, DDLogFlags.ENDELT);
289: }
290:
291: if (curElementType == Common.ATTLIST) {
292: curAttr.validate();
293: } else if (curElementType == Common.ELEMENT) {
294: //System.out.println("endElement: curParentGroup="+curParentGroup);
295: }
296:
297: curElementType = Common.NONE;
298: popLevel();
299: }
300:
301: /**
302: * Called each time a character | is found.
303: */
304: public void character(char c) {
305: //System.out.println("character: c="+c+" curParentGroup="+curParentGroup);
306: if (this .curElementType == Common.ELEMENT) {
307: if (c == '|')
308: curParentGroup.setSequence(Common.SEQUENCE_OR);
309: } else if (this .curElementType == Common.ATTLIST) {
310: if (c == '|') {
311: this .curAttr.checkEnum();
312: }
313: }
314: }
315:
316: /**
317: * Called for each word found in a DTD definition. This can be a
318: * comment, element or attlist definition. For example, this method is
319: * called for each name element found within the scope of an element
320: * (<!element (element1, element2, ...)>. The first element name doesn't
321: * generate a call to this method (@see startElement).
322: *
323: * This is where the subtree of the element definition is built.
324: * The element to add might be a child or sibling to the previous
325: * element. If the element is preceded by a '(', this is child
326: * (@see startGroupElements), otherwise the element is a sibling.
327: *
328: * @param name the name of the element defined within the <!element ...>
329: * declaration.
330: * @param instance has one of the three values: TYPE_0_1,
331: * TYPE_1, TYPE_0_N, TYPE_1_N
332: *
333: */
334: public void element(String uniqueName, String typeName,
335: String attrName, String attrNamespace, int instance,
336: boolean externalType, String defaultValue) {
337: if (DDLogFlags.debug) {
338: TraceLogger.put(TraceLogger.DEBUG, TraceLogger.SVC_DD,
339: DDLogFlags.DBG_DTD, 1, DDLogFlags.ELEMENT, attrName
340: + " : " + typeName
341: + instanceToString(instance, false));
342: }
343: try {
344: if (curElementType == Common.NONE && !externalType) {
345: if (DDLogFlags.debug)
346: System.out.println("Top element def for "
347: + attrName);
348: GraphNode attrNode = getGraphNode(uniqueName, attrName,
349: CREATE);
350: GraphNode node = getGraphNode(uniqueName, typeName,
351: REFERENCE);
352: attrNode.setAlias(node);
353: } else if (curElementType == Common.ELEMENT) {
354: // Get the element reference
355: GraphNode node = getGraphNode(uniqueName, typeName,
356: REFERENCE);
357:
358: GraphLink link = new GraphLink(attrName, attrNamespace);
359: link.setDefaultValue(defaultValue);
360: //System.out.println("curParentGroup="+curParentGroup+" curParentGroup.sibling="+curParentGroup.getSibling());
361: curParentGroup.addChild(link);
362: link.element = node;
363: link.setElementInstance(instance);
364:
365: if (externalType)
366: node.setJavaType(typeName);
367: //System.out.println("Created new GraphLink: "+link+" link.parent="+link.parent+" link.sibling="+link.sibling);
368: } else if (curElementType == Common.ATTLIST) {
369: // If the current attribute is completly built, signal the
370: // Parser by throwing the exception.
371: if (this .curAttr.isComplete())
372: throw new DocDefParser.MissingEndOfEltException(
373: curAttr.getPropertyName());
374:
375: if (defaultValue != null)
376: this .curAttr.setDefaultValue(defaultValue);
377: this .curAttr.addValue(attrName, attrNamespace);
378: if (externalType)
379: curAttr.setJavaType(typeName);
380: }
381: } catch (Schema2BeansException e) {
382: throw new Schema2BeansRuntimeException(e);
383: }
384: }
385:
386: public void element(String uniqueName, String typeName, int instance) {
387: element(uniqueName, typeName, typeName, null, instance, false,
388: null);
389: }
390:
391: public void addExtraDataNode(String uniqueName, String typeName,
392: Object data) throws Schema2BeansException {
393: //System.out.println("** addExtraDataNode: typeName="+typeName+" data="+data);
394: GraphNode node = getGraphNode(uniqueName, typeName, GET);
395: node.addExtraData(data);
396: }
397:
398: public void addExtraDataCurLink(Object data) {
399: //System.out.println("** addExtraDataCurLink: data="+data+" curParentGroup.name="+((curParentGroup == null) ? "curParentGroup null" : curParentGroup.name));
400: if (curElementType == Common.ATTLIST) {
401: //System.out.println("curElementType == Common.ATTLIST, curAttr="+curAttr);
402: if (curAttr != null)
403: curAttr.addExtraData(data);
404: } else {
405: //System.out.println("lastChild="+curParentGroup.getLastChild());
406: if (curParentGroup != null
407: && curParentGroup.getLastChild() != null) {
408: curParentGroup.getLastChild().extraData.add(data);
409: }
410: }
411: }
412:
413: public void addExtraDataNode(String uniqueName, String typeName,
414: Object[] data) throws Schema2BeansException {
415: //System.out.println("** addExtraDataNode: typeName="+typeName+" data="+data);
416: GraphNode node = getGraphNode(uniqueName, typeName, GET);
417: if (data != null)
418: for (int i = 0; i < data.length; i++)
419: node.addExtraData(data[i]);
420: }
421:
422: public void setUnion(String uniqueName, String typeName,
423: boolean value) throws Schema2BeansException {
424: GraphNode node = getGraphNode(uniqueName, typeName, GET);
425: node.setUnion(value);
426: }
427:
428: public void addExtraDataCurLink(Object[] data) {
429: //System.out.println("** addExtraDataCurLink: data="+data+" curParentGroup.name="+((curParentGroup == null) ? "curParentGroup null" : curParentGroup.name));
430: if (curElementType == Common.ATTLIST) {
431: //System.out.println("curElementType == Common.ATTLIST, curAttr="+curAttr);
432: if (curAttr != null && data != null)
433: for (int i = 0; i < data.length; i++)
434: curAttr.addExtraData(data[i]);
435: } else {
436: //System.out.println("lastChild="+curParentGroup.getLastChild());
437: if (curParentGroup != null
438: && curParentGroup.getLastChild() != null) {
439: if (data != null)
440: for (int i = 0; i < data.length; i++)
441: curParentGroup.getLastChild().extraData
442: .add(data[i]);
443: }
444: }
445: }
446:
447: public void nillable(boolean value) {
448: //System.out.println("nillable="+value);
449: if (curParentGroup != null
450: && curParentGroup.getLastChild() != null)
451: curParentGroup.getLastChild().setNillable(value);
452: /*
453: else
454: System.err.println("no parent group for nillable");
455: */
456: }
457:
458: public void setAbstract(String uniqueName, String name,
459: boolean value) {
460: //System.out.println("Setting javaType of "+name+" to "+javaType);
461: if (this .curElementType == Common.ATTLIST) {
462: } else {
463: // Get the element reference
464: GraphNode node;
465: try {
466: node = getGraphNode(uniqueName, name, GET);
467: } catch (Schema2BeansException e) {
468: throw new Schema2BeansRuntimeException(e);
469: }
470: node.setAbstract(value);
471: }
472: }
473:
474: /**
475: * set an extended property on a GraphNode
476: */
477: public void setExtendedProperty(String uniqueName, String typeName,
478: String propertyName, Object value)
479: throws Schema2BeansException {
480: GraphNode node = getGraphNode(uniqueName, typeName, GET);
481: node.setExtendedProperty(propertyName, value);
482: }
483:
484: /**
485: * Called to request that the graph node named name be of a certain
486: * Java class. If the current element type is an attribute, then
487: * we set the javaType of that attribute instead.
488: * @param javaType is the name of a Java class (eg, "java.lang.Integer", or "int").
489: */
490: public void javaType(String uniqueName, String name, String javaType) {
491: //System.out.println("Setting javaType of "+name+" to "+javaType);
492: if (this .curElementType == Common.ATTLIST) {
493: curAttr.setJavaType(javaType);
494: } else {
495: // Get the element reference
496: GraphNode node;
497: try {
498: node = getGraphNode(uniqueName, name, GET);
499: } catch (Schema2BeansException e) {
500: throw new Schema2BeansRuntimeException(e);
501: }
502: node.setJavaType(javaType);
503: node.setCreated(false);
504: }
505: }
506:
507: public void setExtension(String uniqueName, String typeName,
508: String extendsName) throws Schema2BeansException {
509: if (curElementType == Common.ATTLIST) {
510: } else {
511: GraphNode node;
512: GraphNode extendsNode;
513: try {
514: node = getGraphNode(uniqueName, typeName, GET);
515: extendsNode = getGraphNode(null, extendsName, REFERENCE);
516: } catch (Schema2BeansException e) {
517: throw new Schema2BeansRuntimeException(e);
518: }
519: node.setExtension(extendsNode);
520: }
521: }
522:
523: /**
524: * Called when a parenthese is found, meaning that the following
525: * elements (element() calls) should be considered as semantically
526: * grouped.
527: *
528: * Creates a child GraphLink from the current link to group
529: * all the further elements of this group. If any propriety
530: * is defined for this group (as *, ? or + or |) this will be set later
531: * on the current link (as the parent of any of the elements graph link
532: * objects).
533: */
534: public void startGroupElements() {
535: if (DDLogFlags.debug) {
536: TraceLogger.put(TraceLogger.DEBUG, TraceLogger.SVC_DD,
537: DDLogFlags.DBG_DTD, 5, DDLogFlags.STARTGRP);
538: }
539:
540: // A new parenthese in the parsing makes a new GraphLink.
541: if (this .curElementType == Common.ELEMENT) {
542: GraphLink link = new GraphLink(null);
543: curParentGroup.addChild(link);
544: curParentGroup = link;
545: //System.out.println("curParentGroup="+curParentGroup);
546: } else {
547: if (this .curElementType == Common.ATTLIST)
548: this .curAttr.setEnum(true);
549: }
550: }
551:
552: /**
553: * We are done creating the elements of a same group,
554: * set the current link to the parent of the group.
555: * This will allow either to start creating siblings (if element()
556: * is called) or go the next parent level (if this same method
557: * is called again).
558: */
559: public void endGroupElements(int instance) {
560: if (DDLogFlags.debug) {
561: TraceLogger.put(TraceLogger.DEBUG, TraceLogger.SVC_DD,
562: DDLogFlags.DBG_DTD, 5, DDLogFlags.ENDGRP,
563: instanceToString(instance, false));
564: }
565:
566: if (curElementType == Common.ELEMENT) {
567: curParentGroup.setGroupInstance(instance);
568: //System.out.println("curParentGroup="+curParentGroup+" instance="+instance);
569: curParentGroup = curParentGroup.getParent();
570: } else if (this .curElementType == Common.ATTLIST)
571: this .curAttr.setEnum(false);
572: }
573:
574: public void setDefaultNamespace(String ns) {
575: defaultNamespace = ns;
576: }
577:
578: public String getDefaultNamespace() {
579: return defaultNamespace;
580: }
581:
582: private void findRootNode() throws Schema2BeansException {
583: //
584: // Find out who's the root of the graph
585: // The root of the graph is the graph node that has not been
586: // referenced. We might find zero, one or several nodes that
587: // could be the root:
588: // 0: throw an exception
589: // 1: use the node as the root (check with doc root if specified)
590: // 1-n: use the doc root value if specified or ask
591: // for the node to use.
592: //
593: Iterator it = this .nameHash.keySet().iterator();
594: GraphNode node;
595: int count = 0;
596: List list = new ArrayList();
597:
598: while (it.hasNext()) {
599: String uniqueName = (String) it.next();
600: node = (GraphNode) nameHash.get(uniqueName);
601: if (DDLogFlags.debug) {
602: System.out.println("refCount=" + node.getRefCount()
603: + " created=" + node.isCreated() + " javaType="
604: + node.getJavaType() + " uniqueName="
605: + uniqueName + " node=" + node);
606: }
607: if (node.isCreated() && node.getRefCount() == 0) {
608: count++;
609: list.add(node);
610: }
611: }
612:
613: if (count > 1) {
614: // Attempt to find 1 that is most qualified
615: int highestPoints = 0;
616: GraphNode highestNode = null;
617: int tieCount = 0;
618: for (Iterator highit = list.iterator(); highit.hasNext();) {
619: int points = 0;
620: node = (GraphNode) highit.next();
621: if (node.getAlias() != null) {
622: ++points;
623: if (node.getAlias().getRefCount() == 1)
624: ++points;
625: }
626: // See if the default namespace is the same as this node's.
627: if (defaultNamespace == null ? node.getNamespace() == null
628: : defaultNamespace.equals(node.getNamespace()))
629: ++points;
630: GraphLink link = node.getGraphLink();
631: //System.out.println("link="+link+" link.name="+link.name);
632: if (link != null && !"#PCDATA".equals(link.name)) {
633: ++points;
634: GraphLink firstChild = link.getFirstChild();
635: if (firstChild != null) {
636: ++points;
637: if (firstChild.getSibling() != null)
638: ++points;
639: if (firstChild.getFirstChild() != null)
640: ++points;
641: }
642: GraphLink sibling = link.getSibling();
643: if (sibling != null) {
644: ++points;
645: if (sibling.getSibling() != null)
646: ++points;
647: if (sibling.getFirstChild() != null)
648: ++points;
649: }
650: }
651: //System.out.println("points="+points+" node="+node);
652: if (points > highestPoints) {
653: highestPoints = points;
654: highestNode = node;
655: tieCount = 0;
656: } else if (points == highestPoints) {
657: ++tieCount;
658: }
659: }
660: if (tieCount == 0 && highestNode != null) {
661: count = 1;
662: list.clear();
663: list.add(highestNode);
664: }
665: }
666:
667: if (count == 1) {
668: this .rootNode = (GraphNode) list.get(0);
669: // Only one element not referenced in the graph
670: if (this .docRoot != null) {
671: if (!this .docRoot.equals(this .rootNode.getName())) {
672: String str = "Mismatch between doc root name specified ("
673: + this .docRoot
674: + ") and the root name found in the DTD graph ("
675: + this .rootNode.getName() + ")";
676: throw new IllegalStateException(str);
677: }
678: }
679: } else if (count == 0) {
680: this .rootNode = null;
681: if (docRoot != null) {
682: it = this .nameHash.values().iterator();
683: while (it.hasNext()) {
684: node = (GraphNode) it.next();
685: if (docRoot.equals(node.getName())) {
686: rootNode = node;
687: break;
688: }
689: }
690: }
691: if (rootNode == null)
692: throw new IllegalStateException(Common
693: .getMessage("NoRootElementCandidate"));
694: } else {
695: // List the elements and pick the root (if specified) or ask
696: config.messageOut
697: .println("The following elements could be the root "
698: + "of the document:");
699: for (int i = 0; i < list.size(); i++) {
700: GraphNode n = (GraphNode) list.get(i);
701: config.messageOut.print((i + 1) + ". " + n);
702: if ((this .docRoot != null)
703: && (this .docRoot.equals(n.getName()))) {
704:
705: this .rootNode = n;
706: config.messageOut.println(" <= parameter value");
707: } else
708: config.messageOut.println("");
709: }
710:
711: if (this .rootNode == null) {
712: // We still don't know - ask for the element to use
713:
714: String errStr = "Could not find the root of the document. "
715: + "Use the -d option to specify the doc root";
716:
717: if (config.isAuto()) {
718: throw new IllegalStateException(errStr);
719: }
720:
721: try {
722: BufferedReader rd = new BufferedReader(
723: new InputStreamReader(System.in));
724:
725: config.messageOut
726: .print("Enter the element that should be used "
727: + "as the root: ");
728: String str = rd.readLine();
729:
730: int i = Integer.parseInt(str) - 1;
731:
732: if (i < 0 || i >= list.size()) {
733: throw new IllegalStateException(errStr);
734: } else {
735: this .rootNode = (GraphNode) list.get(i);
736: }
737: } catch (Exception e) {
738: TraceLogger.error(e);
739: throw new Schema2BeansNestedException(errStr, e);
740: }
741: }
742: }
743:
744: if (DDLogFlags.debug)
745: config.messageOut.println("Using "
746: + this .rootNode.getName()
747: + " as the root of the document.");
748: }
749:
750: //
751: static String instanceToString(int instance, boolean bean) {
752: switch (instance) {
753: case Common.TYPE_0_1:
754: if (bean)
755: return "[0,1]";
756: else
757: return "?";
758: case Common.TYPE_0_N:
759: if (bean)
760: return "[0,n]";
761: else
762: return "*";
763: case Common.TYPE_1_N:
764: if (bean)
765: return "[1,n]";
766: else
767: return "+";
768: }
769: return "";
770: }
771:
772: //
773: static String typeToString(int type) {
774: switch (type) {
775: case Common.COMMENT:
776: return "comment";
777: case Common.ELEMENT:
778: return "element";
779: case Common.ATTLIST:
780: return "attlist";
781: }
782: return "unknown value: " + type;
783: }
784:
785: /**
786: * TreeParser interface. This is what the BeanBuilder uses to get
787: * elements of the tree. The goal is to try to keep separated
788: * the object graph implementation from its usage.
789: * Not sure, this is very useful though, since the tree builder
790: * knows the gory details of the graph. Just a gentle way to ask
791: * for the graph.
792: */
793: public GraphNode[] getNodes() {
794: //
795: // Try to give the results back with some order from top to bottom
796: // (right now, it's BFS (Breadth First Search)).
797: //
798: int maxSize = nameHash.values().size();
799: List ret = new ArrayList(maxSize);
800: Map insertedNodes = new HashMap();
801: Map ignoredNodes = new HashMap();
802: getNodesInsertNode(rootNode, ret, insertedNodes, ignoredNodes);
803: getNodes(rootNode.getGraphLink(), ret, insertedNodes,
804: ignoredNodes);
805: if (!config.isRemoveUnreferencedNodes()) {
806: for (Iterator it = nameHash.values().iterator(); it
807: .hasNext();) {
808: GraphNode node = (GraphNode) it.next();
809: if (!insertedNodes.containsKey(node)
810: && !ignoredNodes.containsKey(node)) {
811: config.messageOut.println(Common.getMessage(
812: "MSG_FoundUnreferencedNode", node
813: .toString()));
814: ret.add(node);
815: }
816: }
817: }
818: /*
819: for (int i = 0; i < maxSize; ++i)
820: System.out.println("ret["+i+"]="+ret[i]);
821: */
822: return (GraphNode[]) ret.toArray(new GraphNode[ret.size()]);
823: }
824:
825: private void getNodes(GraphLink l, List ret, Map insertedNodes,
826: Map ignoredNodes) {
827: Stack linkStack = new Stack();
828: linkStack.push(l);
829: while (!linkStack.isEmpty()) {
830: l = (GraphLink) linkStack.pop();
831: for (; l != null; l = l.getSibling()) {
832: if (l.element != null) {
833: if (!insertedNodes.containsKey(l.element)) {
834: getNodesInsertNode(l.element, ret,
835: insertedNodes, ignoredNodes);
836: linkStack.push(l.element.getGraphLink());
837: }
838: }
839: linkStack.push(l.getFirstChild());
840: }
841: }
842: }
843:
844: private void getNodesInsertNode(GraphNode node, List ret,
845: Map insertedNodes, Map ignoredNodes) {
846: if (insertedNodes.containsKey(node)) {
847: //System.out.println("Found a duplicate in my insert journey: "+node);
848: return;
849: }
850: ret.add(node);
851: insertedNodes.put(node, null);
852: if (node.getExtension() != null)
853: getNodesInsertNode(node.getExtension(), ret, insertedNodes,
854: ignoredNodes);
855: GraphNode alias = node.getAlias();
856: if (alias != null && !insertedNodes.containsKey(alias)) {
857: if (alias.getRefCount() <= 1) {
858: // Only referenced by the thing which has an alias pointer to it.
859: ignoredNodes.put(alias, null);
860: } else {
861: ret.add(alias);
862: insertedNodes.put(alias, null);
863: }
864: }
865: return;
866: }
867:
868: public GraphNode getNode(String uniqueName) {
869: return (GraphNode) this .nameHash.get(uniqueName);
870: }
871:
872: public GraphNode getRoot() {
873: return this .rootNode;
874: }
875:
876: private static final String INDENT = " ";
877:
878: static void dumpAttributes(GraphNode elt, StringBuffer str,
879: String indent) {
880: AttrProp[] attrList = elt.getAttributes();
881:
882: for (int i = 0; i < attrList.length; i++)
883: str.append(indent + INDENT + "[attr: " + attrList[i]
884: + "]\n");
885: }
886:
887: static void dumpTree(List children, StringBuffer str,
888: String indent, boolean tree) {
889: for (Iterator it = children.iterator(); it.hasNext();) {
890: GraphLink l = (GraphLink) it.next();
891: dumpTree(l, str, indent, tree);
892: }
893: }
894:
895: static void dumpTree(GraphLink l, StringBuffer str, String indent,
896: boolean tree) {
897: if (l == null)
898: return;
899: //str.append("dumpTree: l.name="+l.name+" l="+l+"\n");
900: if (l.element != null) {
901: //str.append("dumpTree: l.element="+l.element+"\n");
902: str.append(indent);
903: str.append(l.name + " : " + l.element.toString());
904: str.append(instanceToString(l.getElementInstance(), false)
905: + "\n");
906:
907: dumpAttributes(l.element, str, indent);
908:
909: if (tree && (l.element.getMarked() == false)) {
910: l.element.setMarked(true);
911: dumpTree(l.element.getGraphLink(), str, indent + INDENT
912: + instanceToString(l.getGroupInstance(), false)
913: + (l.isSequenceOr() ? "| " : " "), tree);
914: l.element.setMarked(false);
915: }
916: }
917:
918: if (l.isSequenceOr() || (l.getGroupInstance() != Common.TYPE_1)) {
919: str.append(indent
920: + instanceToString(l.getGroupInstance(), false)
921: + (l.isSequenceOr() ? "|\n" : "\n"));
922: }
923:
924: dumpTree(l.getChildren(), str, indent + INDENT, tree);
925: }
926:
927: //
928: public String dump() {
929: StringBuffer str = new StringBuffer();
930: GraphNode n;
931:
932: str.append("Tree:\n");
933: str.append(this .rootNode.toString());
934: str.append("\n");
935:
936: dumpAttributes(rootNode, str, INDENT);
937: dumpTree(this .rootNode.getGraphLink(), str, INDENT, true);
938:
939: return str.toString();
940: }
941:
942: public void setPrefixGuesser(PrefixGuesser guesser) {
943: prefixGuesser = guesser;
944: }
945:
946: public PrefixGuesser getPrefixGuesser() {
947: return prefixGuesser;
948: }
949: }
950:
951: //******************************************************************************
952: // END_NOI18N
953: // This class does not (and will not) cantain strings that need to be localized.
954: //******************************************************************************
|