/*
* The Apache Software License, Version 1.1
*
*
* Copyright (c) 2002 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.apache.axis.description;
import org.apache.axis.utils.ClassUtils;
import org.apache.axis.utils.JavaUtils;
import org.apache.axis.utils.BeanUtils;
import org.apache.axis.utils.BeanPropertyDescriptor;
import org.apache.axis.components.logger.LogFactory;
import org.apache.commons.logging.Log;
import javax.xml.namespace.QName;
import java.lang.reflect.Method;
import java.util.HashMap;
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 {
public static final Class [] noClasses = new Class [] {};
public static final Object[] noObjects = new Object[] {};
/** Have we already introspected for the special "any" property desc? */
private boolean lookedForAny = false;
public TypeDesc(Class javaClass) {
this.javaClass = javaClass;
}
/**
* Static function for centralizing access to type metadata for a
* given class.
*
* 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).
*
* (Could introduce a cache here for speed as an optimization)
*/
public static TypeDesc getTypeDescForClass(Class cls)
{
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;
}
/** The Java class for this type */
private Class javaClass = null;
/** The XML type QName for this type */
private QName xmlType = null;
/** 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 = null;
/** Are there any fields which are serialized as attributes? */
private boolean _hasAttributes = false;
/** Introspected property descriptors */
private BeanPropertyDescriptor[] propertyDescriptors = null;
/** Map with key = property descriptor name, value = descriptor */
private Map propertyMap = null;
/**
* Indication if this type has support for xsd:any.
*/
private BeanPropertyDescriptor anyDesc = 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);
FieldDesc [] ret = new FieldDesc[parentFields.length + fields.length];
System.arraycopy(fields, 0, ret, 0, fields.length);
System.arraycopy(parentFields, 0, ret, fields.length, parentFields.length);
}
}
}
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(
JavaUtils.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.
*
* If the "ignoreNS" argument is true, just compare localNames.
*/
public String getFieldNameForElement(QName qname, boolean ignoreNS)
{
if (fields == null)
return null;
// 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; i < fields.length; i++) {
FieldDesc field = fields[i];
if (field.isElement()) {
QName xmlName = field.getXmlName();
if (localPart.equals(xmlName.getLocalPart())) {
if (ignoreNS || qname.getNamespaceURI().
equals(xmlName.getNamespaceURI())) {
result = field.getFieldName();
}
}
}
}
// 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, ignoreNS);
}
}
}
// 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)
{
if (fields == null)
return null;
String possibleMatch = null;
for (int i = 0; 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;
}
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 descriptor = propertyDescriptors[i];
propertyMap.put(descriptor.getName(), descriptor);
}
return propertyMap;
}
}