KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > hibernate > cfg > HbmBinder


1 // $Id: HbmBinder.java,v 1.116 2005/07/21 01:58:44 oneovthafew Exp $
2
package org.hibernate.cfg;
3
4 import java.io.Serializable JavaDoc;
5 import java.util.ArrayList JavaDoc;
6 import java.util.Comparator JavaDoc;
7 import java.util.HashMap JavaDoc;
8 import java.util.HashSet JavaDoc;
9 import java.util.Iterator JavaDoc;
10 import java.util.Properties JavaDoc;
11 import java.util.StringTokenizer JavaDoc;
12
13 import org.apache.commons.collections.SequencedHashMap;
14 import org.apache.commons.logging.Log;
15 import org.apache.commons.logging.LogFactory;
16 import org.dom4j.Attribute;
17 import org.dom4j.Document;
18 import org.dom4j.Element;
19 import org.hibernate.EntityMode;
20 import org.hibernate.FetchMode;
21 import org.hibernate.FlushMode;
22 import org.hibernate.LockMode;
23 import org.hibernate.MappingException;
24 import org.hibernate.engine.FilterDefinition;
25 import org.hibernate.engine.NamedQueryDefinition;
26 import org.hibernate.engine.NamedSQLQueryDefinition;
27 import org.hibernate.engine.ResultSetMappingDefinition;
28 import org.hibernate.engine.Versioning;
29 import org.hibernate.id.PersistentIdentifierGenerator;
30 import org.hibernate.loader.custom.SQLQueryCollectionReturn;
31 import org.hibernate.loader.custom.SQLQueryJoinReturn;
32 import org.hibernate.loader.custom.SQLQueryRootReturn;
33 import org.hibernate.loader.custom.SQLQueryScalarReturn;
34 import org.hibernate.mapping.Any;
35 import org.hibernate.mapping.Array;
36 import org.hibernate.mapping.Backref;
37 import org.hibernate.mapping.Bag;
38 import org.hibernate.mapping.Collection;
39 import org.hibernate.mapping.Column;
40 import org.hibernate.mapping.Component;
41 import org.hibernate.mapping.DependantValue;
42 import org.hibernate.mapping.Fetchable;
43 import org.hibernate.mapping.Filterable;
44 import org.hibernate.mapping.Formula;
45 import org.hibernate.mapping.IdentifierBag;
46 import org.hibernate.mapping.IdentifierCollection;
47 import org.hibernate.mapping.IndexBackref;
48 import org.hibernate.mapping.IndexedCollection;
49 import org.hibernate.mapping.Join;
50 import org.hibernate.mapping.JoinedSubclass;
51 import org.hibernate.mapping.KeyValue;
52 import org.hibernate.mapping.List;
53 import org.hibernate.mapping.ManyToOne;
54 import org.hibernate.mapping.Map;
55 import org.hibernate.mapping.MetaAttribute;
56 import org.hibernate.mapping.OneToMany;
57 import org.hibernate.mapping.OneToOne;
58 import org.hibernate.mapping.PersistentClass;
59 import org.hibernate.mapping.PrimitiveArray;
60 import org.hibernate.mapping.Property;
61 import org.hibernate.mapping.RootClass;
62 import org.hibernate.mapping.Selectable;
63 import org.hibernate.mapping.Set;
64 import org.hibernate.mapping.SimpleValue;
65 import org.hibernate.mapping.SingleTableSubclass;
66 import org.hibernate.mapping.Subclass;
67 import org.hibernate.mapping.Table;
68 import org.hibernate.mapping.ToOne;
69 import org.hibernate.mapping.TypeDef;
70 import org.hibernate.mapping.UnionSubclass;
71 import org.hibernate.mapping.UniqueKey;
72 import org.hibernate.mapping.Value;
73 import org.hibernate.persister.entity.JoinedSubclassEntityPersister;
74 import org.hibernate.persister.entity.SingleTableEntityPersister;
75 import org.hibernate.persister.entity.UnionSubclassEntityPersister;
76 import org.hibernate.type.DiscriminatorType;
77 import org.hibernate.type.ForeignKeyDirection;
78 import org.hibernate.type.Type;
79 import org.hibernate.type.TypeFactory;
80 import org.hibernate.util.ArrayHelper;
81 import org.hibernate.util.CollectionHelper;
82 import org.hibernate.util.JoinedIterator;
83 import org.hibernate.util.ReflectHelper;
84 import org.hibernate.util.StringHelper;
85
86 /**
87  * Walks an XML mapping document and produces the Hibernate configuration-time metamodel (the
88  * classes in the <tt>mapping</tt> package)
89  *
90  * @author Gavin King
91  */

92 public final class HbmBinder {
93
94     private static final Log log = LogFactory.getLog( HbmBinder.class );
95
96     /**
97      * Private constructor to disallow instantiation.
98      */

99     private HbmBinder() {
100     }
101
102     /**
103      * The main contract into the hbm.xml-based binder. Performs necessary binding operations
104      * represented by the given DOM.
105      *
106      * @param doc The DOM to be parsed and bound.
107      * @param mappings Current bind state.
108      * @param inheritedMetas Any inherited meta-tag information.
109      * @throws MappingException
110      */

111     public static void bindRoot(Document doc, Mappings mappings, java.util.Map JavaDoc inheritedMetas)
112             throws MappingException {
113
114         java.util.List JavaDoc names = HbmBinder.getExtendsNeeded( doc, mappings );
115         if ( !names.isEmpty() ) {
116             // classes mentioned in extends not available - so put it in queue
117
for ( Iterator JavaDoc iter = names.iterator(); iter.hasNext(); ) {
118                 String JavaDoc className = (String JavaDoc) iter.next();
119                 mappings.addToExtendsQueue( className, doc );
120             }
121             return;
122         }
123
124         Element hmNode = doc.getRootElement();
125         inheritedMetas = getMetas( hmNode, inheritedMetas, true ); // get meta's from
126
// <hibernate-mapping>
127
extractRootAttributes( hmNode, mappings );
128
129         Iterator JavaDoc filterDefs = hmNode.elementIterator( "filter-def" );
130         while ( filterDefs.hasNext() ) {
131             parseFilterDef( (Element) filterDefs.next(), mappings );
132         }
133
134         Iterator JavaDoc typeDefs = hmNode.elementIterator( "typedef" );
135         while ( typeDefs.hasNext() ) {
136             Element typeDef = (Element) typeDefs.next();
137             String JavaDoc typeClass = typeDef.attributeValue( "class" );
138             String JavaDoc typeName = typeDef.attributeValue( "name" );
139             Iterator JavaDoc paramIter = typeDef.elementIterator( "param" );
140             Properties JavaDoc parameters = new Properties JavaDoc();
141             while ( paramIter.hasNext() ) {
142                 Element param = (Element) paramIter.next();
143                 parameters.setProperty( param.attributeValue( "name" ), param.getTextTrim() );
144             }
145
146             mappings.addTypeDef( typeName, typeClass, parameters );
147         }
148
149         Iterator JavaDoc nodes = hmNode.elementIterator( "class" );
150         while ( nodes.hasNext() ) {
151             Element n = (Element) nodes.next();
152             RootClass rootclass = new RootClass();
153             bindRootClass( n, rootclass, mappings, inheritedMetas );
154             mappings.addClass( rootclass );
155         }
156
157         Iterator JavaDoc subclassnodes = hmNode.elementIterator( "subclass" );
158         while ( subclassnodes.hasNext() ) {
159             Element subnode = (Element) subclassnodes.next();
160             PersistentClass superModel = getSuperclass( mappings, subnode );
161             handleSubclass( superModel, mappings, subnode, inheritedMetas );
162         }
163
164         Iterator JavaDoc joinedsubclassnodes = hmNode.elementIterator( "joined-subclass" );
165         while ( joinedsubclassnodes.hasNext() ) {
166             Element subnode = (Element) joinedsubclassnodes.next();
167             PersistentClass superModel = getSuperclass( mappings, subnode );
168             handleJoinedSubclass( superModel, mappings, subnode, inheritedMetas );
169         }
170
171         Iterator JavaDoc unionsubclassnodes = hmNode.elementIterator( "union-subclass" );
172         while ( unionsubclassnodes.hasNext() ) {
173             Element subnode = (Element) unionsubclassnodes.next();
174             PersistentClass superModel = getSuperclass( mappings, subnode );
175             handleUnionSubclass( superModel, mappings, subnode, inheritedMetas );
176         }
177
178         nodes = hmNode.elementIterator( "query" );
179         while ( nodes.hasNext() ) {
180             bindNamedQuery( (Element) nodes.next(), null, mappings );
181         }
182
183         nodes = hmNode.elementIterator( "sql-query" );
184         while ( nodes.hasNext() ) {
185             bindNamedSQLQuery( (Element) nodes.next(), null, mappings );
186         }
187
188         nodes = hmNode.elementIterator( "resultset" );
189         while ( nodes.hasNext() ) {
190             bindResultSetMappingDefinition( (Element) nodes.next(), null, mappings );
191         }
192
193         nodes = hmNode.elementIterator( "import" );
194         while ( nodes.hasNext() ) {
195             Element n = (Element) nodes.next();
196             String JavaDoc className = getClassName( n.attribute( "class" ), mappings );
197             Attribute renameNode = n.attribute( "rename" );
198             String JavaDoc rename = ( renameNode == null )
199                 ? StringHelper.unqualify( className )
200                 : renameNode.getValue();
201             log.debug( "Import: " + rename + " -> " + className );
202             mappings.addImport( className, rename );
203         }
204     }
205
206     private static void extractRootAttributes(Element hmNode, Mappings mappings) {
207         Attribute schemaNode = hmNode.attribute( "schema" );
208         mappings.setSchemaName( ( schemaNode == null ) ? null : schemaNode.getValue() );
209
210         Attribute catalogNode = hmNode.attribute( "catalog" );
211         mappings.setCatalogName( ( catalogNode == null ) ? null : catalogNode.getValue() );
212
213         Attribute dcNode = hmNode.attribute( "default-cascade" );
214         mappings.setDefaultCascade( ( dcNode == null ) ? "none" : dcNode.getValue() );
215
216         Attribute daNode = hmNode.attribute( "default-access" );
217         mappings.setDefaultAccess( ( daNode == null ) ? "property" : daNode.getValue() );
218
219         Attribute dlNode = hmNode.attribute( "default-lazy" );
220         mappings.setDefaultLazy( dlNode == null || dlNode.getValue().equals( "true" ) );
221
222         Attribute aiNode = hmNode.attribute( "auto-import" );
223         mappings.setAutoImport( ( aiNode == null ) ? true : "true".equals( aiNode.getValue() ) );
224
225         Attribute packNode = hmNode.attribute( "package" );
226         if ( packNode != null ) mappings.setDefaultPackage( packNode.getValue() );
227     }
228
229     /**
230      * Responsible for perfoming the bind operation related to an &lt;class/&gt; mapping element.
231      *
232      * @param node The DOM Element for the &lt;class/&gt; element.
233      * @param rootClass The mapping instance to which to bind the information.
234      * @param mappings The current bind state.
235      * @param inheritedMetas Any inherited meta-tag information.
236      * @throws MappingException
237      */

238     public static void bindRootClass(Element node, RootClass rootClass, Mappings mappings,
239             java.util.Map JavaDoc inheritedMetas) throws MappingException {
240         bindClass( node, rootClass, mappings, inheritedMetas );
241         inheritedMetas = getMetas( node, inheritedMetas, true ); // get meta's from <class>
242
bindRootPersistentClassCommonValues( node, inheritedMetas, mappings, rootClass );
243     }
244
245     private static void bindRootPersistentClassCommonValues(Element node,
246             java.util.Map JavaDoc inheritedMetas, Mappings mappings, RootClass entity)
247             throws MappingException {
248
249         // DB-OBJECTNAME
250

251         Attribute schemaNode = node.attribute( "schema" );
252         String JavaDoc schema = schemaNode == null ? mappings.getSchemaName() : schemaNode.getValue();
253
254         Attribute catalogNode = node.attribute( "catalog" );
255         String JavaDoc catalog = catalogNode == null ? mappings.getCatalogName() : catalogNode.getValue();
256
257         Table table = mappings.addTable(
258                 schema,
259                 catalog,
260                 getClassTableName( entity, node, mappings ),
261                 getSubselect( node ),
262                 entity.isAbstract()
263             );
264         entity.setTable( table );
265         bindComment(table, node);
266         
267         log.info( "Mapping class: "
268                 + entity.getEntityName()
269                 + " -> "
270                 + entity.getTable().getName() );
271
272         // MUTABLE
273
Attribute mutableNode = node.attribute( "mutable" );
274         entity.setMutable( ( mutableNode == null ) || mutableNode.getValue().equals( "true" ) );
275
276         // WHERE
277
Attribute whereNode = node.attribute( "where" );
278         if ( whereNode != null ) entity.setWhere( whereNode.getValue() );
279
280         // CHECK
281
Attribute chNode = node.attribute( "check" );
282         if ( chNode != null ) table.addCheckConstraint( chNode.getValue() );
283
284         // POLYMORPHISM
285
Attribute polyNode = node.attribute( "polymorphism" );
286         entity.setExplicitPolymorphism( ( polyNode != null )
287             && polyNode.getValue().equals( "explicit" ) );
288
289         // ROW ID
290
Attribute rowidNode = node.attribute( "rowid" );
291         if ( rowidNode != null ) table.setRowId( rowidNode.getValue() );
292
293         Iterator JavaDoc subnodes = node.elementIterator();
294         while ( subnodes.hasNext() ) {
295
296             Element subnode = (Element) subnodes.next();
297             String JavaDoc name = subnode.getName();
298
299             if ( "id".equals( name ) ) {
300                 // ID
301
bindSimpleId( subnode, entity, mappings, inheritedMetas );
302             }
303             else if ( "composite-id".equals( name ) ) {
304                 // COMPOSITE-ID
305
bindCompositeId( subnode, entity, mappings, inheritedMetas );
306             }
307             else if ( "version".equals( name ) || "timestamp".equals( name ) ) {
308                 // VERSION / TIMESTAMP
309
bindVersioningProperty( table, subnode, mappings, name, entity, inheritedMetas );
310             }
311             else if ( "discriminator".equals( name ) ) {
312                 // DISCRIMINATOR
313
bindDiscriminatorProperty( table, entity, subnode, mappings );
314             }
315             else if ( "cache".equals( name ) ) {
316                 entity.setCacheConcurrencyStrategy( subnode.attributeValue( "usage" ) );
317                 entity.setCacheRegionName( subnode.attributeValue( "region" ) );
318             }
319
320         }
321
322         // Primary key constraint
323
entity.createPrimaryKey();
324
325         createClassProperties( node, entity, mappings, inheritedMetas );
326     }
327
328     private static void bindSimpleId(Element idNode, RootClass entity, Mappings mappings,
329             java.util.Map JavaDoc inheritedMetas) throws MappingException {
330         String JavaDoc propertyName = idNode.attributeValue( "name" );
331
332         SimpleValue id = new SimpleValue( entity.getTable() );
333         entity.setIdentifier( id );
334
335         // if ( propertyName == null || entity.getPojoRepresentation() == null ) {
336
// bindSimpleValue( idNode, id, false, RootClass.DEFAULT_IDENTIFIER_COLUMN_NAME, mappings );
337
// if ( !id.isTypeSpecified() ) {
338
// throw new MappingException( "must specify an identifier type: " + entity.getEntityName()
339
// );
340
// }
341
// }
342
// else {
343
// bindSimpleValue( idNode, id, false, propertyName, mappings );
344
// PojoRepresentation pojo = entity.getPojoRepresentation();
345
// id.setTypeUsingReflection( pojo.getClassName(), propertyName );
346
//
347
// Property prop = new Property();
348
// prop.setValue( id );
349
// bindProperty( idNode, prop, mappings, inheritedMetas );
350
// entity.setIdentifierProperty( prop );
351
// }
352

353         if ( propertyName == null ) {
354             bindSimpleValue( idNode, id, false, RootClass.DEFAULT_IDENTIFIER_COLUMN_NAME, mappings );
355         }
356         else {
357             bindSimpleValue( idNode, id, false, propertyName, mappings );
358         }
359
360         if ( propertyName == null || !entity.hasPojoRepresentation() ) {
361             if ( !id.isTypeSpecified() ) {
362                 throw new MappingException( "must specify an identifier type: "
363                     + entity.getEntityName() );
364             }
365         }
366         else {
367             id.setTypeUsingReflection( entity.getClassName(), propertyName );
368         }
369
370         if ( propertyName != null ) {
371             Property prop = new Property();
372             prop.setValue( id );
373             bindProperty( idNode, prop, mappings, inheritedMetas );
374             entity.setIdentifierProperty( prop );
375         }
376
377         // TODO:
378
/*
379          * if ( id.getHibernateType().getReturnedClass().isArray() ) throw new MappingException(
380          * "illegal use of an array as an identifier (arrays don't reimplement equals)" );
381          */

382         makeIdentifier( idNode, id, mappings );
383     }
384
385     private static void bindCompositeId(Element idNode, RootClass entity, Mappings mappings,
386             java.util.Map JavaDoc inheritedMetas) throws MappingException {
387         String JavaDoc propertyName = idNode.attributeValue( "name" );
388         Component id = new Component( entity );
389         entity.setIdentifier( id );
390         bindCompositeId( idNode, id, entity, propertyName, mappings, inheritedMetas );
391         if ( propertyName == null ) {
392             entity.setEmbeddedIdentifier( id.isEmbedded() );
393             if ( id.isEmbedded() ) {
394                 // todo : what is the implication of this?
395
id.setDynamic( !entity.hasPojoRepresentation() );
396                 /*
397                  * Property prop = new Property(); prop.setName("id");
398                  * prop.setPropertyAccessorName("embedded"); prop.setValue(id);
399                  * entity.setIdentifierProperty(prop);
400                  */

401             }
402         }
403         else {
404             Property prop = new Property();
405             prop.setValue( id );
406             bindProperty( idNode, prop, mappings, inheritedMetas );
407             entity.setIdentifierProperty( prop );
408         }
409
410         makeIdentifier( idNode, id, mappings );
411
412     }
413
414     private static void bindVersioningProperty(Table table, Element subnode, Mappings mappings,
415             String JavaDoc name, RootClass entity, java.util.Map JavaDoc inheritedMetas) {
416
417         String JavaDoc propertyName = subnode.attributeValue( "name" );
418         SimpleValue val = new SimpleValue( table );
419         bindSimpleValue( subnode, val, false, propertyName, mappings );
420         if ( !val.isTypeSpecified() ) {
421             val.setTypeName( "version".equals( name ) ? "integer" : "timestamp" );
422         }
423         Property prop = new Property();
424         prop.setValue( val );
425         bindProperty( subnode, prop, mappings, inheritedMetas );
426         makeVersion( subnode, val );
427         entity.setVersion( prop );
428         entity.addProperty( prop );
429     }
430
431     private static void bindDiscriminatorProperty(Table table, RootClass entity, Element subnode,
432             Mappings mappings) {
433         SimpleValue discrim = new SimpleValue( table );
434         entity.setDiscriminator( discrim );
435         bindSimpleValue(
436                 subnode,
437                 discrim,
438                 false,
439                 RootClass.DEFAULT_DISCRIMINATOR_COLUMN_NAME,
440                 mappings
441             );
442         if ( !discrim.isTypeSpecified() ) {
443             discrim.setTypeName( "string" );
444             // ( (Column) discrim.getColumnIterator().next() ).setType(type);
445
}
446         entity.setPolymorphic( true );
447         if ( "true".equals( subnode.attributeValue( "force" ) ) )
448             entity.setForceDiscriminator( true );
449         if ( "false".equals( subnode.attributeValue( "insert" ) ) )
450             entity.setDiscriminatorInsertable( false );
451     }
452
453     public static void bindClass(Element node, PersistentClass persistentClass, Mappings mappings,
454             java.util.Map JavaDoc inheritedMetas) throws MappingException {
455         // transfer an explicitly defined entity name
456
// handle the lazy attribute
457
Attribute lazyNode = node.attribute( "lazy" );
458         boolean lazy = lazyNode == null ? mappings.isDefaultLazy() : "true".equals( lazyNode
459             .getValue() );
460         // go ahead and set the lazy here, since pojo.proxy can override it.
461
persistentClass.setLazy( lazy );
462
463         String JavaDoc entityName = node.attributeValue( "entity-name" );
464         if ( entityName == null ) entityName = getClassName( node.attribute("name"), mappings );
465         if ( entityName==null ) {
466             throw new MappingException( "Unable to determine entity name" );
467         }
468         persistentClass.setEntityName( entityName );
469
470         bindPojoRepresentation( node, persistentClass, mappings, inheritedMetas );
471         bindDom4jRepresentation( node, persistentClass, mappings, inheritedMetas );
472         bindMapRepresentation( node, persistentClass, mappings, inheritedMetas );
473
474         bindPersistentClassCommonValues( node, persistentClass, mappings, inheritedMetas );
475
476     }
477
478     private static void bindPojoRepresentation(Element node, PersistentClass entity,
479             Mappings mappings, java.util.Map JavaDoc metaTags) {
480
481         String JavaDoc className = getClassName( node.attribute( "name" ), mappings );
482         String JavaDoc proxyName = getClassName( node.attribute( "proxy" ), mappings );
483
484         entity.setClassName( className );
485
486         if ( proxyName != null ) {
487             entity.setProxyInterfaceName( proxyName );
488             entity.setLazy( true );
489         }
490         else if ( entity.isLazy() ) {
491             entity.setProxyInterfaceName( className );
492         }
493
494         Element tuplizer = locateTuplizerDefinition( node, EntityMode.POJO );
495         if ( tuplizer != null ) {
496             entity.addTuplizer( EntityMode.POJO, tuplizer.attributeValue( "class" ) );
497         }
498     }
499
500     private static void bindDom4jRepresentation(Element node, PersistentClass entity,
501             Mappings mappings, java.util.Map JavaDoc inheritedMetas) {
502         String JavaDoc nodeName = node.attributeValue( "node" );
503         if (nodeName==null) nodeName = StringHelper.unqualify( entity.getEntityName() );
504         entity.setNodeName(nodeName);
505
506         Element tuplizer = locateTuplizerDefinition( node, EntityMode.DOM4J );
507         if ( tuplizer != null ) {
508             entity.addTuplizer( EntityMode.DOM4J, tuplizer.attributeValue( "class" ) );
509         }
510     }
511
512     private static void bindMapRepresentation(Element node, PersistentClass entity,
513             Mappings mappings, java.util.Map JavaDoc inheritedMetas) {
514         Element tuplizer = locateTuplizerDefinition( node, EntityMode.MAP );
515         if ( tuplizer != null ) {
516             entity.addTuplizer( EntityMode.MAP, tuplizer.attributeValue( "class" ) );
517         }
518     }
519
520     /**
521      * Locate any explicit tuplizer definition in the metadata, for the given entity-mode.
522      *
523      * @param container The containing element (representing the entity/component)
524      * @param entityMode The entity-mode for which to locate the tuplizer element
525      * @return The tuplizer element, or null.
526      */

527     private static Element locateTuplizerDefinition(Element container, EntityMode entityMode) {
528         Iterator JavaDoc itr = container.elements( "tuplizer" ).iterator();
529         while( itr.hasNext() ) {
530             final Element tuplizerElem = ( Element ) itr.next();
531             if ( entityMode.toString().equals( tuplizerElem.attributeValue( "entity-mode") ) ) {
532                 return tuplizerElem;
533             }
534         }
535         return null;
536     }
537
538     private static void bindPersistentClassCommonValues(Element node, PersistentClass entity,
539             Mappings mappings, java.util.Map JavaDoc inheritedMetas) throws MappingException {
540         // DISCRIMINATOR
541
Attribute discriminatorNode = node.attribute( "discriminator-value" );
542         entity.setDiscriminatorValue( ( discriminatorNode == null )
543             ? entity.getEntityName()
544             : discriminatorNode.getValue() );
545
546         // DYNAMIC UPDATE
547
Attribute dynamicNode = node.attribute( "dynamic-update" );
548         entity.setDynamicUpdate( ( dynamicNode == null ) ? false : "true".equals( dynamicNode
549             .getValue() ) );
550
551         // DYNAMIC INSERT
552
Attribute insertNode = node.attribute( "dynamic-insert" );
553         entity.setDynamicInsert( ( insertNode == null ) ? false : "true".equals( insertNode
554             .getValue() ) );
555
556         // IMPORT
557
mappings.addImport( entity.getEntityName(), entity.getEntityName() );
558         if ( mappings.isAutoImport() && entity.getEntityName().indexOf( '.' ) > 0 ) {
559             mappings.addImport( entity.getEntityName(), StringHelper.unqualify( entity
560                 .getEntityName() ) );
561         }
562
563         // BATCH SIZE
564
Attribute batchNode = node.attribute( "batch-size" );
565         if ( batchNode != null ) entity.setBatchSize( Integer.parseInt( batchNode.getValue() ) );
566
567         // SELECT BEFORE UPDATE
568
Attribute sbuNode = node.attribute( "select-before-update" );
569         if ( sbuNode != null ) entity.setSelectBeforeUpdate( "true".equals( sbuNode.getValue() ) );
570
571         // OPTIMISTIC LOCK MODE
572
Attribute olNode = node.attribute( "optimistic-lock" );
573         entity.setOptimisticLockMode( getOptimisticLockMode( olNode ) );
574
575         entity.setMetaAttributes( getMetas( node, inheritedMetas ) );
576
577         // PERSISTER
578
Attribute persisterNode = node.attribute( "persister" );
579         if ( persisterNode == null ) {
580             // persister = SingleTableEntityPersister.class;
581
}
582         else {
583             try {
584                 entity.setEntityPersisterClass( ReflectHelper.classForName( persisterNode
585                     .getValue() ) );
586             }
587             catch (ClassNotFoundException JavaDoc cnfe) {
588                 throw new MappingException( "Could not find persister class: "
589                     + persisterNode.getValue() );
590             }
591         }
592
593         // CUSTOM SQL
594
handleCustomSQL( node, entity );
595
596         Iterator JavaDoc tables = node.elementIterator( "synchronize" );
597         while ( tables.hasNext() ) {
598             entity.addSynchronizedTable( ( (Element) tables.next() ).attributeValue( "table" ) );
599         }
600
601         Attribute abstractNode = node.attribute( "abstract" );
602         if ( abstractNode != null ) entity.setAbstract( "true".equals( abstractNode.getValue() ) );
603     }
604
605     private static void handleCustomSQL(Element node, PersistentClass model)
606             throws MappingException {
607         Element element = node.element( "sql-insert" );
608         if ( element != null ) {
609             boolean callable = false;
610             callable = isCallable( element );
611             model.setCustomSQLInsert( element.getText(), callable );
612         }
613
614         element = node.element( "sql-delete" );
615         if ( element != null ) {
616             boolean callable = false;
617             callable = isCallable( element );
618             model.setCustomSQLDelete( element.getText(), callable );
619         }
620
621         element = node.element( "sql-update" );
622         if ( element != null ) {
623             boolean callable = false;
624             callable = isCallable( element );
625             model.setCustomSQLUpdate( element.getText(), callable );
626         }
627
628         element = node.element( "loader" );
629         if ( element != null ) {
630             model.setLoaderName( element.attributeValue( "query-ref" ) );
631         }
632     }
633
634     private static void handleCustomSQL(Element node, Join model) throws MappingException {
635         Element element = node.element( "sql-insert" );
636         if ( element != null ) {
637             boolean callable = false;
638             callable = isCallable( element );
639             model.setCustomSQLInsert( element.getText(), callable );
640         }
641
642         element = node.element( "sql-delete" );
643         if ( element != null ) {
644             boolean callable = false;
645             callable = isCallable( element );
646             model.setCustomSQLDelete( element.getText(), callable );
647         }
648
649         element = node.element( "sql-update" );
650         if ( element != null ) {
651             boolean callable = false;
652             callable = isCallable( element );
653             model.setCustomSQLUpdate( element.getText(), callable );
654         }
655     }
656
657     private static void handleCustomSQL(Element node, Collection model) throws MappingException {
658         Element element = node.element( "sql-insert" );
659         if ( element != null ) {
660             boolean callable = false;
661             callable = isCallable( element, true );
662             model.setCustomSQLInsert( element.getText(), callable );
663         }
664
665         element = node.element( "sql-delete" );
666         if ( element != null ) {
667             boolean callable = false;
668             callable = isCallable( element, true );
669             model.setCustomSQLDelete( element.getText(), callable );
670         }
671
672         element = node.element( "sql-update" );
673         if ( element != null ) {
674             boolean callable = false;
675             callable = isCallable( element, true );
676             model.setCustomSQLUpdate( element.getText(), callable );
677         }
678
679         element = node.element( "sql-delete-all" );
680         if ( element != null ) {
681             boolean callable = false;
682             callable = isCallable( element, true );
683             model.setCustomSQLDeleteAll( element.getText(), callable );
684         }
685     }
686
687     private static boolean isCallable(Element e) throws MappingException {
688         return isCallable( e, true );
689     }
690
691     /**
692      * @param element
693      * @param supportsCallable
694      * @return
695      */

696     private static boolean isCallable(Element element, boolean supportsCallable)
697             throws MappingException {
698         Attribute attrib = element.attribute( "callable" );
699         if ( attrib != null && "true".equals( attrib.getValue() ) ) {
700             if ( !supportsCallable ) {
701                 throw new MappingException( "callable attribute not supported yet!" );
702             }
703             return true;
704         }
705         return false;
706     }
707
708     public static void bindUnionSubclass(Element node, UnionSubclass unionSubclass,
709             Mappings mappings, java.util.Map JavaDoc inheritedMetas) throws MappingException {
710
711         bindClass( node, unionSubclass, mappings, inheritedMetas );
712         inheritedMetas = getMetas( node, inheritedMetas, true ); // get meta's from <subclass>
713

714         if ( unionSubclass.getEntityPersisterClass() == null ) {
715             unionSubclass.getRootClass().setEntityPersisterClass(
716                 UnionSubclassEntityPersister.class );
717         }
718
719         Attribute schemaNode = node.attribute( "schema" );
720         String JavaDoc schema = schemaNode == null ? mappings.getSchemaName() : schemaNode.getValue();
721
722         Attribute catalogNode = node.attribute( "catalog" );
723         String JavaDoc catalog = catalogNode == null ? mappings.getCatalogName() : catalogNode.getValue();
724
725         Table mytable = mappings.addDenormalizedTable(
726                 schema,
727                 catalog,
728                 getClassTableName(unionSubclass, node, mappings ),
729                 unionSubclass.isAbstract(),
730                 getSubselect( node ),
731                 unionSubclass.getSuperclass().getTable() );
732         unionSubclass.setTable( mytable );
733
734         log.info( "Mapping union-subclass: "
735             + unionSubclass.getEntityName()
736             + " -> "
737             + unionSubclass.getTable().getName() );
738
739         createClassProperties( node, unionSubclass, mappings, inheritedMetas );
740
741     }
742
743     public static void bindSubclass(Element node, Subclass subclass, Mappings mappings,
744             java.util.Map JavaDoc inheritedMetas) throws MappingException {
745
746         bindClass( node, subclass, mappings, inheritedMetas );
747         inheritedMetas = getMetas( node, inheritedMetas, true ); // get meta's from <subclass>
748

749         if ( subclass.getEntityPersisterClass() == null ) {
750             subclass.getRootClass().setEntityPersisterClass( SingleTableEntityPersister.class );
751         }
752
753         log.info( "Mapping subclass: "
754             + subclass.getEntityName()
755             + " -> "
756             + subclass.getTable().getName() );
757
758         // properties
759
createClassProperties( node, subclass, mappings, inheritedMetas );
760     }
761
762     private static String JavaDoc getClassTableName(PersistentClass model, Element node, Mappings mappings) {
763         Attribute tableNameNode = node.attribute( "table" );
764         if ( tableNameNode == null ) {
765             return mappings.getNamingStrategy().classToTableName( model.getEntityName() );
766         }
767         else {
768             return mappings.getNamingStrategy().tableName( tableNameNode.getValue() );
769         }
770     }
771
772     public static void bindJoinedSubclass(Element node, JoinedSubclass joinedSubclass,
773             Mappings mappings, java.util.Map JavaDoc inheritedMetas) throws MappingException {
774
775         bindClass( node, joinedSubclass, mappings, inheritedMetas );
776         inheritedMetas = getMetas( node, inheritedMetas, true ); // get meta's from
777
// <joined-subclass>
778

779         // joined subclasses
780
if ( joinedSubclass.getEntityPersisterClass() == null ) {
781             joinedSubclass.getRootClass().setEntityPersisterClass(
782                 JoinedSubclassEntityPersister.class );
783         }
784
785         Attribute schemaNode = node.attribute( "schema" );
786         String JavaDoc schema = schemaNode == null ? mappings.getSchemaName() : schemaNode.getValue();
787
788         Attribute catalogNode = node.attribute( "catalog" );
789         String JavaDoc catalog = catalogNode == null ? mappings.getCatalogName() : catalogNode.getValue();
790
791         Table mytable = mappings.addTable(
792             schema,
793             catalog,
794             getClassTableName( joinedSubclass, node, mappings ),
795             getSubselect( node ),
796             false );
797         joinedSubclass.setTable( mytable );
798         bindComment(mytable, node);
799
800         log.info( "Mapping joined-subclass: "
801             + joinedSubclass.getEntityName()
802             + " -> "
803             + joinedSubclass.getTable().getName() );
804
805         // KEY
806
Element keyNode = node.element( "key" );
807         SimpleValue key = new DependantValue( mytable, joinedSubclass.getIdentifier() );
808         joinedSubclass.setKey( key );
809         key.setCascadeDeleteEnabled( "cascade".equals( keyNode.attributeValue( "on-delete" ) ) );
810         bindSimpleValue( keyNode, key, false, joinedSubclass.getEntityName(), mappings );
811
812         // model.getKey().setType( new Type( model.getIdentifier() ) );
813
joinedSubclass.createPrimaryKey();
814         joinedSubclass.createForeignKey();
815
816         // CHECK
817
Attribute chNode = node.attribute( "check" );
818         if ( chNode != null ) mytable.addCheckConstraint( chNode.getValue() );
819
820         // properties
821
createClassProperties( node, joinedSubclass, mappings, inheritedMetas );
822
823     }
824
825     private static void bindJoin(Element node, Join join, Mappings mappings,
826             java.util.Map JavaDoc inheritedMetas) throws MappingException {
827
828         PersistentClass persistentClass = join.getPersistentClass();
829         String JavaDoc path = persistentClass.getEntityName();
830
831         // TABLENAME
832

833         Attribute schemaNode = node.attribute( "schema" );
834         String JavaDoc schema = schemaNode == null ? mappings.getSchemaName() : schemaNode.getValue();
835
836         Attribute catalogNode = node.attribute( "catalog" );
837         String JavaDoc catalog = catalogNode == null ? mappings.getCatalogName() : catalogNode.getValue();
838
839         Table table = mappings.addTable(
840             schema,
841             catalog,
842             getClassTableName( persistentClass, node, mappings ),
843             getSubselect( node ),
844             false );
845         join.setTable( table );
846         bindComment(table, node);
847
848         Attribute fetchNode = node.attribute( "fetch" );
849         if ( fetchNode != null )
850             join.setSequentialSelect( "select".equals( fetchNode.getValue() ) );
851
852         Attribute invNode = node.attribute( "inverse" );
853         if ( invNode != null ) join.setInverse( "true".equals( invNode.getValue() ) );
854
855         Attribute nullNode = node.attribute( "optional" );
856         if ( nullNode != null ) join.setOptional( "true".equals( nullNode.getValue() ) );
857
858         log.info( "Mapping class join: "
859             + persistentClass.getEntityName()
860             + " -> "
861             + join.getTable().getName() );
862
863         // KEY
864
Element keyNode = node.element( "key" );
865         SimpleValue key = new DependantValue( table, persistentClass.getIdentifier() );
866         join.setKey( key );
867         key.setCascadeDeleteEnabled( "cascade".equals( keyNode.attributeValue( "on-delete" ) ) );
868         bindSimpleValue( keyNode, key, false, persistentClass.getEntityName(), mappings );
869
870         // join.getKey().setType( new Type( lazz.getIdentifier() ) );
871
join.createPrimaryKey();
872         join.createForeignKey();
873
874         // PROPERTIES
875
Iterator JavaDoc iter = node.elementIterator();
876         while ( iter.hasNext() ) {
877             Element subnode = (Element) iter.next();
878             String JavaDoc name = subnode.getName();
879             String JavaDoc propertyName = subnode.attributeValue( "name" );
880
881             Value value = null;
882             if ( "many-to-one".equals( name ) ) {
883                 value = new ManyToOne( table );
884                 bindManyToOne( subnode, (ManyToOne) value, propertyName, true, mappings );
885             }
886             else if ( "any".equals( name ) ) {
887                 value = new Any( table );
888                 bindAny( subnode, (Any) value, true, mappings );
889             }
890             else if ( "property".equals( name ) ) {
891                 value = new SimpleValue( table );
892                 bindSimpleValue( subnode, (SimpleValue) value, true, propertyName, mappings );
893             }
894             else if ( "component".equals( name ) || "dynamic-component".equals( name ) ) {
895                 String JavaDoc subpath = StringHelper.qualify( path, propertyName );
896                 value = new Component( join );
897                 bindComponent(
898                         subnode,
899                         (Component) value,
900                         join.getPersistentClass().getClassName(),
901                         propertyName,
902                         subpath,
903                         true,
904                         false,
905                         mappings,
906                         inheritedMetas,
907                         false
908                     );
909             }
910
911             if ( value != null ) {
912                 Property prop = createProperty( value, propertyName, persistentClass
913                     .getEntityName(), subnode, mappings, inheritedMetas );
914                 prop.setOptional( join.isOptional() );
915                 join.addProperty( prop );
916             }
917
918         }
919
920         // CUSTOM SQL
921
handleCustomSQL( node, join );
922
923     }
924
925     public static void bindColumns(final Element node, final SimpleValue simpleValue,
926             final boolean isNullable, final boolean autoColumn, final String JavaDoc propertyPath,
927             final Mappings mappings) throws MappingException {
928
929         Table table = simpleValue.getTable();
930
931         // COLUMN(S)
932
Attribute columnAttribute = node.attribute( "column" );
933         if ( columnAttribute == null ) {
934             Iterator JavaDoc iter = node.elementIterator();
935             int count = 0;
936             while ( iter.hasNext() ) {
937                 Element columnElement = (Element) iter.next();
938                 if ( columnElement.getName().equals( "column" ) ) {
939                     Column column = new Column();
940                     column.setValue( simpleValue );
941                     column.setTypeIndex( count++ );
942                     bindColumn( columnElement, column, isNullable );
943                     column.setName( mappings.getNamingStrategy().columnName(
944                         columnElement.attributeValue( "name" ) ) );
945                     if ( table != null ) table.addColumn( column ); // table=null -> an association
946
// - fill it in later
947
simpleValue.addColumn( column );
948                     // column index
949
bindIndex( columnElement.attribute( "index" ), table, column );
950                     bindIndex( node.attribute( "index" ), table, column );
951                     //column unique-key
952
bindUniqueKey( columnElement.attribute( "unique-key" ), table, column );
953                     bindUniqueKey( node.attribute( "unique-key" ), table, column );
954                 }
955                 else if ( columnElement.getName().equals( "formula" ) ) {
956                     Formula formula = new Formula();
957                     formula.setFormula( columnElement.getText() );
958                     simpleValue.addFormula( formula );
959                 }
960             }
961         }
962         else {
963             if ( node.elementIterator( "column" ).hasNext() ) {
964                 throw new MappingException(
965                     "column attribute may not be used together with <column> subelement" );
966             }
967             if ( node.elementIterator( "formula" ).hasNext() ) {
968                 throw new MappingException(
969                     "column attribute may not be used together with <formula> subelement" );
970             }
971
972             Column column = new Column();
973             column.setValue( simpleValue );
974             bindColumn( node, column, isNullable );
975             column.setName( mappings.getNamingStrategy().columnName( columnAttribute.getValue() ) );
976             if ( table != null ) table.addColumn( column ); // table=null -> an association - fill
977
// it in later
978
simpleValue.addColumn( column );
979             bindIndex( node.attribute( "index" ), table, column );
980             bindUniqueKey( node.attribute( "unique-key" ), table, column );
981         }
982
983         if ( autoColumn && simpleValue.getColumnSpan() == 0 ) {
984             Column col = new Column();
985             col.setValue( simpleValue );
986             bindColumn( node, col, isNullable );
987             col.setName( mappings.getNamingStrategy().propertyToColumnName( propertyPath ) );
988             simpleValue.getTable().addColumn( col );
989             simpleValue.addColumn( col );
990             bindIndex( node.attribute( "index" ), table, col );
991             bindUniqueKey( node.attribute( "unique-key" ), table, col );
992         }
993
994     }
995
996     private static void bindIndex(Attribute indexAttribute, Table table, Column column) {
997         if ( indexAttribute != null && table != null ) {
998             StringTokenizer JavaDoc tokens = new StringTokenizer JavaDoc( indexAttribute.getValue(), ", " );
999             while ( tokens.hasMoreTokens() ) {
1000                table.getOrCreateIndex( tokens.nextToken() ).addColumn( column );
1001            }
1002        }
1003    }
1004
1005    private static void bindUniqueKey(Attribute uniqueKeyAttribute, Table table, Column column) {
1006        if ( uniqueKeyAttribute != null && table != null ) {
1007            StringTokenizer JavaDoc tokens = new StringTokenizer JavaDoc( uniqueKeyAttribute.getValue(), ", " );
1008            while ( tokens.hasMoreTokens() ) {
1009                table.getOrCreateUniqueKey( tokens.nextToken() ).addColumn( column );
1010            }
1011        }
1012    }
1013
1014    // automatically makes a column with the default name if none is specifed by XML
1015
public static void bindSimpleValue(Element node, SimpleValue simpleValue, boolean isNullable,
1016            String JavaDoc path, Mappings mappings) throws MappingException {
1017        bindSimpleValueType( node, simpleValue, mappings );
1018
1019        bindColumnsOrFormula( node, simpleValue, path, isNullable, mappings );
1020
1021        Attribute fkNode = node.attribute( "foreign-key" );
1022        if ( fkNode != null ) simpleValue.setForeignKeyName( fkNode.getValue() );
1023    }
1024
1025    private static void bindSimpleValueType(Element node, SimpleValue simpleValue, Mappings mappings)
1026            throws MappingException {
1027        String JavaDoc typeName = null;
1028
1029        Properties JavaDoc parameters = new Properties JavaDoc();
1030
1031        Attribute typeNode = node.attribute( "type" );
1032        if ( typeNode == null ) typeNode = node.attribute( "id-type" ); // for an any
1033
if ( typeNode != null ) typeName = typeNode.getValue();
1034
1035        Element typeChild = node.element( "type" );
1036        if ( typeName == null && typeChild != null ) {
1037            typeName = typeChild.attribute( "name" ).getValue();
1038            Iterator JavaDoc typeParameters = typeChild.elementIterator( "param" );
1039
1040            while ( typeParameters.hasNext() ) {
1041                Element paramElement = (Element) typeParameters.next();
1042                parameters.setProperty( paramElement.attributeValue( "name" ), paramElement
1043                    .getTextTrim() );
1044            }
1045        }
1046
1047        TypeDef typeDef = mappings.getTypeDef( typeName );
1048        if ( typeDef != null ) {
1049            typeName = typeDef.getTypeClass();
1050            // parameters on the property mapping should
1051
// override parameters in the typedef
1052
Properties JavaDoc allParameters = new Properties JavaDoc();
1053            allParameters.putAll( typeDef.getParameters() );
1054            allParameters.putAll( parameters );
1055            parameters = allParameters;
1056        }
1057
1058        if ( !parameters.isEmpty() ) simpleValue.setTypeParameters( parameters );
1059
1060        if ( typeName != null ) simpleValue.setTypeName( typeName );
1061    }
1062
1063    public static void bindProperty(Element node, Property property, Mappings mappings,
1064            java.util.Map JavaDoc inheritedMetas) throws MappingException {
1065        
1066        String JavaDoc propName = node.attributeValue( "name" );
1067        property.setName( propName );
1068        String JavaDoc nodeName = node.attributeValue( "node" );
1069        if (nodeName==null) nodeName = propName;
1070        property.setNodeName( nodeName );
1071        
1072        // TODO:
1073
//Type type = model.getValue().getType();
1074
//if (type==null) throw new MappingException(
1075
//"Could not determine a property type for: " + model.getName() );
1076

1077        Attribute accessNode = node.attribute( "access" );
1078        if ( accessNode != null ) {
1079            property.setPropertyAccessorName( accessNode.getValue() );
1080        }
1081        else if ( node.getName().equals( "properties" ) ) {
1082            property.setPropertyAccessorName( "embedded" );
1083        }
1084        else {
1085            property.setPropertyAccessorName( mappings.getDefaultAccess() );
1086        }
1087
1088        Attribute cascadeNode = node.attribute( "cascade" );
1089        property.setCascade( cascadeNode == null ? mappings.getDefaultCascade() : cascadeNode
1090            .getValue() );
1091
1092        Attribute updateNode = node.attribute( "update" );
1093        property.setUpdateable( updateNode == null || "true".equals( updateNode.getValue() ) );
1094
1095        Attribute insertNode = node.attribute( "insert" );
1096        property.setInsertable( insertNode == null || "true".equals( insertNode.getValue() ) );
1097
1098        Attribute lockNode = node.attribute( "optimistic-lock" );
1099        property.setOptimisticLocked( lockNode == null || "true".equals( lockNode.getValue() ) );
1100
1101        boolean isLazyable = "property".equals( node.getName() )
1102            || "component".equals( node.getName() )
1103            || "many-to-one".equals( node.getName() )
1104            || "one-to-one".equals( node.getName() )
1105            || "any".equals( node.getName() );
1106        if ( isLazyable ) {
1107            Attribute lazyNode = node.attribute( "lazy" );
1108            property.setLazy( lazyNode != null && "true".equals( lazyNode.getValue() ) );
1109        }
1110
1111        if ( log.isDebugEnabled() ) {
1112            String JavaDoc msg = "Mapped property: " + property.getName();
1113            String JavaDoc columns = columns( property.getValue() );
1114            if ( columns.length() > 0 ) msg += " -> " + columns;
1115            // TODO: this fails if we run with debug on!
1116
// if ( model.getType()!=null ) msg += ", type: " + model.getType().getName();
1117
log.debug( msg );
1118        }
1119
1120        property.setMetaAttributes( getMetas( node, inheritedMetas ) );
1121
1122    }
1123
1124    private static String JavaDoc columns(Value val) {
1125        StringBuffer JavaDoc columns = new StringBuffer JavaDoc();
1126        Iterator JavaDoc iter = val.getColumnIterator();
1127        while ( iter.hasNext() ) {
1128            columns.append( ( (Selectable) iter.next() ).getText() );
1129            if ( iter.hasNext() ) columns.append( ", " );
1130        }
1131        return columns.toString();
1132    }
1133
1134    /**
1135     * Called for all collections
1136     */

1137    public static void bindCollection(Element node, Collection collection, String JavaDoc className,
1138            String JavaDoc path, Mappings mappings) throws MappingException {
1139
1140        // ROLENAME
1141
collection.setRole(path);
1142
1143        Attribute inverseNode = node.attribute( "inverse" );
1144        if ( inverseNode != null ) {
1145            collection.setInverse( "true".equals( inverseNode.getValue() ) );
1146        }
1147
1148        Attribute mutableNode = node.attribute( "mutable" );
1149        if ( mutableNode != null ) {
1150            collection.setMutable( !"false".equals( mutableNode.getValue() ) );
1151        }
1152
1153        Attribute olNode = node.attribute( "optimistic-lock" );
1154        collection.setOptimisticLocked( olNode == null || "true".equals( olNode.getValue() ) );
1155
1156        Attribute orderNode = node.attribute( "order-by" );
1157        if ( orderNode != null ) {
1158            if ( Environment.jvmSupportsLinkedHashCollections() || ( collection instanceof Bag ) ) {
1159                collection.setOrderBy( orderNode.getValue() );
1160            }
1161            else {
1162                log.warn( "Attribute \"order-by\" ignored in JDK1.3 or less" );
1163            }
1164        }
1165        Attribute whereNode = node.attribute( "where" );
1166        if ( whereNode != null ) {
1167            collection.setWhere( whereNode.getValue() );
1168        }
1169        Attribute batchNode = node.attribute( "batch-size" );
1170        if ( batchNode != null ) {
1171            collection.setBatchSize( Integer.parseInt( batchNode.getValue() ) );
1172        }
1173
1174        String JavaDoc nodeName = node.attributeValue( "node" );
1175        if ( nodeName == null ) nodeName = node.attributeValue( "name" );
1176        collection.setNodeName( nodeName );
1177        String JavaDoc embed = node.attributeValue( "embed-xml" );
1178        collection.setEmbedded( embed==null || "true".equals(embed) );
1179        
1180
1181        // PERSISTER
1182
Attribute persisterNode = node.attribute( "persister" );
1183        if ( persisterNode != null ) {
1184            try {
1185                collection.setCollectionPersisterClass( ReflectHelper.classForName( persisterNode
1186                    .getValue() ) );
1187            }
1188            catch (ClassNotFoundException JavaDoc cnfe) {
1189                throw new MappingException( "Could not find collection persister class: "
1190                    + persisterNode.getValue() );
1191            }
1192        }
1193
1194        Attribute typeNode = node.attribute( "collection-type" );
1195        if ( typeNode != null ) collection.setTypeName( typeNode.getValue() );
1196        
1197        // FETCH STRATEGY
1198

1199        initOuterJoinFetchSetting( node, collection );
1200        
1201        if ( "subselect".equals( node.attributeValue("fetch") ) ) {
1202            collection.setSubselectLoadable(true);
1203            collection.getOwner().setSubselectLoadableCollections(true);
1204        }
1205
1206        initLaziness( node, collection, mappings, "true", mappings.isDefaultLazy() );
1207
1208        Element oneToManyNode = node.element( "one-to-many" );
1209        if ( oneToManyNode != null ) {
1210            OneToMany oneToMany = new OneToMany( collection.getOwner() );
1211            collection.setElement( oneToMany );
1212            bindOneToMany( oneToManyNode, oneToMany, mappings );
1213            // we have to set up the table later!! yuck
1214
}
1215        else {
1216            // TABLE
1217
Attribute tableNode = node.attribute( "table" );
1218            String JavaDoc tableName;
1219            if ( tableNode != null ) {
1220                tableName = mappings.getNamingStrategy().tableName( tableNode.getValue() );
1221            }
1222            else {
1223                tableName = mappings.getNamingStrategy().propertyToTableName( className, path );
1224            }
1225            Attribute schemaNode = node.attribute( "schema" );
1226            String JavaDoc schema = schemaNode == null ? mappings.getSchemaName() : schemaNode.getValue();
1227
1228            Attribute catalogNode = node.attribute( "catalog" );
1229            String JavaDoc catalog = catalogNode == null ? mappings.getCatalogName() : catalogNode
1230                .getValue();
1231
1232            Table table = mappings.addTable(
1233                schema,
1234                catalog,
1235                tableName,
1236                getSubselect( node ),
1237                false );
1238            collection.setCollectionTable( table );
1239            bindComment(table, node);
1240
1241            log.info( "Mapping collection: "
1242                + collection.getRole()
1243                + " -> "
1244                + collection.getCollectionTable().getName() );
1245        }
1246
1247        // SORT
1248
Attribute sortedAtt = node.attribute( "sort" );
1249        // unsorted, natural, comparator.class.name
1250
if ( sortedAtt == null || sortedAtt.getValue().equals( "unsorted" ) ) {
1251            collection.setSorted( false );
1252        }
1253        else {
1254            collection.setSorted( true );
1255            String JavaDoc comparatorClassName = sortedAtt.getValue();
1256            if ( !comparatorClassName.equals( "natural" ) ) {
1257                try {
1258                    collection.setComparator( (Comparator JavaDoc) ReflectHelper.classForName(
1259                        comparatorClassName ).newInstance() );
1260                }
1261                catch (Exception JavaDoc e) {
1262                    throw new MappingException( "Could not instantiate comparator class: "
1263                        + comparatorClassName );
1264                }
1265            }
1266        }
1267
1268        // ORPHAN DELETE (used for programmer error detection)
1269
Attribute cascadeAtt = node.attribute( "cascade" );
1270        if ( cascadeAtt != null && cascadeAtt.getValue().indexOf( "delete-orphan" ) >= 0 ) {
1271            collection.setOrphanDelete( true );
1272        }
1273
1274        // CUSTOM SQL
1275
handleCustomSQL( node, collection );
1276        // set up second pass
1277
if ( collection instanceof List JavaDoc ) {
1278            mappings.addSecondPass( new ListSecondPass( node, mappings, (List JavaDoc) collection ) );
1279        }
1280        else if ( collection instanceof Map JavaDoc ) {
1281            mappings.addSecondPass( new MapSecondPass( node, mappings, (Map JavaDoc) collection ) );
1282        }
1283        else if ( collection instanceof IdentifierCollection ) {
1284            mappings.addSecondPass( new IdentifierCollectionSecondPass(
1285                node,
1286                mappings,
1287                (IdentifierCollection) collection ) );
1288        }
1289        else {
1290            mappings.addSecondPass( new CollectionSecondPass( node, mappings, collection ) );
1291        }
1292
1293        Iterator JavaDoc iter = node.elementIterator( "filter" );
1294        while ( iter.hasNext() ) {
1295            final Element filter = (Element) iter.next();
1296            parseFilter( filter, collection, mappings );
1297        }
1298
1299        Iterator JavaDoc tables = node.elementIterator( "synchronize" );
1300        while ( tables.hasNext() ) {
1301            collection.getSynchronizedTables().add(
1302                ( (Element) tables.next() ).attributeValue( "table" ) );
1303        }
1304
1305        Element element = node.element( "loader" );
1306        if ( element != null ) {
1307            collection.setLoaderName( element.attributeValue( "query-ref" ) );
1308        }
1309
1310        collection.setReferencedPropertyName( node.element( "key" ).attributeValue( "property-ref" ) );
1311    }
1312
1313    private static void initLaziness(
1314            Element node,
1315            Fetchable fetchable,
1316            Mappings mappings,
1317            String JavaDoc proxyVal,
1318            boolean defaultLazy
1319    ) {
1320        Attribute lazyNode = node.attribute( "lazy" );
1321        boolean isLazyTrue = lazyNode == null ?
1322                defaultLazy && fetchable.isLazy() : //fetch="join" overrides default laziness
1323
lazyNode.getValue().equals(proxyVal); //fetch="join" overrides default laziness
1324
fetchable.setLazy( isLazyTrue );
1325    }
1326
1327    private static void initLaziness(
1328            Element node,
1329            ToOne fetchable,
1330            Mappings mappings,
1331            boolean defaultLazy
1332    ) {
1333        if ( "no-proxy".equals( node.attributeValue( "lazy" ) ) ) {
1334            fetchable.setUnwrapProxy(true);
1335            fetchable.setLazy(true);
1336            //TODO: better to degrade to lazy="false" if uninstrumented
1337
}
1338        else {
1339            initLaziness(node, fetchable, mappings, "proxy", defaultLazy);
1340        }
1341    }
1342
1343    private static void bindColumnsOrFormula(Element node, SimpleValue simpleValue, String JavaDoc path,
1344            boolean isNullable, Mappings mappings) {
1345        Attribute formulaNode = node.attribute( "formula" );
1346        if ( formulaNode != null ) {
1347            Formula f = new Formula();
1348            f.setFormula( formulaNode.getText() );
1349            simpleValue.addFormula( f );
1350        }
1351        else {
1352            bindColumns( node, simpleValue, isNullable, true, path, mappings );
1353        }
1354    }
1355    
1356    private static void bindComment(Table table, Element node) {
1357        Element comment = node.element("comment");
1358        if (comment!=null) table.setComment( comment.getTextTrim() );
1359    }
1360
1361    public static void bindManyToOne(Element node, ManyToOne manyToOne, String JavaDoc path,
1362            boolean isNullable, Mappings mappings) throws MappingException {
1363
1364        bindColumnsOrFormula( node, manyToOne, path, isNullable, mappings );
1365        initOuterJoinFetchSetting( node, manyToOne );
1366        initLaziness( node, manyToOne, mappings, true );
1367
1368        Attribute ukName = node.attribute( "property-ref" );
1369        if ( ukName != null ) {
1370            manyToOne.setReferencedPropertyName( ukName.getValue() );
1371        }
1372
1373        manyToOne.setReferencedEntityName( getEntityName( node, mappings ) );
1374
1375        String JavaDoc embed = node.attributeValue( "embed-xml" );
1376        manyToOne.setEmbedded( embed == null || "true".equals( embed ) );
1377
1378        String JavaDoc notFound = node.attributeValue( "not-found" );
1379        manyToOne.setIgnoreNotFound( "ignore".equals( notFound ) );
1380
1381        if( ukName != null && !manyToOne.isIgnoreNotFound() ) {
1382            if ( !node.getName().equals("many-to-many") ) { //TODO: really bad, evil hack to fix!!!
1383
mappings.addSecondPass( new ManyToOneSecondPass(mappings,manyToOne) );
1384            }
1385        }
1386        
1387        Attribute fkNode = node.attribute( "foreign-key" );
1388        if ( fkNode != null ) manyToOne.setForeignKeyName( fkNode.getValue() );
1389        
1390        validateCascade( node, path );
1391    }
1392
1393    private static void validateCascade(Element node, String JavaDoc path) {
1394        String JavaDoc cascade = node.attributeValue("cascade");
1395        if ( cascade!=null && cascade.indexOf("delete-orphan")>0 ) {
1396            throw new MappingException("single-valued associations do not support orphan delete: " + path);
1397        }
1398    }
1399
1400    public static void bindAny(Element node, Any any, boolean isNullable, Mappings mappings)
1401            throws MappingException {
1402        any.setIdentifierType( getTypeFromXML( node ) );
1403        Attribute metaAttribute = node.attribute( "meta-type" );
1404        if ( metaAttribute != null ) {
1405            any.setMetaType( metaAttribute.getValue() );
1406
1407            Iterator JavaDoc iter = node.elementIterator( "meta-value" );
1408            if ( iter.hasNext() ) {
1409                HashMap JavaDoc values = new HashMap JavaDoc();
1410                org.hibernate.type.Type metaType = TypeFactory.heuristicType( any.getMetaType() );
1411                while ( iter.hasNext() ) {
1412                    Element metaValue = (Element) iter.next();
1413                    try {
1414                        Object JavaDoc value = ( (DiscriminatorType) metaType ).stringToObject( metaValue
1415                            .attributeValue( "value" ) );
1416                        String JavaDoc entityName = getClassName( metaValue.attribute( "class" ), mappings );
1417                        values.put( value, entityName );
1418                    }
1419                    catch (ClassCastException JavaDoc cce) {
1420                        throw new MappingException( "meta-type was not a DiscriminatorType: "
1421                            + metaType.getName() );
1422                    }
1423                    catch (Exception JavaDoc e) {
1424                        throw new MappingException( "could not interpret meta-value", e );
1425                    }
1426                }
1427                any.setMetaValues( values );
1428            }
1429
1430        }
1431
1432        bindColumns( node, any, isNullable, false, null, mappings );
1433    }
1434
1435    public static void bindOneToOne(Element node, OneToOne oneToOne, String JavaDoc path, boolean isNullable,
1436            Mappings mappings) throws MappingException {
1437        
1438        bindColumns( node, oneToOne, isNullable, false, null, mappings );
1439
1440        Attribute constrNode = node.attribute( "constrained" );
1441        boolean constrained = constrNode != null && constrNode.getValue().equals( "true" );
1442        oneToOne.setConstrained( constrained );
1443
1444        oneToOne.setForeignKeyType( constrained ?
1445                ForeignKeyDirection.FOREIGN_KEY_FROM_PARENT :
1446                ForeignKeyDirection.FOREIGN_KEY_TO_PARENT );
1447
1448        initOuterJoinFetchSetting( node, oneToOne );
1449        initLaziness( node, oneToOne, mappings, true );
1450
1451        oneToOne.setEmbedded( "true".equals( node.attributeValue( "embed-xml" ) ) );
1452
1453        Attribute fkNode = node.attribute( "foreign-key" );
1454        if ( fkNode != null ) oneToOne.setForeignKeyName( fkNode.getValue() );
1455
1456        Attribute ukName = node.attribute( "property-ref" );
1457        if ( ukName != null ) oneToOne.setReferencedPropertyName( ukName.getValue() );
1458        
1459        oneToOne.setPropertyName( node.attributeValue( "name" ) );
1460
1461        oneToOne.setReferencedEntityName( getEntityName( node, mappings ) );
1462
1463        validateCascade( node, path );
1464    }
1465
1466    public static void bindOneToMany(Element node, OneToMany oneToMany, Mappings mappings)
1467            throws MappingException {
1468        
1469        oneToMany.setReferencedEntityName( getEntityName( node, mappings ) );
1470        
1471        String JavaDoc embed = node.attributeValue( "embed-xml" );
1472        oneToMany.setEmbedded( embed == null || "true".equals( embed ) );
1473
1474        String JavaDoc notFound = node.attributeValue( "not-found" );
1475        oneToMany.setIgnoreNotFound( "ignore".equals( notFound ) );
1476
1477    }
1478
1479    public static void bindColumn(Element node, Column column, boolean isNullable) {
1480        Attribute lengthNode = node.attribute( "length" );
1481        if ( lengthNode != null ) column.setLength( Integer.parseInt( lengthNode.getValue() ) );
1482        Attribute scalNode = node.attribute( "scale" );
1483        if ( scalNode != null ) column.setScale( Integer.parseInt( scalNode.getValue() ) );
1484        Attribute precNode = node.attribute( "precision" );
1485        if ( precNode != null ) column.setPrecision( Integer.parseInt( precNode.getValue() ) );
1486
1487        Attribute nullNode = node.attribute( "not-null" );
1488        column.setNullable( nullNode == null ? isNullable : nullNode.getValue().equals( "false" ) );
1489
1490        Attribute unqNode = node.attribute( "unique" );
1491        if ( unqNode != null ) column.setUnique( unqNode.getValue().equals( "true" ) );
1492
1493        column.setCheckConstraint( node.attributeValue( "check" ) );
1494
1495        Attribute typeNode = node.attribute( "sql-type" );
1496        if ( typeNode != null ) column.setSqlType( typeNode.getValue() );
1497
1498        Element comment = node.element("comment");
1499        if (comment!=null) column.setComment( comment.getTextTrim() );
1500    
1501    }
1502
1503    /**
1504     * Called for arrays and primitive arrays
1505     */

1506    public static void bindArray(Element node, Array array, String JavaDoc prefix, String JavaDoc path,
1507            Mappings mappings) throws MappingException {
1508
1509        bindCollection( node, array, prefix, path, mappings );
1510
1511        Attribute att = node.attribute( "element-class" );
1512        if ( att != null ) array.setElementClassName( getClassName( att, mappings ) );
1513
1514    }
1515
1516    private static Class JavaDoc reflectedPropertyClass(String JavaDoc className, String JavaDoc propertyName)
1517            throws MappingException {
1518        if ( className == null ) return null;
1519        return ReflectHelper.reflectedPropertyClass( className, propertyName );
1520    }
1521
1522    public static void bindComposite(Element node, Component component, String JavaDoc path,
1523            boolean isNullable, Mappings mappings, java.util.Map JavaDoc inheritedMetas)
1524            throws MappingException {
1525        bindComponent(
1526                node,
1527                component,
1528                null,
1529                null,
1530                path,
1531                isNullable,
1532                false,
1533                mappings,
1534                inheritedMetas,
1535                false
1536            );
1537    }
1538
1539    public static void bindCompositeId(Element node, Component component,
1540            PersistentClass persistentClass, String JavaDoc propertyName, Mappings mappings,
1541            java.util.Map JavaDoc inheritedMetas) throws MappingException {
1542
1543        component.setKey( true );
1544
1545        String JavaDoc path = StringHelper.qualify(
1546                persistentClass.getEntityName(),
1547                propertyName == null ? "id" : propertyName );
1548
1549        bindComponent(
1550                node,
1551                component,
1552                persistentClass.getClassName(),
1553                propertyName,
1554                path,
1555                false,
1556                node.attribute( "class" ) == null
1557                        && propertyName == null,
1558                mappings,
1559                inheritedMetas,
1560                false
1561            );
1562        
1563        if ( "true".equals( node.attributeValue("mapped") ) ) {
1564            if ( propertyName!=null ) {
1565                throw new MappingException("cannot combine mapped=\"true\" with specified name");
1566            }
1567            Component mapper = new Component(persistentClass);
1568            bindComponent(
1569                    node,
1570                    mapper,
1571                    persistentClass.getClassName(),
1572                    null,
1573                    path,
1574                    false,
1575                    true,
1576                    mappings,
1577                    inheritedMetas,
1578                    true
1579                );
1580            persistentClass.setIdentifierMapper(mapper);
1581            Property property = new Property();
1582            property.setName("_identifierMapper");
1583            property.setNodeName("id");
1584            property.setUpdateable(false);
1585            property.setInsertable(false);
1586            property.setValue(mapper);
1587            property.setPropertyAccessorName( "embedded" );
1588            persistentClass.addProperty(property);
1589        }
1590
1591    }
1592
1593    public static void bindComponent(Element node, Component component, String JavaDoc ownerClassName,
1594            String JavaDoc parentProperty, String JavaDoc path, boolean isNullable, boolean isEmbedded,
1595            Mappings mappings, java.util.Map JavaDoc inheritedMetas, boolean isIdentifierMapper) throws MappingException {
1596
1597        component.setEmbedded( isEmbedded );
1598
1599        component.setMetaAttributes( getMetas( node, inheritedMetas ) );
1600
1601        Attribute classNode = isIdentifierMapper ? null : node.attribute( "class" );
1602        if ( classNode != null ) {
1603            component.setComponentClassName( getClassName( classNode, mappings ) );
1604        }
1605        else if ( "dynamic-component".equals( node.getName() ) ) {
1606            component.setDynamic( true );
1607        }
1608        else if ( isEmbedded ) {
1609            // an "embedded" component (composite ids and unique)
1610
// note that this does not handle nested components
1611
if ( component.getOwner().hasPojoRepresentation() ) {
1612                component.setComponentClassName( component.getOwner().getClassName() );
1613            }
1614            else {
1615                component.setDynamic(true);
1616            }
1617        }
1618        else {
1619            // todo : again, how *should* this work for non-pojo entities?
1620
if ( component.getOwner().hasPojoRepresentation() ) {
1621                Class JavaDoc reflectedClass = reflectedPropertyClass( ownerClassName, parentProperty );
1622                if ( reflectedClass != null ) {
1623                    component.setComponentClassName( reflectedClass.getName() );
1624                }
1625            }
1626            else {
1627                component.setDynamic(true);
1628            }
1629        }
1630
1631        String JavaDoc nodeName = node.attributeValue( "node" );
1632        if ( nodeName == null ) nodeName = node.attributeValue( "name" );
1633        if ( nodeName == null ) nodeName = component.getOwner().getNodeName();
1634        component.setNodeName( nodeName );
1635
1636        Iterator JavaDoc iter = node.elementIterator();
1637        while ( iter.hasNext() ) {
1638
1639            Element subnode = (Element) iter.next();
1640            String JavaDoc name = subnode.getName();
1641            String JavaDoc propertyName = getPropertyName( subnode );
1642            String JavaDoc subpath = propertyName == null ? null : StringHelper
1643                .qualify( path, propertyName );
1644
1645            CollectionType collectType = CollectionType.collectionTypeFromString( name );
1646            Value value = null;
1647            if ( collectType != null ) {
1648                Collection collection = collectType.create(
1649                        subnode,
1650                        subpath,
1651                        component.getOwner(),
1652                        mappings
1653                    );
1654                mappings.addCollection( collection );
1655                value = collection;
1656            }
1657            else if ( "many-to-one".equals( name ) || "key-many-to-one".equals( name ) ) {
1658                value = new ManyToOne( component.getTable() );
1659                bindManyToOne( subnode, (ManyToOne) value, propertyName, isNullable, mappings );
1660            }
1661            else if ( "one-to-one".equals( name ) ) {
1662                value = new OneToOne( component.getTable(), component.getOwner() );
1663                bindOneToOne( subnode, (OneToOne) value, propertyName, isNullable, mappings );
1664            }
1665            else if ( "any".equals( name ) ) {
1666                value = new Any( component.getTable() );
1667                bindAny( subnode, (Any) value, isNullable, mappings );
1668            }
1669            else if ( "property".equals( name ) || "key-property".equals( name ) ) {
1670                value = new SimpleValue( component.getTable() );
1671                bindSimpleValue( subnode, (SimpleValue) value, isNullable, propertyName, mappings );
1672            }
1673            else if ( "component".equals( name )
1674                || "dynamic-component".equals( name )
1675                || "nested-composite-element".equals( name ) ) {
1676                value = new Component( component ); // a nested composite element
1677
bindComponent(
1678                        subnode,
1679                        (Component) value,
1680                        component.getComponentClassName(),
1681                        propertyName,
1682                        subpath,
1683                        isNullable,
1684                        isEmbedded,
1685                        mappings,
1686                        inheritedMetas,
1687                        isIdentifierMapper
1688                    );
1689            }
1690            else if ( "parent".equals( name ) ) {
1691                component.setParentProperty( propertyName );
1692            }
1693
1694            if ( value != null ) {
1695                Property property = createProperty( value, propertyName, component
1696                    .getComponentClassName(), subnode, mappings, inheritedMetas );
1697                if (isIdentifierMapper) {
1698                    property.setInsertable(false);
1699                    property.setUpdateable(false);
1700                }
1701                component.addProperty( property );
1702            }
1703        }
1704
1705        if ( "true".equals( node.attributeValue( "unique" ) ) ) {
1706            iter = component.getColumnIterator();
1707            ArrayList JavaDoc cols = new ArrayList JavaDoc();
1708            while ( iter.hasNext() ) {
1709                cols.add( iter.next() );
1710            }
1711            component.getOwner().getTable().createUniqueKey( cols );
1712        }
1713
1714        iter = node.elementIterator( "tuplizer" );
1715        while ( iter.hasNext() ) {
1716            final Element tuplizerElem = ( Element ) iter.next();
1717            EntityMode mode = EntityMode.parse( tuplizerElem.attributeValue( "entity-mode" ) );
1718            component.addTuplizer( mode, node.attributeValue( "class" ) );
1719        }
1720    }
1721
1722    private static String JavaDoc getTypeFromXML(Element node) throws MappingException {
1723        // TODO: handle TypeDefs
1724
Attribute typeNode = node.attribute( "type" );
1725        if ( typeNode == null ) typeNode = node.attribute( "id-type" ); // for an any
1726
if ( typeNode == null ) return null; // we will have to use reflection
1727
return typeNode.getValue();
1728    }
1729
1730    private static void initOuterJoinFetchSetting(Element node, Fetchable model) {
1731        Attribute fetchNode = node.attribute( "fetch" );
1732        final FetchMode fetchStyle;
1733        boolean lazy = true;
1734        if ( fetchNode == null ) {
1735            Attribute jfNode = node.attribute( "outer-join" );
1736            if ( jfNode == null ) {
1737                if ( "many-to-many".equals( node.getName() ) ) {
1738                    //NOTE SPECIAL CASE:
1739
// default to join and non-lazy for the "second join"
1740
// of the many-to-many
1741
lazy = false;
1742                    fetchStyle = FetchMode.JOIN;
1743                }
1744                else if ( "one-to-one".equals( node.getName() ) ) {
1745                    //NOTE SPECIAL CASE:
1746
// one-to-one constrained=false cannot be proxied,
1747
// so default to join and non-lazy
1748
lazy = ( (OneToOne) model ).isConstrained();
1749                    fetchStyle = lazy ? FetchMode.DEFAULT : FetchMode.JOIN;
1750                }
1751                else {
1752                    fetchStyle = FetchMode.DEFAULT;
1753                }
1754            }
1755            else {
1756                // use old (HB 2.1) defaults if outer-join is specified
1757
String JavaDoc eoj = jfNode.getValue();
1758                if ( "auto".equals( eoj ) ) {
1759                    fetchStyle = FetchMode.DEFAULT;
1760                }
1761                else {
1762                    boolean join = "true".equals( eoj );
1763                    fetchStyle = join ? FetchMode.JOIN : FetchMode.SELECT;
1764                }
1765            }
1766        }
1767        else {
1768            boolean join = "join".equals( fetchNode.getValue() );
1769            //lazy = !join;
1770
fetchStyle = join ? FetchMode.JOIN : FetchMode.SELECT;
1771        }
1772        model.setFetchMode( fetchStyle );
1773        model.setLazy(lazy);
1774    }
1775
1776    private static void makeIdentifier(Element node, SimpleValue model, Mappings mappings) {
1777
1778        // GENERATOR
1779
Element subnode = node.element( "generator" );
1780        if ( subnode != null ) {
1781            model.setIdentifierGeneratorStrategy( subnode.attributeValue( "class" ) );
1782
1783            Properties JavaDoc params = new Properties JavaDoc();
1784
1785            if ( mappings.getSchemaName() != null ) {
1786                params.setProperty( PersistentIdentifierGenerator.SCHEMA, mappings.getSchemaName() );
1787            }
1788            if ( mappings.getCatalogName() != null ) {
1789                params.setProperty( PersistentIdentifierGenerator.CATALOG, mappings.getCatalogName() );
1790            }
1791
1792            Iterator JavaDoc iter = subnode.elementIterator( "param" );
1793            while ( iter.hasNext() ) {
1794                Element childNode = (Element) iter.next();
1795                params.setProperty( childNode.attributeValue( "name" ), childNode.getText() );
1796            }
1797
1798            model.setIdentifierGeneratorProperties( params );
1799        }
1800
1801        model.getTable().setIdentifierValue( model );
1802
1803        // ID UNSAVED-VALUE
1804
Attribute nullValueNode = node.attribute( "unsaved-value" );
1805        if ( nullValueNode != null ) {
1806            model.setNullValue( nullValueNode.getValue() );
1807        }
1808        else {
1809            if ( "assigned".equals( model.getIdentifierGeneratorStrategy() ) ) {
1810                model.setNullValue( "undefined" );
1811            }
1812            else {
1813                model.setNullValue( null );
1814            }
1815        }
1816    }
1817
1818    private static final void makeVersion(Element node, SimpleValue model) {
1819
1820        // VERSION UNSAVED-VALUE
1821
Attribute nullValueNode = node.attribute( "unsaved-value" );
1822        if ( nullValueNode != null ) {
1823            model.setNullValue( nullValueNode.getValue() );
1824        }
1825        else {
1826            model.setNullValue( "undefined" );
1827        }
1828
1829    }
1830
1831    protected static void createClassProperties(Element node, PersistentClass persistentClass,
1832            Mappings mappings, java.util.Map JavaDoc inheritedMetas) throws MappingException {
1833        createClassProperties(node, persistentClass, mappings, inheritedMetas, null, true, true, false);
1834    }
1835    
1836    protected static void createClassProperties(Element node, PersistentClass persistentClass,
1837            Mappings mappings, java.util.Map JavaDoc inheritedMetas, UniqueKey uniqueKey,
1838            boolean mutable, boolean nullable, boolean naturalId) throws MappingException {
1839
1840        String JavaDoc entityName = persistentClass.getEntityName();
1841        Table table = persistentClass.getTable();
1842
1843        Iterator JavaDoc iter = node.elementIterator();
1844        while ( iter.hasNext() ) {
1845            Element subnode = (Element) iter.next();
1846            String JavaDoc name = subnode.getName();
1847            String JavaDoc propertyName = subnode.attributeValue( "name" );
1848
1849            CollectionType collectType = CollectionType.collectionTypeFromString( name );
1850            Value value = null;
1851            if ( collectType != null ) {
1852                Collection collection = collectType.create(
1853                        subnode,
1854                        StringHelper.qualify( entityName, propertyName ),
1855                        persistentClass,
1856                        mappings
1857                    );
1858                mappings.addCollection( collection );
1859                value = collection;
1860            }
1861            else if ( "many-to-one".equals( name ) ) {
1862                value = new ManyToOne( table );
1863                bindManyToOne( subnode, (ManyToOne) value, propertyName, nullable, mappings );
1864            }
1865            else if ( "any".equals( name ) ) {
1866                value = new Any( table );
1867                bindAny( subnode, (Any) value, nullable, mappings );
1868            }
1869            else if ( "one-to-one".equals( name ) ) {
1870                value = new OneToOne( table, persistentClass );
1871                bindOneToOne( subnode, (OneToOne) value, propertyName, true, mappings );
1872            }
1873            else if ( "property".equals( name ) ) {
1874                value = new SimpleValue( table );
1875                bindSimpleValue( subnode, (SimpleValue) value, nullable, propertyName, mappings );
1876            }
1877            else if ( "component".equals( name )
1878                || "dynamic-component".equals( name )
1879                || "properties".equals( name ) ) {
1880                String JavaDoc subpath = StringHelper.qualify( entityName, propertyName );
1881                value = new Component( persistentClass );
1882
1883                bindComponent(
1884                        subnode,
1885                        (Component) value,
1886                        persistentClass.getClassName(),
1887                        propertyName,
1888                        subpath,
1889                        true,
1890                        "properties".equals( name ),
1891                        mappings,
1892                        inheritedMetas,
1893                        false
1894                    );
1895            }
1896            else if ( "join".equals( name ) ) {
1897                Join join = new Join();
1898                join.setPersistentClass( persistentClass );
1899                bindJoin( subnode, join, mappings, inheritedMetas );
1900                persistentClass.addJoin( join );
1901            }
1902            else if ( "subclass".equals( name ) ) {
1903                handleSubclass( persistentClass, mappings, subnode, inheritedMetas );
1904            }
1905            else if ( "joined-subclass".equals( name ) ) {
1906                handleJoinedSubclass( persistentClass, mappings, subnode, inheritedMetas );
1907            }
1908            else if ( "union-subclass".equals( name ) ) {
1909                handleUnionSubclass( persistentClass, mappings, subnode, inheritedMetas );
1910            }
1911            else if ( "filter".equals( name ) ) {
1912                parseFilter( subnode, persistentClass, mappings );
1913            }
1914            else if ( "natural-id".equals( name ) ) {
1915                UniqueKey uk = new UniqueKey();
1916                uk.setName("_UniqueKey");
1917                uk.setTable(table);
1918                //by default, natural-ids are "immutable" (constant)
1919
boolean mutableId = "true".equals( subnode.attributeValue("mutable") );
1920                createClassProperties(
1921                        subnode,
1922                        persistentClass,
1923                        mappings,
1924                        inheritedMetas,
1925                        uk,
1926                        mutableId,
1927                        false,
1928                        true
1929                    );
1930                table.addUniqueKey(uk);
1931            }
1932            else if ( "query".equals(name) ) {
1933                bindNamedQuery(subnode, persistentClass.getEntityName(), mappings);
1934            }
1935            else if ( "sql-query".equals(name) ) {
1936                bindNamedSQLQuery(subnode, persistentClass.getEntityName(), mappings);
1937            }
1938            else if ( "resultset".equals(name) ) {
1939                bindResultSetMappingDefinition( subnode, persistentClass.getEntityName(), mappings );
1940            }
1941
1942            if ( value != null ) {
1943                Property property = createProperty( value, propertyName, persistentClass
1944                    .getClassName(), subnode, mappings, inheritedMetas );
1945                if ( !mutable ) property.setUpdateable(false);
1946                if ( naturalId ) property.setNaturalIdentifier(true);
1947                persistentClass.addProperty( property );
1948                if ( uniqueKey!=null ) uniqueKey.addColumns( property.getColumnIterator() );
1949            }
1950
1951        }
1952    }
1953
1954    private static Property createProperty(final Value value, final String JavaDoc propertyName,
1955            final String JavaDoc className, final Element subnode, final Mappings mappings,
1956            java.util.Map JavaDoc inheritedMetas) throws MappingException {
1957
1958        value.setTypeUsingReflection( className, propertyName );
1959
1960        // this is done here 'cos we might only know the type here (ugly!)
1961
// TODO: improve this a lot:
1962
if ( value instanceof ToOne ) {
1963            ToOne toOne = (ToOne) value;
1964            String JavaDoc propertyRef = toOne.getReferencedPropertyName();
1965            if ( propertyRef != null ) {
1966                mappings.addUniquePropertyReference( toOne.getReferencedEntityName(), propertyRef );
1967            }
1968        }
1969        else if ( value instanceof Collection ) {
1970            Collection coll = (Collection) value;
1971            String JavaDoc propertyRef = coll.getReferencedPropertyName();
1972            // not necessarily a *unique* property reference
1973
if ( propertyRef != null ) {
1974                mappings.addPropertyReference( coll.getOwnerEntityName(), propertyRef );
1975            }
1976        }
1977
1978        value.createForeignKey();
1979        Property prop = new Property();
1980        prop.setValue( value );
1981        bindProperty( subnode, prop, mappings, inheritedMetas );
1982        return prop;
1983    }
1984
1985    private static void handleUnionSubclass(PersistentClass model, Mappings mappings,
1986            Element subnode, java.util.Map JavaDoc inheritedMetas) throws MappingException {
1987        UnionSubclass subclass = new UnionSubclass( model );
1988        bindUnionSubclass( subnode, subclass, mappings, inheritedMetas );
1989        model.addSubclass( subclass );
1990        mappings.addClass( subclass );
1991    }
1992
1993    private static void handleJoinedSubclass(PersistentClass model, Mappings mappings,
1994            Element subnode, java.util.Map JavaDoc inheritedMetas) throws MappingException {
1995        JoinedSubclass subclass = new JoinedSubclass( model );
1996        bindJoinedSubclass( subnode, subclass, mappings, inheritedMetas );
1997        model.addSubclass( subclass );
1998        mappings.addClass( subclass );
1999    }
2000
2001    private static void handleSubclass(PersistentClass model, Mappings mappings, Element subnode,
2002            java.util.Map JavaDoc inheritedMetas) throws MappingException {
2003        Subclass subclass = new SingleTableSubclass( model );
2004        bindSubclass( subnode, subclass, mappings, inheritedMetas );
2005        model.addSubclass( subclass );
2006        mappings.addClass( subclass );
2007    }
2008
2009    /**
2010     * Called for Lists, arrays, primitive arrays
2011     */

2012    public static void bindListSecondPass(Element node, List JavaDoc list, java.util.Map JavaDoc classes,
2013            Mappings mappings, java.util.Map JavaDoc inheritedMetas) throws MappingException {
2014
2015        bindCollectionSecondPass( node, list, classes, mappings, inheritedMetas );
2016
2017        Element subnode = node.element( "list-index" );
2018        if ( subnode == null ) subnode = node.element( "index" );
2019        SimpleValue iv = new SimpleValue( list.getCollectionTable() );
2020        bindSimpleValue(
2021                subnode,
2022                iv,
2023                list.isOneToMany(),
2024                IndexedCollection.DEFAULT_INDEX_COLUMN_NAME,
2025                mappings
2026            );
2027        iv.setTypeName( "integer" );
2028        list.setIndex( iv );
2029        String JavaDoc baseIndex = subnode.attributeValue( "base" );
2030        if ( baseIndex != null ) list.setBaseIndex( Integer.parseInt( baseIndex ) );
2031        list.setIndexNodeName( subnode.attributeValue("node") );
2032
2033        if ( list.isOneToMany() && !list.getKey().isNullable() && !list.isInverse() ) {
2034            String JavaDoc entityName = ( (OneToMany) list.getElement() ).getReferencedEntityName();
2035            PersistentClass referenced = mappings.getClass( entityName );
2036            IndexBackref ib = new IndexBackref();
2037            ib.setName( '_' + node.attributeValue( "name" ) + "IndexBackref" );
2038            ib.setUpdateable( false );
2039            ib.setSelectable( false );
2040            ib.setCollectionRole( list.getRole() );
2041            ib.setEntityName( list.getOwner().getEntityName() );
2042            ib.setValue( list.getIndex() );
2043            // ( (Column) ( (SimpleValue) ic.getIndex() ).getColumnIterator().next()
2044
// ).setNullable(false);
2045
referenced.addProperty( ib );
2046        }
2047    }
2048
2049    public static void bindIdentifierCollectionSecondPass(Element node,
2050            IdentifierCollection collection, java.util.Map JavaDoc persistentClasses, Mappings mappings,
2051            java.util.Map JavaDoc inheritedMetas) throws MappingException {
2052
2053        bindCollectionSecondPass( node, collection, persistentClasses, mappings, inheritedMetas );
2054
2055        Element subnode = node.element( "collection-id" );
2056        SimpleValue id = new SimpleValue( collection.getCollectionTable() );
2057        bindSimpleValue(
2058                subnode,
2059                id,
2060                false,
2061                IdentifierCollection.DEFAULT_IDENTIFIER_COLUMN_NAME,
2062                mappings
2063            );
2064        collection.setIdentifier( id );
2065        makeIdentifier( subnode, id, mappings );
2066
2067    }
2068
2069    /**
2070     * Called for Maps
2071     */

2072    public static void bindMapSecondPass(Element node, Map JavaDoc map, java.util.Map JavaDoc classes,
2073            Mappings mappings, java.util.Map JavaDoc inheritedMetas) throws MappingException {
2074
2075        bindCollectionSecondPass( node, map, classes, mappings, inheritedMetas );
2076
2077        Iterator JavaDoc iter = node.elementIterator();
2078        while ( iter.hasNext() ) {
2079            Element subnode = (Element) iter.next();
2080            String JavaDoc name = subnode.getName();
2081
2082            if ( "index".equals( name ) || "map-key".equals( name ) ) {
2083                SimpleValue value = new SimpleValue( map.getCollectionTable() );
2084                bindSimpleValue(
2085                        subnode,
2086                        value,
2087                        map.isOneToMany(),
2088                        IndexedCollection.DEFAULT_INDEX_COLUMN_NAME,
2089                        mappings
2090                    );
2091                if ( !value.isTypeSpecified() ) {
2092                    throw new MappingException( "map index element must specify a type: "
2093                        + map.getRole() );
2094                }
2095                map.setIndex( value );
2096                map.setIndexNodeName( subnode.attributeValue("node") );
2097            }
2098            else if ( "index-many-to-many".equals( name ) || "map-key-many-to-many".equals( name ) ) {
2099                ManyToOne mto = new ManyToOne( map.getCollectionTable() );
2100                bindManyToOne(
2101                        subnode,
2102                        mto,
2103                        IndexedCollection.DEFAULT_INDEX_COLUMN_NAME,
2104                        map.isOneToMany(),
2105                        mappings
2106                    );
2107                map.setIndex( mto );
2108
2109            }
2110            else if ( "composite-index".equals( name ) || "composite-map-key".equals( name ) ) {
2111                Component component = new Component( map );
2112                bindComposite(
2113                    subnode,
2114                    component,
2115                    map.getRole() + ".index",
2116                    map.isOneToMany(),
2117                    mappings,
2118                    inheritedMetas );
2119                map.setIndex( component );
2120            }
2121            else if ( "index-many-to-any".equals( name ) ) {
2122                Any any = new Any( map.getCollectionTable() );
2123                bindAny( subnode, any, map.isOneToMany(), mappings );
2124                map.setIndex( any );
2125            }
2126        }
2127
2128        // TODO: this is a bit of copy/paste from IndexedCollection.createPrimaryKey()
2129
boolean indexIsFormula = false;
2130        Iterator JavaDoc colIter = map.getIndex().getColumnIterator();
2131        while ( colIter.hasNext() ) {
2132            if ( ( (Selectable) colIter.next() ).isFormula() ) indexIsFormula = true;
2133        }
2134
2135        if ( map.isOneToMany() && !map.getKey().isNullable() && !map.isInverse() && !indexIsFormula ) {
2136            String JavaDoc entityName = ( (OneToMany) map.getElement() ).getReferencedEntityName();
2137            PersistentClass referenced = mappings.getClass( entityName );
2138            IndexBackref ib = new IndexBackref();
2139            ib.setName( '_' + node.attributeValue( "name" ) + "IndexBackref" );
2140            ib.setUpdateable( false );
2141            ib.setSelectable( false );
2142            ib.setCollectionRole( map.getRole() );
2143            ib.setEntityName( map.getOwner().getEntityName() );
2144            ib.setValue( map.getIndex() );
2145            // ( (Column) ( (SimpleValue) ic.getIndex() ).getColumnIterator().next()
2146
// ).setNullable(false);
2147
referenced.addProperty( ib );
2148        }
2149    }
2150
2151    /**
2152     * Called for all collections
2153     */

2154    public static void bindCollectionSecondPass(Element node, Collection collection,
2155            java.util.Map JavaDoc persistentClasses, Mappings mappings, java.util.Map JavaDoc inheritedMetas)
2156            throws MappingException {
2157
2158        if ( collection.isOneToMany() ) {
2159            OneToMany oneToMany = (OneToMany) collection.getElement();
2160            String JavaDoc assocClass = oneToMany.getReferencedEntityName();
2161            PersistentClass persistentClass = (PersistentClass) persistentClasses.get( assocClass );
2162            if ( persistentClass == null ) {
2163                throw new MappingException( "Association references unmapped class: " + assocClass );
2164            }
2165            oneToMany.setAssociatedClass( persistentClass );
2166            collection.setCollectionTable( persistentClass.getTable() );
2167
2168            log.info( "Mapping collection: "
2169                + collection.getRole()
2170                + " -> "
2171                + collection.getCollectionTable().getName() );
2172        }
2173
2174        // CHECK
2175
Attribute chNode = node.attribute( "check" );
2176        if ( chNode != null ) {
2177            collection.getCollectionTable().addCheckConstraint( chNode.getValue() );
2178        }
2179
2180        // contained elements:
2181
Iterator JavaDoc iter = node.elementIterator();
2182        while ( iter.hasNext() ) {
2183            Element subnode = (Element) iter.next();
2184            String JavaDoc name = subnode.getName();
2185
2186            if ( "key".equals( name ) ) {
2187                KeyValue keyVal;
2188                String JavaDoc propRef = collection.getReferencedPropertyName();
2189                if ( propRef == null ) {
2190                    keyVal = collection.getOwner().getIdentifier();
2191                }
2192                else {
2193                    keyVal = (KeyValue) collection.getOwner().getProperty( propRef ).getValue();
2194                }
2195                SimpleValue key = new DependantValue( collection.getCollectionTable(), keyVal );
2196                key.setCascadeDeleteEnabled( "cascade"
2197                    .equals( subnode.attributeValue( "on-delete" ) ) );
2198                bindSimpleValue(
2199                        subnode,
2200                        key,
2201                        collection.isOneToMany(),
2202                        Collection.DEFAULT_KEY_COLUMN_NAME,
2203                        mappings
2204                    );
2205                collection.setKey( key );
2206
2207                Attribute notNull = subnode.attribute( "not-null" );
2208                ( (DependantValue) key ).setNullable( notNull == null
2209                    || notNull.getValue().equals( "false" ) );
2210                Attribute updateable = subnode.attribute( "update" );
2211                ( (DependantValue) key ).setUpdateable( updateable == null
2212                    || updateable.getValue().equals( "true" ) );
2213
2214            }
2215            else if ( "element".equals( name ) ) {
2216                SimpleValue elt = new SimpleValue( collection.getCollectionTable() );
2217                collection.setElement( elt );
2218                bindSimpleValue(
2219                    subnode,
2220                    elt,
2221                    true,
2222                    Collection.DEFAULT_ELEMENT_COLUMN_NAME,
2223                    mappings );
2224            }
2225            else if ( "many-to-many".equals( name ) ) {
2226                ManyToOne element = new ManyToOne( collection.getCollectionTable() );
2227                collection.setElement( element );
2228                bindManyToOne(
2229                        subnode,
2230                        element,
2231                        Collection.DEFAULT_ELEMENT_COLUMN_NAME,
2232                        false,
2233                        mappings
2234                    );
2235                bindManyToManyFilters( collection, subnode, mappings );
2236            }
2237            else if ( "composite-element".equals( name ) ) {
2238                Component element = new Component( collection );
2239                collection.setElement( element );
2240                bindComposite(
2241                    subnode,
2242                    element,
2243                    collection.getRole() + ".element",
2244                    true,
2245                    mappings,
2246                    inheritedMetas );
2247            }
2248            else if ( "many-to-any".equals( name ) ) {
2249                Any element = new Any( collection.getCollectionTable() );
2250                collection.setElement( element );
2251                bindAny( subnode, element, true, mappings );
2252            }
2253            else if ( "cache".equals( name ) ) {
2254                collection.setCacheConcurrencyStrategy( subnode.attributeValue( "usage" ) );
2255                collection.setCacheRegionName( subnode.attributeValue( "region" ) );
2256            }
2257
2258            String JavaDoc nodeName = subnode.attributeValue( "node" );
2259            if ( nodeName != null ) collection.setElementNodeName( nodeName );
2260
2261        }
2262
2263        if ( collection.isOneToMany()
2264            && !collection.isInverse()
2265            && !collection.getKey().isNullable() ) {
2266            // for non-inverse one-to-many, with a not-null fk, add a backref!
2267
String JavaDoc entityName = ( (OneToMany) collection.getElement() ).getReferencedEntityName();
2268            PersistentClass referenced = mappings.getClass( entityName );
2269            Backref prop = new Backref();
2270            prop.setName( '_' + node.attributeValue( "name" ) + "Backref" );
2271            prop.setUpdateable( false );
2272            prop.setSelectable( false );
2273            prop.setCollectionRole( collection.getRole() );
2274            prop.setEntityName( collection.getOwner().getEntityName() );
2275            prop.setValue( collection.getKey() );
2276            referenced.addProperty( prop );
2277        }
2278    }
2279
2280    private static void bindManyToManyFilters(
2281            Collection collection,
2282            Element manyToManyNode,
2283            Mappings model) throws MappingException {
2284        // Bind the where
2285
Attribute where = manyToManyNode.attribute( "where" );
2286        String JavaDoc whereCondition = where == null ? null : where.getValue();
2287        collection.setManyToManyWhere( whereCondition );
2288
2289        // Bind the filters
2290
Iterator JavaDoc filters = manyToManyNode.elementIterator( "filter" );
2291        if ( ( filters.hasNext() || whereCondition != null ) &&
2292                collection.getFetchMode() == FetchMode.JOIN &&
2293                collection.getElement().getFetchMode() != FetchMode.JOIN ) {
2294            throw new MappingException(
2295                    "many-to-many defining filter or where without join fetching " +
2296                    "not valid within collection using join fetching [" + collection.getRole() + "]"
2297            );
2298        }
2299        while ( filters.hasNext() ) {
2300            final Element filterElement = ( Element ) filters.next();
2301            final String JavaDoc name = filterElement.attributeValue( "name" );
2302            String JavaDoc condition = filterElement.getTextTrim();
2303            if ( StringHelper.isEmpty(condition) ) condition = filterElement.attributeValue( "condition" );
2304            if ( StringHelper.isEmpty(condition) ) {
2305                condition = model.getFilterDefinition(name).getDefaultFilterCondition();
2306            }
2307            if ( condition==null) {
2308                throw new MappingException("no filter condition found for filter: " + name);
2309            }
2310            log.debug( "Applying many-to-many filter [" + name + "] as [" + condition + "] to role [" + collection.getRole() + "]" );
2311            collection.addManyToManyFilter( name, condition );
2312        }
2313    }
2314
2315    private static final LockMode getLockMode(String JavaDoc lockMode) {
2316        if ( lockMode == null || "read".equals( lockMode ) ) {
2317            return LockMode.READ;
2318        }
2319        else if ( "none".equals( lockMode ) ) {
2320            return LockMode.NONE;
2321        }
2322        else if ( "upgrade".equals( lockMode ) ) {
2323            return LockMode.UPGRADE;
2324        }
2325        else if ( "upgrade-nowait".equals( lockMode ) ) {
2326            return LockMode.UPGRADE_NOWAIT;
2327        }
2328        else if ( "upgrade-nowait".equals( lockMode ) ) {
2329            return LockMode.UPGRADE_NOWAIT;
2330        }
2331        else if ( "write".equals( lockMode ) ) {
2332            return LockMode.WRITE;
2333        }
2334        else {
2335            throw new MappingException( "unknown lockmode" );
2336        }
2337    }
2338
2339    private static final FlushMode getFlushMode(String JavaDoc flushMode) {
2340        if ( flushMode == null ) {
2341            return null;
2342        }
2343        else if ( "auto".equals( flushMode ) ) {
2344            return FlushMode.AUTO;
2345        }
2346        else if ( "commit".equals( flushMode ) ) {
2347            return FlushMode.COMMIT;
2348        }
2349        else if ( "never".equals( flushMode ) ) {
2350            return FlushMode.NEVER;
2351        }
2352        else if ( "always".equals( flushMode ) ) {
2353            return FlushMode.ALWAYS;
2354        }
2355        else {
2356            throw new MappingException( "unknown flushmode" );
2357        }
2358    }
2359
2360    private static void bindNamedQuery(Element queryElem, String JavaDoc path, Mappings mappings) {
2361        String JavaDoc queryName = queryElem.attributeValue( "name" );
2362        if (path!=null) queryName = path + '.' + queryName;
2363        String JavaDoc query = queryElem.getText();
2364        log.debug( "Named query: " + queryName + " -> " + query );
2365
2366        boolean cacheable = "true".equals( queryElem.attributeValue( "cacheable" ) );
2367        String JavaDoc region = queryElem.attributeValue( "cache-region" );
2368        Attribute tAtt = queryElem.attribute( "timeout" );
2369        Integer JavaDoc timeout = tAtt == null ? null : new Integer JavaDoc( tAtt.getValue() );
2370        Attribute fsAtt = queryElem.attribute( "fetch-size" );
2371        Integer JavaDoc fetchSize = fsAtt == null ? null : new Integer JavaDoc( fsAtt.getValue() );
2372
2373        NamedQueryDefinition namedQuery = new NamedQueryDefinition(
2374                query,
2375                cacheable,
2376                region,
2377                timeout,
2378                fetchSize,
2379                getFlushMode( queryElem.attributeValue( "flush-mode" ) ) ,
2380                getParameterTypes(queryElem)
2381            );
2382
2383        mappings.addQuery( queryName, namedQuery );
2384    }
2385    
2386    private static java.util.Map JavaDoc getParameterTypes(Element queryElem) {
2387        java.util.Map JavaDoc result = new SequencedHashMap();
2388        Iterator JavaDoc iter = queryElem.elementIterator("query-param");
2389        while ( iter.hasNext() ) {
2390            Element element = (Element) iter.next();
2391            result.put(
2392                    element.attributeValue("name"),
2393                    TypeFactory.heuristicType( element.attributeValue("type") )
2394                );
2395        }
2396        return result;
2397    }
2398
2399    /**
2400     * @param alias
2401     * @param returnElement
2402     * @return
2403     */

2404    private static java.util.Map JavaDoc bindPropertyResults(String JavaDoc alias, Element returnElement) {
2405        
2406        HashMap JavaDoc propertyresults = new HashMap JavaDoc(); // maybe a concrete SQLpropertyresult type, but Map is exactly what is required at the moment
2407

2408        Element discriminatorResult = returnElement.element("return-discriminator");
2409        if(discriminatorResult!=null) {
2410            ArrayList JavaDoc resultColumns = getResultColumns(discriminatorResult);
2411            propertyresults.put("class", ArrayHelper.toStringArray(resultColumns) );
2412        }
2413        
2414        Iterator JavaDoc iterator = returnElement.elementIterator("return-property");
2415        while ( iterator.hasNext() ) {
2416            Element propertyresult = (Element) iterator.next();
2417            String JavaDoc name = propertyresult.attributeValue("name");
2418            if ( "class".equals(name) ) {
2419                throw new MappingException("class is not a valid property name to use in a <return-property>. Use <return-discriminator> instead.");
2420            }
2421            //TODO: validate existing of property with the chosen name. (secondpass )
2422
ArrayList JavaDoc allResultColumns = getResultColumns(propertyresult);
2423            
2424            if ( allResultColumns.isEmpty() ) {
2425                throw new MappingException("return-property for alias " + alias + " must specify at least one column or return-column name");
2426            }
2427            String JavaDoc[] existing = (String JavaDoc[]) propertyresults.put( name, ArrayHelper.toStringArray(allResultColumns) );
2428            if (existing!=null) {
2429                throw new MappingException("duplicate return-property for property " + name + " on alias " + alias);
2430            }
2431        }
2432        return propertyresults.isEmpty() ? CollectionHelper.EMPTY_MAP : propertyresults;
2433    }
2434
2435    /**
2436     * @param propertyresult
2437     * @return
2438     */

2439    private static ArrayList JavaDoc getResultColumns(Element propertyresult) {
2440        String JavaDoc column = propertyresult.attributeValue("column");
2441        ArrayList JavaDoc allResultColumns = new ArrayList JavaDoc();
2442        if(column!=null) allResultColumns.add(column);
2443        Iterator JavaDoc resultColumns = propertyresult.elementIterator("return-column");
2444        while ( resultColumns.hasNext() ) {
2445            Element element = (Element) resultColumns.next();
2446            allResultColumns.add( element.attributeValue("name") );
2447        }
2448        return allResultColumns;
2449    }
2450
2451    private static ResultSetMappingDefinition buildResultSetMappingDefinition(Element resultSetElem, String JavaDoc path, Mappings mappings) {
2452        String JavaDoc resultSetName = resultSetElem.attribute( "name" ).getValue();
2453        if (path != null) resultSetName = path + '.' + resultSetName;
2454        ResultSetMappingDefinition definition = new ResultSetMappingDefinition( resultSetName );
2455        Iterator JavaDoc returns = resultSetElem.elementIterator( "return-scalar" );
2456        while ( returns.hasNext() ) {
2457            Element returnElem = (Element) returns.next();
2458            String JavaDoc column = returnElem.attributeValue( "column" );
2459            Type type = TypeFactory.heuristicType( getTypeFromXML( returnElem ) );
2460            if ( type == null ) {
2461                throw new MappingException( "could not determine type " + type );
2462            }
2463            definition.addScalarQueryReturn( new SQLQueryScalarReturn( column, type ) );
2464        }
2465        returns = resultSetElem.elementIterator();
2466        int cnt = 0;
2467        while ( returns.hasNext() ) {
2468            cnt++;
2469            Element returnElem = (Element) returns.next();
2470            String JavaDoc name = returnElem.getName();
2471            if ( "return".equals( name ) ) {
2472                definition.addEntityQueryReturn( bindReturn( returnElem, mappings, cnt ) );
2473            }
2474            else if ( "return-join".equals( name ) ) {
2475                definition.addEntityQueryReturn( bindReturnJoin( returnElem, mappings ) );
2476            }
2477            else if ( "load-collection".equals( name ) ) {
2478                definition.addEntityQueryReturn( bindLoadCollection( returnElem, mappings ) );
2479            }
2480        }
2481        return definition;
2482    }
2483
2484    private static SQLQueryRootReturn bindReturn(Element returnElem, Mappings mappings, int elementCount) {
2485        String JavaDoc alias = returnElem.attributeValue( "alias" );
2486        if(StringHelper.isEmpty(alias)) {
2487            alias = "alias_" + elementCount; // hack/workaround as sqlquery impl depend on having a key.
2488
}
2489
2490        String JavaDoc entityName = getEntityName(returnElem, mappings);
2491        if(entityName==null) {
2492            throw new MappingException( "<return alias='" + alias + "'> must specify either a class or entity-name");
2493        }
2494        LockMode lockMode = getLockMode( returnElem.attributeValue( "lock-mode" ) );
2495
2496        java.util.Map JavaDoc propertyResults = bindPropertyResults(alias, returnElem);
2497
2498        return new SQLQueryRootReturn(
2499            alias,
2500            entityName,
2501            propertyResults,
2502            lockMode );
2503    }
2504
2505    private static SQLQueryJoinReturn bindReturnJoin(Element returnElem, Mappings mappings) {
2506        String JavaDoc alias = returnElem.attributeValue( "alias" );
2507        String JavaDoc roleAttribute = returnElem.attributeValue( "property" );
2508        LockMode lockMode = getLockMode( returnElem.attributeValue( "lock-mode" ) );
2509        int dot = roleAttribute.lastIndexOf( '.' );
2510        if ( dot == -1 ) {
2511            throw new MappingException( "Role attribute for sql query return [alias="
2512                + alias
2513                + "] not formatted correctly {owningAlias.propertyName}" );
2514        }
2515        String JavaDoc roleOwnerAlias = roleAttribute.substring( 0, dot );
2516        String JavaDoc roleProperty = roleAttribute.substring( dot + 1 );
2517
2518        java.util.Map JavaDoc propertyResults = bindPropertyResults(alias, returnElem);
2519
2520        return new SQLQueryJoinReturn(
2521                    alias,
2522                    roleOwnerAlias,
2523                    roleProperty,
2524                    propertyResults, // TODO: bindpropertyresults(alias, returnElem)
2525
lockMode );
2526    }
2527
2528    private static SQLQueryCollectionReturn bindLoadCollection(Element returnElem, Mappings mappings) {
2529        String JavaDoc alias = returnElem.attributeValue( "alias" );
2530        String JavaDoc collectionAttribute = returnElem.attributeValue( "role" );
2531        LockMode lockMode = getLockMode( returnElem.attributeValue( "lock-mode" ) );
2532        int dot = collectionAttribute.lastIndexOf( '.' );
2533        if ( dot == -1 ) {
2534            throw new MappingException( "Collection attribute for sql query return [alias="
2535                + alias
2536                + "] not formatted correctly {OwnerClassName.propertyName}" );
2537        }
2538        String JavaDoc ownerClassName = getClassName( collectionAttribute.substring( 0, dot ), mappings );
2539        String JavaDoc ownerPropertyName = collectionAttribute.substring( dot + 1 );
2540
2541        java.util.Map JavaDoc propertyResults = bindPropertyResults(alias, returnElem);
2542
2543        return new SQLQueryCollectionReturn(
2544            alias,
2545            ownerClassName,
2546            ownerPropertyName,
2547            propertyResults,
2548            lockMode );
2549    }
2550
2551    private static void bindResultSetMappingDefinition(Element resultSetElem, String JavaDoc path, Mappings mappings) {
2552        ResultSetMappingDefinition definition = buildResultSetMappingDefinition( resultSetElem, path, mappings);
2553        mappings.addResultSetMapping( definition );
2554    }
2555
2556    private static void bindNamedSQLQuery(Element queryElem, String JavaDoc path, Mappings mappings) {
2557        String JavaDoc queryName = queryElem.attribute( "name" ).getValue();
2558        if (path!=null) queryName = path + '.' + queryName;
2559
2560        boolean cacheable = "true".equals( queryElem.attributeValue( "cacheable" ) );
2561        String JavaDoc region = queryElem.attributeValue( "cache-region" );
2562        Attribute tAtt = queryElem.attribute( "timeout" );
2563        Integer JavaDoc timeout = tAtt == null ? null : new Integer JavaDoc( tAtt.getValue() );
2564        Attribute fsAtt = queryElem.attribute( "fetch-size" );
2565        Integer JavaDoc fetchSize = fsAtt == null ? null : new Integer JavaDoc( fsAtt.getValue() );
2566
2567        java.util.List JavaDoc synchronizedTables = new ArrayList JavaDoc();
2568        Iterator JavaDoc tables = queryElem.elementIterator( "synchronize" );
2569        while ( tables.hasNext() ) {
2570            synchronizedTables.add( ( (Element) tables.next() ).attributeValue( "table" ) );
2571        }
2572        boolean callable = "true".equals( queryElem.attributeValue( "callable" ) );
2573
2574        NamedSQLQueryDefinition namedQuery;
2575        Attribute ref = queryElem.attribute( "resultset-ref" );
2576        String JavaDoc resultSetRef = ref == null ? null : ref.getValue();
2577        if ( StringHelper.isNotEmpty( resultSetRef ) ) {
2578            namedQuery = new NamedSQLQueryDefinition(
2579                    queryElem.getText(),
2580                    resultSetRef,
2581                    synchronizedTables,
2582                    cacheable,
2583                    region,
2584                    timeout,
2585                    fetchSize,
2586                    getFlushMode( queryElem.attributeValue( "flush-mode" ) ),
2587                    getParameterTypes(queryElem),
2588                    callable
2589                );
2590            //TODO check there is no actual definition elemnents when a ref is defined
2591
}
2592        else {
2593            ResultSetMappingDefinition definition = buildResultSetMappingDefinition(queryElem, path, mappings);
2594            namedQuery = new NamedSQLQueryDefinition(
2595                    queryElem.getText(),
2596                    definition.getEntityQueryReturns(),
2597                    definition.getScalarQueryReturns(),
2598                    synchronizedTables,
2599                    cacheable,
2600                    region,
2601                    timeout,
2602                    fetchSize,
2603                    getFlushMode( queryElem.attributeValue( "flush-mode" ) ),
2604                    getParameterTypes(queryElem),
2605                    callable
2606                );
2607        }
2608
2609        log.debug( "Named SQL query: " + queryName + " -> " + namedQuery.getQueryString() );
2610        mappings.addSQLQuery( queryName, namedQuery );
2611    }
2612
2613    private static String JavaDoc getPropertyName(Element node) {
2614        return node.attributeValue( "name" );
2615    }
2616
2617    private static PersistentClass getSuperclass(Mappings mappings, Element subnode)
2618            throws MappingException {
2619        String JavaDoc superClass = getClassName( subnode.attributeValue( "extends" ), mappings );
2620        PersistentClass superModel = mappings.getClass( superClass );
2621
2622        if ( superModel == null ) {
2623            throw new MappingException( "Cannot extend unmapped class " + superClass );
2624        }
2625        return superModel;
2626    }
2627
2628    abstract static class SecondPass implements Serializable JavaDoc {
2629        Element node;
2630        Mappings mappings;
2631        Collection collection;
2632
2633        SecondPass(Element node, Mappings mappings, Collection collection) {
2634            this.node = node;
2635            this.collection = collection;
2636            this.mappings = mappings;
2637        }
2638
2639        void doSecondPass(java.util.Map JavaDoc persistentClasses, java.util.Map JavaDoc inheritedMetas)
2640                throws MappingException {
2641            if ( log.isDebugEnabled() )
2642                log.debug( "Second pass for collection: " + collection.getRole() );
2643
2644            secondPass( persistentClasses, inheritedMetas );
2645            collection.createAllKeys();
2646
2647            if ( log.isDebugEnabled() ) {
2648                String JavaDoc msg = "Mapped collection key: " + columns( collection.getKey() );
2649                if ( collection.isIndexed() )
2650                    msg += ", index: " + columns( ( (IndexedCollection) collection ).getIndex() );
2651                if ( collection.isOneToMany() ) {
2652                    msg += ", one-to-many: "
2653                        + ( (OneToMany) collection.getElement() ).getReferencedEntityName();
2654                }
2655                else {
2656                    msg += ", element: " + columns( collection.getElement() );
2657                }
2658                log.debug( msg );
2659            }
2660        }
2661
2662        abstract void secondPass(java.util.Map JavaDoc persistentClasses, java.util.Map JavaDoc inheritedMetas)
2663                throws MappingException;
2664    }
2665
2666    static class CollectionSecondPass extends SecondPass {
2667        CollectionSecondPass(Element node, Mappings mappings, Collection collection) {
2668            super( node, mappings, collection );
2669        }
2670
2671        void secondPass(java.util.Map JavaDoc persistentClasses, java.util.Map JavaDoc inheritedMetas)
2672                throws MappingException {
2673            HbmBinder.bindCollectionSecondPass(
2674                node,
2675                collection,
2676                persistentClasses,
2677                mappings,
2678                inheritedMetas );
2679        }
2680
2681    }
2682
2683    static class IdentifierCollectionSecondPass extends SecondPass {
2684        IdentifierCollectionSecondPass(Element node, Mappings mappings,
2685                IdentifierCollection collection) {
2686            super( node, mappings, collection );
2687        }
2688
2689        void secondPass(java.util.Map JavaDoc persistentClasses, java.util.Map JavaDoc inheritedMetas)
2690                throws MappingException {
2691            HbmBinder.bindIdentifierCollectionSecondPass(
2692                node,
2693                (IdentifierCollection) collection,
2694                persistentClasses,
2695                mappings,
2696                inheritedMetas );
2697        }
2698
2699    }
2700
2701    static class MapSecondPass extends SecondPass {
2702        MapSecondPass(Element node, Mappings mappings, Map JavaDoc collection) {
2703            super( node, mappings, collection );
2704        }
2705
2706        void secondPass(java.util.Map JavaDoc persistentClasses, java.util.Map JavaDoc inheritedMetas)
2707                throws MappingException {
2708            HbmBinder.bindMapSecondPass(
2709                node,
2710                (Map JavaDoc) collection,
2711                persistentClasses,
2712                mappings,
2713                inheritedMetas );
2714        }
2715
2716    }
2717
2718    // TODO: this secondpass "misuses" SecondPass that assumes it is for a collection.
2719
static class ManyToOneSecondPass extends SecondPass {
2720        private final ManyToOne manyToOne;
2721
2722        ManyToOneSecondPass(Mappings mappings, ManyToOne manyToOne) {
2723            super( null, mappings, null);
2724            this.manyToOne = manyToOne;
2725        }
2726
2727        void doSecondPass(java.util.Map JavaDoc persistentClasses, java.util.Map JavaDoc inheritedMetas) throws MappingException {
2728            manyToOne.createPropertyRefConstraints(persistentClasses);
2729        }
2730        
2731        void secondPass(java.util.Map JavaDoc persistentClasses, java.util.Map JavaDoc inheritedMetas) throws MappingException {
2732            // noop
2733
}
2734    }
2735    
2736    static class ListSecondPass extends SecondPass {
2737        ListSecondPass(Element node, Mappings mappings, List JavaDoc collection) {
2738            super( node, mappings, collection );
2739        }
2740
2741        void secondPass(java.util.Map JavaDoc persistentClasses, java.util.Map JavaDoc inheritedMetas)
2742                throws MappingException {
2743            HbmBinder.bindListSecondPass(
2744                node,
2745                (List JavaDoc) collection,
2746                persistentClasses,
2747                mappings,
2748                inheritedMetas );
2749        }
2750
2751    }
2752
2753    // This inner class implements a case statement....perhaps im being a bit over-clever here
2754
abstract static class CollectionType {
2755        private String JavaDoc xmlTag;
2756
2757        public abstract Collection create(Element node, String JavaDoc path, PersistentClass owner,
2758                Mappings mappings) throws MappingException;
2759
2760        CollectionType(String JavaDoc xmlTag) {
2761            this.xmlTag = xmlTag;
2762        }
2763
2764        public String JavaDoc toString() {
2765            return xmlTag;
2766        }
2767
2768        private static final CollectionType MAP = new CollectionType( "map" ) {
2769            public Collection create(Element node, String JavaDoc path, PersistentClass owner,
2770                    Mappings mappings) throws MappingException {
2771                Map JavaDoc map = new Map JavaDoc( owner );
2772                bindCollection( node, map, owner.getEntityName(), path, mappings );
2773                return map;
2774            }
2775        };
2776        private static final CollectionType SET = new CollectionType( "set" ) {
2777            public Collection create(Element node, String JavaDoc path, PersistentClass owner,
2778                    Mappings mappings) throws MappingException {
2779                Set JavaDoc set = new Set JavaDoc( owner );
2780                bindCollection( node, set, owner.getEntityName(), path, mappings );
2781                return set;
2782            }
2783        };
2784        private static final CollectionType LIST = new CollectionType( "list" ) {
2785            public Collection create(Element node, String JavaDoc path, PersistentClass owner,
2786                    Mappings mappings) throws MappingException {
2787                List JavaDoc list = new List JavaDoc( owner );
2788                bindCollection( node, list, owner.getEntityName(), path, mappings );
2789                return list;
2790            }
2791        };
2792        private static final CollectionType BAG = new CollectionType( "bag" ) {
2793            public Collection create(Element node, String JavaDoc path, PersistentClass owner,
2794                    Mappings mappings) throws MappingException {
2795                Bag bag = new Bag( owner );
2796                bindCollection( node, bag, owner.getEntityName(), path, mappings );
2797                return bag;
2798            }
2799        };
2800        private static final CollectionType IDBAG = new CollectionType( "idbag" ) {
2801            public Collection create(Element node, String JavaDoc path, PersistentClass owner,
2802                    Mappings mappings) throws MappingException {
2803                IdentifierBag bag = new IdentifierBag( owner );
2804                bindCollection( node, bag, owner.getEntityName(), path, mappings );
2805                return bag;
2806            }
2807        };
2808        private static final CollectionType ARRAY = new CollectionType( "array" ) {
2809            public Collection create(Element node, String JavaDoc path, PersistentClass owner,
2810                    Mappings mappings) throws MappingException {
2811                Array array = new Array( owner );
2812                bindArray( node, array, owner.getEntityName(), path, mappings );
2813                return array;
2814            }
2815        };
2816        private static final CollectionType PRIMITIVE_ARRAY = new CollectionType( "primitive-array" ) {
2817            public Collection create(Element node, String JavaDoc path, PersistentClass owner,
2818                    Mappings mappings) throws MappingException {
2819                PrimitiveArray array = new PrimitiveArray( owner );
2820                bindArray( node, array, owner.getEntityName(), path, mappings );
2821                return array;
2822            }
2823        };
2824        private static final HashMap JavaDoc INSTANCES = new HashMap JavaDoc();
2825
2826        static {
2827            INSTANCES.put( MAP.toString(), MAP );
2828            INSTANCES.put( BAG.toString(), BAG );
2829            INSTANCES.put( IDBAG.toString(), IDBAG );
2830            INSTANCES.put( SET.toString(), SET );
2831            INSTANCES.put( LIST.toString(), LIST );
2832            INSTANCES.put( ARRAY.toString(), ARRAY );
2833            INSTANCES.put( PRIMITIVE_ARRAY.toString(), PRIMITIVE_ARRAY );
2834        }
2835
2836        public static CollectionType collectionTypeFromString(String JavaDoc xmlTagName) {
2837            return (CollectionType) INSTANCES.get( xmlTagName );
2838        }
2839    }
2840
2841    private static int getOptimisticLockMode(Attribute olAtt) throws MappingException {
2842
2843        if ( olAtt == null ) return Versioning.OPTIMISTIC_LOCK_VERSION;
2844        String JavaDoc olMode = olAtt.getValue();
2845        if ( olMode == null || "version".equals( olMode ) ) {
2846            return Versioning.OPTIMISTIC_LOCK_VERSION;
2847        }
2848        else if ( "dirty".equals( olMode ) ) {
2849            return Versioning.OPTIMISTIC_LOCK_DIRTY;
2850        }
2851        else if ( "all".equals( olMode ) ) {
2852            return Versioning.OPTIMISTIC_LOCK_ALL;
2853        }
2854        else if ( "none".equals( olMode ) ) {
2855            return Versioning.OPTIMISTIC_LOCK_NONE;
2856        }
2857        else {
2858            throw new MappingException( "Unsupported optimistic-lock style: " + olMode );
2859        }
2860    }
2861
2862    private static final java.util.Map JavaDoc getMetas(Element node, java.util.Map JavaDoc inheritedMeta) {
2863        return getMetas( node, inheritedMeta, false );
2864    }
2865
2866    private static final java.util.Map JavaDoc getMetas(Element node, java.util.Map JavaDoc inheritedMeta,
2867            boolean onlyInheritable) {
2868        java.util.Map JavaDoc map = new HashMap JavaDoc();
2869        map.putAll( inheritedMeta );
2870
2871        Iterator JavaDoc iter = node.elementIterator( "meta" );
2872        while ( iter.hasNext() ) {
2873            Element metaNode = (Element) iter.next();
2874            boolean inheritable = Boolean
2875                .valueOf( metaNode.attributeValue( "inherit" ) )
2876                .booleanValue();
2877            if ( onlyInheritable & !inheritable ) {
2878                continue;
2879            }
2880            String JavaDoc name = metaNode.attributeValue( "attribute" );
2881
2882            MetaAttribute meta = (MetaAttribute) map.get( name );
2883            if ( meta == null ) {
2884                meta = new MetaAttribute( name );
2885                map.put( name, meta );
2886            }
2887            meta.addValue( metaNode.getText() );
2888        }
2889        return map;
2890    }
2891
2892    private static String JavaDoc getEntityName(Element elem, Mappings model) {
2893        String JavaDoc entityName = elem.attributeValue( "entity-name" );
2894        return entityName == null ? getClassName( elem.attribute( "class" ), model ) : entityName;
2895    }
2896
2897    private static String JavaDoc getClassName(Attribute att, Mappings model) {
2898        if ( att == null ) return null;
2899        return getClassName( att.getValue(), model );
2900    }
2901
2902    private static String JavaDoc getClassName(String JavaDoc unqualifiedName, Mappings model) {
2903        if ( unqualifiedName == null ) return null;
2904        if ( unqualifiedName.indexOf( '.' ) < 0 && model.getDefaultPackage() != null ) {
2905            return model.getDefaultPackage() + '.' + unqualifiedName;
2906        }
2907        return unqualifiedName;
2908    }
2909
2910    private static void parseFilterDef(Element element, Mappings mappings) {
2911        String JavaDoc name = element.attributeValue( "name" );
2912        log.debug( "Parsing filter-def [" + name + "]" );
2913        FilterDefinition def = new FilterDefinition( name );
2914        Iterator JavaDoc params = element.elementIterator( "filter-param" );
2915        while ( params.hasNext() ) {
2916            final Element param = (Element) params.next();
2917            final String JavaDoc paramName = param.attributeValue( "name" );
2918            final String JavaDoc paramType = param.attributeValue( "type" );
2919            log.debug( "adding filter parameter : " + paramName + " -> " + paramType );
2920            final Type heuristicType = TypeFactory.heuristicType( paramType );
2921            log.debug( "parameter heuristic type : " + heuristicType );
2922            def.addParameterType( paramName, heuristicType );
2923        }
2924        String JavaDoc condition = element.getTextTrim();
2925        if ( StringHelper.isEmpty(condition) ) condition = element.attributeValue( "condition" );
2926        def.setDefaultFilterCondition(condition);
2927        log.debug( "Parsed filter-def [" + name + "]" );
2928        mappings.addFilterDefinition( def );
2929    }
2930
2931    private static void parseFilter(Element filterElement, Filterable filterable, Mappings model) {
2932        final String JavaDoc name = filterElement.attributeValue( "name" );
2933        String JavaDoc condition = filterElement.getTextTrim();
2934        if ( StringHelper.isEmpty(condition) ) condition = filterElement.attributeValue( "condition" );
2935        //TODO: bad implementation, cos it depends upon ordering of mapping doc
2936
if ( StringHelper.isEmpty(condition) ) {
2937            condition = model.getFilterDefinition(name).getDefaultFilterCondition();
2938        }
2939        if ( condition==null) {
2940            throw new MappingException("no filter condition found for filter: " + name);
2941        }
2942        log.debug( "Applying filter [" + name + "] as [" + condition + "]" );
2943        filterable.addFilter( name, condition );
2944    }
2945
2946    private static String JavaDoc getSubselect(Element element) {
2947        String JavaDoc subselect = element.attributeValue( "subselect" );
2948        if ( subselect != null ) {
2949            return subselect;
2950        }
2951        else {
2952            Element subselectElement = element.element( "subselect" );
2953            return subselectElement == null ? null : subselectElement.getText();
2954        }
2955    }
2956
2957    /**
2958     * @param doc
2959     * @return
2960     */

2961    public static java.util.List JavaDoc getExtendsNeeded(Document doc, Mappings mappings) {
2962
2963        java.util.List JavaDoc extendz = new ArrayList JavaDoc();
2964
2965        Iterator JavaDoc[] subclasses = new Iterator JavaDoc[3];
2966
2967        final Element hmNode = doc.getRootElement();
2968        Attribute packNode = hmNode.attribute( "package" );
2969        if ( packNode != null ) mappings.setDefaultPackage( packNode.getValue() );
2970
2971        subclasses[0] = hmNode.elementIterator( "subclass" );
2972        subclasses[1] = hmNode.elementIterator( "joined-subclass" );
2973        subclasses[2] = hmNode.elementIterator( "union-subclass" );
2974
2975        Iterator JavaDoc iterator = new JoinedIterator( subclasses );
2976        while ( iterator.hasNext() ) {
2977            Element element = (Element) iterator.next();
2978            String JavaDoc superClass = getClassName( element.attributeValue( "extends" ), mappings );
2979            if ( mappings.getClass( superClass ) == null ) {
2980                extendz.add( superClass );
2981            }
2982        }
2983
2984        if ( !extendz.isEmpty() ) {
2985            java.util.List JavaDoc names = new ArrayList JavaDoc();
2986            findClassNames( mappings, hmNode, names );
2987
2988            java.util.Set JavaDoc set = new HashSet JavaDoc( extendz );
2989            set.removeAll( names );
2990
2991            extendz.clear();
2992            extendz.addAll( set );
2993        }
2994
2995        return extendz;
2996    }
2997
2998    /**
2999     * @param mappings
3000     * @param startNode
3001     * @param names
3002     */

3003    private static void findClassNames(Mappings mappings, final Element startNode,
3004            final java.util.List JavaDoc names) {
3005        // if we have some extends we need to check if those classes possibly could be inside the
3006
// same hbm.xml file...
3007
Iterator JavaDoc[] classes = new Iterator JavaDoc[4];
3008        classes[0] = startNode.elementIterator( "class" );
3009        classes[1] = startNode.elementIterator( "subclass" );
3010        classes[2] = startNode.elementIterator( "joined-subclass" );
3011        classes[3] = startNode.elementIterator( "union-subclass" );
3012
3013        Iterator JavaDoc classIterator = new JoinedIterator( classes );
3014        while ( classIterator.hasNext() ) {
3015            Element element = (Element) classIterator.next();
3016            String JavaDoc entityName = element.attributeValue( "entity-name" );
3017            if ( entityName == null ) entityName = getClassName( element.attribute("name"), mappings );
3018            names.add( entityName );
3019            findClassNames( mappings, element, names );
3020        }
3021    }
3022
3023}
3024
Popular Tags