| TypeDesc.java |
/*
* The Apache Software License, Version 1.1
*
*
* Copyright (c) 2002-2003 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 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 end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Axis" and "Apache Software Foundation" must
* not be used to endorse or promote products derived from this
* software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache",
* nor may "Apache" appear in their name, without prior written
* permission of the Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``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 THE APACHE SOFTWARE FOUNDATION 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*/
package org.jboss.axis.description;
import org.jboss.axis.utils.BeanPropertyDescriptor;
import org.jboss.axis.utils.BeanUtils;
import org.jboss.axis.utils.ClassUtils;
import org.jboss.axis.utils.Messages;
import org.jboss.logging.Logger;
import javax.xml.namespace.QName;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Map;
/**
* A TypeDesc represents a Java<->XML data binding. It is essentially
* a collection of FieldDescs describing how to map each field in a Java
* class to XML.
*
* @author Glen Daniels (gdaniels@apache.org)
*/
public class TypeDesc
{
// provide logging
private final Logger log = Logger.getLogger(TypeDesc.class);
public static final Class[] noClasses = new Class[]{};
public static final Object[] noObjects = new Object[]{};
/**
* A map of class -> TypeDesc
*/
private static Map classMap = new Hashtable();
/**
* Have we already introspected for the special "any" property desc?
*/
private boolean lookedForAny;
/**
* The Java class for this type
*/
private Class javaClass;
/**
* The XML type QName for this type
*/
private QName xmlType;
/**
* The various fields in here
*/
private FieldDesc[] fields;
/**
* A cache of FieldDescs by name
*/
private HashMap fieldNameMap = new HashMap();
/**
* A cache of FieldDescs by Element QName
*/
private HashMap fieldElementMap;
/**
* Are there any fields which are serialized as attributes?
*/
private boolean hasAttributes;
/**
* Introspected property descriptors
*/
private BeanPropertyDescriptor[] propertyDescriptors;
/**
* Map with key = property descriptor name, value = descriptor
*/
private Map propertyMap;
/**
* Indication if this type has support for xsd:any.
*/
private BeanPropertyDescriptor anyDesc;
public TypeDesc(Class javaClass)
{
this.javaClass = javaClass;
}
/**
* Static function to explicitly register a type description for
* a given class.
*
* @param cls the Class we're registering metadata about
* @param td the TypeDesc containing the metadata
*/
public static void registerTypeDescForClass(Class cls, TypeDesc td)
{
classMap.put(cls, td);
}
/**
* Static function for centralizing access to type metadata for a
* given class.
* <p/>
* This checks for a static getTypeDesc() method on the
* class or _Helper class.
* Eventually we may extend this to provide for external
* metadata config (via files sitting in the classpath, etc).
* <p/>
* (Could introduce a cache here for speed as an optimization)
*/
public static TypeDesc getTypeDescForClass(Class cls)
{
// First see if we have one explicitly registered
TypeDesc result = (TypeDesc)classMap.get(cls);
if (result != null)
{
return result;
}
try
{
Method getTypeDesc = null;
try
{
getTypeDesc =
cls.getMethod("getTypeDesc", noClasses);
}
catch (NoSuchMethodException e)
{
}
if (getTypeDesc == null)
{
// Look for a Helper Class
Class helper = ClassUtils.forName(cls.getName() + "_Helper");
try
{
getTypeDesc =
helper.getMethod("getTypeDesc", noClasses);
}
catch (NoSuchMethodException e)
{
}
}
if (getTypeDesc != null)
{
return (TypeDesc)getTypeDesc.invoke(null,
noObjects);
}
}
catch (Exception e)
{
}
return null;
}
public BeanPropertyDescriptor getAnyDesc()
{
return anyDesc;
}
/**
* Obtain the current array of FieldDescs
*/
public FieldDesc[] getFields()
{
return fields;
}
public FieldDesc[] getFields(boolean searchParents)
{
if (searchParents)
{
// check superclasses if they exist
Class cls = javaClass.getSuperclass();
if (cls != null && !cls.getName().startsWith("java."))
{
TypeDesc superDesc = getTypeDescForClass(cls);
if (superDesc != null)
{
FieldDesc[] parentFields = superDesc.getFields(true);
// START FIX http://nagoya.apache.org/bugzilla/show_bug.cgi?id=17188
if (parentFields != null)
{
if (fields != null)
{
FieldDesc[] ret = new FieldDesc[parentFields.length + fields.length];
System.arraycopy(parentFields, 0, ret, 0, parentFields.length);
System.arraycopy(fields, 0, ret, parentFields.length, fields.length);
fields = ret;
}
else
{
FieldDesc[] ret = new FieldDesc[parentFields.length];
System.arraycopy(parentFields, 0, ret, 0, parentFields.length);
fields = ret;
}
}
// END FIX http://nagoya.apache.org/bugzilla/show_bug.cgi?id=17188
}
}
}
return fields;
}
/**
* Replace the array of FieldDescs, making sure we keep our convenience
* caches in sync.
*/
public void setFields(FieldDesc[] newFields)
{
fieldNameMap = new HashMap();
fields = newFields;
hasAttributes = false;
fieldElementMap = null;
for (int i = 0; i < newFields.length; i++)
{
FieldDesc field = newFields[i];
if (field.isElement())
{
fieldNameMap.put(field.getFieldName(), field);
}
else
{
hasAttributes = true;
}
}
}
/**
* Add a new FieldDesc, keeping the convenience fields in sync.
*/
public void addFieldDesc(FieldDesc field)
{
if (field == null)
{
throw new IllegalArgumentException(Messages.getMessage("nullFieldDesc"));
}
int numFields = 0;
if (fields != null)
{
numFields = fields.length;
}
FieldDesc[] newFields = new FieldDesc[numFields + 1];
if (fields != null)
{
System.arraycopy(fields, 0, newFields, 0, numFields);
}
newFields[numFields] = field;
fields = newFields;
// Keep track of the field by name for fast lookup
fieldNameMap.put(field.getFieldName(), field);
if (!hasAttributes && !field.isElement())
hasAttributes = true;
}
/**
* Get the QName associated with this field, but only if it's
* marked as an element.
*/
public QName getElementNameForField(String fieldName)
{
FieldDesc desc = (FieldDesc)fieldNameMap.get(fieldName);
if (desc == null)
{
// check superclasses if they exist
Class cls = javaClass.getSuperclass();
if (cls != null && !cls.getName().startsWith("java."))
{
TypeDesc superDesc = getTypeDescForClass(cls);
if (superDesc != null)
{
return superDesc.getElementNameForField(fieldName);
}
}
}
else if (!desc.isElement())
{
return null;
}
return desc.getXmlName();
}
/**
* Get the QName associated with this field, but only if it's
* marked as an attribute.
*/
public QName getAttributeNameForField(String fieldName)
{
FieldDesc desc = (FieldDesc)fieldNameMap.get(fieldName);
if (desc == null)
{
// check superclasses if they exist
Class cls = javaClass.getSuperclass();
if (cls != null && !cls.getName().startsWith("java."))
{
TypeDesc superDesc = getTypeDescForClass(cls);
if (superDesc != null)
{
return superDesc.getAttributeNameForField(fieldName);
}
}
}
else if (desc.isElement())
{
return null;
}
QName ret = desc.getXmlName();
if (ret == null)
{
ret = new QName("", fieldName);
}
return ret;
}
/**
* Get the field name associated with this QName, but only if it's
* marked as an element.
*/
public String getFieldNameForElement(QName qname)
{
// have we already computed the answer to this question?
if (fieldElementMap != null)
{
String cached = (String)fieldElementMap.get(qname);
if (cached != null) return cached;
}
String result = null;
String localPart = qname.getLocalPart();
// check fields in this class
for (int i = 0; fields != null && i < fields.length; i++)
{
FieldDesc field = fields[i];
if (field.isElement())
{
QName xmlName = field.getXmlName();
if (localPart.equals(xmlName.getLocalPart()))
{
if (qname.getNamespaceURI().equals(xmlName.getNamespaceURI()))
{
result = field.getFieldName();
break;
}
}
}
}
// check superclasses if they exist
if (result == null)
{
Class cls = javaClass.getSuperclass();
if (cls != null && !cls.getName().startsWith("java."))
{
TypeDesc superDesc = getTypeDescForClass(cls);
if (superDesc != null)
{
result = superDesc.getFieldNameForElement(qname);
}
}
}
// cache the answer away for quicker retrieval next time.
if (result != null)
{
if (fieldElementMap == null) fieldElementMap = new HashMap();
fieldElementMap.put(qname, result);
}
return result;
}
/**
* Get the field name associated with this QName, but only if it's
* marked as an attribute.
*/
public String getFieldNameForAttribute(QName qname)
{
String possibleMatch = null;
for (int i = 0; fields != null && i < fields.length; i++)
{
FieldDesc field = fields[i];
if (!field.isElement())
{
// It's an attribute, so if we have a solid match, return
// its name.
if (qname.equals(field.getXmlName()))
{
return field.getFieldName();
}
// Not a solid match, but it's still possible we might match
// the default (i.e. QName("", fieldName))
if (qname.getNamespaceURI().equals("") &&
qname.getLocalPart().equals(field.getFieldName()))
{
possibleMatch = field.getFieldName();
}
}
}
if (possibleMatch == null)
{
// check superclasses if they exist
Class cls = javaClass.getSuperclass();
if (cls != null && !cls.getName().startsWith("java."))
{
TypeDesc superDesc = getTypeDescForClass(cls);
if (superDesc != null)
{
possibleMatch = superDesc.getFieldNameForAttribute(qname);
}
}
}
return possibleMatch;
}
/**
* Get a FieldDesc by field name.
*/
public FieldDesc getFieldByName(String name)
{
FieldDesc ret = (FieldDesc)fieldNameMap.get(name);
if (ret == null)
{
Class cls = javaClass.getSuperclass();
if (cls != null && !cls.getName().startsWith("java."))
{
TypeDesc superDesc = getTypeDescForClass(cls);
if (superDesc != null)
{
ret = superDesc.getFieldByName(name);
}
}
}
return ret;
}
/**
* Do we have any FieldDescs marked as attributes?
*/
public boolean hasAttributes()
{
return hasAttributes;
}
public QName getXmlType()
{
return xmlType;
}
public void setXmlType(QName xmlType)
{
this.xmlType = xmlType;
}
/**
* Get/Cache the property descriptors
*
* @return PropertyDescriptor
*/
public BeanPropertyDescriptor[] getPropertyDescriptors()
{
// Return the propertyDescriptors if already set.
// If not set, use BeanUtils.getPd to get the property descriptions.
//
// Since javaClass is a generated class, there
// may be a faster way to set the property descriptions than
// using BeanUtils.getPd. But for now calling getPd is sufficient.
if (propertyDescriptors == null)
{
propertyDescriptors = BeanUtils.getPd(javaClass, this);
if (!lookedForAny)
{
anyDesc = BeanUtils.getAnyContentPD(javaClass);
lookedForAny = true;
}
}
return propertyDescriptors;
}
/**
* Set the property descriptors for this type, for example in a different order
*/
public void setPropertyDescriptors(BeanPropertyDescriptor[] propertyDescriptors)
{
this.propertyDescriptors = propertyDescriptors;
this.propertyMap = null;
}
public BeanPropertyDescriptor getAnyContentDescriptor()
{
if (!lookedForAny)
{
anyDesc = BeanUtils.getAnyContentPD(javaClass);
lookedForAny = true;
}
return anyDesc;
}
/**
* Get/Cache the property descriptor map
*
* @return Map with key=propertyName, value=descriptor
*/
public Map getPropertyDescriptorMap()
{
// Return map if already set.
if (propertyMap != null)
{
return propertyMap;
}
// Make sure properties exist
if (propertyDescriptors == null)
{
getPropertyDescriptors();
}
// Build the map
propertyMap = new HashMap();
for (int i = 0; i < propertyDescriptors.length; i++)
{
BeanPropertyDescriptor bpd = propertyDescriptors[i];
String bpName = bpd.getName();
if ("class".equals(bpName) == false)
{
// TDI 13-Feb-2005 (hack allert)
// Seen when running samples2docclient
// fieldName == Person_2
// bpName == person_2
if (fieldNameMap.keySet().contains(bpName) == false)
{
log.debug("Cannot find field name '" + bpName + "' in: " + fieldNameMap.keySet());
Iterator it = fieldNameMap.keySet().iterator();
while (it.hasNext())
{
String fieldName = (String)it.next();
if (fieldName.equalsIgnoreCase(bpName))
{
log.debug("Additionaly map BeanPropertyDescriptor to: '" + fieldName + "'");
propertyMap.put(fieldName, bpd);
break;
}
}
}
}
propertyMap.put(bpName, bpd);
}
return propertyMap;
}
}
| TypeDesc.java |