alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

Hibernate example source code file (JPAOverridenAnnotationReader.java)

This example Hibernate source code file (JPAOverridenAnnotationReader.java) is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Java - Hibernate tags/keywords

annotation, annotation, annotationdescriptor, annotationdescriptor, annotationexception, arraylist, bean, class, element, element, javabean, list, list, reflection, schema_validation, string, string, t, util

The Hibernate JPAOverridenAnnotationReader.java source code

/*
 * Hibernate, Relational Persistence for Idiomatic Java
 *
 * Copyright (c) 2011 by Red Hat Inc and/or its affiliates or by
 * third-party contributors as indicated by either @author tags or express
 * copyright attribution statements applied by the authors.  All
 * third-party contributions are distributed under license by Red Hat Inc.
 *
 * 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.CollectionTable;
import javax.persistence.Column;
import javax.persistence.ColumnResult;
import javax.persistence.DiscriminatorColumn;
import javax.persistence.DiscriminatorType;
import javax.persistence.DiscriminatorValue;
import javax.persistence.ElementCollection;
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.MapKeyClass;
import javax.persistence.MapKeyColumn;
import javax.persistence.MapKeyEnumerated;
import javax.persistence.MapKeyJoinColumn;
import javax.persistence.MapKeyJoinColumns;
import javax.persistence.MapKeyTemporal;
import javax.persistence.MappedSuperclass;
import javax.persistence.MapsId;
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.OrderColumn;
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 org.dom4j.Attribute;
import org.dom4j.Element;
import org.hibernate.AnnotationException;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.annotations.Cascade;
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.internal.util.ReflectHelper;
import org.hibernate.internal.util.StringHelper;
import org.jboss.logging.Logger;

/**
 * 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 static final CoreMessageLogger LOG = Logger.getMessageLogger(CoreMessageLogger.class,
                                                                       JPAOverridenAnnotationReader.class.getName());
	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( AttributeOverride.class, "map-key-attribute-override" );
		annotationToXml.put( AttributeOverrides.class, "map-key-attribute-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" );
		annotationToXml.put( CollectionTable.class, "collection-table" );
		annotationToXml.put( MapKeyClass.class, "map-key-class" );
		annotationToXml.put( MapKeyTemporal.class, "map-key-temporal" );
		annotationToXml.put( MapKeyEnumerated.class, "map-key-enumerated" );
		annotationToXml.put( MapKeyColumn.class, "map-key-column" );
		annotationToXml.put( MapKeyJoinColumn.class, "map-key-join-column" );
		annotationToXml.put( MapKeyJoinColumns.class, "map-key-join-column" );
		annotationToXml.put( OrderColumn.class, "order-column" );
	}

	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 annotationType) {
		initAnnotations();
		return (T) annotationsMap.get( annotationType );
	}

	public <T extends Annotation> boolean isAnnotationPresent(Class 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( 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, true ) );
				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, true ) );
				addIfNotNull( annotationList, getAssociationOverrides( tree, defaults, true ) );
				addIfNotNull( annotationList, getEntityListeners( tree, defaults ) );
				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( 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 ) );
				}
				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();
			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.propertyNotFound(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();
			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 == 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;
		}
	}

	private void getJoinTable(List<Annotation> annotationList, Element tree, XMLContext.Default defaults) {
		addIfNotNull( annotationList, buildJoinTable( tree, defaults ) );
	}

	/*
	 * no partial overriding possible
	 */
	private JoinTable buildJoinTable(Element tree, XMLContext.Default defaults) {
		Element subelement = tree == null ? null : tree.element( "join-table" );
		final Class<JoinTable> annotationType = JoinTable.class;
		if ( subelement == null ) {
			return 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 ) );
		return AnnotationFactory.create( annotation );
	}

	/**
	 * As per section 12.2 of the JPA 2.0 specification, the association
	 * subelements (many-to-one, one-to-many, one-to-one, many-to-many,
	 * element-collection) completely override the mapping for the specified
	 * field or property.  Thus, any methods which might in some contexts merge
	 * with annotations must not do so in this context.
	 *
	 * @see #getElementCollection(List, org.hibernate.cfg.annotations.reflection.XMLContext.Default)
	 */
	private void getAssociation(
			Class<? extends Annotation> annotationType, List 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, false );
				addIfNotNull( annotationList, annotation );
				copyBooleanAttribute( ad, element, "optional" );
				copyBooleanAttribute( ad, element, "orphan-removal" );
				copyStringAttribute( ad, element, "mapped-by", false );
				getOrderBy( annotationList, element );
				getMapKey( annotationList, element );
				getMapKeyClass( annotationList, element, defaults );
				getMapKeyColumn( annotationList, element );
				getOrderColumn( annotationList, element );
				getMapKeyTemporal( annotationList, element );
				getMapKeyEnumerated( annotationList, element );
				annotation = getMapKeyAttributeOverrides( element, defaults );
				addIfNotNull( annotationList, annotation );
				buildMapKeyJoinColumns( annotationList, element );
				getAssociationId( annotationList, element );
				getMapsId( 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 );
				annotation = getJavaAnnotation( MapKeyClass.class );
				addIfNotNull( annotationList, annotation );
				annotation = getJavaAnnotation( MapKeyTemporal.class );
				addIfNotNull( annotationList, annotation );
				annotation = getJavaAnnotation( MapKeyEnumerated.class );
				addIfNotNull( annotationList, annotation );
				annotation = getJavaAnnotation( MapKeyColumn.class );
				addIfNotNull( annotationList, annotation );
				annotation = getJavaAnnotation( MapKeyJoinColumn.class );
				addIfNotNull( annotationList, annotation );
				annotation = getJavaAnnotation( MapKeyJoinColumns.class );
				addIfNotNull( annotationList, annotation );
				annotation = getJavaAnnotation( OrderColumn.class );
				addIfNotNull( annotationList, annotation );
				annotation = getJavaAnnotation( Cascade.class );
				addIfNotNull( annotationList, annotation );
			}
			else if ( isJavaAnnotationPresent( ElementCollection.class ) ) { //JPA2
				annotation = overridesDefaultsInJoinTable( getJavaAnnotation( ElementCollection.class ), defaults );
				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( OrderColumn.class );
				addIfNotNull( annotationList, annotation );
				annotation = getJavaAnnotation( MapKeyClass.class );
				addIfNotNull( annotationList, annotation );
				annotation = getJavaAnnotation( MapKeyTemporal.class );
				addIfNotNull( annotationList, annotation );
				annotation = getJavaAnnotation( MapKeyEnumerated.class );
				addIfNotNull( annotationList, annotation );
				annotation = getJavaAnnotation( MapKeyColumn.class );
				addIfNotNull( annotationList, annotation );
				annotation = getJavaAnnotation( MapKeyJoinColumn.class );
				addIfNotNull( annotationList, annotation );
				annotation = getJavaAnnotation( MapKeyJoinColumns.class );
				addIfNotNull( annotationList, annotation );
				annotation = getJavaAnnotation( CollectionTable.class );
				addIfNotNull( annotationList, annotation );
			}
		}
	}

	private void buildMapKeyJoinColumns(List<Annotation> annotationList, Element element) {
		MapKeyJoinColumn[] joinColumns = getMapKeyJoinColumns( element );
		if ( joinColumns.length > 0 ) {
			AnnotationDescriptor ad = new AnnotationDescriptor( MapKeyJoinColumns.class );
			ad.setValue( "value", joinColumns );
			annotationList.add( AnnotationFactory.create( ad ) );
		}
	}

	private MapKeyJoinColumn[] getMapKeyJoinColumns(Element element) {
		List<Element> subelements = element != null ? element.elements( "map-key-join-column" ) : null;
		List<MapKeyJoinColumn> joinColumns = new ArrayList();
		if ( subelements != null ) {
			for ( Element subelement : subelements ) {
				AnnotationDescriptor column = new AnnotationDescriptor( MapKeyJoinColumn.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( (MapKeyJoinColumn) AnnotationFactory.create( column ) );
			}
		}
		return joinColumns.toArray( new MapKeyJoinColumn[joinColumns.size()] );
	}

	private AttributeOverrides getMapKeyAttributeOverrides(Element tree, XMLContext.Default defaults) {
		List<AttributeOverride> attributes = buildAttributeOverrides( tree, "map-key-attribute-override" );
		return mergeAttributeOverrides( defaults, attributes, false );
	}

	/**
	 * Adds a @MapKeyEnumerated annotation to the specified annotationList if the specified element
	 * contains a map-key-enumerated sub-element. This should only be the case for
	 * element-collection, many-to-many, or one-to-many associations.
	 */
	private void getMapKeyEnumerated(List<Annotation> annotationList, Element element) {
		Element subelement = element != null ? element.element( "map-key-enumerated" ) : null;
		if ( subelement != null ) {
			AnnotationDescriptor ad = new AnnotationDescriptor( MapKeyEnumerated.class );
			EnumType value = EnumType.valueOf( subelement.getTextTrim() );
			ad.setValue( "value", value );
			annotationList.add( AnnotationFactory.create( ad ) );
		}
	}

	/**
	 * Adds a @MapKeyTemporal annotation to the specified annotationList if the specified element
	 * contains a map-key-temporal sub-element. This should only be the case for element-collection,
	 * many-to-many, or one-to-many associations.
	 */
	private void getMapKeyTemporal(List<Annotation> annotationList, Element element) {
		Element subelement = element != null ? element.element( "map-key-temporal" ) : null;
		if ( subelement != null ) {
			AnnotationDescriptor ad = new AnnotationDescriptor( MapKeyTemporal.class );
			TemporalType value = TemporalType.valueOf( subelement.getTextTrim() );
			ad.setValue( "value", value );
			annotationList.add( AnnotationFactory.create( ad ) );
		}
	}

	/**
	 * Adds an @OrderColumn annotation to the specified annotationList if the specified element
	 * contains an order-column sub-element. This should only be the case for element-collection,
	 * many-to-many, or one-to-many associations.
	 */
	private void getOrderColumn(List<Annotation> annotationList, Element element) {
		Element subelement = element != null ? element.element( "order-column" ) : null;
		if ( subelement != null ) {
			AnnotationDescriptor ad = new AnnotationDescriptor( OrderColumn.class );
			copyStringAttribute( ad, subelement, "name", false );
			copyBooleanAttribute( ad, subelement, "nullable" );
			copyBooleanAttribute( ad, subelement, "insertable" );
			copyBooleanAttribute( ad, subelement, "updatable" );
			copyStringAttribute( ad, subelement, "column-definition", false );
			annotationList.add( AnnotationFactory.create( ad ) );
		}
	}

	/**
	 * Adds a @MapsId annotation to the specified annotationList if the specified element has the
	 * maps-id attribute set. This should only be the case for many-to-one or one-to-one
	 * associations.
	 */
	private void getMapsId(List<Annotation> annotationList, Element element) {
		String attrVal = element.attributeValue( "maps-id" );
		if ( attrVal != null ) {
			AnnotationDescriptor ad = new AnnotationDescriptor( MapsId.class );
			ad.setValue( "value", attrVal );
			annotationList.add( AnnotationFactory.create( ad ) );
		}
	}

	/**
	 * Adds an @Id annotation to the specified annotationList if the specified element has the id
	 * attribute set to true. This should only be the case for many-to-one or one-to-one
	 * associations.
	 */
	private void getAssociationId(List<Annotation> annotationList, Element element) {
		String attrVal = element.attributeValue( "id" );
		if ( "true".equals( attrVal ) ) {
			AnnotationDescriptor ad = new AnnotationDescriptor( Id.class );
			annotationList.add( AnnotationFactory.create( ad ) );
		}
	}

	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 );
		}
	}

	/**
	 * As per sections 12.2.3.23.9, 12.2.4.8.9 and 12.2.5.3.6 of the JPA 2.0
	 * specification, the element-collection subelement completely overrides the
	 * mapping for the specified field or property.  Thus, any methods which
	 * might in some contexts merge with annotations must not do so in this
	 * context.
	 */
	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 );
				getFetchType( ad, element );
				getOrderBy( annotationList, element );
				getOrderColumn( annotationList, element );
				getMapKey( annotationList, element );
				getMapKeyClass( annotationList, element, defaults );
				getMapKeyTemporal( annotationList, element );
				getMapKeyEnumerated( annotationList, element );
				getMapKeyColumn( annotationList, element );
				buildMapKeyJoinColumns( annotationList, element );
				Annotation annotation = getColumn( element.element( "column" ), false, element );
				addIfNotNull( annotationList, annotation );
				getTemporal( annotationList, element );
				getEnumerated( annotationList, element );
				getLob( annotationList, element );
				//Both map-key-attribute-overrides and attribute-overrides
				//translate into AttributeOverride annotations, which need
				//need to be wrapped in the same AttributeOverrides annotation.
				List<AttributeOverride> attributes = new ArrayList();
				attributes.addAll( buildAttributeOverrides( element, "map-key-attribute-override" ) );
				attributes.addAll( buildAttributeOverrides( element, "attribute-override" ) );
				annotation = mergeAttributeOverrides( defaults, attributes, false );
				addIfNotNull( annotationList, annotation );
				annotation = getAssociationOverrides( element, defaults, false );
				addIfNotNull( annotationList, annotation );
				getCollectionTable( annotationList, element, 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 ) {
			AnnotationDescriptor ad = new AnnotationDescriptor( OrderBy.class );
			copyStringElement( subelement, ad, "value" );
			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 ) {
			AnnotationDescriptor ad = new AnnotationDescriptor( MapKey.class );
			copyStringAttribute( ad, subelement, "name", false );
			annotationList.add( AnnotationFactory.create( ad ) );
		}
	}

	private void getMapKeyColumn(List<Annotation> annotationList, Element element) {
		Element subelement = element != null ? element.element( "map-key-column" ) : null;
		if ( subelement != null ) {
			AnnotationDescriptor ad = new AnnotationDescriptor( MapKeyColumn.class );
			copyStringAttribute( ad, subelement, "name", false );
			copyBooleanAttribute( ad, subelement, "unique" );
			copyBooleanAttribute( ad, subelement, "nullable" );
			copyBooleanAttribute( ad, subelement, "insertable" );
			copyBooleanAttribute( ad, subelement, "updatable" );
			copyStringAttribute( ad, subelement, "column-definition", false );
			copyStringAttribute( ad, subelement, "table", false );
			copyIntegerAttribute( ad, subelement, "length" );
			copyIntegerAttribute( ad, subelement, "precision" );
			copyIntegerAttribute( ad, subelement, "scale" );
			annotationList.add( AnnotationFactory.create( ad ) );
		}
	}

	private void getMapKeyClass(List<Annotation> annotationList, Element element, XMLContext.Default defaults) {
		String nodeName = "map-key-class";
		Element subelement = element != null ? element.element( nodeName ) : null;
		if ( subelement != null ) {
			String mapKeyClassName = subelement.attributeValue( "class" );
			AnnotationDescriptor ad = new AnnotationDescriptor( MapKeyClass.class );
			if ( StringHelper.isNotEmpty( mapKeyClassName ) ) {
				Class clazz;
				try {
					clazz = ReflectHelper.classForName(
							XMLContext.buildSafeClassName( mapKeyClassName, defaults ),
							this.getClass()
					);
				}
				catch ( ClassNotFoundException e ) {
					throw new AnnotationException(
							"Unable to find " + element.getPath() + " " + nodeName + ": " + mapKeyClassName, e
					);
				}
				ad.setValue( "value", clazz );
			}
			annotationList.add( AnnotationFactory.create( ad ) );
		}
	}

	private void getCollectionTable(List<Annotation> annotationList, Element element, XMLContext.Default defaults) {
		Element subelement = element != null ? element.element( "collection-table" ) : null;
		if ( subelement != null ) {
			AnnotationDescriptor annotation = new AnnotationDescriptor( CollectionTable.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() );
			}
			JoinColumn[] joinColumns = getJoinColumns( subelement, false );
			if ( joinColumns.length > 0 ) {
				annotation.setValue( "joinColumns", joinColumns );
			}
			buildUniqueConstraints( annotation, subelement );
			annotationList.add( AnnotationFactory.create( annotation ) );
		}
	}

	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( 0 );
		List<CascadeType> cascades = new ArrayList();
		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 ) );
				Annotation annotation = getAttributeOverrides( element, defaults, false );
				addIfNotNull( annotationList, annotation );
				annotation = getAssociationOverrides( element, defaults, false );
				addIfNotNull( annotationList, annotation );
				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, false );
					addIfNotNull( annotationList, annotation );
					annotation = getAssociationOverrides( element, defaults, false );
					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( 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 ) );
		}
	}

	/**
	 * @param mergeWithAnnotations Whether to use Java annotations for this
	 * element, if present and not disabled by the XMLContext defaults.
	 * In some contexts (such as an element-collection mapping) merging
	 * with annotations is never allowed.
	 */
	private AssociationOverrides getAssociationOverrides(Element tree, XMLContext.Default defaults, boolean mergeWithAnnotations) {
		List<AssociationOverride> attributes = buildAssociationOverrides( tree, defaults );
		if ( mergeWithAnnotations && 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, XMLContext.Default defaults) {
		List<Element> subelements = element == null ? null : element.elements( "association-override" );
		List<AssociationOverride> overrides = new ArrayList();
		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 ) );
				JoinTable joinTable = buildJoinTable( current, defaults );
				if ( joinTable != null ) {
					override.setValue( "joinTable", joinTable );
				}
				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();
		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 );
			}
		}
	}

	/**
	 * @param mergeWithAnnotations Whether to use Java annotations for this
	 * element, if present and not disabled by the XMLContext defaults.
	 * In some contexts (such as an association mapping) merging with
	 * annotations is never allowed.
	 */
	private AttributeOverrides getAttributeOverrides(Element tree, XMLContext.Default defaults, boolean mergeWithAnnotations) {
		List<AttributeOverride> attributes = buildAttributeOverrides( tree, "attribute-override" );
		return mergeAttributeOverrides( defaults, attributes, mergeWithAnnotations );
	}

	/**
	 * @param mergeWithAnnotations Whether to use Java annotations for this
	 * element, if present and not disabled by the XMLContext defaults.
	 * In some contexts (such as an association mapping) merging with
	 * annotations is never allowed.
	 */
	private AttributeOverrides mergeAttributeOverrides(XMLContext.Default defaults, List<AttributeOverride> attributes, boolean mergeWithAnnotations) {
		if ( mergeWithAnnotations && 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, String nodeName) {
		List<Element> subelements = element == null ? null : element.elements( nodeName );
		return buildAttributeOverrides( subelements, nodeName );
	}

	private List<AttributeOverride> buildAttributeOverrides(List subelements, String nodeName) {
		List<AttributeOverride> overrides = new ArrayList();
		if ( subelements != null && subelements.size() > 0 ) {
			for ( Element current : subelements ) {
				if ( !current.getName().equals( nodeName ) ) {
					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 = 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();
		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( 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();
				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( 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) 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) 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 );
			}
			copyStringElement( queryElt, ann, "query" );
			List<Element> elements = subelement.elements( "hint" );
			List<QueryHint> queryHints = new ArrayList( 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;
		}
	}

	/**
	 * @param mergeWithAnnotations Whether to use Java annotations for this
	 * element, if present and not disabled by the XMLContext defaults.
	 * In some contexts (such as an association mapping) merging with
	 * annotations is never allowed.
	 */
	private PrimaryKeyJoinColumns getPrimaryKeyJoinColumns(Element element, XMLContext.Default defaults, boolean mergeWithAnnotations) {
		PrimaryKeyJoinColumn[] columns = buildPrimaryKeyJoinColumns( element );
		if ( mergeWithAnnotations ) {
			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( 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 );
			copyStringAttribute( ucAnn, subelement, "name", false );
			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 overriding. " + 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 annotationType) {
		return element.getAnnotation( annotationType );
	}

	private <T extends Annotation> boolean isJavaAnnotationPresent(Class annotationType) {
		return element.isAnnotationPresent( annotationType );
	}

	private Annotation[] getJavaAnnotations() {
		return element.getAnnotations();
	}
}

Other Hibernate examples (source code examples)

Here is a short list of links related to this Hibernate JPAOverridenAnnotationReader.java source code file:

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.