All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.exolab.castor.xml.Introspector Maven / Gradle / Ivy

/**
 * Redistribution and use of this software and associated documentation ("Software"), with or
 * without modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain copyright statements and notices. Redistributions
 * must also contain a copy of this document.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice, this list of
 * conditions and the following disclaimer in the documentation and/or other materials provided with
 * the distribution.
 *
 * 3. The name "Exolab" must not be used to endorse or promote products derived from this Software
 * without prior written permission of Intalio, Inc. For written permission, please contact
 * [email protected].
 *
 * 4. Products derived from this Software may not be called "Exolab" nor may "Exolab" appear in
 * their names without prior written permission of Intalio, Inc. Exolab is a registered trademark of
 * Intalio, Inc.
 *
 * 5. Due credit should be given to the Exolab Project (http://www.exolab.org/).
 *
 * THIS SOFTWARE IS PROVIDED BY INTALIO, INC. AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESSED OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTALIO, INC. OR ITS
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
 * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * Copyright 1999-2003 (C) Intalio, Inc. All Rights Reserved.
 *
 * $Id$
 */
package org.exolab.castor.xml;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.PrintWriter;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.List;
import java.util.Vector;

import org.castor.xml.InternalContext;
import org.castor.xml.JavaNaming;
import org.castor.xml.XMLProperties;
import org.castor.xml.XMLNaming;
import org.exolab.castor.mapping.CollectionHandler;
import org.exolab.castor.mapping.FieldHandler;
import org.exolab.castor.mapping.FieldHandlerFactory;
import org.exolab.castor.mapping.GeneralizedFieldHandler;
import org.exolab.castor.mapping.MappingException;
import org.exolab.castor.mapping.TypeConvertor;
import org.exolab.castor.mapping.loader.CollectionHandlers;
import org.exolab.castor.mapping.loader.FieldHandlerImpl;
import org.exolab.castor.mapping.loader.TypeInfo;
import org.exolab.castor.util.ReflectionUtil;
import org.exolab.castor.xml.descriptors.CoreDescriptors;
import org.exolab.castor.xml.handlers.ContainerFieldHandler;
import org.exolab.castor.xml.handlers.DateFieldHandler;
import org.exolab.castor.xml.handlers.DefaultFieldHandlerFactory;
import org.exolab.castor.xml.util.ContainerElement;
import org.exolab.castor.xml.util.XMLClassDescriptorImpl;
import org.exolab.castor.xml.util.XMLFieldDescriptorImpl;

/**
 * A Helper class for the Marshaller and Unmarshaller, basically the common code base between the
 * two. This class handles the introspection to dynamically create descriptors.
 *
 * @author Keith Visco
 * @version $Revision$ $Date: 2006-04-14 04:14:43 -0600 (Fri, 14 Apr 2006) $
 */
public final class Introspector implements PropertyChangeListener {

  /** The default FieldHandlerFactory. */
  private static final FieldHandlerFactory DEFAULT_HANDLER_FACTORY =
      new DefaultFieldHandlerFactory();

  private static final Class[] EMPTY_CLASS_ARGS = new Class[0];

  /** Name of the java.util.List collection. */
  private static final String LIST = "java.util.List";

  /** Name of the java.util.Map collection. */
  private static final String MAP = "java.util.Map";

  /** Name of the java.util.Map collection. */
  private static final String SET_COLLECTION = "java.util.Set";

  /** Used as a prefix for the name of a container field. */
  private static final String COLLECTION_WRAPPER_PREFIX = "##container_for_";


  /**
   * The default flag indicating whether or not collections (arrays, vectors, etc) should be wrapped
   * in a container element.
   *
   * @see _wrapCollectionsInContainer
   */
  private static final boolean WRAP_COLLECTIONS_DEFAULT = false;

  /**
   * The set of available collections to use during introspection. JDK dependant.
   **/
  private static final Class[] COLLECTIONS = loadCollections();


  /**
   * The default naming conventions.
   */
  private static XMLNaming _defaultNaming = null;

  /**
   * The naming conventions to use.
   */
  private XMLNaming _xmlNaming = null;

  /**
   * The NodeType to use for primitives.
   */
  private NodeType _primitiveNodeType = null;


  /**
   * The variable flag indicating whether or not collections (arrays, vectors, etc) should be
   * wrapped in a container element. For example:
   *
   * 
   *    <foos>
   *       <foo>foo1</foo>
   *       <foo>foo2</foo>
   *    </foos>
   *
   *   instead of the default:
   *
   *    <foos>foo1<foos>
   *    <foos>foo2</foos>
   *
   * 
* */ private boolean _wrapCollectionsInContainer = WRAP_COLLECTIONS_DEFAULT; /** * The set of registered FieldHandlerFactory instances */ private Vector _handlerFactoryList = null; /** * The set of registered FieldHandlerFactory instances associated with their supported types */ private Hashtable _handlerFactoryMap = null; /** * A flag indicating that MapKeys should be saved. To remain backward compatible this may be * disable via the castor.properties. */ private boolean _saveMapKeys = true; /** * Specifies class loader to be used. */ private ClassLoader _classLoader = null; /** * The {@link JavaNaming} to be used. */ private JavaNaming _javaNaming; private InternalContext _internalContext; /** * Creates a new instance of the Introspector. */ public Introspector() { this(null); } // -- Introspector /** * Creates a new instance of the Introspector. * * @param classLoader */ public Introspector(final ClassLoader classLoader) { super(); _classLoader = classLoader; init(); } // -- Introspector private void init() { if (_internalContext != null) { _javaNaming = _internalContext.getJavaNaming(); _xmlNaming = _internalContext.getXMLNaming(); setPrimitiveNodeType(_internalContext.getPrimitiveNodeType()); _wrapCollectionsInContainer = _internalContext .getBooleanProperty(XMLProperties.WRAP_COLLECTIONS_PROPERTY).booleanValue(); _saveMapKeys = _internalContext.getBooleanProperty(XMLProperties.SAVE_MAP_KEYS).booleanValue(); } } // -- init public void setInternalContext(InternalContext internalContext) { if (internalContext == null) { if (this._internalContext != null) { this._internalContext.removePropertyChangeListener(this); } } else { if (this._internalContext != internalContext) { if (this._internalContext != null) { this._internalContext.removePropertyChangeListener(this); } internalContext.addPropertyChangeListener(this); } } _internalContext = internalContext; init(); } /** * Registers the given "generalized" FieldHandlerFactory with this Introspector. * * @param factory the FieldHandlerFactory to add to this introspector * @throws IllegalArgumentException if the given factory is null */ public synchronized void addFieldHandlerFactory(FieldHandlerFactory factory) { if (factory == null) { String err = "The argument 'factory' must not be null."; throw new IllegalArgumentException(err); } if (_handlerFactoryList == null) { _handlerFactoryList = new Vector<>(); } _handlerFactoryList.add(factory); registerHandlerFactory(factory); } // -- addFieldHandlerFactory /** * Returns the NodeType for java primitives * * @return the NodeType for java primitives **/ public NodeType getPrimitiveNodeType() { return _primitiveNodeType; } // -- getPrimitiveNodeType /** * Creates an XMLClassDescriptor for the given class by using Reflection. * * @param c the Class to create the XMLClassDescriptor for * @return the new XMLClassDescriptor created for the given class * @exception MarshalException when an error occurs during the creation of the ClassDescriptor. **/ public XMLClassDescriptor generateClassDescriptor(Class c) throws MarshalException { return generateClassDescriptor(c, null); } // -- generateClassDescriptor(Class) /** * Creates an XMLClassDescriptor for the given class by using Reflection. * * @param c the Class to create the XMLClassDescriptor for * @param errorWriter a PrintWriter to print error information to * @return the new XMLClassDescriptor created for the given class * @exception MarshalException when an error occurs during the creation of the ClassDescriptor. **/ public XMLClassDescriptor generateClassDescriptor(Class c, PrintWriter errorWriter) throws MarshalException { if (c == null) return null; // -- handle arrays if (c.isArray()) return null; // -- handle base objects if ((c == Void.class) || (c == Class.class) || (c == Object.class)) { throw new MarshalException(MarshalException.BASE_CLASS_OR_VOID_ERR); } // -- handle core descriptors XMLClassDescriptor coreDesc = CoreDescriptors.getDescriptor(c); if (coreDesc != null) return coreDesc; // --------------------------/ // - handle complex objects -/ // --------------------------/ XMLClassDescriptorImpl classDesc = new IntrospectedXMLClassDescriptor(c); Method[] methods = c.getMethods(); List dateDescriptors = new ArrayList<>(3); Hashtable methodSets = new Hashtable<>(); int methodCount = 0; Class superClass = c.getSuperclass(); Class[] interfaces = c.getInterfaces(); // -- create method sets for (int i = 0; i < methods.length; i++) { Method method = methods[i]; Class owner = method.getDeclaringClass(); // -- ignore methods from super-class, that will be // -- introspected separately, if necessary if (owner != c) { // -- if declaring class is anything but // -- an interface, than just continue, // -- the field comes from a super class // -- (e.g. java.lang.Object) if (!owner.isInterface()) continue; // -- owner is an interface, is it an // -- interface this class implements // -- or a parent class? if (interfaces.length > 0) { boolean found = false; for (int count = 0; count < interfaces.length; count++) { if (interfaces[count] == owner) { found = true; break; } } if (!found) continue; } } else { // -- look for overloaded methods if (superClass != null) { Class[] args = method.getParameterTypes(); String name = method.getName(); Method tmpMethod = null; try { tmpMethod = superClass.getMethod(name, args); } catch (NoSuchMethodException nsme) { // -- do nothing } if (tmpMethod != null) continue; } } // -- if method is static...ignore if ((method.getModifiers() & Modifier.STATIC) != 0) continue; String methodName = method.getName(); // -- read methods if (methodName.startsWith(JavaNaming.METHOD_PREFIX_GET)) { if (method.getParameterTypes().length != 0) { continue; } // -- disable direct field access ++methodCount; // -- make sure return type is "descriptable" // -- and not null Class type = method.getReturnType(); if (type == null) continue; if (!isDescriptable(type)) continue; // -- caclulate name from Method name String fieldName = methodName.substring(3); fieldName = _javaNaming.toJavaMemberName(fieldName); MethodSet methodSet = methodSets.get(fieldName); if (methodSet == null) { methodSet = new MethodSet(fieldName); methodSets.put(fieldName, methodSet); } methodSet._get = method; } else if (methodName.startsWith(JavaNaming.METHOD_PREFIX_IS)) { if (method.getParameterTypes().length != 0) continue; // -- make sure type is not null, and a boolean Class type = method.getReturnType(); if (type == null) continue; if (type.isPrimitive()) { if (type != Boolean.TYPE) continue; } else { if (type != Boolean.class) continue; } // -- disable direct field access ++methodCount; // -- caclulate name from Method name String fieldName = methodName.substring(JavaNaming.METHOD_PREFIX_IS.length()); fieldName = _javaNaming.toJavaMemberName(fieldName); MethodSet methodSet = methodSets.get(fieldName); if (methodSet == null) { methodSet = new MethodSet(fieldName); methodSets.put(fieldName, methodSet); } methodSet._get = method; } // -----------------------------------/ // -- write methods (collection item) else if (methodName.startsWith(JavaNaming.METHOD_PREFIX_ADD)) { if (method.getParameterTypes().length != 1) continue; // -- disable direct field access ++methodCount; // -- make sure parameter type is "descriptable" if (!isDescriptable(method.getParameterTypes()[0])) continue; // -- caclulate name from Method name String fieldName = methodName.substring(3); fieldName = _javaNaming.toJavaMemberName(fieldName); MethodSet methodSet = methodSets.get(fieldName); if (methodSet == null) { methodSet = new MethodSet(fieldName); methodSets.put(fieldName, methodSet); } methodSet._add = method; } // -- write method (singleton or collection) else if (methodName.startsWith(JavaNaming.METHOD_PREFIX_SET)) { if (method.getParameterTypes().length != 1) { continue; } // -- disable direct field access ++methodCount; // -- make sure parameter type is "descriptable" if (!isDescriptable(method.getParameterTypes()[0])) continue; // -- caclulate name from Method name String fieldName = methodName.substring(3); fieldName = _javaNaming.toJavaMemberName(fieldName); MethodSet methodSet = methodSets.get(fieldName); if (methodSet == null) { methodSet = new MethodSet(fieldName); methodSets.put(fieldName, methodSet); } methodSet._set = method; } else if (methodName.startsWith(JavaNaming.METHOD_PREFIX_CREATE)) { if (method.getParameterTypes().length != 0) continue; Class type = method.getReturnType(); // -- make sure return type is "descriptable" // -- and not null if (!isDescriptable(type)) continue; // -- caclulate name from Method name String fieldName = methodName.substring(JavaNaming.METHOD_PREFIX_CREATE.length()); fieldName = _javaNaming.toJavaMemberName(fieldName); MethodSet methodSet = methodSets.get(fieldName); if (methodSet == null) { methodSet = new MethodSet(fieldName); methodSets.put(fieldName, methodSet); } methodSet._create = method; } } // -- end create method sets // -- Loop Through MethodSets and create // -- descriptors Enumeration enumeration = methodSets.elements(); while (enumeration.hasMoreElements()) { MethodSet methodSet = enumeration.nextElement(); // -- create XMLFieldDescriptor String xmlName = _xmlNaming.toXMLName(methodSet._fieldName); boolean isCollection = false; // -- calculate class type // -- 1st check for add-method, then set or get method Class type = null; if (methodSet._add != null) { type = methodSet._add.getParameterTypes()[0]; isCollection = true; } // -- if there was no add method, use get/set methods // -- to calculate type. if (type == null) { if (methodSet._get != null) { type = methodSet._get.getReturnType(); } else if (methodSet._set != null) { type = methodSet._set.getParameterTypes()[0]; } else { // -- if we make it here, the only method found // -- was a create method, which is useless by itself. continue; } } // -- Handle Collections isCollection = (isCollection || isCollection(type)); TypeInfo typeInfo = null; CollectionHandler colHandler = null; // -- If the type is a collection and there is no add method, // -- then we obtain a CollectionHandler if (isCollection && (methodSet._add == null)) { try { colHandler = CollectionHandlers.getHandler(type); } catch (MappingException mx) { // -- No collection handler available, // -- proceed anyway... } // -- Find component type if (type.isArray()) { // -- Byte arrays are handled as a special case // -- so don't use CollectionHandler if (type.getComponentType() == Byte.TYPE) { colHandler = null; } else type = type.getComponentType(); } } typeInfo = new TypeInfo(type, null, null, false, null, colHandler); // -- Create FieldHandler first, before the XMLFieldDescriptor // -- in case we need to use a custom handler FieldHandler handler = null; boolean customHandler = false; try { handler = new FieldHandlerImpl(methodSet._fieldName, null, null, methodSet._get, methodSet._set, typeInfo); // -- clean up if (methodSet._add != null) ((FieldHandlerImpl) handler).setAddMethod(methodSet._add); if (methodSet._create != null) ((FieldHandlerImpl) handler).setCreateMethod(methodSet._create); // -- handle Hashtable/Map if (isCollection && _saveMapKeys && isMapCollection(type)) { ((FieldHandlerImpl) handler).setConvertFrom(new IdentityConvertor()); } // -- look for GeneralizedFieldHandler FieldHandlerFactory factory = getHandlerFactory(type); if (factory != null) { GeneralizedFieldHandler gfh = factory.createFieldHandler(type); if (gfh != null) { gfh.setFieldHandler(handler); handler = gfh; customHandler = true; // -- swap type with the type specified by the // -- custom field handler if (gfh.getFieldType() != null) { type = gfh.getFieldType(); } } } } catch (MappingException mx) { throw new MarshalException(mx); } XMLFieldDescriptorImpl fieldDesc = createFieldDescriptor(type, methodSet._fieldName, xmlName); if (isCollection) { fieldDesc.setMultivalued(true); fieldDesc.setNodeType(NodeType.Element); } // -- check for instances of java.util.Date if (java.util.Date.class.isAssignableFrom(type)) { // handler = new DateFieldHandler(handler); if (!customHandler) { dateDescriptors.add(fieldDesc); } } fieldDesc.setHandler(handler); // -- enable use parent namespace if explicit one doesn't exist fieldDesc.setUseParentsNamespace(true); // -- Wrap collections? if (isCollection && _wrapCollectionsInContainer) { String fieldName = COLLECTION_WRAPPER_PREFIX + methodSet._fieldName; // -- If we have a field 'c' that is a collection and // -- we want to wrap that field in an element , we // -- need to create a field descriptor for // -- an object that represents the element and // -- acts as a go-between from the parent of 'c' // -- denoted as P(c) and 'c' itself // // object model: P(c) -> c // xml :

// -- Make new class descriptor for the field that // -- will represent the container element Class cType = ContainerElement.class; XMLClassDescriptorImpl containerClassDesc = new XMLClassDescriptorImpl(cType); // -- add the field descriptor to our new class descriptor containerClassDesc.addFieldDescriptor(fieldDesc); // -- nullify xmlName so that auto-naming will be enabled, // -- we can't do this in the constructor because // -- XMLFieldDescriptorImpl will create a default one. fieldDesc.setXMLName(null); fieldDesc.setMatches("*"); // -- wrap the field handler in a special container field // -- handler that will actually do the delegation work FieldHandler cHandler = new ContainerFieldHandler(handler); fieldDesc.setHandler(cHandler); fieldDesc = createFieldDescriptor(cType, fieldName, xmlName); fieldDesc.setClassDescriptor(containerClassDesc); fieldDesc.setHandler(cHandler); // -- enable use parent namespace if explicit one doesn't exist fieldDesc.setUseParentsNamespace(true); } // -- add FieldDescriptor to ClassDescriptor classDesc.addFieldDescriptor(fieldDesc); } // -- end of method loop // -- If we didn't find any methods we can try // -- direct field access if (methodCount == 0) { Field[] fields = c.getFields(); Hashtable descriptors = new Hashtable<>(); for (int i = 0; i < fields.length; i++) { Field field = fields[i]; Class owner = field.getDeclaringClass(); // -- ignore fields from super-class, that will be // -- introspected separately, if necessary if (owner != c) { // -- if declaring class is anything but // -- an interface, than just continue, // -- the field comes from a super class // -- (e.g. java.lang.Object) if (!owner.isInterface()) continue; // -- owner is an interface, is it an // -- interface this class implements // -- or a parent class? if (interfaces.length > 0) { boolean found = false; for (int count = 0; count < interfaces.length; count++) { if (interfaces[count] == owner) { found = true; break; } } if (!found) continue; } } // -- make sure field is not transient or static final int modifiers = field.getModifiers(); if (Modifier.isTransient(modifiers)) continue; if (Modifier.isFinal(modifiers) && Modifier.isStatic(modifiers)) continue; Class type = field.getType(); if (!isDescriptable(type)) continue; // -- Built-in support for JDK 1.1 Collections // -- we need to a pluggable interface for // -- JDK 1.2+ boolean isCollection = isCollection(type); TypeInfo typeInfo = null; CollectionHandler colHandler = null; // -- If the type is a collection and there is no add method, // -- then we obtain a CollectionHandler if (isCollection) { try { colHandler = CollectionHandlers.getHandler(type); } catch (MappingException mx) { // -- No CollectionHandler available, continue // -- without one... } // -- Find component type if (type.isArray()) { // -- Byte arrays are handled as a special case // -- so don't use CollectionHandler if (type.getComponentType() == Byte.TYPE) { colHandler = null; } else type = type.getComponentType(); } } String fieldName = field.getName(); String xmlName = _xmlNaming.toXMLName(fieldName); // -- Create FieldHandler first, before the XMLFieldDescriptor // -- in case we need to use a custom handler typeInfo = new TypeInfo(type, null, null, false, null, colHandler); FieldHandler handler = null; boolean customHandler = false; try { handler = new FieldHandlerImpl(field, typeInfo); // -- handle Hashtable/Map if (isCollection && _saveMapKeys && isMapCollection(type)) { ((FieldHandlerImpl) handler).setConvertFrom(new IdentityConvertor()); } // -- look for GeneralizedFieldHandler FieldHandlerFactory factory = getHandlerFactory(type); if (factory != null) { GeneralizedFieldHandler gfh = factory.createFieldHandler(type); if (gfh != null) { gfh.setFieldHandler(handler); handler = gfh; customHandler = true; // -- swap type with the type specified by the // -- custom field handler if (gfh.getFieldType() != null) { type = gfh.getFieldType(); } } } } catch (MappingException mx) { throw new MarshalException(mx); } XMLFieldDescriptorImpl fieldDesc = createFieldDescriptor(type, fieldName, xmlName); if (isCollection) { fieldDesc.setNodeType(NodeType.Element); fieldDesc.setMultivalued(true); } descriptors.put(xmlName, fieldDesc); classDesc.addFieldDescriptor(fieldDesc); fieldDesc.setHandler(handler); // -- enable use parent namespace if explicit one doesn't exist fieldDesc.setUseParentsNamespace(true); // -- check for instances of java.util.Date if (java.util.Date.class.isAssignableFrom(type)) { if (!customHandler) { dateDescriptors.add(fieldDesc); } } } } // -- end of direct field access // -- A temporary fix for java.util.Date if (dateDescriptors != null) { for (XMLFieldDescriptorImpl fieldDesc : dateDescriptors) { FieldHandler handler = fieldDesc.getHandler(); fieldDesc.setImmutable(true); DateFieldHandler dfh = new DateFieldHandler(handler); // -- patch for java.sql.Date Class type = fieldDesc.getFieldType(); if (java.sql.Date.class.isAssignableFrom(type)) { dfh.setUseSQLDate(true); } fieldDesc.setHandler(dfh); } } // -- Add reference to superclass...if necessary if ((superClass != null) && (superClass != Void.class) && (superClass != Object.class) && (superClass != Class.class)) { try { XMLClassDescriptor parent = generateClassDescriptor(superClass, errorWriter); if (parent != null) { classDesc.setExtends(parent); } } catch (MarshalException mx) { // -- Ignore for now. } } return classDesc; } // -- generateClassDescriptor /** * Removes the given FieldHandlerFactory from this Introspector * * @param factory the FieldHandlerFactory to remove * @return true if the given FieldHandlerFactory was removed, or false otherwise. * @throws IllegalArgumentException if the given factory is null */ public synchronized boolean removeFieldHandlerFactory(FieldHandlerFactory factory) { if (factory == null) { String err = "The argument 'factory' must not be null."; throw new IllegalArgumentException(err); } // -- if list is null, just return if (_handlerFactoryList == null) return false; if (_handlerFactoryList.remove(factory)) { // -- re-register remaining handlers _handlerFactoryMap.clear(); for (FieldHandlerFactory tmp : _handlerFactoryList) { registerHandlerFactory(tmp); } return true; } return false; } // -- removeFieldHandlerFactory /** * Sets whether or not collections (arrays, vectors, etc) should be wrapped in a container * element. For example: * *

   *
   *    <foos>
   *       <foo>foo1</foo>
   *       <foo>foo2</foo>
   *    </foos>
   *
   *   instead of the default:
   *
   *    <foos>foo1<foos>
   *    <foos>foo2</foos>
   *
   * 
* * @param wrapCollections a boolean that when true indicates collections should be wrapped in a * container element. * */ public void setWrapCollections(boolean wrapCollections) { _wrapCollectionsInContainer = wrapCollections; } // -- setWrapCollections /** * Returns true if the given XMLClassDescriptor was created via introspection **/ public static boolean introspected(XMLClassDescriptor descriptor) { return (descriptor instanceof IntrospectedXMLClassDescriptor); } // -- introspected /** * Returns true if the given Class can be marshalled. * * @param type the Class to check marshallability for. * @return true if the given Class can be marshalled. **/ public static boolean marshallable(Class type) { // -- make sure type is not Void, or Class; if (type == Void.class || type == Class.class) return false; if ((!type.isInterface() || (type == Object.class))) { if (!isPrimitive(type)) { // -- make sure type is serializable // if (!Serializable.class.isAssignableFrom( type )) // return false; // -- make sure we can construct the Object if (!type.isArray()) { // -- try to get the default constructor and make // -- sure we are only looking at classes that can // -- be instantiated by calling Class#newInstance try { type.getConstructor(EMPTY_CLASS_ARGS); } catch (NoSuchMethodException e) { // -- Allow any built-in descriptor classes // -- that don't have default constructors // -- such as java.sql.Date, java.sql.Time, etc. return (CoreDescriptors.getDescriptor(type) != null); } } } } return true; } // -- marshallable /** * Sets the Naming conventions to be used by the Introspector * * @param naming the implementation of Naming to use. A value of null, will reset the XMLNaming to * the default specified in the castor.properties file. **/ public void setNaming(XMLNaming naming) { if (naming == null) _xmlNaming = _defaultNaming; else _xmlNaming = naming; } // -- setNaming /** * Sets the NodeType for primitives. If the NodeType is NodeType.Element, all primitives will be * treated as Elements, otherwise all primitives will be treated as Attributes. * * @param nodeType the NodeType to use for primitive values. **/ public void setPrimitiveNodeType(NodeType nodeType) { if (nodeType == NodeType.Element) _primitiveNodeType = nodeType; else _primitiveNodeType = NodeType.Attribute; } // -- setPrimitiveNodeType /** * Sets whether or not keys from Hastable / Map instances should be saved in the XML. * *

* Note: This is true by default since Castor 0.9.5.3 *

* * @param saveMapKeys a boolean that when true indicates keys from Hashtable or Map instances * should be saved. Otherwise only the value object is saved. */ public void setSaveMapKeys(boolean saveMapKeys) { _saveMapKeys = saveMapKeys; } // -- setSaveMapKeys /** * Converts the given xml name to a Java name. * * @param name the name to convert to a Java Name * @param upperFirst a flag to indicate whether or not the the first character should be converted * to uppercase. **/ public static String toJavaName(String name, boolean upperFirst) { int size = name.length(); char[] ncChars = name.toCharArray(); int next = 0; boolean uppercase = upperFirst; for (int i = 0; i < size; i++) { char ch = ncChars[i]; switch (ch) { case ':': case '-': uppercase = true; break; default: if (uppercase == true) { ncChars[next] = Character.toUpperCase(ch); uppercase = false; } else ncChars[next] = ch; ++next; break; } } return new String(ncChars, 0, next); } // -- toJavaName // -------------------/ // - Private Methods -/ // -------------------/ private XMLFieldDescriptorImpl createFieldDescriptor(Class type, String fieldName, String xmlName) { XMLFieldDescriptorImpl fieldDesc = new XMLFieldDescriptorImpl(type, fieldName, xmlName, null); if (type.isArray()) { fieldDesc.setNodeType(NodeType.Element); } // -- primitive types are converted to attributes by default else if (type.isPrimitive()) { fieldDesc.setNodeType(_primitiveNodeType); } else { fieldDesc.setNodeType(NodeType.Element); } // -- wildcard? if (type == java.lang.Object.class) { fieldDesc.setMatches(xmlName + " *"); } return fieldDesc; } // -- createFieldDescriptor /** * Returns the registered FieldHandlerFactory for the given Class type. * * @param type the Class type to return the registered FieldHandlerFactory for */ private FieldHandlerFactory getHandlerFactory(Class type) { if (_handlerFactoryMap != null) { Class tmp = type; while (tmp != null) { Object obj = _handlerFactoryMap.get(tmp); if (obj != null) { return (FieldHandlerFactory) obj; } tmp = tmp.getSuperclass(); } } // -- check DefaultFieldHandlerFactory if (DEFAULT_HANDLER_FACTORY.isSupportedType(type)) return DEFAULT_HANDLER_FACTORY; return null; } // -- getHandlerFactory /** * Registers the supported class types for the given FieldHandlerFactory into the map (for faster * lookups) */ private void registerHandlerFactory(FieldHandlerFactory factory) { if (_handlerFactoryMap == null) _handlerFactoryMap = new Hashtable<>(); Class[] types = factory.getSupportedTypes(); for (int i = 0; i < types.length; i++) { _handlerFactoryMap.put(types[i], factory); } } // -- registerHandlerFactory /** * Returns true if the given Class is an instance of a collection class. */ public static boolean isCollection(Class clazz) { if (clazz.isArray()) return true; for (int i = 0; i < COLLECTIONS.length; i++) { // -- check to see if clazz is either the // -- same as or a subclass of one of the // -- available collections. For performance // -- reasons we first check if class is // -- directly equal to one of the collections // -- instead of just calling isAssignableFrom. if ((clazz == COLLECTIONS[i]) || (COLLECTIONS[i].isAssignableFrom(clazz))) { return true; } } return false; } // -- isCollection /** * Returns true if the given Class is an instance of a collection class. */ public static boolean isMapCollection(Class clazz) { if (clazz.isArray()) return false; for (int i = 0; i < COLLECTIONS.length; i++) { // -- check to see if clazz is either the // -- same as or a subclass of one of the // -- available collections. For performance // -- reasons we first check if class is // -- directly equal to one of the collections // -- instead of just calling isAssignableFrom. if ((clazz == COLLECTIONS[i]) || (COLLECTIONS[i].isAssignableFrom(clazz))) { if (COLLECTIONS[i] == java.util.Hashtable.class) return true; // -- For JDK 1.1 compatibility use string name "java.util.Map" if (COLLECTIONS[i].getName().equals(MAP)) return true; } } return false; } // -- isMapCollection /** * Returns true if we are allowed to create a descriptor for a given class type * * @param type the Class type to test * @return true if we are allowed to create a descriptor for a given class type **/ private static boolean isDescriptable(Class type) { // -- make sure type is not Void, or Class; if (type == Void.class || type == Class.class) return false; // -- check whether it is a Java 5.0 enum float javaVersion = Float.valueOf(System.getProperty("java.specification.version")).floatValue(); if (javaVersion >= 1.5) { try { Boolean isEnum = ReflectionUtil.isEnumViaReflection(type); if (isEnum.booleanValue()) { return true; } } catch (Exception e) { // nothing to report; implies that there's no such method } } if ((!type.isInterface()) && (type != Object.class) && (!isPrimitive(type))) { // -- make sure type is serializable // if (!Serializable.class.isAssignableFrom( type )) // return false; // -- make sure we can construct the Object if (!type.isArray()) { // -- try to get the default constructor and make // -- sure we are only looking at classes that can // -- be instantiated by calling Class#newInstance try { type.getConstructor(EMPTY_CLASS_ARGS); } catch (NoSuchMethodException e) { // -- Allow any built-in descriptor classes // -- that don't have default constructors // -- such as java.sql.Date, java.sql.Time, etc. return (CoreDescriptors.getDescriptor(type) != null); } } } return true; } // -- isDescriptable /** * Returns true if the given class should be treated as a primitive type * * @return true if the given class should be treated as a primitive type **/ private static boolean isPrimitive(Class type) { if (type.isPrimitive()) { return true; } if ((type == Boolean.class) || (type == Character.class)) { return true; } Class superClass = type.getSuperclass(); if (superClass == Number.class) { return true; } if (superClass != null) { return superClass.getName().equals("java.lang.Enum"); } else { return false; } } // -- isPrimitive /** * Returns an array of collections available during introspection. Allows JDK 1.2+ support without * breaking JDK 1.1 support. * * @return a list of available collections **/ private static Class[] loadCollections() { List collections = new ArrayList<>(6); // -- JDK 1.1 collections.add(Vector.class); collections.add(Enumeration.class); collections.add(Hashtable.class); // -- JDK 1.2+ ClassLoader loader = Vector.class.getClassLoader(); Class clazz = null; try { if (loader != null) { clazz = loader.loadClass(LIST); } else clazz = Class.forName(LIST); } catch (ClassNotFoundException cnfx) { // -- just ignore...either JDK 1.1 // -- or some nasty ClassLoader // -- issue has occurred. } if (clazz != null) { // -- java.util.List found, add to collections, // -- also add java.util.Map collections.add(clazz); clazz = null; try { // -- java.util.Map if (loader != null) { clazz = loader.loadClass(MAP); } else clazz = Class.forName(MAP); if (clazz != null) { collections.add(clazz); } // -- java.util.Set if (loader != null) { clazz = loader.loadClass(SET_COLLECTION); } else clazz = Class.forName(SET_COLLECTION); if (clazz != null) { collections.add(clazz); } } catch (ClassNotFoundException cnfx) { // -- just ignore...for now // -- some nasty ClassLoader issue has occurred. } } return collections.toArray(new Class[collections.size()]); } // -- loadCollections public void propertyChange(PropertyChangeEvent event) { if (event.getPropertyName().equals(XMLProperties.PRIMITIVE_NODE_TYPE)) { if (event.getNewValue() instanceof String) { this.setPrimitiveNodeType(NodeType.getNodeType((String) event.getNewValue())); } else { throw new IllegalArgumentException( "The value for '" + XMLProperties.PRIMITIVE_NODE_TYPE + "' must be of type String"); } } } /** * A special TypeConvertor that simply returns the object given. This is used for preventing the * FieldHandlerImpl from using a CollectionHandler when getValue is called. */ class IdentityConvertor implements TypeConvertor { public Object convert(final Object object) { return object; } } // -- class: IdentityConvertor /** * A simple struct for holding a set of accessor methods. */ class MethodSet { /** A reference to the add method. */ Method _add = null; /** A reference to the create method. */ Method _create = null; /** A reference to the get method. */ Method _get = null; /** A reference to the set method. */ Method _set = null; /** The fieldName for the field accessed by the methods in this method set. */ String _fieldName = null; MethodSet(String fieldName) { super(); this._fieldName = fieldName; } } // -- inner class: MethodSet } // -- Introspector /** * A simple extension of XMLClassDescriptor so that we can set the "instrospected" flag. **/ class IntrospectedXMLClassDescriptor extends XMLClassDescriptorImpl { /** * Creates an IntrospectedXMLClassDescriptor * * @param type the Class type with which this ClassDescriptor describes. **/ IntrospectedXMLClassDescriptor(Class type) { super(type); setIntrospected(true); } // -- XMLClassDescriptorImpl /** * Creates an IntrospectedXMLClassDescriptor * * @param type the Class type with which this ClassDescriptor describes. **/ public IntrospectedXMLClassDescriptor(Class type, String xmlName) { super(type, xmlName); setIntrospected(true); } // -- XMLClassDescriptorImpl } // -- IntrospectedClassDescriptor




© 2015 - 2024 Weber Informatics LLC | Privacy Policy