001: /*
002: Copyright (c) 2004-2006, Dennis M. Sosnoski
003: All rights reserved.
004:
005: Redistribution and use in source and binary forms, with or without modification,
006: are permitted provided that the following conditions are met:
007:
008: * Redistributions of source code must retain the above copyright notice, this
009: list of conditions and the following disclaimer.
010: * Redistributions in binary form must reproduce the above copyright notice,
011: this list of conditions and the following disclaimer in the documentation
012: and/or other materials provided with the distribution.
013: * Neither the name of JiBX nor the names of its contributors may be used
014: to endorse or promote products derived from this software without specific
015: prior written permission.
016:
017: THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
018: ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
019: WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
020: DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
021: ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
022: (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
023: LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
024: ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
025: (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
026: SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
027: */
028:
029: package org.jibx.binding.model;
030:
031: import java.util.ArrayList;
032: import java.util.HashSet;
033:
034: import org.jibx.binding.util.ObjectStack;
035:
036: /**
037: * Handles walking the tree structure of a binding model, tracking
038: * order-dependent state information collected along the way.
039: *
040: * @author Dennis M. Sosnoski
041: */
042: public class TreeContext {
043: /** Global definition context (outside of binding). */
044: private DefinitionContext m_globalContext;
045:
046: /** Binding element model root (may be <code>null</code>, if not configured
047: by caller). */
048: private BindingElement m_bindingRoot;
049:
050: /** Stack of items for parent hierarchy to current node in tree. */
051: private ObjectStack m_treeHierarchy;
052:
053: /** Class locator set by environment code. */
054: private IClassLocator m_locator;
055:
056: /** Set of elements to be skipped in walking tree. */
057: private HashSet m_skipSet;
058:
059: /**
060: * Internal null constructor.
061: */
062: private TreeContext() {
063: }
064:
065: /**
066: * Constructor.
067: *
068: * @param iloc class locator to be used
069: */
070: public TreeContext(IClassLocator iloc) {
071: m_treeHierarchy = new ObjectStack();
072: m_locator = iloc;
073: m_skipSet = new HashSet();
074: }
075:
076: /**
077: * Get a secondary context for the same tree as this instance. The secondary
078: * context shares the same skip set, context, and binding root as the
079: * original context. This allows activites invoked during the touring of the
080: * original tree to start subtours of their own part of the tree.
081: *
082: * @return new context linked to original context
083: */
084: public TreeContext getChildContext() {
085: TreeContext child = new TreeContext();
086: child.m_bindingRoot = m_bindingRoot;
087: child.m_globalContext = m_globalContext;
088: child.m_locator = m_locator;
089: child.m_skipSet = m_skipSet;
090: child.m_treeHierarchy = new ObjectStack(m_treeHierarchy);
091: return child;
092: }
093:
094: /**
095: * Set the global definition context. This context is external to the actual
096: * binding definition, providing defaults that can be overridden by values
097: * set within the actual binding.
098: *
099: * @param dctx global definition context
100: */
101: public void setGlobalDefinitions(DefinitionContext dctx) {
102: m_globalContext = dctx;
103: }
104:
105: /**
106: * Tour complete binding model tree. This tours the entire binding model,
107: * starting from the root binding element. Using this method automatically
108: * sets the root binding element for access by processing performed during
109: * the tour. It <b>must</b> be used for the binding element in order to
110: * handle included binding definitions properly.
111: *
112: * @param root binding element root of tree
113: * @param visitor target visitor for element notifications
114: */
115: public void tourTree(BindingElement root, ModelVisitor visitor) {
116:
117: // set up binding root reference for access during processing
118: BindingElement hold = m_bindingRoot;
119: m_bindingRoot = root;
120:
121: // run the actual tour
122: tourTree((ElementBase) root, visitor);
123:
124: // restore prior binding root reference
125: m_bindingRoot = hold;
126: }
127:
128: /**
129: * Tour binding model tree. This recursively traverses the binding model
130: * tree rooted in the supplied element, notifying the visitor of each
131: * element visited during the traversal. Elements with fatal errors are
132: * skipped in processing, along with all child elements. The method may
133: * itself be called recursively.
134: *
135: * @param root node of tree to be toured
136: * @param visitor target visitor for element notifications
137: */
138: public void tourTree(ElementBase root, ModelVisitor visitor) {
139:
140: // check for fatal error on element
141: if (m_skipSet.contains(root)) {
142: return;
143: }
144:
145: // visit the actual root of tree
146: boolean expand = false;
147: m_treeHierarchy.push(root);
148: switch (root.type()) {
149:
150: case ElementBase.BINDING_ELEMENT:
151: expand = visitor.visit((BindingElement) root);
152: break;
153:
154: case ElementBase.COLLECTION_ELEMENT:
155: expand = visitor.visit((CollectionElement) root);
156: break;
157:
158: case ElementBase.FORMAT_ELEMENT:
159: visitor.visit((FormatElement) root);
160: break;
161:
162: case ElementBase.INCLUDE_ELEMENT:
163: expand = visitor.visit((IncludeElement) root);
164: break;
165:
166: case ElementBase.INPUT_ELEMENT:
167: expand = visitor.visit((InputElement) root);
168: break;
169:
170: case ElementBase.MAPPING_ELEMENT:
171: expand = visitor.visit((MappingElement) root);
172: break;
173:
174: case ElementBase.NAMESPACE_ELEMENT:
175: visitor.visit((NamespaceElement) root);
176: break;
177:
178: case ElementBase.OUTPUT_ELEMENT:
179: expand = visitor.visit((OutputElement) root);
180: break;
181:
182: case ElementBase.SPLIT_ELEMENT:
183: expand = visitor.visit((SplitElement) root);
184: break;
185:
186: case ElementBase.STRUCTURE_ELEMENT:
187: expand = visitor.visit((StructureElement) root);
188: break;
189:
190: case ElementBase.TEMPLATE_ELEMENT:
191: expand = visitor.visit((TemplateElement) root);
192: break;
193:
194: case ElementBase.VALUE_ELEMENT:
195: visitor.visit((ValueElement) root);
196: break;
197:
198: default:
199: throw new IllegalStateException(
200: "Internal error: unknown element type");
201:
202: }
203:
204: // check for expansion needed
205: if (expand && !m_skipSet.contains(root)) {
206: if (root instanceof IncludeElement) {
207:
208: // include just delegates to the included binding element
209: BindingElement binding = ((IncludeElement) root)
210: .getBinding();
211: if (binding != null) {
212: m_treeHierarchy.pop();
213: tourTree((ElementBase) binding, visitor);
214: m_treeHierarchy.push(root);
215: }
216:
217: } else if (root instanceof NestingElementBase) {
218:
219: // process each container child as root of own tree
220: ArrayList childs = null;
221: if (root instanceof MappingElement) {
222: childs = ((MappingElement) root).topChildren();
223: for (int i = 0; i < childs.size(); i++) {
224: tourTree((ElementBase) childs.get(i), visitor);
225: }
226: }
227: if (root instanceof BindingElement) {
228: childs = ((BindingElement) root).topChildren();
229: } else {
230: childs = ((NestingElementBase) root).children();
231: }
232: for (int i = 0; i < childs.size(); i++) {
233: tourTree((ElementBase) childs.get(i), visitor);
234: }
235: }
236: }
237:
238: // exit the actual root of tree
239: switch (root.type()) {
240:
241: case ElementBase.BINDING_ELEMENT:
242: visitor.exit((BindingElement) root);
243: break;
244:
245: case ElementBase.COLLECTION_ELEMENT:
246: visitor.exit((CollectionElement) root);
247: break;
248:
249: case ElementBase.INCLUDE_ELEMENT:
250: visitor.exit((IncludeElement) root);
251: break;
252:
253: case ElementBase.INPUT_ELEMENT:
254: visitor.exit((InputElement) root);
255: break;
256:
257: case ElementBase.MAPPING_ELEMENT:
258: visitor.exit((MappingElement) root);
259: break;
260:
261: case ElementBase.OUTPUT_ELEMENT:
262: visitor.exit((OutputElement) root);
263: break;
264:
265: case ElementBase.SPLIT_ELEMENT:
266: visitor.exit((SplitElement) root);
267: break;
268:
269: case ElementBase.STRUCTURE_ELEMENT:
270: visitor.exit((StructureElement) root);
271: break;
272:
273: case ElementBase.TEMPLATE_ELEMENT:
274: visitor.exit((TemplateElement) root);
275: break;
276:
277: case ElementBase.VALUE_ELEMENT:
278: visitor.exit((ValueElement) root);
279: break;
280:
281: default:
282: break;
283:
284: }
285: m_treeHierarchy.pop();
286: }
287:
288: /**
289: * Get depth of nesting in binding.
290: *
291: * @return nesting depth
292: */
293: public int getNestingDepth() {
294: return m_treeHierarchy.size();
295: }
296:
297: /**
298: * Peek current element of hierarchy.
299: *
300: * @return current element
301: */
302: protected ElementBase peekElement() {
303: return (ElementBase) m_treeHierarchy.peek();
304: }
305:
306: /**
307: * Check if a component is being skipped due to a fatal error.
308: *
309: * @param obj component to be checked
310: * @return flag for component being skipped
311: */
312: public boolean isSkipped(Object obj) {
313: return m_skipSet.contains(obj);
314: }
315:
316: /**
317: * Add element to set to be skipped.
318: *
319: * @param skip
320: */
321: protected void addSkip(Object skip) {
322: if (skip instanceof ElementBase) {
323: m_skipSet.add(skip);
324: }
325: }
326:
327: /**
328: * Get root element of binding.
329: *
330: * @return root element of binding
331: * @throws IllegalStateException if no root element known
332: */
333: public BindingElement getBindingRoot() {
334: if (m_bindingRoot == null) {
335: throw new IllegalStateException("No binding root defined");
336: } else {
337: return m_bindingRoot;
338: }
339: }
340:
341: /**
342: * Set root element of binding. This should be called by the user if an
343: * element other than the binding element is going to be used as the root
344: * for a tour.
345: *
346: * @param root root element of binding
347: */
348: public void setBindingRoot(BindingElement root) {
349: m_bindingRoot = root;
350: }
351:
352: /**
353: * Get containing element. This is equivalent to the generation
354: * <code>1</code> parent, except that it checks for the case where there's
355: * no parent present.
356: *
357: * @return binding definition component for parent element, or
358: * <code>null</code> if no parent
359: */
360: public NestingElementBase getParentElement() {
361: if (m_treeHierarchy.size() > 1) {
362: return (NestingElementBase) m_treeHierarchy.peek(1);
363: } else {
364: return null;
365: }
366: }
367:
368: /**
369: * Get containing element at generation level. All except the zero-level
370: * containing element are guaranteed to be instances of {@link
371: * org.jibx.binding.model.NestingElementBase}.
372: *
373: * @param level generation level of parent
374: * @return binding definition component for parent at level
375: */
376: public ElementBase getParentElement(int level) {
377: return (ElementBase) m_treeHierarchy.peek(level);
378: }
379:
380: /**
381: * Get parent container information. This returns the innermost containing
382: * binding component which refers to an object.
383: *
384: * @return innermost containing element referencing bound object
385: */
386: public ContainerElementBase getParentContainer() {
387: int index = 1;
388: while (index < m_treeHierarchy.size()) {
389: NestingElementBase nest = (NestingElementBase) m_treeHierarchy
390: .peek(index++);
391: if (nest instanceof ContainerElementBase) {
392: return (ContainerElementBase) nest;
393: }
394: }
395: throw new IllegalStateException("Internal error: no container");
396: }
397:
398: /**
399: * Get parent container with linked object. This returns the innermost
400: * containing binding component which defines a context object.
401: *
402: * @return innermost containing element defining a context object
403: */
404: public ContainerElementBase getContextObject() {
405: int index = 1;
406: while (index < m_treeHierarchy.size()) {
407: NestingElementBase nest = (NestingElementBase) m_treeHierarchy
408: .peek(index++);
409: if (nest instanceof ContainerElementBase) {
410: ContainerElementBase contain = (ContainerElementBase) nest;
411: if (contain.hasObject()) {
412: return contain;
413: }
414: }
415: }
416: throw new IllegalStateException(
417: "Internal error: no context object");
418: }
419:
420: /**
421: * Check if binding supports input.
422: *
423: * @return <code>true</code> if input binding, <code>false</code> if not
424: */
425: public boolean isInBinding() {
426: return m_bindingRoot == null ? true : m_bindingRoot
427: .isInBinding();
428: }
429:
430: /**
431: * Check if binding supports output.
432: *
433: * @return <code>true</code> if output binding, <code>false</code> if not
434: */
435: public boolean isOutBinding() {
436: return m_bindingRoot == null ? true : m_bindingRoot
437: .isOutBinding();
438: }
439:
440: /**
441: * Get innermost containing definition context.
442: *
443: * @return innermost definition context containing this element
444: */
445: public DefinitionContext getDefinitions() {
446: int index = 1;
447: while (index < m_treeHierarchy.size()) {
448: NestingElementBase nest = (NestingElementBase) m_treeHierarchy
449: .peek(index++);
450: if (nest.getDefinitions() != null) {
451: return nest.getDefinitions();
452: }
453: }
454: if (m_globalContext == null) {
455: throw new IllegalStateException(
456: "Internal error: no definition context");
457: } else {
458: return m_globalContext;
459: }
460: }
461:
462: /**
463: * Get definition context for innermost nesting element. If the context for
464: * this element isn't already defined it's created by the call.
465: *
466: * @return definition context for innermost nesting element
467: */
468: public DefinitionContext getCurrentDefinitions() {
469: NestingElementBase parent = getParentElement();
470: DefinitionContext dctx = parent.getDefinitions();
471: if (dctx == null) {
472: dctx = new DefinitionContext(getDefinitions());
473: parent.setDefinitions(dctx);
474: }
475: return dctx;
476: }
477:
478: /**
479: * Get definition context for innermost nesting element for use by a
480: * <b>format</b> (or <b>namespace</b>). If the context for this element
481: * isn't already defined it's created by the call, along with the contexts
482: * for any containing elements. This is ugly, but necessary to keep the tree
483: * structure of contexts from getting split when other items are added by
484: * the registration pass (since the formats are registered in the
485: * prevalidation pass).
486: *
487: * @return definition context for innermost nesting element
488: */
489: public DefinitionContext getFormatDefinitions() {
490: NestingElementBase parent = getParentElement();
491: DefinitionContext dctx = parent.getDefinitions();
492: if (dctx == null) {
493:
494: // scan to find innermost nesting with context
495: int index = 1;
496: DefinitionContext pctx = null;
497: while (++index < m_treeHierarchy.size()) {
498: NestingElementBase nest = (NestingElementBase) m_treeHierarchy
499: .peek(index);
500: pctx = nest.getDefinitions();
501: if (pctx != null) {
502: break;
503: }
504: }
505:
506: // add contexts for all ancestors to level
507: while (index >= 2) {
508: dctx = new DefinitionContext(pctx);
509: NestingElementBase nest = (NestingElementBase) m_treeHierarchy
510: .peek(--index);
511: nest.setDefinitions(dctx);
512: pctx = dctx;
513: }
514: }
515: return dctx;
516: }
517:
518: /**
519: * Get class information. Finds a class by name using the class locator
520: * configured by the environment code.
521: *
522: * @param name fully-qualified name of class to be found
523: * @return class information, or <code>null</code> if class not found
524: */
525: public IClass getClassInfo(String name) {
526: return m_locator.getClassInfo(name);
527: }
528:
529: /**
530: * Get requiried class information. Finds a class by name using the class
531: * locator configured by the environment code. If the class cannot be found
532: * a runtime exception is thrown.
533: *
534: * @param name fully-qualified name of class to be found
535: * @return class information
536: */
537: public IClass getRequiredClassInfo(String name) {
538: IClass iclas = m_locator.getClassInfo(name);
539: if (iclas == null) {
540: throw new IllegalStateException("Internal error: class "
541: + name + " cannot be found");
542: } else {
543: return iclas;
544: }
545: }
546: }
|