Save This Page
Home » hibernate-distribution-3.3.1.GA-dist » org.hibernate » cfg » [javadoc | source]
    1   /*
    2    * Hibernate, Relational Persistence for Idiomatic Java
    3    *
    4    * Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
    5    * indicated by the @author tags or express copyright attribution
    6    * statements applied by the authors.  All third-party contributions are
    7    * distributed under license by Red Hat Middleware LLC.
    8    *
    9    * This copyrighted material is made available to anyone wishing to use, modify,
   10    * copy, or redistribute it subject to the terms and conditions of the GNU
   11    * Lesser General Public License, as published by the Free Software Foundation.
   12    *
   13    * This program is distributed in the hope that it will be useful,
   14    * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
   15    * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
   16    * for more details.
   17    *
   18    * You should have received a copy of the GNU Lesser General Public License
   19    * along with this distribution; if not, write to:
   20    * Free Software Foundation, Inc.
   21    * 51 Franklin Street, Fifth Floor
   22    * Boston, MA  02110-1301  USA
   23    *
   24    */
   25   package org.hibernate.cfg;
   26   
   27   import java.util.ArrayList;
   28   import java.util.Collections;
   29   import java.util.HashMap;
   30   import java.util.HashSet;
   31   import java.util.Iterator;
   32   import java.util.Properties;
   33   import java.util.StringTokenizer;
   34   
   35   import org.slf4j.Logger;
   36   import org.slf4j.LoggerFactory;
   37   import org.dom4j.Attribute;
   38   import org.dom4j.Document;
   39   import org.dom4j.Element;
   40   import org.hibernate.CacheMode;
   41   import org.hibernate.EntityMode;
   42   import org.hibernate.FetchMode;
   43   import org.hibernate.FlushMode;
   44   import org.hibernate.MappingException;
   45   import org.hibernate.engine.FilterDefinition;
   46   import org.hibernate.engine.NamedQueryDefinition;
   47   import org.hibernate.engine.Versioning;
   48   import org.hibernate.engine.ExecuteUpdateResultCheckStyle;
   49   import org.hibernate.id.PersistentIdentifierGenerator;
   50   import org.hibernate.mapping.Any;
   51   import org.hibernate.mapping.Array;
   52   import org.hibernate.mapping.AuxiliaryDatabaseObject;
   53   import org.hibernate.mapping.Backref;
   54   import org.hibernate.mapping.Bag;
   55   import org.hibernate.mapping.Collection;
   56   import org.hibernate.mapping.Column;
   57   import org.hibernate.mapping.Component;
   58   import org.hibernate.mapping.DependantValue;
   59   import org.hibernate.mapping.Fetchable;
   60   import org.hibernate.mapping.Filterable;
   61   import org.hibernate.mapping.Formula;
   62   import org.hibernate.mapping.IdentifierBag;
   63   import org.hibernate.mapping.IdentifierCollection;
   64   import org.hibernate.mapping.IndexBackref;
   65   import org.hibernate.mapping.IndexedCollection;
   66   import org.hibernate.mapping.Join;
   67   import org.hibernate.mapping.JoinedSubclass;
   68   import org.hibernate.mapping.KeyValue;
   69   import org.hibernate.mapping.List;
   70   import org.hibernate.mapping.ManyToOne;
   71   import org.hibernate.mapping.Map;
   72   import org.hibernate.mapping.MetaAttribute;
   73   import org.hibernate.mapping.OneToMany;
   74   import org.hibernate.mapping.OneToOne;
   75   import org.hibernate.mapping.PersistentClass;
   76   import org.hibernate.mapping.PrimitiveArray;
   77   import org.hibernate.mapping.Property;
   78   import org.hibernate.mapping.PropertyGeneration;
   79   import org.hibernate.mapping.RootClass;
   80   import org.hibernate.mapping.Selectable;
   81   import org.hibernate.mapping.Set;
   82   import org.hibernate.mapping.SimpleAuxiliaryDatabaseObject;
   83   import org.hibernate.mapping.SimpleValue;
   84   import org.hibernate.mapping.SingleTableSubclass;
   85   import org.hibernate.mapping.Subclass;
   86   import org.hibernate.mapping.Table;
   87   import org.hibernate.mapping.ToOne;
   88   import org.hibernate.mapping.TypeDef;
   89   import org.hibernate.mapping.UnionSubclass;
   90   import org.hibernate.mapping.UniqueKey;
   91   import org.hibernate.mapping.Value;
   92   import org.hibernate.persister.entity.JoinedSubclassEntityPersister;
   93   import org.hibernate.persister.entity.SingleTableEntityPersister;
   94   import org.hibernate.persister.entity.UnionSubclassEntityPersister;
   95   import org.hibernate.type.DiscriminatorType;
   96   import org.hibernate.type.ForeignKeyDirection;
   97   import org.hibernate.type.Type;
   98   import org.hibernate.type.TypeFactory;
   99   import org.hibernate.util.JoinedIterator;
  100   import org.hibernate.util.ReflectHelper;
  101   import org.hibernate.util.StringHelper;
  102   
  103   /**
  104    * Walks an XML mapping document and produces the Hibernate configuration-time metamodel (the
  105    * classes in the <tt>mapping</tt> package)
  106    *
  107    * @author Gavin King
  108    */
  109   public final class HbmBinder {
  110   
  111   	private static final Logger log = LoggerFactory.getLogger( HbmBinder.class );
  112   
  113   	/**
  114   	 * Private constructor to disallow instantiation.
  115   	 */
  116   	private HbmBinder() {
  117   	}
  118   
  119   	/**
  120   	 * The main contract into the hbm.xml-based binder. Performs necessary binding operations
  121   	 * represented by the given DOM.
  122   	 *
  123   	 * @param doc The DOM to be parsed and bound.
  124   	 * @param mappings Current bind state.
  125   	 * @param inheritedMetas Any inherited meta-tag information.
  126   	 * @throws MappingException
  127   	 */
  128   	public static void bindRoot(Document doc, Mappings mappings, java.util.Map inheritedMetas)
  129   			throws MappingException {
  130   
  131   		java.util.List names = HbmBinder.getExtendsNeeded( doc, mappings );
  132   		if ( !names.isEmpty() ) {
  133   			// classes mentioned in extends not available - so put it in queue
  134   			Element hmNode = doc.getRootElement();
  135   			Attribute packNode = hmNode.attribute( "package" );
  136   			String packageName = null;
  137   			if ( packNode != null ) {
  138   				packageName = packNode.getValue();
  139   			}
  140   			Iterator itr = names.iterator();
  141   			while ( itr.hasNext() ) {
  142   				String extendsName = (String) itr.next();
  143   				mappings.addToExtendsQueue( new ExtendsQueueEntry( extendsName, packageName, doc ) );
  144   			}
  145   			return;
  146   		}
  147   
  148   		Element hmNode = doc.getRootElement();
  149   		// get meta's from <hibernate-mapping>
  150   		inheritedMetas = getMetas( hmNode, inheritedMetas, true );
  151   		extractRootAttributes( hmNode, mappings );
  152   
  153   		Iterator rootChildren = hmNode.elementIterator();
  154   		while ( rootChildren.hasNext() ) {
  155   			final Element element = (Element) rootChildren.next();
  156   			final String elementName = element.getName();
  157   
  158   			if ( "filter-def".equals( elementName ) ) {
  159   				parseFilterDef( element, mappings );
  160   			}
  161   			else if ( "typedef".equals( elementName ) ) {
  162   				bindTypeDef( element, mappings );
  163   			}
  164   			else if ( "class".equals( elementName ) ) {
  165   				RootClass rootclass = new RootClass();
  166   				bindRootClass( element, rootclass, mappings, inheritedMetas );
  167   				mappings.addClass( rootclass );
  168   			}
  169   			else if ( "subclass".equals( elementName ) ) {
  170   				PersistentClass superModel = getSuperclass( mappings, element );
  171   				handleSubclass( superModel, mappings, element, inheritedMetas );
  172   			}
  173   			else if ( "joined-subclass".equals( elementName ) ) {
  174   				PersistentClass superModel = getSuperclass( mappings, element );
  175   				handleJoinedSubclass( superModel, mappings, element, inheritedMetas );
  176   			}
  177   			else if ( "union-subclass".equals( elementName ) ) {
  178   				PersistentClass superModel = getSuperclass( mappings, element );
  179   				handleUnionSubclass( superModel, mappings, element, inheritedMetas );
  180   			}
  181   			else if ( "query".equals( elementName ) ) {
  182   				bindNamedQuery( element, null, mappings );
  183   			}
  184   			else if ( "sql-query".equals( elementName ) ) {
  185   				bindNamedSQLQuery( element, null, mappings );
  186   			}
  187   			else if ( "resultset".equals( elementName ) ) {
  188   				bindResultSetMappingDefinition( element, null, mappings );
  189   			}
  190   			else if ( "import".equals( elementName ) ) {
  191   				bindImport( element, mappings );
  192   			}
  193   			else if ( "database-object".equals( elementName ) ) {
  194   				bindAuxiliaryDatabaseObject( element, mappings );
  195   			}
  196   		}
  197   	}
  198   
  199   	private static void bindImport(Element importNode, Mappings mappings) {
  200   		String className = getClassName( importNode.attribute( "class" ), mappings );
  201   		Attribute renameNode = importNode.attribute( "rename" );
  202   		String rename = ( renameNode == null ) ?
  203   						StringHelper.unqualify( className ) :
  204   						renameNode.getValue();
  205   		log.debug( "Import: " + rename + " -> " + className );
  206   		mappings.addImport( className, rename );
  207   	}
  208   
  209   	private static void bindTypeDef(Element typedefNode, Mappings mappings) {
  210   		String typeClass = typedefNode.attributeValue( "class" );
  211   		String typeName = typedefNode.attributeValue( "name" );
  212   		Iterator paramIter = typedefNode.elementIterator( "param" );
  213   		Properties parameters = new Properties();
  214   		while ( paramIter.hasNext() ) {
  215   			Element param = (Element) paramIter.next();
  216   			parameters.setProperty( param.attributeValue( "name" ), param.getTextTrim() );
  217   		}
  218   		mappings.addTypeDef( typeName, typeClass, parameters );
  219   	}
  220   
  221   	private static void bindAuxiliaryDatabaseObject(Element auxDbObjectNode, Mappings mappings) {
  222   		AuxiliaryDatabaseObject auxDbObject = null;
  223   		Element definitionNode = auxDbObjectNode.element( "definition" );
  224   		if ( definitionNode != null ) {
  225   			try {
  226   				auxDbObject = ( AuxiliaryDatabaseObject ) ReflectHelper
  227   						.classForName( definitionNode.attributeValue( "class" ) )
  228   						.newInstance();
  229   			}
  230   			catch( ClassNotFoundException e ) {
  231   				throw new MappingException(
  232   						"could not locate custom database object class [" +
  233   						definitionNode.attributeValue( "class" ) + "]"
  234   					);
  235   			}
  236   			catch( Throwable t ) {
  237   				throw new MappingException(
  238   						"could not instantiate custom database object class [" +
  239   						definitionNode.attributeValue( "class" ) + "]"
  240   					);
  241   			}
  242   		}
  243   		else {
  244   			auxDbObject = new SimpleAuxiliaryDatabaseObject(
  245   					auxDbObjectNode.elementTextTrim( "create" ),
  246   					auxDbObjectNode.elementTextTrim( "drop" )
  247   				);
  248   		}
  249   
  250   		Iterator dialectScopings = auxDbObjectNode.elementIterator( "dialect-scope" );
  251   		while ( dialectScopings.hasNext() ) {
  252   			Element dialectScoping = ( Element ) dialectScopings.next();
  253   			auxDbObject.addDialectScope( dialectScoping.attributeValue( "name" ) );
  254   		}
  255   
  256   		mappings.addAuxiliaryDatabaseObject( auxDbObject );
  257   	}
  258   
  259   	private static void extractRootAttributes(Element hmNode, Mappings mappings) {
  260   		Attribute schemaNode = hmNode.attribute( "schema" );
  261   		mappings.setSchemaName( ( schemaNode == null ) ? null : schemaNode.getValue() );
  262   
  263   		Attribute catalogNode = hmNode.attribute( "catalog" );
  264   		mappings.setCatalogName( ( catalogNode == null ) ? null : catalogNode.getValue() );
  265   
  266   		Attribute dcNode = hmNode.attribute( "default-cascade" );
  267   		mappings.setDefaultCascade( ( dcNode == null ) ? "none" : dcNode.getValue() );
  268   
  269   		Attribute daNode = hmNode.attribute( "default-access" );
  270   		mappings.setDefaultAccess( ( daNode == null ) ? "property" : daNode.getValue() );
  271   
  272   		Attribute dlNode = hmNode.attribute( "default-lazy" );
  273   		mappings.setDefaultLazy( dlNode == null || dlNode.getValue().equals( "true" ) );
  274   
  275   		Attribute aiNode = hmNode.attribute( "auto-import" );
  276   		mappings.setAutoImport( ( aiNode == null ) || "true".equals( aiNode.getValue() ) );
  277   
  278   		Attribute packNode = hmNode.attribute( "package" );
  279   		if ( packNode != null ) mappings.setDefaultPackage( packNode.getValue() );
  280   	}
  281   
  282   	/**
  283   	 * Responsible for perfoming the bind operation related to an &lt;class/&gt; mapping element.
  284   	 *
  285   	 * @param node The DOM Element for the &lt;class/&gt; element.
  286   	 * @param rootClass The mapping instance to which to bind the information.
  287   	 * @param mappings The current bind state.
  288   	 * @param inheritedMetas Any inherited meta-tag information.
  289   	 * @throws MappingException
  290   	 */
  291   	public static void bindRootClass(Element node, RootClass rootClass, Mappings mappings,
  292   			java.util.Map inheritedMetas) throws MappingException {
  293   		bindClass( node, rootClass, mappings, inheritedMetas );
  294   		inheritedMetas = getMetas( node, inheritedMetas, true ); // get meta's from <class>
  295   		bindRootPersistentClassCommonValues( node, inheritedMetas, mappings, rootClass );
  296   	}
  297   
  298   	private static void bindRootPersistentClassCommonValues(Element node,
  299   			java.util.Map inheritedMetas, Mappings mappings, RootClass entity)
  300   			throws MappingException {
  301   
  302   		// DB-OBJECTNAME
  303   
  304   		Attribute schemaNode = node.attribute( "schema" );
  305   		String schema = schemaNode == null ?
  306   				mappings.getSchemaName() : schemaNode.getValue();
  307   
  308   		Attribute catalogNode = node.attribute( "catalog" );
  309   		String catalog = catalogNode == null ?
  310   				mappings.getCatalogName() : catalogNode.getValue();
  311   
  312   		Table table = mappings.addTable(
  313   				schema,
  314   				catalog,
  315   				getClassTableName( entity, node, schema, catalog, null, mappings ),
  316   				getSubselect( node ),
  317   		        entity.isAbstract() != null && entity.isAbstract().booleanValue()
  318   			);
  319   		entity.setTable( table );
  320   		bindComment(table, node);
  321   
  322   		log.info(
  323   				"Mapping class: " + entity.getEntityName() +
  324   				" -> " + entity.getTable().getName()
  325   			);
  326   
  327   		// MUTABLE
  328   		Attribute mutableNode = node.attribute( "mutable" );
  329   		entity.setMutable( ( mutableNode == null ) || mutableNode.getValue().equals( "true" ) );
  330   
  331   		// WHERE
  332   		Attribute whereNode = node.attribute( "where" );
  333   		if ( whereNode != null ) entity.setWhere( whereNode.getValue() );
  334   
  335   		// CHECK
  336   		Attribute chNode = node.attribute( "check" );
  337   		if ( chNode != null ) table.addCheckConstraint( chNode.getValue() );
  338   
  339   		// POLYMORPHISM
  340   		Attribute polyNode = node.attribute( "polymorphism" );
  341   		entity.setExplicitPolymorphism( ( polyNode != null )
  342   			&& polyNode.getValue().equals( "explicit" ) );
  343   
  344   		// ROW ID
  345   		Attribute rowidNode = node.attribute( "rowid" );
  346   		if ( rowidNode != null ) table.setRowId( rowidNode.getValue() );
  347   
  348   		Iterator subnodes = node.elementIterator();
  349   		while ( subnodes.hasNext() ) {
  350   
  351   			Element subnode = (Element) subnodes.next();
  352   			String name = subnode.getName();
  353   
  354   			if ( "id".equals( name ) ) {
  355   				// ID
  356   				bindSimpleId( subnode, entity, mappings, inheritedMetas );
  357   			}
  358   			else if ( "composite-id".equals( name ) ) {
  359   				// COMPOSITE-ID
  360   				bindCompositeId( subnode, entity, mappings, inheritedMetas );
  361   			}
  362   			else if ( "version".equals( name ) || "timestamp".equals( name ) ) {
  363   				// VERSION / TIMESTAMP
  364   				bindVersioningProperty( table, subnode, mappings, name, entity, inheritedMetas );
  365   			}
  366   			else if ( "discriminator".equals( name ) ) {
  367   				// DISCRIMINATOR
  368   				bindDiscriminatorProperty( table, entity, subnode, mappings );
  369   			}
  370   			else if ( "cache".equals( name ) ) {
  371   				entity.setCacheConcurrencyStrategy( subnode.attributeValue( "usage" ) );
  372   				entity.setCacheRegionName( subnode.attributeValue( "region" ) );
  373   				entity.setLazyPropertiesCacheable( !"non-lazy".equals( subnode.attributeValue( "include" ) ) );
  374   			}
  375   
  376   		}
  377   
  378   		// Primary key constraint
  379   		entity.createPrimaryKey();
  380   
  381   		createClassProperties( node, entity, mappings, inheritedMetas );
  382   	}
  383   
  384   	private static void bindSimpleId(Element idNode, RootClass entity, Mappings mappings,
  385   			java.util.Map inheritedMetas) throws MappingException {
  386   		String propertyName = idNode.attributeValue( "name" );
  387   
  388   		SimpleValue id = new SimpleValue( entity.getTable() );
  389   		entity.setIdentifier( id );
  390   
  391   		// if ( propertyName == null || entity.getPojoRepresentation() == null ) {
  392   		// bindSimpleValue( idNode, id, false, RootClass.DEFAULT_IDENTIFIER_COLUMN_NAME, mappings );
  393   		// if ( !id.isTypeSpecified() ) {
  394   		// throw new MappingException( "must specify an identifier type: " + entity.getEntityName()
  395   		// );
  396   		// }
  397   		// }
  398   		// else {
  399   		// bindSimpleValue( idNode, id, false, propertyName, mappings );
  400   		// PojoRepresentation pojo = entity.getPojoRepresentation();
  401   		// id.setTypeUsingReflection( pojo.getClassName(), propertyName );
  402   		//
  403   		// Property prop = new Property();
  404   		// prop.setValue( id );
  405   		// bindProperty( idNode, prop, mappings, inheritedMetas );
  406   		// entity.setIdentifierProperty( prop );
  407   		// }
  408   
  409   		if ( propertyName == null ) {
  410   			bindSimpleValue( idNode, id, false, RootClass.DEFAULT_IDENTIFIER_COLUMN_NAME, mappings );
  411   		}
  412   		else {
  413   			bindSimpleValue( idNode, id, false, propertyName, mappings );
  414   		}
  415   
  416   		if ( propertyName == null || !entity.hasPojoRepresentation() ) {
  417   			if ( !id.isTypeSpecified() ) {
  418   				throw new MappingException( "must specify an identifier type: "
  419   					+ entity.getEntityName() );
  420   			}
  421   		}
  422   		else {
  423   			id.setTypeUsingReflection( entity.getClassName(), propertyName );
  424   		}
  425   
  426   		if ( propertyName != null ) {
  427   			Property prop = new Property();
  428   			prop.setValue( id );
  429   			bindProperty( idNode, prop, mappings, inheritedMetas );
  430   			entity.setIdentifierProperty( prop );
  431   		}
  432   
  433   		// TODO:
  434   		/*
  435   		 * if ( id.getHibernateType().getReturnedClass().isArray() ) throw new MappingException(
  436   		 * "illegal use of an array as an identifier (arrays don't reimplement equals)" );
  437   		 */
  438   		makeIdentifier( idNode, id, mappings );
  439   	}
  440   
  441   	private static void bindCompositeId(Element idNode, RootClass entity, Mappings mappings,
  442   			java.util.Map inheritedMetas) throws MappingException {
  443   		String propertyName = idNode.attributeValue( "name" );
  444   		Component id = new Component( entity );
  445   		entity.setIdentifier( id );
  446   		bindCompositeId( idNode, id, entity, propertyName, mappings, inheritedMetas );
  447   		if ( propertyName == null ) {
  448   			entity.setEmbeddedIdentifier( id.isEmbedded() );
  449   			if ( id.isEmbedded() ) {
  450   				// todo : what is the implication of this?
  451   				id.setDynamic( !entity.hasPojoRepresentation() );
  452   				/*
  453   				 * Property prop = new Property(); prop.setName("id");
  454   				 * prop.setPropertyAccessorName("embedded"); prop.setValue(id);
  455   				 * entity.setIdentifierProperty(prop);
  456   				 */
  457   			}
  458   		}
  459   		else {
  460   			Property prop = new Property();
  461   			prop.setValue( id );
  462   			bindProperty( idNode, prop, mappings, inheritedMetas );
  463   			entity.setIdentifierProperty( prop );
  464   		}
  465   
  466   		makeIdentifier( idNode, id, mappings );
  467   
  468   	}
  469   
  470   	private static void bindVersioningProperty(Table table, Element subnode, Mappings mappings,
  471   			String name, RootClass entity, java.util.Map inheritedMetas) {
  472   
  473   		String propertyName = subnode.attributeValue( "name" );
  474   		SimpleValue val = new SimpleValue( table );
  475   		bindSimpleValue( subnode, val, false, propertyName, mappings );
  476   		if ( !val.isTypeSpecified() ) {
  477   			// this is either a <version/> tag with no type attribute,
  478   			// or a <timestamp/> tag
  479   			if ( "version".equals( name ) ) {
  480   				val.setTypeName( "integer" );
  481   			}
  482   			else {
  483   				if ( "db".equals( subnode.attributeValue( "source" ) ) ) {
  484   					val.setTypeName( "dbtimestamp" );
  485   				}
  486   				else {
  487   					val.setTypeName( "timestamp" );
  488   				}
  489   			}
  490   		}
  491   		Property prop = new Property();
  492   		prop.setValue( val );
  493   		bindProperty( subnode, prop, mappings, inheritedMetas );
  494   		// for version properties marked as being generated, make sure they are "always"
  495   		// generated; aka, "insert" is invalid; this is dis-allowed by the DTD,
  496   		// but just to make sure...
  497   		if ( prop.getGeneration() == PropertyGeneration.INSERT ) {
  498   			throw new MappingException( "'generated' attribute cannot be 'insert' for versioning property" );
  499   		}
  500   		makeVersion( subnode, val );
  501   		entity.setVersion( prop );
  502   		entity.addProperty( prop );
  503   	}
  504   
  505   	private static void bindDiscriminatorProperty(Table table, RootClass entity, Element subnode,
  506   			Mappings mappings) {
  507   		SimpleValue discrim = new SimpleValue( table );
  508   		entity.setDiscriminator( discrim );
  509   		bindSimpleValue(
  510   				subnode,
  511   				discrim,
  512   				false,
  513   				RootClass.DEFAULT_DISCRIMINATOR_COLUMN_NAME,
  514   				mappings
  515   			);
  516   		if ( !discrim.isTypeSpecified() ) {
  517   			discrim.setTypeName( "string" );
  518   			// ( (Column) discrim.getColumnIterator().next() ).setType(type);
  519   		}
  520   		entity.setPolymorphic( true );
  521   		if ( "true".equals( subnode.attributeValue( "force" ) ) )
  522   			entity.setForceDiscriminator( true );
  523   		if ( "false".equals( subnode.attributeValue( "insert" ) ) )
  524   			entity.setDiscriminatorInsertable( false );
  525   	}
  526   
  527   	public static void bindClass(Element node, PersistentClass persistentClass, Mappings mappings,
  528   			java.util.Map inheritedMetas) throws MappingException {
  529   		// transfer an explicitly defined entity name
  530   		// handle the lazy attribute
  531   		Attribute lazyNode = node.attribute( "lazy" );
  532   		boolean lazy = lazyNode == null ?
  533   				mappings.isDefaultLazy() :
  534   				"true".equals( lazyNode.getValue() );
  535   		// go ahead and set the lazy here, since pojo.proxy can override it.
  536   		persistentClass.setLazy( lazy );
  537   
  538   		String entityName = node.attributeValue( "entity-name" );
  539   		if ( entityName == null ) entityName = getClassName( node.attribute("name"), mappings );
  540   		if ( entityName==null ) {
  541   			throw new MappingException( "Unable to determine entity name" );
  542   		}
  543   		persistentClass.setEntityName( entityName );
  544   
  545   		bindPojoRepresentation( node, persistentClass, mappings, inheritedMetas );
  546   		bindDom4jRepresentation( node, persistentClass, mappings, inheritedMetas );
  547   		bindMapRepresentation( node, persistentClass, mappings, inheritedMetas );
  548   
  549   		bindPersistentClassCommonValues( node, persistentClass, mappings, inheritedMetas );
  550   
  551   	}
  552   
  553   	private static void bindPojoRepresentation(Element node, PersistentClass entity,
  554   			Mappings mappings, java.util.Map metaTags) {
  555   
  556   		String className = getClassName( node.attribute( "name" ), mappings );
  557   		String proxyName = getClassName( node.attribute( "proxy" ), mappings );
  558   
  559   		entity.setClassName( className );
  560   
  561   		if ( proxyName != null ) {
  562   			entity.setProxyInterfaceName( proxyName );
  563   			entity.setLazy( true );
  564   		}
  565   		else if ( entity.isLazy() ) {
  566   			entity.setProxyInterfaceName( className );
  567   		}
  568   
  569   		Element tuplizer = locateTuplizerDefinition( node, EntityMode.POJO );
  570   		if ( tuplizer != null ) {
  571   			entity.addTuplizer( EntityMode.POJO, tuplizer.attributeValue( "class" ) );
  572   		}
  573   	}
  574   
  575   	private static void bindDom4jRepresentation(Element node, PersistentClass entity,
  576   			Mappings mappings, java.util.Map inheritedMetas) {
  577   		String nodeName = node.attributeValue( "node" );
  578   		if (nodeName==null) nodeName = StringHelper.unqualify( entity.getEntityName() );
  579   		entity.setNodeName(nodeName);
  580   
  581   		Element tuplizer = locateTuplizerDefinition( node, EntityMode.DOM4J );
  582   		if ( tuplizer != null ) {
  583   			entity.addTuplizer( EntityMode.DOM4J, tuplizer.attributeValue( "class" ) );
  584   		}
  585   	}
  586   
  587   	private static void bindMapRepresentation(Element node, PersistentClass entity,
  588   			Mappings mappings, java.util.Map inheritedMetas) {
  589   		Element tuplizer = locateTuplizerDefinition( node, EntityMode.MAP );
  590   		if ( tuplizer != null ) {
  591   			entity.addTuplizer( EntityMode.MAP, tuplizer.attributeValue( "class" ) );
  592   		}
  593   	}
  594   
  595   	/**
  596   	 * Locate any explicit tuplizer definition in the metadata, for the given entity-mode.
  597   	 *
  598   	 * @param container The containing element (representing the entity/component)
  599   	 * @param entityMode The entity-mode for which to locate the tuplizer element
  600   	 * @return The tuplizer element, or null.
  601   	 */
  602   	private static Element locateTuplizerDefinition(Element container, EntityMode entityMode) {
  603   		Iterator itr = container.elements( "tuplizer" ).iterator();
  604   		while( itr.hasNext() ) {
  605   			final Element tuplizerElem = ( Element ) itr.next();
  606   			if ( entityMode.toString().equals( tuplizerElem.attributeValue( "entity-mode") ) ) {
  607   				return tuplizerElem;
  608   			}
  609   		}
  610   		return null;
  611   	}
  612   
  613   	private static void bindPersistentClassCommonValues(Element node, PersistentClass entity,
  614   			Mappings mappings, java.util.Map inheritedMetas) throws MappingException {
  615   		// DISCRIMINATOR
  616   		Attribute discriminatorNode = node.attribute( "discriminator-value" );
  617   		entity.setDiscriminatorValue( ( discriminatorNode == null )
  618   			? entity.getEntityName()
  619   			: discriminatorNode.getValue() );
  620   
  621   		// DYNAMIC UPDATE
  622   		Attribute dynamicNode = node.attribute( "dynamic-update" );
  623   		entity.setDynamicUpdate(
  624   				dynamicNode != null && "true".equals( dynamicNode.getValue() )
  625   		);
  626   
  627   		// DYNAMIC INSERT
  628   		Attribute insertNode = node.attribute( "dynamic-insert" );
  629   		entity.setDynamicInsert(
  630   				insertNode != null && "true".equals( insertNode.getValue() )
  631   		);
  632   
  633   		// IMPORT
  634   		mappings.addImport( entity.getEntityName(), entity.getEntityName() );
  635   		if ( mappings.isAutoImport() && entity.getEntityName().indexOf( '.' ) > 0 ) {
  636   			mappings.addImport(
  637   					entity.getEntityName(),
  638   					StringHelper.unqualify( entity.getEntityName() )
  639   				);
  640   		}
  641   
  642   		// BATCH SIZE
  643   		Attribute batchNode = node.attribute( "batch-size" );
  644   		if ( batchNode != null ) entity.setBatchSize( Integer.parseInt( batchNode.getValue() ) );
  645   
  646   		// SELECT BEFORE UPDATE
  647   		Attribute sbuNode = node.attribute( "select-before-update" );
  648   		if ( sbuNode != null ) entity.setSelectBeforeUpdate( "true".equals( sbuNode.getValue() ) );
  649   
  650   		// OPTIMISTIC LOCK MODE
  651   		Attribute olNode = node.attribute( "optimistic-lock" );
  652   		entity.setOptimisticLockMode( getOptimisticLockMode( olNode ) );
  653   
  654   		entity.setMetaAttributes( getMetas( node, inheritedMetas ) );
  655   
  656   		// PERSISTER
  657   		Attribute persisterNode = node.attribute( "persister" );
  658   		if ( persisterNode != null ) {
  659   			try {
  660   				entity.setEntityPersisterClass( ReflectHelper.classForName( persisterNode
  661   					.getValue() ) );
  662   			}
  663   			catch (ClassNotFoundException cnfe) {
  664   				throw new MappingException( "Could not find persister class: "
  665   					+ persisterNode.getValue() );
  666   			}
  667   		}
  668   
  669   		// CUSTOM SQL
  670   		handleCustomSQL( node, entity );
  671   
  672   		Iterator tables = node.elementIterator( "synchronize" );
  673   		while ( tables.hasNext() ) {
  674   			entity.addSynchronizedTable( ( (Element) tables.next() ).attributeValue( "table" ) );
  675   		}
  676   
  677   		Attribute abstractNode = node.attribute( "abstract" );
  678   		Boolean isAbstract = abstractNode == null
  679   				? null
  680   		        : "true".equals( abstractNode.getValue() )
  681   						? Boolean.TRUE
  682   	                    : "false".equals( abstractNode.getValue() )
  683   								? Boolean.FALSE
  684   	                            : null;
  685   		entity.setAbstract( isAbstract );
  686   	}
  687   
  688   	private static void handleCustomSQL(Element node, PersistentClass model)
  689   			throws MappingException {
  690   		Element element = node.element( "sql-insert" );
  691   		if ( element != null ) {
  692   			boolean callable = isCallable( element );
  693   			model.setCustomSQLInsert( element.getTextTrim(), callable, getResultCheckStyle( element, callable ) );
  694   		}
  695   
  696   		element = node.element( "sql-delete" );
  697   		if ( element != null ) {
  698   			boolean callable = isCallable( element );
  699   			model.setCustomSQLDelete( element.getTextTrim(), callable, getResultCheckStyle( element, callable ) );
  700   		}
  701   
  702   		element = node.element( "sql-update" );
  703   		if ( element != null ) {
  704   			boolean callable = isCallable( element );
  705   			model.setCustomSQLUpdate( element.getTextTrim(), callable, getResultCheckStyle( element, callable ) );
  706   		}
  707   
  708   		element = node.element( "loader" );
  709   		if ( element != null ) {
  710   			model.setLoaderName( element.attributeValue( "query-ref" ) );
  711   		}
  712   	}
  713   
  714   	private static void handleCustomSQL(Element node, Join model) throws MappingException {
  715   		Element element = node.element( "sql-insert" );
  716   		if ( element != null ) {
  717   			boolean callable = isCallable( element );
  718   			model.setCustomSQLInsert( element.getTextTrim(), callable, getResultCheckStyle( element, callable ) );
  719   		}
  720   
  721   		element = node.element( "sql-delete" );
  722   		if ( element != null ) {
  723   			boolean callable = isCallable( element );
  724   			model.setCustomSQLDelete( element.getTextTrim(), callable, getResultCheckStyle( element, callable ) );
  725   		}
  726   
  727   		element = node.element( "sql-update" );
  728   		if ( element != null ) {
  729   			boolean callable = isCallable( element );
  730   			model.setCustomSQLUpdate( element.getTextTrim(), callable, getResultCheckStyle( element, callable ) );
  731   		}
  732   	}
  733   
  734   	private static void handleCustomSQL(Element node, Collection model) throws MappingException {
  735   		Element element = node.element( "sql-insert" );
  736   		if ( element != null ) {
  737   			boolean callable = isCallable( element, true );
  738   			model.setCustomSQLInsert( element.getTextTrim(), callable, getResultCheckStyle( element, callable ) );
  739   		}
  740   
  741   		element = node.element( "sql-delete" );
  742   		if ( element != null ) {
  743   			boolean callable = isCallable( element, true );
  744   			model.setCustomSQLDelete( element.getTextTrim(), callable, getResultCheckStyle( element, callable ) );
  745   		}
  746   
  747   		element = node.element( "sql-update" );
  748   		if ( element != null ) {
  749   			boolean callable = isCallable( element, true );
  750   			model.setCustomSQLUpdate( element.getTextTrim(), callable, getResultCheckStyle( element, callable ) );
  751   		}
  752   
  753   		element = node.element( "sql-delete-all" );
  754   		if ( element != null ) {
  755   			boolean callable = isCallable( element, true );
  756   			model.setCustomSQLDeleteAll( element.getTextTrim(), callable, getResultCheckStyle( element, callable ) );
  757   		}
  758   	}
  759   
  760   	private static boolean isCallable(Element e) throws MappingException {
  761   		return isCallable( e, true );
  762   	}
  763   
  764   	private static boolean isCallable(Element element, boolean supportsCallable)
  765   			throws MappingException {
  766   		Attribute attrib = element.attribute( "callable" );
  767   		if ( attrib != null && "true".equals( attrib.getValue() ) ) {
  768   			if ( !supportsCallable ) {
  769   				throw new MappingException( "callable attribute not supported yet!" );
  770   			}
  771   			return true;
  772   		}
  773   		return false;
  774   	}
  775   
  776   	private static ExecuteUpdateResultCheckStyle getResultCheckStyle(Element element, boolean callable) throws MappingException {
  777   		Attribute attr = element.attribute( "check" );
  778   		if ( attr == null ) {
  779   			// use COUNT as the default.  This mimics the old behavior, although
  780   			// NONE might be a better option moving forward in the case of callable
  781   			return ExecuteUpdateResultCheckStyle.COUNT;
  782   		}
  783   		return ExecuteUpdateResultCheckStyle.parse( attr.getValue() );
  784   	}
  785   
  786   	public static void bindUnionSubclass(Element node, UnionSubclass unionSubclass,
  787   			Mappings mappings, java.util.Map inheritedMetas) throws MappingException {
  788   
  789   		bindClass( node, unionSubclass, mappings, inheritedMetas );
  790   		inheritedMetas = getMetas( node, inheritedMetas, true ); // get meta's from <subclass>
  791   
  792   		if ( unionSubclass.getEntityPersisterClass() == null ) {
  793   			unionSubclass.getRootClass().setEntityPersisterClass(
  794   				UnionSubclassEntityPersister.class );
  795   		}
  796   
  797   		Attribute schemaNode = node.attribute( "schema" );
  798   		String schema = schemaNode == null ?
  799   				mappings.getSchemaName() : schemaNode.getValue();
  800   
  801   		Attribute catalogNode = node.attribute( "catalog" );
  802   		String catalog = catalogNode == null ?
  803   				mappings.getCatalogName() : catalogNode.getValue();
  804   
  805   		Table denormalizedSuperTable = unionSubclass.getSuperclass().getTable();
  806   		Table mytable = mappings.addDenormalizedTable(
  807   				schema,
  808   				catalog,
  809   				getClassTableName(unionSubclass, node, schema, catalog, denormalizedSuperTable, mappings ),
  810   		        unionSubclass.isAbstract() != null && unionSubclass.isAbstract().booleanValue(),
  811   				getSubselect( node ),
  812   				denormalizedSuperTable
  813   			);
  814   		unionSubclass.setTable( mytable );
  815   
  816   		log.info(
  817   				"Mapping union-subclass: " + unionSubclass.getEntityName() +
  818   				" -> " + unionSubclass.getTable().getName()
  819   			);
  820   
  821   		createClassProperties( node, unionSubclass, mappings, inheritedMetas );
  822   
  823   	}
  824   
  825   	public static void bindSubclass(Element node, Subclass subclass, Mappings mappings,
  826   			java.util.Map inheritedMetas) throws MappingException {
  827   
  828   		bindClass( node, subclass, mappings, inheritedMetas );
  829   		inheritedMetas = getMetas( node, inheritedMetas, true ); // get meta's from <subclass>
  830   
  831   		if ( subclass.getEntityPersisterClass() == null ) {
  832   			subclass.getRootClass()
  833   					.setEntityPersisterClass( SingleTableEntityPersister.class );
  834   		}
  835   
  836   		log.info(
  837   				"Mapping subclass: " + subclass.getEntityName() +
  838   				" -> " + subclass.getTable().getName()
  839   			);
  840   
  841   		// properties
  842   		createClassProperties( node, subclass, mappings, inheritedMetas );
  843   	}
  844   
  845   	private static String getClassTableName(
  846   			PersistentClass model, Element node, String schema, String catalog, Table denormalizedSuperTable,
  847   			Mappings mappings
  848   	) {
  849   		Attribute tableNameNode = node.attribute( "table" );
  850   		String logicalTableName;
  851   		String physicalTableName;
  852   		if ( tableNameNode == null ) {
  853   			logicalTableName = StringHelper.unqualify( model.getEntityName() );
  854   			physicalTableName = mappings.getNamingStrategy().classToTableName( model.getEntityName() );
  855   		}
  856   		else {
  857   			logicalTableName = tableNameNode.getValue();
  858   			physicalTableName = mappings.getNamingStrategy().tableName( logicalTableName );
  859   		}
  860   		mappings.addTableBinding( schema, catalog, logicalTableName, physicalTableName, denormalizedSuperTable );
  861   		return physicalTableName;
  862   	}
  863   
  864   	public static void bindJoinedSubclass(Element node, JoinedSubclass joinedSubclass,
  865   			Mappings mappings, java.util.Map inheritedMetas) throws MappingException {
  866   
  867   		bindClass( node, joinedSubclass, mappings, inheritedMetas );
  868   		inheritedMetas = getMetas( node, inheritedMetas, true ); // get meta's from
  869   																	// <joined-subclass>
  870   
  871   		// joined subclasses
  872   		if ( joinedSubclass.getEntityPersisterClass() == null ) {
  873   			joinedSubclass.getRootClass()
  874   				.setEntityPersisterClass( JoinedSubclassEntityPersister.class );
  875   		}
  876   
  877   		Attribute schemaNode = node.attribute( "schema" );
  878   		String schema = schemaNode == null ?
  879   				mappings.getSchemaName() : schemaNode.getValue();
  880   
  881   		Attribute catalogNode = node.attribute( "catalog" );
  882   		String catalog = catalogNode == null ?
  883   				mappings.getCatalogName() : catalogNode.getValue();
  884   
  885   		Table mytable = mappings.addTable(
  886   				schema,
  887   				catalog,
  888   				getClassTableName( joinedSubclass, node, schema, catalog, null, mappings ),
  889   				getSubselect( node ),
  890   				false
  891   			);
  892   		joinedSubclass.setTable( mytable );
  893   		bindComment(mytable, node);
  894   
  895   		log.info(
  896   				"Mapping joined-subclass: " + joinedSubclass.getEntityName() +
  897   				" -> " + joinedSubclass.getTable().getName()
  898   			);
  899   
  900   		// KEY
  901   		Element keyNode = node.element( "key" );
  902   		SimpleValue key = new DependantValue( mytable, joinedSubclass.getIdentifier() );
  903   		joinedSubclass.setKey( key );
  904   		key.setCascadeDeleteEnabled( "cascade".equals( keyNode.attributeValue( "on-delete" ) ) );
  905   		bindSimpleValue( keyNode, key, false, joinedSubclass.getEntityName(), mappings );
  906   
  907   		// model.getKey().setType( new Type( model.getIdentifier() ) );
  908   		joinedSubclass.createPrimaryKey();
  909   		joinedSubclass.createForeignKey();
  910   
  911   		// CHECK
  912   		Attribute chNode = node.attribute( "check" );
  913   		if ( chNode != null ) mytable.addCheckConstraint( chNode.getValue() );
  914   
  915   		// properties
  916   		createClassProperties( node, joinedSubclass, mappings, inheritedMetas );
  917   
  918   	}
  919   
  920   	private static void bindJoin(Element node, Join join, Mappings mappings,
  921   			java.util.Map inheritedMetas) throws MappingException {
  922   
  923   		PersistentClass persistentClass = join.getPersistentClass();
  924   		String path = persistentClass.getEntityName();
  925   
  926   		// TABLENAME
  927   
  928   		Attribute schemaNode = node.attribute( "schema" );
  929   		String schema = schemaNode == null ?
  930   				mappings.getSchemaName() : schemaNode.getValue();
  931   		Attribute catalogNode = node.attribute( "catalog" );
  932   		String catalog = catalogNode == null ?
  933   				mappings.getCatalogName() : catalogNode.getValue();
  934   		Table primaryTable = persistentClass.getTable();
  935   		Table table = mappings.addTable(
  936   				schema,
  937   				catalog,
  938   				getClassTableName( persistentClass, node, schema, catalog, primaryTable, mappings ),
  939   				getSubselect( node ),
  940   				false
  941   			);
  942   		join.setTable( table );
  943   		bindComment(table, node);
  944   
  945   		Attribute fetchNode = node.attribute( "fetch" );
  946   		if ( fetchNode != null ) {
  947   			join.setSequentialSelect( "select".equals( fetchNode.getValue() ) );
  948   		}
  949   
  950   		Attribute invNode = node.attribute( "inverse" );
  951   		if ( invNode != null ) {
  952   			join.setInverse( "true".equals( invNode.getValue() ) );
  953   		}
  954   
  955   		Attribute nullNode = node.attribute( "optional" );
  956   		if ( nullNode != null ) {
  957   			join.setOptional( "true".equals( nullNode.getValue() ) );
  958   		}
  959   
  960   		log.info(
  961   				"Mapping class join: " + persistentClass.getEntityName() +
  962   				" -> " + join.getTable().getName()
  963   			);
  964   
  965   		// KEY
  966   		Element keyNode = node.element( "key" );
  967   		SimpleValue key = new DependantValue( table, persistentClass.getIdentifier() );
  968   		join.setKey( key );
  969   		key.setCascadeDeleteEnabled( "cascade".equals( keyNode.attributeValue( "on-delete" ) ) );
  970   		bindSimpleValue( keyNode, key, false, persistentClass.getEntityName(), mappings );
  971   
  972   		// join.getKey().setType( new Type( lazz.getIdentifier() ) );
  973   		join.createPrimaryKey();
  974   		join.createForeignKey();
  975   
  976   		// PROPERTIES
  977   		Iterator iter = node.elementIterator();
  978   		while ( iter.hasNext() ) {
  979   			Element subnode = (Element) iter.next();
  980   			String name = subnode.getName();
  981   			String propertyName = subnode.attributeValue( "name" );
  982   
  983   			Value value = null;
  984   			if ( "many-to-one".equals( name ) ) {
  985   				value = new ManyToOne( table );
  986   				bindManyToOne( subnode, (ManyToOne) value, propertyName, true, mappings );
  987   			}
  988   			else if ( "any".equals( name ) ) {
  989   				value = new Any( table );
  990   				bindAny( subnode, (Any) value, true, mappings );
  991   			}
  992   			else if ( "property".equals( name ) ) {
  993   				value = new SimpleValue( table );
  994   				bindSimpleValue( subnode, (SimpleValue) value, true, propertyName, mappings );
  995   			}
  996   			else if ( "component".equals( name ) || "dynamic-component".equals( name ) ) {
  997   				String subpath = StringHelper.qualify( path, propertyName );
  998   				value = new Component( join );
  999   				bindComponent(
 1000   						subnode,
 1001   						(Component) value,
 1002   						join.getPersistentClass().getClassName(),
 1003   						propertyName,
 1004   						subpath,
 1005   						true,
 1006   						false,
 1007   						mappings,
 1008   						inheritedMetas,
 1009   						false
 1010   					);
 1011   			}
 1012   
 1013   			if ( value != null ) {
 1014   				Property prop = createProperty( value, propertyName, persistentClass
 1015   					.getEntityName(), subnode, mappings, inheritedMetas );
 1016   				prop.setOptional( join.isOptional() );
 1017   				join.addProperty( prop );
 1018   			}
 1019   
 1020   		}
 1021   
 1022   		// CUSTOM SQL
 1023   		handleCustomSQL( node, join );
 1024   
 1025   	}
 1026   
 1027   	public static void bindColumns(final Element node, final SimpleValue simpleValue,
 1028   			final boolean isNullable, final boolean autoColumn, final String propertyPath,
 1029   			final Mappings mappings) throws MappingException {
 1030   
 1031   		Table table = simpleValue.getTable();
 1032   
 1033   		// COLUMN(S)
 1034   		Attribute columnAttribute = node.attribute( "column" );
 1035   		if ( columnAttribute == null ) {
 1036   			Iterator iter = node.elementIterator();
 1037   			int count = 0;
 1038   			while ( iter.hasNext() ) {
 1039   				Element columnElement = (Element) iter.next();
 1040   				if ( columnElement.getName().equals( "column" ) ) {
 1041   					Column column = new Column();
 1042   					column.setValue( simpleValue );
 1043   					column.setTypeIndex( count++ );
 1044   					bindColumn( columnElement, column, isNullable );
 1045   					String logicalColumnName = mappings.getNamingStrategy().logicalColumnName(
 1046   							columnElement.attributeValue( "name" ), propertyPath
 1047   					);
 1048   					column.setName( mappings.getNamingStrategy().columnName(
 1049   						logicalColumnName ) );
 1050   					if ( table != null ) {
 1051   						table.addColumn( column ); // table=null -> an association
 1052   						                           // - fill it in later
 1053   						//TODO fill in the mappings for table == null
 1054   						mappings.addColumnBinding( logicalColumnName, column, table );
 1055   					}
 1056   
 1057   
 1058   					simpleValue.addColumn( column );
 1059   					// column index
 1060   					bindIndex( columnElement.attribute( "index" ), table, column, mappings );
 1061   					bindIndex( node.attribute( "index" ), table, column, mappings );
 1062   					//column unique-key
 1063   					bindUniqueKey( columnElement.attribute( "unique-key" ), table, column, mappings );
 1064   					bindUniqueKey( node.attribute( "unique-key" ), table, column, mappings );
 1065   				}
 1066   				else if ( columnElement.getName().equals( "formula" ) ) {
 1067   					Formula formula = new Formula();
 1068   					formula.setFormula( columnElement.getText() );
 1069   					simpleValue.addFormula( formula );
 1070   				}
 1071   			}
 1072   		}
 1073   		else {
 1074   			if ( node.elementIterator( "column" ).hasNext() ) {
 1075   				throw new MappingException(
 1076   					"column attribute may not be used together with <column> subelement" );
 1077   			}
 1078   			if ( node.elementIterator( "formula" ).hasNext() ) {
 1079   				throw new MappingException(
 1080   					"column attribute may not be used together with <formula> subelement" );
 1081   			}
 1082   
 1083   			Column column = new Column();
 1084   			column.setValue( simpleValue );
 1085   			bindColumn( node, column, isNullable );
 1086   			String logicalColumnName = mappings.getNamingStrategy().logicalColumnName(
 1087   					columnAttribute.getValue(), propertyPath
 1088   			);
 1089   			column.setName( mappings.getNamingStrategy().columnName( logicalColumnName ) );
 1090   			if ( table != null ) {
 1091   				table.addColumn( column ); // table=null -> an association - fill
 1092   				                           // it in later
 1093   				//TODO fill in the mappings for table == null
 1094   				mappings.addColumnBinding( logicalColumnName, column, table );
 1095   			}
 1096   			simpleValue.addColumn( column );
 1097   			bindIndex( node.attribute( "index" ), table, column, mappings );
 1098   			bindUniqueKey( node.attribute( "unique-key" ), table, column, mappings );
 1099   		}
 1100   
 1101   		if ( autoColumn && simpleValue.getColumnSpan() == 0 ) {
 1102   			Column column = new Column();
 1103   			column.setValue( simpleValue );
 1104   			bindColumn( node, column, isNullable );
 1105   			column.setName( mappings.getNamingStrategy().propertyToColumnName( propertyPath ) );
 1106   			String logicalName = mappings.getNamingStrategy().logicalColumnName( null, propertyPath );
 1107   			mappings.addColumnBinding( logicalName, column, table );
 1108   			/* TODO: joinKeyColumnName & foreignKeyColumnName should be called either here or at a
 1109   			 * slightly higer level in the stack (to get all the information we need)
 1110   			 * Right now HbmBinder does not support the
 1111   			 */
 1112   			simpleValue.getTable().addColumn( column );
 1113   			simpleValue.addColumn( column );
 1114   			bindIndex( node.attribute( "index" ), table, column, mappings );
 1115   			bindUniqueKey( node.attribute( "unique-key" ), table, column, mappings );
 1116   		}
 1117   
 1118   	}
 1119   
 1120   	private static void bindIndex(Attribute indexAttribute, Table table, Column column, Mappings mappings) {
 1121   		if ( indexAttribute != null && table != null ) {
 1122   			StringTokenizer tokens = new StringTokenizer( indexAttribute.getValue(), ", " );
 1123   			while ( tokens.hasMoreTokens() ) {
 1124   				table.getOrCreateIndex( tokens.nextToken() ).addColumn( column );
 1125   			}
 1126   		}
 1127   	}
 1128   
 1129   	private static void bindUniqueKey(Attribute uniqueKeyAttribute, Table table, Column column, Mappings mappings) {
 1130   		if ( uniqueKeyAttribute != null && table != null ) {
 1131   			StringTokenizer tokens = new StringTokenizer( uniqueKeyAttribute.getValue(), ", " );
 1132   			while ( tokens.hasMoreTokens() ) {
 1133   				table.getOrCreateUniqueKey( tokens.nextToken() ).addColumn( column );
 1134   			}
 1135   		}
 1136   	}
 1137   
 1138   	// automatically makes a column with the default name if none is specifed by XML
 1139   	public static void bindSimpleValue(Element node, SimpleValue simpleValue, boolean isNullable,
 1140   			String path, Mappings mappings) throws MappingException {
 1141   		bindSimpleValueType( node, simpleValue, mappings );
 1142   
 1143   		bindColumnsOrFormula( node, simpleValue, path, isNullable, mappings );
 1144   
 1145   		Attribute fkNode = node.attribute( "foreign-key" );
 1146   		if ( fkNode != null ) simpleValue.setForeignKeyName( fkNode.getValue() );
 1147   	}
 1148   
 1149   	private static void bindSimpleValueType(Element node, SimpleValue simpleValue, Mappings mappings)
 1150   			throws MappingException {
 1151   		String typeName = null;
 1152   
 1153   		Properties parameters = new Properties();
 1154   
 1155   		Attribute typeNode = node.attribute( "type" );
 1156   		if ( typeNode == null ) typeNode = node.attribute( "id-type" ); // for an any
 1157   		if ( typeNode != null ) typeName = typeNode.getValue();
 1158   
 1159   		Element typeChild = node.element( "type" );
 1160   		if ( typeName == null && typeChild != null ) {
 1161   			typeName = typeChild.attribute( "name" ).getValue();
 1162   			Iterator typeParameters = typeChild.elementIterator( "param" );
 1163   
 1164   			while ( typeParameters.hasNext() ) {
 1165   				Element paramElement = (Element) typeParameters.next();
 1166   				parameters.setProperty(
 1167   						paramElement.attributeValue( "name" ),
 1168   						paramElement.getTextTrim()
 1169   					);
 1170   			}
 1171   		}
 1172   
 1173   		TypeDef typeDef = mappings.getTypeDef( typeName );
 1174   		if ( typeDef != null ) {
 1175   			typeName = typeDef.getTypeClass();
 1176   			// parameters on the property mapping should
 1177   			// override parameters in the typedef
 1178   			Properties allParameters = new Properties();
 1179   			allParameters.putAll( typeDef.getParameters() );
 1180   			allParameters.putAll( parameters );
 1181   			parameters = allParameters;
 1182   		}
 1183   
 1184   		if ( !parameters.isEmpty() ) simpleValue.setTypeParameters( parameters );
 1185   
 1186   		if ( typeName != null ) simpleValue.setTypeName( typeName );
 1187   	}
 1188   
 1189   	public static void bindProperty(
 1190   			Element node,
 1191   	        Property property,
 1192   	        Mappings mappings,
 1193   			java.util.Map inheritedMetas) throws MappingException {
 1194   
 1195   		String propName = node.attributeValue( "name" );
 1196   		property.setName( propName );
 1197   		String nodeName = node.attributeValue( "node" );
 1198   		if (nodeName==null) nodeName = propName;
 1199   		property.setNodeName( nodeName );
 1200   
 1201   		// TODO:
 1202   		//Type type = model.getValue().getType();
 1203   		//if (type==null) throw new MappingException(
 1204   		//"Could not determine a property type for: " + model.getName() );
 1205   
 1206   		Attribute accessNode = node.attribute( "access" );
 1207   		if ( accessNode != null ) {
 1208   			property.setPropertyAccessorName( accessNode.getValue() );
 1209   		}
 1210   		else if ( node.getName().equals( "properties" ) ) {
 1211   			property.setPropertyAccessorName( "embedded" );
 1212   		}
 1213   		else {
 1214   			property.setPropertyAccessorName( mappings.getDefaultAccess() );
 1215   		}
 1216   
 1217   		Attribute cascadeNode = node.attribute( "cascade" );
 1218   		property.setCascade( cascadeNode == null ? mappings.getDefaultCascade() : cascadeNode
 1219   			.getValue() );
 1220   
 1221   		Attribute updateNode = node.attribute( "update" );
 1222   		property.setUpdateable( updateNode == null || "true".equals( updateNode.getValue() ) );
 1223   
 1224   		Attribute insertNode = node.attribute( "insert" );
 1225   		property.setInsertable( insertNode == null || "true".equals( insertNode.getValue() ) );
 1226   
 1227   		Attribute lockNode = node.attribute( "optimistic-lock" );
 1228   		property.setOptimisticLocked( lockNode == null || "true".equals( lockNode.getValue() ) );
 1229   
 1230   		Attribute generatedNode = node.attribute( "generated" );
 1231           String generationName = generatedNode == null ? null : generatedNode.getValue();
 1232           PropertyGeneration generation = PropertyGeneration.parse( generationName );
 1233   		property.setGeneration( generation );
 1234   
 1235           if ( generation == PropertyGeneration.ALWAYS || generation == PropertyGeneration.INSERT ) {
 1236   	        // generated properties can *never* be insertable...
 1237   	        if ( property.isInsertable() ) {
 1238   		        if ( insertNode == null ) {
 1239   			        // insertable simply because that is the user did not specify
 1240   			        // anything; just override it
 1241   					property.setInsertable( false );
 1242   		        }
 1243   		        else {
 1244   			        // the user specifically supplied insert="true",
 1245   			        // which constitutes an illegal combo
 1246   					throw new MappingException(
 1247   							"cannot specify both insert=\"true\" and generated=\"" + generation.getName() +
 1248   							"\" for property: " +
 1249   							propName
 1250   					);
 1251   		        }
 1252   	        }
 1253   
 1254   	        // properties generated on update can never be updateable...
 1255   	        if ( property.isUpdateable() && generation == PropertyGeneration.ALWAYS ) {
 1256   		        if ( updateNode == null ) {
 1257   			        // updateable only because the user did not specify 
 1258   			        // anything; just override it
 1259   			        property.setUpdateable( false );
 1260   		        }
 1261   		        else {
 1262   			        // the user specifically supplied update="true",
 1263   			        // which constitutes an illegal combo
 1264   					throw new MappingException(
 1265   							"cannot specify both update=\"true\" and generated=\"" + generation.getName() +
 1266   							"\" for property: " +
 1267   							propName
 1268   					);
 1269   		        }
 1270   	        }
 1271           }
 1272   
 1273   		boolean isLazyable = "property".equals( node.getName() ) ||
 1274   				"component".equals( node.getName() ) ||
 1275   				"many-to-one".equals( node.getName() ) ||
 1276   				"one-to-one".equals( node.getName() ) ||
 1277   				"any".equals( node.getName() );
 1278   		if ( isLazyable ) {
 1279   			Attribute lazyNode = node.attribute( "lazy" );
 1280   			property.setLazy( lazyNode != null && "true".equals( lazyNode.getValue() ) );
 1281   		}
 1282   
 1283   		if ( log.isDebugEnabled() ) {
 1284   			String msg = "Mapped property: " + property.getName();
 1285   			String columns = columns( property.getValue() );
 1286   			if ( columns.length() > 0 ) msg += " -> " + columns;
 1287   			// TODO: this fails if we run with debug on!
 1288   			// if ( model.getType()!=null ) msg += ", type: " + model.getType().getName();
 1289   			log.debug( msg );
 1290   		}
 1291   
 1292   		property.setMetaAttributes( getMetas( node, inheritedMetas ) );
 1293   
 1294   	}
 1295   
 1296   	private static String columns(Value val) {
 1297   		StringBuffer columns = new StringBuffer();
 1298   		Iterator iter = val.getColumnIterator();
 1299   		while ( iter.hasNext() ) {
 1300   			columns.append( ( (Selectable) iter.next() ).getText() );
 1301   			if ( iter.hasNext() ) columns.append( ", " );
 1302   		}
 1303   		return columns.toString();
 1304   	}
 1305   
 1306   	/**
 1307   	 * Called for all collections
 1308   	 */
 1309   	public static void bindCollection(Element node, Collection collection, String className,
 1310   			String path, Mappings mappings, java.util.Map inheritedMetas) throws MappingException {
 1311   
 1312   		// ROLENAME
 1313   		collection.setRole(path);
 1314   
 1315   		Attribute inverseNode = node.attribute( "inverse" );
 1316   		if ( inverseNode != null ) {
 1317   			collection.setInverse( "true".equals( inverseNode.getValue() ) );
 1318   		}
 1319   
 1320   		Attribute mutableNode = node.attribute( "mutable" );
 1321   		if ( mutableNode != null ) {
 1322   			collection.setMutable( !"false".equals( mutableNode.getValue() ) );
 1323   		}
 1324   
 1325   		Attribute olNode = node.attribute( "optimistic-lock" );
 1326   		collection.setOptimisticLocked( olNode == null || "true".equals( olNode.getValue() ) );
 1327   
 1328   		Attribute orderNode = node.attribute( "order-by" );
 1329   		if ( orderNode != null ) {
 1330   			if ( Environment.jvmSupportsLinkedHashCollections() || ( collection instanceof Bag ) ) {
 1331   				collection.setOrderBy( orderNode.getValue() );
 1332   			}
 1333   			else {
 1334   				log.warn( "Attribute \"order-by\" ignored in JDK1.3 or less" );
 1335   			}
 1336   		}
 1337   		Attribute whereNode = node.attribute( "where" );
 1338   		if ( whereNode != null ) {
 1339   			collection.setWhere( whereNode.getValue() );
 1340   		}
 1341   		Attribute batchNode = node.attribute( "batch-size" );
 1342   		if ( batchNode != null ) {
 1343   			collection.setBatchSize( Integer.parseInt( batchNode.getValue() ) );
 1344   		}
 1345   
 1346   		String nodeName = node.attributeValue( "node" );
 1347   		if ( nodeName == null ) nodeName = node.attributeValue( "name" );
 1348   		collection.setNodeName( nodeName );
 1349   		String embed = node.attributeValue( "embed-xml" );
 1350   		collection.setEmbedded( embed==null || "true".equals(embed) );
 1351   
 1352   
 1353   		// PERSISTER
 1354   		Attribute persisterNode = node.attribute( "persister" );
 1355   		if ( persisterNode != null ) {
 1356   			try {
 1357   				collection.setCollectionPersisterClass( ReflectHelper.classForName( persisterNode
 1358   					.getValue() ) );
 1359   			}
 1360   			catch (ClassNotFoundException cnfe) {
 1361   				throw new MappingException( "Could not find collection persister class: "
 1362   					+ persisterNode.getValue() );
 1363   			}
 1364   		}
 1365   
 1366   		Attribute typeNode = node.attribute( "collection-type" );
 1367   		if ( typeNode != null ) {
 1368   			String typeName = typeNode.getValue();
 1369   			TypeDef typeDef = mappings.getTypeDef( typeName );
 1370   			if ( typeDef != null ) {
 1371   				collection.setTypeName( typeDef.getTypeClass() );
 1372   				collection.setTypeParameters( typeDef.getParameters() );
 1373   			}
 1374   			else {
 1375   				collection.setTypeName( typeName );
 1376   			}
 1377   		}
 1378   
 1379   		// FETCH STRATEGY
 1380   
 1381   		initOuterJoinFetchSetting( node, collection );
 1382   
 1383   		if ( "subselect".equals( node.attributeValue("fetch") ) ) {
 1384   			collection.setSubselectLoadable(true);
 1385   			collection.getOwner().setSubselectLoadableCollections(true);
 1386   		}
 1387   
 1388   		initLaziness( node, collection, mappings, "true", mappings.isDefaultLazy() );
 1389   		//TODO: suck this into initLaziness!
 1390   		if ( "extra".equals( node.attributeValue("lazy") ) ) {
 1391   			collection.setLazy(true);
 1392   			collection.setExtraLazy(true);
 1393   		}
 1394   
 1395   		Element oneToManyNode = node.element( "one-to-many" );
 1396   		if ( oneToManyNode != null ) {
 1397   			OneToMany oneToMany = new OneToMany( collection.getOwner() );
 1398   			collection.setElement( oneToMany );
 1399   			bindOneToMany( oneToManyNode, oneToMany, mappings );
 1400   			// we have to set up the table later!! yuck
 1401   		}
 1402   		else {
 1403   			// TABLE
 1404   			Attribute tableNode = node.attribute( "table" );
 1405   			String tableName;
 1406   			if ( tableNode != null ) {
 1407   				tableName = mappings.getNamingStrategy().tableName( tableNode.getValue() );
 1408   			}
 1409   			else {
 1410   				//tableName = mappings.getNamingStrategy().propertyToTableName( className, path );
 1411   				Table ownerTable = collection.getOwner().getTable();
 1412   				//TODO mappings.getLogicalTableName(ownerTable)
 1413   				String logicalOwnerTableName = ownerTable.getName();
 1414   				//FIXME we don't have the associated entity table name here, has to be done in a second pass
 1415   				tableName = mappings.getNamingStrategy().collectionTableName(
 1416   						collection.getOwner().getEntityName(),
 1417   						logicalOwnerTableName ,
 1418   						null,
 1419   						null,
 1420   						path
 1421   				);
 1422   			}
 1423   			Attribute schemaNode = node.attribute( "schema" );
 1424   			String schema = schemaNode == null ?
 1425   					mappings.getSchemaName() : schemaNode.getValue();
 1426   
 1427   			Attribute catalogNode = node.attribute( "catalog" );
 1428   			String catalog = catalogNode == null ?
 1429   					mappings.getCatalogName() : catalogNode.getValue();
 1430   
 1431   			Table table = mappings.addTable(
 1432   					schema,
 1433   					catalog,
 1434   					tableName,
 1435   					getSubselect( node ),
 1436   					false
 1437   				);
 1438   			collection.setCollectionTable( table );
 1439   			bindComment(table, node);
 1440   
 1441   			log.info(
 1442   					"Mapping collection: " + collection.getRole() +
 1443   					" -> " + collection.getCollectionTable().getName()
 1444   				);
 1445   		}
 1446   
 1447   		// SORT
 1448   		Attribute sortedAtt = node.attribute( "sort" );
 1449   		// unsorted, natural, comparator.class.name
 1450   		if ( sortedAtt == null || sortedAtt.getValue().equals( "unsorted" ) ) {
 1451   			collection.setSorted( false );
 1452   		}
 1453   		else {
 1454   			collection.setSorted( true );
 1455   			String comparatorClassName = sortedAtt.getValue();
 1456   			if ( !comparatorClassName.equals( "natural" ) ) {
 1457   				collection.setComparatorClassName(comparatorClassName);
 1458   			}
 1459   		}
 1460   
 1461   		// ORPHAN DELETE (used for programmer error detection)
 1462   		Attribute cascadeAtt = node.attribute( "cascade" );
 1463   		if ( cascadeAtt != null && cascadeAtt.getValue().indexOf( "delete-orphan" ) >= 0 ) {
 1464   			collection.setOrphanDelete( true );
 1465   		}
 1466   
 1467   		// CUSTOM SQL
 1468   		handleCustomSQL( node, collection );
 1469   		// set up second pass
 1470   		if ( collection instanceof List ) {
 1471   			mappings.addSecondPass( new ListSecondPass( node, mappings, (List) collection, inheritedMetas ) );
 1472   		}
 1473   		else if ( collection instanceof Map ) {
 1474   			mappings.addSecondPass( new MapSecondPass( node, mappings, (Map) collection, inheritedMetas ) );
 1475   		}
 1476   		else if ( collection instanceof IdentifierCollection ) {
 1477   			mappings.addSecondPass( new IdentifierCollectionSecondPass(
 1478   					node,
 1479   					mappings,
 1480   					collection,
 1481   					inheritedMetas
 1482   				) );
 1483   		}
 1484   		else {
 1485   			mappings.addSecondPass( new CollectionSecondPass( node, mappings, collection, inheritedMetas ) );
 1486   		}
 1487   
 1488   		Iterator iter = node.elementIterator( "filter" );
 1489   		while ( iter.hasNext() ) {
 1490   			final Element filter = (Element) iter.next();
 1491   			parseFilter( filter, collection, mappings );
 1492   		}
 1493   
 1494   		Iterator tables = node.elementIterator( "synchronize" );
 1495   		while ( tables.hasNext() ) {
 1496   			collection.getSynchronizedTables().add(
 1497   				( (Element) tables.next() ).attributeValue( "table" ) );
 1498   		}
 1499   
 1500   		Element element = node.element( "loader" );
 1501   		if ( element != null ) {
 1502   			collection.setLoaderName( element.attributeValue( "query-ref" ) );
 1503   		}
 1504   
 1505   		collection.setReferencedPropertyName( node.element( "key" ).attributeValue( "property-ref" ) );
 1506   	}
 1507   
 1508   	private static void initLaziness(
 1509   			Element node,
 1510   			Fetchable fetchable,
 1511   			Mappings mappings,
 1512   			String proxyVal,
 1513   			boolean defaultLazy
 1514   	) {
 1515   		Attribute lazyNode = node.attribute( "lazy" );
 1516   		boolean isLazyTrue = lazyNode == null ?
 1517   				defaultLazy && fetchable.isLazy() : //fetch="join" overrides default laziness
 1518   				lazyNode.getValue().equals(proxyVal); //fetch="join" overrides default laziness
 1519   		fetchable.setLazy( isLazyTrue );
 1520   	}
 1521   
 1522   	private static void initLaziness(
 1523   			Element node,
 1524   			ToOne fetchable,
 1525   			Mappings mappings,
 1526   			boolean defaultLazy
 1527   	) {
 1528   		if ( "no-proxy".equals( node.attributeValue( "lazy" ) ) ) {
 1529   			fetchable.setUnwrapProxy(true);
 1530   			fetchable.setLazy(true);
 1531   			//TODO: better to degrade to lazy="false" if uninstrumented
 1532   		}
 1533   		else {
 1534   			initLaziness(node, fetchable, mappings, "proxy", defaultLazy);
 1535   		}
 1536   	}
 1537   
 1538   	private static void bindColumnsOrFormula(Element node, SimpleValue simpleValue, String path,
 1539   			boolean isNullable, Mappings mappings) {
 1540   		Attribute formulaNode = node.attribute( "formula" );
 1541   		if ( formulaNode != null ) {
 1542   			Formula f = new Formula();
 1543   			f.setFormula( formulaNode.getText() );
 1544   			simpleValue.addFormula( f );
 1545   		}
 1546   		else {
 1547   			bindColumns( node, simpleValue, isNullable, true, path, mappings );
 1548   		}
 1549   	}
 1550   
 1551   	private static void bindComment(Table table, Element node) {
 1552   		Element comment = node.element("comment");
 1553   		if (comment!=null) table.setComment( comment.getTextTrim() );
 1554   	}
 1555   
 1556   	public static void bindManyToOne(Element node, ManyToOne manyToOne, String path,
 1557   			boolean isNullable, Mappings mappings) throws MappingException {
 1558   
 1559   		bindColumnsOrFormula( node, manyToOne, path, isNullable, mappings );
 1560   		initOuterJoinFetchSetting( node, manyToOne );
 1561   		initLaziness( node, manyToOne, mappings, true );
 1562   
 1563   		Attribute ukName = node.attribute( "property-ref" );
 1564   		if ( ukName != null ) {
 1565   			manyToOne.setReferencedPropertyName( ukName.getValue() );
 1566   		}
 1567   
 1568   		manyToOne.setReferencedEntityName( getEntityName( node, mappings ) );
 1569   
 1570   		String embed = node.attributeValue( "embed-xml" );
 1571   		manyToOne.setEmbedded( embed == null || "true".equals( embed ) );
 1572   
 1573   		String notFound = node.attributeValue( "not-found" );
 1574   		manyToOne.setIgnoreNotFound( "ignore".equals( notFound ) );
 1575   
 1576   		if( ukName != null && !manyToOne.isIgnoreNotFound() ) {
 1577   			if ( !node.getName().equals("many-to-many") ) { //TODO: really bad, evil hack to fix!!!
 1578   				mappings.addSecondPass( new ManyToOneSecondPass(manyToOne) );
 1579   			}
 1580   		}
 1581   
 1582   		Attribute fkNode = node.attribute( "foreign-key" );
 1583   		if ( fkNode != null ) manyToOne.setForeignKeyName( fkNode.getValue() );
 1584   
 1585   		validateCascade( node, path );
 1586   	}
 1587   
 1588   	private static void validateCascade(Element node, String path) {
 1589   		String cascade = node.attributeValue( "cascade" );
 1590   		if ( cascade != null && cascade.indexOf( "delete-orphan" ) >= 0 ) {
 1591   			throw new MappingException( "single-valued associations do not support orphan delete: " + path );
 1592   		}
 1593   	}
 1594   
 1595   	public static void bindAny(Element node, Any any, boolean isNullable, Mappings mappings)
 1596   			throws MappingException {
 1597   		any.setIdentifierType( getTypeFromXML( node ) );
 1598   		Attribute metaAttribute = node.attribute( "meta-type" );
 1599   		if ( metaAttribute != null ) {
 1600   			any.setMetaType( metaAttribute.getValue() );
 1601   
 1602   			Iterator iter = node.elementIterator( "meta-value" );
 1603   			if ( iter.hasNext() ) {
 1604   				HashMap values = new HashMap();
 1605   				org.hibernate.type.Type metaType = TypeFactory.heuristicType( any.getMetaType() );
 1606   				while ( iter.hasNext() ) {
 1607   					Element metaValue = (Element) iter.next();
 1608   					try {
 1609   						Object value = ( (DiscriminatorType) metaType ).stringToObject( metaValue
 1610   							.attributeValue( "value" ) );
 1611   						String entityName = getClassName( metaValue.attribute( "class" ), mappings );
 1612   						values.put( value, entityName );
 1613   					}
 1614   					catch (ClassCastException cce) {
 1615   						throw new MappingException( "meta-type was not a DiscriminatorType: "
 1616   							+ metaType.getName() );
 1617   					}
 1618   					catch (Exception e) {
 1619   						throw new MappingException( "could not interpret meta-value", e );
 1620   					}
 1621   				}
 1622   				any.setMetaValues( values );
 1623   			}
 1624   
 1625   		}
 1626   
 1627   		bindColumns( node, any, isNullable, false, null, mappings );
 1628   	}
 1629   
 1630   	public static void bindOneToOne(Element node, OneToOne oneToOne, String path, boolean isNullable,
 1631   			Mappings mappings) throws MappingException {
 1632   
 1633   		bindColumns( node, oneToOne, isNullable, false, null, mappings );
 1634   
 1635   		Attribute constrNode = node.attribute( "constrained" );
 1636   		boolean constrained = constrNode != null && constrNode.getValue().equals( "true" );
 1637   		oneToOne.setConstrained( constrained );
 1638   
 1639   		oneToOne.setForeignKeyType( constrained ?
 1640   				ForeignKeyDirection.FOREIGN_KEY_FROM_PARENT :
 1641   				ForeignKeyDirection.FOREIGN_KEY_TO_PARENT );
 1642   
 1643   		initOuterJoinFetchSetting( node, oneToOne );
 1644   		initLaziness( node, oneToOne, mappings, true );
 1645   
 1646   		oneToOne.setEmbedded( "true".equals( node.attributeValue( "embed-xml" ) ) );
 1647   
 1648   		Attribute fkNode = node.attribute( "foreign-key" );
 1649   		if ( fkNode != null ) oneToOne.setForeignKeyName( fkNode.getValue() );
 1650   
 1651   		Attribute ukName = node.attribute( "property-ref" );
 1652   		if ( ukName != null ) oneToOne.setReferencedPropertyName( ukName.getValue() );
 1653   
 1654   		oneToOne.setPropertyName( node.attributeValue( "name" ) );
 1655   
 1656   		oneToOne.setReferencedEntityName( getEntityName( node, mappings ) );
 1657   
 1658   		validateCascade( node, path );
 1659   	}
 1660   
 1661   	public static void bindOneToMany(Element node, OneToMany oneToMany, Mappings mappings)
 1662   			throws MappingException {
 1663   
 1664   		oneToMany.setReferencedEntityName( getEntityName( node, mappings ) );
 1665   
 1666   		String embed = node.attributeValue( "embed-xml" );
 1667   		oneToMany.setEmbedded( embed == null || "true".equals( embed ) );
 1668   
 1669   		String notFound = node.attributeValue( "not-found" );
 1670   		oneToMany.setIgnoreNotFound( "ignore".equals( notFound ) );
 1671   
 1672   	}
 1673   
 1674   	public static void bindColumn(Element node, Column column, boolean isNullable) {
 1675   		Attribute lengthNode = node.attribute( "length" );
 1676   		if ( lengthNode != null ) column.setLength( Integer.parseInt( lengthNode.getValue() ) );
 1677   		Attribute scalNode = node.attribute( "scale" );
 1678   		if ( scalNode != null ) column.setScale( Integer.parseInt( scalNode.getValue() ) );
 1679   		Attribute precNode = node.attribute( "precision" );
 1680   		if ( precNode != null ) column.setPrecision( Integer.parseInt( precNode.getValue() ) );
 1681   
 1682   		Attribute nullNode = node.attribute( "not-null" );
 1683   		column.setNullable( nullNode == null ? isNullable : nullNode.getValue().equals( "false" ) );
 1684   
 1685   		Attribute unqNode = node.attribute( "unique" );
 1686   		if ( unqNode != null ) column.setUnique( unqNode.getValue().equals( "true" ) );
 1687   
 1688   		column.setCheckConstraint( node.attributeValue( "check" ) );
 1689   		column.setDefaultValue( node.attributeValue( "default" ) );
 1690   
 1691   		Attribute typeNode = node.attribute( "sql-type" );
 1692   		if ( typeNode != null ) column.setSqlType( typeNode.getValue() );
 1693   
 1694   		Element comment = node.element("comment");
 1695   		if (comment!=null) column.setComment( comment.getTextTrim() );
 1696   
 1697   	}
 1698   
 1699   	/**
 1700   	 * Called for arrays and primitive arrays
 1701   	 */
 1702   	public static void bindArray(Element node, Array array, String prefix, String path,
 1703   			Mappings mappings, java.util.Map inheritedMetas) throws MappingException {
 1704   
 1705   		bindCollection( node, array, prefix, path, mappings, inheritedMetas );
 1706   
 1707   		Attribute att = node.attribute( "element-class" );
 1708   		if ( att != null ) array.setElementClassName( getClassName( att, mappings ) );
 1709   
 1710   	}
 1711   
 1712   	private static Class reflectedPropertyClass(String className, String propertyName)
 1713   			throws MappingException {
 1714   		if ( className == null ) return null;
 1715   		return ReflectHelper.reflectedPropertyClass( className, propertyName );
 1716   	}
 1717   
 1718   	public static void bindComposite(Element node, Component component, String path,
 1719   			boolean isNullable, Mappings mappings, java.util.Map inheritedMetas)
 1720   			throws MappingException {
 1721   		bindComponent(
 1722   				node,
 1723   				component,
 1724   				null,
 1725   				null,
 1726   				path,
 1727   				isNullable,
 1728   				false,
 1729   				mappings,
 1730   				inheritedMetas,
 1731   				false
 1732   			);
 1733   	}
 1734   
 1735   	public static void bindCompositeId(Element node, Component component,
 1736   			PersistentClass persistentClass, String propertyName, Mappings mappings,
 1737   			java.util.Map inheritedMetas) throws MappingException {
 1738   
 1739   		component.setKey( true );
 1740   
 1741   		String path = StringHelper.qualify(
 1742   				persistentClass.getEntityName(),
 1743   				propertyName == null ? "id" : propertyName );
 1744   
 1745   		bindComponent(
 1746   				node,
 1747   				component,
 1748   				persistentClass.getClassName(),
 1749   				propertyName,
 1750   				path,
 1751   				false,
 1752   				node.attribute( "class" ) == null
 1753   						&& propertyName == null,
 1754   				mappings,
 1755   				inheritedMetas,
 1756   				false
 1757   			);
 1758   
 1759   		if ( "true".equals( node.attributeValue("mapped") ) ) {
 1760   			if ( propertyName!=null ) {
 1761   				throw new MappingException("cannot combine mapped=\"true\" with specified name");
 1762   			}
 1763   			Component mapper = new Component(persistentClass);
 1764   			bindComponent(
 1765   					node,
 1766   					mapper,
 1767   					persistentClass.getClassName(),
 1768   					null,
 1769   					path,
 1770   					false,
 1771   					true,
 1772   					mappings,
 1773   					inheritedMetas,
 1774   					true
 1775   				);
 1776   			persistentClass.setIdentifierMapper(mapper);
 1777   			Property property = new Property();
 1778   			property.setName("_identifierMapper");
 1779   			property.setNodeName("id");
 1780   			property.setUpdateable(false);
 1781   			property.setInsertable(false);
 1782   			property.setValue(mapper);
 1783   			property.setPropertyAccessorName( "embedded" );
 1784   			persistentClass.addProperty(property);
 1785   		}
 1786   
 1787   	}
 1788   
 1789   	public static void bindComponent(
 1790   			Element node,
 1791   			Component component,
 1792   			String ownerClassName,
 1793   			String parentProperty,
 1794   			String path,
 1795   			boolean isNullable,
 1796   			boolean isEmbedded,
 1797   			Mappings mappings,
 1798   			java.util.Map inheritedMetas,
 1799   			boolean isIdentifierMapper) throws MappingException {
 1800   
 1801   		component.setEmbedded( isEmbedded );
 1802   		component.setRoleName( path );
 1803   
 1804   		inheritedMetas = getMetas( node, inheritedMetas );
 1805   		component.setMetaAttributes( inheritedMetas );
 1806   
 1807   		Attribute classNode = isIdentifierMapper ? null : node.attribute( "class" );
 1808   		if ( classNode != null ) {
 1809   			component.setComponentClassName( getClassName( classNode, mappings ) );
 1810   		}
 1811   		else if ( "dynamic-component".equals( node.getName() ) ) {
 1812   			component.setDynamic( true );
 1813   		}
 1814   		else if ( isEmbedded ) {
 1815   			// an "embedded" component (composite ids and unique)
 1816   			// note that this does not handle nested components
 1817   			if ( component.getOwner().hasPojoRepresentation() ) {
 1818   				component.setComponentClassName( component.getOwner().getClassName() );
 1819   			}
 1820   			else {
 1821   				component.setDynamic(true);
 1822   			}
 1823   		}
 1824   		else {
 1825   			// todo : again, how *should* this work for non-pojo entities?
 1826   			if ( component.getOwner().hasPojoRepresentation() ) {
 1827   				Class reflectedClass = reflectedPropertyClass( ownerClassName, parentProperty );
 1828   				if ( reflectedClass != null ) {
 1829   					component.setComponentClassName( reflectedClass.getName() );
 1830   				}
 1831   			}
 1832   			else {
 1833   				component.setDynamic(true);
 1834   			}
 1835   		}
 1836   
 1837   		String nodeName = node.attributeValue( "node" );
 1838   		if ( nodeName == null ) nodeName = node.attributeValue( "name" );
 1839   		if ( nodeName == null ) nodeName = component.getOwner().getNodeName();
 1840   		component.setNodeName( nodeName );
 1841   
 1842   		Iterator iter = node.elementIterator();
 1843   		while ( iter.hasNext() ) {
 1844   
 1845   			Element subnode = (Element) iter.next();
 1846   			String name = subnode.getName();
 1847   			String propertyName = getPropertyName( subnode );
 1848   			String subpath = propertyName == null ? null : StringHelper
 1849   				.qualify( path, propertyName );
 1850   
 1851   			CollectionType collectType = CollectionType.collectionTypeFromString( name );
 1852   			Value value = null;
 1853   			if ( collectType != null ) {
 1854   				Collection collection = collectType.create(
 1855   						subnode,
 1856   						subpath,
 1857   						component.getOwner(),
 1858   						mappings, inheritedMetas
 1859   					);
 1860   				mappings.addCollection( collection );
 1861   				value = collection;
 1862   			}
 1863   			else if ( "many-to-one".equals( name ) || "key-many-to-one".equals( name ) ) {
 1864   				value = new ManyToOne( component.getTable() );
 1865   				String relativePath;
 1866   				if (isEmbedded) {
 1867   					relativePath = propertyName;
 1868   				}
 1869   				else {
 1870   					relativePath = subpath.substring( component.getOwner().getEntityName().length() + 1 );
 1871   				}
 1872   				bindManyToOne( subnode, (ManyToOne) value, relativePath, isNullable, mappings );
 1873   			}
 1874   			else if ( "one-to-one".equals( name ) ) {
 1875   				value = new OneToOne( component.getTable(), component.getOwner() );
 1876   				String relativePath;
 1877   				if (isEmbedded) {
 1878   					relativePath = propertyName;
 1879   				}
 1880   				else {
 1881   					relativePath = subpath.substring( component.getOwner().getEntityName().length() + 1 );
 1882   				}
 1883   				bindOneToOne( subnode, (OneToOne) value, relativePath, isNullable, mappings );
 1884   			}
 1885   			else if ( "any".equals( name ) ) {
 1886   				value = new Any( component.getTable() );
 1887   				bindAny( subnode, (Any) value, isNullable, mappings );
 1888   			}
 1889   			else if ( "property".equals( name ) || "key-property".equals( name ) ) {
 1890   				value = new SimpleValue( component.getTable() );
 1891   				String relativePath;
 1892   				if (isEmbedded) {
 1893   					relativePath = propertyName;
 1894   				}
 1895   				else {
 1896   					relativePath = subpath.substring( component.getOwner().getEntityName().length() + 1 );
 1897   				}
 1898   				bindSimpleValue( subnode, (SimpleValue) value, isNullable, relativePath, mappings );
 1899   			}
 1900   			else if ( "component".equals( name )
 1901   				|| "dynamic-component".equals( name )
 1902   				|| "nested-composite-element".equals( name ) ) {
 1903   				value = new Component( component ); // a nested composite element
 1904   				bindComponent(
 1905   						subnode,
 1906   						(Component) value,
 1907   						component.getComponentClassName(),
 1908   						propertyName,
 1909   						subpath,
 1910   						isNullable,
 1911   						isEmbedded,
 1912   						mappings,
 1913   						inheritedMetas,
 1914   						isIdentifierMapper
 1915   					);
 1916   			}
 1917   			else if ( "parent".equals( name ) ) {
 1918   				component.setParentProperty( propertyName );
 1919   			}
 1920   
 1921   			if ( value != null ) {
 1922   				Property property = createProperty( value, propertyName, component
 1923   					.getComponentClassName(), subnode, mappings, inheritedMetas );
 1924   				if (isIdentifierMapper) {
 1925   					property.setInsertable(false);
 1926   					property.setUpdateable(false);
 1927   				}
 1928   				component.addProperty( property );
 1929   			}
 1930   		}
 1931   
 1932   		if ( "true".equals( node.attributeValue( "unique" ) ) ) {
 1933   			iter = component.getColumnIterator();
 1934   			ArrayList cols = new ArrayList();
 1935   			while ( iter.hasNext() ) {
 1936   				cols.add( iter.next() );
 1937   			}
 1938   			component.getOwner().getTable().createUniqueKey( cols );
 1939   		}
 1940   
 1941   		iter = node.elementIterator( "tuplizer" );
 1942   		while ( iter.hasNext() ) {
 1943   			final Element tuplizerElem = ( Element ) iter.next();
 1944   			EntityMode mode = EntityMode.parse( tuplizerElem.attributeValue( "entity-mode" ) );
 1945   			component.addTuplizer( mode, tuplizerElem.attributeValue( "class" ) );
 1946   		}
 1947   	}
 1948   
 1949   	public static String getTypeFromXML(Element node) throws MappingException {
 1950   		// TODO: handle TypeDefs
 1951   		Attribute typeNode = node.attribute( "type" );
 1952   		if ( typeNode == null ) typeNode = node.attribute( "id-type" ); // for an any
 1953   		if ( typeNode == null ) return null; // we will have to use reflection
 1954   		return typeNode.getValue();
 1955   	}
 1956   
 1957   	private static void initOuterJoinFetchSetting(Element node, Fetchable model) {
 1958   		Attribute fetchNode = node.attribute( "fetch" );
 1959   		final FetchMode fetchStyle;
 1960   		boolean lazy = true;
 1961   		if ( fetchNode == null ) {
 1962   			Attribute jfNode = node.attribute( "outer-join" );
 1963   			if ( jfNode == null ) {
 1964   				if ( "many-to-many".equals( node.getName() ) ) {
 1965   					//NOTE SPECIAL CASE:
 1966   					// default to join and non-lazy for the "second join"
 1967   					// of the many-to-many
 1968   					lazy = false;
 1969   					fetchStyle = FetchMode.JOIN;
 1970   				}
 1971   				else if ( "one-to-one".equals( node.getName() ) ) {
 1972   					//NOTE SPECIAL CASE:
 1973   					// one-to-one constrained=false cannot be proxied,
 1974   					// so default to join and non-lazy
 1975   					lazy = ( (OneToOne) model ).isConstrained();
 1976   					fetchStyle = lazy ? FetchMode.DEFAULT : FetchMode.JOIN;
 1977   				}
 1978   				else {
 1979   					fetchStyle = FetchMode.DEFAULT;
 1980   				}
 1981   			}
 1982   			else {
 1983   				// use old (HB 2.1) defaults if outer-join is specified
 1984   				String eoj = jfNode.getValue();
 1985   				if ( "auto".equals( eoj ) ) {
 1986   					fetchStyle = FetchMode.DEFAULT;
 1987   				}
 1988   				else {
 1989   					boolean join = "true".equals( eoj );
 1990   					fetchStyle = join ? FetchMode.JOIN : FetchMode.SELECT;
 1991   				}
 1992   			}
 1993   		}
 1994   		else {
 1995   			boolean join = "join".equals( fetchNode.getValue() );
 1996   			//lazy = !join;
 1997   			fetchStyle = join ? FetchMode.JOIN : FetchMode.SELECT;
 1998   		}
 1999   		model.setFetchMode( fetchStyle );
 2000   		model.setLazy(lazy);
 2001   	}
 2002   
 2003   	private static void makeIdentifier(Element node, SimpleValue model, Mappings mappings) {
 2004   
 2005   		// GENERATOR
 2006   		Element subnode = node.element( "generator" );
 2007   		if ( subnode != null ) {
 2008   			model.setIdentifierGeneratorStrategy( subnode.attributeValue( "class" ) );
 2009   
 2010   			Properties params = new Properties();
 2011   
 2012   			if ( mappings.getSchemaName() != null ) {
 2013   				params.setProperty( PersistentIdentifierGenerator.SCHEMA, mappings.getSchemaName() );
 2014   			}
 2015   			if ( mappings.getCatalogName() != null ) {
 2016   				params.setProperty( PersistentIdentifierGenerator.CATALOG, mappings.getCatalogName() );
 2017   			}
 2018   
 2019   			Iterator iter = subnode.elementIterator( "param" );
 2020   			while ( iter.hasNext() ) {
 2021   				Element childNode = (Element) iter.next();
 2022   				params.setProperty( childNode.attributeValue( "name" ), childNode.getTextTrim() );
 2023   			}
 2024   
 2025   			model.setIdentifierGeneratorProperties( params );
 2026   		}
 2027   
 2028   		model.getTable().setIdentifierValue( model );
 2029   
 2030   		// ID UNSAVED-VALUE
 2031   		Attribute nullValueNode = node.attribute( "unsaved-value" );
 2032   		if ( nullValueNode != null ) {
 2033   			model.setNullValue( nullValueNode.getValue() );
 2034   		}
 2035   		else {
 2036   			if ( "assigned".equals( model.getIdentifierGeneratorStrategy() ) ) {
 2037   				model.setNullValue( "undefined" );
 2038   			}
 2039   			else {
 2040   				model.setNullValue( null );
 2041   			}
 2042   		}
 2043   	}
 2044   
 2045   	private static final void makeVersion(Element node, SimpleValue model) {
 2046   
 2047   		// VERSION UNSAVED-VALUE
 2048   		Attribute nullValueNode = node.attribute( "unsaved-value" );
 2049   		if ( nullValueNode != null ) {
 2050   			model.setNullValue( nullValueNode.getValue() );
 2051   		}
 2052   		else {
 2053   			model.setNullValue( "undefined" );
 2054   		}
 2055   
 2056   	}
 2057   
 2058   	protected static void createClassProperties(Element node, PersistentClass persistentClass,
 2059   			Mappings mappings, java.util.Map inheritedMetas) throws MappingException {
 2060   		createClassProperties(node, persistentClass, mappings, inheritedMetas, null, true, true, false);
 2061   	}
 2062   
 2063   	protected static void createClassProperties(Element node, PersistentClass persistentClass,
 2064   			Mappings mappings, java.util.Map inheritedMetas, UniqueKey uniqueKey,
 2065   			boolean mutable, boolean nullable, boolean naturalId) throws MappingException {
 2066   
 2067   		String entityName = persistentClass.getEntityName();
 2068   		Table table = persistentClass.getTable();
 2069   
 2070   		Iterator iter = node.elementIterator();
 2071   		while ( iter.hasNext() ) {
 2072   			Element subnode = (Element) iter.next();
 2073   			String name = subnode.getName();
 2074   			String propertyName = subnode.attributeValue( "name" );
 2075   
 2076   			CollectionType collectType = CollectionType.collectionTypeFromString( name );
 2077   			Value value = null;
 2078   			if ( collectType != null ) {
 2079   				Collection collection = collectType.create(
 2080   						subnode,
 2081   						StringHelper.qualify( entityName, propertyName ),
 2082   						persistentClass,
 2083   						mappings, inheritedMetas
 2084   					);
 2085   				mappings.addCollection( collection );
 2086   				value = collection;
 2087   			}
 2088   			else if ( "many-to-one".equals( name ) ) {
 2089   				value = new ManyToOne( table );
 2090   				bindManyToOne( subnode, (ManyToOne) value, propertyName, nullable, mappings );
 2091   			}
 2092   			else if ( "any".equals( name ) ) {
 2093   				value = new Any( table );
 2094   				bindAny( subnode, (Any) value, nullable, mappings );
 2095   			}
 2096   			else if ( "one-to-one".equals( name ) ) {
 2097   				value = new OneToOne( table, persistentClass );
 2098   				bindOneToOne( subnode, (OneToOne) value, propertyName, true, mappings );
 2099   			}
 2100   			else if ( "property".equals( name ) ) {
 2101   				value = new SimpleValue( table );
 2102   				bindSimpleValue( subnode, (SimpleValue) value, nullable, propertyName, mappings );
 2103   			}
 2104   			else if ( "component".equals( name )
 2105   				|| "dynamic-component".equals( name )
 2106   				|| "properties".equals( name ) ) {
 2107   				String subpath = StringHelper.qualify( entityName, propertyName );
 2108   				value = new Component( persistentClass );
 2109   
 2110   				bindComponent(
 2111   						subnode,
 2112   						(Component) value,
 2113   						persistentClass.getClassName(),
 2114   						propertyName,
 2115   						subpath,
 2116   						true,
 2117   						"properties".equals( name ),
 2118   						mappings,
 2119   						inheritedMetas,
 2120   						false
 2121   					);
 2122   			}
 2123   			else if ( "join".equals( name ) ) {
 2124   				Join join = new Join();
 2125   				join.setPersistentClass( persistentClass );
 2126   				bindJoin( subnode, join, mappings, inheritedMetas );
 2127   				persistentClass.addJoin( join );
 2128   			}
 2129   			else if ( "subclass".equals( name ) ) {
 2130   				handleSubclass( persistentClass, mappings, subnode, inheritedMetas );
 2131   			}
 2132   			else if ( "joined-subclass".equals( name ) ) {
 2133   				handleJoinedSubclass( persistentClass, mappings, subnode, inheritedMetas );
 2134   			}
 2135   			else if ( "union-subclass".equals( name ) ) {
 2136   				handleUnionSubclass( persistentClass, mappings, subnode, inheritedMetas );
 2137   			}
 2138   			else if ( "filter".equals( name ) ) {
 2139   				parseFilter( subnode, persistentClass, mappings );
 2140   			}
 2141   			else if ( "natural-id".equals( name ) ) {
 2142   				UniqueKey uk = new UniqueKey();
 2143   				uk.setName("_UniqueKey");
 2144   				uk.setTable(table);
 2145   				//by default, natural-ids are "immutable" (constant)
 2146   				boolean mutableId = "true".equals( subnode.attributeValue("mutable") );
 2147   				createClassProperties(
 2148   						subnode,
 2149   						persistentClass,
 2150   						mappings,
 2151   						inheritedMetas,
 2152   						uk,
 2153   						mutableId,
 2154   						false,
 2155   						true
 2156   					);
 2157   				table.addUniqueKey(uk);
 2158   			}
 2159   			else if ( "query".equals(name) ) {
 2160   				bindNamedQuery(subnode, persistentClass.getEntityName(), mappings);
 2161   			}
 2162   			else if ( "sql-query".equals(name) ) {
 2163   				bindNamedSQLQuery(subnode, persistentClass.getEntityName(), mappings);
 2164   			}
 2165   			else if ( "resultset".equals(name) ) {
 2166   				bindResultSetMappingDefinition( subnode, persistentClass.getEntityName(), mappings );
 2167   			}
 2168   
 2169   			if ( value != null ) {
 2170   				Property property = createProperty( value, propertyName, persistentClass
 2171   					.getClassName(), subnode, mappings, inheritedMetas );
 2172   				if ( !mutable ) property.setUpdateable(false);
 2173   				if ( naturalId ) property.setNaturalIdentifier(true);
 2174   				persistentClass.addProperty( property );
 2175   				if ( uniqueKey!=null ) uniqueKey.addColumns( property.getColumnIterator() );
 2176   			}
 2177   
 2178   		}
 2179   	}
 2180   
 2181   	private static Property createProperty(
 2182   			final Value value,
 2183   	        final String propertyName,
 2184   			final String className,
 2185   	        final Element subnode,
 2186   	        final Mappings mappings,
 2187   			java.util.Map inheritedMetas) throws MappingException {
 2188   
 2189   		if ( StringHelper.isEmpty( propertyName ) ) {
 2190   			throw new MappingException( subnode.getName() + " mapping must defined a name attribute [" + className + "]" );
 2191   		}
 2192   
 2193   		value.setTypeUsingReflection( className, propertyName );
 2194   
 2195   		// this is done here 'cos we might only know the type here (ugly!)
 2196   		// TODO: improve this a lot:
 2197   		if ( value instanceof ToOne ) {
 2198   			ToOne toOne = (ToOne) value;
 2199   			String propertyRef = toOne.getReferencedPropertyName();
 2200   			if ( propertyRef != null ) {
 2201   				mappings.addUniquePropertyReference( toOne.getReferencedEntityName(), propertyRef );
 2202   			}
 2203   		}
 2204   		else if ( value instanceof Collection ) {
 2205   			Collection coll = (Collection) value;
 2206   			String propertyRef = coll.getReferencedPropertyName();
 2207   			// not necessarily a *unique* property reference
 2208   			if ( propertyRef != null ) {
 2209   				mappings.addPropertyReference( coll.getOwnerEntityName(), propertyRef );
 2210   			}
 2211   		}
 2212   
 2213   		value.createForeignKey();
 2214   		Property prop = new Property();
 2215   		prop.setValue( value );
 2216   		bindProperty( subnode, prop, mappings, inheritedMetas );
 2217   		return prop;
 2218   	}
 2219   
 2220   	private static void handleUnionSubclass(PersistentClass model, Mappings mappings,
 2221   			Element subnode, java.util.Map inheritedMetas) throws MappingException {
 2222   		UnionSubclass subclass = new UnionSubclass( model );
 2223   		bindUnionSubclass( subnode, subclass, mappings, inheritedMetas );
 2224   		model.addSubclass( subclass );
 2225   		mappings.addClass( subclass );
 2226   	}
 2227   
 2228   	private static void handleJoinedSubclass(PersistentClass model, Mappings mappings,
 2229   			Element subnode, java.util.Map inheritedMetas) throws MappingException {
 2230   		JoinedSubclass subclass = new JoinedSubclass( model );
 2231   		bindJoinedSubclass( subnode, subclass, mappings, inheritedMetas );
 2232   		model.addSubclass( subclass );
 2233   		mappings.addClass( subclass );
 2234   	}
 2235   
 2236   	private static void handleSubclass(PersistentClass model, Mappings mappings, Element subnode,
 2237   			java.util.Map inheritedMetas) throws MappingException {
 2238   		Subclass subclass = new SingleTableSubclass( model );
 2239   		bindSubclass( subnode, subclass, mappings, inheritedMetas );
 2240   		model.addSubclass( subclass );
 2241   		mappings.addClass( subclass );
 2242   	}
 2243   
 2244   	/**
 2245   	 * Called for Lists, arrays, primitive arrays
 2246   	 */
 2247   	public static void bindListSecondPass(Element node, List list, java.util.Map classes,
 2248   			Mappings mappings, java.util.Map inheritedMetas) throws MappingException {
 2249   
 2250   		bindCollectionSecondPass( node, list, classes, mappings, inheritedMetas );
 2251   
 2252   		Element subnode = node.element( "list-index" );
 2253   		if ( subnode == null ) subnode = node.element( "index" );
 2254   		SimpleValue iv = new SimpleValue( list.getCollectionTable() );
 2255   		bindSimpleValue(
 2256   				subnode,
 2257   				iv,
 2258   				list.isOneToMany(),
 2259   				IndexedCollection.DEFAULT_INDEX_COLUMN_NAME,
 2260   				mappings
 2261   			);
 2262   		iv.setTypeName( "integer" );
 2263   		list.setIndex( iv );
 2264   		String baseIndex = subnode.attributeValue( "base" );
 2265   		if ( baseIndex != null ) list.setBaseIndex( Integer.parseInt( baseIndex ) );
 2266   		list.setIndexNodeName( subnode.attributeValue("node") );
 2267   
 2268   		if ( list.isOneToMany() && !list.getKey().isNullable() && !list.isInverse() ) {
 2269   			String entityName = ( (OneToMany) list.getElement() ).getReferencedEntityName();
 2270   			PersistentClass referenced = mappings.getClass( entityName );
 2271   			IndexBackref ib = new IndexBackref();
 2272   			ib.setName( '_' + list.getOwnerEntityName() + "." + node.attributeValue( "name" ) + "IndexBackref" );
 2273   			ib.setUpdateable( false );
 2274   			ib.setSelectable( false );
 2275   			ib.setCollectionRole( list.getRole() );
 2276   			ib.setEntityName( list.getOwner().getEntityName() );
 2277   			ib.setValue( list.getIndex() );
 2278   			// ( (Column) ( (SimpleValue) ic.getIndex() ).getColumnIterator().next()
 2279   			// ).setNullable(false);
 2280   			referenced.addProperty( ib );
 2281   		}
 2282   	}
 2283   
 2284   	public static void bindIdentifierCollectionSecondPass(Element node,
 2285   			IdentifierCollection collection, java.util.Map persistentClasses, Mappings mappings,
 2286   			java.util.Map inheritedMetas) throws MappingException {
 2287   
 2288   		bindCollectionSecondPass( node, collection, persistentClasses, mappings, inheritedMetas );
 2289   
 2290   		Element subnode = node.element( "collection-id" );
 2291   		SimpleValue id = new SimpleValue( collection.getCollectionTable() );
 2292   		bindSimpleValue(
 2293   				subnode,
 2294   				id,
 2295   				false,
 2296   				IdentifierCollection.DEFAULT_IDENTIFIER_COLUMN_NAME,
 2297   				mappings
 2298   			);
 2299   		collection.setIdentifier( id );
 2300   		makeIdentifier( subnode, id, mappings );
 2301   
 2302   	}
 2303   
 2304   	/**
 2305   	 * Called for Maps
 2306   	 */
 2307   	public static void bindMapSecondPass(Element node, Map map, java.util.Map classes,
 2308   			Mappings mappings, java.util.Map inheritedMetas) throws MappingException {
 2309   
 2310   		bindCollectionSecondPass( node, map, classes, mappings, inheritedMetas );
 2311   
 2312   		Iterator iter = node.elementIterator();
 2313   		while ( iter.hasNext() ) {
 2314   			Element subnode = (Element) iter.next();
 2315   			String name = subnode.getName();
 2316   
 2317   			if ( "index".equals( name ) || "map-key".equals( name ) ) {
 2318   				SimpleValue value = new SimpleValue( map.getCollectionTable() );
 2319   				bindSimpleValue(
 2320   						subnode,
 2321   						value,
 2322   						map.isOneToMany(),
 2323   						IndexedCollection.DEFAULT_INDEX_COLUMN_NAME,
 2324   						mappings
 2325   					);
 2326   				if ( !value.isTypeSpecified() ) {
 2327   					throw new MappingException( "map index element must specify a type: "
 2328   						+ map.getRole() );
 2329   				}
 2330   				map.setIndex( value );
 2331   				map.setIndexNodeName( subnode.attributeValue("node") );
 2332   			}
 2333   			else if ( "index-many-to-many".equals( name ) || "map-key-many-to-many".equals( name ) ) {
 2334   				ManyToOne mto = new ManyToOne( map.getCollectionTable() );
 2335   				bindManyToOne(
 2336   						subnode,
 2337   						mto,
 2338   						IndexedCollection.DEFAULT_INDEX_COLUMN_NAME,
 2339   						map.isOneToMany(),
 2340   						mappings
 2341   					);
 2342   				map.setIndex( mto );
 2343   
 2344   			}
 2345   			else if ( "composite-index".equals( name ) || "composite-map-key".equals( name ) ) {
 2346   				Component component = new Component( map );
 2347   				bindComposite(
 2348   						subnode,
 2349   						component,
 2350   						map.getRole() + ".index",
 2351   						map.isOneToMany(),
 2352   						mappings,
 2353   						inheritedMetas
 2354   					);
 2355   				map.setIndex( component );
 2356   			}
 2357   			else if ( "index-many-to-any".equals( name ) ) {
 2358   				Any any = new Any( map.getCollectionTable() );
 2359   				bindAny( subnode, any, map.isOneToMany(), mappings );
 2360   				map.setIndex( any );
 2361   			}
 2362   		}
 2363   
 2364   		// TODO: this is a bit of copy/paste from IndexedCollection.createPrimaryKey()
 2365   		boolean indexIsFormula = false;
 2366   		Iterator colIter = map.getIndex().getColumnIterator();
 2367   		while ( colIter.hasNext() ) {
 2368   			if ( ( (Selectable) colIter.next() ).isFormula() ) indexIsFormula = true;
 2369   		}
 2370   
 2371   		if ( map.isOneToMany() && !map.getKey().isNullable() && !map.isInverse() && !indexIsFormula ) {
 2372   			String entityName = ( (OneToMany) map.getElement() ).getReferencedEntityName();
 2373   			PersistentClass referenced = mappings.getClass( entityName );
 2374   			IndexBackref ib = new IndexBackref();
 2375   			ib.setName( '_' + map.getOwnerEntityName() + "." + node.attributeValue( "name" ) + "IndexBackref" );
 2376   			ib.setUpdateable( false );
 2377   			ib.setSelectable( false );
 2378   			ib.setCollectionRole( map.getRole() );
 2379   			ib.setEntityName( map.getOwner().getEntityName() );
 2380   			ib.setValue( map.getIndex() );
 2381   			// ( (Column) ( (SimpleValue) ic.getIndex() ).getColumnIterator().next()
 2382   			// ).setNullable(false);
 2383   			referenced.addProperty( ib );
 2384   		}
 2385   	}
 2386   
 2387   	/**
 2388   	 * Called for all collections
 2389   	 */
 2390   	public static void bindCollectionSecondPass(Element node, Collection collection,
 2391   			java.util.Map persistentClasses, Mappings mappings, java.util.Map inheritedMetas)
 2392   			throws MappingException {
 2393   
 2394   		if ( collection.isOneToMany() ) {
 2395   			OneToMany oneToMany = (OneToMany) collection.getElement();
 2396   			String assocClass = oneToMany.getReferencedEntityName();
 2397   			PersistentClass persistentClass = (PersistentClass) persistentClasses.get( assocClass );
 2398   			if ( persistentClass == null ) {
 2399   				throw new MappingException( "Association references unmapped class: " + assocClass );
 2400   			}
 2401   			oneToMany.setAssociatedClass( persistentClass );
 2402   			collection.setCollectionTable( persistentClass.getTable() );
 2403   
 2404   			log.info(
 2405   					"Mapping collection: " + collection.getRole() +
 2406   					" -> " + collection.getCollectionTable().getName()
 2407   				);
 2408   		}
 2409   
 2410   		// CHECK
 2411   		Attribute chNode = node.attribute( "check" );
 2412   		if ( chNode != null ) {
 2413   			collection.getCollectionTable().addCheckConstraint( chNode.getValue() );
 2414   		}
 2415   
 2416   		// contained elements:
 2417   		Iterator iter = node.elementIterator();
 2418   		while ( iter.hasNext() ) {
 2419   			Element subnode = (Element) iter.next();
 2420   			String name = subnode.getName();
 2421   
 2422   			if ( "key".equals( name ) ) {
 2423   				KeyValue keyVal;
 2424   				String propRef = collection.getReferencedPropertyName();
 2425   				if ( propRef == null ) {
 2426   					keyVal = collection.getOwner().getIdentifier();
 2427   				}
 2428   				else {
 2429   					keyVal = (KeyValue) collection.getOwner().getRecursiveProperty( propRef ).getValue();
 2430   				}
 2431   				SimpleValue key = new DependantValue( collection.getCollectionTable(), keyVal );
 2432   				key.setCascadeDeleteEnabled( "cascade"
 2433   					.equals( subnode.attributeValue( "on-delete" ) ) );
 2434   				bindSimpleValue(
 2435   						subnode,
 2436   						key,
 2437   						collection.isOneToMany(),
 2438   						Collection.DEFAULT_KEY_COLUMN_NAME,
 2439   						mappings
 2440   					);
 2441   				collection.setKey( key );
 2442   
 2443   				Attribute notNull = subnode.attribute( "not-null" );
 2444   				( (DependantValue) key ).setNullable( notNull == null
 2445   					|| notNull.getValue().equals( "false" ) );
 2446   				Attribute updateable = subnode.attribute( "update" );
 2447   				( (DependantValue) key ).setUpdateable( updateable == null
 2448   					|| updateable.getValue().equals( "true" ) );
 2449   
 2450   			}
 2451   			else if ( "element".equals( name ) ) {
 2452   				SimpleValue elt = new SimpleValue( collection.getCollectionTable() );
 2453   				collection.setElement( elt );
 2454   				bindSimpleValue(
 2455   						subnode,
 2456   						elt,
 2457   						true,
 2458   						Collection.DEFAULT_ELEMENT_COLUMN_NAME,
 2459   						mappings
 2460   					);
 2461   			}
 2462   			else if ( "many-to-many".equals( name ) ) {
 2463   				ManyToOne element = new ManyToOne( collection.getCollectionTable() );
 2464   				collection.setElement( element );
 2465   				bindManyToOne(
 2466   						subnode,
 2467   						element,
 2468   						Collection.DEFAULT_ELEMENT_COLUMN_NAME,
 2469   						false,
 2470   						mappings
 2471   					);
 2472   				bindManyToManySubelements( collection, subnode, mappings );
 2473   			}
 2474   			else if ( "composite-element".equals( name ) ) {
 2475   				Component element = new Component( collection );
 2476   				collection.setElement( element );
 2477   				bindComposite(
 2478   						subnode,
 2479   						element,
 2480   						collection.getRole() + ".element",
 2481   						true,
 2482   						mappings,
 2483   						inheritedMetas
 2484   					);
 2485   			}
 2486   			else if ( "many-to-any".equals( name ) ) {
 2487   				Any element = new Any( collection.getCollectionTable() );
 2488   				collection.setElement( element );
 2489   				bindAny( subnode, element, true, mappings );
 2490   			}
 2491   			else if ( "cache".equals( name ) ) {
 2492   				collection.setCacheConcurrencyStrategy( subnode.attributeValue( "usage" ) );
 2493   				collection.setCacheRegionName( subnode.attributeValue( "region" ) );
 2494   			}
 2495   
 2496   			String nodeName = subnode.attributeValue( "node" );
 2497   			if ( nodeName != null ) collection.setElementNodeName( nodeName );
 2498   
 2499   		}
 2500   
 2501   		if ( collection.isOneToMany()
 2502   			&& !collection.isInverse()
 2503   			&& !collection.getKey().isNullable() ) {
 2504   			// for non-inverse one-to-many, with a not-null fk, add a backref!
 2505   			String entityName = ( (OneToMany) collection.getElement() ).getReferencedEntityName();
 2506   			PersistentClass referenced = mappings.getClass( entityName );
 2507   			Backref prop = new Backref();
 2508   			prop.setName( '_' + collection.getOwnerEntityName() + "." + node.attributeValue( "name" ) + "Backref" );
 2509   			prop.setUpdateable( false );
 2510   			prop.setSelectable( false );
 2511   			prop.setCollectionRole( collection.getRole() );
 2512   			prop.setEntityName( collection.getOwner().getEntityName() );
 2513   			prop.setValue( collection.getKey() );
 2514   			referenced.addProperty( prop );
 2515   		}
 2516   	}
 2517   
 2518   	private static void bindManyToManySubelements(
 2519   	        Collection collection,
 2520   	        Element manyToManyNode,
 2521   	        Mappings model) throws MappingException {
 2522   		// Bind the where
 2523   		Attribute where = manyToManyNode.attribute( "where" );
 2524   		String whereCondition = where == null ? null : where.getValue();
 2525   		collection.setManyToManyWhere( whereCondition );
 2526   
 2527   		// Bind the order-by
 2528   		Attribute order = manyToManyNode.attribute( "order-by" );
 2529   		String orderFragment = order == null ? null : order.getValue();
 2530   		collection.setManyToManyOrdering( orderFragment );
 2531   
 2532   		// Bind the filters
 2533   		Iterator filters = manyToManyNode.elementIterator( "filter" );
 2534   		if ( ( filters.hasNext() || whereCondition != null ) &&
 2535   		        collection.getFetchMode() == FetchMode.JOIN &&
 2536   		        collection.getElement().getFetchMode() != FetchMode.JOIN ) {
 2537   			throw new MappingException(
 2538   			        "many-to-many defining filter or where without join fetching " +
 2539   			        "not valid within collection using join fetching [" + collection.getRole() + "]"
 2540   				);
 2541   		}
 2542   		while ( filters.hasNext() ) {
 2543   			final Element filterElement = ( Element ) filters.next();
 2544   			final String name = filterElement.attributeValue( "name" );
 2545   			String condition = filterElement.getTextTrim();
 2546   			if ( StringHelper.isEmpty(condition) ) condition = filterElement.attributeValue( "condition" );
 2547   			if ( StringHelper.isEmpty(condition) ) {
 2548   				condition = model.getFilterDefinition(name).getDefaultFilterCondition();
 2549   			}
 2550   			if ( condition==null) {
 2551   				throw new MappingException("no filter condition found for filter: " + name);
 2552   			}
 2553   			log.debug(
 2554   					"Applying many-to-many filter [" + name +
 2555   					"] as [" + condition +
 2556   					"] to role [" + collection.getRole() + "]"
 2557   				);
 2558   			collection.addManyToManyFilter( name, condition );
 2559   		}
 2560   	}
 2561   
 2562   	public static final FlushMode getFlushMode(String flushMode) {
 2563   		if ( flushMode == null ) {
 2564   			return null;
 2565   		}
 2566   		else if ( "auto".equals( flushMode ) ) {
 2567   			return FlushMode.AUTO;
 2568   		}
 2569   		else if ( "commit".equals( flushMode ) ) {
 2570   			return FlushMode.COMMIT;
 2571   		}
 2572   		else if ( "never".equals( flushMode ) ) {
 2573   			return FlushMode.NEVER;
 2574   		}
 2575   		else if ( "manual".equals( flushMode ) ) {
 2576   			return FlushMode.MANUAL;
 2577   		}
 2578   		else if ( "always".equals( flushMode ) ) {
 2579   			return FlushMode.ALWAYS;
 2580   		}
 2581   		else {
 2582   			throw new MappingException( "unknown flushmode" );
 2583   		}
 2584   	}
 2585   
 2586   	private static void bindNamedQuery(Element queryElem, String path, Mappings mappings) {
 2587   		String queryName = queryElem.attributeValue( "name" );
 2588   		if (path!=null) queryName = path + '.' + queryName;
 2589   		String query = queryElem.getText();
 2590   		log.debug( "Named query: " + queryName + " -> " + query );
 2591   
 2592   		boolean cacheable = "true".equals( queryElem.attributeValue( "cacheable" ) );
 2593   		String region = queryElem.attributeValue( "cache-region" );
 2594   		Attribute tAtt = queryElem.attribute( "timeout" );
 2595   		Integer timeout = tAtt == null ? null : new Integer( tAtt.getValue() );
 2596   		Attribute fsAtt = queryElem.attribute( "fetch-size" );
 2597   		Integer fetchSize = fsAtt == null ? null : new Integer( fsAtt.getValue() );
 2598   		Attribute roAttr = queryElem.attribute( "read-only" );
 2599   		boolean readOnly = roAttr != null && "true".equals( roAttr.getValue() );
 2600   		Attribute cacheModeAtt = queryElem.attribute( "cache-mode" );
 2601   		String cacheMode = cacheModeAtt == null ? null : cacheModeAtt.getValue();
 2602   		Attribute cmAtt = queryElem.attribute( "comment" );
 2603   		String comment = cmAtt == null ? null : cmAtt.getValue();
 2604   
 2605   		NamedQueryDefinition namedQuery = new NamedQueryDefinition(
 2606   				query,
 2607   				cacheable,
 2608   				region,
 2609   				timeout,
 2610   				fetchSize,
 2611   				getFlushMode( queryElem.attributeValue( "flush-mode" ) ) ,
 2612   				getCacheMode( cacheMode ),
 2613   				readOnly,
 2614   				comment,
 2615   				getParameterTypes(queryElem)
 2616   			);
 2617   
 2618   		mappings.addQuery( queryName, namedQuery );
 2619   	}
 2620   
 2621   	public static CacheMode getCacheMode(String cacheMode) {
 2622   		if (cacheMode == null) return null;
 2623   		if ( "get".equals( cacheMode ) ) return CacheMode.GET;
 2624   		if ( "ignore".equals( cacheMode ) ) return CacheMode.IGNORE;
 2625   		if ( "normal".equals( cacheMode ) ) return CacheMode.NORMAL;
 2626   		if ( "put".equals( cacheMode ) ) return CacheMode.PUT;
 2627   		if ( "refresh".equals( cacheMode ) ) return CacheMode.REFRESH;
 2628   		throw new MappingException("Unknown Cache Mode: " + cacheMode);
 2629   	}
 2630   
 2631   	public static java.util.Map getParameterTypes(Element queryElem) {
 2632   		java.util.Map result = new java.util.LinkedHashMap();
 2633   		Iterator iter = queryElem.elementIterator("query-param");
 2634   		while ( iter.hasNext() ) {
 2635   			Element element = (Element) iter.next();
 2636   			result.put( element.attributeValue("name"), element.attributeValue("type") );
 2637   		}
 2638   		return result;
 2639   	}
 2640   
 2641   	private static void bindResultSetMappingDefinition(Element resultSetElem, String path, Mappings mappings) {
 2642   		mappings.addSecondPass( new ResultSetMappingSecondPass( resultSetElem, path, mappings ) );
 2643   	}
 2644   
 2645   	private static void bindNamedSQLQuery(Element queryElem, String path, Mappings mappings) {
 2646   		mappings.addSecondPass( new NamedSQLQuerySecondPass( queryElem, path, mappings ) );
 2647   	}
 2648   
 2649   	private static String getPropertyName(Element node) {
 2650   		return node.attributeValue( "name" );
 2651   	}
 2652   
 2653   	private static PersistentClass getSuperclass(Mappings mappings, Element subnode)
 2654   			throws MappingException {
 2655   		String extendsName = subnode.attributeValue( "extends" );
 2656   		PersistentClass superModel = mappings.getClass( extendsName );
 2657   		if ( superModel == null ) {
 2658   			String qualifiedExtendsName = getClassName( extendsName, mappings );
 2659   			superModel = mappings.getClass( qualifiedExtendsName );
 2660   		}
 2661   
 2662   		if ( superModel == null ) {
 2663   			throw new MappingException( "Cannot extend unmapped class " + extendsName );
 2664   		}
 2665   		return superModel;
 2666   	}
 2667   
 2668   	static class CollectionSecondPass extends org.hibernate.cfg.CollectionSecondPass {
 2669   		Element node;
 2670   
 2671   		CollectionSecondPass(Element node, Mappings mappings, Collection collection, java.util.Map inheritedMetas) {
 2672   			super(mappings, collection, inheritedMetas);
 2673   			this.node = node;
 2674   		}
 2675   
 2676   		public void secondPass(java.util.Map persistentClasses, java.util.Map inheritedMetas)
 2677   				throws MappingException {
 2678   			HbmBinder.bindCollectionSecondPass(
 2679   					node,
 2680   					collection,
 2681   					persistentClasses,
 2682   					mappings,
 2683   					inheritedMetas
 2684   				);
 2685   		}
 2686   	}
 2687   
 2688   	static class IdentifierCollectionSecondPass extends CollectionSecondPass {
 2689   		IdentifierCollectionSecondPass(Element node, Mappings mappings, Collection collection, java.util.Map inheritedMetas) {
 2690   			super( node, mappings, collection, inheritedMetas );
 2691   		}
 2692   
 2693   		public void secondPass(java.util.Map persistentClasses, java.util.Map inheritedMetas)
 2694   				throws MappingException {
 2695   			HbmBinder.bindIdentifierCollectionSecondPass(
 2696   					node,
 2697   					(IdentifierCollection) collection,
 2698   					persistentClasses,
 2699   					mappings,
 2700   					inheritedMetas 
 2701   				);
 2702   		}
 2703   
 2704   	}
 2705   
 2706   	static class MapSecondPass extends CollectionSecondPass {
 2707   		MapSecondPass(Element node, Mappings mappings, Map collection, java.util.Map inheritedMetas) {
 2708   			super( node, mappings, collection, inheritedMetas );
 2709   		}
 2710   
 2711   		public void secondPass(java.util.Map persistentClasses, java.util.Map inheritedMetas)
 2712   				throws MappingException {
 2713   			HbmBinder.bindMapSecondPass(
 2714   					node,
 2715   					(Map) collection,
 2716   					persistentClasses,
 2717   					mappings,
 2718   					inheritedMetas 
 2719   				);
 2720   		}
 2721   
 2722   	}
 2723   
 2724   
 2725   	static class ManyToOneSecondPass implements SecondPass {
 2726   		private final ManyToOne manyToOne;
 2727   
 2728   		ManyToOneSecondPass(ManyToOne manyToOne) {
 2729   			this.manyToOne = manyToOne;
 2730   		}
 2731   
 2732   		public void doSecondPass(java.util.Map persistentClasses) throws MappingException {
 2733   			manyToOne.createPropertyRefConstraints(persistentClasses);
 2734   		}
 2735   
 2736   	}
 2737   	
 2738   	static class ListSecondPass extends CollectionSecondPass {
 2739   		ListSecondPass(Element node, Mappings mappings, List collection, java.util.Map inheritedMetas) {
 2740   			super( node, mappings, collection, inheritedMetas );
 2741   		}
 2742   
 2743   		public void secondPass(java.util.Map persistentClasses, java.util.Map inheritedMetas)
 2744   				throws MappingException {
 2745   			HbmBinder.bindListSecondPass(
 2746   					node,
 2747   					(List) collection,
 2748   					persistentClasses,
 2749   					mappings,
 2750   					inheritedMetas 
 2751   				);
 2752   		}
 2753   
 2754   	}
 2755   
 2756   	// This inner class implements a case statement....perhaps im being a bit over-clever here
 2757   	abstract static class CollectionType {
 2758   		private String xmlTag;
 2759   
 2760   		public abstract Collection create(Element node, String path, PersistentClass owner,
 2761   				Mappings mappings, java.util.Map inheritedMetas) throws MappingException;
 2762   
 2763   		CollectionType(String xmlTag) {
 2764   			this.xmlTag = xmlTag;
 2765   		}
 2766   
 2767   		public String toString() {
 2768   			return xmlTag;
 2769   		}
 2770   
 2771   		private static final CollectionType MAP = new CollectionType( "map" ) {
 2772   			public Collection create(Element node, String path, PersistentClass owner,
 2773   					Mappings mappings, java.util.Map inheritedMetas) throws MappingException {
 2774   				Map map = new Map( owner );
 2775   				bindCollection( node, map, owner.getEntityName(), path, mappings, inheritedMetas );
 2776   				return map;
 2777   			}
 2778   		};
 2779   		private static final CollectionType SET = new CollectionType( "set" ) {
 2780   			public Collection create(Element node, String path, PersistentClass owner,
 2781   					Mappings mappings, java.util.Map inheritedMetas) throws MappingException {
 2782   				Set set = new Set( owner );
 2783   				bindCollection( node, set, owner.getEntityName(), path, mappings, inheritedMetas );
 2784   				return set;
 2785   			}
 2786   		};
 2787   		private static final CollectionType LIST = new CollectionType( "list" ) {
 2788   			public Collection create(Element node, String path, PersistentClass owner,
 2789   					Mappings mappings, java.util.Map inheritedMetas) throws MappingException {
 2790   				List list = new List( owner );
 2791   				bindCollection( node, list, owner.getEntityName(), path, mappings, inheritedMetas );
 2792   				return list;
 2793   			}
 2794   		};
 2795   		private static final CollectionType BAG = new CollectionType( "bag" ) {
 2796   			public Collection create(Element node, String path, PersistentClass owner,
 2797   					Mappings mappings, java.util.Map inheritedMetas) throws MappingException {
 2798   				Bag bag = new Bag( owner );
 2799   				bindCollection( node, bag, owner.getEntityName(), path, mappings, inheritedMetas );
 2800   				return bag;
 2801   			}
 2802   		};
 2803   		private static final CollectionType IDBAG = new CollectionType( "idbag" ) {
 2804   			public Collection create(Element node, String path, PersistentClass owner,
 2805   					Mappings mappings, java.util.Map inheritedMetas) throws MappingException {
 2806   				IdentifierBag bag = new IdentifierBag( owner );
 2807   				bindCollection( node, bag, owner.getEntityName(), path, mappings, inheritedMetas );
 2808   				return bag;
 2809   			}
 2810   		};
 2811   		private static final CollectionType ARRAY = new CollectionType( "array" ) {
 2812   			public Collection create(Element node, String path, PersistentClass owner,
 2813   					Mappings mappings, java.util.Map inheritedMetas) throws MappingException {
 2814   				Array array = new Array( owner );
 2815   				bindArray( node, array, owner.getEntityName(), path, mappings, inheritedMetas );
 2816   				return array;
 2817   			}
 2818   		};
 2819   		private static final CollectionType PRIMITIVE_ARRAY = new CollectionType( "primitive-array" ) {
 2820   			public Collection create(Element node, String path, PersistentClass owner,
 2821   					Mappings mappings, java.util.Map inheritedMetas) throws MappingException {
 2822   				PrimitiveArray array = new PrimitiveArray( owner );
 2823   				bindArray( node, array, owner.getEntityName(), path, mappings, inheritedMetas );
 2824   				return array;
 2825   			}
 2826   		};
 2827   		private static final HashMap INSTANCES = new HashMap();
 2828   
 2829   		static {
 2830   			INSTANCES.put( MAP.toString(), MAP );
 2831   			INSTANCES.put( BAG.toString(), BAG );
 2832   			INSTANCES.put( IDBAG.toString(), IDBAG );
 2833   			INSTANCES.put( SET.toString(), SET );
 2834   			INSTANCES.put( LIST.toString(), LIST );
 2835   			INSTANCES.put( ARRAY.toString(), ARRAY );
 2836   			INSTANCES.put( PRIMITIVE_ARRAY.toString(), PRIMITIVE_ARRAY );
 2837   		}
 2838   
 2839   		public static CollectionType collectionTypeFromString(String xmlTagName) {
 2840   			return (CollectionType) INSTANCES.get( xmlTagName );
 2841   		}
 2842   	}
 2843   
 2844   	private static int getOptimisticLockMode(Attribute olAtt) throws MappingException {
 2845   
 2846   		if ( olAtt == null ) return Versioning.OPTIMISTIC_LOCK_VERSION;
 2847   		String olMode = olAtt.getValue();
 2848   		if ( olMode == null || "version".equals( olMode ) ) {
 2849   			return Versioning.OPTIMISTIC_LOCK_VERSION;
 2850   		}
 2851   		else if ( "dirty".equals( olMode ) ) {
 2852   			return Versioning.OPTIMISTIC_LOCK_DIRTY;
 2853   		}
 2854   		else if ( "all".equals( olMode ) ) {
 2855   			return Versioning.OPTIMISTIC_LOCK_ALL;
 2856   		}
 2857   		else if ( "none".equals( olMode ) ) {
 2858   			return Versioning.OPTIMISTIC_LOCK_NONE;
 2859   		}
 2860   		else {
 2861   			throw new MappingException( "Unsupported optimistic-lock style: " + olMode );
 2862   		}
 2863   	}
 2864   
 2865   	private static final java.util.Map getMetas(Element node, java.util.Map inheritedMeta) {
 2866   		return getMetas( node, inheritedMeta, false );
 2867   	}
 2868   
 2869   	public static final java.util.Map getMetas(Element node, java.util.Map inheritedMeta,
 2870   			boolean onlyInheritable) {
 2871   		java.util.Map map = new HashMap();
 2872   		map.putAll( inheritedMeta );
 2873   
 2874   		Iterator iter = node.elementIterator( "meta" );
 2875   		while ( iter.hasNext() ) {
 2876   			Element metaNode = (Element) iter.next();
 2877   			boolean inheritable = Boolean
 2878   				.valueOf( metaNode.attributeValue( "inherit" ) )
 2879   				.booleanValue();
 2880   			if ( onlyInheritable & !inheritable ) {
 2881   				continue;
 2882   			}
 2883   			String name = metaNode.attributeValue( "attribute" );
 2884   
 2885   			MetaAttribute meta = (MetaAttribute) map.get( name );
 2886   			MetaAttribute inheritedAttribute = (MetaAttribute) inheritedMeta.get( name );
 2887   			if ( meta == null  ) {
 2888   				meta = new MetaAttribute( name );
 2889   				map.put( name, meta );
 2890   			} else if (meta == inheritedAttribute) { // overriding inherited meta attribute. HBX-621 & HBX-793			
 2891   				meta = new MetaAttribute( name );				
 2892   				map.put( name, meta );				
 2893   			}			
 2894   			meta.addValue( metaNode.getText() );
 2895   		}
 2896   		return map;
 2897   	}
 2898   
 2899   	public static String getEntityName(Element elem, Mappings model) {
 2900   		String entityName = elem.attributeValue( "entity-name" );
 2901   		return entityName == null ? getClassName( elem.attribute( "class" ), model ) : entityName;
 2902   	}
 2903   
 2904   	private static String getClassName(Attribute att, Mappings model) {
 2905   		if ( att == null ) return null;
 2906   		return getClassName( att.getValue(), model );
 2907   	}
 2908   
 2909   	public static String getClassName(String unqualifiedName, Mappings model) {
 2910   		return getClassName( unqualifiedName, model.getDefaultPackage() );
 2911   	}
 2912   
 2913   	public static String getClassName(String unqualifiedName, String defaultPackage) {
 2914   		if ( unqualifiedName == null ) return null;
 2915   		if ( unqualifiedName.indexOf( '.' ) < 0 && defaultPackage != null ) {
 2916   			return defaultPackage + '.' + unqualifiedName;
 2917   		}
 2918   		return unqualifiedName;
 2919   	}
 2920   
 2921   	private static void parseFilterDef(Element element, Mappings mappings) {
 2922   		String name = element.attributeValue( "name" );
 2923   		log.debug( "Parsing filter-def [" + name + "]" );
 2924   		String defaultCondition = element.getTextTrim();
 2925   		if ( StringHelper.isEmpty( defaultCondition ) ) {
 2926   			defaultCondition = element.attributeValue( "condition" );
 2927   		}
 2928   		HashMap paramMappings = new HashMap();
 2929   		Iterator params = element.elementIterator( "filter-param" );
 2930   		while ( params.hasNext() ) {
 2931   			final Element param = (Element) params.next();
 2932   			final String paramName = param.attributeValue( "name" );
 2933   			final String paramType = param.attributeValue( "type" );
 2934   			log.debug( "adding filter parameter : " + paramName + " -> " + paramType );
 2935   			final Type heuristicType = TypeFactory.heuristicType( paramType );
 2936   			log.debug( "parameter heuristic type : " + heuristicType );
 2937   			paramMappings.put( paramName, heuristicType );
 2938   		}
 2939   		log.debug( "Parsed filter-def [" + name + "]" );
 2940   		FilterDefinition def = new FilterDefinition( name, defaultCondition, paramMappings );
 2941   		mappings.addFilterDefinition( def );
 2942   	}
 2943   
 2944   	private static void parseFilter(Element filterElement, Filterable filterable, Mappings model) {
 2945   		final String name = filterElement.attributeValue( "name" );
 2946   		String condition = filterElement.getTextTrim();
 2947   		if ( StringHelper.isEmpty(condition) ) {
 2948   			condition = filterElement.attributeValue( "condition" );
 2949   		}
 2950   		//TODO: bad implementation, cos it depends upon ordering of mapping doc
 2951   		//      fixing this requires that Collection/PersistentClass gain access
 2952   		//      to the Mappings reference from Configuration (or the filterDefinitions
 2953   		//      map directly) sometime during Configuration.buildSessionFactory
 2954   		//      (after all the types/filter-defs are known and before building
 2955   		//      persisters).
 2956   		if ( StringHelper.isEmpty(condition) ) {
 2957   			condition = model.getFilterDefinition(name).getDefaultFilterCondition();
 2958   		}
 2959   		if ( condition==null) {
 2960   			throw new MappingException("no filter condition found for filter: " + name);
 2961   		}
 2962   		log.debug( "Applying filter [" + name + "] as [" + condition + "]" );
 2963   		filterable.addFilter( name, condition );
 2964   	}
 2965   
 2966   	private static String getSubselect(Element element) {
 2967   		String subselect = element.attributeValue( "subselect" );
 2968   		if ( subselect != null ) {
 2969   			return subselect;
 2970   		}
 2971   		else {
 2972   			Element subselectElement = element.element( "subselect" );
 2973   			return subselectElement == null ? null : subselectElement.getText();
 2974   		}
 2975   	}
 2976   
 2977   	/**
 2978   	 * For the given document, locate all extends attributes which refer to
 2979   	 * entities (entity-name or class-name) not defined within said document.
 2980   	 *
 2981   	 * @param doc The document to check
 2982   	 * @param mappings The already processed mappings.
 2983   	 * @return The list of unresolved extends names.
 2984   	 */
 2985   	public static java.util.List getExtendsNeeded(Document doc, Mappings mappings) {
 2986   		java.util.List extendz = new ArrayList();
 2987   		Iterator[] subclasses = new Iterator[3];
 2988   		final Element hmNode = doc.getRootElement();
 2989   
 2990   		Attribute packNode = hmNode.attribute( "package" );
 2991   		final String packageName = packNode == null ? null : packNode.getValue();
 2992   		if ( packageName != null ) {
 2993   			mappings.setDefaultPackage( packageName );
 2994   		}
 2995   
 2996   		// first, iterate over all elements capable of defining an extends attribute
 2997   		// collecting all found extends references if they cannot be resolved
 2998   		// against the already processed mappings.
 2999   		subclasses[0] = hmNode.elementIterator( "subclass" );
 3000   		subclasses[1] = hmNode.elementIterator( "joined-subclass" );
 3001   		subclasses[2] = hmNode.elementIterator( "union-subclass" );
 3002   
 3003   		Iterator iterator = new JoinedIterator( subclasses );
 3004   		while ( iterator.hasNext() ) {
 3005   			final Element element = (Element) iterator.next();
 3006   			final String extendsName = element.attributeValue( "extends" );
 3007   			// mappings might contain either the "raw" extends name (in the case of
 3008   			// an entity-name mapping) or a FQN (in the case of a POJO mapping).
 3009   			if ( mappings.getClass( extendsName ) == null && mappings.getClass( getClassName( extendsName, mappings ) ) == null ) {
 3010   				extendz.add( extendsName );
 3011   			}
 3012   		}
 3013   
 3014   		if ( !extendz.isEmpty() ) {
 3015   			// we found some extends attributes referencing entities which were
 3016   			// not already processed.  here we need to locate all entity-names
 3017   			// and class-names contained in this document itself, making sure
 3018   			// that these get removed from the extendz list such that only
 3019   			// extends names which require us to delay processing (i.e.
 3020   			// external to this document and not yet processed) are contained
 3021   			// in the returned result
 3022   			final java.util.Set set = new HashSet( extendz );
 3023   			EntityElementHandler handler = new EntityElementHandler() {
 3024   				public void handleEntity(String entityName, String className, Mappings mappings) {
 3025   					if ( entityName != null ) {
 3026   						set.remove( entityName );
 3027   					}
 3028   					else {
 3029   						String fqn = getClassName( className, packageName );
 3030   						set.remove( fqn );
 3031   						if ( packageName != null ) {
 3032   							set.remove( StringHelper.unqualify( fqn ) );
 3033   						}
 3034   					}
 3035   				}
 3036   			};
 3037   			recognizeEntities( mappings, hmNode, handler );
 3038   			extendz.clear();
 3039   			extendz.addAll( set );
 3040   		}
 3041   
 3042   		return extendz;
 3043   	}
 3044   
 3045   	/**
 3046   	 * Given an entity-containing-element (startNode) recursively locate all
 3047   	 * entity names defined within that element.
 3048   	 *
 3049   	 * @param mappings The already processed mappings
 3050   	 * @param startNode The containing element
 3051   	 * @param handler The thing that knows what to do whenever we recognize an
 3052   	 * entity-name
 3053   	 */
 3054   	private static void recognizeEntities(
 3055   			Mappings mappings,
 3056   	        final Element startNode,
 3057   			EntityElementHandler handler) {
 3058   		Iterator[] classes = new Iterator[4];
 3059   		classes[0] = startNode.elementIterator( "class" );
 3060   		classes[1] = startNode.elementIterator( "subclass" );
 3061   		classes[2] = startNode.elementIterator( "joined-subclass" );
 3062   		classes[3] = startNode.elementIterator( "union-subclass" );
 3063   
 3064   		Iterator classIterator = new JoinedIterator( classes );
 3065   		while ( classIterator.hasNext() ) {
 3066   			Element element = (Element) classIterator.next();
 3067   			handler.handleEntity(
 3068   					element.attributeValue( "entity-name" ),
 3069   		            element.attributeValue( "name" ),
 3070   			        mappings
 3071   			);
 3072   			recognizeEntities( mappings, element, handler );
 3073   		}
 3074   	}
 3075   
 3076   	private static interface EntityElementHandler {
 3077   		public void handleEntity(String entityName, String className, Mappings mappings);
 3078   	}
 3079   }

Save This Page
Home » hibernate-distribution-3.3.1.GA-dist » org.hibernate » cfg » [javadoc | source]