Java tutorial
// $Id: JPAOverridenAnnotationReader.java 18522 2010-01-12 20:14:31Z hardy.ferentschik $ /* * Hibernate, Relational Persistence for Idiomatic Java * * Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as * indicated by the @author tags or express copyright attribution * statements applied by the authors. All third-party contributions are * distributed under license by Red Hat Middleware LLC. * * This copyrighted material is made available to anyone wishing to use, modify, * copy, or redistribute it subject to the terms and conditions of the GNU * Lesser General Public License, as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this distribution; if not, write to: * Free Software Foundation, Inc. * 51 Franklin Street, Fifth Floor * Boston, MA 02110-1301 USA */ package org.hibernate.cfg.annotations.reflection; import java.beans.Introspector; import java.lang.annotation.Annotation; import java.lang.reflect.AccessibleObject; import java.lang.reflect.AnnotatedElement; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import javax.persistence.Access; import javax.persistence.AccessType; import javax.persistence.AssociationOverride; import javax.persistence.AssociationOverrides; import javax.persistence.AttributeOverride; import javax.persistence.AttributeOverrides; import javax.persistence.Basic; import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.ColumnResult; import javax.persistence.DiscriminatorColumn; import javax.persistence.DiscriminatorType; import javax.persistence.DiscriminatorValue; import javax.persistence.Embeddable; import javax.persistence.Embedded; import javax.persistence.EmbeddedId; import javax.persistence.Entity; import javax.persistence.EntityListeners; import javax.persistence.EntityResult; import javax.persistence.EnumType; import javax.persistence.Enumerated; import javax.persistence.ExcludeDefaultListeners; import javax.persistence.ExcludeSuperclassListeners; import javax.persistence.FetchType; import javax.persistence.FieldResult; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.IdClass; import javax.persistence.Inheritance; import javax.persistence.InheritanceType; import javax.persistence.JoinColumn; import javax.persistence.JoinColumns; import javax.persistence.JoinTable; import javax.persistence.Lob; import javax.persistence.ManyToMany; import javax.persistence.ManyToOne; import javax.persistence.MapKey; import javax.persistence.MappedSuperclass; import javax.persistence.NamedNativeQueries; import javax.persistence.NamedNativeQuery; import javax.persistence.NamedQueries; import javax.persistence.NamedQuery; import javax.persistence.OneToMany; import javax.persistence.OneToOne; import javax.persistence.OrderBy; import javax.persistence.PostLoad; import javax.persistence.PostPersist; import javax.persistence.PostRemove; import javax.persistence.PostUpdate; import javax.persistence.PrePersist; import javax.persistence.PreRemove; import javax.persistence.PreUpdate; import javax.persistence.PrimaryKeyJoinColumn; import javax.persistence.PrimaryKeyJoinColumns; import javax.persistence.QueryHint; import javax.persistence.SecondaryTable; import javax.persistence.SecondaryTables; import javax.persistence.SequenceGenerator; import javax.persistence.SqlResultSetMapping; import javax.persistence.SqlResultSetMappings; import javax.persistence.Table; import javax.persistence.TableGenerator; import javax.persistence.Temporal; import javax.persistence.TemporalType; import javax.persistence.Transient; import javax.persistence.UniqueConstraint; import javax.persistence.Version; import javax.persistence.ElementCollection; import org.dom4j.Attribute; import org.dom4j.Element; import org.hibernate.AnnotationException; import org.hibernate.annotations.CollectionOfElements; import org.hibernate.annotations.Columns; import org.hibernate.annotations.common.annotationfactory.AnnotationDescriptor; import org.hibernate.annotations.common.annotationfactory.AnnotationFactory; import org.hibernate.annotations.common.reflection.AnnotationReader; import org.hibernate.annotations.common.reflection.Filter; import org.hibernate.annotations.common.reflection.ReflectionUtil; import org.hibernate.util.ReflectHelper; import org.hibernate.util.StringHelper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Encapsulates the overriding of Java annotations from an EJB 3.0 descriptor. * * @author Paolo Perrotta * @author Davide Marchignoli * @author Emmanuel Bernard * @author Hardy Ferentschik */ @SuppressWarnings("unchecked") public class JPAOverridenAnnotationReader implements AnnotationReader { private Logger log = LoggerFactory.getLogger(JPAOverridenAnnotationReader.class); private static final Map<Class, String> annotationToXml; private static final String SCHEMA_VALIDATION = "Activate schema validation for more information"; private static final Filter FILTER = new Filter() { public boolean returnStatic() { return false; } public boolean returnTransient() { return false; } }; static { annotationToXml = new HashMap<Class, String>(); annotationToXml.put(Entity.class, "entity"); annotationToXml.put(MappedSuperclass.class, "mapped-superclass"); annotationToXml.put(Embeddable.class, "embeddable"); annotationToXml.put(Table.class, "table"); annotationToXml.put(SecondaryTable.class, "secondary-table"); annotationToXml.put(SecondaryTables.class, "secondary-table"); annotationToXml.put(PrimaryKeyJoinColumn.class, "primary-key-join-column"); annotationToXml.put(PrimaryKeyJoinColumns.class, "primary-key-join-column"); annotationToXml.put(IdClass.class, "id-class"); annotationToXml.put(Inheritance.class, "inheritance"); annotationToXml.put(DiscriminatorValue.class, "discriminator-value"); annotationToXml.put(DiscriminatorColumn.class, "discriminator-column"); annotationToXml.put(SequenceGenerator.class, "sequence-generator"); annotationToXml.put(TableGenerator.class, "table-generator"); annotationToXml.put(NamedQuery.class, "named-query"); annotationToXml.put(NamedQueries.class, "named-query"); annotationToXml.put(NamedNativeQuery.class, "named-native-query"); annotationToXml.put(NamedNativeQueries.class, "named-native-query"); annotationToXml.put(SqlResultSetMapping.class, "sql-result-set-mapping"); annotationToXml.put(SqlResultSetMappings.class, "sql-result-set-mapping"); annotationToXml.put(ExcludeDefaultListeners.class, "exclude-default-listeners"); annotationToXml.put(ExcludeSuperclassListeners.class, "exclude-superclass-listeners"); annotationToXml.put(AccessType.class, "access"); annotationToXml.put(AttributeOverride.class, "attribute-override"); annotationToXml.put(AttributeOverrides.class, "attribute-override"); annotationToXml.put(AttributeOverride.class, "association-override"); annotationToXml.put(AttributeOverrides.class, "association-override"); annotationToXml.put(Id.class, "id"); annotationToXml.put(EmbeddedId.class, "embedded-id"); annotationToXml.put(GeneratedValue.class, "generated-value"); annotationToXml.put(Column.class, "column"); annotationToXml.put(Columns.class, "column"); annotationToXml.put(Temporal.class, "temporal"); annotationToXml.put(Lob.class, "lob"); annotationToXml.put(Enumerated.class, "enumerated"); annotationToXml.put(Version.class, "version"); annotationToXml.put(Transient.class, "transient"); annotationToXml.put(Basic.class, "basic"); annotationToXml.put(Embedded.class, "embedded"); annotationToXml.put(ManyToOne.class, "many-to-one"); annotationToXml.put(OneToOne.class, "one-to-one"); annotationToXml.put(OneToMany.class, "one-to-many"); annotationToXml.put(ManyToMany.class, "many-to-many"); annotationToXml.put(JoinTable.class, "join-table"); annotationToXml.put(JoinColumn.class, "join-column"); annotationToXml.put(JoinColumns.class, "join-column"); annotationToXml.put(MapKey.class, "map-key"); annotationToXml.put(OrderBy.class, "order-by"); annotationToXml.put(EntityListeners.class, "entity-listeners"); annotationToXml.put(PrePersist.class, "pre-persist"); annotationToXml.put(PreRemove.class, "pre-remove"); annotationToXml.put(PreUpdate.class, "pre-update"); annotationToXml.put(PostPersist.class, "post-persist"); annotationToXml.put(PostRemove.class, "post-remove"); annotationToXml.put(PostUpdate.class, "post-update"); annotationToXml.put(PostLoad.class, "post-load"); } private XMLContext xmlContext; private String className; private String propertyName; private PropertyType propertyType; private transient Annotation[] annotations; private transient Map<Class, Annotation> annotationsMap; private static final String WORD_SEPARATOR = "-"; private transient List<Element> elementsForProperty; private AccessibleObject mirroredAttribute; private final AnnotatedElement element; private enum PropertyType { PROPERTY, FIELD, METHOD } public JPAOverridenAnnotationReader(AnnotatedElement el, XMLContext xmlContext) { this.element = el; this.xmlContext = xmlContext; if (el instanceof Class) { Class clazz = (Class) el; className = clazz.getName(); } else if (el instanceof Field) { Field field = (Field) el; className = field.getDeclaringClass().getName(); propertyName = field.getName(); propertyType = PropertyType.FIELD; String expectedGetter = "get" + Character.toUpperCase(propertyName.charAt(0)) + propertyName.substring(1); try { mirroredAttribute = field.getDeclaringClass().getDeclaredMethod(expectedGetter); } catch (NoSuchMethodException e) { //no method } } else if (el instanceof Method) { Method method = (Method) el; className = method.getDeclaringClass().getName(); propertyName = method.getName(); if (ReflectionUtil.isProperty(method, null, //this is yukky!! we'd rather get the TypeEnvironment() FILTER)) { if (propertyName.startsWith("get")) { propertyName = Introspector.decapitalize(propertyName.substring("get".length())); } else if (propertyName.startsWith("is")) { propertyName = Introspector.decapitalize(propertyName.substring("is".length())); } else { throw new RuntimeException("Method " + propertyName + " is not a property getter"); } propertyType = PropertyType.PROPERTY; try { mirroredAttribute = method.getDeclaringClass().getDeclaredField(propertyName); } catch (NoSuchFieldException e) { //no method } } else { propertyType = PropertyType.METHOD; } } else { className = null; propertyName = null; } } public <T extends Annotation> T getAnnotation(Class<T> annotationType) { initAnnotations(); return (T) annotationsMap.get(annotationType); } public <T extends Annotation> boolean isAnnotationPresent(Class<T> annotationType) { initAnnotations(); return (T) annotationsMap.get(annotationType) != null; } public Annotation[] getAnnotations() { initAnnotations(); return annotations; } /* * The idea is to create annotation proxies for the xml configuration elements. Using this proxy annotations together * with the {@code JPAMetadataprovider} allows to handle xml configuration the same way as annotation configuration. */ private void initAnnotations() { if (annotations == null) { XMLContext.Default defaults = xmlContext.getDefault(className); if (className != null && propertyName == null) { //is a class Element tree = xmlContext.getXMLTree(className); Annotation[] annotations = getJavaAnnotations(); List<Annotation> annotationList = new ArrayList<Annotation>(annotations.length + 5); annotationsMap = new HashMap<Class, Annotation>(annotations.length + 5); for (Annotation annotation : annotations) { if (!annotationToXml.containsKey(annotation.annotationType())) { //unknown annotations are left over annotationList.add(annotation); } } addIfNotNull(annotationList, getEntity(tree, defaults)); addIfNotNull(annotationList, getMappedSuperclass(tree, defaults)); addIfNotNull(annotationList, getEmbeddable(tree, defaults)); addIfNotNull(annotationList, getTable(tree, defaults)); addIfNotNull(annotationList, getSecondaryTables(tree, defaults)); addIfNotNull(annotationList, getPrimaryKeyJoinColumns(tree, defaults)); addIfNotNull(annotationList, getIdClass(tree, defaults)); addIfNotNull(annotationList, getInheritance(tree, defaults)); addIfNotNull(annotationList, getDiscriminatorValue(tree, defaults)); addIfNotNull(annotationList, getDiscriminatorColumn(tree, defaults)); addIfNotNull(annotationList, getSequenceGenerator(tree, defaults)); addIfNotNull(annotationList, getTableGenerator(tree, defaults)); addIfNotNull(annotationList, getNamedQueries(tree, defaults)); addIfNotNull(annotationList, getNamedNativeQueries(tree, defaults)); addIfNotNull(annotationList, getSqlResultSetMappings(tree, defaults)); addIfNotNull(annotationList, getExcludeDefaultListeners(tree, defaults)); addIfNotNull(annotationList, getExcludeSuperclassListeners(tree, defaults)); addIfNotNull(annotationList, getAccessType(tree, defaults)); addIfNotNull(annotationList, getAttributeOverrides(tree, defaults)); addIfNotNull(annotationList, getAssociationOverrides(tree, defaults)); addIfNotNull(annotationList, getEntityListeners(tree, defaults)); //FIXME use annotationsMap rather than annotationList this will be faster since the annotation type is usually known at put() time this.annotations = annotationList.toArray(new Annotation[annotationList.size()]); for (Annotation ann : this.annotations) { annotationsMap.put(ann.annotationType(), ann); } checkForOrphanProperties(tree); } else if (className != null) { //&& propertyName != null ) { //always true but less confusing Element tree = xmlContext.getXMLTree(className); Annotation[] annotations = getJavaAnnotations(); List<Annotation> annotationList = new ArrayList<Annotation>(annotations.length + 5); annotationsMap = new HashMap<Class, Annotation>(annotations.length + 5); for (Annotation annotation : annotations) { if (!annotationToXml.containsKey(annotation.annotationType())) { //unknown annotations are left over annotationList.add(annotation); } } preCalculateElementsForProperty(tree); Transient transientAnn = getTransient(defaults); if (transientAnn != null) { annotationList.add(transientAnn); } else { if (defaults.canUseJavaAnnotations()) { Annotation annotation = getJavaAnnotation(Access.class); addIfNotNull(annotationList, annotation); } getId(annotationList, defaults); getEmbeddedId(annotationList, defaults); getEmbedded(annotationList, defaults); getBasic(annotationList, defaults); getVersion(annotationList, defaults); getAssociation(ManyToOne.class, annotationList, defaults); getAssociation(OneToOne.class, annotationList, defaults); getAssociation(OneToMany.class, annotationList, defaults); getAssociation(ManyToMany.class, annotationList, defaults); getElementCollection(annotationList, defaults); addIfNotNull(annotationList, getSequenceGenerator(elementsForProperty, defaults)); addIfNotNull(annotationList, getTableGenerator(elementsForProperty, defaults)); addIfNotNull(annotationList, getAttributeOverrides(elementsForProperty, defaults)); } processEventAnnotations(annotationList, defaults); //FIXME use annotationsMap rather than annotationList this will be faster since the annotation type is usually known at put() time this.annotations = annotationList.toArray(new Annotation[annotationList.size()]); for (Annotation ann : this.annotations) { annotationsMap.put(ann.annotationType(), ann); } } else { this.annotations = getJavaAnnotations(); annotationsMap = new HashMap<Class, Annotation>(annotations.length + 5); for (Annotation ann : this.annotations) { annotationsMap.put(ann.annotationType(), ann); } } } } private void checkForOrphanProperties(Element tree) { Class clazz; try { clazz = ReflectHelper.classForName(className, this.getClass()); } catch (ClassNotFoundException e) { return; //a primitive type most likely } Element element = tree != null ? tree.element("attributes") : null; //put entity.attributes elements if (element != null) { //precompute the list of properties //TODO is it really useful... Set<String> properties = new HashSet<String>(); for (Field field : clazz.getFields()) { properties.add(field.getName()); } for (Method method : clazz.getMethods()) { String name = method.getName(); if (name.startsWith("get")) { properties.add(Introspector.decapitalize(name.substring("get".length()))); } else if (name.startsWith("is")) { properties.add(Introspector.decapitalize(name.substring("is".length()))); } } for (Element subelement : (List<Element>) element.elements()) { String propertyName = subelement.attributeValue("name"); if (!properties.contains(propertyName)) { log.warn( "Property {} not found in class" + " but described in <mapping-file/> (possible typo error)", StringHelper.qualify(className, propertyName)); } } } } /** * Adds {@code annotation} to the list (only if it's not null) and then returns it. * * @param annotationList The list of annotations. * @param annotation The annotation to add to the list. * * @return The annotation which was added to the list or {@code null}. */ private Annotation addIfNotNull(List<Annotation> annotationList, Annotation annotation) { if (annotation != null) { annotationList.add(annotation); } return annotation; } //TODO mutualize the next 2 methods private Annotation getTableGenerator(List<Element> elementsForProperty, XMLContext.Default defaults) { for (Element element : elementsForProperty) { Element subelement = element != null ? element.element(annotationToXml.get(TableGenerator.class)) : null; if (subelement != null) { return buildTableGeneratorAnnotation(subelement, defaults); } } if (elementsForProperty.size() == 0 && defaults.canUseJavaAnnotations()) { return getJavaAnnotation(TableGenerator.class); } else { return null; } } private Annotation getSequenceGenerator(List<Element> elementsForProperty, XMLContext.Default defaults) { for (Element element : elementsForProperty) { Element subelement = element != null ? element.element(annotationToXml.get(SequenceGenerator.class)) : null; if (subelement != null) { return buildSequenceGeneratorAnnotation(subelement); } } if (elementsForProperty.size() == 0 && defaults.canUseJavaAnnotations()) { return getJavaAnnotation(SequenceGenerator.class); } else { return null; } } private void processEventAnnotations(List<Annotation> annotationList, XMLContext.Default defaults) { boolean eventElement = false; for (Element element : elementsForProperty) { String elementName = element.getName(); if ("pre-persist".equals(elementName)) { AnnotationDescriptor ad = new AnnotationDescriptor(PrePersist.class); annotationList.add(AnnotationFactory.create(ad)); eventElement = true; } else if ("pre-remove".equals(elementName)) { AnnotationDescriptor ad = new AnnotationDescriptor(PreRemove.class); annotationList.add(AnnotationFactory.create(ad)); eventElement = true; } else if ("pre-update".equals(elementName)) { AnnotationDescriptor ad = new AnnotationDescriptor(PreUpdate.class); annotationList.add(AnnotationFactory.create(ad)); eventElement = true; } else if ("post-persist".equals(elementName)) { AnnotationDescriptor ad = new AnnotationDescriptor(PostPersist.class); annotationList.add(AnnotationFactory.create(ad)); eventElement = true; } else if ("post-remove".equals(elementName)) { AnnotationDescriptor ad = new AnnotationDescriptor(PostRemove.class); annotationList.add(AnnotationFactory.create(ad)); eventElement = true; } else if ("post-update".equals(elementName)) { AnnotationDescriptor ad = new AnnotationDescriptor(PostUpdate.class); annotationList.add(AnnotationFactory.create(ad)); eventElement = true; } else if ("post-load".equals(elementName)) { AnnotationDescriptor ad = new AnnotationDescriptor(PostLoad.class); annotationList.add(AnnotationFactory.create(ad)); eventElement = true; } } if (!eventElement && defaults.canUseJavaAnnotations()) { Annotation ann = getJavaAnnotation(PrePersist.class); addIfNotNull(annotationList, ann); ann = getJavaAnnotation(PreRemove.class); addIfNotNull(annotationList, ann); ann = getJavaAnnotation(PreUpdate.class); addIfNotNull(annotationList, ann); ann = getJavaAnnotation(PostPersist.class); addIfNotNull(annotationList, ann); ann = getJavaAnnotation(PostRemove.class); addIfNotNull(annotationList, ann); ann = getJavaAnnotation(PostUpdate.class); addIfNotNull(annotationList, ann); ann = getJavaAnnotation(PostLoad.class); addIfNotNull(annotationList, ann); } } private EntityListeners getEntityListeners(Element tree, XMLContext.Default defaults) { Element element = tree != null ? tree.element("entity-listeners") : null; if (element != null) { List<Class> entityListenerClasses = new ArrayList<Class>(); for (Element subelement : (List<Element>) element.elements("entity-listener")) { String className = subelement.attributeValue("class"); try { entityListenerClasses.add(ReflectHelper .classForName(XMLContext.buildSafeClassName(className, defaults), this.getClass())); } catch (ClassNotFoundException e) { throw new AnnotationException("Unable to find " + element.getPath() + ".class: " + className, e); } } AnnotationDescriptor ad = new AnnotationDescriptor(EntityListeners.class); ad.setValue("value", entityListenerClasses.toArray(new Class[entityListenerClasses.size()])); return AnnotationFactory.create(ad); } else if (defaults.canUseJavaAnnotations()) { return getJavaAnnotation(EntityListeners.class); } else { return null; } } private JoinTable overridesDefaultsInJoinTable(Annotation annotation, XMLContext.Default defaults) { //no element but might have some default or some annotation boolean defaultToJoinTable = !(isJavaAnnotationPresent(JoinColumn.class) || isJavaAnnotationPresent(JoinColumns.class)); final Class<? extends Annotation> annotationClass = annotation.annotationType(); defaultToJoinTable = defaultToJoinTable && ((annotationClass == ManyToMany.class && StringHelper.isEmpty(((ManyToMany) annotation).mappedBy())) || (annotationClass == OneToMany.class && StringHelper.isEmpty(((OneToMany) annotation).mappedBy())) || (annotationClass == CollectionOfElements.class) //legacy Hibernate || (annotationClass == ElementCollection.class)); final Class<JoinTable> annotationType = JoinTable.class; if (defaultToJoinTable && (StringHelper.isNotEmpty(defaults.getCatalog()) || StringHelper.isNotEmpty(defaults.getSchema()))) { AnnotationDescriptor ad = new AnnotationDescriptor(annotationType); if (defaults.canUseJavaAnnotations()) { JoinTable table = getJavaAnnotation(annotationType); if (table != null) { ad.setValue("name", table.name()); ad.setValue("schema", table.schema()); ad.setValue("catalog", table.catalog()); ad.setValue("uniqueConstraints", table.uniqueConstraints()); ad.setValue("joinColumns", table.joinColumns()); ad.setValue("inverseJoinColumns", table.inverseJoinColumns()); } } if (StringHelper.isEmpty((String) ad.valueOf("schema")) && StringHelper.isNotEmpty(defaults.getSchema())) { ad.setValue("schema", defaults.getSchema()); } if (StringHelper.isEmpty((String) ad.valueOf("catalog")) && StringHelper.isNotEmpty(defaults.getCatalog())) { ad.setValue("catalog", defaults.getCatalog()); } return AnnotationFactory.create(ad); } else if (defaults.canUseJavaAnnotations()) { return getJavaAnnotation(annotationType); } else { return null; } } /* * no partial overriding possible */ private void getJoinTable(List<Annotation> annotationList, Element tree, XMLContext.Default defaults) { Element subelement = tree == null ? null : tree.element("join-table"); final Class<JoinTable> annotationType = JoinTable.class; if (subelement != null) { //ignore java annotation, an element is defined AnnotationDescriptor annotation = new AnnotationDescriptor(annotationType); copyStringAttribute(annotation, subelement, "name", false); copyStringAttribute(annotation, subelement, "catalog", false); if (StringHelper.isNotEmpty(defaults.getCatalog()) && StringHelper.isEmpty((String) annotation.valueOf("catalog"))) { annotation.setValue("catalog", defaults.getCatalog()); } copyStringAttribute(annotation, subelement, "schema", false); if (StringHelper.isNotEmpty(defaults.getSchema()) && StringHelper.isEmpty((String) annotation.valueOf("schema"))) { annotation.setValue("schema", defaults.getSchema()); } buildUniqueConstraints(annotation, subelement); annotation.setValue("joinColumns", getJoinColumns(subelement, false)); annotation.setValue("inverseJoinColumns", getJoinColumns(subelement, true)); annotationList.add(AnnotationFactory.create(annotation)); } } private void getAssociation(Class<? extends Annotation> annotationType, List<Annotation> annotationList, XMLContext.Default defaults) { String xmlName = annotationToXml.get(annotationType); for (Element element : elementsForProperty) { if (xmlName.equals(element.getName())) { AnnotationDescriptor ad = new AnnotationDescriptor(annotationType); addTargetClass(element, ad, "target-entity", defaults); getFetchType(ad, element); getCascades(ad, element, defaults); getJoinTable(annotationList, element, defaults); buildJoinColumns(annotationList, element); Annotation annotation = getPrimaryKeyJoinColumns(element, defaults); addIfNotNull(annotationList, annotation); copyBooleanAttribute(ad, element, "optional"); copyStringAttribute(ad, element, "mapped-by", false); getOrderBy(annotationList, element); getMapKey(annotationList, element); annotationList.add(AnnotationFactory.create(ad)); getAccessType(annotationList, element); } } if (elementsForProperty.size() == 0 && defaults.canUseJavaAnnotations()) { Annotation annotation = getJavaAnnotation(annotationType); if (annotation != null) { annotationList.add(annotation); annotation = overridesDefaultsInJoinTable(annotation, defaults); addIfNotNull(annotationList, annotation); annotation = getJavaAnnotation(JoinColumn.class); addIfNotNull(annotationList, annotation); annotation = getJavaAnnotation(JoinColumns.class); addIfNotNull(annotationList, annotation); annotation = getJavaAnnotation(PrimaryKeyJoinColumn.class); addIfNotNull(annotationList, annotation); annotation = getJavaAnnotation(PrimaryKeyJoinColumns.class); addIfNotNull(annotationList, annotation); annotation = getJavaAnnotation(MapKey.class); addIfNotNull(annotationList, annotation); annotation = getJavaAnnotation(OrderBy.class); addIfNotNull(annotationList, annotation); annotation = getJavaAnnotation(AttributeOverride.class); addIfNotNull(annotationList, annotation); annotation = getJavaAnnotation(AttributeOverrides.class); addIfNotNull(annotationList, annotation); annotation = getJavaAnnotation(AssociationOverride.class); addIfNotNull(annotationList, annotation); annotation = getJavaAnnotation(AssociationOverrides.class); addIfNotNull(annotationList, annotation); annotation = getJavaAnnotation(Lob.class); addIfNotNull(annotationList, annotation); annotation = getJavaAnnotation(Enumerated.class); addIfNotNull(annotationList, annotation); annotation = getJavaAnnotation(Temporal.class); addIfNotNull(annotationList, annotation); annotation = getJavaAnnotation(Column.class); addIfNotNull(annotationList, annotation); annotation = getJavaAnnotation(Columns.class); addIfNotNull(annotationList, annotation); } else if (isJavaAnnotationPresent(ElementCollection.class)) { //JPA2 annotation = overridesDefaultsInJoinTable(getJavaAnnotation(ElementCollection.class), defaults); addIfNotNull(annotationList, annotation); annotation = getJavaAnnotation(JoinColumn.class); addIfNotNull(annotationList, annotation); annotation = getJavaAnnotation(JoinColumns.class); addIfNotNull(annotationList, annotation); annotation = getJavaAnnotation(PrimaryKeyJoinColumn.class); addIfNotNull(annotationList, annotation); annotation = getJavaAnnotation(PrimaryKeyJoinColumns.class); addIfNotNull(annotationList, annotation); annotation = getJavaAnnotation(MapKey.class); addIfNotNull(annotationList, annotation); annotation = getJavaAnnotation(OrderBy.class); addIfNotNull(annotationList, annotation); annotation = getJavaAnnotation(AttributeOverride.class); addIfNotNull(annotationList, annotation); annotation = getJavaAnnotation(AttributeOverrides.class); addIfNotNull(annotationList, annotation); annotation = getJavaAnnotation(AssociationOverride.class); addIfNotNull(annotationList, annotation); annotation = getJavaAnnotation(AssociationOverrides.class); addIfNotNull(annotationList, annotation); annotation = getJavaAnnotation(Lob.class); addIfNotNull(annotationList, annotation); annotation = getJavaAnnotation(Enumerated.class); addIfNotNull(annotationList, annotation); annotation = getJavaAnnotation(Temporal.class); addIfNotNull(annotationList, annotation); annotation = getJavaAnnotation(Column.class); addIfNotNull(annotationList, annotation); annotation = getJavaAnnotation(Columns.class); addIfNotNull(annotationList, annotation); } else if (isJavaAnnotationPresent(CollectionOfElements.class)) { //legacy Hibernate annotation = overridesDefaultsInJoinTable(getJavaAnnotation(CollectionOfElements.class), defaults); addIfNotNull(annotationList, annotation); annotation = getJavaAnnotation(JoinColumn.class); addIfNotNull(annotationList, annotation); annotation = getJavaAnnotation(JoinColumns.class); addIfNotNull(annotationList, annotation); annotation = getJavaAnnotation(PrimaryKeyJoinColumn.class); addIfNotNull(annotationList, annotation); annotation = getJavaAnnotation(PrimaryKeyJoinColumns.class); addIfNotNull(annotationList, annotation); annotation = getJavaAnnotation(MapKey.class); addIfNotNull(annotationList, annotation); annotation = getJavaAnnotation(OrderBy.class); addIfNotNull(annotationList, annotation); annotation = getJavaAnnotation(AttributeOverride.class); addIfNotNull(annotationList, annotation); annotation = getJavaAnnotation(AttributeOverrides.class); addIfNotNull(annotationList, annotation); annotation = getJavaAnnotation(AssociationOverride.class); addIfNotNull(annotationList, annotation); annotation = getJavaAnnotation(AssociationOverrides.class); addIfNotNull(annotationList, annotation); annotation = getJavaAnnotation(Lob.class); addIfNotNull(annotationList, annotation); annotation = getJavaAnnotation(Enumerated.class); addIfNotNull(annotationList, annotation); annotation = getJavaAnnotation(Temporal.class); addIfNotNull(annotationList, annotation); annotation = getJavaAnnotation(Column.class); addIfNotNull(annotationList, annotation); annotation = getJavaAnnotation(Columns.class); addIfNotNull(annotationList, annotation); } } } private void addTargetClass(Element element, AnnotationDescriptor ad, String nodeName, XMLContext.Default defaults) { String className = element.attributeValue(nodeName); if (className != null) { Class clazz; try { clazz = ReflectHelper.classForName(XMLContext.buildSafeClassName(className, defaults), this.getClass()); } catch (ClassNotFoundException e) { throw new AnnotationException( "Unable to find " + element.getPath() + " " + nodeName + ": " + className, e); } ad.setValue(getJavaAttributeNameFromXMLOne(nodeName), clazz); } } // TODO: Complete parsing of all element-collection related xml private void getElementCollection(List<Annotation> annotationList, XMLContext.Default defaults) { for (Element element : elementsForProperty) { if ("element-collection".equals(element.getName())) { AnnotationDescriptor ad = new AnnotationDescriptor(ElementCollection.class); addTargetClass(element, ad, "target-class", defaults); annotationList.add(AnnotationFactory.create(ad)); getAccessType(annotationList, element); } } } private void getOrderBy(List<Annotation> annotationList, Element element) { Element subelement = element != null ? element.element("order-by") : null; if (subelement != null) { String orderByString = subelement.getTextTrim(); AnnotationDescriptor ad = new AnnotationDescriptor(OrderBy.class); if (StringHelper.isNotEmpty(orderByString)) ad.setValue("value", orderByString); annotationList.add(AnnotationFactory.create(ad)); } } private void getMapKey(List<Annotation> annotationList, Element element) { Element subelement = element != null ? element.element("map-key") : null; if (subelement != null) { String mapKeyString = subelement.attributeValue("name"); AnnotationDescriptor ad = new AnnotationDescriptor(MapKey.class); if (StringHelper.isNotEmpty(mapKeyString)) ad.setValue("name", mapKeyString); annotationList.add(AnnotationFactory.create(ad)); } } private void buildJoinColumns(List<Annotation> annotationList, Element element) { JoinColumn[] joinColumns = getJoinColumns(element, false); if (joinColumns.length > 0) { AnnotationDescriptor ad = new AnnotationDescriptor(JoinColumns.class); ad.setValue("value", joinColumns); annotationList.add(AnnotationFactory.create(ad)); } } private void getCascades(AnnotationDescriptor ad, Element element, XMLContext.Default defaults) { List<Element> elements = element != null ? element.elements("cascade") : new ArrayList<Element>(0); List<CascadeType> cascades = new ArrayList<CascadeType>(); for (Element subelement : elements) { if (subelement.element("cascade-all") != null) cascades.add(CascadeType.ALL); if (subelement.element("cascade-persist") != null) cascades.add(CascadeType.PERSIST); if (subelement.element("cascade-merge") != null) cascades.add(CascadeType.MERGE); if (subelement.element("cascade-remove") != null) cascades.add(CascadeType.REMOVE); if (subelement.element("cascade-refresh") != null) cascades.add(CascadeType.REFRESH); if (subelement.element("cascade-detach") != null) cascades.add(CascadeType.DETACH); } if (Boolean.TRUE.equals(defaults.getCascadePersist()) && !cascades.contains(CascadeType.ALL) && !cascades.contains(CascadeType.PERSIST)) { cascades.add(CascadeType.PERSIST); } if (cascades.size() > 0) { ad.setValue("cascade", cascades.toArray(new CascadeType[cascades.size()])); } } private void getEmbedded(List<Annotation> annotationList, XMLContext.Default defaults) { for (Element element : elementsForProperty) { if ("embedded".equals(element.getName())) { AnnotationDescriptor ad = new AnnotationDescriptor(Embedded.class); annotationList.add(AnnotationFactory.create(ad)); getAccessType(annotationList, element); } } if (elementsForProperty.size() == 0 && defaults.canUseJavaAnnotations()) { Annotation annotation = getJavaAnnotation(Embedded.class); if (annotation != null) { annotationList.add(annotation); annotation = getJavaAnnotation(AttributeOverride.class); addIfNotNull(annotationList, annotation); annotation = getJavaAnnotation(AttributeOverrides.class); addIfNotNull(annotationList, annotation); annotation = getJavaAnnotation(AssociationOverride.class); addIfNotNull(annotationList, annotation); annotation = getJavaAnnotation(AssociationOverrides.class); addIfNotNull(annotationList, annotation); } } } private Transient getTransient(XMLContext.Default defaults) { for (Element element : elementsForProperty) { if ("transient".equals(element.getName())) { AnnotationDescriptor ad = new AnnotationDescriptor(Transient.class); return AnnotationFactory.create(ad); } } if (elementsForProperty.size() == 0 && defaults.canUseJavaAnnotations()) { return getJavaAnnotation(Transient.class); } else { return null; } } private void getVersion(List<Annotation> annotationList, XMLContext.Default defaults) { for (Element element : elementsForProperty) { if ("version".equals(element.getName())) { Annotation annotation = buildColumns(element); addIfNotNull(annotationList, annotation); getTemporal(annotationList, element); AnnotationDescriptor basic = new AnnotationDescriptor(Version.class); annotationList.add(AnnotationFactory.create(basic)); getAccessType(annotationList, element); } } if (elementsForProperty.size() == 0 && defaults.canUseJavaAnnotations()) { //we have nothing, so Java annotations might occurs Annotation annotation = getJavaAnnotation(Version.class); if (annotation != null) { annotationList.add(annotation); annotation = getJavaAnnotation(Column.class); addIfNotNull(annotationList, annotation); annotation = getJavaAnnotation(Columns.class); addIfNotNull(annotationList, annotation); annotation = getJavaAnnotation(Temporal.class); addIfNotNull(annotationList, annotation); } } } private void getBasic(List<Annotation> annotationList, XMLContext.Default defaults) { for (Element element : elementsForProperty) { if ("basic".equals(element.getName())) { Annotation annotation = buildColumns(element); addIfNotNull(annotationList, annotation); getAccessType(annotationList, element); getTemporal(annotationList, element); getLob(annotationList, element); getEnumerated(annotationList, element); AnnotationDescriptor basic = new AnnotationDescriptor(Basic.class); getFetchType(basic, element); copyBooleanAttribute(basic, element, "optional"); annotationList.add(AnnotationFactory.create(basic)); } } if (elementsForProperty.size() == 0 && defaults.canUseJavaAnnotations()) { //no annotation presence constraint, basic is the default Annotation annotation = getJavaAnnotation(Basic.class); addIfNotNull(annotationList, annotation); annotation = getJavaAnnotation(Lob.class); addIfNotNull(annotationList, annotation); annotation = getJavaAnnotation(Enumerated.class); addIfNotNull(annotationList, annotation); annotation = getJavaAnnotation(Temporal.class); addIfNotNull(annotationList, annotation); annotation = getJavaAnnotation(Column.class); addIfNotNull(annotationList, annotation); annotation = getJavaAnnotation(Columns.class); addIfNotNull(annotationList, annotation); annotation = getJavaAnnotation(AttributeOverride.class); addIfNotNull(annotationList, annotation); annotation = getJavaAnnotation(AttributeOverrides.class); addIfNotNull(annotationList, annotation); annotation = getJavaAnnotation(AssociationOverride.class); addIfNotNull(annotationList, annotation); annotation = getJavaAnnotation(AssociationOverrides.class); addIfNotNull(annotationList, annotation); } } private void getEnumerated(List<Annotation> annotationList, Element element) { Element subElement = element != null ? element.element("enumerated") : null; if (subElement != null) { AnnotationDescriptor ad = new AnnotationDescriptor(Enumerated.class); String enumerated = subElement.getTextTrim(); if ("ORDINAL".equalsIgnoreCase(enumerated)) { ad.setValue("value", EnumType.ORDINAL); } else if ("STRING".equalsIgnoreCase(enumerated)) { ad.setValue("value", EnumType.STRING); } else if (StringHelper.isNotEmpty(enumerated)) { throw new AnnotationException("Unknown EnumType: " + enumerated + ". " + SCHEMA_VALIDATION); } annotationList.add(AnnotationFactory.create(ad)); } } private void getLob(List<Annotation> annotationList, Element element) { Element subElement = element != null ? element.element("lob") : null; if (subElement != null) { annotationList.add(AnnotationFactory.create(new AnnotationDescriptor(Lob.class))); } } private void getFetchType(AnnotationDescriptor descriptor, Element element) { String fetchString = element != null ? element.attributeValue("fetch") : null; if (fetchString != null) { if ("eager".equalsIgnoreCase(fetchString)) { descriptor.setValue("fetch", FetchType.EAGER); } else if ("lazy".equalsIgnoreCase(fetchString)) { descriptor.setValue("fetch", FetchType.LAZY); } } } private void getEmbeddedId(List<Annotation> annotationList, XMLContext.Default defaults) { for (Element element : elementsForProperty) { if ("embedded-id".equals(element.getName())) { if (isProcessingId(defaults)) { Annotation annotation = getAttributeOverrides(element, defaults); addIfNotNull(annotationList, annotation); annotation = getAssociationOverrides(element, defaults); addIfNotNull(annotationList, annotation); AnnotationDescriptor ad = new AnnotationDescriptor(EmbeddedId.class); annotationList.add(AnnotationFactory.create(ad)); getAccessType(annotationList, element); } } } if (elementsForProperty.size() == 0 && defaults.canUseJavaAnnotations()) { Annotation annotation = getJavaAnnotation(EmbeddedId.class); if (annotation != null) { annotationList.add(annotation); annotation = getJavaAnnotation(Column.class); addIfNotNull(annotationList, annotation); annotation = getJavaAnnotation(Columns.class); addIfNotNull(annotationList, annotation); annotation = getJavaAnnotation(GeneratedValue.class); addIfNotNull(annotationList, annotation); annotation = getJavaAnnotation(Temporal.class); addIfNotNull(annotationList, annotation); annotation = getJavaAnnotation(TableGenerator.class); addIfNotNull(annotationList, annotation); annotation = getJavaAnnotation(SequenceGenerator.class); addIfNotNull(annotationList, annotation); annotation = getJavaAnnotation(AttributeOverride.class); addIfNotNull(annotationList, annotation); annotation = getJavaAnnotation(AttributeOverrides.class); addIfNotNull(annotationList, annotation); annotation = getJavaAnnotation(AssociationOverride.class); addIfNotNull(annotationList, annotation); annotation = getJavaAnnotation(AssociationOverrides.class); addIfNotNull(annotationList, annotation); } } } private void preCalculateElementsForProperty(Element tree) { elementsForProperty = new ArrayList<Element>(); Element element = tree != null ? tree.element("attributes") : null; //put entity.attributes elements if (element != null) { for (Element subelement : (List<Element>) element.elements()) { if (propertyName.equals(subelement.attributeValue("name"))) { elementsForProperty.add(subelement); } } } //add pre-* etc from entity and pure entity listener classes if (tree != null) { for (Element subelement : (List<Element>) tree.elements()) { if (propertyName.equals(subelement.attributeValue("method-name"))) { elementsForProperty.add(subelement); } } } } private void getId(List<Annotation> annotationList, XMLContext.Default defaults) { for (Element element : elementsForProperty) { if ("id".equals(element.getName())) { boolean processId = isProcessingId(defaults); if (processId) { Annotation annotation = buildColumns(element); addIfNotNull(annotationList, annotation); annotation = buildGeneratedValue(element); addIfNotNull(annotationList, annotation); getTemporal(annotationList, element); //FIXME: fix the priority of xml over java for generator names annotation = getTableGenerator(element, defaults); addIfNotNull(annotationList, annotation); annotation = getSequenceGenerator(element, defaults); addIfNotNull(annotationList, annotation); AnnotationDescriptor id = new AnnotationDescriptor(Id.class); annotationList.add(AnnotationFactory.create(id)); getAccessType(annotationList, element); } } } if (elementsForProperty.size() == 0 && defaults.canUseJavaAnnotations()) { Annotation annotation = getJavaAnnotation(Id.class); if (annotation != null) { annotationList.add(annotation); annotation = getJavaAnnotation(Column.class); addIfNotNull(annotationList, annotation); annotation = getJavaAnnotation(Columns.class); addIfNotNull(annotationList, annotation); annotation = getJavaAnnotation(GeneratedValue.class); addIfNotNull(annotationList, annotation); annotation = getJavaAnnotation(Temporal.class); addIfNotNull(annotationList, annotation); annotation = getJavaAnnotation(TableGenerator.class); addIfNotNull(annotationList, annotation); annotation = getJavaAnnotation(SequenceGenerator.class); addIfNotNull(annotationList, annotation); annotation = getJavaAnnotation(AttributeOverride.class); addIfNotNull(annotationList, annotation); annotation = getJavaAnnotation(AttributeOverrides.class); addIfNotNull(annotationList, annotation); annotation = getJavaAnnotation(AssociationOverride.class); addIfNotNull(annotationList, annotation); annotation = getJavaAnnotation(AssociationOverrides.class); addIfNotNull(annotationList, annotation); } } } private boolean isProcessingId(XMLContext.Default defaults) { boolean isExplicit = defaults.getAccess() != null; boolean correctAccess = (PropertyType.PROPERTY.equals(propertyType) && AccessType.PROPERTY.equals(defaults.getAccess())) || (PropertyType.FIELD.equals(propertyType) && AccessType.FIELD.equals(defaults.getAccess())); boolean hasId = defaults.canUseJavaAnnotations() && (isJavaAnnotationPresent(Id.class) || isJavaAnnotationPresent(EmbeddedId.class)); //if ( properAccessOnMetadataComplete || properOverridingOnMetadataNonComplete ) { boolean mirrorAttributeIsId = defaults.canUseJavaAnnotations() && (mirroredAttribute != null && (mirroredAttribute.isAnnotationPresent(Id.class) || mirroredAttribute.isAnnotationPresent(EmbeddedId.class))); boolean propertyIsDefault = PropertyType.PROPERTY.equals(propertyType) && !mirrorAttributeIsId; return correctAccess || (!isExplicit && hasId) || (!isExplicit && propertyIsDefault); } private Columns buildColumns(Element element) { List<Element> subelements = element.elements("column"); List<Column> columns = new ArrayList<Column>(subelements.size()); for (Element subelement : subelements) { columns.add(getColumn(subelement, false, element)); } if (columns.size() > 0) { AnnotationDescriptor columnsDescr = new AnnotationDescriptor(Columns.class); columnsDescr.setValue("columns", columns.toArray(new Column[columns.size()])); return AnnotationFactory.create(columnsDescr); } else { return null; } } private GeneratedValue buildGeneratedValue(Element element) { Element subElement = element != null ? element.element("generated-value") : null; if (subElement != null) { AnnotationDescriptor ad = new AnnotationDescriptor(GeneratedValue.class); String strategy = subElement.attributeValue("strategy"); if ("TABLE".equalsIgnoreCase(strategy)) { ad.setValue("strategy", GenerationType.TABLE); } else if ("SEQUENCE".equalsIgnoreCase(strategy)) { ad.setValue("strategy", GenerationType.SEQUENCE); } else if ("IDENTITY".equalsIgnoreCase(strategy)) { ad.setValue("strategy", GenerationType.IDENTITY); } else if ("AUTO".equalsIgnoreCase(strategy)) { ad.setValue("strategy", GenerationType.AUTO); } else if (StringHelper.isNotEmpty(strategy)) { throw new AnnotationException("Unknown GenerationType: " + strategy + ". " + SCHEMA_VALIDATION); } copyStringAttribute(ad, subElement, "generator", false); return AnnotationFactory.create(ad); } else { return null; } } private void getTemporal(List<Annotation> annotationList, Element element) { Element subElement = element != null ? element.element("temporal") : null; if (subElement != null) { AnnotationDescriptor ad = new AnnotationDescriptor(Temporal.class); String temporal = subElement.getTextTrim(); if ("DATE".equalsIgnoreCase(temporal)) { ad.setValue("value", TemporalType.DATE); } else if ("TIME".equalsIgnoreCase(temporal)) { ad.setValue("value", TemporalType.TIME); } else if ("TIMESTAMP".equalsIgnoreCase(temporal)) { ad.setValue("value", TemporalType.TIMESTAMP); } else if (StringHelper.isNotEmpty(temporal)) { throw new AnnotationException("Unknown TemporalType: " + temporal + ". " + SCHEMA_VALIDATION); } annotationList.add(AnnotationFactory.create(ad)); } } private void getAccessType(List<Annotation> annotationList, Element element) { if (element == null) { return; } String access = element.attributeValue("access"); if (access != null) { AnnotationDescriptor ad = new AnnotationDescriptor(Access.class); AccessType type; try { type = AccessType.valueOf(access); } catch (IllegalArgumentException e) { throw new AnnotationException(access + " is not a valid access type. Check you xml confguration."); } if ((AccessType.PROPERTY.equals(type) && this.element instanceof Method) || (AccessType.FIELD.equals(type) && this.element instanceof Field)) { return; } ad.setValue("value", type); annotationList.add(AnnotationFactory.create(ad)); } } private AssociationOverrides getAssociationOverrides(Element tree, XMLContext.Default defaults) { List<AssociationOverride> attributes = (List<AssociationOverride>) buildAssociationOverrides(tree); if (defaults.canUseJavaAnnotations()) { AssociationOverride annotation = getJavaAnnotation(AssociationOverride.class); addAssociationOverrideIfNeeded(annotation, attributes); AssociationOverrides annotations = getJavaAnnotation(AssociationOverrides.class); if (annotations != null) { for (AssociationOverride current : annotations.value()) { addAssociationOverrideIfNeeded(current, attributes); } } } if (attributes.size() > 0) { AnnotationDescriptor ad = new AnnotationDescriptor(AssociationOverrides.class); ad.setValue("value", attributes.toArray(new AssociationOverride[attributes.size()])); return AnnotationFactory.create(ad); } else { return null; } } private List<AssociationOverride> buildAssociationOverrides(Element element) { List<Element> subelements = element == null ? null : element.elements("association-override"); List<AssociationOverride> overrides = new ArrayList<AssociationOverride>(); if (subelements != null && subelements.size() > 0) { for (Element current : subelements) { AnnotationDescriptor override = new AnnotationDescriptor(AssociationOverride.class); copyStringAttribute(override, current, "name", true); override.setValue("joinColumns", getJoinColumns(current, false)); overrides.add((AssociationOverride) AnnotationFactory.create(override)); } } return overrides; } private JoinColumn[] getJoinColumns(Element element, boolean isInverse) { List<Element> subelements = element != null ? element.elements(isInverse ? "inverse-join-column" : "join-column") : null; List<JoinColumn> joinColumns = new ArrayList<JoinColumn>(); if (subelements != null) { for (Element subelement : subelements) { AnnotationDescriptor column = new AnnotationDescriptor(JoinColumn.class); copyStringAttribute(column, subelement, "name", false); copyStringAttribute(column, subelement, "referenced-column-name", false); copyBooleanAttribute(column, subelement, "unique"); copyBooleanAttribute(column, subelement, "nullable"); copyBooleanAttribute(column, subelement, "insertable"); copyBooleanAttribute(column, subelement, "updatable"); copyStringAttribute(column, subelement, "column-definition", false); copyStringAttribute(column, subelement, "table", false); joinColumns.add((JoinColumn) AnnotationFactory.create(column)); } } return joinColumns.toArray(new JoinColumn[joinColumns.size()]); } private void addAssociationOverrideIfNeeded(AssociationOverride annotation, List<AssociationOverride> overrides) { if (annotation != null) { String overrideName = annotation.name(); boolean present = false; for (AssociationOverride current : overrides) { if (current.name().equals(overrideName)) { present = true; break; } } if (!present) overrides.add(annotation); } } private AttributeOverrides getAttributeOverrides(Element tree, XMLContext.Default defaults) { List<AttributeOverride> attributes = buildAttributeOverrides(tree); return mergeAttributeOverrides(defaults, attributes); } private AttributeOverrides getAttributeOverrides(List<Element> elements, XMLContext.Default defaults) { List<AttributeOverride> attributes = new ArrayList<AttributeOverride>(); for (Element element : elements) { attributes.addAll(buildAttributeOverrides(element)); } return mergeAttributeOverrides(defaults, attributes); } private AttributeOverrides mergeAttributeOverrides(XMLContext.Default defaults, List<AttributeOverride> attributes) { if (defaults.canUseJavaAnnotations()) { AttributeOverride annotation = getJavaAnnotation(AttributeOverride.class); addAttributeOverrideIfNeeded(annotation, attributes); AttributeOverrides annotations = getJavaAnnotation(AttributeOverrides.class); if (annotations != null) { for (AttributeOverride current : annotations.value()) { addAttributeOverrideIfNeeded(current, attributes); } } } if (attributes.size() > 0) { AnnotationDescriptor ad = new AnnotationDescriptor(AttributeOverrides.class); ad.setValue("value", attributes.toArray(new AttributeOverride[attributes.size()])); return AnnotationFactory.create(ad); } else { return null; } } private List<AttributeOverride> buildAttributeOverrides(Element element) { List<Element> subelements = element == null ? null : element.elements("attribute-override"); return buildAttributeOverrides(subelements); } private List<AttributeOverride> buildAttributeOverrides(List<Element> subelements) { List<AttributeOverride> overrides = new ArrayList<AttributeOverride>(); if (subelements != null && subelements.size() > 0) { for (Element current : subelements) { if (!current.getName().equals("attribute-override")) continue; AnnotationDescriptor override = new AnnotationDescriptor(AttributeOverride.class); copyStringAttribute(override, current, "name", true); Element column = current.element("column"); override.setValue("column", getColumn(column, true, current)); overrides.add((AttributeOverride) AnnotationFactory.create(override)); } } return overrides; } private Column getColumn(Element element, boolean isMandatory, Element current) { //Element subelement = element != null ? element.element( "column" ) : null; if (element != null) { AnnotationDescriptor column = new AnnotationDescriptor(Column.class); copyStringAttribute(column, element, "name", false); copyBooleanAttribute(column, element, "unique"); copyBooleanAttribute(column, element, "nullable"); copyBooleanAttribute(column, element, "insertable"); copyBooleanAttribute(column, element, "updatable"); copyStringAttribute(column, element, "column-definition", false); copyStringAttribute(column, element, "table", false); copyIntegerAttribute(column, element, "length"); copyIntegerAttribute(column, element, "precision"); copyIntegerAttribute(column, element, "scale"); return (Column) AnnotationFactory.create(column); } else { if (isMandatory) { throw new AnnotationException(current.getPath() + ".column is mandatory. " + SCHEMA_VALIDATION); } return null; } } private void addAttributeOverrideIfNeeded(AttributeOverride annotation, List<AttributeOverride> overrides) { if (annotation != null) { String overrideName = annotation.name(); boolean present = false; for (AttributeOverride current : overrides) { if (current.name().equals(overrideName)) { present = true; break; } } if (!present) overrides.add(annotation); } } private Access getAccessType(Element tree, XMLContext.Default defaults) { String access = tree == null ? null : tree.attributeValue("access"); if (access != null) { AnnotationDescriptor ad = new AnnotationDescriptor(Access.class); AccessType type; try { type = AccessType.valueOf(access); } catch (IllegalArgumentException e) { throw new AnnotationException(access + " is not a valid access type. Check you xml confguration."); } ad.setValue("value", type); return AnnotationFactory.create(ad); } else if (defaults.canUseJavaAnnotations() && isJavaAnnotationPresent(Access.class)) { return getJavaAnnotation(Access.class); } else if (defaults.getAccess() != null) { AnnotationDescriptor ad = new AnnotationDescriptor(Access.class); ad.setValue("value", defaults.getAccess()); return AnnotationFactory.create(ad); } else { return null; } } private ExcludeSuperclassListeners getExcludeSuperclassListeners(Element tree, XMLContext.Default defaults) { return (ExcludeSuperclassListeners) getMarkerAnnotation(ExcludeSuperclassListeners.class, tree, defaults); } private ExcludeDefaultListeners getExcludeDefaultListeners(Element tree, XMLContext.Default defaults) { return (ExcludeDefaultListeners) getMarkerAnnotation(ExcludeDefaultListeners.class, tree, defaults); } private Annotation getMarkerAnnotation(Class<? extends Annotation> clazz, Element element, XMLContext.Default defaults) { Element subelement = element == null ? null : element.element(annotationToXml.get(clazz)); if (subelement != null) { return AnnotationFactory.create(new AnnotationDescriptor(clazz)); } else if (defaults.canUseJavaAnnotations()) { //TODO wonder whether it should be excluded so that user can undone it return getJavaAnnotation(clazz); } else { return null; } } private SqlResultSetMappings getSqlResultSetMappings(Element tree, XMLContext.Default defaults) { List<SqlResultSetMapping> results = (List<SqlResultSetMapping>) buildSqlResultsetMappings(tree, defaults); if (defaults.canUseJavaAnnotations()) { SqlResultSetMapping annotation = getJavaAnnotation(SqlResultSetMapping.class); addSqlResultsetMappingIfNeeded(annotation, results); SqlResultSetMappings annotations = getJavaAnnotation(SqlResultSetMappings.class); if (annotations != null) { for (SqlResultSetMapping current : annotations.value()) { addSqlResultsetMappingIfNeeded(current, results); } } } if (results.size() > 0) { AnnotationDescriptor ad = new AnnotationDescriptor(SqlResultSetMappings.class); ad.setValue("value", results.toArray(new SqlResultSetMapping[results.size()])); return AnnotationFactory.create(ad); } else { return null; } } public static List<SqlResultSetMapping> buildSqlResultsetMappings(Element element, XMLContext.Default defaults) { if (element == null) return new ArrayList<SqlResultSetMapping>(); List resultsetElementList = element.elements("sql-result-set-mapping"); List<SqlResultSetMapping> resultsets = new ArrayList<SqlResultSetMapping>(); Iterator it = resultsetElementList.listIterator(); while (it.hasNext()) { Element subelement = (Element) it.next(); AnnotationDescriptor ann = new AnnotationDescriptor(SqlResultSetMapping.class); copyStringAttribute(ann, subelement, "name", true); List<Element> elements = subelement.elements("entity-result"); List<EntityResult> entityResults = new ArrayList<EntityResult>(elements.size()); for (Element entityResult : elements) { AnnotationDescriptor entityResultDescriptor = new AnnotationDescriptor(EntityResult.class); String clazzName = entityResult.attributeValue("entity-class"); if (clazzName == null) { throw new AnnotationException("<entity-result> without entity-class. " + SCHEMA_VALIDATION); } Class clazz; try { clazz = ReflectHelper.classForName(XMLContext.buildSafeClassName(clazzName, defaults), JPAOverridenAnnotationReader.class); } catch (ClassNotFoundException e) { throw new AnnotationException("Unable to find entity-class: " + clazzName, e); } entityResultDescriptor.setValue("entityClass", clazz); copyStringAttribute(entityResultDescriptor, entityResult, "discriminator-column", false); List<FieldResult> fieldResults = new ArrayList<FieldResult>(); for (Element fieldResult : (List<Element>) entityResult.elements("field-result")) { AnnotationDescriptor fieldResultDescriptor = new AnnotationDescriptor(FieldResult.class); copyStringAttribute(fieldResultDescriptor, fieldResult, "name", true); copyStringAttribute(fieldResultDescriptor, fieldResult, "column", true); fieldResults.add((FieldResult) AnnotationFactory.create(fieldResultDescriptor)); } entityResultDescriptor.setValue("fields", fieldResults.toArray(new FieldResult[fieldResults.size()])); entityResults.add((EntityResult) AnnotationFactory.create(entityResultDescriptor)); } ann.setValue("entities", entityResults.toArray(new EntityResult[entityResults.size()])); elements = subelement.elements("column-result"); List<ColumnResult> columnResults = new ArrayList<ColumnResult>(elements.size()); for (Element columnResult : elements) { AnnotationDescriptor columnResultDescriptor = new AnnotationDescriptor(ColumnResult.class); copyStringAttribute(columnResultDescriptor, columnResult, "name", true); columnResults.add((ColumnResult) AnnotationFactory.create(columnResultDescriptor)); } ann.setValue("columns", columnResults.toArray(new ColumnResult[columnResults.size()])); //FIXME there is never such a result-class, get rid of it? String clazzName = subelement.attributeValue("result-class"); if (StringHelper.isNotEmpty(clazzName)) { Class clazz; try { clazz = ReflectHelper.classForName(XMLContext.buildSafeClassName(clazzName, defaults), JPAOverridenAnnotationReader.class); } catch (ClassNotFoundException e) { throw new AnnotationException("Unable to find entity-class: " + clazzName, e); } ann.setValue("resultClass", clazz); } copyStringAttribute(ann, subelement, "result-set-mapping", false); resultsets.add((SqlResultSetMapping) AnnotationFactory.create(ann)); } return resultsets; } private void addSqlResultsetMappingIfNeeded(SqlResultSetMapping annotation, List<SqlResultSetMapping> resultsets) { if (annotation != null) { String resultsetName = annotation.name(); boolean present = false; for (SqlResultSetMapping current : resultsets) { if (current.name().equals(resultsetName)) { present = true; break; } } if (!present) resultsets.add(annotation); } } private NamedQueries getNamedQueries(Element tree, XMLContext.Default defaults) { //TODO avoid the Proxy Creation (@NamedQueries) when possible List<NamedQuery> queries = (List<NamedQuery>) buildNamedQueries(tree, false, defaults); if (defaults.canUseJavaAnnotations()) { NamedQuery annotation = getJavaAnnotation(NamedQuery.class); addNamedQueryIfNeeded(annotation, queries); NamedQueries annotations = getJavaAnnotation(NamedQueries.class); if (annotations != null) { for (NamedQuery current : annotations.value()) { addNamedQueryIfNeeded(current, queries); } } } if (queries.size() > 0) { AnnotationDescriptor ad = new AnnotationDescriptor(NamedQueries.class); ad.setValue("value", queries.toArray(new NamedQuery[queries.size()])); return AnnotationFactory.create(ad); } else { return null; } } private void addNamedQueryIfNeeded(NamedQuery annotation, List<NamedQuery> queries) { if (annotation != null) { String queryName = annotation.name(); boolean present = false; for (NamedQuery current : queries) { if (current.name().equals(queryName)) { present = true; break; } } if (!present) queries.add(annotation); } } private NamedNativeQueries getNamedNativeQueries(Element tree, XMLContext.Default defaults) { List<NamedNativeQuery> queries = (List<NamedNativeQuery>) buildNamedQueries(tree, true, defaults); if (defaults.canUseJavaAnnotations()) { NamedNativeQuery annotation = getJavaAnnotation(NamedNativeQuery.class); addNamedNativeQueryIfNeeded(annotation, queries); NamedNativeQueries annotations = getJavaAnnotation(NamedNativeQueries.class); if (annotations != null) { for (NamedNativeQuery current : annotations.value()) { addNamedNativeQueryIfNeeded(current, queries); } } } if (queries.size() > 0) { AnnotationDescriptor ad = new AnnotationDescriptor(NamedNativeQueries.class); ad.setValue("value", queries.toArray(new NamedNativeQuery[queries.size()])); return AnnotationFactory.create(ad); } else { return null; } } private void addNamedNativeQueryIfNeeded(NamedNativeQuery annotation, List<NamedNativeQuery> queries) { if (annotation != null) { String queryName = annotation.name(); boolean present = false; for (NamedNativeQuery current : queries) { if (current.name().equals(queryName)) { present = true; break; } } if (!present) queries.add(annotation); } } public static List buildNamedQueries(Element element, boolean isNative, XMLContext.Default defaults) { if (element == null) return new ArrayList(); List namedQueryElementList = isNative ? element.elements("named-native-query") : element.elements("named-query"); List namedQueries = new ArrayList(); Iterator it = namedQueryElementList.listIterator(); while (it.hasNext()) { Element subelement = (Element) it.next(); AnnotationDescriptor ann = new AnnotationDescriptor( isNative ? NamedNativeQuery.class : NamedQuery.class); copyStringAttribute(ann, subelement, "name", false); Element queryElt = subelement.element("query"); if (queryElt == null) throw new AnnotationException("No <query> element found." + SCHEMA_VALIDATION); ann.setValue("query", queryElt.getTextTrim()); List<Element> elements = subelement.elements("hint"); List<QueryHint> queryHints = new ArrayList<QueryHint>(elements.size()); for (Element hint : elements) { AnnotationDescriptor hintDescriptor = new AnnotationDescriptor(QueryHint.class); String value = hint.attributeValue("name"); if (value == null) throw new AnnotationException("<hint> without name. " + SCHEMA_VALIDATION); hintDescriptor.setValue("name", value); value = hint.attributeValue("value"); if (value == null) throw new AnnotationException("<hint> without value. " + SCHEMA_VALIDATION); hintDescriptor.setValue("value", value); queryHints.add((QueryHint) AnnotationFactory.create(hintDescriptor)); } ann.setValue("hints", queryHints.toArray(new QueryHint[queryHints.size()])); String clazzName = subelement.attributeValue("result-class"); if (StringHelper.isNotEmpty(clazzName)) { Class clazz; try { clazz = ReflectHelper.classForName(XMLContext.buildSafeClassName(clazzName, defaults), JPAOverridenAnnotationReader.class); } catch (ClassNotFoundException e) { throw new AnnotationException("Unable to find entity-class: " + clazzName, e); } ann.setValue("resultClass", clazz); } copyStringAttribute(ann, subelement, "result-set-mapping", false); namedQueries.add(AnnotationFactory.create(ann)); } return namedQueries; } private TableGenerator getTableGenerator(Element tree, XMLContext.Default defaults) { Element element = tree != null ? tree.element(annotationToXml.get(TableGenerator.class)) : null; if (element != null) { return buildTableGeneratorAnnotation(element, defaults); } else if (defaults.canUseJavaAnnotations() && isJavaAnnotationPresent(TableGenerator.class)) { TableGenerator tableAnn = getJavaAnnotation(TableGenerator.class); if (StringHelper.isNotEmpty(defaults.getSchema()) || StringHelper.isNotEmpty(defaults.getCatalog())) { AnnotationDescriptor annotation = new AnnotationDescriptor(TableGenerator.class); annotation.setValue("name", tableAnn.name()); annotation.setValue("table", tableAnn.table()); annotation.setValue("catalog", tableAnn.table()); if (StringHelper.isEmpty((String) annotation.valueOf("catalog")) && StringHelper.isNotEmpty(defaults.getCatalog())) { annotation.setValue("catalog", defaults.getCatalog()); } annotation.setValue("schema", tableAnn.table()); if (StringHelper.isEmpty((String) annotation.valueOf("schema")) && StringHelper.isNotEmpty(defaults.getSchema())) { annotation.setValue("catalog", defaults.getSchema()); } annotation.setValue("pkColumnName", tableAnn.pkColumnName()); annotation.setValue("valueColumnName", tableAnn.valueColumnName()); annotation.setValue("pkColumnValue", tableAnn.pkColumnValue()); annotation.setValue("initialValue", tableAnn.initialValue()); annotation.setValue("allocationSize", tableAnn.allocationSize()); annotation.setValue("uniqueConstraints", tableAnn.uniqueConstraints()); return AnnotationFactory.create(annotation); } else { return tableAnn; } } else { return null; } } public static TableGenerator buildTableGeneratorAnnotation(Element element, XMLContext.Default defaults) { AnnotationDescriptor ad = new AnnotationDescriptor(TableGenerator.class); copyStringAttribute(ad, element, "name", false); copyStringAttribute(ad, element, "table", false); copyStringAttribute(ad, element, "catalog", false); copyStringAttribute(ad, element, "schema", false); copyStringAttribute(ad, element, "pk-column-name", false); copyStringAttribute(ad, element, "value-column-name", false); copyStringAttribute(ad, element, "pk-column-value", false); copyIntegerAttribute(ad, element, "initial-value"); copyIntegerAttribute(ad, element, "allocation-size"); buildUniqueConstraints(ad, element); if (StringHelper.isEmpty((String) ad.valueOf("schema")) && StringHelper.isNotEmpty(defaults.getSchema())) { ad.setValue("schema", defaults.getSchema()); } if (StringHelper.isEmpty((String) ad.valueOf("catalog")) && StringHelper.isNotEmpty(defaults.getCatalog())) { ad.setValue("catalog", defaults.getCatalog()); } return AnnotationFactory.create(ad); } private SequenceGenerator getSequenceGenerator(Element tree, XMLContext.Default defaults) { Element element = tree != null ? tree.element(annotationToXml.get(SequenceGenerator.class)) : null; if (element != null) { return buildSequenceGeneratorAnnotation(element); } else if (defaults.canUseJavaAnnotations()) { return getJavaAnnotation(SequenceGenerator.class); } else { return null; } } public static SequenceGenerator buildSequenceGeneratorAnnotation(Element element) { if (element != null) { AnnotationDescriptor ad = new AnnotationDescriptor(SequenceGenerator.class); copyStringAttribute(ad, element, "name", false); copyStringAttribute(ad, element, "sequence-name", false); copyIntegerAttribute(ad, element, "initial-value"); copyIntegerAttribute(ad, element, "allocation-size"); return AnnotationFactory.create(ad); } else { return null; } } private DiscriminatorColumn getDiscriminatorColumn(Element tree, XMLContext.Default defaults) { Element element = tree != null ? tree.element("discriminator-column") : null; if (element != null) { AnnotationDescriptor ad = new AnnotationDescriptor(DiscriminatorColumn.class); copyStringAttribute(ad, element, "name", false); copyStringAttribute(ad, element, "column-definition", false); String value = element.attributeValue("discriminator-type"); DiscriminatorType type = DiscriminatorType.STRING; if (value != null) { if ("STRING".equals(value)) { type = DiscriminatorType.STRING; } else if ("CHAR".equals(value)) { type = DiscriminatorType.CHAR; } else if ("INTEGER".equals(value)) { type = DiscriminatorType.INTEGER; } else { throw new AnnotationException( "Unknown DiscrimiatorType in XML: " + value + " (" + SCHEMA_VALIDATION + ")"); } } ad.setValue("discriminatorType", type); copyIntegerAttribute(ad, element, "length"); return AnnotationFactory.create(ad); } else if (defaults.canUseJavaAnnotations()) { return getJavaAnnotation(DiscriminatorColumn.class); } else { return null; } } private DiscriminatorValue getDiscriminatorValue(Element tree, XMLContext.Default defaults) { Element element = tree != null ? tree.element("discriminator-value") : null; if (element != null) { AnnotationDescriptor ad = new AnnotationDescriptor(DiscriminatorValue.class); copyStringElement(element, ad, "value"); return AnnotationFactory.create(ad); } else if (defaults.canUseJavaAnnotations()) { return getJavaAnnotation(DiscriminatorValue.class); } else { return null; } } private Inheritance getInheritance(Element tree, XMLContext.Default defaults) { Element element = tree != null ? tree.element("inheritance") : null; if (element != null) { AnnotationDescriptor ad = new AnnotationDescriptor(Inheritance.class); Attribute attr = element.attribute("strategy"); InheritanceType strategy = InheritanceType.SINGLE_TABLE; if (attr != null) { String value = attr.getValue(); if ("SINGLE_TABLE".equals(value)) { strategy = InheritanceType.SINGLE_TABLE; } else if ("JOINED".equals(value)) { strategy = InheritanceType.JOINED; } else if ("TABLE_PER_CLASS".equals(value)) { strategy = InheritanceType.TABLE_PER_CLASS; } else { throw new AnnotationException( "Unknown InheritanceType in XML: " + value + " (" + SCHEMA_VALIDATION + ")"); } } ad.setValue("strategy", strategy); return AnnotationFactory.create(ad); } else if (defaults.canUseJavaAnnotations()) { return getJavaAnnotation(Inheritance.class); } else { return null; } } private IdClass getIdClass(Element tree, XMLContext.Default defaults) { Element element = tree == null ? null : tree.element("id-class"); if (element != null) { Attribute attr = element.attribute("class"); if (attr != null) { AnnotationDescriptor ad = new AnnotationDescriptor(IdClass.class); Class clazz; try { clazz = ReflectHelper.classForName(XMLContext.buildSafeClassName(attr.getValue(), defaults), this.getClass()); } catch (ClassNotFoundException e) { throw new AnnotationException("Unable to find id-class: " + attr.getValue(), e); } ad.setValue("value", clazz); return AnnotationFactory.create(ad); } else { throw new AnnotationException("id-class without class. " + SCHEMA_VALIDATION); } } else if (defaults.canUseJavaAnnotations()) { return getJavaAnnotation(IdClass.class); } else { return null; } } private PrimaryKeyJoinColumns getPrimaryKeyJoinColumns(Element element, XMLContext.Default defaults) { PrimaryKeyJoinColumn[] columns = buildPrimaryKeyJoinColumns(element); if (columns.length == 0 && defaults.canUseJavaAnnotations()) { PrimaryKeyJoinColumn annotation = getJavaAnnotation(PrimaryKeyJoinColumn.class); if (annotation != null) { columns = new PrimaryKeyJoinColumn[] { annotation }; } else { PrimaryKeyJoinColumns annotations = getJavaAnnotation(PrimaryKeyJoinColumns.class); columns = annotations != null ? annotations.value() : columns; } } if (columns.length > 0) { AnnotationDescriptor ad = new AnnotationDescriptor(PrimaryKeyJoinColumns.class); ad.setValue("value", columns); return AnnotationFactory.create(ad); } else { return null; } } private Entity getEntity(Element tree, XMLContext.Default defaults) { if (tree == null) { return defaults.canUseJavaAnnotations() ? getJavaAnnotation(Entity.class) : null; } else { if ("entity".equals(tree.getName())) { AnnotationDescriptor entity = new AnnotationDescriptor(Entity.class); copyStringAttribute(entity, tree, "name", false); if (defaults.canUseJavaAnnotations() && StringHelper.isEmpty((String) entity.valueOf("name"))) { Entity javaAnn = getJavaAnnotation(Entity.class); if (javaAnn != null) entity.setValue("name", javaAnn.name()); } return AnnotationFactory.create(entity); } else { return null; //this is not an entity } } } private MappedSuperclass getMappedSuperclass(Element tree, XMLContext.Default defaults) { if (tree == null) { return defaults.canUseJavaAnnotations() ? getJavaAnnotation(MappedSuperclass.class) : null; } else { if ("mapped-superclass".equals(tree.getName())) { AnnotationDescriptor entity = new AnnotationDescriptor(MappedSuperclass.class); return AnnotationFactory.create(entity); } else { return null; //this is not an entity } } } private Embeddable getEmbeddable(Element tree, XMLContext.Default defaults) { if (tree == null) { return defaults.canUseJavaAnnotations() ? getJavaAnnotation(Embeddable.class) : null; } else { if ("embeddable".equals(tree.getName())) { AnnotationDescriptor entity = new AnnotationDescriptor(Embeddable.class); return AnnotationFactory.create(entity); } else { return null; //this is not an entity } } } private Table getTable(Element tree, XMLContext.Default defaults) { Element subelement = tree == null ? null : tree.element("table"); if (subelement == null) { //no element but might have some default or some annotation if (StringHelper.isNotEmpty(defaults.getCatalog()) || StringHelper.isNotEmpty(defaults.getSchema())) { AnnotationDescriptor annotation = new AnnotationDescriptor(Table.class); if (defaults.canUseJavaAnnotations()) { Table table = getJavaAnnotation(Table.class); if (table != null) { annotation.setValue("name", table.name()); annotation.setValue("schema", table.schema()); annotation.setValue("catalog", table.catalog()); annotation.setValue("uniqueConstraints", table.uniqueConstraints()); } } if (StringHelper.isEmpty((String) annotation.valueOf("schema")) && StringHelper.isNotEmpty(defaults.getSchema())) { annotation.setValue("schema", defaults.getSchema()); } if (StringHelper.isEmpty((String) annotation.valueOf("catalog")) && StringHelper.isNotEmpty(defaults.getCatalog())) { annotation.setValue("catalog", defaults.getCatalog()); } return AnnotationFactory.create(annotation); } else if (defaults.canUseJavaAnnotations()) { return getJavaAnnotation(Table.class); } else { return null; } } else { //ignore java annotation, an element is defined AnnotationDescriptor annotation = new AnnotationDescriptor(Table.class); copyStringAttribute(annotation, subelement, "name", false); copyStringAttribute(annotation, subelement, "catalog", false); if (StringHelper.isNotEmpty(defaults.getCatalog()) && StringHelper.isEmpty((String) annotation.valueOf("catalog"))) { annotation.setValue("catalog", defaults.getCatalog()); } copyStringAttribute(annotation, subelement, "schema", false); if (StringHelper.isNotEmpty(defaults.getSchema()) && StringHelper.isEmpty((String) annotation.valueOf("schema"))) { annotation.setValue("schema", defaults.getSchema()); } buildUniqueConstraints(annotation, subelement); return AnnotationFactory.create(annotation); } } private SecondaryTables getSecondaryTables(Element tree, XMLContext.Default defaults) { List<Element> elements = tree == null ? new ArrayList<Element>() : (List<Element>) tree.elements("secondary-table"); List<SecondaryTable> secondaryTables = new ArrayList<SecondaryTable>(3); for (Element element : elements) { AnnotationDescriptor annotation = new AnnotationDescriptor(SecondaryTable.class); copyStringAttribute(annotation, element, "name", false); copyStringAttribute(annotation, element, "catalog", false); if (StringHelper.isNotEmpty(defaults.getCatalog()) && StringHelper.isEmpty((String) annotation.valueOf("catalog"))) { annotation.setValue("catalog", defaults.getCatalog()); } copyStringAttribute(annotation, element, "schema", false); if (StringHelper.isNotEmpty(defaults.getSchema()) && StringHelper.isEmpty((String) annotation.valueOf("schema"))) { annotation.setValue("schema", defaults.getSchema()); } buildUniqueConstraints(annotation, element); annotation.setValue("pkJoinColumns", buildPrimaryKeyJoinColumns(element)); secondaryTables.add((SecondaryTable) AnnotationFactory.create(annotation)); } /* * You can't have both secondary table in XML and Java, * since there would be no way to "remove" a secondary table */ if (secondaryTables.size() == 0 && defaults.canUseJavaAnnotations()) { SecondaryTable secTableAnn = getJavaAnnotation(SecondaryTable.class); overridesDefaultInSecondaryTable(secTableAnn, defaults, secondaryTables); SecondaryTables secTablesAnn = getJavaAnnotation(SecondaryTables.class); if (secTablesAnn != null) { for (SecondaryTable table : secTablesAnn.value()) { overridesDefaultInSecondaryTable(table, defaults, secondaryTables); } } } if (secondaryTables.size() > 0) { AnnotationDescriptor descriptor = new AnnotationDescriptor(SecondaryTables.class); descriptor.setValue("value", secondaryTables.toArray(new SecondaryTable[secondaryTables.size()])); return AnnotationFactory.create(descriptor); } else { return null; } } private void overridesDefaultInSecondaryTable(SecondaryTable secTableAnn, XMLContext.Default defaults, List<SecondaryTable> secondaryTables) { if (secTableAnn != null) { //handle default values if (StringHelper.isNotEmpty(defaults.getCatalog()) || StringHelper.isNotEmpty(defaults.getSchema())) { AnnotationDescriptor annotation = new AnnotationDescriptor(SecondaryTable.class); annotation.setValue("name", secTableAnn.name()); annotation.setValue("schema", secTableAnn.schema()); annotation.setValue("catalog", secTableAnn.catalog()); annotation.setValue("uniqueConstraints", secTableAnn.uniqueConstraints()); annotation.setValue("pkJoinColumns", secTableAnn.pkJoinColumns()); if (StringHelper.isEmpty((String) annotation.valueOf("schema")) && StringHelper.isNotEmpty(defaults.getSchema())) { annotation.setValue("schema", defaults.getSchema()); } if (StringHelper.isEmpty((String) annotation.valueOf("catalog")) && StringHelper.isNotEmpty(defaults.getCatalog())) { annotation.setValue("catalog", defaults.getCatalog()); } secondaryTables.add((SecondaryTable) AnnotationFactory.create(annotation)); } else { secondaryTables.add(secTableAnn); } } } private static void buildUniqueConstraints(AnnotationDescriptor annotation, Element element) { List uniqueConstraintElementList = element.elements("unique-constraint"); UniqueConstraint[] uniqueConstraints = new UniqueConstraint[uniqueConstraintElementList.size()]; int ucIndex = 0; Iterator ucIt = uniqueConstraintElementList.listIterator(); while (ucIt.hasNext()) { Element subelement = (Element) ucIt.next(); List<Element> columnNamesElements = subelement.elements("column-name"); String[] columnNames = new String[columnNamesElements.size()]; int columnNameIndex = 0; Iterator it = columnNamesElements.listIterator(); while (it.hasNext()) { Element columnNameElt = (Element) it.next(); columnNames[columnNameIndex++] = columnNameElt.getTextTrim(); } AnnotationDescriptor ucAnn = new AnnotationDescriptor(UniqueConstraint.class); ucAnn.setValue("columnNames", columnNames); uniqueConstraints[ucIndex++] = AnnotationFactory.create(ucAnn); } annotation.setValue("uniqueConstraints", uniqueConstraints); } private PrimaryKeyJoinColumn[] buildPrimaryKeyJoinColumns(Element element) { if (element == null) return new PrimaryKeyJoinColumn[] {}; List pkJoinColumnElementList = element.elements("primary-key-join-column"); PrimaryKeyJoinColumn[] pkJoinColumns = new PrimaryKeyJoinColumn[pkJoinColumnElementList.size()]; int index = 0; Iterator pkIt = pkJoinColumnElementList.listIterator(); while (pkIt.hasNext()) { Element subelement = (Element) pkIt.next(); AnnotationDescriptor pkAnn = new AnnotationDescriptor(PrimaryKeyJoinColumn.class); copyStringAttribute(pkAnn, subelement, "name", false); copyStringAttribute(pkAnn, subelement, "referenced-column-name", false); copyStringAttribute(pkAnn, subelement, "column-definition", false); pkJoinColumns[index++] = AnnotationFactory.create(pkAnn); } return pkJoinColumns; } private static void copyStringAttribute(AnnotationDescriptor annotation, Element element, String attributeName, boolean mandatory) { String attribute = element.attributeValue(attributeName); if (attribute != null) { String annotationAttributeName = getJavaAttributeNameFromXMLOne(attributeName); annotation.setValue(annotationAttributeName, attribute); } else { if (mandatory) { throw new AnnotationException(element.getName() + "." + attributeName + " is mandatory in XML overring. " + SCHEMA_VALIDATION); } } } private static void copyIntegerAttribute(AnnotationDescriptor annotation, Element element, String attributeName) { String attribute = element.attributeValue(attributeName); if (attribute != null) { String annotationAttributeName = getJavaAttributeNameFromXMLOne(attributeName); annotation.setValue(annotationAttributeName, attribute); try { int length = Integer.parseInt(attribute); annotation.setValue(annotationAttributeName, length); } catch (NumberFormatException e) { throw new AnnotationException(element.getPath() + attributeName + " not parseable: " + attribute + " (" + SCHEMA_VALIDATION + ")"); } } } private static String getJavaAttributeNameFromXMLOne(String attributeName) { StringBuilder annotationAttributeName = new StringBuilder(attributeName); int index = annotationAttributeName.indexOf(WORD_SEPARATOR); while (index != -1) { annotationAttributeName.deleteCharAt(index); annotationAttributeName.setCharAt(index, Character.toUpperCase(annotationAttributeName.charAt(index))); index = annotationAttributeName.indexOf(WORD_SEPARATOR); } return annotationAttributeName.toString(); } private static void copyStringElement(Element element, AnnotationDescriptor ad, String annotationAttribute) { String discr = element.getTextTrim(); ad.setValue(annotationAttribute, discr); } private static void copyBooleanAttribute(AnnotationDescriptor descriptor, Element element, String attribute) { String attributeValue = element.attributeValue(attribute); if (StringHelper.isNotEmpty(attributeValue)) { String javaAttribute = getJavaAttributeNameFromXMLOne(attribute); descriptor.setValue(javaAttribute, Boolean.parseBoolean(attributeValue)); } } private <T extends Annotation> T getJavaAnnotation(Class<T> annotationType) { return element.getAnnotation(annotationType); } private <T extends Annotation> boolean isJavaAnnotationPresent(Class<T> annotationType) { return element.isAnnotationPresent(annotationType); } private Annotation[] getJavaAnnotations() { return element.getAnnotations(); } }