001: /**
002: * Redistribution and use of this software and associated documentation
003: * ("Software"), with or without modification, are permitted provided
004: * that the following conditions are met:
005: *
006: * 1. Redistributions of source code must retain copyright
007: * statements and notices. Redistributions must also contain a
008: * copy of this document.
009: *
010: * 2. Redistributions in binary form must reproduce the
011: * above copyright notice, this list of conditions and the
012: * following disclaimer in the documentation and/or other
013: * materials provided with the distribution.
014: *
015: * 3. The name "Exolab" must not be used to endorse or promote
016: * products derived from this Software without prior written
017: * permission of Intalio, Inc. For written permission,
018: * please contact info@exolab.org.
019: *
020: * 4. Products derived from this Software may not be called "Exolab"
021: * nor may "Exolab" appear in their names without prior written
022: * permission of Intalio, Inc. Exolab is a registered
023: * trademark of Intalio, Inc.
024: *
025: * 5. Due credit should be given to the Exolab Project
026: * (http://www.exolab.org/).
027: *
028: * THIS SOFTWARE IS PROVIDED BY INTALIO, INC. AND CONTRIBUTORS
029: * ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
030: * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
031: * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
032: * INTALIO, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
033: * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
034: * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
035: * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
036: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
037: * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
038: * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
039: * OF THE POSSIBILITY OF SUCH DAMAGE.
040: *
041: * Copyright 1999-2004 (C) Intalio, Inc. All Rights Reserved.
042: *
043: * $Id: MarshalFramework.java 7004 2007-05-28 13:07:06Z wguttmn $
044: */package org.exolab.castor.xml;
045:
046: import org.exolab.castor.mapping.ClassDescriptor;
047: import org.exolab.castor.mapping.CollectionHandler;
048: import org.exolab.castor.mapping.FieldDescriptor;
049: import org.exolab.castor.mapping.MappingException;
050: import org.exolab.castor.mapping.loader.CollectionHandlers;
051: import org.exolab.castor.util.ReflectionUtil;
052:
053: import java.lang.reflect.InvocationTargetException;
054: import java.lang.reflect.Method;
055: import java.util.Iterator;
056: import java.util.Vector;
057:
058: /**
059: * A core class for common code shared throughout the
060: * Marshalling Framework
061: *
062: * @author <a href="mailto:kvisco-at-intalio.com">Keith Visco</a>
063: * @version $Revision: 7004 $ $Date: 2005-12-13 14:58:48 -0700 (Tue, 13 Dec 2005) $
064: */
065: abstract class MarshalFramework {
066:
067: //--------------------------/
068: //- Public class variables -/
069: //--------------------------/
070:
071: /**
072: * The XSI Namespace URI
073: **/
074: public static final String XSI_NAMESPACE = "http://www.w3.org/2001/XMLSchema-instance";
075:
076: /**
077: * The name of the Schema location attribute
078: **/
079: public static final String XSI_SCHEMA_LOCATION = "schemaLocation";
080:
081: /**
082: * The name of the no namespace schema location attribute
083: **/
084: public static final String XSI_NO_NAMESPACE_SCHEMA_LOCATION = "noNamespaceSchemaLocation";
085:
086: /**
087: * The xml:lang attribute name
088: */
089: public static final String XML_LANG_ATTR = "xml:lang";
090:
091: /**
092: * The xml:lang attribute, without the "xml:" prefix.
093: */
094: public static final String LANG_ATTR = "lang";
095:
096: /**
097: * The xsi:nil attribute, without the "xsi:" prefix.
098: */
099: public static final String NIL_ATTR = "nil";
100:
101: public static final String XSI_NIL_ATTR = "xsi:nil";
102:
103: /**
104: * The xml:space attribute name
105: */
106: public static final String XML_SPACE_ATTR = "xml:space";
107:
108: /**
109: * The xml:space attribute name, without the "xml:" prefix
110: */
111: public static final String SPACE_ATTR = "space";
112:
113: /**
114: * The xsi:type attribute name, without the "xsi:" prefix
115: */
116: public static final String TYPE_ATTR = "type";
117:
118: /**
119: * The value of 'true'
120: */
121: public static final String TRUE_VALUE = "true";
122:
123: //-----------------------------/
124: //- Protected class variables -/
125: //-----------------------------/
126:
127: static final String INTERNAL_XML_NAME = "-error-if-this-is-used-";
128:
129: /**
130: * The default prefix used for specifying the
131: * xsi:type as a classname instead of a schema name.
132: * This is a Castor specific hack.
133: */
134: static final String JAVA_PREFIX = "java:";
135:
136: /**
137: * The name of the QName type
138: */
139: static final String QNAME_NAME = "QName";
140:
141: /**
142: * An empty array of field descriptors
143: */
144: static final XMLFieldDescriptor[] NO_FIELD_DESCRIPTORS = new XMLFieldDescriptor[0];
145:
146: /**
147: * Returns true if the given Class is a considered a
148: * collection by the marshalling framework.
149: *
150: * @return true if the given Class is considered a collection.
151: */
152: public static boolean isCollection(Class clazz) {
153: return CollectionHandlers.hasHandler(clazz);
154: } //-- isCollection
155:
156: /**
157: * Returns the CollectionHandler associated with the
158: * given collection, or null if no such handler exists.
159: *
160: * @return the CollectionHandler for the associated type.
161: */
162: public CollectionHandler getCollectionHandler(Class clazz) {
163: CollectionHandler handler = null;
164: try {
165: handler = CollectionHandlers.getHandler(clazz);
166: } catch (MappingException mx) {
167: //-- Not a collection, or no handler exists, return null.
168: }
169: return handler;
170: } //-- getCollectionHandler
171:
172: /**
173: * Returns true if the given class should be treated as a primitive
174: * type. This method will return true for all Java primitive
175: * types, the set of primitive object wrappers, as well
176: * as Strings.
177: *
178: * @return true if the given class should be treated as a primitive
179: * type
180: **/
181: static boolean isPrimitive(Class type) {
182:
183: if (type == null)
184: return false;
185:
186: //-- java primitive
187: if (type.isPrimitive())
188: return true;
189:
190: //-- we treat strings as primitives
191: if (type == String.class)
192: return true;
193:
194: //-- primtive wrapper classes
195: if ((type == Boolean.class) || (type == Character.class)) {
196: return true;
197: }
198:
199: Class super Class = type.getSuperclass();
200: if (super Class == Number.class) {
201: return true;
202: }
203:
204: if (super Class != null) {
205: return super Class.getName().equals("java.lang.Enum");
206: }
207:
208: return false;
209:
210: } //-- isPrimitive
211:
212: /**
213: * Returns true if the given class should be treated as an enum type. This method
214: * will return true for all Java 5 (or later) enums, and for enum-style
215: * classes.
216: *
217: * @return true if the given class should be treated as an enum
218: **/
219: static boolean isEnum(Class type) {
220:
221: if (type == null) {
222: return false;
223: }
224:
225: float javaVersion = Float.valueOf(
226: System.getProperty("java.specification.version"))
227: .floatValue();
228: if (javaVersion >= 1.5) {
229: try {
230: Boolean isEnum = ReflectionUtil
231: .isEnumViaReflection(type);
232: return isEnum.booleanValue();
233: } catch (Exception e) {
234: // nothing to report; implies that there's no such method
235: }
236: }
237:
238: // TODO: add code to cover 1.4 enum-stype classes as well.
239:
240: return false;
241:
242: } //-- isPrimitive
243:
244: // /**
245: // * Calls isEnum() method on target class vi areflection to find out
246: // * whether the given type is a Java 5 enumeration.
247: // * @param type The type to analyze.
248: // * @return True if the type given is a Java 5.0 enum.
249: // * @throws NoSuchMethodException If the method can not be found.
250: // * @throws IllegalAccessException If access to this method is illegal
251: // * @throws InvocationTargetException If the target method can not be invoked.
252: // */
253: // private static Boolean isEnumViaReflection(Class type)
254: // throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
255: // Method isEnumMethod = type.getClass().getMethod("isEnum", (Class[]) null);
256: // return (Boolean) isEnumMethod.invoke(type, (Object[]) null);
257: // }
258: //
259: /**
260: * Returns true if any of the fields associated with the given
261: * XMLClassDescriptor are located at, or beneath, the given location.
262: *
263: * @param location the location to compare against
264: * @param classDesc the XMLClassDescriptor in which to check the field
265: * locations
266: */
267: static final boolean hasFieldsAtLocation(String location,
268: XMLClassDescriptor classDesc) {
269: //-- check elements
270: XMLFieldDescriptor[] descriptors = classDesc
271: .getElementDescriptors();
272: for (int i = 0; i < descriptors.length; i++) {
273: if (descriptors[i] == null)
274: continue;
275: String tmpLocation = descriptors[i].getLocationPath();
276: if ((tmpLocation != null)
277: && (tmpLocation.startsWith(location)))
278: return true;
279: }
280:
281: //-- check attributes
282: descriptors = classDesc.getAttributeDescriptors();
283: for (int i = 0; i < descriptors.length; i++) {
284: if (descriptors[i] == null)
285: continue;
286: String tmpLocation = descriptors[i].getLocationPath();
287: if ((tmpLocation != null)
288: && (tmpLocation.startsWith(location)))
289: return true;
290: }
291:
292: //-- check content/text
293: XMLFieldDescriptor content = classDesc.getContentDescriptor();
294: if (content != null) {
295: String tmpLocation = content.getLocationPath();
296: if ((tmpLocation != null)
297: && (tmpLocation.startsWith(location)))
298: return true;
299: }
300: return false;
301: } //-- hasFieldsAtLocation
302:
303: /**
304: * Compares the given namespaces (as strings) for equality.
305: * null and empty values are considered equal.
306: *
307: * @param ns1 the namespace to compare to argument ns2
308: * @param ns2 the namespace to compare to argument ns1
309: */
310: public static boolean namespaceEquals(String ns1, String ns2) {
311: if (ns1 == null) {
312: return ((ns2 == null) || (ns2.length() == 0));
313: }
314: if (ns2 == null) {
315: return (ns1.length() == 0);
316: }
317: return ns1.equals(ns2);
318: } //-- namespaceEquals
319:
320: /**
321: * Returns true if the given classes are both the same
322: * primitive or primitive wrapper class. For exmaple, if
323: * class "a" is an int (Integer.TYPE) and class "b" is
324: * either an int or Integer.class then true will be
325: * returned, otherwise false.
326: *
327: * @return true if both a and b are considered equal
328: */
329: static boolean primitiveOrWrapperEquals(Class a, Class b) {
330: if (!isPrimitive(a))
331: return false;
332: if (!isPrimitive(b))
333: return false;
334:
335: if (a == b)
336: return true;
337:
338: //-- Boolean/boolean
339: if ((a == Boolean.class) || (a == Boolean.TYPE)) {
340: return ((b == Boolean.class) || (b == Boolean.TYPE));
341: }
342: //-- Byte/byte
343: else if ((a == Byte.class) || (a == Byte.TYPE)) {
344: return ((b == Byte.class) || (b == Byte.TYPE));
345: }
346: //-- Character/char
347: else if ((a == Character.class) || (a == Character.TYPE)) {
348: return ((b == Character.class) || (b == Character.TYPE));
349: }
350: //-- Double/double
351: else if ((a == Double.class) || (a == Double.TYPE)) {
352: return ((b == Double.class) || (b == Double.TYPE));
353: } else if ((a == Float.class) || (a == Float.TYPE)) {
354: return ((b == Float.class) || (b == Float.TYPE));
355: }
356: //-- Integer/int
357: else if ((a == Integer.class) || (a == Integer.TYPE)) {
358: return ((b == Integer.class) || (b == Integer.TYPE));
359: }
360: //-- Long/long
361: else if ((a == Long.class) || (a == Long.TYPE)) {
362: return ((b == Long.class) || (b == Long.TYPE));
363: }
364: //-- Short/short
365: else if ((a == Short.class) || (a == Short.TYPE)) {
366: return ((b == Short.class) || (b == Short.TYPE));
367: }
368:
369: return false;
370: } //-- primitiveOrWrapperEquals
371:
372: /**
373: *
374: */
375: private static final InheritanceMatch[] NO_MATCH_ARRAY = new InheritanceMatch[0];
376:
377: /**
378: * Search there is a field descriptor which can accept one of the class
379: * descriptor which match the given name and namespace.
380: *
381: * @return An array of InheritanceMatch.
382: */
383: public static InheritanceMatch[] searchInheritance(String name,
384: String namespace, XMLClassDescriptor classDesc,
385: XMLClassDescriptorResolver cdResolver)
386: throws MarshalException {
387: Iterator classDescriptorIterator = null;
388:
389: try {
390: //-- A little required logic for finding Not-Yet-Loaded
391: //-- descriptors
392: String className = JavaNaming.toJavaClassName(name);
393: //-- should use namespace-to-prefix mappings, but
394: //-- just create package for now.
395: Class clazz = classDesc.getJavaClass();
396: String pkg = null;
397: if (clazz != null) {
398: while (clazz.getDeclaringClass() != null)
399: clazz = clazz.getDeclaringClass();
400: pkg = clazz.getName();
401: int idx = pkg.lastIndexOf('.');
402: if (idx >= 0) {
403: pkg = pkg.substring(0, idx + 1);
404: className = pkg + className;
405: }
406: }
407: cdResolver.resolve(className, classDesc.getClass()
408: .getClassLoader());
409: //-- end Not-Yet-Loaded descriptor logic
410:
411: //-- resolve all by XML name + namespace URI
412: classDescriptorIterator = cdResolver.resolveAllByXMLName(
413: name, namespace, null);
414: } catch (ResolverException rx) {
415: Throwable actual = rx.getCause();
416: if (actual instanceof MarshalException) {
417: throw (MarshalException) actual;
418: }
419: if (actual != null) {
420: throw new MarshalException(actual);
421: }
422: throw new MarshalException(rx);
423: }
424:
425: Vector inheritanceList = null;
426: XMLFieldDescriptor descriptor = null;
427: XMLFieldDescriptor[] descriptors = classDesc
428: .getElementDescriptors();
429: XMLClassDescriptor cdInherited = null;
430:
431: if (classDescriptorIterator.hasNext()) {
432: while (classDescriptorIterator.hasNext()
433: && (descriptor == null)) {
434: cdInherited = (XMLClassDescriptor) classDescriptorIterator
435: .next();
436: Class subclass = cdInherited.getJavaClass();
437:
438: for (int i = 0; i < descriptors.length; i++) {
439:
440: if (descriptors[i] == null)
441: continue;
442:
443: //-- skip descriptors with special internal name
444: if (INTERNAL_XML_NAME.equals(descriptors[i]
445: .getXMLName()))
446: continue;
447:
448: //-- check for inheritence
449: Class super class = descriptors[i].getFieldType();
450:
451: // It is possible that the superclass is of type object if we use any node.
452: if (super class.isAssignableFrom(subclass)
453: && (super class != Object.class)) {
454: descriptor = descriptors[i];
455: if (inheritanceList == null)
456: inheritanceList = new Vector(3);
457: inheritanceList
458: .addElement(new InheritanceMatch(
459: descriptor, cdInherited));
460: }
461: }
462: }
463: //-- reset inherited class descriptor, if necessary
464: if (descriptor == null)
465: cdInherited = null;
466: }
467:
468: if (inheritanceList != null) {
469: InheritanceMatch[] result = new InheritanceMatch[inheritanceList
470: .size()];
471: inheritanceList.toArray(result);
472: return result;
473: }
474: return NO_MATCH_ARRAY;
475:
476: }
477:
478: /**
479: * Used to store the information when we find a possible inheritance. It
480: * store the XMLClassDescriptor of the object to instantiate and the
481: * XMLFieldDescriptor of the parent, where the instance of the
482: * XMLClassDescriptor will be put.
483: */
484: public static class InheritanceMatch {
485:
486: public XMLFieldDescriptor parentFieldDesc;
487: public XMLClassDescriptor inheritedClassDesc;
488:
489: public InheritanceMatch(XMLFieldDescriptor fieldDesc,
490: XMLClassDescriptor classDesc) {
491: parentFieldDesc = fieldDesc;
492: inheritedClassDesc = classDesc;
493: }
494: }
495:
496: /**
497: * An internal implementation of XMLClassDescriptor used by
498: * the Unmarshaller and Marshaller...
499: */
500: class InternalXMLClassDescriptor implements XMLClassDescriptor {
501:
502: private XMLClassDescriptor _classDesc = null;
503:
504: /**
505: * Cached arrays
506: */
507: private XMLFieldDescriptor[] _attributes = null;
508: private XMLFieldDescriptor[] _elements = null;
509: private FieldDescriptor[] _fields = null;
510:
511: /**
512: * Creates a new InternalXMLClassDescriptor for the given
513: * XMLClassDescriptor
514: */
515: protected InternalXMLClassDescriptor(
516: XMLClassDescriptor classDesc) {
517: if (classDesc == null) {
518: String err = "The argument 'classDesc' must not be null.";
519: throw new IllegalArgumentException(err);
520: }
521:
522: //-- prevent wrapping another InternalXMLClassDescriptor,
523: while (classDesc instanceof InternalXMLClassDescriptor) {
524: classDesc = ((InternalXMLClassDescriptor) classDesc)
525: .getClassDescriptor();
526: }
527: _classDesc = classDesc;
528: }
529:
530: /**
531: * Returns the XMLClassDescriptor that this InternalXMLClassDescriptor
532: * wraps.
533: *
534: * @return the XMLClassDescriptor
535: */
536: public XMLClassDescriptor getClassDescriptor() {
537: return _classDesc;
538: } //-- getClassDescriptor
539:
540: /**
541: * Returns the set of XMLFieldDescriptors for all members
542: * that should be marshalled as XML attributes. This
543: * includes namespace nodes.
544: *
545: * @return an array of XMLFieldDescriptors for all members
546: * that should be marshalled as XML attributes.
547: */
548: public XMLFieldDescriptor[] getAttributeDescriptors() {
549: if (_attributes == null) {
550: _attributes = _classDesc.getAttributeDescriptors();
551: }
552: return _attributes;
553: } //-- getAttributeDescriptors
554:
555: /**
556: * Returns the XMLFieldDescriptor for the member
557: * that should be marshalled as text content.
558: *
559: * @return the XMLFieldDescriptor for the member
560: * that should be marshalled as text content.
561: */
562: public XMLFieldDescriptor getContentDescriptor() {
563: return _classDesc.getContentDescriptor();
564: } //-- getContentDescriptor
565:
566: /**
567: * Returns the XML field descriptor matching the given
568: * xml name and nodeType. If NodeType is null, then
569: * either an AttributeDescriptor, or ElementDescriptor
570: * may be returned. Null is returned if no matching
571: * descriptor is available.
572: *
573: * @param name the xml name to match against
574: * @param nodeType the NodeType to match against, or null if
575: * the node type is not known.
576: * @return the matching descriptor, or null if no matching
577: * descriptor is available.
578: *
579: */
580: public XMLFieldDescriptor getFieldDescriptor(String name,
581: String namespace, NodeType nodeType) {
582: return _classDesc.getFieldDescriptor(name, namespace,
583: nodeType);
584: } //-- getFieldDescriptor
585:
586: /**
587: * Returns the set of XMLFieldDescriptors for all members
588: * that should be marshalled as XML elements.
589: *
590: * @return an array of XMLFieldDescriptors for all members
591: * that should be marshalled as XML elements.
592: */
593: public XMLFieldDescriptor[] getElementDescriptors() {
594: if (_elements == null) {
595: _elements = _classDesc.getElementDescriptors();
596: }
597: return _elements;
598: } //-- getElementDescriptors
599:
600: /**
601: * @return the namespace prefix to use when marshalling as XML.
602: */
603: public String getNameSpacePrefix() {
604: return _classDesc.getNameSpacePrefix();
605: } //-- getNameSpacePrefix
606:
607: /**
608: * @return the namespace URI used when marshalling and unmarshalling as XML.
609: */
610: public String getNameSpaceURI() {
611: return _classDesc.getNameSpaceURI();
612: } //-- getNameSpaceURI
613:
614: /**
615: * Returns a specific validator for the class described by
616: * this ClassDescriptor. A null value may be returned
617: * if no specific validator exists.
618: *
619: * @return the type validator for the class described by this
620: * ClassDescriptor.
621: */
622: public TypeValidator getValidator() {
623: return _classDesc.getValidator();
624: } //-- getValidator
625:
626: /**
627: * Returns the XML Name for the Class being described.
628: *
629: * @return the XML name.
630: */
631: public String getXMLName() {
632: return _classDesc.getXMLName();
633: } //-- getXMLName
634:
635: /**
636: * Returns true if the wrapped ClassDescriptor was created
637: * by introspection.
638: *
639: * @return true if the wrapped ClassDescriptor was created
640: * by introspection.
641: */
642: public boolean introspected() {
643: return Introspector.introspected(_classDesc);
644: } //-- introspected
645:
646: /**
647: * @see org.exolab.castor.xml.XMLClassDescriptor#canAccept(
648: * java.lang.String, java.lang.String, java.lang.Object)
649: */
650: public boolean canAccept(String name, String namespace,
651: Object object) {
652: return _classDesc.canAccept(name, namespace, object);
653: } //-- canAccept
654:
655: /**
656: * {@inheritDoc}
657: *
658: * @see org.exolab.castor.xml.XMLClassDescriptor#checkDescriptorForCorrectOrderWithinSequence(org.exolab.castor.xml.XMLFieldDescriptor, org.exolab.castor.xml.UnmarshalState, java.lang.String)
659: */
660: public void checkDescriptorForCorrectOrderWithinSequence(
661: final XMLFieldDescriptor elementDescriptor,
662: final UnmarshalState parentState, final String xmlName)
663: throws ValidationException {
664: _classDesc.checkDescriptorForCorrectOrderWithinSequence(
665: elementDescriptor, parentState, xmlName);
666: }
667:
668: //-------------------------------------/
669: //- Implementation of ClassDescriptor -/
670: //-------------------------------------/
671:
672: /**
673: * Returns the Java class represented by this descriptor.
674: *
675: * @return The Java class
676: */
677: public Class getJavaClass() {
678: return _classDesc.getJavaClass();
679: } //-- getJavaClass
680:
681: /**
682: * Returns a list of fields represented by this descriptor.
683: *
684: * @return A list of fields
685: */
686: public FieldDescriptor[] getFields() {
687: if (_fields == null) {
688: _fields = _classDesc.getFields();
689: }
690: return _fields;
691: } //-- getFields
692:
693: /**
694: * Returns the class descriptor of the class extended by this class.
695: *
696: * @return The extended class descriptor
697: */
698: public ClassDescriptor getExtends() {
699: return _classDesc.getExtends();
700: } //-- getExtends
701:
702: /**
703: * Returns the identity field, null if this class has no identity.
704: *
705: * @return The identity field
706: */
707: public FieldDescriptor getIdentity() {
708: return _classDesc.getIdentity();
709: } //-- getIdentity
710: } //-- InternalXMLClassDescriptor
711: } //-- MarshalFramework
|