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

org.exolab.castor.xml.util.XMLFieldDescriptorImpl Maven / Gradle / Ivy

/*
 * Copyright 2006 Keith Visco, Ralf Joachim
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing permissions and limitations under
 * the License.
 */
package org.exolab.castor.xml.util;

import org.exolab.castor.mapping.ClassDescriptor;
import org.exolab.castor.mapping.FieldDescriptor;
import org.exolab.castor.mapping.FieldHandler;
import org.exolab.castor.mapping.MapItem;
import org.exolab.castor.mapping.MappingException;
import org.exolab.castor.mapping.loader.FieldDescriptorImpl;
import org.exolab.castor.mapping.loader.FieldHandlerImpl;
import org.exolab.castor.xml.FieldValidator;
import org.exolab.castor.xml.NodeType;
import org.exolab.castor.xml.XMLClassDescriptor;
import org.exolab.castor.xml.XMLFieldDescriptor;
import org.exolab.castor.xml.descriptors.CoreDescriptors;
import org.exolab.castor.xml.handlers.DateFieldHandler;

import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;
import java.util.Properties;

/**
 * XML field descriptor. Wraps {@link FieldDescriptor} and adds XML-related information, type
 * conversion, etc.
 * 

* Note: When using a GeneralizedFieldHandler the getFieldType() methods of handler and descriptor * need to return the same result. * * @author Keith Visco * @author Ralf Joachim * @version $Revision$ $Date: 2006-04-13 06:47:36 -0600 (Thu, 13 Apr 2006) $ */ public class XMLFieldDescriptorImpl extends FieldDescriptorImpl implements XMLFieldDescriptor { private static final String WILD_CARD = "*"; private static final String NULL_CLASS_ERR = "The 'type' argument passed to the constructor of " + "XMLFieldDescriptorImpl may not be null."; private static final String NULL_NAME_ERR = "The 'fieldName' argument passed to the constructor of " + "XMLFieldDescriptorImpl may not be null."; /** * The index of this field within the constructor arguments. Note: This field is only applicable * if the field is an attribute field and it's supposed to be set via the constructor. A value * less than zero indicates that this field is not part of the constructor arguments. */ private int _argIndex = -1; /** True if the field is a container field. */ private boolean _container = false; /** * Flag to indicate that objects should be added to their as soon as they are created, but before * they are finished being populated. */ private boolean _incremental = false; /** True if the field is a reference to another Object in the hierarchy. */ public boolean _isReference = false; private boolean _isWild = false; /** True if the field type is mapped in a Hashtable or Map. */ private boolean _mapped = false; private String[] _matches = null; /** True if the field is allowed to have nil content. */ private boolean _nillable = false; /** The node type (attribute, element, text). */ private NodeType _nodeType = null; /** The namespace prefix that is to be used when marshaling */ private String _nsPrefix = null; /** The namespace URI used for both marshaling and unmarshaling. */ private String _nsURI = null; /** The "user-set" properties of this XMLFieldDescriptor. */ private Properties _xmlProperties = null; /** The XML Schema type of this field value. */ private String _schemaType = null; /** The XML Schema type of the components for XML schema lists. */ private String _componentType = null; /** The prefix used in case the value of the field described is of type QName. */ private String _qNamePrefix = null; /** A flag which indicates the parent class' namespace should be used by default. */ private boolean _useParentClassNamespace = false; private FieldValidator _validator = null; /** * The local XML name of the field; this does not include any path elements. */ private String _xmlName = null; /** * The relative XML path used when wrapping in nested elements, does not include the name of the * field itself. */ private String _xmlPath = null; private List _substitutes; /** * Indicates whether the field described by this {@link XMLFieldDescriptorImpl} is created as a * result of a definition. */ private boolean _derivedFromXSList; // ----------------/ // - Constructors -/ // ----------------/ public XMLFieldDescriptorImpl(final Class fieldType, final String fieldName, final String xmlName, final NodeType nodeType) { _matches = new String[0]; if (fieldName == null) { throw new IllegalArgumentException(NULL_NAME_ERR); } if (fieldType == null) { throw new IllegalArgumentException(NULL_CLASS_ERR); } setFieldName(fieldName); if (fieldType == org.exolab.castor.types.AnyNode.class) { // if the field type is an AnyNode Castor must treat it as // an object to avoid changes in the marshaling framework setFieldType(java.lang.Object.class); } else { setFieldType(fieldType); } _nodeType = ((nodeType == null) ? NodeType.Attribute : nodeType); // -- call the setXMLName method to handle checking for full path setXMLName(xmlName); } /** * Construct a new field descriptor for the specified field. This is an XML field descriptor * wrapping a field descriptor and adding XML related properties and methods. * * @param fieldDesc The field descriptor * @param xmlName The XML name of the field * @param nodeType The node type of this field * @param primitiveNodeType * @throws MappingException Invalid mapping information */ public XMLFieldDescriptorImpl(final FieldDescriptor fieldDesc, final String xmlName, final NodeType nodeType, final NodeType primitiveNodeType) throws MappingException { _matches = new String[0]; if (fieldDesc instanceof XMLFieldDescriptor) { setContainingClassDescriptor(fieldDesc.getContainingClassDescriptor()); } setFieldName(fieldDesc.getFieldName()); if (fieldDesc.getFieldType() == org.exolab.castor.types.AnyNode.class) { // if the field type is an AnyNode Castor must treat it as // an object to avoid changes in the marshaling framework setFieldType(java.lang.Object.class); } else { setFieldType(fieldDesc.getFieldType()); } ClassDescriptor cd = fieldDesc.getClassDescriptor(); if (cd != null) { if (cd instanceof XMLClassDescriptor) { setClassDescriptor(cd); } else { setClassDescriptor(new XMLClassDescriptorAdapter(cd, null, primitiveNodeType)); } } setHandler(fieldDesc.getHandler()); // Check for instances of java.util.Date. This logic really doesn't belong here, // as it can interfere with user specified handlers. Instead it should go into // XMLMappingLoader. if (getFieldType() != null) { if (java.util.Date.class.isAssignableFrom(getFieldType())) { if (getHandler() instanceof FieldHandlerImpl) { setHandler(new DateFieldHandler(getHandler())); } } } setTransient(fieldDesc.isTransient()); setImmutable(fieldDesc.isImmutable()); setRequired(fieldDesc.isRequired()); setMultivalued(fieldDesc.isMultivalued()); // -- handle xml name if (xmlName == null) { setXMLName(getFieldName()); } else { setXMLName(xmlName); } if (nodeType == null) { if (isMultivalued()) { _nodeType = NodeType.Element; } else { _nodeType = NodeType.Attribute; } } else { _nodeType = nodeType; } if (isRequired()) { _validator = new FieldValidator(); _validator.setMinOccurs(1); _validator.setDescriptor(this); } } /** * Sets whether or not the value of the field represented by this FieldDescriptor should be set * via the constructor of the containing ClassDescriptor. The index value greater than 0 specifies * the index within the argument array that the value of this field should be. *

* Note: This only applies to attribute mapped fields at this time. * * @param index the index within the argument array. A value less than zero indicates that this * field should not be part of the constructor arguments. */ public void setConstructorArgumentIndex(final int index) { if (_nodeType != NodeType.Attribute) { String err = "constructor arguments only valid for attribute mapped fields."; throw new IllegalStateException(err); } _argIndex = index; } /** * @see org.exolab.castor.xml.XMLFieldDescriptor#getConstructorArgumentIndex() {@inheritDoc} */ public int getConstructorArgumentIndex() { return _argIndex; } /** * @see org.exolab.castor.xml.XMLFieldDescriptor#isConstructorArgument() {@inheritDoc} */ public boolean isConstructorArgument() { return (_argIndex >= 0); } /** * Sets the location path for the field being described. *

* In most cases, this isn't needed. However sometimes a field may be mapped to a nested element. * In which case the value of the location path should be the nested element name. If more than * one level of nesting is needed each nested element name should be separated by a path separator * (forward slash '/'). *

* The location path name is "relative" to the parent Class. The name of the parent should not be * included in the path. *

* For example, give the following two classes: * class Root { * Bar bar; * } * * class Bar { * String value; * } * * * And the following XML: * * * <root> * <foo> * <bar> value of bar </bar> * </foo> * </root> * * * Since foo has no associated class, the path for 'bar' would be: "foo" * * @param path the "relative" location path for the field. * @see #getLocationPath */ public void setLocationPath(final String path) { // -- need to add some validation to the path at some point. _xmlPath = path; } /** * @see org.exolab.castor.xml.XMLFieldDescriptor#getLocationPath() {@inheritDoc} */ public String getLocationPath() { return _xmlPath; } /** * Sets the namespace prefix used when marshaling as XML. * * @param nsPrefix The namespace prefix used when marshaling the "described" object. */ public void setNameSpacePrefix(final String nsPrefix) { _nsPrefix = nsPrefix; } /** * @see org.exolab.castor.xml.XMLFieldDescriptor#getNameSpacePrefix() {@inheritDoc} */ public String getNameSpacePrefix() { return _nsPrefix; } /** * Sets whether or not the namespace for the parent "containing" class should be used during * marshaling/unmarshaling when no specific namespace URI has been set for this field. */ public void setUseParentsNamespace(final boolean useParentsNamespace) { _useParentClassNamespace = useParentsNamespace; } /** * Sets the namespace URI used when marshaling and unmarshaling as XML. * * @param nsURI The namespace URI used when marshaling and unmarshaling the "described" Object. */ public void setNameSpaceURI(final String nsURI) { _nsURI = nsURI; } /** * @see org.exolab.castor.xml.XMLFieldDescriptor#getNameSpaceURI() {@inheritDoc} */ public String getNameSpaceURI() { ClassDescriptor parent = getContainingClassDescriptor(); if ((_nsURI == null) && (parent != null) && _useParentClassNamespace) { Class type = getFieldType(); boolean test = !(isAnyNode(type)); if ((_nodeType == NodeType.Element) && test) { if (parent instanceof XMLClassDescriptor) { return ((XMLClassDescriptor) parent).getNameSpaceURI(); } } } return _nsURI; } /** * Sets the XML node type for the described field. * * @param nodeType the NodeType for the described field. */ public void setNodeType(final NodeType nodeType) { _nodeType = ((nodeType == null) ? NodeType.Attribute : nodeType); } /** * @see org.exolab.castor.xml.XMLFieldDescriptor#getNodeType() {@inheritDoc} */ public NodeType getNodeType() { return _nodeType; } /** * Sets the value property with the given name. * * @param propertyName The name of the property to set the value of. * @param value The value of the property. * @see #getProperty */ public void setXMLProperty(final String propertyName, final String value) { if (propertyName == null) { String err = "The argument 'propertyName' must not be null."; throw new IllegalArgumentException(err); } if (_xmlProperties == null) { _xmlProperties = new Properties(); } if (value == null) { _xmlProperties.remove(propertyName); } else { _xmlProperties.put(propertyName, value); } } /** * @see org.exolab.castor.xml.XMLFieldDescriptor#getProperty(java.lang.String) {@inheritDoc} */ public String getXMLProperty(final String propertyName) { if ((_xmlProperties == null) || (propertyName == null)) { return null; } return _xmlProperties.getProperty(propertyName); } /** * Sets the type of the XML Schema type of the value for the field being described. * * @param schemaType The value type. */ public void setSchemaType(final String schemaType) { _schemaType = schemaType; } /** * Sets the type of the XML Schema type of the value for the field being described. * * @param componentType The component type for <xs:list>s. */ public void setComponentType(final String componentType) { _componentType = componentType; } /** * @see org.exolab.castor.xml.XMLFieldDescriptor#getSchemaType() {@inheritDoc} */ public String getSchemaType() { return _schemaType; } /** * {@inheritDoc} * * @see org.exolab.castor.xml.XMLFieldDescriptor#getComponentType() */ public String getComponentType() { return _componentType; } public void setValidator(final FieldValidator validator) { if (_validator != null) { _validator.setDescriptor(null); } _validator = validator; if (_validator != null) { _validator.setDescriptor(this); } } /** * @see org.exolab.castor.xml.XMLFieldDescriptor#getValidator() {@inheritDoc} */ public FieldValidator getValidator() { return _validator; } /** * Sets the xml name for the described field. * * @param xmlName the XML name for the described field. */ public void setXMLName(final String xmlName) { _xmlName = xmlName; } /** * @see org.exolab.castor.xml.XMLFieldDescriptor#getXMLName() {@inheritDoc} */ public String getXMLName() { return _xmlName; } /** * Set if the field is a container field or not. * * @param isContainer a boolean indicating whether or not the field is a container field. */ public void setContainer(final boolean isContainer) { _container = isContainer; } /** * @see org.exolab.castor.xml.XMLFieldDescriptor#isContainer() {@inheritDoc} */ public boolean isContainer() { return _container; } /** * Sets the incremental flag which indicates whether this member can be added before the * unmarshaler is finished unmarshaling it. * * @param incremental the boolean which if true indicated that this member can safely be added * before the unmarshaler is finished unmarshaling it. */ public void setIncremental(final boolean incremental) { _incremental = incremental; } /** * @see org.exolab.castor.xml.XMLFieldDescriptor#isIncremental() {@inheritDoc} */ public boolean isIncremental() { return _incremental; } /** * Sets whether or not this field has been mapped in a Map or Hashtable. * * @param mapped a boolean that when true indicates this field is a Hashtable or Map. */ public void setMapped(final boolean mapped) { _mapped = mapped; } /** * @see org.exolab.castor.xml.XMLFieldDescriptor#isMapped() {@inheritDoc} */ public boolean isMapped() { return _mapped; } /** * Sets whether or not the described field is allowed to be nil. A nillable field can have empty * content (text or element content), but may have attribute values, and still be considered * value, even if the child elements are required. * * @param nillable a boolean indicating whether or not the described field may be nillable. */ public void setNillable(final boolean nillable) { _nillable = nillable; } /** * @see org.exolab.castor.xml.XMLFieldDescriptor#isNillable() {@inheritDoc} */ public boolean isNillable() { return _nillable; } /** * Sets the flag indicating that the field described by this descriptor is a reference to another * field in the object model. * * @param isReference true if the field is a reference to another field. */ public void setReference(final boolean isReference) { _isReference = isReference; } /** * @see org.exolab.castor.xml.XMLFieldDescriptor#isReference() {@inheritDoc} */ public boolean isReference() { return _isReference; } /** * Sets the prefix used in case the value of the field described by this descriptor is of type * QName. * * @param qNamePrefix */ public void setQNamePrefix(final String qNamePrefix) { _qNamePrefix = qNamePrefix; } /** * Returns the prefix used in case the value of the field described by this descriptor is of type * QName. This is helpful for the marshaler but not mandatory. * * @return the prefix used in the QName value. */ public String getQNamePrefix() { return _qNamePrefix; } /** * This is a space separated list of xml names that this Field descriptor matches. A '*' is wild. * * @param matchExpr the space separated list of xml names, matched by this descriptor. */ public void setMatches(String matchExpr) { _isWild = false; if ((matchExpr == null) || (matchExpr.length() == 0)) { return; } StringTokenizer st = new StringTokenizer(matchExpr); List names = new ArrayList(); while (st.hasMoreTokens()) { String token = st.nextToken(); if (WILD_CARD.equals(token)) { _isWild = true; break; } names.add(token); } _matches = new String[names.size()]; names.toArray(_matches); } /** * @see org.exolab.castor.xml.XMLFieldDescriptor#matches(java.lang.String) {@inheritDoc} */ public boolean matches(final String xmlName) { if (xmlName != null) { if (_isWild) { return true; } else if (_matches.length > 0) { for (int i = 0; i < _matches.length; i++) { if (xmlName.equals(_matches[i])) { return true; } } } else { return xmlName.equals(_xmlName); } } return false; } /** * @see org.exolab.castor.xml.XMLFieldDescriptor#matches(java.lang.String, java.lang.String) * {@inheritDoc} */ public boolean matches(final String xmlName, final String namespace) { // compare namespaces if (namespace == null) { if ((_nsURI != null) && (_nsURI.length() > 0)) { return false; } } else if (_nsURI == null) { if ((namespace.length() > 0) && (!_isWild)) { return false; } } else if (!_nsURI.equals(namespace)) { return false; } // if we make this far the namespaces match, now compare names return matches(xmlName); } /** * Returns true if two XMLFieldDescriptors should be treated as equal. Any XMLFieldDescriptor that * handles the same field is considered equal. * * @param obj The object to compare to this * * @return true if two XMLFieldDescriptors should be treated as equal. */ public boolean equals(final Object obj) { if (obj == this) { return true; } if ((obj == null) || (!(obj instanceof XMLFieldDescriptor))) { return false; } XMLFieldDescriptor descriptor = (XMLFieldDescriptor) obj; // check field names if (!getFieldName().equals(descriptor.getFieldName())) { return false; } // check field types if (!getFieldType().equals(descriptor.getFieldType())) { return false; } // check field handler FieldHandler tmpHandler = descriptor.getHandler(); if (getHandler() == null) { return (tmpHandler == null); } else if (tmpHandler == null) { return false; } // The following line causes some issues when used against a FieldHandlerImpl // because the equals method for FieldHandlerImpl is the default. Temporarily // replace it with a slightly more generic comparison but this should probably // change in the future. (kv) // return (_handler.equals(tmpHandler)); return (getHandler().getClass().isInstance(tmpHandler)); } /** * Returns the hashCode for this XMLFieldDescriptor * * @return the hashCode for this XMLFieldDescriptor */ public int hashCode() { int hash = 17; hash = 17 * getFieldName().hashCode(); hash = hash * 17 * getFieldType().hashCode(); if (getHandler() != null) { hash = hash * 17 * getHandler().hashCode(); } return hash; } public String toString() { StringBuilder buffer = new StringBuilder(32).append("XMLFieldDesciptor: ") .append(getFieldName()).append(" AS ").append(_xmlName); if (getNameSpaceURI() != null) { buffer.append('{').append(getNameSpaceURI()).append('}'); } return buffer.toString(); } /** * Returns true if the given class should be treated as a primitive type. This method will return * true for all Java primitive types, the set of primitive object wrappers, as well as Strings. * * @return true If the given class should be treated as a primitive type. */ private static boolean isPrimitive(final Class type) { if (type == null) { return false; } if (type.isPrimitive()) { return true; } if (type == String.class) { return true; } if ((type == Boolean.class) || (type == Character.class)) { return true; } // Any class which extends Number should be treated as a primitive. return (type.getSuperclass() == Number.class); } /** * Return true if the given class is a "built-in" type. A built-in type is one in which Castor * provides the default descriptor for. * * @param type The class to check. * @return true If the given class is a built-in type. */ private static boolean isBuiltInType(final Class type) { if (type == null) { return false; } // -- All built-in Java types, such as java.util.Date, // -- java.sql.Date, various Collection classes, etc. return (CoreDescriptors.getDescriptor(type) != null); } private static boolean isMappedItem(final Class fieldType) { return (fieldType == MapItem.class); } private static boolean isAnyNode(final Class type) { return (type == Object.class); } /** * Returns the possible substitution groups for this class. * * @return the possible substitution groups for this class. */ public List getSubstitutes() { return _substitutes; } /** * Sets the possible substitution groups for this class. * * @param substitutes Possible substitution groups for this class. */ public void setSubstitutes(List substitutes) { _substitutes = substitutes; } /** * {@inheritDoc} * * @see org.exolab.castor.xml.XMLFieldDescriptor#setDerivedFromXSList(boolean) */ public void setDerivedFromXSList(final boolean derivedFromXSList) { _derivedFromXSList = derivedFromXSList; } /** * {@inheritDoc} * * @see org.exolab.castor.xml.XMLFieldDescriptor#isDerivedFromXSList() */ public boolean isDerivedFromXSList() { return _derivedFromXSList; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy