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.io.IOException;
032: import java.io.InputStream;
033: import java.net.URL;
034: import java.util.ArrayList;
035: import java.util.HashMap;
036: import java.util.HashSet;
037: import java.util.Iterator;
038: import java.util.Map;
039: import java.util.Set;
040:
041: import org.jibx.binding.classes.ClassCache;
042: import org.jibx.binding.util.StringArray;
043: import org.jibx.runtime.BindingDirectory;
044: import org.jibx.runtime.EnumSet;
045: import org.jibx.runtime.IBindingFactory;
046: import org.jibx.runtime.IMarshallingContext;
047: import org.jibx.runtime.IUnmarshallingContext;
048: import org.jibx.runtime.IXMLWriter;
049: import org.jibx.runtime.JiBXException;
050: import org.jibx.runtime.Utility;
051: import org.jibx.runtime.impl.UnmarshallingContext;
052:
053: /**
054: * Model component for <b>binding</b> element.
055: *
056: * @author Dennis M. Sosnoski
057: * @version 1.0
058: */
059:
060: public class BindingElement extends NestingElementBase {
061: /** Enumeration of allowed attribute names */
062: public static final StringArray s_allowedAttributes = new StringArray(
063: new String[] { "add-constructors", "direction",
064: "force-classes", "forwards", "name", "package",
065: "track-source" },
066: NestingElementBase.s_allowedAttributes);
067:
068: //
069: // Value set information
070:
071: public static final int IN_BINDING = 0;
072: public static final int OUT_BINDING = 1;
073: public static final int BOTH_BINDING = 2;
074:
075: /*package*/static final EnumSet s_directionEnum = new EnumSet(
076: IN_BINDING, new String[] { "input", "output", "both" });
077:
078: //
079: // Instance data
080:
081: /** Binding name. */
082: private String m_name;
083:
084: /** Binding direction. */
085: private String m_direction;
086:
087: /** Input binding flag. */
088: private boolean m_isInput;
089:
090: /** Output binding flag. */
091: private boolean m_isOutput;
092:
093: /** Support forward references to IDs flag. */
094: private boolean m_isForward;
095:
096: /** Generate souce tracking interface flag. */
097: private boolean m_isTrackSource;
098:
099: /** Generate souce tracking interface flag. */
100: private boolean m_isForceClasses;
101:
102: /** Add default constructors where needed flag. */
103: private boolean m_isAddConstructors;
104:
105: /** Package for generated context factory. */
106: private String m_targetPackage;
107:
108: /** Base URL for use with relative include paths. */
109: private URL m_baseUrl;
110:
111: /** Set of paths for includes. */
112: private final Set m_includePaths;
113:
114: /** Map from include path to actual binding. */
115: private final Map m_includeBindings;
116:
117: /** List of child elements. */
118: private final ArrayList m_children;
119:
120: /** Set of class names which can be referenced by ID. */
121: private Set m_idClassSet;
122:
123: /** List of namespace declarations to be added on output (lazy create,
124: <code>null</code> if none). */
125: private ArrayList m_namespaceDeclares;
126:
127: /**
128: * Default constructor.
129: */
130: public BindingElement() {
131: super (BINDING_ELEMENT);
132: m_includePaths = new HashSet();
133: m_includeBindings = new HashMap();
134: m_children = new ArrayList();
135: m_isForward = true;
136: }
137:
138: /**
139: * Set binding name.
140: *
141: * @param name binding definition name
142: */
143: public void setName(String name) {
144: m_name = name;
145: }
146:
147: /**
148: * Get binding name.
149: *
150: * @return binding definition name
151: */
152: public String getName() {
153: return m_name;
154: }
155:
156: /**
157: * Set forward references to IDs be supported in XML.
158: *
159: * @param forward <code>true</code> if forward references supported,
160: * <code>false</code> if not
161: */
162: public void setForward(boolean forward) {
163: m_isForward = forward;
164: }
165:
166: /**
167: * Check if forward references to IDs must be supported in XML.
168: *
169: * @return <code>true</code> if forward references required,
170: * <code>false</code> if not
171: */
172: public boolean isForward() {
173: return m_isForward;
174: }
175:
176: /**
177: * Set source position tracking for unmarshalling.
178: *
179: * @param track <code>true</code> if source position tracking enabled,
180: * <code>false</code> if not
181: */
182: public void setTrackSource(boolean track) {
183: m_isTrackSource = track;
184: }
185:
186: /**
187: * Check if source position tracking enabled for unmarshalling.
188: *
189: * @return <code>true</code> if source position tracking enabled,
190: * <code>false</code> if not
191: */
192: public boolean isTrackSource() {
193: return m_isTrackSource;
194: }
195:
196: /**
197: * Set force marshaller/unmarshaller class creation for top-level non-base
198: * abstract mappings.
199: *
200: * @param force <code>true</code> if class generation forced,
201: * <code>false</code> if not
202: */
203: public void setForceClasses(boolean force) {
204: m_isForceClasses = force;
205: }
206:
207: /**
208: * Check if marshaller/unmarshaller class creation for top-level non-base
209: * abstract mappings is forced.
210: *
211: * @return <code>true</code> if class generation forced,
212: * <code>false</code> if not
213: */
214: public boolean isForceClasses() {
215: return m_isForceClasses;
216: }
217:
218: /**
219: * Set default constructor generation.
220: *
221: * @param add <code>true</code> if constructors should be added,
222: * <code>false</code> if not
223: */
224: public void setAddConstructors(boolean add) {
225: m_isAddConstructors = add;
226: }
227:
228: /**
229: * Check if default constructor generation is enabled.
230: *
231: * @return <code>true</code> if default constructor generation enabled,
232: * <code>false</code> if not
233: */
234: public boolean isAddConstructors() {
235: return m_isAddConstructors;
236: }
237:
238: /**
239: * Set package for generated context factory class.
240: *
241: * @param pack generated context factory package
242: */
243: public void setTargetPackage(String pack) {
244: m_targetPackage = pack;
245: }
246:
247: /**
248: * Get package for generated context factory class.
249: *
250: * @return package for generated context factory
251: */
252: public String getTargetPackage() {
253: return m_targetPackage;
254: }
255:
256: /**
257: * Set base URL for relative include paths.
258: *
259: * @param base
260: */
261: public void setBaseUrl(URL base) {
262: m_baseUrl = base;
263: }
264:
265: /**
266: * Get base URL for relative include paths.
267: *
268: * @return base URL
269: */
270: public URL getBaseUrl() {
271: return m_baseUrl;
272: }
273:
274: /**
275: * Set the correct direction text. This should be used whenever the
276: * individual in and out flags are set, so that modifications are output
277: * correctly when a binding is marshalled.
278: */
279: private void setDirection() {
280: int direct = m_isInput ? (m_isOutput ? BOTH_BINDING
281: : IN_BINDING) : OUT_BINDING;
282: m_direction = s_directionEnum.getName(direct);
283: }
284:
285: /**
286: * Set binding component applies for marshalling XML.
287: *
288: * @param out <code>true</code> if binding supports output,
289: * <code>false</code> if not
290: */
291: public void setOutBinding(boolean out) {
292: m_isOutput = out;
293: setDirection();
294: }
295:
296: /**
297: * Check if this binding component applies for marshalling XML.
298: *
299: * @return <code>true</code> if binding supports output, <code>false</code>
300: * if not
301: */
302: public boolean isOutBinding() {
303: return m_isOutput;
304: }
305:
306: /**
307: * Set binding component applies for unmarshalling XML.
308: *
309: * @param in <code>true</code> if binding supports input,
310: * <code>false</code> if not
311: */
312: public void setInBinding(boolean in) {
313: m_isInput = in;
314: setDirection();
315: }
316:
317: /**
318: * Check if this binding component applies for unmarshalling XML.
319: *
320: * @return <code>true</code> if binding supports input, <code>false</code>
321: * if not
322: */
323: public boolean isInBinding() {
324: return m_isInput;
325: }
326:
327: /**
328: * Add include path to set processed.
329: *
330: * @param path
331: * @return <code>true</code> if new path, <code>false</code> if duplicate
332: */
333: public boolean addIncludePath(String path) {
334: return m_includePaths.add(path);
335: }
336:
337: /**
338: * Get included binding. If the binding was supplied directly it's just
339: * returned; otherwise, it's read from the URL. This method should only be
340: * called if {@link #addIncludePath(String)} returns <code>true</code>,
341: * so that each unique included binding is only processed once.
342: *
343: * @param url binding path
344: * @param root binding containing the include
345: * @param vctx validation context
346: * @return binding
347: * @throws IOException
348: * @throws JiBXException
349: */
350: public BindingElement getIncludeBinding(URL url,
351: BindingElement root, ValidationContext vctx)
352: throws IOException, JiBXException {
353: String path = url.toExternalForm();
354: BindingElement bind = (BindingElement) m_includeBindings
355: .get(path);
356: if (bind == null) {
357:
358: // get base name from path
359: path = path.replace('\\', '/');
360: int split = path.lastIndexOf('/');
361: String fname = path;
362: if (split >= 0) {
363: fname = fname.substring(split + 1);
364: }
365:
366: // read stream to create object model
367: InputStream is = url.openStream();
368: bind = BindingElement.readBinding(is, fname, root, vctx);
369:
370: }
371:
372: // set binding base, and context from root context
373: bind.setBaseUrl(url);
374: bind.setDefinitions(root.getDefinitions().getIncludeCopy());
375: m_includeBindings.put(path, bind);
376: return bind;
377: }
378:
379: /**
380: * Add binding accessible to includes. This allows bindings to be supplied
381: * directly, without needing to be parsed from an input document.
382: *
383: * @param path URL string identifying the binding (virtual path)
384: * @param bind
385: */
386: public void addIncludeBinding(String path, BindingElement bind) {
387: m_includeBindings.put(path, bind);
388: }
389:
390: /**
391: * Add a class defined with a ID value. This is used to track the classes
392: * with ID values for validating ID references in the binding. If the
393: * binding uses global IDs, the actual ID class is added to the table along
394: * with all interfaces implemented by the class and all superclasses, since
395: * instances of the ID class can be referenced in any of those forms. If the
396: * binding does not use global IDs, only the actual ID class is added, since
397: * references must be type-specific.
398: *
399: * @param clas information for class with ID value
400: */
401: public void addIdClass(IClass clas) {
402:
403: // create the set if not already present
404: if (m_idClassSet == null) {
405: m_idClassSet = new HashSet();
406: }
407:
408: // add the class if not already present
409: if (m_idClassSet.add(clas.getName())) {
410:
411: // new class, add all interfaces if not previously defined
412: String[] inames = clas.getInterfaces();
413: for (int i = 0; i < inames.length; i++) {
414: m_idClassSet.add(inames[i]);
415: }
416: while (clas != null && m_idClassSet.add(clas.getName())) {
417: clas = clas.getSuperClass();
418: }
419: }
420: }
421:
422: /**
423: * Check if a class can be referenced by ID. This just checks if any classes
424: * compatible with the reference type are bound with ID values.
425: *
426: * @param name fully qualified name of class
427: * @return <code>true</code> if class is bound with an ID,
428: * <code>false</code> if not
429: */
430: public boolean isIdClass(String name) {
431: if (m_idClassSet == null) {
432: return false;
433: } else {
434: return m_idClassSet.contains(name);
435: }
436: }
437:
438: /**
439: * Add top-level child element.
440: * TODO: should be ElementBase argument, but JiBX doesn't allow yet
441: *
442: * @param child element to be added as child of this element
443: */
444: public void addTopChild(Object child) {
445: m_children.add(child);
446: }
447:
448: /**
449: * Get list of top-level child elements.
450: *
451: * @return list of child elements, or <code>null</code> if none
452: */
453: public ArrayList topChildren() {
454: return m_children;
455: }
456:
457: /**
458: * Get iterator for top-level child elements.
459: *
460: * @return iterator for child elements
461: */
462: public Iterator topChildIterator() {
463: return m_children.iterator();
464: }
465:
466: /**
467: * Add namespace declaration for output when marshalling.
468: *
469: * @param prefix
470: * @param uri
471: */
472: public void addNamespaceDecl(String prefix, String uri) {
473: if (m_namespaceDeclares == null) {
474: m_namespaceDeclares = new ArrayList();
475: }
476: m_namespaceDeclares.add(prefix);
477: m_namespaceDeclares.add(uri);
478: }
479:
480: //
481: // Overrides of base class methods.
482:
483: /* (non-Javadoc)
484: * @see org.jibx.binding.model.ElementBase#hasAttribute()
485: */
486: public boolean hasAttribute() {
487: throw new IllegalStateException(
488: "Internal error: method should never be called");
489: }
490:
491: /* (non-Javadoc)
492: * @see org.jibx.binding.model.ElementBase#hasContent()
493: */
494: public boolean hasContent() {
495: throw new IllegalStateException(
496: "Internal error: method should never be called");
497: }
498:
499: /* (non-Javadoc)
500: * @see org.jibx.binding.model.ElementBase#isOptional()
501: */
502: public boolean isOptional() {
503: throw new IllegalStateException(
504: "Internal error: method should never be called");
505: }
506:
507: /**
508: * Get default style value for child components. This call is only
509: * meaningful after validation.
510: *
511: * @return default style value for child components
512: */
513: public int getDefaultStyle() {
514: int style = super .getDefaultStyle();
515: if (style < 0) {
516: style = NestingAttributes.s_styleEnum.getValue("element");
517: }
518: return style;
519: }
520:
521: //
522: // Marshalling/unmarshalling methods
523:
524: /**
525: * Marshalling hook method to add namespace declarations to <binding>
526: * element.
527: *
528: * @param ictx
529: * @throws IOException
530: */
531: private void preGet(IMarshallingContext ictx) throws IOException {
532: if (m_namespaceDeclares != null) {
533:
534: // set up information for namespace indexes and prefixes
535: IXMLWriter writer = ictx.getXmlWriter();
536: String[] uris = new String[m_namespaceDeclares.size() / 2];
537: int[] indexes = new int[uris.length];
538: String[] prefs = new String[uris.length];
539: int base = writer.getNamespaceCount();
540: for (int i = 0; i < uris.length; i++) {
541: indexes[i] = base + i;
542: prefs[i] = (String) m_namespaceDeclares.get(i * 2);
543: uris[i] = (String) m_namespaceDeclares.get(i * 2 + 1);
544: }
545:
546: // add the namespace declarations to current element
547: writer.pushExtensionNamespaces(uris);
548: writer.openNamespaces(indexes, prefs);
549: for (int i = 0; i < uris.length; i++) {
550: String prefix = prefs[i];
551: String name = prefix.length() > 0 ? "xmlns:" + prefix
552: : "xmlns";
553: writer.addAttribute(0, name, uris[i]);
554: }
555:
556: }
557: }
558:
559: //
560: // Validation methods
561:
562: /**
563: * Make sure all attributes are defined.
564: *
565: * @param ictx unmarshalling context
566: * @exception JiBXException on unmarshalling error
567: */
568: private void preSet(IUnmarshallingContext ictx)
569: throws JiBXException {
570:
571: // validate the attributes defined for this element
572: validateAttributes(ictx, s_allowedAttributes);
573:
574: // get the unmarshal wrapper
575: UnmarshalWrapper wrapper = (UnmarshalWrapper) ictx
576: .getStackObject(ictx.getStackDepth() - 1);
577: if (wrapper.getContainingBinding() != null) {
578:
579: // check attributes not allowed on included binding
580: BindingElement root = wrapper.getContainingBinding();
581: ValidationContext vctx = wrapper.getValidation();
582: UnmarshallingContext uctx = (UnmarshallingContext) ictx;
583: for (int i = 0; i < uctx.getAttributeCount(); i++) {
584:
585: // check if nonamespace attribute is in the allowed set
586: String name = uctx.getAttributeName(i);
587: if (uctx.getAttributeNamespace(i).length() == 0) {
588: if (s_allowedAttributes.indexOf(name) >= 0) {
589: String value = uctx.getAttributeValue(i);
590: if ("direction".equals(name)) {
591: if (!root.m_direction.equals(value)) {
592: vctx
593: .addError(
594: "'direction' value on included binding must match the root binding",
595: this );
596: }
597: } else if ("track-source".equals(name)) {
598: boolean flag = Utility.parseBoolean(value);
599: if (root.m_isTrackSource != flag) {
600: vctx
601: .addError(
602: "'track-source' value on included binding must match the root binding",
603: this );
604: }
605: } else if ("value-style".equals(name)) {
606: if (!root.getStyleName().equals(value)) {
607: vctx
608: .addError(
609: "'value-style' value on included binding must match the root binding",
610: this );
611: }
612: } else if ("force-classes".equals(name)) {
613: boolean flag = Utility.parseBoolean(value);
614: if (root.m_isForceClasses != flag) {
615: vctx
616: .addError(
617: "'force-classes' value on included binding must match the root binding",
618: this );
619: }
620: } else if ("add-constructors".equals(name)) {
621: boolean flag = Utility.parseBoolean(value);
622: if (root.m_isAddConstructors != flag) {
623: vctx
624: .addError(
625: "'add-constructors' value on included binding must match the root binding",
626: this );
627: }
628: } else if ("forwards".equals(name)) {
629: boolean flag = Utility.parseBoolean(value);
630: if (root.m_isForward != flag) {
631: vctx
632: .addError(
633: "'forwards' value on included binding must match the root binding",
634: this );
635: }
636: } else {
637: vctx
638: .addError(
639: "Attribute '"
640: + name
641: + "' not allowed on included binding",
642: this );
643: }
644: }
645: }
646: }
647:
648: }
649: }
650:
651: /**
652: * Prevalidate all attributes of element in isolation.
653: *
654: * @param vctx validation context
655: */
656: public void prevalidate(ValidationContext vctx) {
657:
658: // set the direction flags
659: int index = -1;
660: if (m_direction != null) {
661: index = s_directionEnum.getValue(m_direction);
662: if (index < 0) {
663: vctx.addError("Value \"" + m_direction
664: + "\" is not a valid choice for direction");
665: }
666: } else {
667: index = BOTH_BINDING;
668: }
669: m_isInput = index == IN_BINDING || index == BOTH_BINDING;
670: m_isOutput = index == OUT_BINDING || index == BOTH_BINDING;
671: super .prevalidate(vctx);
672: }
673:
674: private static FormatElement buildFormat(String name, String type,
675: boolean use, String sname, String dname, String dflt) {
676: FormatElement format = new FormatElement();
677: format.setLabel(name);
678: format.setTypeName(type);
679: format.setDefaultFormat(use);
680: format.setSerializerName(sname);
681: format.setDeserializerName(dname);
682: format.setDefaultText(dflt);
683: return format;
684: }
685:
686: private void defineBaseFormat(FormatElement format,
687: DefinitionContext dctx, ValidationContext vctx) {
688: format.prevalidate(vctx);
689: format.validate(vctx);
690: dctx.addFormat(format, vctx);
691: }
692:
693: /**
694: * Run the actual validation of a binding model.
695: *
696: * @param vctx context for controlling validation
697: */
698: public void runValidation(ValidationContext vctx) {
699:
700: // initially enable both directions for format setup
701: m_isInput = true;
702: m_isOutput = true;
703:
704: // create outer definition context
705: DefinitionContext dctx = new DefinitionContext(null);
706: vctx.setGlobalDefinitions(dctx);
707: defineBaseFormat(buildFormat("byte.default", "byte", true,
708: "org.jibx.runtime.Utility.serializeByte",
709: "org.jibx.runtime.Utility.parseByte", "0"), dctx, vctx);
710: defineBaseFormat(buildFormat("char.default", "char", true,
711: "org.jibx.runtime.Utility.serializeChar",
712: "org.jibx.runtime.Utility.parseChar", "0"), dctx, vctx);
713: defineBaseFormat(buildFormat("double.default", "double", true,
714: "org.jibx.runtime.Utility.serializeDouble",
715: "org.jibx.runtime.Utility.parseDouble", "0.0"), dctx,
716: vctx);
717: defineBaseFormat(buildFormat("float.default", "float", true,
718: "org.jibx.runtime.Utility.serializeFloat",
719: "org.jibx.runtime.Utility.parseFloat", "0.0"), dctx,
720: vctx);
721: defineBaseFormat(buildFormat("int.default", "int", true,
722: "org.jibx.runtime.Utility.serializeInt",
723: "org.jibx.runtime.Utility.parseInt", "0"), dctx, vctx);
724: defineBaseFormat(buildFormat("long.default", "long", true,
725: "org.jibx.runtime.Utility.serializeLong",
726: "org.jibx.runtime.Utility.parseLong", "0"), dctx, vctx);
727: defineBaseFormat(buildFormat("short.default", "short", true,
728: "org.jibx.runtime.Utility.serializeShort",
729: "org.jibx.runtime.Utility.parseShort", "0"), dctx, vctx);
730: defineBaseFormat(buildFormat("boolean.default", "boolean",
731: true, "org.jibx.runtime.Utility.serializeBoolean",
732: "org.jibx.runtime.Utility.parseBoolean", "false"),
733: dctx, vctx);
734: defineBaseFormat(buildFormat("Date.default", "java.util.Date",
735: true, "org.jibx.runtime.Utility.serializeDateTime",
736: "org.jibx.runtime.Utility.deserializeDateTime", null),
737: dctx, vctx);
738: //#!j2me{
739: defineBaseFormat(buildFormat("SqlDate.default",
740: "java.sql.Date", true,
741: "org.jibx.runtime.Utility.serializeSqlDate",
742: "org.jibx.runtime.Utility.deserializeSqlDate", null),
743: dctx, vctx);
744: defineBaseFormat(buildFormat("SqlTime.default",
745: "java.sql.Time", true,
746: "org.jibx.runtime.Utility.serializeSqlTime",
747: "org.jibx.runtime.Utility.deserializeSqlTime", null),
748: dctx, vctx);
749: defineBaseFormat(buildFormat("SqlTimestamp.default",
750: "java.sql.Timestamp", true,
751: "org.jibx.runtime.Utility.serializeTimestamp",
752: "org.jibx.runtime.Utility.deserializeTimestamp", null),
753: dctx, vctx);
754: //#j2me}
755: defineBaseFormat(buildFormat("byte-array.default", "byte[]",
756: true, "org.jibx.runtime.Utility.serializeBase64",
757: "org.jibx.runtime.Utility.deserializeBase64", null),
758: dctx, vctx);
759: defineBaseFormat(buildFormat("String.default",
760: "java.lang.String", true, null, null, null), dctx, vctx);
761: defineBaseFormat(buildFormat("Object.default",
762: "java.lang.Object", true, null, null, null), dctx, vctx);
763: FormatElement format = buildFormat("char.string", "char",
764: false, "org.jibx.runtime.Utility.serializeCharString",
765: "org.jibx.runtime.Utility.deserializeCharString", "0");
766: format.setDefaultFormat(false);
767: format.prevalidate(vctx);
768: format.validate(vctx);
769: dctx.addFormat(format, vctx);
770: NamespaceElement ns = new NamespaceElement();
771: ns.setDefaultName("all");
772: ns.prevalidate(vctx);
773: dctx.addNamespace(ns);
774: // TODO: check for errors in basic configuration
775:
776: // create a definition context for the binding
777: setDefinitions(new DefinitionContext(dctx));
778:
779: // run the actual validation
780: vctx.prevalidate(this );
781: RegistrationVisitor rvisitor = new RegistrationVisitor(vctx);
782: rvisitor.visitTree(this );
783: vctx.validate(this );
784: }
785:
786: /**
787: * Read a binding definition (possibly as an include) to construct binding
788: * model.
789: *
790: * @param is input stream for reading binding
791: * @param fname name of input file (<code>null</code> if unknown)
792: * @param contain containing binding (<code>null</code> if none)
793: * @param vctx validation context used during unmarshalling
794: * @return root of binding definition model
795: * @throws JiBXException on error in reading binding
796: */
797: public static BindingElement readBinding(InputStream is,
798: String fname, BindingElement contain, ValidationContext vctx)
799: throws JiBXException {
800:
801: // look up the binding factory
802: IBindingFactory bfact = BindingDirectory
803: .getFactory(BindingElement.class);
804:
805: // unmarshal document to construct objects
806: IUnmarshallingContext uctx = bfact.createUnmarshallingContext();
807: uctx.setDocument(is, fname, null);
808: uctx.pushObject(new UnmarshalWrapper(vctx, contain));
809: BindingElement binding = (BindingElement) uctx
810: .unmarshalElement();
811: uctx.popObject();
812: return binding;
813: }
814:
815: /**
816: * Read a binding definition to construct binding model.
817: *
818: * @param is input stream for reading binding
819: * @param fname name of input file (<code>null</code> if unknown)
820: * @param vctx validation context used during unmarshalling
821: * @return root of binding definition model
822: * @throws JiBXException on error in reading binding
823: */
824: public static BindingElement readBinding(InputStream is,
825: String fname, ValidationContext vctx) throws JiBXException {
826: return readBinding(is, fname, null, vctx);
827: }
828:
829: /**
830: * Validate a binding definition.
831: *
832: * @param name binding definition name
833: * @param is input stream for reading binding
834: * @param vctx validation context to record problems
835: * @return root of binding definition model, or <code>null</code> if error
836: * in unmarshalling
837: * @throws JiBXException on error in binding XML structure
838: */
839: public static BindingElement validateBinding(String name, URL path,
840: InputStream is, ValidationContext vctx)
841: throws JiBXException {
842:
843: // construct object model for binding
844: BindingElement binding = readBinding(is, name, vctx);
845: binding.setBaseUrl(path);
846: vctx.setBindingRoot(binding);
847:
848: // validate the binding definition
849: binding.runValidation(vctx);
850:
851: // list validation errors
852: ArrayList probs = vctx.getProblems();
853: if (probs.size() > 0) {
854: for (int i = 0; i < probs.size(); i++) {
855: ValidationProblem prob = (ValidationProblem) probs
856: .get(i);
857: System.out
858: .print(prob.getSeverity() >= ValidationProblem.ERROR_LEVEL ? "Error: "
859: : "Warning: ");
860: System.out.println(prob.getDescription());
861: }
862: }
863: return binding;
864: }
865:
866: /**
867: * Create a default validation context.
868: *
869: * @return new validation context
870: */
871: public static ValidationContext newValidationContext() {
872: IClassLocator locate = new IClassLocator() {
873: public IClass getClassInfo(String name) {
874: try {
875: return new ClassWrapper(this , ClassCache
876: .getClassFile(name));
877: } catch (JiBXException e) {
878: return null;
879: }
880: }
881: };
882: return new ValidationContext(locate);
883: }
884:
885: /* // test runner
886: // This code only used in testing, to roundtrip binding definitions
887: public static void test(String ipath, String opath, ValidationContext vctx)
888: throws Exception {
889:
890: // validate the binding definition
891: FileInputStream is = new FileInputStream(ipath);
892: URL url = new URL("file://" + ipath);
893: BindingElement binding = validateBinding(ipath, url, is, vctx);
894:
895: // marshal back out for comparison purposes
896: IBindingFactory bfact =
897: BindingDirectory.getFactory(BindingElement.class);
898: IMarshallingContext mctx = bfact.createMarshallingContext();
899: mctx.setIndent(2);
900: mctx.marshalDocument(binding, null, null, new FileOutputStream(opath));
901: System.out.println("Wrote output binding " + opath);
902:
903: // compare input document with output document
904: DocumentComparator comp = new DocumentComparator(System.err);
905: boolean match = comp.compare(new FileReader(ipath),
906: new FileReader(opath));
907: if (!match) {
908: System.err.println("Mismatch from input " + ipath +
909: " to output " + opath);
910: }
911: }
912:
913: // test runner
914: public static void main(String[] args) throws Exception {
915:
916: // configure class loading
917: String[] paths = new String[] { "." };
918: ClassCache.setPaths(paths);
919: ClassFile.setPaths(paths);
920: ValidationContext vctx = newValidationContext();
921:
922: // process all bindings listed on command line
923: for (int i = 0; i < args.length; i++) {
924: try {
925: String ipath = args[i];
926: int split = ipath.lastIndexOf(File.separatorChar);
927: String opath = "x" + ipath.substring(split+1);
928: test(ipath, opath, vctx);
929: } catch (Exception e) {
930: System.err.println("Error handling binding " + args[i]);
931: e.printStackTrace();
932: }
933: }
934: } */
935:
936: /**
937: * Inner class as wrapper for binding element on unmarshalling. This
938: * provides a handle for passing the validation context, allowing elements
939: * to check for problems during unmarshalling.
940: */
941: public static class UnmarshalWrapper {
942: private final ValidationContext m_validationContext;
943: private final BindingElement m_containingBinding;
944:
945: protected UnmarshalWrapper(ValidationContext vctx,
946: BindingElement contain) {
947: m_validationContext = vctx;
948: m_containingBinding = contain;
949: }
950:
951: public ValidationContext getValidation() {
952: return m_validationContext;
953: }
954:
955: public BindingElement getContainingBinding() {
956: return m_containingBinding;
957: }
958: }
959: }
|