com.zimbra.soap.util.JaxbInfo.java Source code

Java tutorial

Introduction

Here is the source code for com.zimbra.soap.util.JaxbInfo.java

Source

/*
 * ***** BEGIN LICENSE BLOCK *****
 * Zimbra Collaboration Suite Server
 * Copyright (C) 2011, 2012, 2013, 2014, 2016 Synacor, Inc.
 *
 * This program is free software: you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free Software Foundation,
 * version 2 of the License.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU General Public License for more details.
 * You should have received a copy of the GNU General Public License along with this program.
 * If not, see <https://www.gnu.org/licenses/>.
 * ***** END LICENSE BLOCK *****
 */

package com.zimbra.soap.util;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.Collection;
import java.util.List;
import java.util.Map;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementRef;
import javax.xml.bind.annotation.XmlElementRefs;
import javax.xml.bind.annotation.XmlElementWrapper;
import javax.xml.bind.annotation.XmlElements;
import javax.xml.bind.annotation.XmlNsForm;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlSchema;
import javax.xml.bind.annotation.XmlTransient;
import javax.xml.bind.annotation.XmlType;
import javax.xml.bind.annotation.XmlValue;

import com.google.common.base.Strings;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.zimbra.common.util.Log;
import com.zimbra.common.util.ZimbraLog;
import com.zimbra.soap.json.jackson.annotate.ZimbraKeyValuePairs;

/**
 * Zimbra SOAP interfaces are more flexible than Jaxb in what is acceptable.
 * In particular, attributes can be represented as elements.
 * This class provides a limited amount of Jaxb information for a class to
 * aid transforming Zimbra acceptable Xml into Jaxb acceptable Xml.
 *
 * @author gren
 *
 * Treatment of annotations:
 *     XmlAccessType      : Supported
 *     XmlAccessorType    : Supported
 *     XmlAnyAttribute    : Ignored
 *     XmlAnyElement      : Ignored
 *     XmlAttribute       : Supported
 *     XmlElement         : Supported
 *     XmlElementRef      : Supported
 *     XmlElementRefs     : Supported
 *     XmlElementWrapper  : Supported
 *     XmlElements        : Supported
 *     XmlEnum            : Ignored
 *     XmlEnumValue       : Ignored
 *     XmlMixed           : Ignored
 *     XmlRootElement     : Supported
 *     XmlSeeAlso         : Not supported - useful for finding subclasses
 *     XmlTransient       : Supported
 *     XmlType            : Useful for propOrder
 *     XmlValue           : Ignored
 */

public final class JaxbInfo {

    private static final Log LOG = ZimbraLog.soap;
    // Various annotation classes use this
    public static final String DEFAULT_MARKER = "##default";

    private Class<?> jaxbClass = null;
    private String stamp = null;
    private String rootElementName = null;
    private String rootElementNamespace = null;
    private XmlType xmlType = null;

    /**
     * names of known attributes that can be associated with the element
     */
    private final List<String> attributeNames = Lists.newArrayList();

    public static final Map<String, JaxbInfo> CACHE = Maps.newHashMap();

    /* excludes attributes represented in super classes */
    private final List<JaxbAttributeInfo> attrInfo = Lists.newArrayList();

    /* Note: There isn't quite a one-to-one correspondence between elements in this array and XML sub-elements
     * as "pseudo" parent entries are added to collect together groups of elements where they are in an
     * choice group (These pseudo parents represent JAXB XmlElements or XmlElementRefs)
     * Also excludes elements represented in super classes
     */
    private final List<JaxbNodeInfo> jaxbElemNodeInfo = Lists.newArrayList();
    private JaxbValueInfo elementValue = null;
    private boolean haveKvpXmlInfo = false;
    private KeyValuePairXmlRepresentationInfo kvpXmlInfo;

    /**
     * @param klass is a JAXB annotated class associated with a particular element
     */
    private JaxbInfo(Class<?> klass) {
        this.jaxbClass = klass;
        if (klass == null) {
            LOG.error("null class provided to JaxbInfo constructor");
            return;
        }
        stamp = "JaxbInfo class=" + this.jaxbClass.getName() + ":";
        gatherInfo();
        synchronized (CACHE) {
            CACHE.put(klass.getName(), this);
        }
    }

    public static JaxbInfo getFromCache(Class<?> klass) {
        if (klass == null || klass.isPrimitive()) {
            return null;
        }
        JaxbInfo jbi = null;
        synchronized (CACHE) {
            jbi = CACHE.get(klass.getName());
        }
        if (jbi == null) {
            jbi = new JaxbInfo(klass);
        }
        return jbi;
    }

    public static void clearCache() {
        synchronized (CACHE) {
            CACHE.clear();
        }
    }

    private JaxbInfo getSuperClassInfo() {
        Class<?> superClass = jaxbClass.getSuperclass();
        if (superClass == null) {
            return null;
        }
        JaxbInfo encJaxbInfo = JaxbInfo.getFromCache(superClass);
        if (encJaxbInfo == null) {
            encJaxbInfo = new JaxbInfo(superClass);
        }
        return encJaxbInfo;
    }

    public Iterable<String> getAttributeNames() {
        List<String> allNames = Lists.newArrayList();
        Iterables.addAll(allNames, this.attributeNames);
        JaxbInfo encClassInfo = getSuperClassInfo();
        if (encClassInfo != null) {
            Iterables.addAll(allNames, encClassInfo.getAttributeNames());
        }
        return allNames;
    }

    public Iterable<JaxbAttributeInfo> getAttributes() {
        List<JaxbAttributeInfo> attrs = Lists.newArrayList();
        Iterables.addAll(attrs, this.attrInfo);
        JaxbInfo encClassInfo = getSuperClassInfo();
        if (encClassInfo != null) {
            Iterables.addAll(attrs, encClassInfo.getAttributes());
        }
        return attrs;
    }

    /**
     * Get XML names of all possible sub-elements (including those described in superclasses)
     */
    public Iterable<String> getElementNames() {
        List<String> elemNames = Lists.newArrayList();
        for (JaxbNodeInfo node : jaxbElemNodeInfo) {
            if (node instanceof JaxbPseudoNodeChoiceInfo) {
                JaxbPseudoNodeChoiceInfo pseudoNode = (JaxbPseudoNodeChoiceInfo) node;
                Iterables.addAll(elemNames, pseudoNode.getElementNames());
            } else {
                elemNames.add(node.getName());
            }
        }
        JaxbInfo encClassInfo = getSuperClassInfo();
        if (encClassInfo != null) {
            Iterables.addAll(elemNames, encClassInfo.getElementNames());
        }
        return elemNames;
    }

    /**
     * Get information relating to sub-elements (including those described in superclasses)
     */
    public Iterable<JaxbNodeInfo> getJaxbNodeInfos() {
        List<JaxbNodeInfo> elems = Lists.newArrayList();
        Iterables.addAll(elems, this.jaxbElemNodeInfo);
        JaxbInfo encClassInfo = getSuperClassInfo();
        if (encClassInfo != null) {
            Iterables.addAll(elems, encClassInfo.getJaxbNodeInfos());
        }
        return elems;
    }

    public boolean hasElement(String name) {
        return (getElemNodeInfo(name) != null);
    }

    public WrappedElementInfo getWrapperInfo(String name) {
        for (JaxbNodeInfo node : jaxbElemNodeInfo) {
            if ((node instanceof WrappedElementInfo) && (name.equals(node.getName()))) {
                return (WrappedElementInfo) node;
            }
        }
        JaxbInfo encClassInfo = getSuperClassInfo();
        if (encClassInfo != null) {
            return encClassInfo.getWrapperInfo(name);
        }
        return null;
    }

    public boolean hasWrapperElement(String name) {
        return (getWrapperInfo(name) != null);
    }

    public Iterable<String> getWrappedSubElementNames(String wrapperName) {
        WrappedElementInfo info = getWrapperInfo(wrapperName);
        if (info != null) {
            return info.getElementNames();
        }
        return null;
    }

    public Class<?> getClassForWrappedElement(String wrapperName, String elementName) {
        WrappedElementInfo info = getWrapperInfo(wrapperName);
        if (info != null) {
            Class<?> wKlass = info.getClassForElementName(elementName);
            if (wKlass == null) {
                LOG.debug("%s No class for wrapper element=%s sub-element=%s", stamp, wrapperName, elementName);
            }
            return wKlass;
        }
        LOG.debug("%s Unknown wrapper element=%s (looking for sub-element=%s)", stamp, wrapperName, elementName);
        return null;
    }

    public boolean hasAttribute(String name) {
        return Iterables.contains(this.getAttributeNames(), name);
    }

    public List<String> getPropOrder() {
        List<String> propOrder = Lists.newArrayList();
        if ((null == xmlType) || (null == xmlType.propOrder()))
            return propOrder;
        for (String prop : xmlType.propOrder()) {
            propOrder.add(prop);
        }
        return propOrder;
    }

    private List<List<org.dom4j.QName>> getNameOrderFromElems() {
        List<List<org.dom4j.QName>> nameOrder = Lists.newArrayList();
        for (JaxbNodeInfo node : jaxbElemNodeInfo) {
            if (node instanceof JaxbPseudoNodeChoiceInfo) {
                JaxbPseudoNodeChoiceInfo choiceNode = (JaxbPseudoNodeChoiceInfo) node;
                List<org.dom4j.QName> names = Lists.newArrayList();
                for (JaxbNodeInfo choiceSub : choiceNode.getElements()) {
                    if (choiceSub instanceof JaxbElementInfo) {
                        JaxbElementInfo jei = (JaxbElementInfo) choiceSub;
                        names.add(getQName(jei));
                    }
                }
                nameOrder.add(names);
            } else if (node instanceof JaxbElementInfo) {
                nameOrder.add(Lists.newArrayList(getQName(node)));
            } else if (node instanceof WrappedElementInfo) {
                nameOrder.add(Lists.newArrayList(getQName(node)));
            }
        }
        return nameOrder;
    }

    public List<List<org.dom4j.QName>> getElementNameOrder() {
        List<List<org.dom4j.QName>> nameOrder = Lists.newArrayList();
        JaxbInfo encClassInfo = getSuperClassInfo();
        if (encClassInfo != null) {
            nameOrder.addAll(encClassInfo.getElementNameOrder());
        }
        List<String> propOrder = getPropOrder();
        if (propOrder.isEmpty()) {
            if (!nameOrder.isEmpty()) {
                // Super class specifies order, forcing it to be required.  Add names in order
                nameOrder.addAll(getNameOrderFromElems());
            }
            return nameOrder;
        }
        for (String fieldName : propOrder) {
            List<org.dom4j.QName> names = Lists.newArrayList();
            for (JaxbNodeInfo node : jaxbElemNodeInfo) {
                if (node instanceof JaxbPseudoNodeChoiceInfo) {
                    JaxbPseudoNodeChoiceInfo choiceNode = (JaxbPseudoNodeChoiceInfo) node;
                    if (fieldName.equals(choiceNode.getFieldName())) {
                        for (JaxbNodeInfo choiceSub : choiceNode.getElements()) {
                            if (choiceSub instanceof JaxbElementInfo) {
                                JaxbElementInfo jei = (JaxbElementInfo) choiceSub;
                                names.add(getQName(jei));
                            }
                        }
                    }
                } else if (node instanceof JaxbElementInfo) {
                    JaxbElementInfo elemNode = (JaxbElementInfo) node;
                    if (fieldName.equals(elemNode.getFieldName())) {
                        names.add(getQName(elemNode));
                    }
                } else if (node instanceof WrappedElementInfo) {
                    WrappedElementInfo wNode = (WrappedElementInfo) node;
                    if (fieldName.equals(wNode.getFieldName())) {
                        names.add(getQName(wNode));
                    }
                }
            }
            if (!names.isEmpty()) {
                nameOrder.add(names);
            }
        }
        return nameOrder;
    }

    private org.dom4j.QName getQName(JaxbNodeInfo elemNode) {
        String elemNs = elemNode.getNamespace();
        if (DEFAULT_MARKER.equals(elemNs)) {
            XmlSchema xmlSchemaAnnot = jaxbClass.getPackage().getAnnotation(XmlSchema.class);
            elemNs = XmlNsForm.QUALIFIED.equals(xmlSchemaAnnot.elementFormDefault()) ? xmlSchemaAnnot.namespace()
                    : "";
        }
        org.dom4j.Namespace dom4jNS = new org.dom4j.Namespace("", elemNs);
        return new org.dom4j.QName(elemNode.getName(), dom4jNS);
    }

    public static String getRootElementName(Class<?> kls) {
        String name;
        if (kls == null) {
            return null;
        }
        XmlRootElement re = kls.getAnnotation(XmlRootElement.class);
        if (re != null) {
            name = re.name();
        } else {
            name = kls.getName();
            int lastDot = name.lastIndexOf('.');
            if (lastDot >= 0) {
                name = name.substring(lastDot + 1);
            }
        }
        return name;
    }

    public String getRootElementName() {
        if (rootElementName == null) {
            rootElementName = getRootElementName(jaxbClass);
        }
        return rootElementName;
    }

    public final class KeyValuePairXmlRepresentationInfo {
        private final String xmlElementName;
        private final String xmlAttributeName;

        public KeyValuePairXmlRepresentationInfo(String elemName, String attrName) {
            xmlElementName = elemName;
            xmlAttributeName = attrName;
        }

        public String getXmlElementName() {
            return xmlElementName;
        }

        public String getXmlAttributeName() {
            return xmlAttributeName;
        }
    }

    /**
     * If this object has keyvaluepairs for children, this returns information about them, otherwise returns null.
     * Note implicit assumption that there can only be one set of keyvaluepairs - this is because the JSON
     * representation would be unable to differentiate between them.
     */
    public KeyValuePairXmlRepresentationInfo getKeyValuePairElementInfo() {
        if (haveKvpXmlInfo) {
            return kvpXmlInfo;
        }
        String elemName = null;
        String attrName = null;
        Field fields[] = jaxbClass.getDeclaredFields();
        for (Field field : fields) {
            ZimbraKeyValuePairs annot = field.getAnnotation(ZimbraKeyValuePairs.class);
            if (annot == null) {
                continue;
            }
            XmlElement xmlElemAnnot = field.getAnnotation(XmlElement.class);
            if (xmlElemAnnot != null) {
                elemName = xmlElemAnnot.name();
            } else {
                elemName = field.getName();
            }
        }
        if (elemName != null) {
            Method methods[] = jaxbClass.getDeclaredMethods();
            for (Method method : methods) {
                ZimbraKeyValuePairs annot = method.getAnnotation(ZimbraKeyValuePairs.class);
                if (annot == null) {
                    continue;
                }
                XmlElement xmlElemAnnot = method.getAnnotation(XmlElement.class);
                if (xmlElemAnnot != null) {
                    elemName = xmlElemAnnot.name();
                } else {
                    elemName = method.getName();
                }
            }
        }
        if (elemName != null) {
            Class<?> kvpElemClass = this.getClassForElement(elemName);
            if (kvpElemClass != null) {
                JaxbInfo kvpJaxbInfo = JaxbInfo.getFromCache(kvpElemClass);
                if (kvpJaxbInfo != null) {
                    Iterable<String> attribNames = kvpJaxbInfo.getAttributeNames();
                    if (attribNames != null) {
                        for (String attribName : attribNames) { // Should only be one...
                            attrName = attribName;
                            break;
                        }
                    }
                }
            }
        }
        if ((elemName != null) && (attrName != null)) {
            kvpXmlInfo = new KeyValuePairXmlRepresentationInfo(elemName, attrName);
        } else {
            kvpXmlInfo = null;
        }
        haveKvpXmlInfo = true;
        return kvpXmlInfo;
    }

    public static String getRootElementNamespace(Class<?> kls) {
        String namespace = null;
        if (kls == null) {
            return null;
        }
        XmlRootElement re = kls.getAnnotation(XmlRootElement.class);
        if (re != null) {
            namespace = re.namespace();
        }
        return namespace;
    }

    public String getRootElementNameSpace() {
        if (rootElementNamespace == null) {
            rootElementNamespace = getRootElementName(jaxbClass);
        }
        return rootElementNamespace;
    }

    public Class<?> getClassForElement(String name) {
        JaxbNodeInfo node = getElemNodeInfo(name);
        if (node != null) {
            if (node instanceof JaxbElementInfo) {
                JaxbElementInfo elemInfo = (JaxbElementInfo) node;
                return elemInfo.getAtomClass();
            }
            // note: Can't have got a JaxbPseudoNodeChoiceInfo - there is no name associated with them
            //       If we have a Wrapper element, then there isn't a class associated with it
            return null;
        }

        JaxbInfo encClassInfo = getSuperClassInfo();
        if (encClassInfo != null) {
            return encClassInfo.getClassForElement(name);
        }
        return null;
    }

    public JaxbNodeInfo getElemNodeInfo(String name) {
        if (name == null) {
            return null;
        }
        for (JaxbNodeInfo entry : jaxbElemNodeInfo) {
            if (entry instanceof JaxbPseudoNodeChoiceInfo) {
                JaxbPseudoNodeChoiceInfo pseudoNode = (JaxbPseudoNodeChoiceInfo) entry;
                JaxbElementInfo choiceElem = pseudoNode.getElemInfo(name);
                if (choiceElem != null) {
                    return choiceElem;
                }
            } else if (name.equals(entry.getName())) {
                return entry;
            }
        }
        JaxbInfo encClassInfo = getSuperClassInfo();
        if (encClassInfo != null) {
            return encClassInfo.getElemNodeInfo(name);
        }
        return null;
    }

    private JaxbAttributeInfo getAttrInfo(String name) {
        if (name == null) {
            return null;
        }
        for (JaxbAttributeInfo entry : attrInfo) {
            if (name.equals(entry.getName())) {
                return entry;
            }
        }
        return null;
    }

    public Class<?> getClassForAttribute(String name) {
        JaxbAttributeInfo info = getAttrInfo(name);
        if (info == null) {
            JaxbInfo encClassInfo = getSuperClassInfo();
            if (encClassInfo != null) {
                return encClassInfo.getClassForAttribute(name);
            }
            return null;
        }
        return info.getAtomClass();
    }

    private void setXmlAttributeInfo(XmlAttribute attr, String fieldName, Type defaultGenericType) {
        JaxbAttributeInfo info = new JaxbAttributeInfo(this, attr, fieldName, defaultGenericType);
        String name = info.getName();
        Class<?> atomClass = info.getAtomClass();
        if (atomClass != null && !Strings.isNullOrEmpty(name)) {
            attrInfo.add(info);
        }
    }

    private void setXmlElementInfo(XmlElement elem, String fieldName, Type defaultGenericType) {
        JaxbElementInfo info = new JaxbElementInfo(elem, fieldName, defaultGenericType);
        String name = info.getName();
        Class<?> atomClass = info.getAtomClass();
        if (atomClass != null && !Strings.isNullOrEmpty(name)) {
            jaxbElemNodeInfo.add(info);
        }
    }

    private void setXmlElementInfo(XmlElementRef elemRef, String fieldName, Type defaultGenericType) {
        JaxbElementInfo info = new JaxbElementInfo(elemRef, fieldName, defaultGenericType);
        String name = info.getName();
        Class<?> atomClass = info.getAtomClass();
        if (atomClass != null && !Strings.isNullOrEmpty(name)) {
            jaxbElemNodeInfo.add(info);
        }
    }

    private void processFieldRelatedAnnotations(Annotation annots[], String fieldName, Type defaultGenericType) {
        WrappedElementInfo wrappedInfo = null;
        for (Annotation annot : annots) {
            if (annot instanceof XmlElementWrapper) {
                XmlElementWrapper wrapper = (XmlElementWrapper) annot;
                wrappedInfo = new WrappedElementInfo(wrapper, fieldName);
                jaxbElemNodeInfo.add(wrappedInfo);
                break;
            }
        }
        for (Annotation annot : annots) {
            if (annot instanceof XmlValue) {
                elementValue = new JaxbValueInfo((XmlValue) annot, fieldName, defaultGenericType);
            } else if (annot instanceof XmlAttribute) {
                XmlAttribute attr = (XmlAttribute) annot;
                String attrName = attr.name();
                if ((attrName == null) || DEFAULT_MARKER.equals(attrName)) {
                    attrName = fieldName;
                }
                this.setXmlAttributeInfo(attr, fieldName, defaultGenericType);
                this.attributeNames.add(attrName);
            } else if (annot instanceof XmlElement) {
                XmlElement xmlElem = (XmlElement) annot;
                if (wrappedInfo == null) {
                    setXmlElementInfo(xmlElem, fieldName, defaultGenericType);
                } else {
                    wrappedInfo.add(xmlElem, fieldName, defaultGenericType);
                }
            } else if (annot instanceof XmlElementRef) {
                XmlElementRef xmlElemR = (XmlElementRef) annot;
                if (wrappedInfo == null) {
                    setXmlElementInfo(xmlElemR, null, null);
                } else {
                    wrappedInfo.add(xmlElemR, null, null);
                }
            } else if (annot instanceof XmlElements) {
                JaxbPseudoNodeChoiceInfo choiceNode = new JaxbPseudoNodeChoiceInfo(fieldName, defaultGenericType);
                if (wrappedInfo == null) {
                    jaxbElemNodeInfo.add(choiceNode);
                } else {
                    wrappedInfo.add(choiceNode);
                }
                XmlElements xmlElemsAnnot = (XmlElements) annot;
                for (XmlElement xmlE : xmlElemsAnnot.value()) {
                    choiceNode.add(xmlE);
                }
            } else if (annot instanceof XmlElementRefs) {
                JaxbPseudoNodeChoiceInfo choiceNode = new JaxbPseudoNodeChoiceInfo(fieldName, defaultGenericType);
                if (wrappedInfo == null) {
                    jaxbElemNodeInfo.add(choiceNode);
                } else {
                    wrappedInfo.add(choiceNode);
                }
                XmlElementRefs elemRefs = (XmlElementRefs) annot;
                for (XmlElementRef xmlE : elemRefs.value()) {
                    choiceNode.add(xmlE);
                }
            }
        }
    }

    public static boolean isGetter(Method method) {
        String methodName = method.getName();
        if ((!methodName.startsWith("get")) && (!methodName.startsWith("is"))) {
            return false;
        }
        if (method.getParameterTypes().length != 0) {
            return false;
        }
        return (!void.class.equals(method.getReturnType()));
    }

    public static boolean isSetter(Method method) {
        if (!method.getName().startsWith("set")) {
            return false;
        }
        return (method.getParameterTypes().length == 1);
    }

    public static boolean isGetterOrSetter(Method method) {
        return isGetter(method) || isSetter(method);
    }

    private String guessFieldNameFromGetterOrSetter(String methodName) {
        String fieldName = null;
        if ((methodName.startsWith("set")) || (methodName.startsWith("get"))) {
            fieldName = methodName.substring(3, 4).toLowerCase() + methodName.substring(4);
        } else if (methodName.startsWith("is")) {
            fieldName = methodName.substring(2, 3).toLowerCase() + methodName.substring(3);
        }
        return fieldName;
    }

    /**
     * Returns the most elemental class associated with {@link genericType}
     * May return null
     */
    public static Class<?> classFromType(Type genericType) {
        Class<?> defKlass;
        if (genericType == null) {
            return null;
        }
        if (genericType instanceof Class<?>) {
            defKlass = (Class<?>) genericType;
        } else if (genericType instanceof ParameterizedType) {
            ParameterizedType pt = (ParameterizedType) genericType;
            Type typeArgs[] = pt.getActualTypeArguments();
            if (typeArgs.length != 1) {
                // Odd - better to ignore this
                return null;
            }
            return classFromType(typeArgs[0]);
        } else if (genericType instanceof GenericArrayType) {
            GenericArrayType gat = (GenericArrayType) genericType;
            defKlass = gat.getGenericComponentType().getClass();
        } else if (genericType instanceof TypeVariable<?>) {
            TypeVariable<?> tv = (TypeVariable<?>) genericType;
            defKlass = tv.getClass();
        } else {
            LOG.debug("classFromType unknown instance type [" + genericType.toString() + "] - ignoring");
            defKlass = null;
        }
        return defKlass;
    }

    /**
     * Returns the most elemental class associated with {@link genericType}
     * May return null
     */
    public static boolean representsMultipleElements(Type genericType) {
        Class<?> defKlass = null;
        if (genericType == null) {
            return false;
        }
        if (genericType instanceof Class<?>) {
            defKlass = (Class<?>) genericType;
            return Collection.class.isAssignableFrom(defKlass);
        } else if (genericType instanceof ParameterizedType) {
            // e.g. java.util.List<com.zimbra.soap.type.AttributeName>
            ParameterizedType pt = (ParameterizedType) genericType;
            Type rawType = pt.getRawType();
            if (rawType instanceof Class<?>) {
                return Collection.class.isAssignableFrom((Class<?>) rawType);
            }
            return false;
        } else if (genericType instanceof GenericArrayType) {
            return true;
        } else if (genericType instanceof TypeVariable<?>) {
            TypeVariable<?> tv = (TypeVariable<?>) genericType;
            return Collection.class.isAssignableFrom(tv.getClass());
        } else {
            return false;
        }
    }

    private void gatherInfo() {
        XmlAccessorType accessorType;
        rootElementName = null;
        XmlAccessType accessType = null;

        XmlRootElement rootE = jaxbClass.getAnnotation(XmlRootElement.class);
        if (rootE != null) {
            rootElementName = rootE.name();
        }
        xmlType = jaxbClass.getAnnotation(XmlType.class);

        accessorType = jaxbClass.getAnnotation(XmlAccessorType.class);
        if (accessorType == null) {
            Package pkg = jaxbClass.getPackage();
            accessorType = pkg.getAnnotation(XmlAccessorType.class);
        }
        if (accessorType != null) {
            accessType = accessorType.value();
        }
        if (accessType == null) {
            // Default value for JAXB
            accessType = XmlAccessType.PUBLIC_MEMBER;
        }

        Field fields[] = jaxbClass.getDeclaredFields();
        for (Field field : fields) {
            XmlTransient xmlTransient = field.getAnnotation(XmlTransient.class);
            if (xmlTransient != null) {
                continue;
            }
            Annotation fAnnots[] = field.getAnnotations();
            if ((fAnnots == null) || (fAnnots.length == 0)) {
                boolean autoFields = (accessType.equals(XmlAccessType.PUBLIC_MEMBER)
                        || accessType.equals(XmlAccessType.FIELD));
                if (!autoFields) {
                    continue;
                }
            }
            processFieldRelatedAnnotations(fAnnots, field.getName(), field.getGenericType());
        }

        Method methods[] = jaxbClass.getDeclaredMethods();
        for (Method method : methods) {
            XmlTransient xmlTransient = method.getAnnotation(XmlTransient.class);
            if (xmlTransient != null) {
                continue;
            }
            if (!isGetterOrSetter(method)) {
                continue;
            }
            Annotation mAnnots[] = method.getAnnotations();
            if ((mAnnots == null) || (mAnnots.length == 0)) {
                boolean autoGettersSetters = (accessType.equals(XmlAccessType.PUBLIC_MEMBER)
                        || accessType.equals(XmlAccessType.PROPERTY));
                if (!autoGettersSetters) {
                    continue;
                }
            }
            processFieldRelatedAnnotations(mAnnots, guessFieldNameFromGetterOrSetter(method.getName()),
                    method.getGenericReturnType());
        }
    }

    protected String getStamp() {
        return stamp;
    }

    public Class<?> getJaxbClass() {
        return jaxbClass;
    }

    public JaxbValueInfo getElementValue() {
        return elementValue;
    }
}