Open Source Repository

Home /hibernate/hibernate-3.2.4.ga | Repository Home


org/hibernate/cfg/HbmBinder.java
// $Id: HbmBinder.java 11495 2007-05-09 03:52:56Z [email protected] $
package org.hibernate.cfg;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Properties;
import java.util.StringTokenizer;

import org.apache.commons.collections.SequencedHashMap;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.Element;
import org.hibernate.CacheMode;
import org.hibernate.EntityMode;
import org.hibernate.FetchMode;
import org.hibernate.FlushMode;
import org.hibernate.MappingException;
import org.hibernate.engine.FilterDefinition;
import org.hibernate.engine.NamedQueryDefinition;
import org.hibernate.engine.Versioning;
import org.hibernate.engine.ExecuteUpdateResultCheckStyle;
import org.hibernate.id.PersistentIdentifierGenerator;
import org.hibernate.mapping.Any;
import org.hibernate.mapping.Array;
import org.hibernate.mapping.AuxiliaryDatabaseObject;
import org.hibernate.mapping.Backref;
import org.hibernate.mapping.Bag;
import org.hibernate.mapping.Collection;
import org.hibernate.mapping.Column;
import org.hibernate.mapping.Component;
import org.hibernate.mapping.DependantValue;
import org.hibernate.mapping.Fetchable;
import org.hibernate.mapping.Filterable;
import org.hibernate.mapping.Formula;
import org.hibernate.mapping.IdentifierBag;
import org.hibernate.mapping.IdentifierCollection;
import org.hibernate.mapping.IndexBackref;
import org.hibernate.mapping.IndexedCollection;
import org.hibernate.mapping.Join;
import org.hibernate.mapping.JoinedSubclass;
import org.hibernate.mapping.KeyValue;
import org.hibernate.mapping.List;
import org.hibernate.mapping.ManyToOne;
import org.hibernate.mapping.Map;
import org.hibernate.mapping.MetaAttribute;
import org.hibernate.mapping.OneToMany;
import org.hibernate.mapping.OneToOne;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.PrimitiveArray;
import org.hibernate.mapping.Property;
import org.hibernate.mapping.PropertyGeneration;
import org.hibernate.mapping.RootClass;
import org.hibernate.mapping.Selectable;
import org.hibernate.mapping.Set;
import org.hibernate.mapping.SimpleAuxiliaryDatabaseObject;
import org.hibernate.mapping.SimpleValue;
import org.hibernate.mapping.SingleTableSubclass;
import org.hibernate.mapping.Subclass;
import org.hibernate.mapping.Table;
import org.hibernate.mapping.ToOne;
import org.hibernate.mapping.TypeDef;
import org.hibernate.mapping.UnionSubclass;
import org.hibernate.mapping.UniqueKey;
import org.hibernate.mapping.Value;
import org.hibernate.persister.entity.JoinedSubclassEntityPersister;
import org.hibernate.persister.entity.SingleTableEntityPersister;
import org.hibernate.persister.entity.UnionSubclassEntityPersister;
import org.hibernate.type.DiscriminatorType;
import org.hibernate.type.ForeignKeyDirection;
import org.hibernate.type.Type;
import org.hibernate.type.TypeFactory;
import org.hibernate.util.JoinedIterator;
import org.hibernate.util.ReflectHelper;
import org.hibernate.util.StringHelper;

/**
 * Walks an XML mapping document and produces the Hibernate configuration-time metamodel (the
 * classes in the <tt>mapping</tt> package)
 *
 @author Gavin King
 */
public final class HbmBinder {

  private static final Log log = LogFactory.getLogHbmBinder.class );

  /**
   * Private constructor to disallow instantiation.
   */
  private HbmBinder() {
  }

  /**
   * The main contract into the hbm.xml-based binder. Performs necessary binding operations
   * represented by the given DOM.
   *
   @param doc The DOM to be parsed and bound.
   @param mappings Current bind state.
   @param inheritedMetas Any inherited meta-tag information.
   @throws MappingException
   */
  public static void bindRoot(Document doc, Mappings mappings, java.util.Map inheritedMetas)
      throws MappingException {

    java.util.List names = HbmBinder.getExtendsNeededdoc, mappings );
    if !names.isEmpty() ) {
      // classes mentioned in extends not available - so put it in queue
      Element hmNode = doc.getRootElement();
      Attribute packNode = hmNode.attribute"package" );
      String packageName = null;
      if packNode != null ) {
        packageName = packNode.getValue();
      }
      Iterator itr = names.iterator();
      while itr.hasNext() ) {
        String extendsName = (Stringitr.next();
        mappings.addToExtendsQueuenew ExtendsQueueEntryextendsName, packageName, doc ) );
      }
      return;
    }

    Element hmNode = doc.getRootElement();
    // get meta's from <hibernate-mapping>
    inheritedMetas = getMetashmNode, inheritedMetas, true );
    extractRootAttributeshmNode, mappings );

    Iterator rootChildren = hmNode.elementIterator();
    while rootChildren.hasNext() ) {
      final Element element = (ElementrootChildren.next();
      final String elementName = element.getName();

      if "filter-def".equalselementName ) ) {
        parseFilterDefelement, mappings );
      }
      else if "typedef".equalselementName ) ) {
        bindTypeDefelement, mappings );
      }
      else if "class".equalselementName ) ) {
        RootClass rootclass = new RootClass();
        bindRootClasselement, rootclass, mappings, inheritedMetas );
        mappings.addClassrootclass );
      }
      else if "subclass".equalselementName ) ) {
        PersistentClass superModel = getSuperclassmappings, element );
        handleSubclasssuperModel, mappings, element, inheritedMetas );
      }
      else if "joined-subclass".equalselementName ) ) {
        PersistentClass superModel = getSuperclassmappings, element );
        handleJoinedSubclasssuperModel, mappings, element, inheritedMetas );
      }
      else if "union-subclass".equalselementName ) ) {
        PersistentClass superModel = getSuperclassmappings, element );
        handleUnionSubclasssuperModel, mappings, element, inheritedMetas );
      }
      else if "query".equalselementName ) ) {
        bindNamedQueryelement, null, mappings );
      }
      else if "sql-query".equalselementName ) ) {
        bindNamedSQLQueryelement, null, mappings );
      }
      else if "resultset".equalselementName ) ) {
        bindResultSetMappingDefinitionelement, null, mappings );
      }
      else if "import".equalselementName ) ) {
        bindImportelement, mappings );
      }
      else if "database-object".equalselementName ) ) {
        bindAuxiliaryDatabaseObjectelement, mappings );
      }
    }
  }

  private static void bindImport(Element importNode, Mappings mappings) {
    String className = getClassNameimportNode.attribute"class" ), mappings );
    Attribute renameNode = importNode.attribute"rename" );
    String rename = renameNode == null ?
            StringHelper.unqualifyclassName :
            renameNode.getValue();
    log.debug"Import: " + rename + " -> " + className );
    mappings.addImportclassName, rename );
  }

  private static void bindTypeDef(Element typedefNode, Mappings mappings) {
    String typeClass = typedefNode.attributeValue"class" );
    String typeName = typedefNode.attributeValue"name" );
    Iterator paramIter = typedefNode.elementIterator"param" );
    Properties parameters = new Properties();
    while paramIter.hasNext() ) {
      Element param = (ElementparamIter.next();
      parameters.setPropertyparam.attributeValue"name" ), param.getTextTrim() );
    }
    mappings.addTypeDeftypeName, typeClass, parameters );
  }

  private static void bindAuxiliaryDatabaseObject(Element auxDbObjectNode, Mappings mappings) {
    AuxiliaryDatabaseObject auxDbObject = null;
    Element definitionNode = auxDbObjectNode.element"definition" );
    if definitionNode != null ) {
      try {
        auxDbObject = AuxiliaryDatabaseObject ReflectHelper
            .classForNamedefinitionNode.attributeValue"class" ) )
            .newInstance();
      }
      catchClassNotFoundException e ) {
        throw new MappingException(
            "could not locate custom database object class [" +
            definitionNode.attributeValue"class" "]"
          );
      }
      catchThrowable t ) {
        throw new MappingException(
            "could not instantiate custom database object class [" +
            definitionNode.attributeValue"class" "]"
          );
      }
    }
    else {
      auxDbObject = new SimpleAuxiliaryDatabaseObject(
          auxDbObjectNode.elementTextTrim"create" ),
          auxDbObjectNode.elementTextTrim"drop" )
        );
    }

    Iterator dialectScopings = auxDbObjectNode.elementIterator"dialect-scope" );
    while dialectScopings.hasNext() ) {
      Element dialectScoping = Element dialectScopings.next();
      auxDbObject.addDialectScopedialectScoping.attributeValue"name" ) );
    }

    mappings.addAuxiliaryDatabaseObjectauxDbObject );
  }

  private static void extractRootAttributes(Element hmNode, Mappings mappings) {
    Attribute schemaNode = hmNode.attribute"schema" );
    mappings.setSchemaName( ( schemaNode == null null : schemaNode.getValue() );

    Attribute catalogNode = hmNode.attribute"catalog" );
    mappings.setCatalogName( ( catalogNode == null null : catalogNode.getValue() );

    Attribute dcNode = hmNode.attribute"default-cascade" );
    mappings.setDefaultCascade( ( dcNode == null "none" : dcNode.getValue() );

    Attribute daNode = hmNode.attribute"default-access" );
    mappings.setDefaultAccess( ( daNode == null "property" : daNode.getValue() );

    Attribute dlNode = hmNode.attribute"default-lazy" );
    mappings.setDefaultLazydlNode == null || dlNode.getValue().equals"true" ) );

    Attribute aiNode = hmNode.attribute"auto-import" );
    mappings.setAutoImport( ( aiNode == null || "true".equalsaiNode.getValue() ) );

    Attribute packNode = hmNode.attribute"package" );
    if packNode != null mappings.setDefaultPackagepackNode.getValue() );
  }

  /**
   * Responsible for perfoming the bind operation related to an &lt;class/&gt; mapping element.
   *
   @param node The DOM Element for the &lt;class/&gt; element.
   @param rootClass The mapping instance to which to bind the information.
   @param mappings The current bind state.
   @param inheritedMetas Any inherited meta-tag information.
   @throws MappingException
   */
  public static void bindRootClass(Element node, RootClass rootClass, Mappings mappings,
      java.util.Map inheritedMetasthrows MappingException {
    bindClassnode, rootClass, mappings, inheritedMetas );
    inheritedMetas = getMetasnode, inheritedMetas, true )// get meta's from <class>
    bindRootPersistentClassCommonValuesnode, inheritedMetas, mappings, rootClass );
  }

  private static void bindRootPersistentClassCommonValues(Element node,
      java.util.Map inheritedMetas, Mappings mappings, RootClass entity)
      throws MappingException {

    // DB-OBJECTNAME

    Attribute schemaNode = node.attribute"schema" );
    String schema = schemaNode == null ?
        mappings.getSchemaName() : schemaNode.getValue();

    Attribute catalogNode = node.attribute"catalog" );
    String catalog = catalogNode == null ?
        mappings.getCatalogName() : catalogNode.getValue();

    Table table = mappings.addTable(
        schema,
        catalog,
        getClassTableNameentity, node, schema, catalog, null, mappings ),
        getSubselectnode ),
            entity.isAbstract() != null && entity.isAbstract().booleanValue()
      );
    entity.setTabletable );
    bindComment(table, node);

    log.info(
        "Mapping class: " + entity.getEntityName() +
        " -> " + entity.getTable().getName()
      );

    // MUTABLE
    Attribute mutableNode = node.attribute"mutable" );
    entity.setMutable( ( mutableNode == null || mutableNode.getValue().equals"true" ) );

    // WHERE
    Attribute whereNode = node.attribute"where" );
    if whereNode != null entity.setWherewhereNode.getValue() );

    // CHECK
    Attribute chNode = node.attribute"check" );
    if chNode != null table.addCheckConstraintchNode.getValue() );

    // POLYMORPHISM
    Attribute polyNode = node.attribute"polymorphism" );
    entity.setExplicitPolymorphism( ( polyNode != null )
      && polyNode.getValue().equals"explicit" ) );

    // ROW ID
    Attribute rowidNode = node.attribute"rowid" );
    if rowidNode != null table.setRowIdrowidNode.getValue() );

    Iterator subnodes = node.elementIterator();
    while subnodes.hasNext() ) {

      Element subnode = (Elementsubnodes.next();
      String name = subnode.getName();

      if "id".equalsname ) ) {
        // ID
        bindSimpleIdsubnode, entity, mappings, inheritedMetas );
      }
      else if "composite-id".equalsname ) ) {
        // COMPOSITE-ID
        bindCompositeIdsubnode, entity, mappings, inheritedMetas );
      }
      else if "version".equalsname || "timestamp".equalsname ) ) {
        // VERSION / TIMESTAMP
        bindVersioningPropertytable, subnode, mappings, name, entity, inheritedMetas );
      }
      else if "discriminator".equalsname ) ) {
        // DISCRIMINATOR
        bindDiscriminatorPropertytable, entity, subnode, mappings );
      }
      else if "cache".equalsname ) ) {
        entity.setCacheConcurrencyStrategysubnode.attributeValue"usage" ) );
        entity.setCacheRegionNamesubnode.attributeValue"region" ) );
        entity.setLazyPropertiesCacheable!"non-lazy".equalssubnode.attributeValue"include" ) ) );
      }

    }

    // Primary key constraint
    entity.createPrimaryKey();

    createClassPropertiesnode, entity, mappings, inheritedMetas );
  }

  private static void bindSimpleId(Element idNode, RootClass entity, Mappings mappings,
      java.util.Map inheritedMetasthrows MappingException {
    String propertyName = idNode.attributeValue"name" );

    SimpleValue id = new SimpleValueentity.getTable() );
    entity.setIdentifierid );

    // if ( propertyName == null || entity.getPojoRepresentation() == null ) {
    // bindSimpleValue( idNode, id, false, RootClass.DEFAULT_IDENTIFIER_COLUMN_NAME, mappings );
    // if ( !id.isTypeSpecified() ) {
    // throw new MappingException( "must specify an identifier type: " + entity.getEntityName()
    // );
    // }
    // }
    // else {
    // bindSimpleValue( idNode, id, false, propertyName, mappings );
    // PojoRepresentation pojo = entity.getPojoRepresentation();
    // id.setTypeUsingReflection( pojo.getClassName(), propertyName );
    //
    // Property prop = new Property();
    // prop.setValue( id );
    // bindProperty( idNode, prop, mappings, inheritedMetas );
    // entity.setIdentifierProperty( prop );
    // }

    if propertyName == null ) {
      bindSimpleValueidNode, id, false, RootClass.DEFAULT_IDENTIFIER_COLUMN_NAME, mappings );
    }
    else {
      bindSimpleValueidNode, id, false, propertyName, mappings );
    }

    if propertyName == null || !entity.hasPojoRepresentation() ) {
      if !id.isTypeSpecified() ) {
        throw new MappingException"must specify an identifier type: "
          + entity.getEntityName() );
      }
    }
    else {
      id.setTypeUsingReflectionentity.getClassName(), propertyName );
    }

    if propertyName != null ) {
      Property prop = new Property();
      prop.setValueid );
      bindPropertyidNode, prop, mappings, inheritedMetas );
      entity.setIdentifierPropertyprop );
    }

    // TODO:
    /*
     * if ( id.getHibernateType().getReturnedClass().isArray() ) throw new MappingException(
     * "illegal use of an array as an identifier (arrays don't reimplement equals)" );
     */
    makeIdentifieridNode, id, mappings );
  }

  private static void bindCompositeId(Element idNode, RootClass entity, Mappings mappings,
      java.util.Map inheritedMetasthrows MappingException {
    String propertyName = idNode.attributeValue"name" );
    Component id = new Componententity );
    entity.setIdentifierid );
    bindCompositeIdidNode, id, entity, propertyName, mappings, inheritedMetas );
    if propertyName == null ) {
      entity.setEmbeddedIdentifierid.isEmbedded() );
      if id.isEmbedded() ) {
        // todo : what is the implication of this?
        id.setDynamic!entity.hasPojoRepresentation() );
        /*
         * Property prop = new Property(); prop.setName("id");
         * prop.setPropertyAccessorName("embedded"); prop.setValue(id);
         * entity.setIdentifierProperty(prop);
         */
      }
    }
    else {
      Property prop = new Property();
      prop.setValueid );
      bindPropertyidNode, prop, mappings, inheritedMetas );
      entity.setIdentifierPropertyprop );
    }

    makeIdentifieridNode, id, mappings );

  }

  private static void bindVersioningProperty(Table table, Element subnode, Mappings mappings,
      String name, RootClass entity, java.util.Map inheritedMetas) {

    String propertyName = subnode.attributeValue"name" );
    SimpleValue val = new SimpleValuetable );
    bindSimpleValuesubnode, val, false, propertyName, mappings );
    if !val.isTypeSpecified() ) {
      // this is either a <version/> tag with no type attribute,
      // or a <timestamp/> tag
      if "version".equalsname ) ) {
        val.setTypeName"integer" );
      }
      else {
        if "db".equalssubnode.attributeValue"source" ) ) ) {
          val.setTypeName"dbtimestamp" );
        }
        else {
          val.setTypeName"timestamp" );
        }
      }
    }
    Property prop = new Property();
    prop.setValueval );
    bindPropertysubnode, prop, mappings, inheritedMetas );
    // for version properties marked as being generated, make sure they are "always"
    // generated; aka, "insert" is invalid; this is dis-allowed by the DTD,
    // but just to make sure...
    if prop.getGeneration() == PropertyGeneration.INSERT ) {
      throw new MappingException"'generated' attribute cannot be 'insert' for versioning property" );
    }
    makeVersionsubnode, val );
    entity.setVersionprop );
    entity.addPropertyprop );
  }

  private static void bindDiscriminatorProperty(Table table, RootClass entity, Element subnode,
      Mappings mappings) {
    SimpleValue discrim = new SimpleValuetable );
    entity.setDiscriminatordiscrim );
    bindSimpleValue(
        subnode,
        discrim,
        false,
        RootClass.DEFAULT_DISCRIMINATOR_COLUMN_NAME,
        mappings
      );
    if !discrim.isTypeSpecified() ) {
      discrim.setTypeName"string" );
      // ( (Column) discrim.getColumnIterator().next() ).setType(type);
    }
    entity.setPolymorphictrue );
    if "true".equalssubnode.attributeValue"force" ) ) )
      entity.setForceDiscriminatortrue );
    if "false".equalssubnode.attributeValue"insert" ) ) )
      entity.setDiscriminatorInsertablefalse );
  }

  public static void bindClass(Element node, PersistentClass persistentClass, Mappings mappings,
      java.util.Map inheritedMetasthrows MappingException {
    // transfer an explicitly defined entity name
    // handle the lazy attribute
    Attribute lazyNode = node.attribute"lazy" );
    boolean lazy = lazyNode == null ?
        mappings.isDefaultLazy() :
        "true".equalslazyNode.getValue() );
    // go ahead and set the lazy here, since pojo.proxy can override it.
    persistentClass.setLazylazy );

    String entityName = node.attributeValue"entity-name" );
    if entityName == null entityName = getClassNamenode.attribute("name"), mappings );
    if entityName==null ) {
      throw new MappingException"Unable to determine entity name" );
    }
    persistentClass.setEntityNameentityName );

    bindPojoRepresentationnode, persistentClass, mappings, inheritedMetas );
    bindDom4jRepresentationnode, persistentClass, mappings, inheritedMetas );
    bindMapRepresentationnode, persistentClass, mappings, inheritedMetas );

    bindPersistentClassCommonValuesnode, persistentClass, mappings, inheritedMetas );

  }

  private static void bindPojoRepresentation(Element node, PersistentClass entity,
      Mappings mappings, java.util.Map metaTags) {

    String className = getClassNamenode.attribute"name" ), mappings );
    String proxyName = getClassNamenode.attribute"proxy" ), mappings );

    entity.setClassNameclassName );

    if proxyName != null ) {
      entity.setProxyInterfaceNameproxyName );
      entity.setLazytrue );
    }
    else if entity.isLazy() ) {
      entity.setProxyInterfaceNameclassName );
    }

    Element tuplizer = locateTuplizerDefinitionnode, EntityMode.POJO );
    if tuplizer != null ) {
      entity.addTuplizerEntityMode.POJO, tuplizer.attributeValue"class" ) );
    }
  }

  private static void bindDom4jRepresentation(Element node, PersistentClass entity,
      Mappings mappings, java.util.Map inheritedMetas) {
    String nodeName = node.attributeValue"node" );
    if (nodeName==nullnodeName = StringHelper.unqualifyentity.getEntityName() );
    entity.setNodeName(nodeName);

    Element tuplizer = locateTuplizerDefinitionnode, EntityMode.DOM4J );
    if tuplizer != null ) {
      entity.addTuplizerEntityMode.DOM4J, tuplizer.attributeValue"class" ) );
    }
  }

  private static void bindMapRepresentation(Element node, PersistentClass entity,
      Mappings mappings, java.util.Map inheritedMetas) {
    Element tuplizer = locateTuplizerDefinitionnode, EntityMode.MAP );
    if tuplizer != null ) {
      entity.addTuplizerEntityMode.MAP, tuplizer.attributeValue"class" ) );
    }
  }

  /**
   * Locate any explicit tuplizer definition in the metadata, for the given entity-mode.
   *
   @param container The containing element (representing the entity/component)
   @param entityMode The entity-mode for which to locate the tuplizer element
   @return The tuplizer element, or null.
   */
  private static Element locateTuplizerDefinition(Element container, EntityMode entityMode) {
    Iterator itr = container.elements"tuplizer" ).iterator();
    whileitr.hasNext() ) {
      final Element tuplizerElem = Element itr.next();
      if entityMode.toString().equalstuplizerElem.attributeValue"entity-mode") ) ) {
        return tuplizerElem;
      }
    }
    return null;
  }

  private static void bindPersistentClassCommonValues(Element node, PersistentClass entity,
      Mappings mappings, java.util.Map inheritedMetasthrows MappingException {
    // DISCRIMINATOR
    Attribute discriminatorNode = node.attribute"discriminator-value" );
    entity.setDiscriminatorValue( ( discriminatorNode == null )
      ? entity.getEntityName()
      : discriminatorNode.getValue() );

    // DYNAMIC UPDATE
    Attribute dynamicNode = node.attribute"dynamic-update" );
    entity.setDynamicUpdate(
        dynamicNode != null && "true".equalsdynamicNode.getValue() )
    );

    // DYNAMIC INSERT
    Attribute insertNode = node.attribute"dynamic-insert" );
    entity.setDynamicInsert(
        insertNode != null && "true".equalsinsertNode.getValue() )
    );

    // IMPORT
    mappings.addImportentity.getEntityName(), entity.getEntityName() );
    if mappings.isAutoImport() && entity.getEntityName().indexOf'.' ) {
      mappings.addImport(
          entity.getEntityName(),
          StringHelper.unqualifyentity.getEntityName() )
        );
    }

    // BATCH SIZE
    Attribute batchNode = node.attribute"batch-size" );
    if batchNode != null entity.setBatchSizeInteger.parseIntbatchNode.getValue() ) );

    // SELECT BEFORE UPDATE
    Attribute sbuNode = node.attribute"select-before-update" );
    if sbuNode != null entity.setSelectBeforeUpdate"true".equalssbuNode.getValue() ) );

    // OPTIMISTIC LOCK MODE
    Attribute olNode = node.attribute"optimistic-lock" );
    entity.setOptimisticLockModegetOptimisticLockModeolNode ) );

    entity.setMetaAttributesgetMetasnode, inheritedMetas ) );

    // PERSISTER
    Attribute persisterNode = node.attribute"persister" );
    if persisterNode == null ) {
      // persister = SingleTableEntityPersister.class;
    }
    else {
      try {
        entity.setEntityPersisterClassReflectHelper.classForNamepersisterNode
          .getValue() ) );
      }
      catch (ClassNotFoundException cnfe) {
        throw new MappingException"Could not find persister class: "
          + persisterNode.getValue() );
      }
    }

    // CUSTOM SQL
    handleCustomSQLnode, entity );

    Iterator tables = node.elementIterator"synchronize" );
    while tables.hasNext() ) {
      entity.addSynchronizedTable( ( (Elementtables.next() ).attributeValue"table" ) );
    }

    Attribute abstractNode = node.attribute"abstract" );
    Boolean isAbstract = abstractNode == null
        null
            "true".equalsabstractNode.getValue() )
            ? Boolean.TRUE
                      "false".equalsabstractNode.getValue() )
                ? Boolean.FALSE
                              null;
    entity.setAbstractisAbstract );
  }

  private static void handleCustomSQL(Element node, PersistentClass model)
      throws MappingException {
    Element element = node.element"sql-insert" );
    if element != null ) {
      boolean callable = isCallableelement );
      model.setCustomSQLInsertelement.getTextTrim(), callable, getResultCheckStyleelement, callable ) );
    }

    element = node.element"sql-delete" );
    if element != null ) {
      boolean callable = isCallableelement );
      model.setCustomSQLDeleteelement.getTextTrim(), callable, getResultCheckStyleelement, callable ) );
    }

    element = node.element"sql-update" );
    if element != null ) {
      boolean callable = isCallableelement );
      model.setCustomSQLUpdateelement.getTextTrim(), callable, getResultCheckStyleelement, callable ) );
    }

    element = node.element"loader" );
    if element != null ) {
      model.setLoaderNameelement.attributeValue"query-ref" ) );
    }
  }

  private static void handleCustomSQL(Element node, Join modelthrows MappingException {
    Element element = node.element"sql-insert" );
    if element != null ) {
      boolean callable = isCallableelement );
      model.setCustomSQLInsertelement.getTextTrim(), callable, getResultCheckStyleelement, callable ) );
    }

    element = node.element"sql-delete" );
    if element != null ) {
      boolean callable = isCallableelement );
      model.setCustomSQLDeleteelement.getTextTrim(), callable, getResultCheckStyleelement, callable ) );
    }

    element = node.element"sql-update" );
    if element != null ) {
      boolean callable = isCallableelement );
      model.setCustomSQLUpdateelement.getTextTrim(), callable, getResultCheckStyleelement, callable ) );
    }
  }

  private static void handleCustomSQL(Element node, Collection modelthrows MappingException {
    Element element = node.element"sql-insert" );
    if element != null ) {
      boolean callable = isCallableelement, true );
      model.setCustomSQLInsertelement.getTextTrim(), callable, getResultCheckStyleelement, callable ) );
    }

    element = node.element"sql-delete" );
    if element != null ) {
      boolean callable = isCallableelement, true );
      model.setCustomSQLDeleteelement.getTextTrim(), callable, getResultCheckStyleelement, callable ) );
    }

    element = node.element"sql-update" );
    if element != null ) {
      boolean callable = isCallableelement, true );
      model.setCustomSQLUpdateelement.getTextTrim(), callable, getResultCheckStyleelement, callable ) );
    }

    element = node.element"sql-delete-all" );
    if element != null ) {
      boolean callable = isCallableelement, true );
      model.setCustomSQLDeleteAllelement.getTextTrim(), callable, getResultCheckStyleelement, callable ) );
    }
  }

  private static boolean isCallable(Element ethrows MappingException {
    return isCallablee, true );
  }

  private static boolean isCallable(Element element, boolean supportsCallable)
      throws MappingException {
    Attribute attrib = element.attribute"callable" );
    if attrib != null && "true".equalsattrib.getValue() ) ) {
      if !supportsCallable ) {
        throw new MappingException"callable attribute not supported yet!" );
      }
      return true;
    }
    return false;
  }

  private static ExecuteUpdateResultCheckStyle getResultCheckStyle(Element element, boolean callablethrows MappingException {
    Attribute attr = element.attribute"check" );
    if attr == null ) {
      // use COUNT as the default.  This mimics the old behavior, although
      // NONE might be a better option moving forward in the case of callable
      return ExecuteUpdateResultCheckStyle.COUNT;
    }
    return ExecuteUpdateResultCheckStyle.parseattr.getValue() );
  }

  public static void bindUnionSubclass(Element node, UnionSubclass unionSubclass,
      Mappings mappings, java.util.Map inheritedMetasthrows MappingException {

    bindClassnode, unionSubclass, mappings, inheritedMetas );
    inheritedMetas = getMetasnode, inheritedMetas, true )// get meta's from <subclass>

    if unionSubclass.getEntityPersisterClass() == null ) {
      unionSubclass.getRootClass().setEntityPersisterClass(
        UnionSubclassEntityPersister.class );
    }

    Attribute schemaNode = node.attribute"schema" );
    String schema = schemaNode == null ?
        mappings.getSchemaName() : schemaNode.getValue();

    Attribute catalogNode = node.attribute"catalog" );
    String catalog = catalogNode == null ?
        mappings.getCatalogName() : catalogNode.getValue();

    Table denormalizedSuperTable = unionSubclass.getSuperclass().getTable();
    Table mytable = mappings.addDenormalizedTable(
        schema,
        catalog,
        getClassTableName(unionSubclass, node, schema, catalog, denormalizedSuperTable, mappings ),
            unionSubclass.isAbstract() != null && unionSubclass.isAbstract().booleanValue(),
        getSubselectnode ),
        denormalizedSuperTable
      );
    unionSubclass.setTablemytable );

    log.info(
        "Mapping union-subclass: " + unionSubclass.getEntityName() +
        " -> " + unionSubclass.getTable().getName()
      );

    createClassPropertiesnode, unionSubclass, mappings, inheritedMetas );

  }

  public static void bindSubclass(Element node, Subclass subclass, Mappings mappings,
      java.util.Map inheritedMetasthrows MappingException {

    bindClassnode, subclass, mappings, inheritedMetas );
    inheritedMetas = getMetasnode, inheritedMetas, true )// get meta's from <subclass>

    if subclass.getEntityPersisterClass() == null ) {
      subclass.getRootClass()
          .setEntityPersisterClassSingleTableEntityPersister.class );
    }

    log.info(
        "Mapping subclass: " + subclass.getEntityName() +
        " -> " + subclass.getTable().getName()
      );

    // properties
    createClassPropertiesnode, subclass, mappings, inheritedMetas );
  }

  private static String getClassTableName(
      PersistentClass model, Element node, String schema, String catalog, Table denormalizedSuperTable,
      Mappings mappings
  ) {
    Attribute tableNameNode = node.attribute"table" );
    String logicalTableName;
    String physicalTableName;
    if tableNameNode == null ) {
      logicalTableName = StringHelper.unqualifymodel.getEntityName() );
      physicalTableName = mappings.getNamingStrategy().classToTableNamemodel.getEntityName() );
    }
    else {
      logicalTableName = tableNameNode.getValue();
      physicalTableName = mappings.getNamingStrategy().tableNamelogicalTableName );
    }
    mappings.addTableBindingschema, catalog, logicalTableName, physicalTableName, denormalizedSuperTable );
    return physicalTableName;
  }

  public static void bindJoinedSubclass(Element node, JoinedSubclass joinedSubclass,
      Mappings mappings, java.util.Map inheritedMetasthrows MappingException {

    bindClassnode, joinedSubclass, mappings, inheritedMetas );
    inheritedMetas = getMetasnode, inheritedMetas, true )// get meta's from
                                  // <joined-subclass>

    // joined subclasses
    if joinedSubclass.getEntityPersisterClass() == null ) {
      joinedSubclass.getRootClass()
        .setEntityPersisterClassJoinedSubclassEntityPersister.class );
    }

    Attribute schemaNode = node.attribute"schema" );
    String schema = schemaNode == null ?
        mappings.getSchemaName() : schemaNode.getValue();

    Attribute catalogNode = node.attribute"catalog" );
    String catalog = catalogNode == null ?
        mappings.getCatalogName() : catalogNode.getValue();

    Table mytable = mappings.addTable(
        schema,
        catalog,
        getClassTableNamejoinedSubclass, node, schema, catalog, null, mappings ),
        getSubselectnode ),
        false
      );
    joinedSubclass.setTablemytable );
    bindComment(mytable, node);

    log.info(
        "Mapping joined-subclass: " + joinedSubclass.getEntityName() +
        " -> " + joinedSubclass.getTable().getName()
      );

    // KEY
    Element keyNode = node.element"key" );
    SimpleValue key = new DependantValuemytable, joinedSubclass.getIdentifier() );
    joinedSubclass.setKeykey );
    key.setCascadeDeleteEnabled"cascade".equalskeyNode.attributeValue"on-delete" ) ) );
    bindSimpleValuekeyNode, key, false, joinedSubclass.getEntityName(), mappings );

    // model.getKey().setType( new Type( model.getIdentifier() ) );
    joinedSubclass.createPrimaryKey();
    joinedSubclass.createForeignKey();

    // CHECK
    Attribute chNode = node.attribute"check" );
    if chNode != null mytable.addCheckConstraintchNode.getValue() );

    // properties
    createClassPropertiesnode, joinedSubclass, mappings, inheritedMetas );

  }

  private static void bindJoin(Element node, Join join, Mappings mappings,
      java.util.Map inheritedMetasthrows MappingException {

    PersistentClass persistentClass = join.getPersistentClass();
    String path = persistentClass.getEntityName();

    // TABLENAME

    Attribute schemaNode = node.attribute"schema" );
    String schema = schemaNode == null ?
        mappings.getSchemaName() : schemaNode.getValue();
    Attribute catalogNode = node.attribute"catalog" );
    String catalog = catalogNode == null ?
        mappings.getCatalogName() : catalogNode.getValue();
    Table primaryTable = persistentClass.getTable();
    Table table = mappings.addTable(
        schema,
        catalog,
        getClassTableNamepersistentClass, node, schema, catalog, primaryTable, mappings ),
        getSubselectnode ),
        false
      );
    join.setTabletable );
    bindComment(table, node);

    Attribute fetchNode = node.attribute"fetch" );
    if fetchNode != null ) {
      join.setSequentialSelect"select".equalsfetchNode.getValue() ) );
    }

    Attribute invNode = node.attribute"inverse" );
    if invNode != null ) {
      join.setInverse"true".equalsinvNode.getValue() ) );
    }

    Attribute nullNode = node.attribute"optional" );
    if nullNode != null ) {
      join.setOptional"true".equalsnullNode.getValue() ) );
    }

    log.info(
        "Mapping class join: " + persistentClass.getEntityName() +
        " -> " + join.getTable().getName()
      );

    // KEY
    Element keyNode = node.element"key" );
    SimpleValue key = new DependantValuetable, persistentClass.getIdentifier() );
    join.setKeykey );
    key.setCascadeDeleteEnabled"cascade".equalskeyNode.attributeValue"on-delete" ) ) );
    bindSimpleValuekeyNode, key, false, persistentClass.getEntityName(), mappings );

    // join.getKey().setType( new Type( lazz.getIdentifier() ) );
    join.createPrimaryKey();
    join.createForeignKey();

    // PROPERTIES
    Iterator iter = node.elementIterator();
    while iter.hasNext() ) {
      Element subnode = (Elementiter.next();
      String name = subnode.getName();
      String propertyName = subnode.attributeValue"name" );

      Value value = null;
      if "many-to-one".equalsname ) ) {
        value = new ManyToOnetable );
        bindManyToOnesubnode, (ManyToOnevalue, propertyName, true, mappings );
      }
      else if "any".equalsname ) ) {
        value = new Anytable );
        bindAnysubnode, (Anyvalue, true, mappings );
      }
      else if "property".equalsname ) ) {
        value = new SimpleValuetable );
        bindSimpleValuesubnode, (SimpleValuevalue, true, propertyName, mappings );
      }
      else if "component".equalsname || "dynamic-component".equalsname ) ) {
        String subpath = StringHelper.qualifypath, propertyName );
        value = new Componentjoin );
        bindComponent(
            subnode,
            (Componentvalue,
            join.getPersistentClass().getClassName(),
            propertyName,
            subpath,
            true,
            false,
            mappings,
            inheritedMetas,
            false
          );
      }

      if value != null ) {
        Property prop = createPropertyvalue, propertyName, persistentClass
          .getEntityName(), subnode, mappings, inheritedMetas );
        prop.setOptionaljoin.isOptional() );
        join.addPropertyprop );
      }

    }

    // CUSTOM SQL
    handleCustomSQLnode, join );

  }

  public static void bindColumns(final Element node, final SimpleValue simpleValue,
      final boolean isNullable, final boolean autoColumn, final String propertyPath,
      final Mappings mappingsthrows MappingException {

    Table table = simpleValue.getTable();

    // COLUMN(S)
    Attribute columnAttribute = node.attribute"column" );
    if columnAttribute == null ) {
      Iterator iter = node.elementIterator();
      int count = 0;
      while iter.hasNext() ) {
        Element columnElement = (Elementiter.next();
        if columnElement.getName().equals"column" ) ) {
          Column column = new Column();
          column.setValuesimpleValue );
          column.setTypeIndexcount++ );
          bindColumncolumnElement, column, isNullable );
          String logicalColumnName = mappings.getNamingStrategy().logicalColumnName(
              columnElement.attributeValue"name" ), propertyPath
          );
          column.setNamemappings.getNamingStrategy().columnName(
            logicalColumnName ) );
          if table != null ) {
            table.addColumncolumn )// table=null -> an association
                                       // - fill it in later
            //TODO fill in the mappings for table == null
            mappings.addColumnBindinglogicalColumnName, column, table );
          }


          simpleValue.addColumncolumn );
          // column index
          bindIndexcolumnElement.attribute"index" ), table, column, mappings );
          bindIndexnode.attribute"index" ), table, column, mappings );
          //column unique-key
          bindUniqueKeycolumnElement.attribute"unique-key" ), table, column, mappings );
          bindUniqueKeynode.attribute"unique-key" ), table, column, mappings );
        }
        else if columnElement.getName().equals"formula" ) ) {
          Formula formula = new Formula();
          formula.setFormulacolumnElement.getText() );
          simpleValue.addFormulaformula );
        }
      }
    }
    else {
      if node.elementIterator"column" ).hasNext() ) {
        throw new MappingException(
          "column attribute may not be used together with <column> subelement" );
      }
      if node.elementIterator"formula" ).hasNext() ) {
        throw new MappingException(
          "column attribute may not be used together with <formula> subelement" );
      }

      Column column = new Column();
      column.setValuesimpleValue );
      bindColumnnode, column, isNullable );
      String logicalColumnName = mappings.getNamingStrategy().logicalColumnName(
          columnAttribute.getValue(), propertyPath
      );
      column.setNamemappings.getNamingStrategy().columnNamelogicalColumnName ) );
      if table != null ) {
        table.addColumncolumn )// table=null -> an association - fill
                                   // it in later
        //TODO fill in the mappings for table == null
        mappings.addColumnBindinglogicalColumnName, column, table );
      }
      simpleValue.addColumncolumn );
      bindIndexnode.attribute"index" ), table, column, mappings );
      bindUniqueKeynode.attribute"unique-key" ), table, column, mappings );
    }

    if autoColumn && simpleValue.getColumnSpan() == ) {
      Column column = new Column();
      column.setValuesimpleValue );
      bindColumnnode, column, isNullable );
      column.setNamemappings.getNamingStrategy().propertyToColumnNamepropertyPath ) );
      String logicalName = mappings.getNamingStrategy().logicalColumnNamenull, propertyPath );
      mappings.addColumnBindinglogicalName, column, table );
      /* TODO: joinKeyColumnName & foreignKeyColumnName should be called either here or at a
       * slightly higer level in the stack (to get all the information we need)
       * Right now HbmBinder does not support the
       */
      simpleValue.getTable().addColumncolumn );
      simpleValue.addColumncolumn );
      bindIndexnode.attribute"index" ), table, column, mappings );
      bindUniqueKeynode.attribute"unique-key" ), table, column, mappings );
    }

  }

  private static void bindIndex(Attribute indexAttribute, Table table, Column column, Mappings mappings) {
    if indexAttribute != null && table != null ) {
      StringTokenizer tokens = new StringTokenizerindexAttribute.getValue()", " );
      while tokens.hasMoreTokens() ) {
        table.getOrCreateIndextokens.nextToken() ).addColumncolumn );
      }
    }
  }

  private static void bindUniqueKey(Attribute uniqueKeyAttribute, Table table, Column column, Mappings mappings) {
    if uniqueKeyAttribute != null && table != null ) {
      StringTokenizer tokens = new StringTokenizeruniqueKeyAttribute.getValue()", " );
      while tokens.hasMoreTokens() ) {
        table.getOrCreateUniqueKeytokens.nextToken() ).addColumncolumn );
      }
    }
  }

  // automatically makes a column with the default name if none is specifed by XML
  public static void bindSimpleValue(Element node, SimpleValue simpleValue, boolean isNullable,
      String path, Mappings mappingsthrows MappingException {
    bindSimpleValueTypenode, simpleValue, mappings );

    bindColumnsOrFormulanode, simpleValue, path, isNullable, mappings );

    Attribute fkNode = node.attribute"foreign-key" );
    if fkNode != null simpleValue.setForeignKeyNamefkNode.getValue() );
  }

  private static void bindSimpleValueType(Element node, SimpleValue simpleValue, Mappings mappings)
      throws MappingException {
    String typeName = null;

    Properties parameters = new Properties();

    Attribute typeNode = node.attribute"type" );
    if typeNode == null typeNode = node.attribute"id-type" )// for an any
    if typeNode != null typeName = typeNode.getValue();

    Element typeChild = node.element"type" );
    if typeName == null && typeChild != null ) {
      typeName = typeChild.attribute"name" ).getValue();
      Iterator typeParameters = typeChild.elementIterator"param" );

      while typeParameters.hasNext() ) {
        Element paramElement = (ElementtypeParameters.next();
        parameters.setProperty(
            paramElement.attributeValue"name" ),
            paramElement.getTextTrim()
          );
      }
    }

    TypeDef typeDef = mappings.getTypeDeftypeName );
    if typeDef != null ) {
      typeName = typeDef.getTypeClass();
      // parameters on the property mapping should
      // override parameters in the typedef
      Properties allParameters = new Properties();
      allParameters.putAlltypeDef.getParameters() );
      allParameters.putAllparameters );
      parameters = allParameters;
    }

    if !parameters.isEmpty() ) simpleValue.setTypeParametersparameters );

    if typeName != null simpleValue.setTypeNametypeName );
  }

  public static void bindProperty(
      Element node,
          Property property,
          Mappings mappings,
      java.util.Map inheritedMetasthrows MappingException {

    String propName = node.attributeValue"name" );
    property.setNamepropName );
    String nodeName = node.attributeValue"node" );
    if (nodeName==nullnodeName = propName;
    property.setNodeNamenodeName );

    // TODO:
    //Type type = model.getValue().getType();
    //if (type==null) throw new MappingException(
    //"Could not determine a property type for: " + model.getName() );

    Attribute accessNode = node.attribute"access" );
    if accessNode != null ) {
      property.setPropertyAccessorNameaccessNode.getValue() );
    }
    else if node.getName().equals"properties" ) ) {
      property.setPropertyAccessorName"embedded" );
    }
    else {
      property.setPropertyAccessorNamemappings.getDefaultAccess() );
    }

    Attribute cascadeNode = node.attribute"cascade" );
    property.setCascadecascadeNode == null ? mappings.getDefaultCascade() : cascadeNode
      .getValue() );

    Attribute updateNode = node.attribute"update" );
    property.setUpdateableupdateNode == null || "true".equalsupdateNode.getValue() ) );

    Attribute insertNode = node.attribute"insert" );
    property.setInsertableinsertNode == null || "true".equalsinsertNode.getValue() ) );

    Attribute lockNode = node.attribute"optimistic-lock" );
    property.setOptimisticLockedlockNode == null || "true".equalslockNode.getValue() ) );

    Attribute generatedNode = node.attribute"generated" );
        String generationName = generatedNode == null null : generatedNode.getValue();
        PropertyGeneration generation = PropertyGeneration.parsegenerationName );
    property.setGenerationgeneration );

        if generation == PropertyGeneration.ALWAYS || generation == PropertyGeneration.INSERT ) {
          // generated properties can *never* be insertable...
          if property.isInsertable() ) {
            if insertNode == null ) {
              // insertable simply because that is the user did not specify
              // anything; just override it
          property.setInsertablefalse );
            }
            else {
              // the user specifically supplied insert="true",
              // which constitutes an illegal combo
          throw new MappingException(
              "cannot specify both insert=\"true\" and generated=\"" + generation.getName() +
              "\" for property: " +
              propName
          );
            }
          }

          // properties generated on update can never be updateable...
          if property.isUpdateable() && generation == PropertyGeneration.ALWAYS ) {
            if updateNode == null ) {
              // updateable only because the user did not specify 
              // anything; just override it
              property.setUpdateablefalse );
            }
            else {
              // the user specifically supplied update="true",
              // which constitutes an illegal combo
          throw new MappingException(
              "cannot specify both update=\"true\" and generated=\"" + generation.getName() +
              "\" for property: " +
              propName
          );
            }
          }
        }

    boolean isLazyable = "property".equalsnode.getName() ) ||
        "component".equalsnode.getName() ) ||
        "many-to-one".equalsnode.getName() ) ||
        "one-to-one".equalsnode.getName() ) ||
        "any".equalsnode.getName() );
    if isLazyable ) {
      Attribute lazyNode = node.attribute"lazy" );
      property.setLazylazyNode != null && "true".equalslazyNode.getValue() ) );
    }

    if log.isDebugEnabled() ) {
      String msg = "Mapped property: " + property.getName();
      String columns = columnsproperty.getValue() );
      if columns.length() msg += " -> " + columns;
      // TODO: this fails if we run with debug on!
      // if ( model.getType()!=null ) msg += ", type: " + model.getType().getName();
      log.debugmsg );
    }

    property.setMetaAttributesgetMetasnode, inheritedMetas ) );

  }

  private static String columns(Value val) {
    StringBuffer columns = new StringBuffer();
    Iterator iter = val.getColumnIterator();
    while iter.hasNext() ) {
      columns.append( ( (Selectableiter.next() ).getText() );
      if iter.hasNext() ) columns.append", " );
    }
    return columns.toString();
  }

  /**
   * Called for all collections
   */
  public static void bindCollection(Element node, Collection collection, String className,
      String path, Mappings mappings, java.util.Map inheritedMetasthrows MappingException {

    // ROLENAME
    collection.setRole(path);

    Attribute inverseNode = node.attribute"inverse" );
    if inverseNode != null ) {
      collection.setInverse"true".equalsinverseNode.getValue() ) );
    }

    Attribute mutableNode = node.attribute"mutable" );
    if mutableNode != null ) {
      collection.setMutable!"false".equalsmutableNode.getValue() ) );
    }

    Attribute olNode = node.attribute"optimistic-lock" );
    collection.setOptimisticLockedolNode == null || "true".equalsolNode.getValue() ) );

    Attribute orderNode = node.attribute"order-by" );
    if orderNode != null ) {
      if Environment.jvmSupportsLinkedHashCollections() || collection instanceof Bag ) ) {
        collection.setOrderByorderNode.getValue() );
      }
      else {
        log.warn"Attribute \"order-by\" ignored in JDK1.3 or less" );
      }
    }
    Attribute whereNode = node.attribute"where" );
    if whereNode != null ) {
      collection.setWherewhereNode.getValue() );
    }
    Attribute batchNode = node.attribute"batch-size" );
    if batchNode != null ) {
      collection.setBatchSizeInteger.parseIntbatchNode.getValue() ) );
    }

    String nodeName = node.attributeValue"node" );
    if nodeName == null nodeName = node.attributeValue"name" );
    collection.setNodeNamenodeName );
    String embed = node.attributeValue"embed-xml" );
    collection.setEmbeddedembed==null || "true".equals(embed) );


    // PERSISTER
    Attribute persisterNode = node.attribute"persister" );
    if persisterNode != null ) {
      try {
        collection.setCollectionPersisterClassReflectHelper.classForNamepersisterNode
          .getValue() ) );
      }
      catch (ClassNotFoundException cnfe) {
        throw new MappingException"Could not find collection persister class: "
          + persisterNode.getValue() );
      }
    }

    Attribute typeNode = node.attribute"collection-type" );
    if typeNode != null ) {
      String typeName = typeNode.getValue();
      TypeDef typeDef = mappings.getTypeDeftypeName );
      if typeDef != null ) {
        collection.setTypeNametypeDef.getTypeClass() );
        collection.setTypeParameterstypeDef.getParameters() );
      }
      else {
        collection.setTypeNametypeName );
      }
    }

    // FETCH STRATEGY

    initOuterJoinFetchSettingnode, collection );

    if "subselect".equalsnode.attributeValue("fetch") ) ) {
      collection.setSubselectLoadable(true);
      collection.getOwner().setSubselectLoadableCollections(true);
    }

    initLazinessnode, collection, mappings, "true", mappings.isDefaultLazy() );
    //TODO: suck this into initLaziness!
    if "extra".equalsnode.attributeValue("lazy") ) ) {
      collection.setLazy(true);
      collection.setExtraLazy(true);
    }

    Element oneToManyNode = node.element"one-to-many" );
    if oneToManyNode != null ) {
      OneToMany oneToMany = new OneToManycollection.getOwner() );
      collection.setElementoneToMany );
      bindOneToManyoneToManyNode, oneToMany, mappings );
      // we have to set up the table later!! yuck
    }
    else {
      // TABLE
      Attribute tableNode = node.attribute"table" );
      String tableName;
      if tableNode != null ) {
        tableName = mappings.getNamingStrategy().tableNametableNode.getValue() );
      }
      else {
        //tableName = mappings.getNamingStrategy().propertyToTableName( className, path );
        Table ownerTable = collection.getOwner().getTable();
        //TODO mappings.getLogicalTableName(ownerTable)
        String logicalOwnerTableName = ownerTable.getName();
        //FIXME we don't have the associated entity table name here, has to be done in a second pass
        tableName = mappings.getNamingStrategy().collectionTableName(
            collection.getOwner().getEntityName(),
            logicalOwnerTableName ,
            null,
            null,
            path
        );
      }
      Attribute schemaNode = node.attribute"schema" );
      String schema = schemaNode == null ?
          mappings.getSchemaName() : schemaNode.getValue();

      Attribute catalogNode = node.attribute"catalog" );
      String catalog = catalogNode == null ?
          mappings.getCatalogName() : catalogNode.getValue();

      Table table = mappings.addTable(
          schema,
          catalog,
          tableName,
          getSubselectnode ),
          false
        );
      collection.setCollectionTabletable );
      bindComment(table, node);

      log.info(
          "Mapping collection: " + collection.getRole() +
          " -> " + collection.getCollectionTable().getName()
        );
    }

    // SORT
    Attribute sortedAtt = node.attribute"sort" );
    // unsorted, natural, comparator.class.name
    if sortedAtt == null || sortedAtt.getValue().equals"unsorted" ) ) {
      collection.setSortedfalse );
    }
    else {
      collection.setSortedtrue );
      String comparatorClassName = sortedAtt.getValue();
      if !comparatorClassName.equals"natural" ) ) {
        collection.setComparatorClassName(comparatorClassName);
      }
    }

    // ORPHAN DELETE (used for programmer error detection)
    Attribute cascadeAtt = node.attribute"cascade" );
    if cascadeAtt != null && cascadeAtt.getValue().indexOf"delete-orphan" >= ) {
      collection.setOrphanDeletetrue );
    }

    // CUSTOM SQL
    handleCustomSQLnode, collection );
    // set up second pass
    if collection instanceof List ) {
      mappings.addSecondPassnew ListSecondPassnode, mappings, (Listcollection, inheritedMetas ) );
    }
    else if collection instanceof Map ) {
      mappings.addSecondPassnew MapSecondPassnode, mappings, (Mapcollection, inheritedMetas ) );
    }
    else if collection instanceof IdentifierCollection ) {
      mappings.addSecondPassnew IdentifierCollectionSecondPass(
          node,
          mappings,
          collection,
          inheritedMetas
        ) );
    }
    else {
      mappings.addSecondPassnew CollectionSecondPassnode, mappings, collection, inheritedMetas ) );
    }

    Iterator iter = node.elementIterator"filter" );
    while iter.hasNext() ) {
      final Element filter = (Elementiter.next();
      parseFilterfilter, collection, mappings );
    }

    Iterator tables = node.elementIterator"synchronize" );
    while tables.hasNext() ) {
      collection.getSynchronizedTables().add(
        ( (Elementtables.next() ).attributeValue"table" ) );
    }

    Element element = node.element"loader" );
    if element != null ) {
      collection.setLoaderNameelement.attributeValue"query-ref" ) );
    }

    collection.setReferencedPropertyNamenode.element"key" ).attributeValue"property-ref" ) );
  }

  private static void initLaziness(
      Element node,
      Fetchable fetchable,
      Mappings mappings,
      String proxyVal,
      boolean defaultLazy
  ) {
    Attribute lazyNode = node.attribute"lazy" );
    boolean isLazyTrue = lazyNode == null ?
        defaultLazy && fetchable.isLazy() //fetch="join" overrides default laziness
        lazyNode.getValue().equals(proxyVal)//fetch="join" overrides default laziness
    fetchable.setLazyisLazyTrue );
  }

  private static void initLaziness(
      Element node,
      ToOne fetchable,
      Mappings mappings,
      boolean defaultLazy
  ) {
    if "no-proxy".equalsnode.attributeValue"lazy" ) ) ) {
      fetchable.setUnwrapProxy(true);
      fetchable.setLazy(true);
      //TODO: better to degrade to lazy="false" if uninstrumented
    }
    else {
      initLaziness(node, fetchable, mappings, "proxy", defaultLazy);
    }
  }

  private static void bindColumnsOrFormula(Element node, SimpleValue simpleValue, String path,
      boolean isNullable, Mappings mappings) {
    Attribute formulaNode = node.attribute"formula" );
    if formulaNode != null ) {
      Formula f = new Formula();
      f.setFormulaformulaNode.getText() );
      simpleValue.addFormula);
    }
    else {
      bindColumnsnode, simpleValue, isNullable, true, path, mappings );
    }
  }

  private static void bindComment(Table table, Element node) {
    Element comment = node.element("comment");
    if (comment!=nulltable.setCommentcomment.getTextTrim() );
  }

  public static void bindManyToOne(Element node, ManyToOne manyToOne, String path,
      boolean isNullable, Mappings mappingsthrows MappingException {

    bindColumnsOrFormulanode, manyToOne, path, isNullable, mappings );
    initOuterJoinFetchSettingnode, manyToOne );
    initLazinessnode, manyToOne, mappings, true );

    Attribute ukName = node.attribute"property-ref" );
    if ukName != null ) {
      manyToOne.setReferencedPropertyNameukName.getValue() );
    }

    manyToOne.setReferencedEntityNamegetEntityNamenode, mappings ) );

    String embed = node.attributeValue"embed-xml" );
    manyToOne.setEmbeddedembed == null || "true".equalsembed ) );

    String notFound = node.attributeValue"not-found" );
    manyToOne.setIgnoreNotFound"ignore".equalsnotFound ) );

    ifukName != null && !manyToOne.isIgnoreNotFound() ) {
      if !node.getName().equals("many-to-many") ) { //TODO: really bad, evil hack to fix!!!
        mappings.addSecondPassnew ManyToOneSecondPass(manyToOne) );
      }
    }

    Attribute fkNode = node.attribute"foreign-key" );
    if fkNode != null manyToOne.setForeignKeyNamefkNode.getValue() );

    validateCascadenode, path );
  }

  private static void validateCascade(Element node, String path) {
    String cascade = node.attributeValue("cascade");
    if cascade!=null && cascade.indexOf("delete-orphan")>) {
      throw new MappingException("single-valued associations do not support orphan delete: " + path);
    }
  }

  public static void bindAny(Element node, Any any, boolean isNullable, Mappings mappings)
      throws MappingException {
    any.setIdentifierTypegetTypeFromXMLnode ) );
    Attribute metaAttribute = node.attribute"meta-type" );
    if metaAttribute != null ) {
      any.setMetaTypemetaAttribute.getValue() );

      Iterator iter = node.elementIterator"meta-value" );
      if iter.hasNext() ) {
        HashMap values = new HashMap();
        org.hibernate.type.Type metaType = TypeFactory.heuristicTypeany.getMetaType() );
        while iter.hasNext() ) {
          Element metaValue = (Elementiter.next();
          try {
            Object value = ( (DiscriminatorTypemetaType ).stringToObjectmetaValue
              .attributeValue"value" ) );
            String entityName = getClassNamemetaValue.attribute"class" ), mappings );
            values.putvalue, entityName );
          }
          catch (ClassCastException cce) {
            throw new MappingException"meta-type was not a DiscriminatorType: "
              + metaType.getName() );
          }
          catch (Exception e) {
            throw new MappingException"could not interpret meta-value", e );
          }
        }
        any.setMetaValuesvalues );
      }

    }

    bindColumnsnode, any, isNullable, false, null, mappings );
  }

  public static void bindOneToOne(Element node, OneToOne oneToOne, String path, boolean isNullable,
      Mappings mappingsthrows MappingException {

    bindColumnsnode, oneToOne, isNullable, false, null, mappings );

    Attribute constrNode = node.attribute"constrained" );
    boolean constrained = constrNode != null && constrNode.getValue().equals"true" );
    oneToOne.setConstrainedconstrained );

    oneToOne.setForeignKeyTypeconstrained ?
        ForeignKeyDirection.FOREIGN_KEY_FROM_PARENT :
        ForeignKeyDirection.FOREIGN_KEY_TO_PARENT );

    initOuterJoinFetchSettingnode, oneToOne );
    initLazinessnode, oneToOne, mappings, true );

    oneToOne.setEmbedded"true".equalsnode.attributeValue"embed-xml" ) ) );

    Attribute fkNode = node.attribute"foreign-key" );
    if fkNode != null oneToOne.setForeignKeyNamefkNode.getValue() );

    Attribute ukName = node.attribute"property-ref" );
    if ukName != null oneToOne.setReferencedPropertyNameukName.getValue() );

    oneToOne.setPropertyNamenode.attributeValue"name" ) );

    oneToOne.setReferencedEntityNamegetEntityNamenode, mappings ) );

    validateCascadenode, path );
  }

  public static void bindOneToMany(Element node, OneToMany oneToMany, Mappings mappings)
      throws MappingException {

    oneToMany.setReferencedEntityNamegetEntityNamenode, mappings ) );

    String embed = node.attributeValue"embed-xml" );
    oneToMany.setEmbeddedembed == null || "true".equalsembed ) );

    String notFound = node.attributeValue"not-found" );
    oneToMany.setIgnoreNotFound"ignore".equalsnotFound ) );

  }

  public static void bindColumn(Element node, Column column, boolean isNullable) {
    Attribute lengthNode = node.attribute"length" );
    if lengthNode != null column.setLengthInteger.parseIntlengthNode.getValue() ) );
    Attribute scalNode = node.attribute"scale" );
    if scalNode != null column.setScaleInteger.parseIntscalNode.getValue() ) );
    Attribute precNode = node.attribute"precision" );
    if precNode != null column.setPrecisionInteger.parseIntprecNode.getValue() ) );

    Attribute nullNode = node.attribute"not-null" );
    column.setNullablenullNode == null ? isNullable : nullNode.getValue().equals"false" ) );

    Attribute unqNode = node.attribute"unique" );
    if unqNode != null column.setUniqueunqNode.getValue().equals"true" ) );

    column.setCheckConstraintnode.attributeValue"check" ) );
    column.setDefaultValuenode.attributeValue"default" ) );

    Attribute typeNode = node.attribute"sql-type" );
    if typeNode != null column.setSqlTypetypeNode.getValue() );

    Element comment = node.element("comment");
    if (comment!=nullcolumn.setCommentcomment.getTextTrim() );

  }

  /**
   * Called for arrays and primitive arrays
   */
  public static void bindArray(Element node, Array array, String prefix, String path,
      Mappings mappings, java.util.Map inheritedMetasthrows MappingException {

    bindCollectionnode, array, prefix, path, mappings, inheritedMetas );

    Attribute att = node.attribute"element-class" );
    if att != null array.setElementClassNamegetClassNameatt, mappings ) );

  }

  private static Class reflectedPropertyClass(String className, String propertyName)
      throws MappingException {
    if className == null return null;
    return ReflectHelper.reflectedPropertyClassclassName, propertyName );
  }

  public static void bindComposite(Element node, Component component, String path,
      boolean isNullable, Mappings mappings, java.util.Map inheritedMetas)
      throws MappingException {
    bindComponent(
        node,
        component,
        null,
        null,
        path,
        isNullable,
        false,
        mappings,
        inheritedMetas,
        false
      );
  }

  public static void bindCompositeId(Element node, Component component,
      PersistentClass persistentClass, String propertyName, Mappings mappings,
      java.util.Map inheritedMetasthrows MappingException {

    component.setKeytrue );

    String path = StringHelper.qualify(
        persistentClass.getEntityName(),
        propertyName == null "id" : propertyName );

    bindComponent(
        node,
        component,
        persistentClass.getClassName(),
        propertyName,
        path,
        false,
        node.attribute"class" == null
            && propertyName == null,
        mappings,
        inheritedMetas,
        false
      );

    if "true".equalsnode.attributeValue("mapped") ) ) {
      if propertyName!=null ) {
        throw new MappingException("cannot combine mapped=\"true\" with specified name");
      }
      Component mapper = new Component(persistentClass);
      bindComponent(
          node,
          mapper,
          persistentClass.getClassName(),
          null,
          path,
          false,
          true,
          mappings,
          inheritedMetas,
          true
        );
      persistentClass.setIdentifierMapper(mapper);
      Property property = new Property();
      property.setName("_identifierMapper");
      property.setNodeName("id");
      property.setUpdateable(false);
      property.setInsertable(false);
      property.setValue(mapper);
      property.setPropertyAccessorName"embedded" );
      persistentClass.addProperty(property);
    }

  }

  public static void bindComponent(
      Element node,
      Component component,
      String ownerClassName,
      String parentProperty,
      String path,
      boolean isNullable,
      boolean isEmbedded,
      Mappings mappings,
      java.util.Map inheritedMetas,
      boolean isIdentifierMapperthrows MappingException {

    component.setEmbeddedisEmbedded );
    component.setRoleNamepath );

    inheritedMetas = getMetasnode, inheritedMetas );
    component.setMetaAttributesinheritedMetas );

    Attribute classNode = isIdentifierMapper ? null : node.attribute"class" );
    if classNode != null ) {
      component.setComponentClassNamegetClassNameclassNode, mappings ) );
    }
    else if "dynamic-component".equalsnode.getName() ) ) {
      component.setDynamictrue );
    }
    else if isEmbedded ) {
      // an "embedded" component (composite ids and unique)
      // note that this does not handle nested components
      if component.getOwner().hasPojoRepresentation() ) {
        component.setComponentClassNamecomponent.getOwner().getClassName() );
      }
      else {
        component.setDynamic(true);
      }
    }
    else {
      // todo : again, how *should* this work for non-pojo entities?
      if component.getOwner().hasPojoRepresentation() ) {
        Class reflectedClass = reflectedPropertyClassownerClassName, parentProperty );
        if reflectedClass != null ) {
          component.setComponentClassNamereflectedClass.getName() );
        }
      }
      else {
        component.setDynamic(true);
      }
    }

    String nodeName = node.attributeValue"node" );
    if nodeName == null nodeName = node.attributeValue"name" );
    if nodeName == null nodeName = component.getOwner().getNodeName();
    component.setNodeNamenodeName );

    Iterator iter = node.elementIterator();
    while iter.hasNext() ) {

      Element subnode = (Elementiter.next();
      String name = subnode.getName();
      String propertyName = getPropertyNamesubnode );
      String subpath = propertyName == null null : StringHelper
        .qualifypath, propertyName );

      CollectionType collectType = CollectionType.collectionTypeFromStringname );
      Value value = null;
      if collectType != null ) {
        Collection collection = collectType.create(
            subnode,
            subpath,
            component.getOwner(),
            mappings, inheritedMetas
          );
        mappings.addCollectioncollection );
        value = collection;
      }
      else if "many-to-one".equalsname || "key-many-to-one".equalsname ) ) {
        value = new ManyToOnecomponent.getTable() );
        String relativePath;
        if (isEmbedded) {
          relativePath = propertyName;
        }
        else {
          relativePath = subpath.substringcomponent.getOwner().getEntityName().length() );
        }
        bindManyToOnesubnode, (ManyToOnevalue, relativePath, isNullable, mappings );
      }
      else if "one-to-one".equalsname ) ) {
        value = new OneToOnecomponent.getTable(), component.getOwner() );
        String relativePath;
        if (isEmbedded) {
          relativePath = propertyName;
        }
        else {
          relativePath = subpath.substringcomponent.getOwner().getEntityName().length() );
        }
        bindOneToOnesubnode, (OneToOnevalue, relativePath, isNullable, mappings );
      }
      else if "any".equalsname ) ) {
        value = new Anycomponent.getTable() );
        bindAnysubnode, (Anyvalue, isNullable, mappings );
      }
      else if "property".equalsname || "key-property".equalsname ) ) {
        value = new SimpleValuecomponent.getTable() );
        String relativePath;
        if (isEmbedded) {
          relativePath = propertyName;
        }
        else {
          relativePath = subpath.substringcomponent.getOwner().getEntityName().length() );
        }
        bindSimpleValuesubnode, (SimpleValuevalue, isNullable, relativePath, mappings );
      }
      else if "component".equalsname )
        || "dynamic-component".equalsname )
        || "nested-composite-element".equalsname ) ) {
        value = new Componentcomponent )// a nested composite element
        bindComponent(
            subnode,
            (Componentvalue,
            component.getComponentClassName(),
            propertyName,
            subpath,
            isNullable,
            isEmbedded,
            mappings,
            inheritedMetas,
            isIdentifierMapper
          );
      }
      else if "parent".equalsname ) ) {
        component.setParentPropertypropertyName );
      }

      if value != null ) {
        Property property = createPropertyvalue, propertyName, component
          .getComponentClassName(), subnode, mappings, inheritedMetas );
        if (isIdentifierMapper) {
          property.setInsertable(false);
          property.setUpdateable(false);
        }
        component.addPropertyproperty );
      }
    }

    if "true".equalsnode.attributeValue"unique" ) ) ) {
      iter = component.getColumnIterator();
      ArrayList cols = new ArrayList();
      while iter.hasNext() ) {
        cols.additer.next() );
      }
      component.getOwner().getTable().createUniqueKeycols );
    }

    iter = node.elementIterator"tuplizer" );
    while iter.hasNext() ) {
      final Element tuplizerElem = Element iter.next();
      EntityMode mode = EntityMode.parsetuplizerElem.attributeValue"entity-mode" ) );
      component.addTuplizermode, tuplizerElem.attributeValue"class" ) );
    }
  }

  public static String getTypeFromXML(Element nodethrows MappingException {
    // TODO: handle TypeDefs
    Attribute typeNode = node.attribute"type" );
    if typeNode == null typeNode = node.attribute"id-type" )// for an any
    if typeNode == null return null// we will have to use reflection
    return typeNode.getValue();
  }

  private static void initOuterJoinFetchSetting(Element node, Fetchable model) {
    Attribute fetchNode = node.attribute"fetch" );
    final FetchMode fetchStyle;
    boolean lazy = true;
    if fetchNode == null ) {
      Attribute jfNode = node.attribute"outer-join" );
      if jfNode == null ) {
        if "many-to-many".equalsnode.getName() ) ) {
          //NOTE SPECIAL CASE:
          // default to join and non-lazy for the "second join"
          // of the many-to-many
          lazy = false;
          fetchStyle = FetchMode.JOIN;
        }
        else if "one-to-one".equalsnode.getName() ) ) {
          //NOTE SPECIAL CASE:
          // one-to-one constrained=false cannot be proxied,
          // so default to join and non-lazy
          lazy = ( (OneToOnemodel ).isConstrained();
          fetchStyle = lazy ? FetchMode.DEFAULT : FetchMode.JOIN;
        }
        else {
          fetchStyle = FetchMode.DEFAULT;
        }
      }
      else {
        // use old (HB 2.1) defaults if outer-join is specified
        String eoj = jfNode.getValue();
        if "auto".equalseoj ) ) {
          fetchStyle = FetchMode.DEFAULT;
        }
        else {
          boolean join = "true".equalseoj );
          fetchStyle = join ? FetchMode.JOIN : FetchMode.SELECT;
        }
      }
    }
    else {
      boolean join = "join".equalsfetchNode.getValue() );
      //lazy = !join;
      fetchStyle = join ? FetchMode.JOIN : FetchMode.SELECT;
    }
    model.setFetchModefetchStyle );
    model.setLazy(lazy);
  }

  private static void makeIdentifier(Element node, SimpleValue model, Mappings mappings) {

    // GENERATOR
    Element subnode = node.element"generator" );
    if subnode != null ) {
      model.setIdentifierGeneratorStrategysubnode.attributeValue"class" ) );

      Properties params = new Properties();

      if mappings.getSchemaName() != null ) {
        params.setPropertyPersistentIdentifierGenerator.SCHEMA, mappings.getSchemaName() );
      }
      if mappings.getCatalogName() != null ) {
        params.setPropertyPersistentIdentifierGenerator.CATALOG, mappings.getCatalogName() );
      }

      Iterator iter = subnode.elementIterator"param" );
      while iter.hasNext() ) {
        Element childNode = (Elementiter.next();
        params.setPropertychildNode.attributeValue"name" ), childNode.getText() );
      }

      model.setIdentifierGeneratorPropertiesparams );
    }

    model.getTable().setIdentifierValuemodel );

    // ID UNSAVED-VALUE
    Attribute nullValueNode = node.attribute"unsaved-value" );
    if nullValueNode != null ) {
      model.setNullValuenullValueNode.getValue() );
    }
    else {
      if "assigned".equalsmodel.getIdentifierGeneratorStrategy() ) ) {
        model.setNullValue"undefined" );
      }
      else {
        model.setNullValuenull );
      }
    }
  }

  private static final void makeVersion(Element node, SimpleValue model) {

    // VERSION UNSAVED-VALUE
    Attribute nullValueNode = node.attribute"unsaved-value" );
    if nullValueNode != null ) {
      model.setNullValuenullValueNode.getValue() );
    }
    else {
      model.setNullValue"undefined" );
    }

  }

  protected static void createClassProperties(Element node, PersistentClass persistentClass,
      Mappings mappings, java.util.Map inheritedMetasthrows MappingException {
    createClassProperties(node, persistentClass, mappings, inheritedMetas, null, true, true, false);
  }

  protected static void createClassProperties(Element node, PersistentClass persistentClass,
      Mappings mappings, java.util.Map inheritedMetas, UniqueKey uniqueKey,
      boolean mutable, boolean nullable, boolean naturalIdthrows MappingException {

    String entityName = persistentClass.getEntityName();
    Table table = persistentClass.getTable();

    Iterator iter = node.elementIterator();
    while iter.hasNext() ) {
      Element subnode = (Elementiter.next();
      String name = subnode.getName();
      String propertyName = subnode.attributeValue"name" );

      CollectionType collectType = CollectionType.collectionTypeFromStringname );
      Value value = null;
      if collectType != null ) {
        Collection collection = collectType.create(
            subnode,
            StringHelper.qualifyentityName, propertyName ),
            persistentClass,
            mappings, inheritedMetas
          );
        mappings.addCollectioncollection );
        value = collection;
      }
      else if "many-to-one".equalsname ) ) {
        value = new ManyToOnetable );
        bindManyToOnesubnode, (ManyToOnevalue, propertyName, nullable, mappings );
      }
      else if "any".equalsname ) ) {
        value = new Anytable );
        bindAnysubnode, (Anyvalue, nullable, mappings );
      }
      else if "one-to-one".equalsname ) ) {
        value = new OneToOnetable, persistentClass );
        bindOneToOnesubnode, (OneToOnevalue, propertyName, true, mappings );
      }
      else if "property".equalsname ) ) {
        value = new SimpleValuetable );
        bindSimpleValuesubnode, (SimpleValuevalue, nullable, propertyName, mappings );
      }
      else if "component".equalsname )
        || "dynamic-component".equalsname )
        || "properties".equalsname ) ) {
        String subpath = StringHelper.qualifyentityName, propertyName );
        value = new ComponentpersistentClass );

        bindComponent(
            subnode,
            (Componentvalue,
            persistentClass.getClassName(),
            propertyName,
            subpath,
            true,
            "properties".equalsname ),
            mappings,
            inheritedMetas,
            false
          );
      }
      else if "join".equalsname ) ) {
        Join join = new Join();
        join.setPersistentClasspersistentClass );
        bindJoinsubnode, join, mappings, inheritedMetas );
        persistentClass.addJoinjoin );
      }
      else if "subclass".equalsname ) ) {
        handleSubclasspersistentClass, mappings, subnode, inheritedMetas );
      }
      else if "joined-subclass".equalsname ) ) {
        handleJoinedSubclasspersistentClass, mappings, subnode, inheritedMetas );
      }
      else if "union-subclass".equalsname ) ) {
        handleUnionSubclasspersistentClass, mappings, subnode, inheritedMetas );
      }
      else if "filter".equalsname ) ) {
        parseFiltersubnode, persistentClass, mappings );
      }
      else if "natural-id".equalsname ) ) {
        UniqueKey uk = new UniqueKey();
        uk.setName("_UniqueKey");
        uk.setTable(table);
        //by default, natural-ids are "immutable" (constant)
        boolean mutableId = "true".equalssubnode.attributeValue("mutable") );
        createClassProperties(
            subnode,
            persistentClass,
            mappings,
            inheritedMetas,
            uk,
            mutableId,
            false,
            true
          );
        table.addUniqueKey(uk);
      }
      else if "query".equals(name) ) {
        bindNamedQuery(subnode, persistentClass.getEntityName(), mappings);
      }
      else if "sql-query".equals(name) ) {
        bindNamedSQLQuery(subnode, persistentClass.getEntityName(), mappings);
      }
      else if "resultset".equals(name) ) {
        bindResultSetMappingDefinitionsubnode, persistentClass.getEntityName(), mappings );
      }

      if value != null ) {
        Property property = createPropertyvalue, propertyName, persistentClass
          .getClassName(), subnode, mappings, inheritedMetas );
        if !mutable property.setUpdateable(false);
        if naturalId property.setNaturalIdentifier(true);
        persistentClass.addPropertyproperty );
        if uniqueKey!=null uniqueKey.addColumnsproperty.getColumnIterator() );
      }

    }
  }

  private static Property createProperty(
      final Value value,
          final String propertyName,
      final String className,
          final Element subnode,
          final Mappings mappings,
      java.util.Map inheritedMetasthrows MappingException {

    if StringHelper.isEmptypropertyName ) ) {
      throw new MappingExceptionsubnode.getName() " mapping must defined a name attribute [" + className + "]" );
    }

    value.setTypeUsingReflectionclassName, propertyName );

    // this is done here 'cos we might only know the type here (ugly!)
    // TODO: improve this a lot:
    if value instanceof ToOne ) {
      ToOne toOne = (ToOnevalue;
      String propertyRef = toOne.getReferencedPropertyName();
      if propertyRef != null ) {
        mappings.addUniquePropertyReferencetoOne.getReferencedEntityName(), propertyRef );
      }
    }
    else if value instanceof Collection ) {
      Collection coll = (Collectionvalue;
      String propertyRef = coll.getReferencedPropertyName();
      // not necessarily a *unique* property reference
      if propertyRef != null ) {
        mappings.addPropertyReferencecoll.getOwnerEntityName(), propertyRef );
      }
    }

    value.createForeignKey();
    Property prop = new Property();
    prop.setValuevalue );
    bindPropertysubnode, prop, mappings, inheritedMetas );
    return prop;
  }

  private static void handleUnionSubclass(PersistentClass model, Mappings mappings,
      Element subnode, java.util.Map inheritedMetasthrows MappingException {
    UnionSubclass subclass = new UnionSubclassmodel );
    bindUnionSubclasssubnode, subclass, mappings, inheritedMetas );
    model.addSubclasssubclass );
    mappings.addClasssubclass );
  }

  private static void handleJoinedSubclass(PersistentClass model, Mappings mappings,
      Element subnode, java.util.Map inheritedMetasthrows MappingException {
    JoinedSubclass subclass = new JoinedSubclassmodel );
    bindJoinedSubclasssubnode, subclass, mappings, inheritedMetas );
    model.addSubclasssubclass );
    mappings.addClasssubclass );
  }

  private static void handleSubclass(PersistentClass model, Mappings mappings, Element subnode,
      java.util.Map inheritedMetasthrows MappingException {
    Subclass subclass = new SingleTableSubclassmodel );
    bindSubclasssubnode, subclass, mappings, inheritedMetas );
    model.addSubclasssubclass );
    mappings.addClasssubclass );
  }

  /**
   * Called for Lists, arrays, primitive arrays
   */
  public static void bindListSecondPass(Element node, List list, java.util.Map classes,
      Mappings mappings, java.util.Map inheritedMetasthrows MappingException {

    bindCollectionSecondPassnode, list, classes, mappings, inheritedMetas );

    Element subnode = node.element"list-index" );
    if subnode == null subnode = node.element"index" );
    SimpleValue iv = new SimpleValuelist.getCollectionTable() );
    bindSimpleValue(
        subnode,
        iv,
        list.isOneToMany(),
        IndexedCollection.DEFAULT_INDEX_COLUMN_NAME,
        mappings
      );
    iv.setTypeName"integer" );
    list.setIndexiv );
    String baseIndex = subnode.attributeValue"base" );
    if baseIndex != null list.setBaseIndexInteger.parseIntbaseIndex ) );
    list.setIndexNodeNamesubnode.attributeValue("node") );

    if list.isOneToMany() && !list.getKey().isNullable() && !list.isInverse() ) {
      String entityName = ( (OneToManylist.getElement() ).getReferencedEntityName();
      PersistentClass referenced = mappings.getClassentityName );
      IndexBackref ib = new IndexBackref();
      ib.setName'_' + node.attributeValue"name" "IndexBackref" );
      ib.setUpdateablefalse );
      ib.setSelectablefalse );
      ib.setCollectionRolelist.getRole() );
      ib.setEntityNamelist.getOwner().getEntityName() );
      ib.setValuelist.getIndex() );
      // ( (Column) ( (SimpleValue) ic.getIndex() ).getColumnIterator().next()
      // ).setNullable(false);
      referenced.addPropertyib );
    }
  }

  public static void bindIdentifierCollectionSecondPass(Element node,
      IdentifierCollection collection, java.util.Map persistentClasses, Mappings mappings,
      java.util.Map inheritedMetasthrows MappingException {

    bindCollectionSecondPassnode, collection, persistentClasses, mappings, inheritedMetas );

    Element subnode = node.element"collection-id" );
    SimpleValue id = new SimpleValuecollection.getCollectionTable() );
    bindSimpleValue(
        subnode,
        id,
        false,
        IdentifierCollection.DEFAULT_IDENTIFIER_COLUMN_NAME,
        mappings
      );
    collection.setIdentifierid );
    makeIdentifiersubnode, id, mappings );

  }

  /**
   * Called for Maps
   */
  public static void bindMapSecondPass(Element node, Map map, java.util.Map classes,
      Mappings mappings, java.util.Map inheritedMetasthrows MappingException {

    bindCollectionSecondPassnode, map, classes, mappings, inheritedMetas );

    Iterator iter = node.elementIterator();
    while iter.hasNext() ) {
      Element subnode = (Elementiter.next();
      String name = subnode.getName();

      if "index".equalsname || "map-key".equalsname ) ) {
        SimpleValue value = new SimpleValuemap.getCollectionTable() );
        bindSimpleValue(
            subnode,
            value,
            map.isOneToMany(),
            IndexedCollection.DEFAULT_INDEX_COLUMN_NAME,
            mappings
          );
        if !value.isTypeSpecified() ) {
          throw new MappingException"map index element must specify a type: "
            + map.getRole() );
        }
        map.setIndexvalue );
        map.setIndexNodeNamesubnode.attributeValue("node") );
      }
      else if "index-many-to-many".equalsname || "map-key-many-to-many".equalsname ) ) {
        ManyToOne mto = new ManyToOnemap.getCollectionTable() );
        bindManyToOne(
            subnode,
            mto,
            IndexedCollection.DEFAULT_INDEX_COLUMN_NAME,
            map.isOneToMany(),
            mappings
          );
        map.setIndexmto );

      }
      else if "composite-index".equalsname || "composite-map-key".equalsname ) ) {
        Component component = new Componentmap );
        bindComposite(
            subnode,
            component,
            map.getRole() ".index",
            map.isOneToMany(),
            mappings,
            inheritedMetas
          );
        map.setIndexcomponent );
      }
      else if "index-many-to-any".equalsname ) ) {
        Any any = new Anymap.getCollectionTable() );
        bindAnysubnode, any, map.isOneToMany(), mappings );
        map.setIndexany );
      }
    }

    // TODO: this is a bit of copy/paste from IndexedCollection.createPrimaryKey()
    boolean indexIsFormula = false;
    Iterator colIter = map.getIndex().getColumnIterator();
    while colIter.hasNext() ) {
      if ( ( (SelectablecolIter.next() ).isFormula() ) indexIsFormula = true;
    }

    if map.isOneToMany() && !map.getKey().isNullable() && !map.isInverse() && !indexIsFormula ) {
      String entityName = ( (OneToManymap.getElement() ).getReferencedEntityName();
      PersistentClass referenced = mappings.getClassentityName );
      IndexBackref ib = new IndexBackref();
      ib.setName'_' + node.attributeValue"name" "IndexBackref" );
      ib.setUpdateablefalse );
      ib.setSelectablefalse );
      ib.setCollectionRolemap.getRole() );
      ib.setEntityNamemap.getOwner().getEntityName() );
      ib.setValuemap.getIndex() );
      // ( (Column) ( (SimpleValue) ic.getIndex() ).getColumnIterator().next()
      // ).setNullable(false);
      referenced.addPropertyib );
    }
  }

  /**
   * Called for all collections
   */
  public static void bindCollectionSecondPass(Element node, Collection collection,
      java.util.Map persistentClasses, Mappings mappings, java.util.Map inheritedMetas)
      throws MappingException {

    if collection.isOneToMany() ) {
      OneToMany oneToMany = (OneToManycollection.getElement();
      String assocClass = oneToMany.getReferencedEntityName();
      PersistentClass persistentClass = (PersistentClasspersistentClasses.getassocClass );
      if persistentClass == null ) {
        throw new MappingException"Association references unmapped class: " + assocClass );
      }
      oneToMany.setAssociatedClasspersistentClass );
      collection.setCollectionTablepersistentClass.getTable() );

      log.info(
          "Mapping collection: " + collection.getRole() +
          " -> " + collection.getCollectionTable().getName()
        );
    }

    // CHECK
    Attribute chNode = node.attribute"check" );
    if chNode != null ) {
      collection.getCollectionTable().addCheckConstraintchNode.getValue() );
    }

    // contained elements:
    Iterator iter = node.elementIterator();
    while iter.hasNext() ) {
      Element subnode = (Elementiter.next();
      String name = subnode.getName();

      if "key".equalsname ) ) {
        KeyValue keyVal;
        String propRef = collection.getReferencedPropertyName();
        if propRef == null ) {
          keyVal = collection.getOwner().getIdentifier();
        }
        else {
          keyVal = (KeyValuecollection.getOwner().getReferencedPropertypropRef ).getValue();
        }
        SimpleValue key = new DependantValuecollection.getCollectionTable(), keyVal );
        key.setCascadeDeleteEnabled"cascade"
          .equalssubnode.attributeValue"on-delete" ) ) );
        bindSimpleValue(
            subnode,
            key,
            collection.isOneToMany(),
            Collection.DEFAULT_KEY_COLUMN_NAME,
            mappings
          );
        collection.setKeykey );

        Attribute notNull = subnode.attribute"not-null" );
        ( (DependantValuekey ).setNullablenotNull == null
          || notNull.getValue().equals"false" ) );
        Attribute updateable = subnode.attribute"update" );
        ( (DependantValuekey ).setUpdateableupdateable == null
          || updateable.getValue().equals"true" ) );

      }
      else if "element".equalsname ) ) {
        SimpleValue elt = new SimpleValuecollection.getCollectionTable() );
        collection.setElementelt );
        bindSimpleValue(
            subnode,
            elt,
            true,
            Collection.DEFAULT_ELEMENT_COLUMN_NAME,
            mappings
          );
      }
      else if "many-to-many".equalsname ) ) {
        ManyToOne element = new ManyToOnecollection.getCollectionTable() );
        collection.setElementelement );
        bindManyToOne(
            subnode,
            element,
            Collection.DEFAULT_ELEMENT_COLUMN_NAME,
            false,
            mappings
          );
        bindManyToManySubelementscollection, subnode, mappings );
      }
      else if "composite-element".equalsname ) ) {
        Component element = new Componentcollection );
        collection.setElementelement );
        bindComposite(
            subnode,
            element,
            collection.getRole() ".element",
            true,
            mappings,
            inheritedMetas
          );
      }
      else if "many-to-any".equalsname ) ) {
        Any element = new Anycollection.getCollectionTable() );
        collection.setElementelement );
        bindAnysubnode, element, true, mappings );
      }
      else if "cache".equalsname ) ) {
        collection.setCacheConcurrencyStrategysubnode.attributeValue"usage" ) );
        collection.setCacheRegionNamesubnode.attributeValue"region" ) );
      }

      String nodeName = subnode.attributeValue"node" );
      if nodeName != null collection.setElementNodeNamenodeName );

    }

    if collection.isOneToMany()
      && !collection.isInverse()
      && !collection.getKey().isNullable() ) {
      // for non-inverse one-to-many, with a not-null fk, add a backref!
      String entityName = ( (OneToManycollection.getElement() ).getReferencedEntityName();
      PersistentClass referenced = mappings.getClassentityName );
      Backref prop = new Backref();
      prop.setName'_' + node.attributeValue"name" "Backref" );
      prop.setUpdateablefalse );
      prop.setSelectablefalse );
      prop.setCollectionRolecollection.getRole() );
      prop.setEntityNamecollection.getOwner().getEntityName() );
      prop.setValuecollection.getKey() );
      referenced.addPropertyprop );
    }
  }

  private static void bindManyToManySubelements(
          Collection collection,
          Element manyToManyNode,
          Mappings modelthrows MappingException {
    // Bind the where
    Attribute where = manyToManyNode.attribute"where" );
    String whereCondition = where == null null : where.getValue();
    collection.setManyToManyWherewhereCondition );

    // Bind the order-by
    Attribute order = manyToManyNode.attribute"order-by" );
    String orderFragment = order == null null : order.getValue();
    collection.setManyToManyOrderingorderFragment );

    // Bind the filters
    Iterator filters = manyToManyNode.elementIterator"filter" );
    if ( ( filters.hasNext() || whereCondition != null &&
            collection.getFetchMode() == FetchMode.JOIN &&
            collection.getElement().getFetchMode() != FetchMode.JOIN ) {
      throw new MappingException(
              "many-to-many defining filter or where without join fetching " +
              "not valid within collection using join fetching [" + collection.getRole() "]"
        );
    }
    while filters.hasNext() ) {
      final Element filterElement = Element filters.next();
      final String name = filterElement.attributeValue"name" );
      String condition = filterElement.getTextTrim();
      if StringHelper.isEmpty(condition) ) condition = filterElement.attributeValue"condition" );
      if StringHelper.isEmpty(condition) ) {
        condition = model.getFilterDefinition(name).getDefaultFilterCondition();
      }
      if condition==null) {
        throw new MappingException("no filter condition found for filter: " + name);
      }
      log.debug(
          "Applying many-to-many filter [" + name +
          "] as [" + condition +
          "] to role [" + collection.getRole() "]"
        );
      collection.addManyToManyFiltername, condition );
    }
  }

  public static final FlushMode getFlushMode(String flushMode) {
    if flushMode == null ) {
      return null;
    }
    else if "auto".equalsflushMode ) ) {
      return FlushMode.AUTO;
    }
    else if "commit".equalsflushMode ) ) {
      return FlushMode.COMMIT;
    }
    else if "never".equalsflushMode ) ) {
      return FlushMode.NEVER;
    }
    else if "manual".equalsflushMode ) ) {
      return FlushMode.MANUAL;
    }
    else if "always".equalsflushMode ) ) {
      return FlushMode.ALWAYS;
    }
    else {
      throw new MappingException"unknown flushmode" );
    }
  }

  private static void bindNamedQuery(Element queryElem, String path, Mappings mappings) {
    String queryName = queryElem.attributeValue"name" );
    if (path!=nullqueryName = path + '.' + queryName;
    String query = queryElem.getText();
    log.debug"Named query: " + queryName + " -> " + query );

    boolean cacheable = "true".equalsqueryElem.attributeValue"cacheable" ) );
    String region = queryElem.attributeValue"cache-region" );
    Attribute tAtt = queryElem.attribute"timeout" );
    Integer timeout = tAtt == null null new IntegertAtt.getValue() );
    Attribute fsAtt = queryElem.attribute"fetch-size" );
    Integer fetchSize = fsAtt == null null new IntegerfsAtt.getValue() );
    Attribute roAttr = queryElem.attribute"read-only" );
    boolean readOnly = roAttr != null && "true".equalsroAttr.getValue() );
    Attribute cacheModeAtt = queryElem.attribute"cache-mode" );
    String cacheMode = cacheModeAtt == null null : cacheModeAtt.getValue();
    Attribute cmAtt = queryElem.attribute"comment" );
    String comment = cmAtt == null null : cmAtt.getValue();

    NamedQueryDefinition namedQuery = new NamedQueryDefinition(
        query,
        cacheable,
        region,
        timeout,
        fetchSize,
        getFlushModequeryElem.attributeValue"flush-mode" ) ) ,
        getCacheModecacheMode ),
        readOnly,
        comment,
        getParameterTypes(queryElem)
      );

    mappings.addQueryqueryName, namedQuery );
  }

  public static CacheMode getCacheMode(String cacheMode) {
    if (cacheMode == nullreturn null;
    if "get".equalscacheMode ) ) return CacheMode.GET;
    if "ignore".equalscacheMode ) ) return CacheMode.IGNORE;
    if "normal".equalscacheMode ) ) return CacheMode.NORMAL;
    if "put".equalscacheMode ) ) return CacheMode.PUT;
    if "refresh".equalscacheMode ) ) return CacheMode.REFRESH;
    throw new MappingException("Unknown Cache Mode: " + cacheMode);
  }

  public static java.util.Map getParameterTypes(Element queryElem) {
    java.util.Map result = new SequencedHashMap();
    Iterator iter = queryElem.elementIterator("query-param");
    while iter.hasNext() ) {
      Element element = (Elementiter.next();
      result.put(
          element.attributeValue("name"),
          element.attributeValue("type"
        );
    }
    return result;
  }

  private static void bindResultSetMappingDefinition(Element resultSetElem, String path, Mappings mappings) {
    mappings.addSecondPassnew ResultSetMappingSecondPassresultSetElem, path, mappings ) );
  }

  private static void bindNamedSQLQuery(Element queryElem, String path, Mappings mappings) {
    mappings.addSecondPassnew NamedSQLQuerySecondPassqueryElem, path, mappings ) );
  }

  private static String getPropertyName(Element node) {
    return node.attributeValue"name" );
  }

  private static PersistentClass getSuperclass(Mappings mappings, Element subnode)
      throws MappingException {
    String extendsName = subnode.attributeValue"extends" );
    PersistentClass superModel = mappings.getClassextendsName );
    if superModel == null ) {
      String qualifiedExtendsName = getClassNameextendsName, mappings );
      superModel = mappings.getClassqualifiedExtendsName );
    }

    if superModel == null ) {
      throw new MappingException"Cannot extend unmapped class " + extendsName );
    }
    return superModel;
  }

  static class CollectionSecondPass extends org.hibernate.cfg.CollectionSecondPass {
    Element node;

    CollectionSecondPass(Element node, Mappings mappings, Collection collection, java.util.Map inheritedMetas) {
      super(mappings, collection, inheritedMetas);
      this.node = node;
    }

    public void secondPass(java.util.Map persistentClasses, java.util.Map inheritedMetas)
        throws MappingException {
      HbmBinder.bindCollectionSecondPass(
          node,
          collection,
          persistentClasses,
          mappings,
          inheritedMetas
        );
    }
  }

  static class IdentifierCollectionSecondPass extends CollectionSecondPass {
    IdentifierCollectionSecondPass(Element node, Mappings mappings, Collection collection, java.util.Map inheritedMetas) {
      supernode, mappings, collection, inheritedMetas );
    }

    public void secondPass(java.util.Map persistentClasses, java.util.Map inheritedMetas)
        throws MappingException {
      HbmBinder.bindIdentifierCollectionSecondPass(
          node,
          (IdentifierCollectioncollection,
          persistentClasses,
          mappings,
          inheritedMetas 
        );
    }

  }

  static class MapSecondPass extends CollectionSecondPass {
    MapSecondPass(Element node, Mappings mappings, Map collection, java.util.Map inheritedMetas) {
      supernode, mappings, collection, inheritedMetas );
    }

    public void secondPass(java.util.Map persistentClasses, java.util.Map inheritedMetas)
        throws MappingException {
      HbmBinder.bindMapSecondPass(
          node,
          (Mapcollection,
          persistentClasses,
          mappings,
          inheritedMetas 
        );
    }

  }


  static class ManyToOneSecondPass implements SecondPass {
    private final ManyToOne manyToOne;

    ManyToOneSecondPass(ManyToOne manyToOne) {
      this.manyToOne = manyToOne;
    }

    public void doSecondPass(java.util.Map persistentClassesthrows MappingException {
      manyToOne.createPropertyRefConstraints(persistentClasses);
    }

  }
  
  static class ListSecondPass extends CollectionSecondPass {
    ListSecondPass(Element node, Mappings mappings, List collection, java.util.Map inheritedMetas) {
      supernode, mappings, collection, inheritedMetas );
    }

    public void secondPass(java.util.Map persistentClasses, java.util.Map inheritedMetas)
        throws MappingException {
      HbmBinder.bindListSecondPass(
          node,
          (Listcollection,
          persistentClasses,
          mappings,
          inheritedMetas 
        );
    }

  }

  // This inner class implements a case statement....perhaps im being a bit over-clever here
  abstract static class CollectionType {
    private String xmlTag;

    public abstract Collection create(Element node, String path, PersistentClass owner,
        Mappings mappings, java.util.Map inheritedMetasthrows MappingException;

    CollectionType(String xmlTag) {
      this.xmlTag = xmlTag;
    }

    public String toString() {
      return xmlTag;
    }

    private static final CollectionType MAP = new CollectionType"map" ) {
      public Collection create(Element node, String path, PersistentClass owner,
          Mappings mappings, java.util.Map inheritedMetasthrows MappingException {
        Map map = new Mapowner );
        bindCollectionnode, map, owner.getEntityName(), path, mappings, inheritedMetas );
        return map;
      }
    };
    private static final CollectionType SET = new CollectionType"set" ) {
      public Collection create(Element node, String path, PersistentClass owner,
          Mappings mappings, java.util.Map inheritedMetasthrows MappingException {
        Set set = new Setowner );
        bindCollectionnode, set, owner.getEntityName(), path, mappings, inheritedMetas );
        return set;
      }
    };
    private static final CollectionType LIST = new CollectionType"list" ) {
      public Collection create(Element node, String path, PersistentClass owner,
          Mappings mappings, java.util.Map inheritedMetasthrows MappingException {
        List list = new Listowner );
        bindCollectionnode, list, owner.getEntityName(), path, mappings, inheritedMetas );
        return list;
      }
    };
    private static final CollectionType BAG = new CollectionType"bag" ) {
      public Collection create(Element node, String path, PersistentClass owner,
          Mappings mappings, java.util.Map inheritedMetasthrows MappingException {
        Bag bag = new Bagowner );
        bindCollectionnode, bag, owner.getEntityName(), path, mappings, inheritedMetas );
        return bag;
      }
    };
    private static final CollectionType IDBAG = new CollectionType"idbag" ) {
      public Collection create(Element node, String path, PersistentClass owner,
          Mappings mappings, java.util.Map inheritedMetasthrows MappingException {
        IdentifierBag bag = new IdentifierBagowner );
        bindCollectionnode, bag, owner.getEntityName(), path, mappings, inheritedMetas );
        return bag;
      }
    };
    private static final CollectionType ARRAY = new CollectionType"array" ) {
      public Collection create(Element node, String path, PersistentClass owner,
          Mappings mappings, java.util.Map inheritedMetasthrows MappingException {
        Array array = new Arrayowner );
        bindArraynode, array, owner.getEntityName(), path, mappings, inheritedMetas );
        return array;
      }
    };
    private static final CollectionType PRIMITIVE_ARRAY = new CollectionType"primitive-array" ) {
      public Collection create(Element node, String path, PersistentClass owner,
          Mappings mappings, java.util.Map inheritedMetasthrows MappingException {
        PrimitiveArray array = new PrimitiveArrayowner );
        bindArraynode, array, owner.getEntityName(), path, mappings, inheritedMetas );
        return array;
      }
    };
    private static final HashMap INSTANCES = new HashMap();

    static {
      INSTANCES.putMAP.toString(), MAP );
      INSTANCES.putBAG.toString(), BAG );
      INSTANCES.putIDBAG.toString(), IDBAG );
      INSTANCES.putSET.toString(), SET );
      INSTANCES.putLIST.toString(), LIST );
      INSTANCES.putARRAY.toString(), ARRAY );
      INSTANCES.putPRIMITIVE_ARRAY.toString(), PRIMITIVE_ARRAY );
    }

    public static CollectionType collectionTypeFromString(String xmlTagName) {
      return (CollectionTypeINSTANCES.getxmlTagName );
    }
  }

  private static int getOptimisticLockMode(Attribute olAttthrows MappingException {

    if olAtt == null return Versioning.OPTIMISTIC_LOCK_VERSION;
    String olMode = olAtt.getValue();
    if olMode == null || "version".equalsolMode ) ) {
      return Versioning.OPTIMISTIC_LOCK_VERSION;
    }
    else if "dirty".equalsolMode ) ) {
      return Versioning.OPTIMISTIC_LOCK_DIRTY;
    }
    else if "all".equalsolMode ) ) {
      return Versioning.OPTIMISTIC_LOCK_ALL;
    }
    else if "none".equalsolMode ) ) {
      return Versioning.OPTIMISTIC_LOCK_NONE;
    }
    else {
      throw new MappingException"Unsupported optimistic-lock style: " + olMode );
    }
  }

  private static final java.util.Map getMetas(Element node, java.util.Map inheritedMeta) {
    return getMetasnode, inheritedMeta, false );
  }

  public static final java.util.Map getMetas(Element node, java.util.Map inheritedMeta,
      boolean onlyInheritable) {
    java.util.Map map = new HashMap();
    map.putAllinheritedMeta );

    Iterator iter = node.elementIterator"meta" );
    while iter.hasNext() ) {
      Element metaNode = (Elementiter.next();
      boolean inheritable = Boolean
        .valueOfmetaNode.attributeValue"inherit" ) )
        .booleanValue();
      if onlyInheritable & !inheritable ) {
        continue;
      }
      String name = metaNode.attributeValue"attribute" );

      MetaAttribute meta = (MetaAttributemap.getname );
      MetaAttribute inheritedAttribute = (MetaAttributeinheritedMeta.getname );
      if meta == null  ) {
        meta = new MetaAttributename );
        map.putname, meta );
      else if (meta == inheritedAttribute) { // overriding inherited meta attribute. HBX-621 & HBX-793      
        meta = new MetaAttributename );        
        map.putname, meta );        
      }      
      meta.addValuemetaNode.getText() );
    }
    return map;
  }

  public static String getEntityName(Element elem, Mappings model) {
    String entityName = elem.attributeValue"entity-name" );
    return entityName == null ? getClassNameelem.attribute"class" ), model : entityName;
  }

  private static String getClassName(Attribute att, Mappings model) {
    if att == null return null;
    return getClassNameatt.getValue(), model );
  }

  public static String getClassName(String unqualifiedName, Mappings model) {
    return getClassNameunqualifiedName, model.getDefaultPackage() );
  }

  public static String getClassName(String unqualifiedName, String defaultPackage) {
    if unqualifiedName == null return null;
    if unqualifiedName.indexOf'.' && defaultPackage != null ) {
      return defaultPackage + '.' + unqualifiedName;
    }
    return unqualifiedName;
  }

  private static void parseFilterDef(Element element, Mappings mappings) {
    String name = element.attributeValue"name" );
    log.debug"Parsing filter-def [" + name + "]" );
    String defaultCondition = element.getTextTrim();
    if StringHelper.isEmptydefaultCondition ) ) {
      defaultCondition = element.attributeValue"condition" );
    }
    HashMap paramMappings = new HashMap();
    Iterator params = element.elementIterator"filter-param" );
    while params.hasNext() ) {
      final Element param = (Elementparams.next();
      final String paramName = param.attributeValue"name" );
      final String paramType = param.attributeValue"type" );
      log.debug"adding filter parameter : " + paramName + " -> " + paramType );
      final Type heuristicType = TypeFactory.heuristicTypeparamType );
      log.debug"parameter heuristic type : " + heuristicType );
      paramMappings.putparamName, heuristicType );
    }
    log.debug"Parsed filter-def [" + name + "]" );
    FilterDefinition def = new FilterDefinitionname, defaultCondition, paramMappings );
    mappings.addFilterDefinitiondef );
  }

  private static void parseFilter(Element filterElement, Filterable filterable, Mappings model) {
    final String name = filterElement.attributeValue"name" );
    String condition = filterElement.getTextTrim();
    if StringHelper.isEmpty(condition) ) {
      condition = filterElement.attributeValue"condition" );
    }
    //TODO: bad implementation, cos it depends upon ordering of mapping doc
    //      fixing this requires that Collection/PersistentClass gain access
    //      to the Mappings reference from Configuration (or the filterDefinitions
    //      map directly) sometime during Configuration.buildSessionFactory
    //      (after all the types/filter-defs are known and before building
    //      persisters).
    if StringHelper.isEmpty(condition) ) {
      condition = model.getFilterDefinition(name).getDefaultFilterCondition();
    }
    if condition==null) {
      throw new MappingException("no filter condition found for filter: " + name);
    }
    log.debug"Applying filter [" + name + "] as [" + condition + "]" );
    filterable.addFiltername, condition );
  }

  private static String getSubselect(Element element) {
    String subselect = element.attributeValue"subselect" );
    if subselect != null ) {
      return subselect;
    }
    else {
      Element subselectElement = element.element"subselect" );
      return subselectElement == null null : subselectElement.getText();
    }
  }

  /**
   * For the given document, locate all extends attributes which refer to
   * entities (entity-name or class-name) not defined within said document.
   *
   @param doc The document to check
   @param mappings The already processed mappings.
   @return The list of unresolved extends names.
   */
  public static java.util.List getExtendsNeeded(Document doc, Mappings mappings) {
    java.util.List extendz = new ArrayList();
    Iterator[] subclasses = new Iterator[3];
    final Element hmNode = doc.getRootElement();

    Attribute packNode = hmNode.attribute"package" );
    final String packageName = packNode == null null : packNode.getValue();
    if packageName != null ) {
      mappings.setDefaultPackagepackageName );
    }

    // first, iterate over all elements capable of defining an extends attribute
    // collecting all found extends references if they cannot be resolved
    // against the already processed mappings.
    subclasses[0= hmNode.elementIterator"subclass" );
    subclasses[1= hmNode.elementIterator"joined-subclass" );
    subclasses[2= hmNode.elementIterator"union-subclass" );

    Iterator iterator = new JoinedIteratorsubclasses );
    while iterator.hasNext() ) {
      final Element element = (Elementiterator.next();
      final String extendsName = element.attributeValue"extends" );
      // mappings might contain either the "raw" extends name (in the case of
      // an entity-name mapping) or a FQN (in the case of a POJO mapping).
      if mappings.getClassextendsName == null && mappings.getClassgetClassNameextendsName, mappings ) ) == null ) {
        extendz.addextendsName );
      }
    }

    if !extendz.isEmpty() ) {
      // we found some extends attributes referencing entities which were
      // not already processed.  here we need to locate all entity-names
      // and class-names contained in this document itself, making sure
      // that these get removed from the extendz list such that only
      // extends names which require us to delay processing (i.e.
      // external to this document and not yet processed) are contained
      // in the returned result
      final java.util.Set set = new HashSetextendz );
      EntityElementHandler handler = new EntityElementHandler() {
        public void handleEntity(String entityName, String className, Mappings mappings) {
          if entityName != null ) {
            set.removeentityName );
          }
          else {
            String fqn = getClassNameclassName, packageName );
            set.removefqn );
            if packageName != null ) {
              set.removeStringHelper.unqualifyfqn ) );
            }
          }
        }
      };
      recognizeEntitiesmappings, hmNode, handler );
      extendz.clear();
      extendz.addAllset );
    }

    return extendz;
  }

  /**
   * Given an entity-containing-element (startNode) recursively locate all
   * entity names defined within that element.
   *
   @param mappings The already processed mappings
   @param startNode The containing element
   @param handler The thing that knows what to do whenever we recognize an
   * entity-name
   */
  private static void recognizeEntities(
      Mappings mappings,
          final Element startNode,
      EntityElementHandler handler) {
    Iterator[] classes = new Iterator[4];
    classes[0= startNode.elementIterator"class" );
    classes[1= startNode.elementIterator"subclass" );
    classes[2= startNode.elementIterator"joined-subclass" );
    classes[3= startNode.elementIterator"union-subclass" );

    Iterator classIterator = new JoinedIteratorclasses );
    while classIterator.hasNext() ) {
      Element element = (ElementclassIterator.next();
      handler.handleEntity(
          element.attributeValue"entity-name" ),
                element.attributeValue"name" ),
              mappings
      );
      recognizeEntitiesmappings, element, handler );
    }
  }

  private static interface EntityElementHandler {
    public void handleEntity(String entityName, String className, Mappings mappings);
  }
}