/**
 * 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
 * info@exolab.org.
 *
 * 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-2004 (C) Intalio, Inc. All Rights Reserved.
 *
 * $Id$
 */

package org.exolab.castor.xml;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.List;
import java.util.Set;

import org.castor.xml.BackwardCompatibilityContext;
import org.castor.xml.InternalContext;
import org.castor.xml.AbstractInternalContext;
import org.castor.xml.JavaNaming;
import org.exolab.castor.mapping.ClassDescriptor;
import org.exolab.castor.mapping.CollectionHandler;
import org.exolab.castor.mapping.FieldDescriptor;
import org.exolab.castor.mapping.MappingException;
import org.exolab.castor.mapping.loader.CollectionHandlers;
import org.exolab.castor.util.ReflectionUtil;


/**
 * A core class for common code shared throughout the Marshalling Framework
 * 
 * @author <a href="mailto:kvisco-at-intalio.com">Keith Visco</a>
 * @version $Revision$ $Date: 2005-12-13 14:58:48 -0700 (Tue, 13 Dec 2005) $
 */
abstract class MarshalFramework {



  // --------------------------/
  // - Public class variables -/
  // --------------------------/

  /**
   * JDK version 1.5 .
   */
  private static final double JDK_VERSION_1_5 = 1.5;

  /**
   * The XSI Namespace URI.
   **/
  public static final String XSI_NAMESPACE = "http://www.w3.org/2001/XMLSchema-instance";

  /**
   * The name of the Schema location attribute.
   **/
  public static final String XSI_SCHEMA_LOCATION = "schemaLocation";

  /**
   * The name of the no namespace schema location attribute.
   **/
  public static final String XSI_NO_NAMESPACE_SCHEMA_LOCATION = "noNamespaceSchemaLocation";

  /**
   * The xml:lang attribute name.
   */
  public static final String XML_LANG_ATTR = "xml:lang";

  /**
   * The xml:lang attribute, without the "xml:" prefix.
   */
  public static final String LANG_ATTR = "lang";

  /**
   * The xsi:nil attribute, without the "xsi:" prefix.
   */
  public static final String NIL_ATTR = "nil";

  /**
   * The xsi:nil attribute.
   */
  public static final String XSI_NIL_ATTR = "xsi:nil";

  /**
   * The xml:space attribute name.
   */
  public static final String XML_SPACE_ATTR = "xml:space";

  /**
   * The xml:space attribute name, without the "xml:" prefix.
   */
  public static final String SPACE_ATTR = "space";

  /**
   * The xsi:type attribute name, without the "xsi:" prefix.
   */
  public static final String TYPE_ATTR = "type";

  /**
   * The value of 'true'.
   */
  public static final String TRUE_VALUE = "true";

  // -----------------------------/
  // - Protected class variables -/
  // -----------------------------/

  /**
   * A constant to indicate a wrong name without setting null.
   */
  static final String INTERNAL_XML_NAME = "-error-if-this-is-used-";

  /**
   * The default prefix used for specifying the xsi:type as a classname instead of a schema name.
   * This is a Castor specific hack.
   */
  static final String JAVA_PREFIX = "java:";

  /**
   * The name of the QName type.
   */
  static final String QNAME_NAME = "QName";

  /**
   * An empty array of field descriptors.
   */
  static final XMLFieldDescriptor[] NO_FIELD_DESCRIPTORS = new XMLFieldDescriptor[0];

  // -----------------------------/
  // - Private variables -/
  // -----------------------------/

  /**
   * The {@link AbstractInternalContext} to use at all un-marshal actions.
   * 
   * @since 1.1.3
   */
  private InternalContext _internalContext;

  // -----------------------------/
  // - Public methods -/
  // -----------------------------/

  /**
   * We need some stuff initialized here. MarshalFramework requires internally an
   * {@link InternalContext}, so either one is given or {@link BackwardCompatibilityContext} is
   * instantiated! Mind that instantiating {@link BackwardCompatibilityContext} means to (re-)read
   * configuration files.
   * 
   * @param internalContext either an {@link InternalContext} comes from outside or
   *        {@link BackwardCompatibilityContext} is instantiated
   */
  public MarshalFramework(final InternalContext internalContext) {
    if (internalContext == null) {
      _internalContext = new BackwardCompatibilityContext();
    } else {
      _internalContext = internalContext;
    }
  }

  /**
   * To get the {@link JavaNaming} instance to be used.
   * 
   * @return the JavaNaming to be used
   */
  public JavaNaming getJavaNaming() {
    return _internalContext.getJavaNaming();
  }

  /**
   * To set the {@link JavaNaming} instance to be used.
   * 
   * @param javaNaming the JavaNaming to be used TODO: joachim remove me if possible!
   */
  private void setJavaNaming(final JavaNaming javaNaming) {
    _internalContext.setJavaNaming(javaNaming);
  }

  /**
   * To get the {@link AbstractInternalContext} to use.
   * 
   * @return the {@link AbstractInternalContext} to use
   */
  public InternalContext getInternalContext() {
    return _internalContext;
  }

  /**
   * To set the {@link AbstractInternalContext} to use.
   * 
   * @param internalContext the {@link AbstractInternalContext} to use
   */
  public void setInternalContext(final InternalContext internalContext) {
    _internalContext = internalContext;
  }

  /**
   * Returns true if the given Class is a considered a collection by the marshalling framework.
   * 
   * @param clazz the Class to check
   * @return true if the given Class is considered a collection. TODO: joachim: this code exists
   *         somewhere else too!!
   */
  public static boolean isCollection(final Class clazz) {
    return CollectionHandlers.hasHandler(clazz);
  } // -- isCollection

  /**
   * Returns the CollectionHandler associated with the given collection, or null if no such handler
   * exists.
   * 
   * @param clazz the Class to check
   * @return the CollectionHandler for the associated type.
   */
  public CollectionHandler getCollectionHandler(final Class clazz) {
    CollectionHandler handler = null;
    try {
      handler = CollectionHandlers.getHandler(clazz);
    } catch (MappingException mx) {
      // -- Not a collection, or no handler exists, return null.
    }
    return handler;
  } // -- getCollectionHandler


  /**
   * 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.
   * 
   * @param type the Class to check
   * @return true if the given class should be treated as a primitive type TODO: joachim: this code
   *         exists somewhere else too!!
   **/
  static boolean isPrimitive(final Class type) {

    if (type == null) {
      return false;
    }

    // -- java primitive
    if (type.isPrimitive()) {
      return true;
    }

    // -- we treat strings as primitives
    if (type == String.class) {
      return true;
    }

    // -- primtive wrapper classes
    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");
    }

    return false;

  } // -- isPrimitive

  /**
   * Returns true if the given class should be treated as an enum type. This method will return true
   * for all Java 5 (or later) enums, and for enum-style classes.
   * 
   * @param type the Class to check
   * @return true if the given class should be treated as an enum
   **/
  static boolean isEnum(final Class type) {

    if (type == null) {
      return false;
    }

    float javaVersion =
        Float.valueOf(System.getProperty("java.specification.version")).floatValue();
    if (javaVersion >= JDK_VERSION_1_5) {
      try {
        Boolean isEnum = ReflectionUtil.isEnumViaReflection(type);
        return isEnum.booleanValue();
      } catch (Exception e) {
        // nothing to report; implies that there's no such method
      }
    }

    // TODO: add code to cover 1.4 enum-stype classes as well.

    return false;

  } // -- isPrimitive

  // /**
  // * Calls isEnum() method on target class vi areflection to find out
  // * whether the given type is a Java 5 enumeration.
  // * @param type The type to analyze.
  // * @return True if the type given is a Java 5.0 enum.
  // * @throws NoSuchMethodException If the method can not be found.
  // * @throws IllegalAccessException If access to this method is illegal
  // * @throws InvocationTargetException If the target method can not be invoked.
  // */
  // private static Boolean isEnumViaReflection(Class type)
  // throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
  // Method isEnumMethod = type.getClass().getMethod("isEnum", (Class[]) null);
  // return (Boolean) isEnumMethod.invoke(type, (Object[]) null);
  // }
  //

  /**
   * Returns true if any of the fields associated with the given XMLClassDescriptor are located at,
   * or beneath, the given location.
   *
   * @param location the location to compare against
   * @param classDesc the XMLClassDescriptor in which to check the field locations
   * @return true if any of the fields has a location associated
   */
  static final boolean hasFieldsAtLocation(final String location,
      final XMLClassDescriptor classDesc) {
    // -- check elements
    XMLFieldDescriptor[] descriptors = classDesc.getElementDescriptors();
    for (int i = 0; i < descriptors.length; i++) {
      if (descriptors[i] == null) {
        continue;
      }
      String tmpLocation = descriptors[i].getLocationPath();
      if ((tmpLocation != null) && (tmpLocation.startsWith(location))) {
        return true;
      }
    }

    // -- check attributes
    descriptors = classDesc.getAttributeDescriptors();
    for (int i = 0; i < descriptors.length; i++) {
      if (descriptors[i] == null) {
        continue;
      }
      String tmpLocation = descriptors[i].getLocationPath();
      if ((tmpLocation != null) && (tmpLocation.startsWith(location))) {
        return true;
      }
    }

    // -- check content/text
    XMLFieldDescriptor content = classDesc.getContentDescriptor();
    if (content != null) {
      String tmpLocation = content.getLocationPath();
      if ((tmpLocation != null) && (tmpLocation.startsWith(location))) {
        return true;
      }
    }
    return false;
  } // -- hasFieldsAtLocation

  /**
   * Compares the given namespaces (as strings) for equality. null and empty values are considered
   * equal.
   *
   * @param ns1 the namespace to compare to argument ns2
   * @param ns2 the namespace to compare to argument ns1
   * @return true if the namespaces are considert equal TODO: joachim put it into XMLNaming!
   */
  public static boolean namespaceEquals(final String ns1, final String ns2) {
    if (ns1 == null) {
      return ((ns2 == null) || (ns2.length() == 0));
    }
    if (ns2 == null) {
      return (ns1.length() == 0);
    }
    return ns1.equals(ns2);
  } // -- namespaceEquals

  /**
   * Returns true if the given classes are both the same primitive or primitive wrapper class. For
   * exmaple, if class "a" is an int (Integer.TYPE) and class "b" is either an int or Integer.class
   * then true will be returned, otherwise false.
   * 
   * @param a compare a with b
   * @param b compare a with b
   * @return true if both a and b are considered equal
   */
  static boolean primitiveOrWrapperEquals(final Class a, final Class b) {
    if (!isPrimitive(a)) {
      return false;
    }
    if (!isPrimitive(b)) {
      return false;
    }

    if (a == b) {
      return true;
    }

    // -- Boolean/boolean
    if ((a == Boolean.class) || (a == Boolean.TYPE)) {
      return ((b == Boolean.class) || (b == Boolean.TYPE));
    }
    // -- Byte/byte
    else if ((a == Byte.class) || (a == Byte.TYPE)) {
      return ((b == Byte.class) || (b == Byte.TYPE));
    }
    // -- Character/char
    else if ((a == Character.class) || (a == Character.TYPE)) {
      return ((b == Character.class) || (b == Character.TYPE));
    }
    // -- Double/double
    else if ((a == Double.class) || (a == Double.TYPE)) {
      return ((b == Double.class) || (b == Double.TYPE));
    } else if ((a == Float.class) || (a == Float.TYPE)) {
      return ((b == Float.class) || (b == Float.TYPE));
    }
    // -- Integer/int
    else if ((a == Integer.class) || (a == Integer.TYPE)) {
      return ((b == Integer.class) || (b == Integer.TYPE));
    }
    // -- Long/long
    else if ((a == Long.class) || (a == Long.TYPE)) {
      return ((b == Long.class) || (b == Long.TYPE));
    }
    // -- Short/short
    else if ((a == Short.class) || (a == Short.TYPE)) {
      return ((b == Short.class) || (b == Short.TYPE));
    }

    return false;
  } // -- primitiveOrWrapperEquals

  /**
   * 
   */
  private static final InheritanceMatch[] NO_MATCH_ARRAY = new InheritanceMatch[0];

  /**
   * Search there is a field descriptor which can accept one of the class descriptor which match the
   * given name and namespace.
   * 
   * @param name XML name of the field
   * @param namespace namespace of the field
   * @param classDesc the class descriptor to match against
   * @param cdResolver the class descriptor resolver to use
   * @return An array of InheritanceMatch.
   * @throws MarshalException if the resolver called fails fatally
   */
  protected InheritanceMatch[] searchInheritance(final String name, final String namespace,
      final XMLClassDescriptor classDesc) throws MarshalException {
    Iterator classDescriptorIterator = null;

    try {
      // -- A little required logic for finding Not-Yet-Loaded
      // -- descriptors
      String className = getJavaNaming().toJavaClassName(name);
      // -- should use namespace-to-prefix mappings, but
      // -- just create package for now.
      Class clazz = classDesc.getJavaClass();
      String pkg = null;
      if (clazz != null) {
        while (clazz.getDeclaringClass() != null) {
          clazz = clazz.getDeclaringClass();
        }
        pkg = clazz.getName();
        int idx = pkg.lastIndexOf('.');
        if (idx >= 0) {
          pkg = pkg.substring(0, idx + 1);
          className = pkg + className;
        }
      }
      getInternalContext().getXMLClassDescriptorResolver().resolve(className,
          classDesc.getClass().getClassLoader());
      // -- end Not-Yet-Loaded descriptor logic

      // -- resolve all by XML name + namespace URI
      classDescriptorIterator = getInternalContext().getXMLClassDescriptorResolver()
          .resolveAllByXMLName(name, namespace, null);
    } catch (ResolverException rx) {
      Throwable actual = rx.getCause();
      if (actual instanceof MarshalException) {
        throw (MarshalException) actual;
      }
      if (actual != null) {
        throw new MarshalException(actual);
      }
      throw new MarshalException(rx);
    }

    List<InheritanceMatch> inheritanceList = null;
    XMLFieldDescriptor descriptor = null;
    XMLFieldDescriptor[] descriptors = classDesc.getElementDescriptors();
    XMLClassDescriptor cdInherited = null;

    if (classDescriptorIterator.hasNext()) {
      while (classDescriptorIterator.hasNext() && (descriptor == null)) {
        cdInherited = (XMLClassDescriptor) classDescriptorIterator.next();
        Class subclass = cdInherited.getJavaClass();

        for (int i = 0; i < descriptors.length; i++) {

          if (descriptors[i] == null) {
            continue;
          }

          // -- skip descriptors with special internal name
          if (INTERNAL_XML_NAME.equals(descriptors[i].getXMLName())) {
            continue;
          }

          // -- check for inheritence
          Class superclass = descriptors[i].getFieldType();

          // It is possible that the superclass is of type object if we use any node.
          if (superclass.isAssignableFrom(subclass) && (superclass != Object.class)) {
            descriptor = descriptors[i];
            if (inheritanceList == null) {
              inheritanceList = new ArrayList<>(3);
            }
            inheritanceList.add(new InheritanceMatch(descriptor, cdInherited));
          }
        }
      }
      // -- reset inherited class descriptor, if necessary
      if (descriptor == null) {
        cdInherited = null;
      }
    }

    if (inheritanceList != null) {
      InheritanceMatch[] result = new InheritanceMatch[inheritanceList.size()];
      return inheritanceList.toArray(result);
    }
    return NO_MATCH_ARRAY;
  }

  /**
   * Used to store the information when we find a possible inheritance. It store the
   * XMLClassDescriptor of the object to instantiate and the XMLFieldDescriptor of the parent, where
   * the instance of the XMLClassDescriptor will be put.
   */
  public static class InheritanceMatch {
    /**
     * The field descriptor of the parent.
     */
    public XMLFieldDescriptor parentFieldDesc;
    /**
     * The class descriptor to instantiate.
     */
    public XMLClassDescriptor inheritedClassDesc;

    public InheritanceMatch(XMLFieldDescriptor fieldDesc, XMLClassDescriptor classDesc) {
      parentFieldDesc = fieldDesc;
      inheritedClassDesc = classDesc;
    }
  }



  /**
   * An internal implementation of XMLClassDescriptor used by the Unmarshaller and Marshaller...
   */
  class InternalXMLClassDescriptor implements XMLClassDescriptor {

    private XMLClassDescriptor _classDesc = null;

    /**
     * Cached arrays.
     */
    private XMLFieldDescriptor[] _attributes = null;
    /**
     * Cached arrays.
     */
    private XMLFieldDescriptor[] _elements = null;
    /**
     * Cached arrays.
     */
    private FieldDescriptor[] _fields = null;

    /**
     * Map holding the properties set and read by Natures.
     */
    private final Map<String, Object> _properties = new HashMap<>();

    /**
     * Map holding the available natures.
     */
    private final Set<String> _natures = new HashSet<>();

    /**
     * Creates a new InternalXMLClassDescriptor for the given XMLClassDescriptor.
     */
    protected InternalXMLClassDescriptor(XMLClassDescriptor classDesc) {
      if (classDesc == null) {
        String err = "The argument 'classDesc' must not be null.";
        throw new IllegalArgumentException(err);
      }

      // -- prevent wrapping another InternalXMLClassDescriptor,
      while (classDesc instanceof InternalXMLClassDescriptor) {
        classDesc = ((InternalXMLClassDescriptor) classDesc).getClassDescriptor();
      }
      _classDesc = classDesc;
    }

    /**
     * Returns the XMLClassDescriptor that this InternalXMLClassDescriptor wraps.
     *
     * @return the XMLClassDescriptor
     */
    public XMLClassDescriptor getClassDescriptor() {
      return _classDesc;
    } // -- getClassDescriptor


    /**
     * Returns the set of XMLFieldDescriptors for all members that should be marshalled as XML
     * attributes. This includes namespace nodes.
     *
     * @return an array of XMLFieldDescriptors for all members that should be marshalled as XML
     *         attributes.
     */
    public XMLFieldDescriptor[] getAttributeDescriptors() {
      if (_attributes == null) {
        _attributes = _classDesc.getAttributeDescriptors();
      }
      return _attributes;
    } // -- getAttributeDescriptors

    /**
     * Returns the XMLFieldDescriptor for the member that should be marshalled as text content.
     *
     * @return the XMLFieldDescriptor for the member that should be marshalled as text content.
     */
    public XMLFieldDescriptor getContentDescriptor() {
      return _classDesc.getContentDescriptor();
    } // -- getContentDescriptor


    /**
     * Returns the XML field descriptor matching the given xml name and nodeType. If NodeType is
     * null, then either an AttributeDescriptor, or ElementDescriptor may be returned. Null is
     * returned if no matching descriptor is available.
     *
     * @param name the xml name to match against
     * @param namespace the xml namespace to match
     * @param nodeType the NodeType to match against, or null if the node type is not known.
     * @return the matching descriptor, or null if no matching descriptor is available.
     *
     */
    public XMLFieldDescriptor getFieldDescriptor(final String name, final String namespace,
        final NodeType nodeType) {
      return _classDesc.getFieldDescriptor(name, namespace, nodeType);
    } // -- getFieldDescriptor

    /**
     * Returns the set of XMLFieldDescriptors for all members that should be marshalled as XML
     * elements.
     *
     * @return an array of XMLFieldDescriptors for all members that should be marshalled as XML
     *         elements.
     */
    public XMLFieldDescriptor[] getElementDescriptors() {
      if (_elements == null) {
        _elements = _classDesc.getElementDescriptors();
      }
      return _elements;
    } // -- getElementDescriptors

    /**
     * @return the namespace prefix to use when marshalling as XML.
     */
    public String getNameSpacePrefix() {
      return _classDesc.getNameSpacePrefix();
    } // -- getNameSpacePrefix

    /**
     * @return the namespace URI used when marshalling and unmarshalling as XML.
     */
    public String getNameSpaceURI() {
      return _classDesc.getNameSpaceURI();
    } // -- getNameSpaceURI

    /**
     * Returns a specific validator for the class described by this ClassDescriptor. A null value
     * may be returned if no specific validator exists.
     *
     * @return the type validator for the class described by this ClassDescriptor.
     */
    public TypeValidator getValidator() {
      return _classDesc.getValidator();
    } // -- getValidator

    /**
     * Returns the XML Name for the Class being described.
     *
     * @return the XML name.
     */
    public String getXMLName() {
      return _classDesc.getXMLName();
    } // -- getXMLName

    /**
     * Returns true if the wrapped ClassDescriptor was created by introspection.
     * 
     * @return true if the wrapped ClassDescriptor was created by introspection.
     */
    public boolean introspected() {
      return Introspector.introspected(_classDesc);
    } // -- introspected

    /**
     * @see org.exolab.castor.xml.XMLClassDescriptor#canAccept( java.lang.String, java.lang.String,
     *      java.lang.Object)
     */
    public boolean canAccept(final String name, final String namespace, final Object object) {
      return _classDesc.canAccept(name, namespace, object);
    } // -- canAccept

    /**
     * {@inheritDoc}
     * 
     * @see org.exolab.castor.xml.XMLClassDescriptor#checkDescriptorForCorrectOrderWithinSequence(org.exolab.castor.xml.XMLFieldDescriptor,
     *      org.exolab.castor.xml.UnmarshalState, java.lang.String)
     */
    public void checkDescriptorForCorrectOrderWithinSequence(
        final XMLFieldDescriptor elementDescriptor, final UnmarshalState parentState,
        final String xmlName) throws ValidationException {
      _classDesc.checkDescriptorForCorrectOrderWithinSequence(elementDescriptor, parentState,
          xmlName);
    }

    // -------------------------------------/
    // - Implementation of ClassDescriptor -/
    // -------------------------------------/

    /**
     * Returns the Java class represented by this descriptor.
     *
     * @return The Java class
     */
    public Class getJavaClass() {
      return _classDesc.getJavaClass();
    } // -- getJavaClass


    /**
     * Returns a list of fields represented by this descriptor.
     *
     * @return A list of fields
     */
    public FieldDescriptor[] getFields() {
      if (_fields == null) {
        _fields = _classDesc.getFields();
      }
      return _fields;
    } // -- getFields


    /**
     * Returns the class descriptor of the class extended by this class.
     *
     * @return The extended class descriptor
     */
    public ClassDescriptor getExtends() {
      return _classDesc.getExtends();
    } // -- getExtends


    /**
     * Returns the identity field, null if this class has no identity.
     *
     * @return The identity field
     */
    public FieldDescriptor getIdentity() {
      return _classDesc.getIdentity();
    } // -- getIdentity

    /**
     * {@inheritDoc}
     * 
     * @see org.exolab.castor.xml.XMLClassDescriptor#isChoice()
     */
    public boolean isChoice() {
      return false;
    }

    /**
     * @see org.exolab.castor.builder.info.nature.PropertyHolder# getProperty(java.lang.String)
     * @param name of the property
     * @return value of the property
     */
    public Object getProperty(final String name) {
      return _properties.get(name);
    }

    /**
     * @see org.exolab.castor.builder.info.nature.PropertyHolder# setProperty(java.lang.String,
     *      java.lang.Object)
     * @param name of the property
     * @param value of the property
     */
    public void setProperty(final String name, final Object value) {
      _properties.put(name, value);
    }

    /**
     * @see org.exolab.castor.builder.info.nature.NatureExtendable# addNature(java.lang.String)
     * @param nature ID of the Nature
     */
    public void addNature(final String nature) {
      _natures.add(nature);
    }

    /**
     * @see org.exolab.castor.builder.info.nature.NatureExtendable# hasNature(java.lang.String)
     * @param nature ID of the Nature
     * @return true if the Nature ID was added.
     */
    public boolean hasNature(final String nature) {
      return _natures.contains(nature);
    }

  } // -- InternalXMLClassDescriptor


} // -- MarshalFramework
