001: /*
002: Copyright (c) 2004-2007, 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:
033: import org.jibx.binding.util.StringArray;
034:
035: /**
036: * Model component for elements that can contain detailed binding information in
037: * the form of nested child components. Elements of this type include
038: * <b>mapping</b>, <b>template</b>, <b>structure</b>, and <b>collection</b>
039: * elements.
040: *
041: * @author Dennis M. Sosnoski
042: */
043: public abstract class ContainerElementBase extends NestingElementBase {
044: /** Enumeration of allowed attribute names */
045: public static final StringArray s_allowedAttributes = new StringArray(
046: NestingElementBase.s_allowedAttributes, new StringArray(
047: ObjectAttributes.s_allowedAttributes,
048: StructureAttributes.s_allowedAttributes));
049:
050: /** Object attributes information for nesting. */
051: private ObjectAttributes m_objectAttrs;
052:
053: /** Structure attributes information for nesting. */
054: private StructureAttributes m_structureAttrs;
055:
056: /** Label for this structure definition. */
057: private String m_label;
058:
059: /** Label for structure to be used as definition. */
060: private String m_using;
061:
062: /** Child component that contributes an ID (<code>null</code> if none). */
063: private IComponent m_idChild;
064:
065: /** Flag for child classification in progress. */
066: private boolean m_inClassify;
067:
068: /** Child components defining content (created during validation, contains
069: subset of child components defining element or character data content). */
070: private ArrayList m_contentComponents;
071:
072: /** Child components defining attributes (created during validation,
073: contains subset of child components defining attributes). */
074: private ArrayList m_attributeComponents;
075:
076: /**
077: * Constructor.
078: *
079: * @param type element type code
080: */
081: protected ContainerElementBase(int type) {
082: super (type);
083: m_objectAttrs = new ObjectAttributes();
084: m_structureAttrs = new StructureAttributes();
085: }
086:
087: /**
088: * Get label for this definition.
089: *
090: * @return label for this definition
091: */
092: public String getLabel() {
093: return m_label;
094: }
095:
096: /**
097: * Set label for this definition.
098: *
099: * @param label label for this definition
100: */
101: public void setLabel(String label) {
102: m_label = label;
103: }
104:
105: /**
106: * Get label for definition to be used.
107: *
108: * @return label for definition to be used
109: */
110: public String getUsing() {
111: return m_using;
112: }
113:
114: /**
115: * Set label for definition to be used.
116: *
117: * @param label label for definition to be used
118: */
119: public void setUsing(String label) {
120: m_using = label;
121: }
122:
123: /**
124: * Get list of child components contributing content items to this
125: * container element. This call is only meaningful after validation.
126: *
127: * @return list of child binding components defining content items
128: */
129: public ArrayList getContentComponents() {
130: return m_contentComponents == null ? EmptyList.INSTANCE
131: : m_contentComponents;
132: }
133:
134: /**
135: * Get list of child components contributing attribute items to this
136: * container element. This call is only meaningful after validation.
137: *
138: * @return list of child binding components defining attribute items
139: */
140: public ArrayList getAttributeComponents() {
141: return m_attributeComponents == null ? EmptyList.INSTANCE
142: : m_attributeComponents;
143: }
144:
145: /**
146: * Check if this container defines a context object.
147: *
148: * @return <code>true</code> if defines context object,
149: * <code>false</code> if not
150: */
151: public abstract boolean hasObject();
152:
153: /**
154: * Get class linked to binding element. This call is only meaningful after
155: * validation.
156: *
157: * @return information for class linked by binding
158: */
159: public abstract IClass getObjectType();
160:
161: /**
162: * Get class passed to child components. This call is only meaningful after
163: * validation.
164: *
165: * @return information for class linked by binding
166: */
167: public IClass getChildObjectType() {
168: return getObjectType();
169: }
170:
171: /**
172: * Set ID property child. Used to set the ID property associated with a
173: * particular class instance. There can only be at most one child ID
174: * property for each actual object instance.
175: *
176: * @param child child defining the ID property
177: * @param vctx validation context
178: */
179: public final void setIdChild(IComponent child,
180: ValidationContext vctx) {
181: if (m_idChild == null) {
182: m_idChild = child;
183: vctx.getBindingRoot().addIdClass(getObjectType());
184: } else {
185: vctx
186: .addError("Only one child ID property allowed for an object "
187: + "- "
188: + ValidationProblem
189: .componentDescription(m_idChild)
190: + " and "
191: + ValidationProblem
192: .componentDescription(child)
193: + " refer to the same object");
194: }
195: }
196:
197: /**
198: * Get ID property child.
199: *
200: * @return ID child
201: */
202: public IComponent getId() {
203: return m_idChild;
204: }
205:
206: //
207: // Object attribute delegate methods
208:
209: /**
210: * Get factory method name.
211: *
212: * @return fully-qualified factory class and method name (or
213: * <code>null</code> if none)
214: */
215: public String getFactoryName() {
216: return m_objectAttrs.getFactoryName();
217: }
218:
219: /**
220: * Get factory method information. This call is only meaningful after
221: * validation.
222: *
223: * @return factory method information (or <code>null</code> if none)
224: */
225: public IClassItem getFactory() {
226: return m_objectAttrs.getFactory();
227: }
228:
229: /**
230: * Set factory method name.
231: *
232: * @param name fully qualified class and method name for object factory
233: */
234: public void setFactoryName(String name) {
235: m_objectAttrs.setFactoryName(name);
236: }
237:
238: /**
239: * Get pre-set method name.
240: *
241: * @return pre-set method name (or <code>null</code> if none)
242: */
243: public String getPresetName() {
244: return m_objectAttrs.getPresetName();
245: }
246:
247: /**
248: * Get pre-set method information. This call is only meaningful after
249: * validation.
250: *
251: * @return pre-set method information (or <code>null</code> if none)
252: */
253: public IClassItem getPreset() {
254: return m_objectAttrs.getPreset();
255: }
256:
257: /**
258: * Set pre-set method name.
259: *
260: * @param name member method name to be called before unmarshalling
261: */
262: public void setPresetName(String name) {
263: m_objectAttrs.setPresetName(name);
264: }
265:
266: /**
267: * Get post-set method name.
268: *
269: * @return post-set method name (or <code>null</code> if none)
270: */
271: public String getPostsetName() {
272: return m_objectAttrs.getPostsetName();
273: }
274:
275: /**
276: * Get post-set method information. This call is only meaningful after
277: * validation.
278: *
279: * @return post-set method information (or <code>null</code> if none)
280: */
281: public IClassItem getPostset() {
282: return m_objectAttrs.getPostset();
283: }
284:
285: /**
286: * Set post-set method name.
287: *
288: * @param name member method name to be called after unmarshalling
289: */
290: public void setPostsetName(String name) {
291: m_objectAttrs.setPostsetName(name);
292: }
293:
294: /**
295: * Get pre-get method name.
296: *
297: * @return pre-get method name (or <code>null</code> if none)
298: */
299: public String getPregetName() {
300: return m_objectAttrs.getPregetName();
301: }
302:
303: /**
304: * Get pre-get method information. This call is only meaningful after
305: * validation.
306: *
307: * @return pre-get method information (or <code>null</code> if none)
308: */
309: public IClassItem getPreget() {
310: return m_objectAttrs.getPreget();
311: }
312:
313: /**
314: * Set pre-get method name.
315: *
316: * @param name member method name to be called before marshalling
317: */
318: public void setPreget(String name) {
319: m_objectAttrs.setPreget(name);
320: }
321:
322: /**
323: * Get marshaller class name.
324: *
325: * @return marshaller class name (or <code>null</code> if none)
326: */
327: public String getMarshallerName() {
328: return m_objectAttrs.getMarshallerName();
329: }
330:
331: /**
332: * Get marshaller class information. This call is only meaningful after
333: * validation.
334: *
335: * @return class information for marshaller (or <code>null</code> if none)
336: */
337: public IClass getMarshaller() {
338: return m_objectAttrs.getMarshaller();
339: }
340:
341: /**
342: * Set marshaller class name.
343: *
344: * @param name class name to be used for marshalling
345: */
346: public void setMarshallerName(String name) {
347: m_objectAttrs.setMarshallerName(name);
348: }
349:
350: /**
351: * Get unmarshaller class name.
352: *
353: * @return unmarshaller class name (or <code>null</code> if none)
354: */
355: public String getUnmarshallerName() {
356: return m_objectAttrs.getUnmarshallerName();
357: }
358:
359: /**
360: * Get unmarshaller class information. This call is only meaningful after
361: * validation.
362: *
363: * @return class information for unmarshaller (or <code>null</code> if none)
364: */
365: public IClass getUnmarshaller() {
366: return m_objectAttrs.getUnmarshaller();
367: }
368:
369: /**
370: * Set unmarshaller class name.
371: *
372: * @param name class name to be used for unmarshalling
373: */
374: public void setUnmarshallerName(String name) {
375: m_objectAttrs.setUnmarshallerName(name);
376: }
377:
378: /**
379: * Check if nillable object.
380: *
381: * @return nillable flag
382: */
383: public boolean isNillable() {
384: return m_objectAttrs.isNillable();
385: }
386:
387: /**
388: * Set nillable flag.
389: *
390: * @param nillable flag
391: */
392: public void setNillable(boolean nillable) {
393: m_objectAttrs.setNillable(nillable);
394: }
395:
396: /**
397: * Get type to be used for creating new instance.
398: *
399: * @return class name for type to be created (or <code>null</code> if none)
400: */
401: public String getCreateType() {
402: return m_objectAttrs.getCreateType();
403: }
404:
405: /**
406: * Get new instance creation class information. This method is only usable
407: * after a call to {@link #validate}.
408: *
409: * @return class information for type to be created (or <code>null</code> if
410: * none)
411: */
412: public IClass getCreateClass() {
413: return m_objectAttrs.getCreateClass();
414: }
415:
416: /**
417: * Set new instance type class name.
418: *
419: * @param name class name to be used for creating new instance
420: */
421: public void setCreateType(String name) {
422: m_objectAttrs.setCreateType(name);
423: }
424:
425: //
426: // Structure attribute delegate methods
427:
428: /**
429: * Get flexible flag.
430: *
431: * @return flexible flag
432: */
433: public boolean isFlexible() {
434: return m_structureAttrs.isFlexible();
435: }
436:
437: /**
438: * Set flexible flag.
439: *
440: * @param flexible
441: */
442: public void setFlexible(boolean flexible) {
443: m_structureAttrs.setFlexible(flexible);
444: }
445:
446: /**
447: * Check if child components are ordered.
448: *
449: * @return <code>true</code> if ordered, <code>false</code> if not
450: */
451: public boolean isOrdered() {
452: return m_structureAttrs.isOrdered();
453: }
454:
455: /**
456: * Set child components ordered flag.
457: *
458: * @param ordered <code>true</code> if ordered, <code>false</code> if not
459: */
460: public void setOrdered(boolean ordered) {
461: m_structureAttrs.setOrdered(ordered);
462: }
463:
464: /**
465: * Check if child components are a choice.
466: *
467: * @return <code>true</code> if choice, <code>false</code> if not
468: */
469: public boolean isChoice() {
470: return m_structureAttrs.isChoice();
471: }
472:
473: /**
474: * Set child components choice flag.
475: *
476: * @param choice <code>true</code> if choice, <code>false</code> if not
477: */
478: public void setChoice(boolean choice) {
479: m_structureAttrs.setChoice(choice);
480: }
481:
482: /**
483: * Check if repeated child elements are allowed.
484: *
485: * @return <code>true</code> if repeats allowed, <code>false</code> if not
486: */
487: public boolean isAllowRepeats() {
488: return m_structureAttrs.isAllowRepeats();
489: }
490:
491: /**
492: * Set repeated child elements allowed flag.
493: *
494: * @param ignore <code>true</code> if repeated child elements to be allowed,
495: * <code>false</code> if not
496: */
497: public void setAllowRepeats(boolean ignore) {
498: m_structureAttrs.setAllowRepeats(ignore);
499: }
500:
501: //
502: // Validation methods.
503:
504: /**
505: * Check that there's a way to construct an instance of an object class for
506: * input bindings. This can be a factory method, an unmarshaller, a
507: * no-argument constructor already defined in the class, or a modifiable
508: * class with constructor generation enabled. If a create-type is specified,
509: * this is used in place of the declared type. The call always succeeds if
510: * the binding is output-only.
511: *
512: * @param vctx validation context
513: * @param type constructed object type
514: */
515: protected void verifyConstruction(ValidationContext vctx,
516: IClass type) {
517: if (vctx.isInBinding() && getFactory() == null
518: && getUnmarshaller() == null) {
519: IClass create = getCreateClass();
520: if (create != null) {
521: if (create.isAssignable(type)) {
522: type = create;
523: } else {
524: vctx.addError("Specified create-type '"
525: + create.getName()
526: + "' is not compatible with type '"
527: + type.getName() + '\'');
528: }
529: }
530: if (type.isAbstract()) {
531: vctx
532: .addError("factory-method needed for abstract type '"
533: + type.getName() + '\'');
534: } else if (!type.getName().endsWith("[]")
535: && type.getInitializerMethod("()V") == null) {
536: BindingElement binding = vctx.getBindingRoot();
537: if (binding.isAddConstructors()) {
538: if (!type.isModifiable()) {
539: vctx
540: .addError("Need no-argument constructor or "
541: + "factory method for unmodifiable class "
542: + type.getName());
543: } else {
544: IClass suptype = type;
545: while ((suptype = suptype.getSuperClass()) != null) {
546: IClassItem cons = suptype
547: .getInitializerMethod("()V");
548: if ((cons == null || !type
549: .isAccessible(cons))
550: && !suptype.isModifiable()) {
551: vctx
552: .addError("Need accessible no-argument constructor for unmodifiable class "
553: + suptype.getName()
554: + " (superclass of "
555: + type.getName() + ')');
556: }
557: }
558: }
559: } else {
560: vctx.addError("Need no-argument constructor or "
561: + "factory method for class "
562: + type.getName());
563: }
564: }
565: }
566: }
567:
568: /**
569: * Check that child components are of types compatible with the container
570: * object type. This method may call itself recursively to process the
571: * children of child components which do not themselves set a type. It's not
572: * used directly, but is here for use by subclasses.
573: *
574: * @param vctx validation context
575: * @param type structure object type
576: * @param children list of child components to be checked
577: */
578: protected void checkCompatibleChildren(ValidationContext vctx,
579: IClass type, ArrayList children) {
580: for (int i = 0; i < children.size(); i++) {
581: ElementBase child = (ElementBase) children.get(i);
582: boolean expand = true;
583: if (child instanceof IComponent && !vctx.isSkipped(child)) {
584: IComponent comp = (IComponent) child;
585: IClass ctype = comp.getType();
586: if (comp instanceof ContainerElementBase) {
587: ContainerElementBase contain = (ContainerElementBase) comp;
588: expand = !contain.hasObject();
589: }
590: if (comp.isImplicit()) {
591: if (!type.isAssignable(ctype)) {
592: vctx.addFatal(
593: "References to structure object must have "
594: + "compatible types: "
595: + type.getName()
596: + " cannot be used as "
597: + ctype.getName(), child);
598: }
599: }
600: }
601: if (expand && child instanceof NestingElementBase) {
602: checkCompatibleChildren(vctx, type,
603: ((NestingElementBase) child).children());
604: }
605: }
606: }
607:
608: /**
609: * Check for child components classified. This is a convenience method for
610: * subclasses to check if classification has already been done.
611: *
612: * @return <code>true</code> if classified, <code>false</code> if not
613: */
614: protected boolean isClassified() {
615: return m_attributeComponents != null;
616: }
617:
618: /**
619: * Classify child components as contributing attributes, content, or both.
620: * This method is needed to handle on-demand classification during
621: * validation. When a child component is another instance of this class, the
622: * method calls itself on the child component prior to checking the child
623: * component's contribution.
624: *
625: * @param vctx
626: */
627: protected void classifyComponents(ValidationContext vctx) {
628: if (m_attributeComponents == null && !m_inClassify) {
629: ArrayList childs = children();
630: if (childs == null || childs.size() == 0) {
631:
632: // no children, so no components of either type
633: m_attributeComponents = EmptyList.INSTANCE;
634: m_contentComponents = EmptyList.INSTANCE;
635:
636: } else {
637:
638: // classify child components, setting flag to catch recursion
639: m_inClassify = true;
640: m_attributeComponents = new ArrayList();
641: m_contentComponents = new ArrayList();
642: for (int i = 0; i < childs.size(); i++) {
643: Object child = childs.get(i);
644: if (child instanceof ContainerElementBase) {
645: ((ContainerElementBase) child)
646: .classifyComponents(vctx);
647: }
648: if (child instanceof IComponent) {
649: IComponent comp = (IComponent) child;
650: if (comp.hasAttribute()) {
651: m_attributeComponents.add(child);
652: }
653: if (comp.hasContent()) {
654: m_contentComponents.add(child);
655: if (!comp.hasName() && isFlexible()) {
656: vctx
657: .addError(
658: "All child components must define element names for flexible='true'",
659: comp);
660: }
661: }
662: }
663: }
664: m_inClassify = false;
665: }
666: }
667: }
668:
669: /**
670: * Set child attribute and content components directly. This is provided for
671: * use by subclasses requiring special handling, in particular the
672: * <structure> element used as a mapping reference.
673: *
674: * @param attribs
675: * @param contents
676: */
677: protected void setComponents(ArrayList attribs, ArrayList contents) {
678: m_attributeComponents = attribs;
679: m_contentComponents = contents;
680: }
681:
682: /* (non-Javadoc)
683: * @see org.jibx.binding.model.ElementBase#prevalidate(org.jibx.binding.model.ValidationContext)
684: */
685: public void prevalidate(ValidationContext vctx) {
686: m_objectAttrs.prevalidate(vctx);
687: m_structureAttrs.prevalidate(vctx);
688: if (m_using != null && children().size() > 0) {
689: vctx
690: .addFatal("Child elements not allowed with using attribute");
691: }
692: super .prevalidate(vctx);
693: }
694:
695: /* (non-Javadoc)
696: * @see org.jibx.binding.model.ElementBase#validate(org.jibx.binding.model.ValidationContext)
697: */
698: public void validate(ValidationContext vctx) {
699: super .validate(vctx);
700: m_objectAttrs.validate(vctx);
701: m_structureAttrs.validate(vctx);
702: classifyComponents(vctx);
703: if (isChoice() && m_attributeComponents.size() > 0) {
704: vctx.addError("Attributes cannot be included in choice");
705: }
706: }
707: }
|