0001: // $Id: HbmBinder.java 11495 2007-05-09 03:52:56Z steve.ebersole@jboss.com $
0002: package org.hibernate.cfg;
0003:
0004: import java.util.ArrayList;
0005: import java.util.Collections;
0006: import java.util.HashMap;
0007: import java.util.HashSet;
0008: import java.util.Iterator;
0009: import java.util.Properties;
0010: import java.util.StringTokenizer;
0011:
0012: import org.apache.commons.collections.SequencedHashMap;
0013: import org.apache.commons.logging.Log;
0014: import org.apache.commons.logging.LogFactory;
0015: import org.dom4j.Attribute;
0016: import org.dom4j.Document;
0017: import org.dom4j.Element;
0018: import org.hibernate.CacheMode;
0019: import org.hibernate.EntityMode;
0020: import org.hibernate.FetchMode;
0021: import org.hibernate.FlushMode;
0022: import org.hibernate.MappingException;
0023: import org.hibernate.engine.FilterDefinition;
0024: import org.hibernate.engine.NamedQueryDefinition;
0025: import org.hibernate.engine.Versioning;
0026: import org.hibernate.engine.ExecuteUpdateResultCheckStyle;
0027: import org.hibernate.id.PersistentIdentifierGenerator;
0028: import org.hibernate.mapping.Any;
0029: import org.hibernate.mapping.Array;
0030: import org.hibernate.mapping.AuxiliaryDatabaseObject;
0031: import org.hibernate.mapping.Backref;
0032: import org.hibernate.mapping.Bag;
0033: import org.hibernate.mapping.Collection;
0034: import org.hibernate.mapping.Column;
0035: import org.hibernate.mapping.Component;
0036: import org.hibernate.mapping.DependantValue;
0037: import org.hibernate.mapping.Fetchable;
0038: import org.hibernate.mapping.Filterable;
0039: import org.hibernate.mapping.Formula;
0040: import org.hibernate.mapping.IdentifierBag;
0041: import org.hibernate.mapping.IdentifierCollection;
0042: import org.hibernate.mapping.IndexBackref;
0043: import org.hibernate.mapping.IndexedCollection;
0044: import org.hibernate.mapping.Join;
0045: import org.hibernate.mapping.JoinedSubclass;
0046: import org.hibernate.mapping.KeyValue;
0047: import org.hibernate.mapping.List;
0048: import org.hibernate.mapping.ManyToOne;
0049: import org.hibernate.mapping.Map;
0050: import org.hibernate.mapping.MetaAttribute;
0051: import org.hibernate.mapping.OneToMany;
0052: import org.hibernate.mapping.OneToOne;
0053: import org.hibernate.mapping.PersistentClass;
0054: import org.hibernate.mapping.PrimitiveArray;
0055: import org.hibernate.mapping.Property;
0056: import org.hibernate.mapping.PropertyGeneration;
0057: import org.hibernate.mapping.RootClass;
0058: import org.hibernate.mapping.Selectable;
0059: import org.hibernate.mapping.Set;
0060: import org.hibernate.mapping.SimpleAuxiliaryDatabaseObject;
0061: import org.hibernate.mapping.SimpleValue;
0062: import org.hibernate.mapping.SingleTableSubclass;
0063: import org.hibernate.mapping.Subclass;
0064: import org.hibernate.mapping.Table;
0065: import org.hibernate.mapping.ToOne;
0066: import org.hibernate.mapping.TypeDef;
0067: import org.hibernate.mapping.UnionSubclass;
0068: import org.hibernate.mapping.UniqueKey;
0069: import org.hibernate.mapping.Value;
0070: import org.hibernate.persister.entity.JoinedSubclassEntityPersister;
0071: import org.hibernate.persister.entity.SingleTableEntityPersister;
0072: import org.hibernate.persister.entity.UnionSubclassEntityPersister;
0073: import org.hibernate.type.DiscriminatorType;
0074: import org.hibernate.type.ForeignKeyDirection;
0075: import org.hibernate.type.Type;
0076: import org.hibernate.type.TypeFactory;
0077: import org.hibernate.util.JoinedIterator;
0078: import org.hibernate.util.ReflectHelper;
0079: import org.hibernate.util.StringHelper;
0080:
0081: /**
0082: * Walks an XML mapping document and produces the Hibernate configuration-time metamodel (the
0083: * classes in the <tt>mapping</tt> package)
0084: *
0085: * @author Gavin King
0086: */
0087: public final class HbmBinder {
0088:
0089: private static final Log log = LogFactory.getLog(HbmBinder.class);
0090:
0091: /**
0092: * Private constructor to disallow instantiation.
0093: */
0094: private HbmBinder() {
0095: }
0096:
0097: /**
0098: * The main contract into the hbm.xml-based binder. Performs necessary binding operations
0099: * represented by the given DOM.
0100: *
0101: * @param doc The DOM to be parsed and bound.
0102: * @param mappings Current bind state.
0103: * @param inheritedMetas Any inherited meta-tag information.
0104: * @throws MappingException
0105: */
0106: public static void bindRoot(Document doc, Mappings mappings,
0107: java.util.Map inheritedMetas) throws MappingException {
0108:
0109: java.util.List names = HbmBinder
0110: .getExtendsNeeded(doc, mappings);
0111: if (!names.isEmpty()) {
0112: // classes mentioned in extends not available - so put it in queue
0113: Element hmNode = doc.getRootElement();
0114: Attribute packNode = hmNode.attribute("package");
0115: String packageName = null;
0116: if (packNode != null) {
0117: packageName = packNode.getValue();
0118: }
0119: Iterator itr = names.iterator();
0120: while (itr.hasNext()) {
0121: String extendsName = (String) itr.next();
0122: mappings.addToExtendsQueue(new ExtendsQueueEntry(
0123: extendsName, packageName, doc));
0124: }
0125: return;
0126: }
0127:
0128: Element hmNode = doc.getRootElement();
0129: // get meta's from <hibernate-mapping>
0130: inheritedMetas = getMetas(hmNode, inheritedMetas, true);
0131: extractRootAttributes(hmNode, mappings);
0132:
0133: Iterator rootChildren = hmNode.elementIterator();
0134: while (rootChildren.hasNext()) {
0135: final Element element = (Element) rootChildren.next();
0136: final String elementName = element.getName();
0137:
0138: if ("filter-def".equals(elementName)) {
0139: parseFilterDef(element, mappings);
0140: } else if ("typedef".equals(elementName)) {
0141: bindTypeDef(element, mappings);
0142: } else if ("class".equals(elementName)) {
0143: RootClass rootclass = new RootClass();
0144: bindRootClass(element, rootclass, mappings,
0145: inheritedMetas);
0146: mappings.addClass(rootclass);
0147: } else if ("subclass".equals(elementName)) {
0148: PersistentClass super Model = getSuperclass(mappings,
0149: element);
0150: handleSubclass(super Model, mappings, element,
0151: inheritedMetas);
0152: } else if ("joined-subclass".equals(elementName)) {
0153: PersistentClass super Model = getSuperclass(mappings,
0154: element);
0155: handleJoinedSubclass(super Model, mappings, element,
0156: inheritedMetas);
0157: } else if ("union-subclass".equals(elementName)) {
0158: PersistentClass super Model = getSuperclass(mappings,
0159: element);
0160: handleUnionSubclass(super Model, mappings, element,
0161: inheritedMetas);
0162: } else if ("query".equals(elementName)) {
0163: bindNamedQuery(element, null, mappings);
0164: } else if ("sql-query".equals(elementName)) {
0165: bindNamedSQLQuery(element, null, mappings);
0166: } else if ("resultset".equals(elementName)) {
0167: bindResultSetMappingDefinition(element, null, mappings);
0168: } else if ("import".equals(elementName)) {
0169: bindImport(element, mappings);
0170: } else if ("database-object".equals(elementName)) {
0171: bindAuxiliaryDatabaseObject(element, mappings);
0172: }
0173: }
0174: }
0175:
0176: private static void bindImport(Element importNode, Mappings mappings) {
0177: String className = getClassName(importNode.attribute("class"),
0178: mappings);
0179: Attribute renameNode = importNode.attribute("rename");
0180: String rename = (renameNode == null) ? StringHelper
0181: .unqualify(className) : renameNode.getValue();
0182: log.debug("Import: " + rename + " -> " + className);
0183: mappings.addImport(className, rename);
0184: }
0185:
0186: private static void bindTypeDef(Element typedefNode,
0187: Mappings mappings) {
0188: String typeClass = typedefNode.attributeValue("class");
0189: String typeName = typedefNode.attributeValue("name");
0190: Iterator paramIter = typedefNode.elementIterator("param");
0191: Properties parameters = new Properties();
0192: while (paramIter.hasNext()) {
0193: Element param = (Element) paramIter.next();
0194: parameters.setProperty(param.attributeValue("name"), param
0195: .getTextTrim());
0196: }
0197: mappings.addTypeDef(typeName, typeClass, parameters);
0198: }
0199:
0200: private static void bindAuxiliaryDatabaseObject(
0201: Element auxDbObjectNode, Mappings mappings) {
0202: AuxiliaryDatabaseObject auxDbObject = null;
0203: Element definitionNode = auxDbObjectNode.element("definition");
0204: if (definitionNode != null) {
0205: try {
0206: auxDbObject = (AuxiliaryDatabaseObject) ReflectHelper
0207: .classForName(
0208: definitionNode.attributeValue("class"))
0209: .newInstance();
0210: } catch (ClassNotFoundException e) {
0211: throw new MappingException(
0212: "could not locate custom database object class ["
0213: + definitionNode
0214: .attributeValue("class") + "]");
0215: } catch (Throwable t) {
0216: throw new MappingException(
0217: "could not instantiate custom database object class ["
0218: + definitionNode
0219: .attributeValue("class") + "]");
0220: }
0221: } else {
0222: auxDbObject = new SimpleAuxiliaryDatabaseObject(
0223: auxDbObjectNode.elementTextTrim("create"),
0224: auxDbObjectNode.elementTextTrim("drop"));
0225: }
0226:
0227: Iterator dialectScopings = auxDbObjectNode
0228: .elementIterator("dialect-scope");
0229: while (dialectScopings.hasNext()) {
0230: Element dialectScoping = (Element) dialectScopings.next();
0231: auxDbObject.addDialectScope(dialectScoping
0232: .attributeValue("name"));
0233: }
0234:
0235: mappings.addAuxiliaryDatabaseObject(auxDbObject);
0236: }
0237:
0238: private static void extractRootAttributes(Element hmNode,
0239: Mappings mappings) {
0240: Attribute schemaNode = hmNode.attribute("schema");
0241: mappings.setSchemaName((schemaNode == null) ? null : schemaNode
0242: .getValue());
0243:
0244: Attribute catalogNode = hmNode.attribute("catalog");
0245: mappings.setCatalogName((catalogNode == null) ? null
0246: : catalogNode.getValue());
0247:
0248: Attribute dcNode = hmNode.attribute("default-cascade");
0249: mappings.setDefaultCascade((dcNode == null) ? "none" : dcNode
0250: .getValue());
0251:
0252: Attribute daNode = hmNode.attribute("default-access");
0253: mappings.setDefaultAccess((daNode == null) ? "property"
0254: : daNode.getValue());
0255:
0256: Attribute dlNode = hmNode.attribute("default-lazy");
0257: mappings.setDefaultLazy(dlNode == null
0258: || dlNode.getValue().equals("true"));
0259:
0260: Attribute aiNode = hmNode.attribute("auto-import");
0261: mappings.setAutoImport((aiNode == null)
0262: || "true".equals(aiNode.getValue()));
0263:
0264: Attribute packNode = hmNode.attribute("package");
0265: if (packNode != null)
0266: mappings.setDefaultPackage(packNode.getValue());
0267: }
0268:
0269: /**
0270: * Responsible for perfoming the bind operation related to an <class/> mapping element.
0271: *
0272: * @param node The DOM Element for the <class/> element.
0273: * @param rootClass The mapping instance to which to bind the information.
0274: * @param mappings The current bind state.
0275: * @param inheritedMetas Any inherited meta-tag information.
0276: * @throws MappingException
0277: */
0278: public static void bindRootClass(Element node, RootClass rootClass,
0279: Mappings mappings, java.util.Map inheritedMetas)
0280: throws MappingException {
0281: bindClass(node, rootClass, mappings, inheritedMetas);
0282: inheritedMetas = getMetas(node, inheritedMetas, true); // get meta's from <class>
0283: bindRootPersistentClassCommonValues(node, inheritedMetas,
0284: mappings, rootClass);
0285: }
0286:
0287: private static void bindRootPersistentClassCommonValues(
0288: Element node, java.util.Map inheritedMetas,
0289: Mappings mappings, RootClass entity)
0290: throws MappingException {
0291:
0292: // DB-OBJECTNAME
0293:
0294: Attribute schemaNode = node.attribute("schema");
0295: String schema = schemaNode == null ? mappings.getSchemaName()
0296: : schemaNode.getValue();
0297:
0298: Attribute catalogNode = node.attribute("catalog");
0299: String catalog = catalogNode == null ? mappings
0300: .getCatalogName() : catalogNode.getValue();
0301:
0302: Table table = mappings.addTable(schema, catalog,
0303: getClassTableName(entity, node, schema, catalog, null,
0304: mappings), getSubselect(node), entity
0305: .isAbstract() != null
0306: && entity.isAbstract().booleanValue());
0307: entity.setTable(table);
0308: bindComment(table, node);
0309:
0310: log.info("Mapping class: " + entity.getEntityName() + " -> "
0311: + entity.getTable().getName());
0312:
0313: // MUTABLE
0314: Attribute mutableNode = node.attribute("mutable");
0315: entity.setMutable((mutableNode == null)
0316: || mutableNode.getValue().equals("true"));
0317:
0318: // WHERE
0319: Attribute whereNode = node.attribute("where");
0320: if (whereNode != null)
0321: entity.setWhere(whereNode.getValue());
0322:
0323: // CHECK
0324: Attribute chNode = node.attribute("check");
0325: if (chNode != null)
0326: table.addCheckConstraint(chNode.getValue());
0327:
0328: // POLYMORPHISM
0329: Attribute polyNode = node.attribute("polymorphism");
0330: entity.setExplicitPolymorphism((polyNode != null)
0331: && polyNode.getValue().equals("explicit"));
0332:
0333: // ROW ID
0334: Attribute rowidNode = node.attribute("rowid");
0335: if (rowidNode != null)
0336: table.setRowId(rowidNode.getValue());
0337:
0338: Iterator subnodes = node.elementIterator();
0339: while (subnodes.hasNext()) {
0340:
0341: Element subnode = (Element) subnodes.next();
0342: String name = subnode.getName();
0343:
0344: if ("id".equals(name)) {
0345: // ID
0346: bindSimpleId(subnode, entity, mappings, inheritedMetas);
0347: } else if ("composite-id".equals(name)) {
0348: // COMPOSITE-ID
0349: bindCompositeId(subnode, entity, mappings,
0350: inheritedMetas);
0351: } else if ("version".equals(name)
0352: || "timestamp".equals(name)) {
0353: // VERSION / TIMESTAMP
0354: bindVersioningProperty(table, subnode, mappings, name,
0355: entity, inheritedMetas);
0356: } else if ("discriminator".equals(name)) {
0357: // DISCRIMINATOR
0358: bindDiscriminatorProperty(table, entity, subnode,
0359: mappings);
0360: } else if ("cache".equals(name)) {
0361: entity.setCacheConcurrencyStrategy(subnode
0362: .attributeValue("usage"));
0363: entity.setCacheRegionName(subnode
0364: .attributeValue("region"));
0365: entity.setLazyPropertiesCacheable(!"non-lazy"
0366: .equals(subnode.attributeValue("include")));
0367: }
0368:
0369: }
0370:
0371: // Primary key constraint
0372: entity.createPrimaryKey();
0373:
0374: createClassProperties(node, entity, mappings, inheritedMetas);
0375: }
0376:
0377: private static void bindSimpleId(Element idNode, RootClass entity,
0378: Mappings mappings, java.util.Map inheritedMetas)
0379: throws MappingException {
0380: String propertyName = idNode.attributeValue("name");
0381:
0382: SimpleValue id = new SimpleValue(entity.getTable());
0383: entity.setIdentifier(id);
0384:
0385: // if ( propertyName == null || entity.getPojoRepresentation() == null ) {
0386: // bindSimpleValue( idNode, id, false, RootClass.DEFAULT_IDENTIFIER_COLUMN_NAME, mappings );
0387: // if ( !id.isTypeSpecified() ) {
0388: // throw new MappingException( "must specify an identifier type: " + entity.getEntityName()
0389: // );
0390: // }
0391: // }
0392: // else {
0393: // bindSimpleValue( idNode, id, false, propertyName, mappings );
0394: // PojoRepresentation pojo = entity.getPojoRepresentation();
0395: // id.setTypeUsingReflection( pojo.getClassName(), propertyName );
0396: //
0397: // Property prop = new Property();
0398: // prop.setValue( id );
0399: // bindProperty( idNode, prop, mappings, inheritedMetas );
0400: // entity.setIdentifierProperty( prop );
0401: // }
0402:
0403: if (propertyName == null) {
0404: bindSimpleValue(idNode, id, false,
0405: RootClass.DEFAULT_IDENTIFIER_COLUMN_NAME, mappings);
0406: } else {
0407: bindSimpleValue(idNode, id, false, propertyName, mappings);
0408: }
0409:
0410: if (propertyName == null || !entity.hasPojoRepresentation()) {
0411: if (!id.isTypeSpecified()) {
0412: throw new MappingException(
0413: "must specify an identifier type: "
0414: + entity.getEntityName());
0415: }
0416: } else {
0417: id.setTypeUsingReflection(entity.getClassName(),
0418: propertyName);
0419: }
0420:
0421: if (propertyName != null) {
0422: Property prop = new Property();
0423: prop.setValue(id);
0424: bindProperty(idNode, prop, mappings, inheritedMetas);
0425: entity.setIdentifierProperty(prop);
0426: }
0427:
0428: // TODO:
0429: /*
0430: * if ( id.getHibernateType().getReturnedClass().isArray() ) throw new MappingException(
0431: * "illegal use of an array as an identifier (arrays don't reimplement equals)" );
0432: */
0433: makeIdentifier(idNode, id, mappings);
0434: }
0435:
0436: private static void bindCompositeId(Element idNode,
0437: RootClass entity, Mappings mappings,
0438: java.util.Map inheritedMetas) throws MappingException {
0439: String propertyName = idNode.attributeValue("name");
0440: Component id = new Component(entity);
0441: entity.setIdentifier(id);
0442: bindCompositeId(idNode, id, entity, propertyName, mappings,
0443: inheritedMetas);
0444: if (propertyName == null) {
0445: entity.setEmbeddedIdentifier(id.isEmbedded());
0446: if (id.isEmbedded()) {
0447: // todo : what is the implication of this?
0448: id.setDynamic(!entity.hasPojoRepresentation());
0449: /*
0450: * Property prop = new Property(); prop.setName("id");
0451: * prop.setPropertyAccessorName("embedded"); prop.setValue(id);
0452: * entity.setIdentifierProperty(prop);
0453: */
0454: }
0455: } else {
0456: Property prop = new Property();
0457: prop.setValue(id);
0458: bindProperty(idNode, prop, mappings, inheritedMetas);
0459: entity.setIdentifierProperty(prop);
0460: }
0461:
0462: makeIdentifier(idNode, id, mappings);
0463:
0464: }
0465:
0466: private static void bindVersioningProperty(Table table,
0467: Element subnode, Mappings mappings, String name,
0468: RootClass entity, java.util.Map inheritedMetas) {
0469:
0470: String propertyName = subnode.attributeValue("name");
0471: SimpleValue val = new SimpleValue(table);
0472: bindSimpleValue(subnode, val, false, propertyName, mappings);
0473: if (!val.isTypeSpecified()) {
0474: // this is either a <version/> tag with no type attribute,
0475: // or a <timestamp/> tag
0476: if ("version".equals(name)) {
0477: val.setTypeName("integer");
0478: } else {
0479: if ("db".equals(subnode.attributeValue("source"))) {
0480: val.setTypeName("dbtimestamp");
0481: } else {
0482: val.setTypeName("timestamp");
0483: }
0484: }
0485: }
0486: Property prop = new Property();
0487: prop.setValue(val);
0488: bindProperty(subnode, prop, mappings, inheritedMetas);
0489: // for version properties marked as being generated, make sure they are "always"
0490: // generated; aka, "insert" is invalid; this is dis-allowed by the DTD,
0491: // but just to make sure...
0492: if (prop.getGeneration() == PropertyGeneration.INSERT) {
0493: throw new MappingException(
0494: "'generated' attribute cannot be 'insert' for versioning property");
0495: }
0496: makeVersion(subnode, val);
0497: entity.setVersion(prop);
0498: entity.addProperty(prop);
0499: }
0500:
0501: private static void bindDiscriminatorProperty(Table table,
0502: RootClass entity, Element subnode, Mappings mappings) {
0503: SimpleValue discrim = new SimpleValue(table);
0504: entity.setDiscriminator(discrim);
0505: bindSimpleValue(subnode, discrim, false,
0506: RootClass.DEFAULT_DISCRIMINATOR_COLUMN_NAME, mappings);
0507: if (!discrim.isTypeSpecified()) {
0508: discrim.setTypeName("string");
0509: // ( (Column) discrim.getColumnIterator().next() ).setType(type);
0510: }
0511: entity.setPolymorphic(true);
0512: if ("true".equals(subnode.attributeValue("force")))
0513: entity.setForceDiscriminator(true);
0514: if ("false".equals(subnode.attributeValue("insert")))
0515: entity.setDiscriminatorInsertable(false);
0516: }
0517:
0518: public static void bindClass(Element node,
0519: PersistentClass persistentClass, Mappings mappings,
0520: java.util.Map inheritedMetas) throws MappingException {
0521: // transfer an explicitly defined entity name
0522: // handle the lazy attribute
0523: Attribute lazyNode = node.attribute("lazy");
0524: boolean lazy = lazyNode == null ? mappings.isDefaultLazy()
0525: : "true".equals(lazyNode.getValue());
0526: // go ahead and set the lazy here, since pojo.proxy can override it.
0527: persistentClass.setLazy(lazy);
0528:
0529: String entityName = node.attributeValue("entity-name");
0530: if (entityName == null)
0531: entityName = getClassName(node.attribute("name"), mappings);
0532: if (entityName == null) {
0533: throw new MappingException(
0534: "Unable to determine entity name");
0535: }
0536: persistentClass.setEntityName(entityName);
0537:
0538: bindPojoRepresentation(node, persistentClass, mappings,
0539: inheritedMetas);
0540: bindDom4jRepresentation(node, persistentClass, mappings,
0541: inheritedMetas);
0542: bindMapRepresentation(node, persistentClass, mappings,
0543: inheritedMetas);
0544:
0545: bindPersistentClassCommonValues(node, persistentClass,
0546: mappings, inheritedMetas);
0547:
0548: }
0549:
0550: private static void bindPojoRepresentation(Element node,
0551: PersistentClass entity, Mappings mappings,
0552: java.util.Map metaTags) {
0553:
0554: String className = getClassName(node.attribute("name"),
0555: mappings);
0556: String proxyName = getClassName(node.attribute("proxy"),
0557: mappings);
0558:
0559: entity.setClassName(className);
0560:
0561: if (proxyName != null) {
0562: entity.setProxyInterfaceName(proxyName);
0563: entity.setLazy(true);
0564: } else if (entity.isLazy()) {
0565: entity.setProxyInterfaceName(className);
0566: }
0567:
0568: Element tuplizer = locateTuplizerDefinition(node,
0569: EntityMode.POJO);
0570: if (tuplizer != null) {
0571: entity.addTuplizer(EntityMode.POJO, tuplizer
0572: .attributeValue("class"));
0573: }
0574: }
0575:
0576: private static void bindDom4jRepresentation(Element node,
0577: PersistentClass entity, Mappings mappings,
0578: java.util.Map inheritedMetas) {
0579: String nodeName = node.attributeValue("node");
0580: if (nodeName == null)
0581: nodeName = StringHelper.unqualify(entity.getEntityName());
0582: entity.setNodeName(nodeName);
0583:
0584: Element tuplizer = locateTuplizerDefinition(node,
0585: EntityMode.DOM4J);
0586: if (tuplizer != null) {
0587: entity.addTuplizer(EntityMode.DOM4J, tuplizer
0588: .attributeValue("class"));
0589: }
0590: }
0591:
0592: private static void bindMapRepresentation(Element node,
0593: PersistentClass entity, Mappings mappings,
0594: java.util.Map inheritedMetas) {
0595: Element tuplizer = locateTuplizerDefinition(node,
0596: EntityMode.MAP);
0597: if (tuplizer != null) {
0598: entity.addTuplizer(EntityMode.MAP, tuplizer
0599: .attributeValue("class"));
0600: }
0601: }
0602:
0603: /**
0604: * Locate any explicit tuplizer definition in the metadata, for the given entity-mode.
0605: *
0606: * @param container The containing element (representing the entity/component)
0607: * @param entityMode The entity-mode for which to locate the tuplizer element
0608: * @return The tuplizer element, or null.
0609: */
0610: private static Element locateTuplizerDefinition(Element container,
0611: EntityMode entityMode) {
0612: Iterator itr = container.elements("tuplizer").iterator();
0613: while (itr.hasNext()) {
0614: final Element tuplizerElem = (Element) itr.next();
0615: if (entityMode.toString().equals(
0616: tuplizerElem.attributeValue("entity-mode"))) {
0617: return tuplizerElem;
0618: }
0619: }
0620: return null;
0621: }
0622:
0623: private static void bindPersistentClassCommonValues(Element node,
0624: PersistentClass entity, Mappings mappings,
0625: java.util.Map inheritedMetas) throws MappingException {
0626: // DISCRIMINATOR
0627: Attribute discriminatorNode = node
0628: .attribute("discriminator-value");
0629: entity
0630: .setDiscriminatorValue((discriminatorNode == null) ? entity
0631: .getEntityName()
0632: : discriminatorNode.getValue());
0633:
0634: // DYNAMIC UPDATE
0635: Attribute dynamicNode = node.attribute("dynamic-update");
0636: entity.setDynamicUpdate(dynamicNode != null
0637: && "true".equals(dynamicNode.getValue()));
0638:
0639: // DYNAMIC INSERT
0640: Attribute insertNode = node.attribute("dynamic-insert");
0641: entity.setDynamicInsert(insertNode != null
0642: && "true".equals(insertNode.getValue()));
0643:
0644: // IMPORT
0645: mappings.addImport(entity.getEntityName(), entity
0646: .getEntityName());
0647: if (mappings.isAutoImport()
0648: && entity.getEntityName().indexOf('.') > 0) {
0649: mappings.addImport(entity.getEntityName(), StringHelper
0650: .unqualify(entity.getEntityName()));
0651: }
0652:
0653: // BATCH SIZE
0654: Attribute batchNode = node.attribute("batch-size");
0655: if (batchNode != null)
0656: entity.setBatchSize(Integer.parseInt(batchNode.getValue()));
0657:
0658: // SELECT BEFORE UPDATE
0659: Attribute sbuNode = node.attribute("select-before-update");
0660: if (sbuNode != null)
0661: entity.setSelectBeforeUpdate("true".equals(sbuNode
0662: .getValue()));
0663:
0664: // OPTIMISTIC LOCK MODE
0665: Attribute olNode = node.attribute("optimistic-lock");
0666: entity.setOptimisticLockMode(getOptimisticLockMode(olNode));
0667:
0668: entity.setMetaAttributes(getMetas(node, inheritedMetas));
0669:
0670: // PERSISTER
0671: Attribute persisterNode = node.attribute("persister");
0672: if (persisterNode == null) {
0673: // persister = SingleTableEntityPersister.class;
0674: } else {
0675: try {
0676: entity.setEntityPersisterClass(ReflectHelper
0677: .classForName(persisterNode.getValue()));
0678: } catch (ClassNotFoundException cnfe) {
0679: throw new MappingException(
0680: "Could not find persister class: "
0681: + persisterNode.getValue());
0682: }
0683: }
0684:
0685: // CUSTOM SQL
0686: handleCustomSQL(node, entity);
0687:
0688: Iterator tables = node.elementIterator("synchronize");
0689: while (tables.hasNext()) {
0690: entity.addSynchronizedTable(((Element) tables.next())
0691: .attributeValue("table"));
0692: }
0693:
0694: Attribute abstractNode = node.attribute("abstract");
0695: Boolean isAbstract = abstractNode == null ? null
0696: : "true".equals(abstractNode.getValue()) ? Boolean.TRUE
0697: : "false".equals(abstractNode.getValue()) ? Boolean.FALSE
0698: : null;
0699: entity.setAbstract(isAbstract);
0700: }
0701:
0702: private static void handleCustomSQL(Element node,
0703: PersistentClass model) throws MappingException {
0704: Element element = node.element("sql-insert");
0705: if (element != null) {
0706: boolean callable = isCallable(element);
0707: model.setCustomSQLInsert(element.getTextTrim(), callable,
0708: getResultCheckStyle(element, callable));
0709: }
0710:
0711: element = node.element("sql-delete");
0712: if (element != null) {
0713: boolean callable = isCallable(element);
0714: model.setCustomSQLDelete(element.getTextTrim(), callable,
0715: getResultCheckStyle(element, callable));
0716: }
0717:
0718: element = node.element("sql-update");
0719: if (element != null) {
0720: boolean callable = isCallable(element);
0721: model.setCustomSQLUpdate(element.getTextTrim(), callable,
0722: getResultCheckStyle(element, callable));
0723: }
0724:
0725: element = node.element("loader");
0726: if (element != null) {
0727: model.setLoaderName(element.attributeValue("query-ref"));
0728: }
0729: }
0730:
0731: private static void handleCustomSQL(Element node, Join model)
0732: throws MappingException {
0733: Element element = node.element("sql-insert");
0734: if (element != null) {
0735: boolean callable = isCallable(element);
0736: model.setCustomSQLInsert(element.getTextTrim(), callable,
0737: getResultCheckStyle(element, callable));
0738: }
0739:
0740: element = node.element("sql-delete");
0741: if (element != null) {
0742: boolean callable = isCallable(element);
0743: model.setCustomSQLDelete(element.getTextTrim(), callable,
0744: getResultCheckStyle(element, callable));
0745: }
0746:
0747: element = node.element("sql-update");
0748: if (element != null) {
0749: boolean callable = isCallable(element);
0750: model.setCustomSQLUpdate(element.getTextTrim(), callable,
0751: getResultCheckStyle(element, callable));
0752: }
0753: }
0754:
0755: private static void handleCustomSQL(Element node, Collection model)
0756: throws MappingException {
0757: Element element = node.element("sql-insert");
0758: if (element != null) {
0759: boolean callable = isCallable(element, true);
0760: model.setCustomSQLInsert(element.getTextTrim(), callable,
0761: getResultCheckStyle(element, callable));
0762: }
0763:
0764: element = node.element("sql-delete");
0765: if (element != null) {
0766: boolean callable = isCallable(element, true);
0767: model.setCustomSQLDelete(element.getTextTrim(), callable,
0768: getResultCheckStyle(element, callable));
0769: }
0770:
0771: element = node.element("sql-update");
0772: if (element != null) {
0773: boolean callable = isCallable(element, true);
0774: model.setCustomSQLUpdate(element.getTextTrim(), callable,
0775: getResultCheckStyle(element, callable));
0776: }
0777:
0778: element = node.element("sql-delete-all");
0779: if (element != null) {
0780: boolean callable = isCallable(element, true);
0781: model.setCustomSQLDeleteAll(element.getTextTrim(),
0782: callable, getResultCheckStyle(element, callable));
0783: }
0784: }
0785:
0786: private static boolean isCallable(Element e)
0787: throws MappingException {
0788: return isCallable(e, true);
0789: }
0790:
0791: private static boolean isCallable(Element element,
0792: boolean supportsCallable) throws MappingException {
0793: Attribute attrib = element.attribute("callable");
0794: if (attrib != null && "true".equals(attrib.getValue())) {
0795: if (!supportsCallable) {
0796: throw new MappingException(
0797: "callable attribute not supported yet!");
0798: }
0799: return true;
0800: }
0801: return false;
0802: }
0803:
0804: private static ExecuteUpdateResultCheckStyle getResultCheckStyle(
0805: Element element, boolean callable) throws MappingException {
0806: Attribute attr = element.attribute("check");
0807: if (attr == null) {
0808: // use COUNT as the default. This mimics the old behavior, although
0809: // NONE might be a better option moving forward in the case of callable
0810: return ExecuteUpdateResultCheckStyle.COUNT;
0811: }
0812: return ExecuteUpdateResultCheckStyle.parse(attr.getValue());
0813: }
0814:
0815: public static void bindUnionSubclass(Element node,
0816: UnionSubclass unionSubclass, Mappings mappings,
0817: java.util.Map inheritedMetas) throws MappingException {
0818:
0819: bindClass(node, unionSubclass, mappings, inheritedMetas);
0820: inheritedMetas = getMetas(node, inheritedMetas, true); // get meta's from <subclass>
0821:
0822: if (unionSubclass.getEntityPersisterClass() == null) {
0823: unionSubclass.getRootClass().setEntityPersisterClass(
0824: UnionSubclassEntityPersister.class);
0825: }
0826:
0827: Attribute schemaNode = node.attribute("schema");
0828: String schema = schemaNode == null ? mappings.getSchemaName()
0829: : schemaNode.getValue();
0830:
0831: Attribute catalogNode = node.attribute("catalog");
0832: String catalog = catalogNode == null ? mappings
0833: .getCatalogName() : catalogNode.getValue();
0834:
0835: Table denormalizedSuperTable = unionSubclass.getSuperclass()
0836: .getTable();
0837: Table mytable = mappings.addDenormalizedTable(schema, catalog,
0838: getClassTableName(unionSubclass, node, schema, catalog,
0839: denormalizedSuperTable, mappings),
0840: unionSubclass.isAbstract() != null
0841: && unionSubclass.isAbstract().booleanValue(),
0842: getSubselect(node), denormalizedSuperTable);
0843: unionSubclass.setTable(mytable);
0844:
0845: log.info("Mapping union-subclass: "
0846: + unionSubclass.getEntityName() + " -> "
0847: + unionSubclass.getTable().getName());
0848:
0849: createClassProperties(node, unionSubclass, mappings,
0850: inheritedMetas);
0851:
0852: }
0853:
0854: public static void bindSubclass(Element node, Subclass subclass,
0855: Mappings mappings, java.util.Map inheritedMetas)
0856: throws MappingException {
0857:
0858: bindClass(node, subclass, mappings, inheritedMetas);
0859: inheritedMetas = getMetas(node, inheritedMetas, true); // get meta's from <subclass>
0860:
0861: if (subclass.getEntityPersisterClass() == null) {
0862: subclass.getRootClass().setEntityPersisterClass(
0863: SingleTableEntityPersister.class);
0864: }
0865:
0866: log.info("Mapping subclass: " + subclass.getEntityName()
0867: + " -> " + subclass.getTable().getName());
0868:
0869: // properties
0870: createClassProperties(node, subclass, mappings, inheritedMetas);
0871: }
0872:
0873: private static String getClassTableName(PersistentClass model,
0874: Element node, String schema, String catalog,
0875: Table denormalizedSuperTable, Mappings mappings) {
0876: Attribute tableNameNode = node.attribute("table");
0877: String logicalTableName;
0878: String physicalTableName;
0879: if (tableNameNode == null) {
0880: logicalTableName = StringHelper.unqualify(model
0881: .getEntityName());
0882: physicalTableName = mappings.getNamingStrategy()
0883: .classToTableName(model.getEntityName());
0884: } else {
0885: logicalTableName = tableNameNode.getValue();
0886: physicalTableName = mappings.getNamingStrategy().tableName(
0887: logicalTableName);
0888: }
0889: mappings.addTableBinding(schema, catalog, logicalTableName,
0890: physicalTableName, denormalizedSuperTable);
0891: return physicalTableName;
0892: }
0893:
0894: public static void bindJoinedSubclass(Element node,
0895: JoinedSubclass joinedSubclass, Mappings mappings,
0896: java.util.Map inheritedMetas) throws MappingException {
0897:
0898: bindClass(node, joinedSubclass, mappings, inheritedMetas);
0899: inheritedMetas = getMetas(node, inheritedMetas, true); // get meta's from
0900: // <joined-subclass>
0901:
0902: // joined subclasses
0903: if (joinedSubclass.getEntityPersisterClass() == null) {
0904: joinedSubclass.getRootClass().setEntityPersisterClass(
0905: JoinedSubclassEntityPersister.class);
0906: }
0907:
0908: Attribute schemaNode = node.attribute("schema");
0909: String schema = schemaNode == null ? mappings.getSchemaName()
0910: : schemaNode.getValue();
0911:
0912: Attribute catalogNode = node.attribute("catalog");
0913: String catalog = catalogNode == null ? mappings
0914: .getCatalogName() : catalogNode.getValue();
0915:
0916: Table mytable = mappings.addTable(schema, catalog,
0917: getClassTableName(joinedSubclass, node, schema,
0918: catalog, null, mappings), getSubselect(node),
0919: false);
0920: joinedSubclass.setTable(mytable);
0921: bindComment(mytable, node);
0922:
0923: log.info("Mapping joined-subclass: "
0924: + joinedSubclass.getEntityName() + " -> "
0925: + joinedSubclass.getTable().getName());
0926:
0927: // KEY
0928: Element keyNode = node.element("key");
0929: SimpleValue key = new DependantValue(mytable, joinedSubclass
0930: .getIdentifier());
0931: joinedSubclass.setKey(key);
0932: key.setCascadeDeleteEnabled("cascade".equals(keyNode
0933: .attributeValue("on-delete")));
0934: bindSimpleValue(keyNode, key, false, joinedSubclass
0935: .getEntityName(), mappings);
0936:
0937: // model.getKey().setType( new Type( model.getIdentifier() ) );
0938: joinedSubclass.createPrimaryKey();
0939: joinedSubclass.createForeignKey();
0940:
0941: // CHECK
0942: Attribute chNode = node.attribute("check");
0943: if (chNode != null)
0944: mytable.addCheckConstraint(chNode.getValue());
0945:
0946: // properties
0947: createClassProperties(node, joinedSubclass, mappings,
0948: inheritedMetas);
0949:
0950: }
0951:
0952: private static void bindJoin(Element node, Join join,
0953: Mappings mappings, java.util.Map inheritedMetas)
0954: throws MappingException {
0955:
0956: PersistentClass persistentClass = join.getPersistentClass();
0957: String path = persistentClass.getEntityName();
0958:
0959: // TABLENAME
0960:
0961: Attribute schemaNode = node.attribute("schema");
0962: String schema = schemaNode == null ? mappings.getSchemaName()
0963: : schemaNode.getValue();
0964: Attribute catalogNode = node.attribute("catalog");
0965: String catalog = catalogNode == null ? mappings
0966: .getCatalogName() : catalogNode.getValue();
0967: Table primaryTable = persistentClass.getTable();
0968: Table table = mappings.addTable(schema, catalog,
0969: getClassTableName(persistentClass, node, schema,
0970: catalog, primaryTable, mappings),
0971: getSubselect(node), false);
0972: join.setTable(table);
0973: bindComment(table, node);
0974:
0975: Attribute fetchNode = node.attribute("fetch");
0976: if (fetchNode != null) {
0977: join.setSequentialSelect("select".equals(fetchNode
0978: .getValue()));
0979: }
0980:
0981: Attribute invNode = node.attribute("inverse");
0982: if (invNode != null) {
0983: join.setInverse("true".equals(invNode.getValue()));
0984: }
0985:
0986: Attribute nullNode = node.attribute("optional");
0987: if (nullNode != null) {
0988: join.setOptional("true".equals(nullNode.getValue()));
0989: }
0990:
0991: log.info("Mapping class join: "
0992: + persistentClass.getEntityName() + " -> "
0993: + join.getTable().getName());
0994:
0995: // KEY
0996: Element keyNode = node.element("key");
0997: SimpleValue key = new DependantValue(table, persistentClass
0998: .getIdentifier());
0999: join.setKey(key);
1000: key.setCascadeDeleteEnabled("cascade".equals(keyNode
1001: .attributeValue("on-delete")));
1002: bindSimpleValue(keyNode, key, false, persistentClass
1003: .getEntityName(), mappings);
1004:
1005: // join.getKey().setType( new Type( lazz.getIdentifier() ) );
1006: join.createPrimaryKey();
1007: join.createForeignKey();
1008:
1009: // PROPERTIES
1010: Iterator iter = node.elementIterator();
1011: while (iter.hasNext()) {
1012: Element subnode = (Element) iter.next();
1013: String name = subnode.getName();
1014: String propertyName = subnode.attributeValue("name");
1015:
1016: Value value = null;
1017: if ("many-to-one".equals(name)) {
1018: value = new ManyToOne(table);
1019: bindManyToOne(subnode, (ManyToOne) value, propertyName,
1020: true, mappings);
1021: } else if ("any".equals(name)) {
1022: value = new Any(table);
1023: bindAny(subnode, (Any) value, true, mappings);
1024: } else if ("property".equals(name)) {
1025: value = new SimpleValue(table);
1026: bindSimpleValue(subnode, (SimpleValue) value, true,
1027: propertyName, mappings);
1028: } else if ("component".equals(name)
1029: || "dynamic-component".equals(name)) {
1030: String subpath = StringHelper.qualify(path,
1031: propertyName);
1032: value = new Component(join);
1033: bindComponent(subnode, (Component) value, join
1034: .getPersistentClass().getClassName(),
1035: propertyName, subpath, true, false, mappings,
1036: inheritedMetas, false);
1037: }
1038:
1039: if (value != null) {
1040: Property prop = createProperty(value, propertyName,
1041: persistentClass.getEntityName(), subnode,
1042: mappings, inheritedMetas);
1043: prop.setOptional(join.isOptional());
1044: join.addProperty(prop);
1045: }
1046:
1047: }
1048:
1049: // CUSTOM SQL
1050: handleCustomSQL(node, join);
1051:
1052: }
1053:
1054: public static void bindColumns(final Element node,
1055: final SimpleValue simpleValue, final boolean isNullable,
1056: final boolean autoColumn, final String propertyPath,
1057: final Mappings mappings) throws MappingException {
1058:
1059: Table table = simpleValue.getTable();
1060:
1061: // COLUMN(S)
1062: Attribute columnAttribute = node.attribute("column");
1063: if (columnAttribute == null) {
1064: Iterator iter = node.elementIterator();
1065: int count = 0;
1066: while (iter.hasNext()) {
1067: Element columnElement = (Element) iter.next();
1068: if (columnElement.getName().equals("column")) {
1069: Column column = new Column();
1070: column.setValue(simpleValue);
1071: column.setTypeIndex(count++);
1072: bindColumn(columnElement, column, isNullable);
1073: String logicalColumnName = mappings
1074: .getNamingStrategy().logicalColumnName(
1075: columnElement
1076: .attributeValue("name"),
1077: propertyPath);
1078: column.setName(mappings.getNamingStrategy()
1079: .columnName(logicalColumnName));
1080: if (table != null) {
1081: table.addColumn(column); // table=null -> an association
1082: // - fill it in later
1083: //TODO fill in the mappings for table == null
1084: mappings.addColumnBinding(logicalColumnName,
1085: column, table);
1086: }
1087:
1088: simpleValue.addColumn(column);
1089: // column index
1090: bindIndex(columnElement.attribute("index"), table,
1091: column, mappings);
1092: bindIndex(node.attribute("index"), table, column,
1093: mappings);
1094: //column unique-key
1095: bindUniqueKey(
1096: columnElement.attribute("unique-key"),
1097: table, column, mappings);
1098: bindUniqueKey(node.attribute("unique-key"), table,
1099: column, mappings);
1100: } else if (columnElement.getName().equals("formula")) {
1101: Formula formula = new Formula();
1102: formula.setFormula(columnElement.getText());
1103: simpleValue.addFormula(formula);
1104: }
1105: }
1106: } else {
1107: if (node.elementIterator("column").hasNext()) {
1108: throw new MappingException(
1109: "column attribute may not be used together with <column> subelement");
1110: }
1111: if (node.elementIterator("formula").hasNext()) {
1112: throw new MappingException(
1113: "column attribute may not be used together with <formula> subelement");
1114: }
1115:
1116: Column column = new Column();
1117: column.setValue(simpleValue);
1118: bindColumn(node, column, isNullable);
1119: String logicalColumnName = mappings.getNamingStrategy()
1120: .logicalColumnName(columnAttribute.getValue(),
1121: propertyPath);
1122: column.setName(mappings.getNamingStrategy().columnName(
1123: logicalColumnName));
1124: if (table != null) {
1125: table.addColumn(column); // table=null -> an association - fill
1126: // it in later
1127: //TODO fill in the mappings for table == null
1128: mappings.addColumnBinding(logicalColumnName, column,
1129: table);
1130: }
1131: simpleValue.addColumn(column);
1132: bindIndex(node.attribute("index"), table, column, mappings);
1133: bindUniqueKey(node.attribute("unique-key"), table, column,
1134: mappings);
1135: }
1136:
1137: if (autoColumn && simpleValue.getColumnSpan() == 0) {
1138: Column column = new Column();
1139: column.setValue(simpleValue);
1140: bindColumn(node, column, isNullable);
1141: column.setName(mappings.getNamingStrategy()
1142: .propertyToColumnName(propertyPath));
1143: String logicalName = mappings.getNamingStrategy()
1144: .logicalColumnName(null, propertyPath);
1145: mappings.addColumnBinding(logicalName, column, table);
1146: /* TODO: joinKeyColumnName & foreignKeyColumnName should be called either here or at a
1147: * slightly higer level in the stack (to get all the information we need)
1148: * Right now HbmBinder does not support the
1149: */
1150: simpleValue.getTable().addColumn(column);
1151: simpleValue.addColumn(column);
1152: bindIndex(node.attribute("index"), table, column, mappings);
1153: bindUniqueKey(node.attribute("unique-key"), table, column,
1154: mappings);
1155: }
1156:
1157: }
1158:
1159: private static void bindIndex(Attribute indexAttribute,
1160: Table table, Column column, Mappings mappings) {
1161: if (indexAttribute != null && table != null) {
1162: StringTokenizer tokens = new StringTokenizer(indexAttribute
1163: .getValue(), ", ");
1164: while (tokens.hasMoreTokens()) {
1165: table.getOrCreateIndex(tokens.nextToken()).addColumn(
1166: column);
1167: }
1168: }
1169: }
1170:
1171: private static void bindUniqueKey(Attribute uniqueKeyAttribute,
1172: Table table, Column column, Mappings mappings) {
1173: if (uniqueKeyAttribute != null && table != null) {
1174: StringTokenizer tokens = new StringTokenizer(
1175: uniqueKeyAttribute.getValue(), ", ");
1176: while (tokens.hasMoreTokens()) {
1177: table.getOrCreateUniqueKey(tokens.nextToken())
1178: .addColumn(column);
1179: }
1180: }
1181: }
1182:
1183: // automatically makes a column with the default name if none is specifed by XML
1184: public static void bindSimpleValue(Element node,
1185: SimpleValue simpleValue, boolean isNullable, String path,
1186: Mappings mappings) throws MappingException {
1187: bindSimpleValueType(node, simpleValue, mappings);
1188:
1189: bindColumnsOrFormula(node, simpleValue, path, isNullable,
1190: mappings);
1191:
1192: Attribute fkNode = node.attribute("foreign-key");
1193: if (fkNode != null)
1194: simpleValue.setForeignKeyName(fkNode.getValue());
1195: }
1196:
1197: private static void bindSimpleValueType(Element node,
1198: SimpleValue simpleValue, Mappings mappings)
1199: throws MappingException {
1200: String typeName = null;
1201:
1202: Properties parameters = new Properties();
1203:
1204: Attribute typeNode = node.attribute("type");
1205: if (typeNode == null)
1206: typeNode = node.attribute("id-type"); // for an any
1207: if (typeNode != null)
1208: typeName = typeNode.getValue();
1209:
1210: Element typeChild = node.element("type");
1211: if (typeName == null && typeChild != null) {
1212: typeName = typeChild.attribute("name").getValue();
1213: Iterator typeParameters = typeChild
1214: .elementIterator("param");
1215:
1216: while (typeParameters.hasNext()) {
1217: Element paramElement = (Element) typeParameters.next();
1218: parameters.setProperty(paramElement
1219: .attributeValue("name"), paramElement
1220: .getTextTrim());
1221: }
1222: }
1223:
1224: TypeDef typeDef = mappings.getTypeDef(typeName);
1225: if (typeDef != null) {
1226: typeName = typeDef.getTypeClass();
1227: // parameters on the property mapping should
1228: // override parameters in the typedef
1229: Properties allParameters = new Properties();
1230: allParameters.putAll(typeDef.getParameters());
1231: allParameters.putAll(parameters);
1232: parameters = allParameters;
1233: }
1234:
1235: if (!parameters.isEmpty())
1236: simpleValue.setTypeParameters(parameters);
1237:
1238: if (typeName != null)
1239: simpleValue.setTypeName(typeName);
1240: }
1241:
1242: public static void bindProperty(Element node, Property property,
1243: Mappings mappings, java.util.Map inheritedMetas)
1244: throws MappingException {
1245:
1246: String propName = node.attributeValue("name");
1247: property.setName(propName);
1248: String nodeName = node.attributeValue("node");
1249: if (nodeName == null)
1250: nodeName = propName;
1251: property.setNodeName(nodeName);
1252:
1253: // TODO:
1254: //Type type = model.getValue().getType();
1255: //if (type==null) throw new MappingException(
1256: //"Could not determine a property type for: " + model.getName() );
1257:
1258: Attribute accessNode = node.attribute("access");
1259: if (accessNode != null) {
1260: property.setPropertyAccessorName(accessNode.getValue());
1261: } else if (node.getName().equals("properties")) {
1262: property.setPropertyAccessorName("embedded");
1263: } else {
1264: property.setPropertyAccessorName(mappings
1265: .getDefaultAccess());
1266: }
1267:
1268: Attribute cascadeNode = node.attribute("cascade");
1269: property.setCascade(cascadeNode == null ? mappings
1270: .getDefaultCascade() : cascadeNode.getValue());
1271:
1272: Attribute updateNode = node.attribute("update");
1273: property.setUpdateable(updateNode == null
1274: || "true".equals(updateNode.getValue()));
1275:
1276: Attribute insertNode = node.attribute("insert");
1277: property.setInsertable(insertNode == null
1278: || "true".equals(insertNode.getValue()));
1279:
1280: Attribute lockNode = node.attribute("optimistic-lock");
1281: property.setOptimisticLocked(lockNode == null
1282: || "true".equals(lockNode.getValue()));
1283:
1284: Attribute generatedNode = node.attribute("generated");
1285: String generationName = generatedNode == null ? null
1286: : generatedNode.getValue();
1287: PropertyGeneration generation = PropertyGeneration
1288: .parse(generationName);
1289: property.setGeneration(generation);
1290:
1291: if (generation == PropertyGeneration.ALWAYS
1292: || generation == PropertyGeneration.INSERT) {
1293: // generated properties can *never* be insertable...
1294: if (property.isInsertable()) {
1295: if (insertNode == null) {
1296: // insertable simply because that is the user did not specify
1297: // anything; just override it
1298: property.setInsertable(false);
1299: } else {
1300: // the user specifically supplied insert="true",
1301: // which constitutes an illegal combo
1302: throw new MappingException(
1303: "cannot specify both insert=\"true\" and generated=\""
1304: + generation.getName()
1305: + "\" for property: " + propName);
1306: }
1307: }
1308:
1309: // properties generated on update can never be updateable...
1310: if (property.isUpdateable()
1311: && generation == PropertyGeneration.ALWAYS) {
1312: if (updateNode == null) {
1313: // updateable only because the user did not specify
1314: // anything; just override it
1315: property.setUpdateable(false);
1316: } else {
1317: // the user specifically supplied update="true",
1318: // which constitutes an illegal combo
1319: throw new MappingException(
1320: "cannot specify both update=\"true\" and generated=\""
1321: + generation.getName()
1322: + "\" for property: " + propName);
1323: }
1324: }
1325: }
1326:
1327: boolean isLazyable = "property".equals(node.getName())
1328: || "component".equals(node.getName())
1329: || "many-to-one".equals(node.getName())
1330: || "one-to-one".equals(node.getName())
1331: || "any".equals(node.getName());
1332: if (isLazyable) {
1333: Attribute lazyNode = node.attribute("lazy");
1334: property.setLazy(lazyNode != null
1335: && "true".equals(lazyNode.getValue()));
1336: }
1337:
1338: if (log.isDebugEnabled()) {
1339: String msg = "Mapped property: " + property.getName();
1340: String columns = columns(property.getValue());
1341: if (columns.length() > 0)
1342: msg += " -> " + columns;
1343: // TODO: this fails if we run with debug on!
1344: // if ( model.getType()!=null ) msg += ", type: " + model.getType().getName();
1345: log.debug(msg);
1346: }
1347:
1348: property.setMetaAttributes(getMetas(node, inheritedMetas));
1349:
1350: }
1351:
1352: private static String columns(Value val) {
1353: StringBuffer columns = new StringBuffer();
1354: Iterator iter = val.getColumnIterator();
1355: while (iter.hasNext()) {
1356: columns.append(((Selectable) iter.next()).getText());
1357: if (iter.hasNext())
1358: columns.append(", ");
1359: }
1360: return columns.toString();
1361: }
1362:
1363: /**
1364: * Called for all collections
1365: */
1366: public static void bindCollection(Element node,
1367: Collection collection, String className, String path,
1368: Mappings mappings, java.util.Map inheritedMetas)
1369: throws MappingException {
1370:
1371: // ROLENAME
1372: collection.setRole(path);
1373:
1374: Attribute inverseNode = node.attribute("inverse");
1375: if (inverseNode != null) {
1376: collection
1377: .setInverse("true".equals(inverseNode.getValue()));
1378: }
1379:
1380: Attribute mutableNode = node.attribute("mutable");
1381: if (mutableNode != null) {
1382: collection.setMutable(!"false".equals(mutableNode
1383: .getValue()));
1384: }
1385:
1386: Attribute olNode = node.attribute("optimistic-lock");
1387: collection.setOptimisticLocked(olNode == null
1388: || "true".equals(olNode.getValue()));
1389:
1390: Attribute orderNode = node.attribute("order-by");
1391: if (orderNode != null) {
1392: if (Environment.jvmSupportsLinkedHashCollections()
1393: || (collection instanceof Bag)) {
1394: collection.setOrderBy(orderNode.getValue());
1395: } else {
1396: log
1397: .warn("Attribute \"order-by\" ignored in JDK1.3 or less");
1398: }
1399: }
1400: Attribute whereNode = node.attribute("where");
1401: if (whereNode != null) {
1402: collection.setWhere(whereNode.getValue());
1403: }
1404: Attribute batchNode = node.attribute("batch-size");
1405: if (batchNode != null) {
1406: collection.setBatchSize(Integer.parseInt(batchNode
1407: .getValue()));
1408: }
1409:
1410: String nodeName = node.attributeValue("node");
1411: if (nodeName == null)
1412: nodeName = node.attributeValue("name");
1413: collection.setNodeName(nodeName);
1414: String embed = node.attributeValue("embed-xml");
1415: collection.setEmbedded(embed == null || "true".equals(embed));
1416:
1417: // PERSISTER
1418: Attribute persisterNode = node.attribute("persister");
1419: if (persisterNode != null) {
1420: try {
1421: collection.setCollectionPersisterClass(ReflectHelper
1422: .classForName(persisterNode.getValue()));
1423: } catch (ClassNotFoundException cnfe) {
1424: throw new MappingException(
1425: "Could not find collection persister class: "
1426: + persisterNode.getValue());
1427: }
1428: }
1429:
1430: Attribute typeNode = node.attribute("collection-type");
1431: if (typeNode != null) {
1432: String typeName = typeNode.getValue();
1433: TypeDef typeDef = mappings.getTypeDef(typeName);
1434: if (typeDef != null) {
1435: collection.setTypeName(typeDef.getTypeClass());
1436: collection.setTypeParameters(typeDef.getParameters());
1437: } else {
1438: collection.setTypeName(typeName);
1439: }
1440: }
1441:
1442: // FETCH STRATEGY
1443:
1444: initOuterJoinFetchSetting(node, collection);
1445:
1446: if ("subselect".equals(node.attributeValue("fetch"))) {
1447: collection.setSubselectLoadable(true);
1448: collection.getOwner().setSubselectLoadableCollections(true);
1449: }
1450:
1451: initLaziness(node, collection, mappings, "true", mappings
1452: .isDefaultLazy());
1453: //TODO: suck this into initLaziness!
1454: if ("extra".equals(node.attributeValue("lazy"))) {
1455: collection.setLazy(true);
1456: collection.setExtraLazy(true);
1457: }
1458:
1459: Element oneToManyNode = node.element("one-to-many");
1460: if (oneToManyNode != null) {
1461: OneToMany oneToMany = new OneToMany(collection.getOwner());
1462: collection.setElement(oneToMany);
1463: bindOneToMany(oneToManyNode, oneToMany, mappings);
1464: // we have to set up the table later!! yuck
1465: } else {
1466: // TABLE
1467: Attribute tableNode = node.attribute("table");
1468: String tableName;
1469: if (tableNode != null) {
1470: tableName = mappings.getNamingStrategy().tableName(
1471: tableNode.getValue());
1472: } else {
1473: //tableName = mappings.getNamingStrategy().propertyToTableName( className, path );
1474: Table ownerTable = collection.getOwner().getTable();
1475: //TODO mappings.getLogicalTableName(ownerTable)
1476: String logicalOwnerTableName = ownerTable.getName();
1477: //FIXME we don't have the associated entity table name here, has to be done in a second pass
1478: tableName = mappings
1479: .getNamingStrategy()
1480: .collectionTableName(
1481: collection.getOwner().getEntityName(),
1482: logicalOwnerTableName, null, null, path);
1483: }
1484: Attribute schemaNode = node.attribute("schema");
1485: String schema = schemaNode == null ? mappings
1486: .getSchemaName() : schemaNode.getValue();
1487:
1488: Attribute catalogNode = node.attribute("catalog");
1489: String catalog = catalogNode == null ? mappings
1490: .getCatalogName() : catalogNode.getValue();
1491:
1492: Table table = mappings.addTable(schema, catalog, tableName,
1493: getSubselect(node), false);
1494: collection.setCollectionTable(table);
1495: bindComment(table, node);
1496:
1497: log.info("Mapping collection: " + collection.getRole()
1498: + " -> "
1499: + collection.getCollectionTable().getName());
1500: }
1501:
1502: // SORT
1503: Attribute sortedAtt = node.attribute("sort");
1504: // unsorted, natural, comparator.class.name
1505: if (sortedAtt == null
1506: || sortedAtt.getValue().equals("unsorted")) {
1507: collection.setSorted(false);
1508: } else {
1509: collection.setSorted(true);
1510: String comparatorClassName = sortedAtt.getValue();
1511: if (!comparatorClassName.equals("natural")) {
1512: collection.setComparatorClassName(comparatorClassName);
1513: }
1514: }
1515:
1516: // ORPHAN DELETE (used for programmer error detection)
1517: Attribute cascadeAtt = node.attribute("cascade");
1518: if (cascadeAtt != null
1519: && cascadeAtt.getValue().indexOf("delete-orphan") >= 0) {
1520: collection.setOrphanDelete(true);
1521: }
1522:
1523: // CUSTOM SQL
1524: handleCustomSQL(node, collection);
1525: // set up second pass
1526: if (collection instanceof List) {
1527: mappings.addSecondPass(new ListSecondPass(node, mappings,
1528: (List) collection, inheritedMetas));
1529: } else if (collection instanceof Map) {
1530: mappings.addSecondPass(new MapSecondPass(node, mappings,
1531: (Map) collection, inheritedMetas));
1532: } else if (collection instanceof IdentifierCollection) {
1533: mappings.addSecondPass(new IdentifierCollectionSecondPass(
1534: node, mappings, collection, inheritedMetas));
1535: } else {
1536: mappings.addSecondPass(new CollectionSecondPass(node,
1537: mappings, collection, inheritedMetas));
1538: }
1539:
1540: Iterator iter = node.elementIterator("filter");
1541: while (iter.hasNext()) {
1542: final Element filter = (Element) iter.next();
1543: parseFilter(filter, collection, mappings);
1544: }
1545:
1546: Iterator tables = node.elementIterator("synchronize");
1547: while (tables.hasNext()) {
1548: collection.getSynchronizedTables().add(
1549: ((Element) tables.next()).attributeValue("table"));
1550: }
1551:
1552: Element element = node.element("loader");
1553: if (element != null) {
1554: collection.setLoaderName(element
1555: .attributeValue("query-ref"));
1556: }
1557:
1558: collection.setReferencedPropertyName(node.element("key")
1559: .attributeValue("property-ref"));
1560: }
1561:
1562: private static void initLaziness(Element node, Fetchable fetchable,
1563: Mappings mappings, String proxyVal, boolean defaultLazy) {
1564: Attribute lazyNode = node.attribute("lazy");
1565: boolean isLazyTrue = lazyNode == null ? defaultLazy
1566: && fetchable.isLazy() : //fetch="join" overrides default laziness
1567: lazyNode.getValue().equals(proxyVal); //fetch="join" overrides default laziness
1568: fetchable.setLazy(isLazyTrue);
1569: }
1570:
1571: private static void initLaziness(Element node, ToOne fetchable,
1572: Mappings mappings, boolean defaultLazy) {
1573: if ("no-proxy".equals(node.attributeValue("lazy"))) {
1574: fetchable.setUnwrapProxy(true);
1575: fetchable.setLazy(true);
1576: //TODO: better to degrade to lazy="false" if uninstrumented
1577: } else {
1578: initLaziness(node, fetchable, mappings, "proxy",
1579: defaultLazy);
1580: }
1581: }
1582:
1583: private static void bindColumnsOrFormula(Element node,
1584: SimpleValue simpleValue, String path, boolean isNullable,
1585: Mappings mappings) {
1586: Attribute formulaNode = node.attribute("formula");
1587: if (formulaNode != null) {
1588: Formula f = new Formula();
1589: f.setFormula(formulaNode.getText());
1590: simpleValue.addFormula(f);
1591: } else {
1592: bindColumns(node, simpleValue, isNullable, true, path,
1593: mappings);
1594: }
1595: }
1596:
1597: private static void bindComment(Table table, Element node) {
1598: Element comment = node.element("comment");
1599: if (comment != null)
1600: table.setComment(comment.getTextTrim());
1601: }
1602:
1603: public static void bindManyToOne(Element node, ManyToOne manyToOne,
1604: String path, boolean isNullable, Mappings mappings)
1605: throws MappingException {
1606:
1607: bindColumnsOrFormula(node, manyToOne, path, isNullable,
1608: mappings);
1609: initOuterJoinFetchSetting(node, manyToOne);
1610: initLaziness(node, manyToOne, mappings, true);
1611:
1612: Attribute ukName = node.attribute("property-ref");
1613: if (ukName != null) {
1614: manyToOne.setReferencedPropertyName(ukName.getValue());
1615: }
1616:
1617: manyToOne
1618: .setReferencedEntityName(getEntityName(node, mappings));
1619:
1620: String embed = node.attributeValue("embed-xml");
1621: manyToOne.setEmbedded(embed == null || "true".equals(embed));
1622:
1623: String notFound = node.attributeValue("not-found");
1624: manyToOne.setIgnoreNotFound("ignore".equals(notFound));
1625:
1626: if (ukName != null && !manyToOne.isIgnoreNotFound()) {
1627: if (!node.getName().equals("many-to-many")) { //TODO: really bad, evil hack to fix!!!
1628: mappings.addSecondPass(new ManyToOneSecondPass(
1629: manyToOne));
1630: }
1631: }
1632:
1633: Attribute fkNode = node.attribute("foreign-key");
1634: if (fkNode != null)
1635: manyToOne.setForeignKeyName(fkNode.getValue());
1636:
1637: validateCascade(node, path);
1638: }
1639:
1640: private static void validateCascade(Element node, String path) {
1641: String cascade = node.attributeValue("cascade");
1642: if (cascade != null && cascade.indexOf("delete-orphan") > 0) {
1643: throw new MappingException(
1644: "single-valued associations do not support orphan delete: "
1645: + path);
1646: }
1647: }
1648:
1649: public static void bindAny(Element node, Any any,
1650: boolean isNullable, Mappings mappings)
1651: throws MappingException {
1652: any.setIdentifierType(getTypeFromXML(node));
1653: Attribute metaAttribute = node.attribute("meta-type");
1654: if (metaAttribute != null) {
1655: any.setMetaType(metaAttribute.getValue());
1656:
1657: Iterator iter = node.elementIterator("meta-value");
1658: if (iter.hasNext()) {
1659: HashMap values = new HashMap();
1660: org.hibernate.type.Type metaType = TypeFactory
1661: .heuristicType(any.getMetaType());
1662: while (iter.hasNext()) {
1663: Element metaValue = (Element) iter.next();
1664: try {
1665: Object value = ((DiscriminatorType) metaType)
1666: .stringToObject(metaValue
1667: .attributeValue("value"));
1668: String entityName = getClassName(metaValue
1669: .attribute("class"), mappings);
1670: values.put(value, entityName);
1671: } catch (ClassCastException cce) {
1672: throw new MappingException(
1673: "meta-type was not a DiscriminatorType: "
1674: + metaType.getName());
1675: } catch (Exception e) {
1676: throw new MappingException(
1677: "could not interpret meta-value", e);
1678: }
1679: }
1680: any.setMetaValues(values);
1681: }
1682:
1683: }
1684:
1685: bindColumns(node, any, isNullable, false, null, mappings);
1686: }
1687:
1688: public static void bindOneToOne(Element node, OneToOne oneToOne,
1689: String path, boolean isNullable, Mappings mappings)
1690: throws MappingException {
1691:
1692: bindColumns(node, oneToOne, isNullable, false, null, mappings);
1693:
1694: Attribute constrNode = node.attribute("constrained");
1695: boolean constrained = constrNode != null
1696: && constrNode.getValue().equals("true");
1697: oneToOne.setConstrained(constrained);
1698:
1699: oneToOne
1700: .setForeignKeyType(constrained ? ForeignKeyDirection.FOREIGN_KEY_FROM_PARENT
1701: : ForeignKeyDirection.FOREIGN_KEY_TO_PARENT);
1702:
1703: initOuterJoinFetchSetting(node, oneToOne);
1704: initLaziness(node, oneToOne, mappings, true);
1705:
1706: oneToOne.setEmbedded("true".equals(node
1707: .attributeValue("embed-xml")));
1708:
1709: Attribute fkNode = node.attribute("foreign-key");
1710: if (fkNode != null)
1711: oneToOne.setForeignKeyName(fkNode.getValue());
1712:
1713: Attribute ukName = node.attribute("property-ref");
1714: if (ukName != null)
1715: oneToOne.setReferencedPropertyName(ukName.getValue());
1716:
1717: oneToOne.setPropertyName(node.attributeValue("name"));
1718:
1719: oneToOne.setReferencedEntityName(getEntityName(node, mappings));
1720:
1721: validateCascade(node, path);
1722: }
1723:
1724: public static void bindOneToMany(Element node, OneToMany oneToMany,
1725: Mappings mappings) throws MappingException {
1726:
1727: oneToMany
1728: .setReferencedEntityName(getEntityName(node, mappings));
1729:
1730: String embed = node.attributeValue("embed-xml");
1731: oneToMany.setEmbedded(embed == null || "true".equals(embed));
1732:
1733: String notFound = node.attributeValue("not-found");
1734: oneToMany.setIgnoreNotFound("ignore".equals(notFound));
1735:
1736: }
1737:
1738: public static void bindColumn(Element node, Column column,
1739: boolean isNullable) {
1740: Attribute lengthNode = node.attribute("length");
1741: if (lengthNode != null)
1742: column.setLength(Integer.parseInt(lengthNode.getValue()));
1743: Attribute scalNode = node.attribute("scale");
1744: if (scalNode != null)
1745: column.setScale(Integer.parseInt(scalNode.getValue()));
1746: Attribute precNode = node.attribute("precision");
1747: if (precNode != null)
1748: column.setPrecision(Integer.parseInt(precNode.getValue()));
1749:
1750: Attribute nullNode = node.attribute("not-null");
1751: column.setNullable(nullNode == null ? isNullable : nullNode
1752: .getValue().equals("false"));
1753:
1754: Attribute unqNode = node.attribute("unique");
1755: if (unqNode != null)
1756: column.setUnique(unqNode.getValue().equals("true"));
1757:
1758: column.setCheckConstraint(node.attributeValue("check"));
1759: column.setDefaultValue(node.attributeValue("default"));
1760:
1761: Attribute typeNode = node.attribute("sql-type");
1762: if (typeNode != null)
1763: column.setSqlType(typeNode.getValue());
1764:
1765: Element comment = node.element("comment");
1766: if (comment != null)
1767: column.setComment(comment.getTextTrim());
1768:
1769: }
1770:
1771: /**
1772: * Called for arrays and primitive arrays
1773: */
1774: public static void bindArray(Element node, Array array,
1775: String prefix, String path, Mappings mappings,
1776: java.util.Map inheritedMetas) throws MappingException {
1777:
1778: bindCollection(node, array, prefix, path, mappings,
1779: inheritedMetas);
1780:
1781: Attribute att = node.attribute("element-class");
1782: if (att != null)
1783: array.setElementClassName(getClassName(att, mappings));
1784:
1785: }
1786:
1787: private static Class reflectedPropertyClass(String className,
1788: String propertyName) throws MappingException {
1789: if (className == null)
1790: return null;
1791: return ReflectHelper.reflectedPropertyClass(className,
1792: propertyName);
1793: }
1794:
1795: public static void bindComposite(Element node, Component component,
1796: String path, boolean isNullable, Mappings mappings,
1797: java.util.Map inheritedMetas) throws MappingException {
1798: bindComponent(node, component, null, null, path, isNullable,
1799: false, mappings, inheritedMetas, false);
1800: }
1801:
1802: public static void bindCompositeId(Element node,
1803: Component component, PersistentClass persistentClass,
1804: String propertyName, Mappings mappings,
1805: java.util.Map inheritedMetas) throws MappingException {
1806:
1807: component.setKey(true);
1808:
1809: String path = StringHelper.qualify(persistentClass
1810: .getEntityName(), propertyName == null ? "id"
1811: : propertyName);
1812:
1813: bindComponent(
1814: node,
1815: component,
1816: persistentClass.getClassName(),
1817: propertyName,
1818: path,
1819: false,
1820: node.attribute("class") == null && propertyName == null,
1821: mappings, inheritedMetas, false);
1822:
1823: if ("true".equals(node.attributeValue("mapped"))) {
1824: if (propertyName != null) {
1825: throw new MappingException(
1826: "cannot combine mapped=\"true\" with specified name");
1827: }
1828: Component mapper = new Component(persistentClass);
1829: bindComponent(node, mapper, persistentClass.getClassName(),
1830: null, path, false, true, mappings, inheritedMetas,
1831: true);
1832: persistentClass.setIdentifierMapper(mapper);
1833: Property property = new Property();
1834: property.setName("_identifierMapper");
1835: property.setNodeName("id");
1836: property.setUpdateable(false);
1837: property.setInsertable(false);
1838: property.setValue(mapper);
1839: property.setPropertyAccessorName("embedded");
1840: persistentClass.addProperty(property);
1841: }
1842:
1843: }
1844:
1845: public static void bindComponent(Element node, Component component,
1846: String ownerClassName, String parentProperty, String path,
1847: boolean isNullable, boolean isEmbedded, Mappings mappings,
1848: java.util.Map inheritedMetas, boolean isIdentifierMapper)
1849: throws MappingException {
1850:
1851: component.setEmbedded(isEmbedded);
1852: component.setRoleName(path);
1853:
1854: inheritedMetas = getMetas(node, inheritedMetas);
1855: component.setMetaAttributes(inheritedMetas);
1856:
1857: Attribute classNode = isIdentifierMapper ? null : node
1858: .attribute("class");
1859: if (classNode != null) {
1860: component.setComponentClassName(getClassName(classNode,
1861: mappings));
1862: } else if ("dynamic-component".equals(node.getName())) {
1863: component.setDynamic(true);
1864: } else if (isEmbedded) {
1865: // an "embedded" component (composite ids and unique)
1866: // note that this does not handle nested components
1867: if (component.getOwner().hasPojoRepresentation()) {
1868: component.setComponentClassName(component.getOwner()
1869: .getClassName());
1870: } else {
1871: component.setDynamic(true);
1872: }
1873: } else {
1874: // todo : again, how *should* this work for non-pojo entities?
1875: if (component.getOwner().hasPojoRepresentation()) {
1876: Class reflectedClass = reflectedPropertyClass(
1877: ownerClassName, parentProperty);
1878: if (reflectedClass != null) {
1879: component.setComponentClassName(reflectedClass
1880: .getName());
1881: }
1882: } else {
1883: component.setDynamic(true);
1884: }
1885: }
1886:
1887: String nodeName = node.attributeValue("node");
1888: if (nodeName == null)
1889: nodeName = node.attributeValue("name");
1890: if (nodeName == null)
1891: nodeName = component.getOwner().getNodeName();
1892: component.setNodeName(nodeName);
1893:
1894: Iterator iter = node.elementIterator();
1895: while (iter.hasNext()) {
1896:
1897: Element subnode = (Element) iter.next();
1898: String name = subnode.getName();
1899: String propertyName = getPropertyName(subnode);
1900: String subpath = propertyName == null ? null : StringHelper
1901: .qualify(path, propertyName);
1902:
1903: CollectionType collectType = CollectionType
1904: .collectionTypeFromString(name);
1905: Value value = null;
1906: if (collectType != null) {
1907: Collection collection = collectType.create(subnode,
1908: subpath, component.getOwner(), mappings,
1909: inheritedMetas);
1910: mappings.addCollection(collection);
1911: value = collection;
1912: } else if ("many-to-one".equals(name)
1913: || "key-many-to-one".equals(name)) {
1914: value = new ManyToOne(component.getTable());
1915: String relativePath;
1916: if (isEmbedded) {
1917: relativePath = propertyName;
1918: } else {
1919: relativePath = subpath.substring(component
1920: .getOwner().getEntityName().length() + 1);
1921: }
1922: bindManyToOne(subnode, (ManyToOne) value, relativePath,
1923: isNullable, mappings);
1924: } else if ("one-to-one".equals(name)) {
1925: value = new OneToOne(component.getTable(), component
1926: .getOwner());
1927: String relativePath;
1928: if (isEmbedded) {
1929: relativePath = propertyName;
1930: } else {
1931: relativePath = subpath.substring(component
1932: .getOwner().getEntityName().length() + 1);
1933: }
1934: bindOneToOne(subnode, (OneToOne) value, relativePath,
1935: isNullable, mappings);
1936: } else if ("any".equals(name)) {
1937: value = new Any(component.getTable());
1938: bindAny(subnode, (Any) value, isNullable, mappings);
1939: } else if ("property".equals(name)
1940: || "key-property".equals(name)) {
1941: value = new SimpleValue(component.getTable());
1942: String relativePath;
1943: if (isEmbedded) {
1944: relativePath = propertyName;
1945: } else {
1946: relativePath = subpath.substring(component
1947: .getOwner().getEntityName().length() + 1);
1948: }
1949: bindSimpleValue(subnode, (SimpleValue) value,
1950: isNullable, relativePath, mappings);
1951: } else if ("component".equals(name)
1952: || "dynamic-component".equals(name)
1953: || "nested-composite-element".equals(name)) {
1954: value = new Component(component); // a nested composite element
1955: bindComponent(subnode, (Component) value, component
1956: .getComponentClassName(), propertyName,
1957: subpath, isNullable, isEmbedded, mappings,
1958: inheritedMetas, isIdentifierMapper);
1959: } else if ("parent".equals(name)) {
1960: component.setParentProperty(propertyName);
1961: }
1962:
1963: if (value != null) {
1964: Property property = createProperty(value, propertyName,
1965: component.getComponentClassName(), subnode,
1966: mappings, inheritedMetas);
1967: if (isIdentifierMapper) {
1968: property.setInsertable(false);
1969: property.setUpdateable(false);
1970: }
1971: component.addProperty(property);
1972: }
1973: }
1974:
1975: if ("true".equals(node.attributeValue("unique"))) {
1976: iter = component.getColumnIterator();
1977: ArrayList cols = new ArrayList();
1978: while (iter.hasNext()) {
1979: cols.add(iter.next());
1980: }
1981: component.getOwner().getTable().createUniqueKey(cols);
1982: }
1983:
1984: iter = node.elementIterator("tuplizer");
1985: while (iter.hasNext()) {
1986: final Element tuplizerElem = (Element) iter.next();
1987: EntityMode mode = EntityMode.parse(tuplizerElem
1988: .attributeValue("entity-mode"));
1989: component.addTuplizer(mode, tuplizerElem
1990: .attributeValue("class"));
1991: }
1992: }
1993:
1994: public static String getTypeFromXML(Element node)
1995: throws MappingException {
1996: // TODO: handle TypeDefs
1997: Attribute typeNode = node.attribute("type");
1998: if (typeNode == null)
1999: typeNode = node.attribute("id-type"); // for an any
2000: if (typeNode == null)
2001: return null; // we will have to use reflection
2002: return typeNode.getValue();
2003: }
2004:
2005: private static void initOuterJoinFetchSetting(Element node,
2006: Fetchable model) {
2007: Attribute fetchNode = node.attribute("fetch");
2008: final FetchMode fetchStyle;
2009: boolean lazy = true;
2010: if (fetchNode == null) {
2011: Attribute jfNode = node.attribute("outer-join");
2012: if (jfNode == null) {
2013: if ("many-to-many".equals(node.getName())) {
2014: //NOTE SPECIAL CASE:
2015: // default to join and non-lazy for the "second join"
2016: // of the many-to-many
2017: lazy = false;
2018: fetchStyle = FetchMode.JOIN;
2019: } else if ("one-to-one".equals(node.getName())) {
2020: //NOTE SPECIAL CASE:
2021: // one-to-one constrained=false cannot be proxied,
2022: // so default to join and non-lazy
2023: lazy = ((OneToOne) model).isConstrained();
2024: fetchStyle = lazy ? FetchMode.DEFAULT
2025: : FetchMode.JOIN;
2026: } else {
2027: fetchStyle = FetchMode.DEFAULT;
2028: }
2029: } else {
2030: // use old (HB 2.1) defaults if outer-join is specified
2031: String eoj = jfNode.getValue();
2032: if ("auto".equals(eoj)) {
2033: fetchStyle = FetchMode.DEFAULT;
2034: } else {
2035: boolean join = "true".equals(eoj);
2036: fetchStyle = join ? FetchMode.JOIN
2037: : FetchMode.SELECT;
2038: }
2039: }
2040: } else {
2041: boolean join = "join".equals(fetchNode.getValue());
2042: //lazy = !join;
2043: fetchStyle = join ? FetchMode.JOIN : FetchMode.SELECT;
2044: }
2045: model.setFetchMode(fetchStyle);
2046: model.setLazy(lazy);
2047: }
2048:
2049: private static void makeIdentifier(Element node, SimpleValue model,
2050: Mappings mappings) {
2051:
2052: // GENERATOR
2053: Element subnode = node.element("generator");
2054: if (subnode != null) {
2055: model.setIdentifierGeneratorStrategy(subnode
2056: .attributeValue("class"));
2057:
2058: Properties params = new Properties();
2059:
2060: if (mappings.getSchemaName() != null) {
2061: params.setProperty(
2062: PersistentIdentifierGenerator.SCHEMA, mappings
2063: .getSchemaName());
2064: }
2065: if (mappings.getCatalogName() != null) {
2066: params.setProperty(
2067: PersistentIdentifierGenerator.CATALOG, mappings
2068: .getCatalogName());
2069: }
2070:
2071: Iterator iter = subnode.elementIterator("param");
2072: while (iter.hasNext()) {
2073: Element childNode = (Element) iter.next();
2074: params.setProperty(childNode.attributeValue("name"),
2075: childNode.getText());
2076: }
2077:
2078: model.setIdentifierGeneratorProperties(params);
2079: }
2080:
2081: model.getTable().setIdentifierValue(model);
2082:
2083: // ID UNSAVED-VALUE
2084: Attribute nullValueNode = node.attribute("unsaved-value");
2085: if (nullValueNode != null) {
2086: model.setNullValue(nullValueNode.getValue());
2087: } else {
2088: if ("assigned".equals(model
2089: .getIdentifierGeneratorStrategy())) {
2090: model.setNullValue("undefined");
2091: } else {
2092: model.setNullValue(null);
2093: }
2094: }
2095: }
2096:
2097: private static final void makeVersion(Element node,
2098: SimpleValue model) {
2099:
2100: // VERSION UNSAVED-VALUE
2101: Attribute nullValueNode = node.attribute("unsaved-value");
2102: if (nullValueNode != null) {
2103: model.setNullValue(nullValueNode.getValue());
2104: } else {
2105: model.setNullValue("undefined");
2106: }
2107:
2108: }
2109:
2110: protected static void createClassProperties(Element node,
2111: PersistentClass persistentClass, Mappings mappings,
2112: java.util.Map inheritedMetas) throws MappingException {
2113: createClassProperties(node, persistentClass, mappings,
2114: inheritedMetas, null, true, true, false);
2115: }
2116:
2117: protected static void createClassProperties(Element node,
2118: PersistentClass persistentClass, Mappings mappings,
2119: java.util.Map inheritedMetas, UniqueKey uniqueKey,
2120: boolean mutable, boolean nullable, boolean naturalId)
2121: throws MappingException {
2122:
2123: String entityName = persistentClass.getEntityName();
2124: Table table = persistentClass.getTable();
2125:
2126: Iterator iter = node.elementIterator();
2127: while (iter.hasNext()) {
2128: Element subnode = (Element) iter.next();
2129: String name = subnode.getName();
2130: String propertyName = subnode.attributeValue("name");
2131:
2132: CollectionType collectType = CollectionType
2133: .collectionTypeFromString(name);
2134: Value value = null;
2135: if (collectType != null) {
2136: Collection collection = collectType.create(subnode,
2137: StringHelper.qualify(entityName, propertyName),
2138: persistentClass, mappings, inheritedMetas);
2139: mappings.addCollection(collection);
2140: value = collection;
2141: } else if ("many-to-one".equals(name)) {
2142: value = new ManyToOne(table);
2143: bindManyToOne(subnode, (ManyToOne) value, propertyName,
2144: nullable, mappings);
2145: } else if ("any".equals(name)) {
2146: value = new Any(table);
2147: bindAny(subnode, (Any) value, nullable, mappings);
2148: } else if ("one-to-one".equals(name)) {
2149: value = new OneToOne(table, persistentClass);
2150: bindOneToOne(subnode, (OneToOne) value, propertyName,
2151: true, mappings);
2152: } else if ("property".equals(name)) {
2153: value = new SimpleValue(table);
2154: bindSimpleValue(subnode, (SimpleValue) value, nullable,
2155: propertyName, mappings);
2156: } else if ("component".equals(name)
2157: || "dynamic-component".equals(name)
2158: || "properties".equals(name)) {
2159: String subpath = StringHelper.qualify(entityName,
2160: propertyName);
2161: value = new Component(persistentClass);
2162:
2163: bindComponent(subnode, (Component) value,
2164: persistentClass.getClassName(), propertyName,
2165: subpath, true, "properties".equals(name),
2166: mappings, inheritedMetas, false);
2167: } else if ("join".equals(name)) {
2168: Join join = new Join();
2169: join.setPersistentClass(persistentClass);
2170: bindJoin(subnode, join, mappings, inheritedMetas);
2171: persistentClass.addJoin(join);
2172: } else if ("subclass".equals(name)) {
2173: handleSubclass(persistentClass, mappings, subnode,
2174: inheritedMetas);
2175: } else if ("joined-subclass".equals(name)) {
2176: handleJoinedSubclass(persistentClass, mappings,
2177: subnode, inheritedMetas);
2178: } else if ("union-subclass".equals(name)) {
2179: handleUnionSubclass(persistentClass, mappings, subnode,
2180: inheritedMetas);
2181: } else if ("filter".equals(name)) {
2182: parseFilter(subnode, persistentClass, mappings);
2183: } else if ("natural-id".equals(name)) {
2184: UniqueKey uk = new UniqueKey();
2185: uk.setName("_UniqueKey");
2186: uk.setTable(table);
2187: //by default, natural-ids are "immutable" (constant)
2188: boolean mutableId = "true".equals(subnode
2189: .attributeValue("mutable"));
2190: createClassProperties(subnode, persistentClass,
2191: mappings, inheritedMetas, uk, mutableId, false,
2192: true);
2193: table.addUniqueKey(uk);
2194: } else if ("query".equals(name)) {
2195: bindNamedQuery(subnode,
2196: persistentClass.getEntityName(), mappings);
2197: } else if ("sql-query".equals(name)) {
2198: bindNamedSQLQuery(subnode, persistentClass
2199: .getEntityName(), mappings);
2200: } else if ("resultset".equals(name)) {
2201: bindResultSetMappingDefinition(subnode, persistentClass
2202: .getEntityName(), mappings);
2203: }
2204:
2205: if (value != null) {
2206: Property property = createProperty(value, propertyName,
2207: persistentClass.getClassName(), subnode,
2208: mappings, inheritedMetas);
2209: if (!mutable)
2210: property.setUpdateable(false);
2211: if (naturalId)
2212: property.setNaturalIdentifier(true);
2213: persistentClass.addProperty(property);
2214: if (uniqueKey != null)
2215: uniqueKey.addColumns(property.getColumnIterator());
2216: }
2217:
2218: }
2219: }
2220:
2221: private static Property createProperty(final Value value,
2222: final String propertyName, final String className,
2223: final Element subnode, final Mappings mappings,
2224: java.util.Map inheritedMetas) throws MappingException {
2225:
2226: if (StringHelper.isEmpty(propertyName)) {
2227: throw new MappingException(subnode.getName()
2228: + " mapping must defined a name attribute ["
2229: + className + "]");
2230: }
2231:
2232: value.setTypeUsingReflection(className, propertyName);
2233:
2234: // this is done here 'cos we might only know the type here (ugly!)
2235: // TODO: improve this a lot:
2236: if (value instanceof ToOne) {
2237: ToOne toOne = (ToOne) value;
2238: String propertyRef = toOne.getReferencedPropertyName();
2239: if (propertyRef != null) {
2240: mappings.addUniquePropertyReference(toOne
2241: .getReferencedEntityName(), propertyRef);
2242: }
2243: } else if (value instanceof Collection) {
2244: Collection coll = (Collection) value;
2245: String propertyRef = coll.getReferencedPropertyName();
2246: // not necessarily a *unique* property reference
2247: if (propertyRef != null) {
2248: mappings.addPropertyReference(
2249: coll.getOwnerEntityName(), propertyRef);
2250: }
2251: }
2252:
2253: value.createForeignKey();
2254: Property prop = new Property();
2255: prop.setValue(value);
2256: bindProperty(subnode, prop, mappings, inheritedMetas);
2257: return prop;
2258: }
2259:
2260: private static void handleUnionSubclass(PersistentClass model,
2261: Mappings mappings, Element subnode,
2262: java.util.Map inheritedMetas) throws MappingException {
2263: UnionSubclass subclass = new UnionSubclass(model);
2264: bindUnionSubclass(subnode, subclass, mappings, inheritedMetas);
2265: model.addSubclass(subclass);
2266: mappings.addClass(subclass);
2267: }
2268:
2269: private static void handleJoinedSubclass(PersistentClass model,
2270: Mappings mappings, Element subnode,
2271: java.util.Map inheritedMetas) throws MappingException {
2272: JoinedSubclass subclass = new JoinedSubclass(model);
2273: bindJoinedSubclass(subnode, subclass, mappings, inheritedMetas);
2274: model.addSubclass(subclass);
2275: mappings.addClass(subclass);
2276: }
2277:
2278: private static void handleSubclass(PersistentClass model,
2279: Mappings mappings, Element subnode,
2280: java.util.Map inheritedMetas) throws MappingException {
2281: Subclass subclass = new SingleTableSubclass(model);
2282: bindSubclass(subnode, subclass, mappings, inheritedMetas);
2283: model.addSubclass(subclass);
2284: mappings.addClass(subclass);
2285: }
2286:
2287: /**
2288: * Called for Lists, arrays, primitive arrays
2289: */
2290: public static void bindListSecondPass(Element node, List list,
2291: java.util.Map classes, Mappings mappings,
2292: java.util.Map inheritedMetas) throws MappingException {
2293:
2294: bindCollectionSecondPass(node, list, classes, mappings,
2295: inheritedMetas);
2296:
2297: Element subnode = node.element("list-index");
2298: if (subnode == null)
2299: subnode = node.element("index");
2300: SimpleValue iv = new SimpleValue(list.getCollectionTable());
2301: bindSimpleValue(subnode, iv, list.isOneToMany(),
2302: IndexedCollection.DEFAULT_INDEX_COLUMN_NAME, mappings);
2303: iv.setTypeName("integer");
2304: list.setIndex(iv);
2305: String baseIndex = subnode.attributeValue("base");
2306: if (baseIndex != null)
2307: list.setBaseIndex(Integer.parseInt(baseIndex));
2308: list.setIndexNodeName(subnode.attributeValue("node"));
2309:
2310: if (list.isOneToMany() && !list.getKey().isNullable()
2311: && !list.isInverse()) {
2312: String entityName = ((OneToMany) list.getElement())
2313: .getReferencedEntityName();
2314: PersistentClass referenced = mappings.getClass(entityName);
2315: IndexBackref ib = new IndexBackref();
2316: ib.setName('_' + node.attributeValue("name")
2317: + "IndexBackref");
2318: ib.setUpdateable(false);
2319: ib.setSelectable(false);
2320: ib.setCollectionRole(list.getRole());
2321: ib.setEntityName(list.getOwner().getEntityName());
2322: ib.setValue(list.getIndex());
2323: // ( (Column) ( (SimpleValue) ic.getIndex() ).getColumnIterator().next()
2324: // ).setNullable(false);
2325: referenced.addProperty(ib);
2326: }
2327: }
2328:
2329: public static void bindIdentifierCollectionSecondPass(Element node,
2330: IdentifierCollection collection,
2331: java.util.Map persistentClasses, Mappings mappings,
2332: java.util.Map inheritedMetas) throws MappingException {
2333:
2334: bindCollectionSecondPass(node, collection, persistentClasses,
2335: mappings, inheritedMetas);
2336:
2337: Element subnode = node.element("collection-id");
2338: SimpleValue id = new SimpleValue(collection
2339: .getCollectionTable());
2340: bindSimpleValue(subnode, id, false,
2341: IdentifierCollection.DEFAULT_IDENTIFIER_COLUMN_NAME,
2342: mappings);
2343: collection.setIdentifier(id);
2344: makeIdentifier(subnode, id, mappings);
2345:
2346: }
2347:
2348: /**
2349: * Called for Maps
2350: */
2351: public static void bindMapSecondPass(Element node, Map map,
2352: java.util.Map classes, Mappings mappings,
2353: java.util.Map inheritedMetas) throws MappingException {
2354:
2355: bindCollectionSecondPass(node, map, classes, mappings,
2356: inheritedMetas);
2357:
2358: Iterator iter = node.elementIterator();
2359: while (iter.hasNext()) {
2360: Element subnode = (Element) iter.next();
2361: String name = subnode.getName();
2362:
2363: if ("index".equals(name) || "map-key".equals(name)) {
2364: SimpleValue value = new SimpleValue(map
2365: .getCollectionTable());
2366: bindSimpleValue(subnode, value, map.isOneToMany(),
2367: IndexedCollection.DEFAULT_INDEX_COLUMN_NAME,
2368: mappings);
2369: if (!value.isTypeSpecified()) {
2370: throw new MappingException(
2371: "map index element must specify a type: "
2372: + map.getRole());
2373: }
2374: map.setIndex(value);
2375: map.setIndexNodeName(subnode.attributeValue("node"));
2376: } else if ("index-many-to-many".equals(name)
2377: || "map-key-many-to-many".equals(name)) {
2378: ManyToOne mto = new ManyToOne(map.getCollectionTable());
2379: bindManyToOne(subnode, mto,
2380: IndexedCollection.DEFAULT_INDEX_COLUMN_NAME,
2381: map.isOneToMany(), mappings);
2382: map.setIndex(mto);
2383:
2384: } else if ("composite-index".equals(name)
2385: || "composite-map-key".equals(name)) {
2386: Component component = new Component(map);
2387: bindComposite(subnode, component, map.getRole()
2388: + ".index", map.isOneToMany(), mappings,
2389: inheritedMetas);
2390: map.setIndex(component);
2391: } else if ("index-many-to-any".equals(name)) {
2392: Any any = new Any(map.getCollectionTable());
2393: bindAny(subnode, any, map.isOneToMany(), mappings);
2394: map.setIndex(any);
2395: }
2396: }
2397:
2398: // TODO: this is a bit of copy/paste from IndexedCollection.createPrimaryKey()
2399: boolean indexIsFormula = false;
2400: Iterator colIter = map.getIndex().getColumnIterator();
2401: while (colIter.hasNext()) {
2402: if (((Selectable) colIter.next()).isFormula())
2403: indexIsFormula = true;
2404: }
2405:
2406: if (map.isOneToMany() && !map.getKey().isNullable()
2407: && !map.isInverse() && !indexIsFormula) {
2408: String entityName = ((OneToMany) map.getElement())
2409: .getReferencedEntityName();
2410: PersistentClass referenced = mappings.getClass(entityName);
2411: IndexBackref ib = new IndexBackref();
2412: ib.setName('_' + node.attributeValue("name")
2413: + "IndexBackref");
2414: ib.setUpdateable(false);
2415: ib.setSelectable(false);
2416: ib.setCollectionRole(map.getRole());
2417: ib.setEntityName(map.getOwner().getEntityName());
2418: ib.setValue(map.getIndex());
2419: // ( (Column) ( (SimpleValue) ic.getIndex() ).getColumnIterator().next()
2420: // ).setNullable(false);
2421: referenced.addProperty(ib);
2422: }
2423: }
2424:
2425: /**
2426: * Called for all collections
2427: */
2428: public static void bindCollectionSecondPass(Element node,
2429: Collection collection, java.util.Map persistentClasses,
2430: Mappings mappings, java.util.Map inheritedMetas)
2431: throws MappingException {
2432:
2433: if (collection.isOneToMany()) {
2434: OneToMany oneToMany = (OneToMany) collection.getElement();
2435: String assocClass = oneToMany.getReferencedEntityName();
2436: PersistentClass persistentClass = (PersistentClass) persistentClasses
2437: .get(assocClass);
2438: if (persistentClass == null) {
2439: throw new MappingException(
2440: "Association references unmapped class: "
2441: + assocClass);
2442: }
2443: oneToMany.setAssociatedClass(persistentClass);
2444: collection.setCollectionTable(persistentClass.getTable());
2445:
2446: log.info("Mapping collection: " + collection.getRole()
2447: + " -> "
2448: + collection.getCollectionTable().getName());
2449: }
2450:
2451: // CHECK
2452: Attribute chNode = node.attribute("check");
2453: if (chNode != null) {
2454: collection.getCollectionTable().addCheckConstraint(
2455: chNode.getValue());
2456: }
2457:
2458: // contained elements:
2459: Iterator iter = node.elementIterator();
2460: while (iter.hasNext()) {
2461: Element subnode = (Element) iter.next();
2462: String name = subnode.getName();
2463:
2464: if ("key".equals(name)) {
2465: KeyValue keyVal;
2466: String propRef = collection.getReferencedPropertyName();
2467: if (propRef == null) {
2468: keyVal = collection.getOwner().getIdentifier();
2469: } else {
2470: keyVal = (KeyValue) collection.getOwner()
2471: .getReferencedProperty(propRef).getValue();
2472: }
2473: SimpleValue key = new DependantValue(collection
2474: .getCollectionTable(), keyVal);
2475: key.setCascadeDeleteEnabled("cascade".equals(subnode
2476: .attributeValue("on-delete")));
2477: bindSimpleValue(subnode, key, collection.isOneToMany(),
2478: Collection.DEFAULT_KEY_COLUMN_NAME, mappings);
2479: collection.setKey(key);
2480:
2481: Attribute notNull = subnode.attribute("not-null");
2482: ((DependantValue) key).setNullable(notNull == null
2483: || notNull.getValue().equals("false"));
2484: Attribute updateable = subnode.attribute("update");
2485: ((DependantValue) key).setUpdateable(updateable == null
2486: || updateable.getValue().equals("true"));
2487:
2488: } else if ("element".equals(name)) {
2489: SimpleValue elt = new SimpleValue(collection
2490: .getCollectionTable());
2491: collection.setElement(elt);
2492: bindSimpleValue(subnode, elt, true,
2493: Collection.DEFAULT_ELEMENT_COLUMN_NAME,
2494: mappings);
2495: } else if ("many-to-many".equals(name)) {
2496: ManyToOne element = new ManyToOne(collection
2497: .getCollectionTable());
2498: collection.setElement(element);
2499: bindManyToOne(subnode, element,
2500: Collection.DEFAULT_ELEMENT_COLUMN_NAME, false,
2501: mappings);
2502: bindManyToManySubelements(collection, subnode, mappings);
2503: } else if ("composite-element".equals(name)) {
2504: Component element = new Component(collection);
2505: collection.setElement(element);
2506: bindComposite(subnode, element, collection.getRole()
2507: + ".element", true, mappings, inheritedMetas);
2508: } else if ("many-to-any".equals(name)) {
2509: Any element = new Any(collection.getCollectionTable());
2510: collection.setElement(element);
2511: bindAny(subnode, element, true, mappings);
2512: } else if ("cache".equals(name)) {
2513: collection.setCacheConcurrencyStrategy(subnode
2514: .attributeValue("usage"));
2515: collection.setCacheRegionName(subnode
2516: .attributeValue("region"));
2517: }
2518:
2519: String nodeName = subnode.attributeValue("node");
2520: if (nodeName != null)
2521: collection.setElementNodeName(nodeName);
2522:
2523: }
2524:
2525: if (collection.isOneToMany() && !collection.isInverse()
2526: && !collection.getKey().isNullable()) {
2527: // for non-inverse one-to-many, with a not-null fk, add a backref!
2528: String entityName = ((OneToMany) collection.getElement())
2529: .getReferencedEntityName();
2530: PersistentClass referenced = mappings.getClass(entityName);
2531: Backref prop = new Backref();
2532: prop.setName('_' + node.attributeValue("name") + "Backref");
2533: prop.setUpdateable(false);
2534: prop.setSelectable(false);
2535: prop.setCollectionRole(collection.getRole());
2536: prop.setEntityName(collection.getOwner().getEntityName());
2537: prop.setValue(collection.getKey());
2538: referenced.addProperty(prop);
2539: }
2540: }
2541:
2542: private static void bindManyToManySubelements(
2543: Collection collection, Element manyToManyNode,
2544: Mappings model) throws MappingException {
2545: // Bind the where
2546: Attribute where = manyToManyNode.attribute("where");
2547: String whereCondition = where == null ? null : where.getValue();
2548: collection.setManyToManyWhere(whereCondition);
2549:
2550: // Bind the order-by
2551: Attribute order = manyToManyNode.attribute("order-by");
2552: String orderFragment = order == null ? null : order.getValue();
2553: collection.setManyToManyOrdering(orderFragment);
2554:
2555: // Bind the filters
2556: Iterator filters = manyToManyNode.elementIterator("filter");
2557: if ((filters.hasNext() || whereCondition != null)
2558: && collection.getFetchMode() == FetchMode.JOIN
2559: && collection.getElement().getFetchMode() != FetchMode.JOIN) {
2560: throw new MappingException(
2561: "many-to-many defining filter or where without join fetching "
2562: + "not valid within collection using join fetching ["
2563: + collection.getRole() + "]");
2564: }
2565: while (filters.hasNext()) {
2566: final Element filterElement = (Element) filters.next();
2567: final String name = filterElement.attributeValue("name");
2568: String condition = filterElement.getTextTrim();
2569: if (StringHelper.isEmpty(condition))
2570: condition = filterElement.attributeValue("condition");
2571: if (StringHelper.isEmpty(condition)) {
2572: condition = model.getFilterDefinition(name)
2573: .getDefaultFilterCondition();
2574: }
2575: if (condition == null) {
2576: throw new MappingException(
2577: "no filter condition found for filter: " + name);
2578: }
2579: log.debug("Applying many-to-many filter [" + name
2580: + "] as [" + condition + "] to role ["
2581: + collection.getRole() + "]");
2582: collection.addManyToManyFilter(name, condition);
2583: }
2584: }
2585:
2586: public static final FlushMode getFlushMode(String flushMode) {
2587: if (flushMode == null) {
2588: return null;
2589: } else if ("auto".equals(flushMode)) {
2590: return FlushMode.AUTO;
2591: } else if ("commit".equals(flushMode)) {
2592: return FlushMode.COMMIT;
2593: } else if ("never".equals(flushMode)) {
2594: return FlushMode.NEVER;
2595: } else if ("manual".equals(flushMode)) {
2596: return FlushMode.MANUAL;
2597: } else if ("always".equals(flushMode)) {
2598: return FlushMode.ALWAYS;
2599: } else {
2600: throw new MappingException("unknown flushmode");
2601: }
2602: }
2603:
2604: private static void bindNamedQuery(Element queryElem, String path,
2605: Mappings mappings) {
2606: String queryName = queryElem.attributeValue("name");
2607: if (path != null)
2608: queryName = path + '.' + queryName;
2609: String query = queryElem.getText();
2610: log.debug("Named query: " + queryName + " -> " + query);
2611:
2612: boolean cacheable = "true".equals(queryElem
2613: .attributeValue("cacheable"));
2614: String region = queryElem.attributeValue("cache-region");
2615: Attribute tAtt = queryElem.attribute("timeout");
2616: Integer timeout = tAtt == null ? null : new Integer(tAtt
2617: .getValue());
2618: Attribute fsAtt = queryElem.attribute("fetch-size");
2619: Integer fetchSize = fsAtt == null ? null : new Integer(fsAtt
2620: .getValue());
2621: Attribute roAttr = queryElem.attribute("read-only");
2622: boolean readOnly = roAttr != null
2623: && "true".equals(roAttr.getValue());
2624: Attribute cacheModeAtt = queryElem.attribute("cache-mode");
2625: String cacheMode = cacheModeAtt == null ? null : cacheModeAtt
2626: .getValue();
2627: Attribute cmAtt = queryElem.attribute("comment");
2628: String comment = cmAtt == null ? null : cmAtt.getValue();
2629:
2630: NamedQueryDefinition namedQuery = new NamedQueryDefinition(
2631: query, cacheable, region, timeout, fetchSize,
2632: getFlushMode(queryElem.attributeValue("flush-mode")),
2633: getCacheMode(cacheMode), readOnly, comment,
2634: getParameterTypes(queryElem));
2635:
2636: mappings.addQuery(queryName, namedQuery);
2637: }
2638:
2639: public static CacheMode getCacheMode(String cacheMode) {
2640: if (cacheMode == null)
2641: return null;
2642: if ("get".equals(cacheMode))
2643: return CacheMode.GET;
2644: if ("ignore".equals(cacheMode))
2645: return CacheMode.IGNORE;
2646: if ("normal".equals(cacheMode))
2647: return CacheMode.NORMAL;
2648: if ("put".equals(cacheMode))
2649: return CacheMode.PUT;
2650: if ("refresh".equals(cacheMode))
2651: return CacheMode.REFRESH;
2652: throw new MappingException("Unknown Cache Mode: " + cacheMode);
2653: }
2654:
2655: public static java.util.Map getParameterTypes(Element queryElem) {
2656: java.util.Map result = new SequencedHashMap();
2657: Iterator iter = queryElem.elementIterator("query-param");
2658: while (iter.hasNext()) {
2659: Element element = (Element) iter.next();
2660: result.put(element.attributeValue("name"), element
2661: .attributeValue("type"));
2662: }
2663: return result;
2664: }
2665:
2666: private static void bindResultSetMappingDefinition(
2667: Element resultSetElem, String path, Mappings mappings) {
2668: mappings.addSecondPass(new ResultSetMappingSecondPass(
2669: resultSetElem, path, mappings));
2670: }
2671:
2672: private static void bindNamedSQLQuery(Element queryElem,
2673: String path, Mappings mappings) {
2674: mappings.addSecondPass(new NamedSQLQuerySecondPass(queryElem,
2675: path, mappings));
2676: }
2677:
2678: private static String getPropertyName(Element node) {
2679: return node.attributeValue("name");
2680: }
2681:
2682: private static PersistentClass getSuperclass(Mappings mappings,
2683: Element subnode) throws MappingException {
2684: String extendsName = subnode.attributeValue("extends");
2685: PersistentClass super Model = mappings.getClass(extendsName);
2686: if (super Model == null) {
2687: String qualifiedExtendsName = getClassName(extendsName,
2688: mappings);
2689: super Model = mappings.getClass(qualifiedExtendsName);
2690: }
2691:
2692: if (super Model == null) {
2693: throw new MappingException("Cannot extend unmapped class "
2694: + extendsName);
2695: }
2696: return super Model;
2697: }
2698:
2699: static class CollectionSecondPass extends
2700: org.hibernate.cfg.CollectionSecondPass {
2701: Element node;
2702:
2703: CollectionSecondPass(Element node, Mappings mappings,
2704: Collection collection, java.util.Map inheritedMetas) {
2705: super (mappings, collection, inheritedMetas);
2706: this .node = node;
2707: }
2708:
2709: public void secondPass(java.util.Map persistentClasses,
2710: java.util.Map inheritedMetas) throws MappingException {
2711: HbmBinder.bindCollectionSecondPass(node, collection,
2712: persistentClasses, mappings, inheritedMetas);
2713: }
2714: }
2715:
2716: static class IdentifierCollectionSecondPass extends
2717: CollectionSecondPass {
2718: IdentifierCollectionSecondPass(Element node, Mappings mappings,
2719: Collection collection, java.util.Map inheritedMetas) {
2720: super (node, mappings, collection, inheritedMetas);
2721: }
2722:
2723: public void secondPass(java.util.Map persistentClasses,
2724: java.util.Map inheritedMetas) throws MappingException {
2725: HbmBinder.bindIdentifierCollectionSecondPass(node,
2726: (IdentifierCollection) collection,
2727: persistentClasses, mappings, inheritedMetas);
2728: }
2729:
2730: }
2731:
2732: static class MapSecondPass extends CollectionSecondPass {
2733: MapSecondPass(Element node, Mappings mappings, Map collection,
2734: java.util.Map inheritedMetas) {
2735: super (node, mappings, collection, inheritedMetas);
2736: }
2737:
2738: public void secondPass(java.util.Map persistentClasses,
2739: java.util.Map inheritedMetas) throws MappingException {
2740: HbmBinder.bindMapSecondPass(node, (Map) collection,
2741: persistentClasses, mappings, inheritedMetas);
2742: }
2743:
2744: }
2745:
2746: static class ManyToOneSecondPass implements SecondPass {
2747: private final ManyToOne manyToOne;
2748:
2749: ManyToOneSecondPass(ManyToOne manyToOne) {
2750: this .manyToOne = manyToOne;
2751: }
2752:
2753: public void doSecondPass(java.util.Map persistentClasses)
2754: throws MappingException {
2755: manyToOne.createPropertyRefConstraints(persistentClasses);
2756: }
2757:
2758: }
2759:
2760: static class ListSecondPass extends CollectionSecondPass {
2761: ListSecondPass(Element node, Mappings mappings,
2762: List collection, java.util.Map inheritedMetas) {
2763: super (node, mappings, collection, inheritedMetas);
2764: }
2765:
2766: public void secondPass(java.util.Map persistentClasses,
2767: java.util.Map inheritedMetas) throws MappingException {
2768: HbmBinder.bindListSecondPass(node, (List) collection,
2769: persistentClasses, mappings, inheritedMetas);
2770: }
2771:
2772: }
2773:
2774: // This inner class implements a case statement....perhaps im being a bit over-clever here
2775: abstract static class CollectionType {
2776: private String xmlTag;
2777:
2778: public abstract Collection create(Element node, String path,
2779: PersistentClass owner, Mappings mappings,
2780: java.util.Map inheritedMetas) throws MappingException;
2781:
2782: CollectionType(String xmlTag) {
2783: this .xmlTag = xmlTag;
2784: }
2785:
2786: public String toString() {
2787: return xmlTag;
2788: }
2789:
2790: private static final CollectionType MAP = new CollectionType(
2791: "map") {
2792: public Collection create(Element node, String path,
2793: PersistentClass owner, Mappings mappings,
2794: java.util.Map inheritedMetas)
2795: throws MappingException {
2796: Map map = new Map(owner);
2797: bindCollection(node, map, owner.getEntityName(), path,
2798: mappings, inheritedMetas);
2799: return map;
2800: }
2801: };
2802: private static final CollectionType SET = new CollectionType(
2803: "set") {
2804: public Collection create(Element node, String path,
2805: PersistentClass owner, Mappings mappings,
2806: java.util.Map inheritedMetas)
2807: throws MappingException {
2808: Set set = new Set(owner);
2809: bindCollection(node, set, owner.getEntityName(), path,
2810: mappings, inheritedMetas);
2811: return set;
2812: }
2813: };
2814: private static final CollectionType LIST = new CollectionType(
2815: "list") {
2816: public Collection create(Element node, String path,
2817: PersistentClass owner, Mappings mappings,
2818: java.util.Map inheritedMetas)
2819: throws MappingException {
2820: List list = new List(owner);
2821: bindCollection(node, list, owner.getEntityName(), path,
2822: mappings, inheritedMetas);
2823: return list;
2824: }
2825: };
2826: private static final CollectionType BAG = new CollectionType(
2827: "bag") {
2828: public Collection create(Element node, String path,
2829: PersistentClass owner, Mappings mappings,
2830: java.util.Map inheritedMetas)
2831: throws MappingException {
2832: Bag bag = new Bag(owner);
2833: bindCollection(node, bag, owner.getEntityName(), path,
2834: mappings, inheritedMetas);
2835: return bag;
2836: }
2837: };
2838: private static final CollectionType IDBAG = new CollectionType(
2839: "idbag") {
2840: public Collection create(Element node, String path,
2841: PersistentClass owner, Mappings mappings,
2842: java.util.Map inheritedMetas)
2843: throws MappingException {
2844: IdentifierBag bag = new IdentifierBag(owner);
2845: bindCollection(node, bag, owner.getEntityName(), path,
2846: mappings, inheritedMetas);
2847: return bag;
2848: }
2849: };
2850: private static final CollectionType ARRAY = new CollectionType(
2851: "array") {
2852: public Collection create(Element node, String path,
2853: PersistentClass owner, Mappings mappings,
2854: java.util.Map inheritedMetas)
2855: throws MappingException {
2856: Array array = new Array(owner);
2857: bindArray(node, array, owner.getEntityName(), path,
2858: mappings, inheritedMetas);
2859: return array;
2860: }
2861: };
2862: private static final CollectionType PRIMITIVE_ARRAY = new CollectionType(
2863: "primitive-array") {
2864: public Collection create(Element node, String path,
2865: PersistentClass owner, Mappings mappings,
2866: java.util.Map inheritedMetas)
2867: throws MappingException {
2868: PrimitiveArray array = new PrimitiveArray(owner);
2869: bindArray(node, array, owner.getEntityName(), path,
2870: mappings, inheritedMetas);
2871: return array;
2872: }
2873: };
2874: private static final HashMap INSTANCES = new HashMap();
2875:
2876: static {
2877: INSTANCES.put(MAP.toString(), MAP);
2878: INSTANCES.put(BAG.toString(), BAG);
2879: INSTANCES.put(IDBAG.toString(), IDBAG);
2880: INSTANCES.put(SET.toString(), SET);
2881: INSTANCES.put(LIST.toString(), LIST);
2882: INSTANCES.put(ARRAY.toString(), ARRAY);
2883: INSTANCES.put(PRIMITIVE_ARRAY.toString(), PRIMITIVE_ARRAY);
2884: }
2885:
2886: public static CollectionType collectionTypeFromString(
2887: String xmlTagName) {
2888: return (CollectionType) INSTANCES.get(xmlTagName);
2889: }
2890: }
2891:
2892: private static int getOptimisticLockMode(Attribute olAtt)
2893: throws MappingException {
2894:
2895: if (olAtt == null)
2896: return Versioning.OPTIMISTIC_LOCK_VERSION;
2897: String olMode = olAtt.getValue();
2898: if (olMode == null || "version".equals(olMode)) {
2899: return Versioning.OPTIMISTIC_LOCK_VERSION;
2900: } else if ("dirty".equals(olMode)) {
2901: return Versioning.OPTIMISTIC_LOCK_DIRTY;
2902: } else if ("all".equals(olMode)) {
2903: return Versioning.OPTIMISTIC_LOCK_ALL;
2904: } else if ("none".equals(olMode)) {
2905: return Versioning.OPTIMISTIC_LOCK_NONE;
2906: } else {
2907: throw new MappingException(
2908: "Unsupported optimistic-lock style: " + olMode);
2909: }
2910: }
2911:
2912: private static final java.util.Map getMetas(Element node,
2913: java.util.Map inheritedMeta) {
2914: return getMetas(node, inheritedMeta, false);
2915: }
2916:
2917: public static final java.util.Map getMetas(Element node,
2918: java.util.Map inheritedMeta, boolean onlyInheritable) {
2919: java.util.Map map = new HashMap();
2920: map.putAll(inheritedMeta);
2921:
2922: Iterator iter = node.elementIterator("meta");
2923: while (iter.hasNext()) {
2924: Element metaNode = (Element) iter.next();
2925: boolean inheritable = Boolean.valueOf(
2926: metaNode.attributeValue("inherit")).booleanValue();
2927: if (onlyInheritable & !inheritable) {
2928: continue;
2929: }
2930: String name = metaNode.attributeValue("attribute");
2931:
2932: MetaAttribute meta = (MetaAttribute) map.get(name);
2933: MetaAttribute inheritedAttribute = (MetaAttribute) inheritedMeta
2934: .get(name);
2935: if (meta == null) {
2936: meta = new MetaAttribute(name);
2937: map.put(name, meta);
2938: } else if (meta == inheritedAttribute) { // overriding inherited meta attribute. HBX-621 & HBX-793
2939: meta = new MetaAttribute(name);
2940: map.put(name, meta);
2941: }
2942: meta.addValue(metaNode.getText());
2943: }
2944: return map;
2945: }
2946:
2947: public static String getEntityName(Element elem, Mappings model) {
2948: String entityName = elem.attributeValue("entity-name");
2949: return entityName == null ? getClassName(elem
2950: .attribute("class"), model) : entityName;
2951: }
2952:
2953: private static String getClassName(Attribute att, Mappings model) {
2954: if (att == null)
2955: return null;
2956: return getClassName(att.getValue(), model);
2957: }
2958:
2959: public static String getClassName(String unqualifiedName,
2960: Mappings model) {
2961: return getClassName(unqualifiedName, model.getDefaultPackage());
2962: }
2963:
2964: public static String getClassName(String unqualifiedName,
2965: String defaultPackage) {
2966: if (unqualifiedName == null)
2967: return null;
2968: if (unqualifiedName.indexOf('.') < 0 && defaultPackage != null) {
2969: return defaultPackage + '.' + unqualifiedName;
2970: }
2971: return unqualifiedName;
2972: }
2973:
2974: private static void parseFilterDef(Element element,
2975: Mappings mappings) {
2976: String name = element.attributeValue("name");
2977: log.debug("Parsing filter-def [" + name + "]");
2978: String defaultCondition = element.getTextTrim();
2979: if (StringHelper.isEmpty(defaultCondition)) {
2980: defaultCondition = element.attributeValue("condition");
2981: }
2982: HashMap paramMappings = new HashMap();
2983: Iterator params = element.elementIterator("filter-param");
2984: while (params.hasNext()) {
2985: final Element param = (Element) params.next();
2986: final String paramName = param.attributeValue("name");
2987: final String paramType = param.attributeValue("type");
2988: log.debug("adding filter parameter : " + paramName + " -> "
2989: + paramType);
2990: final Type heuristicType = TypeFactory
2991: .heuristicType(paramType);
2992: log.debug("parameter heuristic type : " + heuristicType);
2993: paramMappings.put(paramName, heuristicType);
2994: }
2995: log.debug("Parsed filter-def [" + name + "]");
2996: FilterDefinition def = new FilterDefinition(name,
2997: defaultCondition, paramMappings);
2998: mappings.addFilterDefinition(def);
2999: }
3000:
3001: private static void parseFilter(Element filterElement,
3002: Filterable filterable, Mappings model) {
3003: final String name = filterElement.attributeValue("name");
3004: String condition = filterElement.getTextTrim();
3005: if (StringHelper.isEmpty(condition)) {
3006: condition = filterElement.attributeValue("condition");
3007: }
3008: //TODO: bad implementation, cos it depends upon ordering of mapping doc
3009: // fixing this requires that Collection/PersistentClass gain access
3010: // to the Mappings reference from Configuration (or the filterDefinitions
3011: // map directly) sometime during Configuration.buildSessionFactory
3012: // (after all the types/filter-defs are known and before building
3013: // persisters).
3014: if (StringHelper.isEmpty(condition)) {
3015: condition = model.getFilterDefinition(name)
3016: .getDefaultFilterCondition();
3017: }
3018: if (condition == null) {
3019: throw new MappingException(
3020: "no filter condition found for filter: " + name);
3021: }
3022: log.debug("Applying filter [" + name + "] as [" + condition
3023: + "]");
3024: filterable.addFilter(name, condition);
3025: }
3026:
3027: private static String getSubselect(Element element) {
3028: String subselect = element.attributeValue("subselect");
3029: if (subselect != null) {
3030: return subselect;
3031: } else {
3032: Element subselectElement = element.element("subselect");
3033: return subselectElement == null ? null : subselectElement
3034: .getText();
3035: }
3036: }
3037:
3038: /**
3039: * For the given document, locate all extends attributes which refer to
3040: * entities (entity-name or class-name) not defined within said document.
3041: *
3042: * @param doc The document to check
3043: * @param mappings The already processed mappings.
3044: * @return The list of unresolved extends names.
3045: */
3046: public static java.util.List getExtendsNeeded(Document doc,
3047: Mappings mappings) {
3048: java.util.List extendz = new ArrayList();
3049: Iterator[] subclasses = new Iterator[3];
3050: final Element hmNode = doc.getRootElement();
3051:
3052: Attribute packNode = hmNode.attribute("package");
3053: final String packageName = packNode == null ? null : packNode
3054: .getValue();
3055: if (packageName != null) {
3056: mappings.setDefaultPackage(packageName);
3057: }
3058:
3059: // first, iterate over all elements capable of defining an extends attribute
3060: // collecting all found extends references if they cannot be resolved
3061: // against the already processed mappings.
3062: subclasses[0] = hmNode.elementIterator("subclass");
3063: subclasses[1] = hmNode.elementIterator("joined-subclass");
3064: subclasses[2] = hmNode.elementIterator("union-subclass");
3065:
3066: Iterator iterator = new JoinedIterator(subclasses);
3067: while (iterator.hasNext()) {
3068: final Element element = (Element) iterator.next();
3069: final String extendsName = element
3070: .attributeValue("extends");
3071: // mappings might contain either the "raw" extends name (in the case of
3072: // an entity-name mapping) or a FQN (in the case of a POJO mapping).
3073: if (mappings.getClass(extendsName) == null
3074: && mappings.getClass(getClassName(extendsName,
3075: mappings)) == null) {
3076: extendz.add(extendsName);
3077: }
3078: }
3079:
3080: if (!extendz.isEmpty()) {
3081: // we found some extends attributes referencing entities which were
3082: // not already processed. here we need to locate all entity-names
3083: // and class-names contained in this document itself, making sure
3084: // that these get removed from the extendz list such that only
3085: // extends names which require us to delay processing (i.e.
3086: // external to this document and not yet processed) are contained
3087: // in the returned result
3088: final java.util.Set set = new HashSet(extendz);
3089: EntityElementHandler handler = new EntityElementHandler() {
3090: public void handleEntity(String entityName,
3091: String className, Mappings mappings) {
3092: if (entityName != null) {
3093: set.remove(entityName);
3094: } else {
3095: String fqn = getClassName(className,
3096: packageName);
3097: set.remove(fqn);
3098: if (packageName != null) {
3099: set.remove(StringHelper.unqualify(fqn));
3100: }
3101: }
3102: }
3103: };
3104: recognizeEntities(mappings, hmNode, handler);
3105: extendz.clear();
3106: extendz.addAll(set);
3107: }
3108:
3109: return extendz;
3110: }
3111:
3112: /**
3113: * Given an entity-containing-element (startNode) recursively locate all
3114: * entity names defined within that element.
3115: *
3116: * @param mappings The already processed mappings
3117: * @param startNode The containing element
3118: * @param handler The thing that knows what to do whenever we recognize an
3119: * entity-name
3120: */
3121: private static void recognizeEntities(Mappings mappings,
3122: final Element startNode, EntityElementHandler handler) {
3123: Iterator[] classes = new Iterator[4];
3124: classes[0] = startNode.elementIterator("class");
3125: classes[1] = startNode.elementIterator("subclass");
3126: classes[2] = startNode.elementIterator("joined-subclass");
3127: classes[3] = startNode.elementIterator("union-subclass");
3128:
3129: Iterator classIterator = new JoinedIterator(classes);
3130: while (classIterator.hasNext()) {
3131: Element element = (Element) classIterator.next();
3132: handler.handleEntity(element.attributeValue("entity-name"),
3133: element.attributeValue("name"), mappings);
3134: recognizeEntities(mappings, element, handler);
3135: }
3136: }
3137:
3138: private static interface EntityElementHandler {
3139: public void handleEntity(String entityName, String className,
3140: Mappings mappings);
3141: }
3142: }
|