You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cayenne.apache.org by aa...@apache.org on 2006/04/26 22:58:50 UTC
svn commit: r396882 [3/11] - in /incubator/cayenne: branches/ tags/ trunk/
trunk/cayenne-jpa-tck/ trunk/cayenne-jpa-tck/.settings/
trunk/cayenne-jpa-tck/src/ trunk/cayenne-jpa-tck/src/main/
trunk/cayenne-jpa-tck/src/main/java/ trunk/cayenne-jpa-tck/src...
Added: incubator/cayenne/trunk/cayenne-jpa/src/main/java/org/objectstyle/cayenne/jpa/conf/ClassAnnotationProcessorFactory.java
URL: http://svn.apache.org/viewcvs/incubator/cayenne/trunk/cayenne-jpa/src/main/java/org/objectstyle/cayenne/jpa/conf/ClassAnnotationProcessorFactory.java?rev=396882&view=auto
==============================================================================
--- incubator/cayenne/trunk/cayenne-jpa/src/main/java/org/objectstyle/cayenne/jpa/conf/ClassAnnotationProcessorFactory.java (added)
+++ incubator/cayenne/trunk/cayenne-jpa/src/main/java/org/objectstyle/cayenne/jpa/conf/ClassAnnotationProcessorFactory.java Tue Apr 25 06:43:00 2006
@@ -0,0 +1,726 @@
+/* ====================================================================
+ *
+ * The ObjectStyle Group Software License, version 1.1
+ * ObjectStyle Group - http://objectstyle.org/
+ *
+ * Copyright (c) 2002-2005, Andrei (Andrus) Adamchik and individual authors
+ * of the software. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution, if any,
+ * must include the following acknowlegement:
+ * "This product includes software developed by independent contributors
+ * and hosted on ObjectStyle Group web site (http://objectstyle.org/)."
+ * Alternately, this acknowlegement may appear in the software itself,
+ * if and wherever such third-party acknowlegements normally appear.
+ *
+ * 4. The names "ObjectStyle Group" and "Cayenne" must not be used to endorse
+ * or promote products derived from this software without prior written
+ * permission. For written permission, email
+ * "andrus at objectstyle dot org".
+ *
+ * 5. Products derived from this software may not be called "ObjectStyle"
+ * or "Cayenne", nor may "ObjectStyle" or "Cayenne" appear in their
+ * names without prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE OBJECTSTYLE GROUP OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals and hosted on ObjectStyle Group web site. For more
+ * information on the ObjectStyle Group, please see
+ * <http://objectstyle.org/>.
+ */
+package org.objectstyle.cayenne.jpa.conf;
+
+import java.lang.reflect.AnnotatedElement;
+
+import javax.persistence.AttributeOverride;
+import javax.persistence.AttributeOverrides;
+import javax.persistence.DiscriminatorColumn;
+import javax.persistence.DiscriminatorValue;
+import javax.persistence.Entity;
+import javax.persistence.EntityListeners;
+import javax.persistence.IdClass;
+import javax.persistence.Inheritance;
+import javax.persistence.NamedNativeQueries;
+import javax.persistence.NamedNativeQuery;
+import javax.persistence.NamedQueries;
+import javax.persistence.NamedQuery;
+import javax.persistence.PrimaryKeyJoinColumn;
+import javax.persistence.PrimaryKeyJoinColumns;
+import javax.persistence.SecondaryTable;
+import javax.persistence.SecondaryTables;
+import javax.persistence.SequenceGenerator;
+import javax.persistence.SqlResultSetMapping;
+import javax.persistence.Table;
+import javax.persistence.TableGenerator;
+
+import org.objectstyle.cayenne.jpa.map.JpaAttributeOverride;
+import org.objectstyle.cayenne.jpa.map.JpaDiscriminatorColumn;
+import org.objectstyle.cayenne.jpa.map.JpaEmbeddable;
+import org.objectstyle.cayenne.jpa.map.JpaEntity;
+import org.objectstyle.cayenne.jpa.map.JpaEntityListener;
+import org.objectstyle.cayenne.jpa.map.JpaEntityListeners;
+import org.objectstyle.cayenne.jpa.map.JpaEntityMap;
+import org.objectstyle.cayenne.jpa.map.JpaInheritance;
+import org.objectstyle.cayenne.jpa.map.JpaMappedSuperclass;
+import org.objectstyle.cayenne.jpa.map.JpaNamedNativeQuery;
+import org.objectstyle.cayenne.jpa.map.JpaNamedQuery;
+import org.objectstyle.cayenne.jpa.map.JpaPrimaryKeyJoinColumn;
+import org.objectstyle.cayenne.jpa.map.JpaSecondaryTable;
+import org.objectstyle.cayenne.jpa.map.JpaSequenceGenerator;
+import org.objectstyle.cayenne.jpa.map.JpaSqlResultSetMapping;
+import org.objectstyle.cayenne.jpa.map.JpaTable;
+import org.objectstyle.cayenne.jpa.map.JpaTableGenerator;
+import org.objectstyle.cayenne.util.Util;
+
+/**
+ * A factory of class annotation processors.
+ *
+ * @author Andrus Adamchik
+ */
+class ClassAnnotationProcessorFactory extends AnnotationProcessorFactory {
+
+ static final class EntityProcessor implements AnnotationProcessor {
+
+ public void onStartElement(
+ AnnotatedElement element,
+ AnnotationProcessorContext context) {
+ Entity entityAnnotation = element.getAnnotation(Entity.class);
+
+ JpaEntity entity = new JpaEntity();
+ entity.setClassName(((Class) element).getName());
+
+ if (Util.isEmptyString(entityAnnotation.name())) {
+
+ // per JPA spec use unqualified class name
+ String fqName = ((Class) element).getName();
+ int split = fqName.lastIndexOf('.');
+ entity.setName(split > 0 ? fqName.substring(split + 1) : fqName);
+ }
+ else {
+ entity.setName(entityAnnotation.name());
+ }
+
+ context.push(entity);
+ }
+
+ public void onFinishElement(
+ AnnotatedElement element,
+ AnnotationProcessorContext context) {
+ JpaEntity entity = (JpaEntity) context.pop();
+ JpaEntityMap entityMap = (JpaEntityMap) context.peek();
+ entityMap.getEntities().add(entity);
+ }
+ }
+
+ static final class EmbeddableProcessor implements AnnotationProcessor {
+
+ public void onStartElement(
+ AnnotatedElement element,
+ AnnotationProcessorContext context) {
+
+ JpaEmbeddable embeddable = new JpaEmbeddable();
+ embeddable.setClassName(((Class) element).getName());
+ context.push(embeddable);
+ }
+
+ public void onFinishElement(
+ AnnotatedElement element,
+ AnnotationProcessorContext context) {
+ JpaEmbeddable embeddable = (JpaEmbeddable) context.pop();
+ JpaEntityMap entityMap = (JpaEntityMap) context.peek();
+ entityMap.getEmbeddables().add(embeddable);
+ }
+ }
+
+ static final class MappedSuperclassProcessor implements AnnotationProcessor {
+
+ public void onStartElement(
+ AnnotatedElement element,
+ AnnotationProcessorContext context) {
+
+ JpaMappedSuperclass superclass = new JpaMappedSuperclass();
+ superclass.setClassName(((Class) element).getName());
+ context.push(superclass);
+ }
+
+ public void onFinishElement(
+ AnnotatedElement element,
+ AnnotationProcessorContext context) {
+ JpaMappedSuperclass superclass = (JpaMappedSuperclass) context.pop();
+ JpaEntityMap entityMap = (JpaEntityMap) context.peek();
+ entityMap.getMappedSuperclasses().add(superclass);
+ }
+ }
+
+ /**
+ * A superclass of all processors that for class annotations that do not define the
+ * type of the mapping.
+ */
+ abstract static class AbstractChildProcessor implements AnnotationProcessor {
+
+ public void onStartElement(
+ AnnotatedElement element,
+ AnnotationProcessorContext context) {
+
+ Object parent = context.peek();
+
+ if (parent instanceof JpaEntity) {
+ onEntity((JpaEntity) parent, element, context);
+ }
+ else if (parent instanceof JpaMappedSuperclass) {
+ onMappedSuperclass((JpaMappedSuperclass) parent, element, context);
+ }
+ else if (parent instanceof JpaEntityMap) {
+ onEntityMap((JpaEntityMap) parent, element, context);
+ }
+ else if (parent instanceof JpaEmbeddable) {
+ onEmbeddable((JpaEmbeddable) parent, element, context);
+ }
+ else {
+ recordUnsupportedAnnotation(element, context);
+ }
+ }
+
+ public void onFinishElement(
+ AnnotatedElement element,
+ AnnotationProcessorContext context) {
+ // noop
+ }
+
+ void onEntity(
+ JpaEntity entity,
+ AnnotatedElement element,
+ AnnotationProcessorContext context) {
+ recordUnsupportedAnnotation(element, context);
+ }
+
+ void onMappedSuperclass(
+ JpaMappedSuperclass superclass,
+ AnnotatedElement element,
+ AnnotationProcessorContext context) {
+ recordUnsupportedAnnotation(element, context);
+ }
+
+ void onEmbeddable(
+ JpaEmbeddable embeddable,
+ AnnotatedElement element,
+ AnnotationProcessorContext context) {
+ recordUnsupportedAnnotation(element, context);
+ }
+
+ // not sure if this is compatible with the spec, but such annotations are
+ // possible...
+ void onEntityMap(
+ JpaEntityMap entityMap,
+ AnnotatedElement element,
+ AnnotationProcessorContext context) {
+ recordUnsupportedAnnotation(element, context);
+ }
+
+ void recordUnsupportedAnnotation(
+ AnnotatedElement element,
+ AnnotationProcessorContext context) {
+ context.recordConflict(element, AnnotationProcessorFactory
+ .annotationClass(getClass()), "Unsupported in this place");
+ }
+ }
+
+ static final class AttributeOverrideProcessor extends AbstractChildProcessor {
+
+ @Override
+ void onEntity(
+ JpaEntity entity,
+ AnnotatedElement element,
+ AnnotationProcessorContext context) {
+
+ AttributeOverride annotation = element.getAnnotation(AttributeOverride.class);
+ entity.getAttributeOverrides().add(new JpaAttributeOverride(annotation));
+ }
+ }
+
+ static final class AttributeOverridesProcessor extends AbstractChildProcessor {
+
+ @Override
+ void onEntity(
+ JpaEntity entity,
+ AnnotatedElement element,
+ AnnotationProcessorContext context) {
+
+ AttributeOverrides annotation = element
+ .getAnnotation(AttributeOverrides.class);
+ for (int i = 0; i < annotation.value().length; i++) {
+ entity.getAttributeOverrides().add(
+ new JpaAttributeOverride(annotation.value()[i]));
+ }
+ }
+ }
+
+ static final class DiscriminatorColumnProcessor extends AbstractChildProcessor {
+
+ @Override
+ void onEntity(
+ JpaEntity entity,
+ AnnotatedElement element,
+ AnnotationProcessorContext context) {
+
+ DiscriminatorColumn annotation = element
+ .getAnnotation(DiscriminatorColumn.class);
+ entity.setDiscriminatorColumn(new JpaDiscriminatorColumn(annotation));
+ }
+ }
+
+ static final class DiscriminatorValueProcessor extends AbstractChildProcessor {
+
+ @Override
+ void onEntity(
+ JpaEntity entity,
+ AnnotatedElement element,
+ AnnotationProcessorContext context) {
+
+ DiscriminatorValue annotation = element
+ .getAnnotation(DiscriminatorValue.class);
+ entity.setDiscriminatorValue(annotation.value());
+ }
+ }
+
+ static final class EntityListenersProcessor extends AbstractChildProcessor {
+
+ private EntityListenerAnnotationLoader listenerLoader;
+
+ @Override
+ void onEntityMap(
+ JpaEntityMap entityMap,
+ AnnotatedElement element,
+ AnnotationProcessorContext context) {
+
+ EntityListeners annotation = element.getAnnotation(EntityListeners.class);
+
+ if (annotation.value().length > 0) {
+ if (listenerLoader == null) {
+ listenerLoader = new EntityListenerAnnotationLoader();
+ }
+
+ JpaEntityListeners listenerHolder = entityMap.getDefaultEntityListeners();
+ if (listenerHolder == null) {
+ listenerHolder = new JpaEntityListeners();
+ entityMap.setDefaultEntityListeners(listenerHolder);
+ }
+
+ for (int i = 0; i < annotation.value().length; i++) {
+ JpaEntityListener listener = listenerLoader
+ .getEntityListener(annotation.value()[i]);
+ if (listener != null) {
+ listenerHolder.getEntityListeners().add(listener);
+ }
+ }
+ }
+ }
+
+ @Override
+ void onEntity(
+ JpaEntity entity,
+ AnnotatedElement element,
+ AnnotationProcessorContext context) {
+
+ EntityListeners annotation = element.getAnnotation(EntityListeners.class);
+
+ if (annotation.value().length > 0) {
+ if (listenerLoader == null) {
+ listenerLoader = new EntityListenerAnnotationLoader();
+ }
+
+ JpaEntityListeners listenerHolder = entity.getEntityListeners();
+ if (listenerHolder == null) {
+ listenerHolder = new JpaEntityListeners();
+ entity.setEntityListeners(listenerHolder);
+ }
+
+ for (int i = 0; i < annotation.value().length; i++) {
+ JpaEntityListener listener = listenerLoader
+ .getEntityListener(annotation.value()[i]);
+ if (listener != null) {
+ listenerHolder.getEntityListeners().add(listener);
+ }
+ }
+ }
+ }
+
+ @Override
+ void onMappedSuperclass(
+ JpaMappedSuperclass superclass,
+ AnnotatedElement element,
+ AnnotationProcessorContext context) {
+
+ EntityListeners annotation = element.getAnnotation(EntityListeners.class);
+
+ if (annotation.value().length > 0) {
+ if (listenerLoader == null) {
+ listenerLoader = new EntityListenerAnnotationLoader();
+ }
+
+ // superclass takes care of creating a non-null instance lazily
+ JpaEntityListeners listenerHolder = superclass.getEntityListeners();
+
+ for (int i = 0; i < annotation.value().length; i++) {
+ JpaEntityListener listener = listenerLoader
+ .getEntityListener(annotation.value()[i]);
+ if (listener != null) {
+ listenerHolder.getEntityListeners().add(listener);
+ }
+ }
+ }
+ }
+ }
+
+ static final class ExcludeDefaultListenersProcessor extends AbstractChildProcessor {
+
+ @Override
+ void onEntity(
+ JpaEntity entity,
+ AnnotatedElement element,
+ AnnotationProcessorContext context) {
+
+ entity.setExcludeDefaultListeners(true);
+ }
+
+ @Override
+ void onMappedSuperclass(
+ JpaMappedSuperclass superclass,
+ AnnotatedElement element,
+ AnnotationProcessorContext context) {
+ superclass.setExcludeDefaultListeners(true);
+ }
+ }
+
+ static final class ExcludeSuperclassListenersProcessor extends AbstractChildProcessor {
+
+ @Override
+ void onEntity(
+ JpaEntity entity,
+ AnnotatedElement element,
+ AnnotationProcessorContext context) {
+
+ entity.setExcludeSuperclassListeners(true);
+ }
+
+ @Override
+ void onMappedSuperclass(
+ JpaMappedSuperclass superclass,
+ AnnotatedElement element,
+ AnnotationProcessorContext context) {
+ superclass.setExcludeSuperclassListeners(true);
+ }
+ }
+
+ static final class IdClassProcessor extends AbstractChildProcessor {
+
+ @Override
+ void onEntity(
+ JpaEntity entity,
+ AnnotatedElement element,
+ AnnotationProcessorContext context) {
+
+ IdClass annotation = element.getAnnotation(IdClass.class);
+ entity.setIdClassName(annotation.value().getName());
+ }
+
+ @Override
+ void onMappedSuperclass(
+ JpaMappedSuperclass superclass,
+ AnnotatedElement element,
+ AnnotationProcessorContext context) {
+ IdClass annotation = element.getAnnotation(IdClass.class);
+ superclass.setIdClassName(annotation.value().getName());
+ }
+ }
+
+ static final class InheritanceProcessor extends AbstractChildProcessor {
+
+ @Override
+ void onEntity(
+ JpaEntity entity,
+ AnnotatedElement element,
+ AnnotationProcessorContext context) {
+
+ Inheritance annotation = element.getAnnotation(Inheritance.class);
+ entity.setInheritance(new JpaInheritance(annotation));
+ }
+ }
+
+ static final class NamedNativeQueriesProcessor extends AbstractChildProcessor {
+
+ @Override
+ void onEntity(
+ JpaEntity entity,
+ AnnotatedElement element,
+ AnnotationProcessorContext context) {
+
+ NamedNativeQueries annotation = element
+ .getAnnotation(NamedNativeQueries.class);
+ for (int i = 0; i < annotation.value().length; i++) {
+ entity.getNamedNativeQueries().add(
+ new JpaNamedNativeQuery(annotation.value()[i]));
+ }
+ }
+
+ @Override
+ void onEntityMap(
+ JpaEntityMap entityMap,
+ AnnotatedElement element,
+ AnnotationProcessorContext context) {
+
+ NamedNativeQueries annotation = element
+ .getAnnotation(NamedNativeQueries.class);
+ for (int i = 0; i < annotation.value().length; i++) {
+ entityMap.getNamedNativeQueries().add(
+ new JpaNamedNativeQuery(annotation.value()[i]));
+ }
+ }
+ }
+
+ static final class NamedNativeQueryProcessor extends AbstractChildProcessor {
+
+ @Override
+ void onEntity(
+ JpaEntity entity,
+ AnnotatedElement element,
+ AnnotationProcessorContext context) {
+
+ NamedNativeQuery annotation = element.getAnnotation(NamedNativeQuery.class);
+ entity.getNamedNativeQueries().add(new JpaNamedNativeQuery(annotation));
+ }
+
+ @Override
+ void onEntityMap(
+ JpaEntityMap entityMap,
+ AnnotatedElement element,
+ AnnotationProcessorContext context) {
+
+ NamedNativeQuery annotation = element.getAnnotation(NamedNativeQuery.class);
+ entityMap.getNamedNativeQueries().add(new JpaNamedNativeQuery(annotation));
+ }
+ }
+
+ static final class NamedQueriesProcessor extends AbstractChildProcessor {
+
+ @Override
+ void onEntity(
+ JpaEntity entity,
+ AnnotatedElement element,
+ AnnotationProcessorContext context) {
+
+ NamedQueries annotation = element.getAnnotation(NamedQueries.class);
+ for (int i = 0; i < annotation.value().length; i++) {
+ entity.getNamedQueries().add(new JpaNamedQuery(annotation.value()[i]));
+ }
+ }
+
+ @Override
+ void onEntityMap(
+ JpaEntityMap entityMap,
+ AnnotatedElement element,
+ AnnotationProcessorContext context) {
+
+ NamedQueries annotation = element.getAnnotation(NamedQueries.class);
+ for (int i = 0; i < annotation.value().length; i++) {
+ entityMap.getNamedQueries().add(new JpaNamedQuery(annotation.value()[i]));
+ }
+ }
+ }
+
+ static final class NamedQueryProcessor extends AbstractChildProcessor {
+
+ @Override
+ void onEntity(
+ JpaEntity entity,
+ AnnotatedElement element,
+ AnnotationProcessorContext context) {
+
+ NamedQuery annotation = element.getAnnotation(NamedQuery.class);
+ entity.getNamedQueries().add(new JpaNamedQuery(annotation));
+ }
+
+ @Override
+ void onEntityMap(
+ JpaEntityMap entityMap,
+ AnnotatedElement element,
+ AnnotationProcessorContext context) {
+
+ NamedQuery annotation = element.getAnnotation(NamedQuery.class);
+ entityMap.getNamedQueries().add(new JpaNamedQuery(annotation));
+ }
+ }
+
+ static final class PrimaryKeyJoinColumnProcessor extends AbstractChildProcessor {
+
+ @Override
+ void onEntity(
+ JpaEntity entity,
+ AnnotatedElement element,
+ AnnotationProcessorContext context) {
+
+ PrimaryKeyJoinColumn annotation = element
+ .getAnnotation(PrimaryKeyJoinColumn.class);
+ entity
+ .getPrimaryKeyJoinColumns()
+ .add(new JpaPrimaryKeyJoinColumn(annotation));
+ }
+ }
+
+ static final class PrimaryKeyJoinColumnsProcessor extends AbstractChildProcessor {
+
+ @Override
+ void onEntity(
+ JpaEntity entity,
+ AnnotatedElement element,
+ AnnotationProcessorContext context) {
+
+ PrimaryKeyJoinColumns annotation = element
+ .getAnnotation(PrimaryKeyJoinColumns.class);
+ for (int i = 0; i < annotation.value().length; i++) {
+ entity.getPrimaryKeyJoinColumns().add(
+ new JpaPrimaryKeyJoinColumn(annotation.value()[i]));
+ }
+ }
+ }
+
+ static final class SecondaryTableProcessor extends AbstractChildProcessor {
+
+ @Override
+ void onEntity(
+ JpaEntity entity,
+ AnnotatedElement element,
+ AnnotationProcessorContext context) {
+
+ SecondaryTable annotation = element.getAnnotation(SecondaryTable.class);
+ entity.getSecondaryTables().add(new JpaSecondaryTable(annotation));
+ }
+ }
+
+ static final class SecondaryTablesProcessor extends AbstractChildProcessor {
+
+ @Override
+ void onEntity(
+ JpaEntity entity,
+ AnnotatedElement element,
+ AnnotationProcessorContext context) {
+
+ SecondaryTables annotation = element.getAnnotation(SecondaryTables.class);
+ for (int i = 0; i < annotation.value().length; i++) {
+ entity.getSecondaryTables().add(
+ new JpaSecondaryTable(annotation.value()[i]));
+ }
+ }
+ }
+
+ static final class SequenceGeneratorProcessor extends AbstractChildProcessor {
+
+ @Override
+ void onEntity(
+ JpaEntity entity,
+ AnnotatedElement element,
+ AnnotationProcessorContext context) {
+
+ SequenceGenerator annotation = element.getAnnotation(SequenceGenerator.class);
+ entity.setSequenceGenerator(new JpaSequenceGenerator(annotation));
+ }
+
+ @Override
+ void onEntityMap(
+ JpaEntityMap entityMap,
+ AnnotatedElement element,
+ AnnotationProcessorContext context) {
+
+ SequenceGenerator annotation = element.getAnnotation(SequenceGenerator.class);
+ entityMap.getSequenceGenerators().add(new JpaSequenceGenerator(annotation));
+ }
+ }
+
+ static final class SqlResultSetMappingProcessor extends AbstractChildProcessor {
+
+ @Override
+ void onEntity(
+ JpaEntity entity,
+ AnnotatedElement element,
+ AnnotationProcessorContext context) {
+
+ SqlResultSetMapping annotation = element
+ .getAnnotation(SqlResultSetMapping.class);
+ entity.setSqlResultSetMapping(new JpaSqlResultSetMapping(annotation));
+ }
+
+ @Override
+ void onEntityMap(
+ JpaEntityMap entityMap,
+ AnnotatedElement element,
+ AnnotationProcessorContext context) {
+
+ SqlResultSetMapping annotation = element
+ .getAnnotation(SqlResultSetMapping.class);
+ entityMap.getSqlResultSetMappings().add(
+ new JpaSqlResultSetMapping(annotation));
+ }
+ }
+
+ static final class TableGeneratorProcessor extends AbstractChildProcessor {
+
+ @Override
+ void onEntity(
+ JpaEntity entity,
+ AnnotatedElement element,
+ AnnotationProcessorContext context) {
+
+ TableGenerator annotation = element.getAnnotation(TableGenerator.class);
+ entity.setTableGenerator(new JpaTableGenerator(annotation));
+ }
+
+ @Override
+ void onEntityMap(
+ JpaEntityMap entityMap,
+ AnnotatedElement element,
+ AnnotationProcessorContext context) {
+
+ TableGenerator annotation = element.getAnnotation(TableGenerator.class);
+ entityMap.getTableGenerators().add(new JpaTableGenerator(annotation));
+ }
+ }
+
+ static final class TableProcessor extends AbstractChildProcessor {
+
+ @Override
+ void onEntity(
+ JpaEntity entity,
+ AnnotatedElement element,
+ AnnotationProcessorContext context) {
+
+ Table annotation = element.getAnnotation(Table.class);
+ entity.setTable(new JpaTable(annotation));
+ }
+ }
+
+}
Added: incubator/cayenne/trunk/cayenne-jpa/src/main/java/org/objectstyle/cayenne/jpa/conf/EntityCallbackAnnotationProcessorFactory.java
URL: http://svn.apache.org/viewcvs/incubator/cayenne/trunk/cayenne-jpa/src/main/java/org/objectstyle/cayenne/jpa/conf/EntityCallbackAnnotationProcessorFactory.java?rev=396882&view=auto
==============================================================================
--- incubator/cayenne/trunk/cayenne-jpa/src/main/java/org/objectstyle/cayenne/jpa/conf/EntityCallbackAnnotationProcessorFactory.java (added)
+++ incubator/cayenne/trunk/cayenne-jpa/src/main/java/org/objectstyle/cayenne/jpa/conf/EntityCallbackAnnotationProcessorFactory.java Tue Apr 25 06:43:00 2006
@@ -0,0 +1,142 @@
+/* ====================================================================
+ *
+ * The ObjectStyle Group Software License, version 1.1
+ * ObjectStyle Group - http://objectstyle.org/
+ *
+ * Copyright (c) 2002-2005, Andrei (Andrus) Adamchik and individual authors
+ * of the software. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution, if any,
+ * must include the following acknowlegement:
+ * "This product includes software developed by independent contributors
+ * and hosted on ObjectStyle Group web site (http://objectstyle.org/)."
+ * Alternately, this acknowlegement may appear in the software itself,
+ * if and wherever such third-party acknowlegements normally appear.
+ *
+ * 4. The names "ObjectStyle Group" and "Cayenne" must not be used to endorse
+ * or promote products derived from this software without prior written
+ * permission. For written permission, email
+ * "andrus at objectstyle dot org".
+ *
+ * 5. Products derived from this software may not be called "ObjectStyle"
+ * or "Cayenne", nor may "ObjectStyle" or "Cayenne" appear in their
+ * names without prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE OBJECTSTYLE GROUP OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals and hosted on ObjectStyle Group web site. For more
+ * information on the ObjectStyle Group, please see
+ * <http://objectstyle.org/>.
+ */
+package org.objectstyle.cayenne.jpa.conf;
+
+import java.lang.reflect.AnnotatedElement;
+import java.lang.reflect.Method;
+
+import org.objectstyle.cayenne.jpa.map.JpaAbstractEntity;
+import org.objectstyle.cayenne.jpa.map.JpaLifecycleCallback;
+
+class EntityCallbackAnnotationProcessorFactory extends AnnotationProcessorFactory {
+
+ static abstract class AbstractCallbackAnnotationProcessor implements
+ AnnotationProcessor {
+
+ public void onStartElement(
+ AnnotatedElement element,
+ AnnotationProcessorContext context) {
+
+ Method m = (Method) element;
+ updateEntity((JpaAbstractEntity) context.peek(), new JpaLifecycleCallback(m
+ .getName()));
+ }
+
+ public void onFinishElement(
+ AnnotatedElement element,
+ AnnotationProcessorContext context) {
+ // noop...
+ }
+
+ abstract void updateEntity(JpaAbstractEntity entity, JpaLifecycleCallback callback);
+ }
+
+ static final class PrePersistProcessor extends AbstractCallbackAnnotationProcessor {
+
+ @Override
+ void updateEntity(JpaAbstractEntity entity, JpaLifecycleCallback callback) {
+ entity.setPrePersist(callback);
+ }
+ }
+
+ static final class PostPersistProcessor extends AbstractCallbackAnnotationProcessor {
+
+ @Override
+ void updateEntity(JpaAbstractEntity entity, JpaLifecycleCallback callback) {
+ entity.setPostPersist(callback);
+ }
+ }
+
+ static final class PreRemoveProcessor extends AbstractCallbackAnnotationProcessor {
+
+ @Override
+ void updateEntity(JpaAbstractEntity entity, JpaLifecycleCallback callback) {
+ entity.setPreRemove(callback);
+ }
+ }
+
+ static final class PostRemoveProcessor extends AbstractCallbackAnnotationProcessor {
+
+ @Override
+ void updateEntity(JpaAbstractEntity entity, JpaLifecycleCallback callback) {
+ entity.setPostRemove(callback);
+ }
+ }
+
+ static final class PreUpdateProcessor extends AbstractCallbackAnnotationProcessor {
+
+ @Override
+ void updateEntity(JpaAbstractEntity entity, JpaLifecycleCallback callback) {
+ entity.setPreUpdate(callback);
+ }
+ }
+
+ static final class PostUpdateProcessor extends AbstractCallbackAnnotationProcessor {
+
+ @Override
+ void updateEntity(JpaAbstractEntity entity, JpaLifecycleCallback callback) {
+ entity.setPostUpdate(callback);
+ }
+ }
+
+ static final class PostLoadProcessor extends AbstractCallbackAnnotationProcessor {
+
+ @Override
+ void updateEntity(JpaAbstractEntity entity, JpaLifecycleCallback callback) {
+ entity.setPostLoad(callback);
+ }
+ }
+}
Added: incubator/cayenne/trunk/cayenne-jpa/src/main/java/org/objectstyle/cayenne/jpa/conf/EntityListenerAnnotationLoader.java
URL: http://svn.apache.org/viewcvs/incubator/cayenne/trunk/cayenne-jpa/src/main/java/org/objectstyle/cayenne/jpa/conf/EntityListenerAnnotationLoader.java?rev=396882&view=auto
==============================================================================
--- incubator/cayenne/trunk/cayenne-jpa/src/main/java/org/objectstyle/cayenne/jpa/conf/EntityListenerAnnotationLoader.java (added)
+++ incubator/cayenne/trunk/cayenne-jpa/src/main/java/org/objectstyle/cayenne/jpa/conf/EntityListenerAnnotationLoader.java Tue Apr 25 06:43:00 2006
@@ -0,0 +1,178 @@
+/* ====================================================================
+ *
+ * The ObjectStyle Group Software License, version 1.1
+ * ObjectStyle Group - http://objectstyle.org/
+ *
+ * Copyright (c) 2002-2005, Andrei (Andrus) Adamchik and individual authors
+ * of the software. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution, if any,
+ * must include the following acknowlegement:
+ * "This product includes software developed by independent contributors
+ * and hosted on ObjectStyle Group web site (http://objectstyle.org/)."
+ * Alternately, this acknowlegement may appear in the software itself,
+ * if and wherever such third-party acknowlegements normally appear.
+ *
+ * 4. The names "ObjectStyle Group" and "Cayenne" must not be used to endorse
+ * or promote products derived from this software without prior written
+ * permission. For written permission, email
+ * "andrus at objectstyle dot org".
+ *
+ * 5. Products derived from this software may not be called "ObjectStyle"
+ * or "Cayenne", nor may "ObjectStyle" or "Cayenne" appear in their
+ * names without prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE OBJECTSTYLE GROUP OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals and hosted on ObjectStyle Group web site. For more
+ * information on the ObjectStyle Group, please see
+ * <http://objectstyle.org/>.
+ */
+package org.objectstyle.cayenne.jpa.conf;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+
+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 org.objectstyle.cayenne.jpa.map.JpaEntityListener;
+import org.objectstyle.cayenne.jpa.map.JpaLifecycleCallback;
+
+/**
+ * Loads annotations from the entity listener class. Only deals with non-entity formats of
+ * annotation methods.
+ * <h3>JPA Spec, 3.4.1:</h3>
+ * <p>
+ * Callback methods defined on an entity class have the following signature: <em>void
+ * <METHOD>()</em>
+ * Callback methods defined on an entity listener class have the following signature:
+ * <em>void <METHOD>(Object)</em> The Object argument is the entity instance for which
+ * the callback method is invoked. It maybe declared as the actual entity type. The
+ * callback methods can have public, private, protected, or package level access, but must
+ * not be static or final.
+ * </p>
+ *
+ * @author Andrus Adamchik
+ */
+public class EntityListenerAnnotationLoader {
+
+ /**
+ * Returns a listener methods descriptor for the annotated listener, or null if none
+ * of the class methods are properly annotated.
+ */
+ public JpaEntityListener getEntityListener(Class listenerClass) {
+ JpaEntityListener listener = new JpaEntityListener();
+
+ boolean hasAnnotations = false;
+ Method[] methods = listenerClass.getDeclaredMethods();
+ for (int i = 0; i < methods.length; i++) {
+
+ if (isValidListenerMethod(methods[i])) {
+ if (processAnnotations(methods[i], listener)) {
+ hasAnnotations = true;
+ }
+ }
+ }
+
+ if (hasAnnotations) {
+ listener.setClassName(listenerClass.getName());
+ return listener;
+ }
+
+ return null;
+ }
+
+ /**
+ * Checks that the method signature is one of a valid listener method,
+ * <em>void METHOD(Object)</em>.
+ */
+ protected boolean isValidListenerMethod(Method m) {
+ int modifiers = m.getModifiers();
+ if (Modifier.isStatic(modifiers) || Modifier.isFinal(modifiers)) {
+ return false;
+ }
+
+ if (!Void.TYPE.equals(m.getReturnType())) {
+ return false;
+ }
+
+ Class[] params = m.getParameterTypes();
+ if (params.length != 1 || !Object.class.equals(params[0])) {
+ return false;
+ }
+
+ return true;
+ }
+
+ protected boolean processAnnotations(Method method, JpaEntityListener listener) {
+ boolean hasListenerAnnotations = false;
+
+ if (method.isAnnotationPresent(PrePersist.class)) {
+ listener.setPrePersist(new JpaLifecycleCallback(method.getName()));
+ hasListenerAnnotations = true;
+ }
+
+ if (method.isAnnotationPresent(PostPersist.class)) {
+ listener.setPostPersist(new JpaLifecycleCallback(method.getName()));
+ hasListenerAnnotations = true;
+ }
+
+ if (method.isAnnotationPresent(PreRemove.class)) {
+ listener.setPreRemove(new JpaLifecycleCallback(method.getName()));
+ hasListenerAnnotations = true;
+ }
+
+ if (method.isAnnotationPresent(PostRemove.class)) {
+ listener.setPostRemove(new JpaLifecycleCallback(method.getName()));
+ hasListenerAnnotations = true;
+ }
+
+ if (method.isAnnotationPresent(PreUpdate.class)) {
+ listener.setPreUpdate(new JpaLifecycleCallback(method.getName()));
+ hasListenerAnnotations = true;
+ }
+
+ if (method.isAnnotationPresent(PostUpdate.class)) {
+ listener.setPostUpdate(new JpaLifecycleCallback(method.getName()));
+ hasListenerAnnotations = true;
+ }
+
+ if (method.isAnnotationPresent(PostLoad.class)) {
+ listener.setPostLoad(new JpaLifecycleCallback(method.getName()));
+ hasListenerAnnotations = true;
+ }
+
+ return hasListenerAnnotations;
+ }
+}
Added: incubator/cayenne/trunk/cayenne-jpa/src/main/java/org/objectstyle/cayenne/jpa/conf/EntityMapAnnotationLoader.java
URL: http://svn.apache.org/viewcvs/incubator/cayenne/trunk/cayenne-jpa/src/main/java/org/objectstyle/cayenne/jpa/conf/EntityMapAnnotationLoader.java?rev=396882&view=auto
==============================================================================
--- incubator/cayenne/trunk/cayenne-jpa/src/main/java/org/objectstyle/cayenne/jpa/conf/EntityMapAnnotationLoader.java (added)
+++ incubator/cayenne/trunk/cayenne-jpa/src/main/java/org/objectstyle/cayenne/jpa/conf/EntityMapAnnotationLoader.java Tue Apr 25 06:43:00 2006
@@ -0,0 +1,462 @@
+/* ====================================================================
+ *
+ * The ObjectStyle Group Software License, version 1.1
+ * ObjectStyle Group - http://objectstyle.org/
+ *
+ * Copyright (c) 2002-2005, Andrei (Andrus) Adamchik and individual authors
+ * of the software. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution, if any,
+ * must include the following acknowlegement:
+ * "This product includes software developed by independent contributors
+ * and hosted on ObjectStyle Group web site (http://objectstyle.org/)."
+ * Alternately, this acknowlegement may appear in the software itself,
+ * if and wherever such third-party acknowlegements normally appear.
+ *
+ * 4. The names "ObjectStyle Group" and "Cayenne" must not be used to endorse
+ * or promote products derived from this software without prior written
+ * permission. For written permission, email
+ * "andrus at objectstyle dot org".
+ *
+ * 5. Products derived from this software may not be called "ObjectStyle"
+ * or "Cayenne", nor may "ObjectStyle" or "Cayenne" appear in their
+ * names without prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE OBJECTSTYLE GROUP OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals and hosted on ObjectStyle Group web site. For more
+ * information on the ObjectStyle Group, please see
+ * <http://objectstyle.org/>.
+ */
+package org.objectstyle.cayenne.jpa.conf;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.AnnotatedElement;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.persistence.Embeddable;
+import javax.persistence.EmbeddedId;
+import javax.persistence.Entity;
+import javax.persistence.EntityListeners;
+import javax.persistence.Id;
+import javax.persistence.MappedSuperclass;
+import javax.persistence.NamedNativeQueries;
+import javax.persistence.NamedNativeQuery;
+import javax.persistence.NamedQueries;
+import javax.persistence.NamedQuery;
+import javax.persistence.SequenceGenerator;
+import javax.persistence.SqlResultSetMapping;
+import javax.persistence.TableGenerator;
+
+import org.objectstyle.cayenne.jpa.JpaProviderException;
+import org.objectstyle.cayenne.jpa.map.JpaAbstractEntity;
+import org.objectstyle.cayenne.jpa.map.JpaEntityMap;
+import org.objectstyle.cayenne.util.Util;
+import org.objectstyle.cayenne.validation.SimpleValidationFailure;
+import org.objectstyle.cayenne.validation.ValidationResult;
+
+/**
+ * {@link org.objectstyle.cayenne.jpa.map.JpaEntityMap} loader that reads mapping
+ * information from the class annotations per JPA specification.
+ * <h3>Specification Documentation, persistence_1_0.xsd, "class" element.</h3>
+ * <p>
+ * [Each managed class] should be annotated with either \@Entity, \@Embeddable or
+ * \@MappedSuperclass
+ *
+ * @author Andrus Adamchik
+ */
+public class EntityMapAnnotationLoader {
+
+ static final Map<String, Integer> TYPE_ANNOTATION_ORDERING_WEIGHTS;
+ static final Map<String, Integer> MEMBER_ANNOTATION_ORDERING_WEIGHTS;
+ static final Pattern GETTER_PATTERN;
+ static final Pattern SETTER_PATTERN;
+
+ static {
+
+ GETTER_PATTERN = Pattern.compile("^(is|get)([A-Z].*)$");
+ SETTER_PATTERN = Pattern.compile("^set([A-Z].*)$");
+
+ TYPE_ANNOTATION_ORDERING_WEIGHTS = new HashMap<String, Integer>();
+
+ // annotations that are top-level only
+ TYPE_ANNOTATION_ORDERING_WEIGHTS.put(Entity.class.getName(), 1);
+ TYPE_ANNOTATION_ORDERING_WEIGHTS.put(Embeddable.class.getName(), 1);
+ TYPE_ANNOTATION_ORDERING_WEIGHTS.put(MappedSuperclass.class.getName(), 1);
+
+ // annotations that can be a part of Entity or EntityMap
+ TYPE_ANNOTATION_ORDERING_WEIGHTS.put(SequenceGenerator.class.getName(), 2);
+ TYPE_ANNOTATION_ORDERING_WEIGHTS.put(NamedNativeQueries.class.getName(), 2);
+ TYPE_ANNOTATION_ORDERING_WEIGHTS.put(NamedNativeQuery.class.getName(), 2);
+ TYPE_ANNOTATION_ORDERING_WEIGHTS.put(NamedQueries.class.getName(), 2);
+ TYPE_ANNOTATION_ORDERING_WEIGHTS.put(NamedQuery.class.getName(), 2);
+ TYPE_ANNOTATION_ORDERING_WEIGHTS.put(SqlResultSetMapping.class.getName(), 2);
+ TYPE_ANNOTATION_ORDERING_WEIGHTS.put(TableGenerator.class.getName(), 2);
+ TYPE_ANNOTATION_ORDERING_WEIGHTS.put(EntityListeners.class.getName(), 2);
+
+ MEMBER_ANNOTATION_ORDERING_WEIGHTS = new HashMap<String, Integer>();
+
+ // direct entity children annotations
+ MEMBER_ANNOTATION_ORDERING_WEIGHTS.put(EmbeddedId.class.getName(), 1);
+ MEMBER_ANNOTATION_ORDERING_WEIGHTS.put(Id.class.getName(), 1);
+
+ // 'attribute' is implied...
+ }
+
+ protected ValidationResult conflicts;
+ protected Comparator<Annotation> typeAnnotationsSorter;
+ protected Comparator<Annotation> memberAnnotationsSorter;
+
+ protected AnnotationProcessorFactory classProcessorFactory;
+ protected AnnotationProcessorFactory memberProcessorFactory;
+ protected AnnotationProcessorFactory callbackProcessorFactory;
+
+ public EntityMapAnnotationLoader(ValidationResult conflicts) {
+ this.conflicts = conflicts;
+ this.typeAnnotationsSorter = new AnnotationSorter(
+ TYPE_ANNOTATION_ORDERING_WEIGHTS);
+ this.memberAnnotationsSorter = new AnnotationSorter(
+ MEMBER_ANNOTATION_ORDERING_WEIGHTS);
+
+ this.classProcessorFactory = new ClassAnnotationProcessorFactory();
+ this.memberProcessorFactory = new MemberAnnotationProcessorFactory();
+ this.callbackProcessorFactory = new EntityCallbackAnnotationProcessorFactory();
+ }
+
+ /**
+ * Processes annotations of a single Java class, loading ORM mapping information to
+ * the provided entity map.
+ */
+ public void loadClassMapping(JpaEntityMap entityMap, Class managedClass)
+ throws JpaProviderException {
+
+ Annotation[] classAnnotations = managedClass.getAnnotations();
+
+ // per 'getAnnotations' docs, array is returned by copy, so we can modify it...
+ Arrays.sort(classAnnotations, typeAnnotationsSorter);
+
+ Context context = new Context();
+ context.push(entityMap);
+
+ // === push class-level stuff
+ for (int i = 0; i < classAnnotations.length; i++) {
+ AnnotationProcessor processor = classProcessorFactory
+ .getProcessor(classAnnotations[i]);
+ if (processor != null) {
+ processor.onStartElement(managedClass, context);
+ }
+ }
+
+ // if class is not properly annotated, bail early
+ if (context.stack.size() == 1) {
+ return;
+ }
+
+ // apply entity callbacks ...
+ if (context.peek() instanceof JpaAbstractEntity) {
+ for (Method callback : getEntityCallbacks(managedClass)) {
+ applyEntityCallbackAnnotations(callback, context);
+ }
+ }
+
+ // per JPA spec, 2.1.1, regarding access type:
+
+ // When annotations are used, the placement of the mapping annotations on either
+ // the persistent fields or persistent properties of the entity class specifies
+ // the access type as being either field- or property-based access respectively.
+
+ // Question (andrus) - if no annotations are placed at the field or method level,
+ // we still must determine the access type to apply default mappping rules. How?
+ // (using FIELD access for now).
+
+ // === process field annotations
+
+ // JPA Spec, 2.1.9.3, regarding non-entity superclasses:
+
+ // The non-entity superclass serves for inheritance of behavior only. The state of
+ // a non-entity superclass is not persistent. Any state inherited fromnon-entity
+ // superclasses is non-persistent in an inheriting entity class. This
+ // non-persistent state is not managed by the EntityManager, nor it is
+ // required to be retained across transactions. Any annotations on such
+ // superclasses are ignored.
+
+ Field[] fields = managedClass.getDeclaredFields();
+ for (int i = 0; i < fields.length; i++) {
+
+ // skip transient fields (in a Java sense)
+ if (Modifier.isTransient(fields[i].getModifiers())) {
+ continue;
+ }
+
+ applyMemberAnnotations(fields[i], context);
+ }
+
+ // == process getter annotations
+
+ // per JPA spec, 2.1.1:
+ // The property accessor methods must be public or protected. When
+ // property-based access is used, the object/relational mapping annotations for
+ // the entity class annotate the getter property accessors.
+
+ for (Method propertyGetter : getPropertyGetters(managedClass)) {
+ applyMemberAnnotations(propertyGetter, context);
+ }
+
+ // === pop class-level stuff
+ for (int i = classAnnotations.length - 1; i >= 0; i--) {
+ AnnotationProcessor processor = classProcessorFactory
+ .getProcessor(classAnnotations[i]);
+ if (processor != null) {
+ processor.onFinishElement(managedClass, context);
+ }
+ }
+ }
+
+ protected void applyMemberAnnotations(
+ AnnotatedElement member,
+ AnnotationProcessorContext context) {
+
+ Annotation[] annotations = member.getAnnotations();
+ // per 'getAnnotations' docs, array is returned by copy, so we can modify it...
+ Arrays.sort(annotations, memberAnnotationsSorter);
+
+ for (int j = 0; j < annotations.length; j++) {
+
+ AnnotationProcessor memberProcessor = memberProcessorFactory
+ .getProcessor(annotations[j]);
+
+ if (memberProcessor != null) {
+ memberProcessor.onStartElement(member, context);
+ }
+ }
+
+ for (int j = annotations.length - 1; j >= 0; j--) {
+
+ AnnotationProcessor memberProcessor = memberProcessorFactory
+ .getProcessor(annotations[j]);
+
+ if (memberProcessor != null) {
+ memberProcessor.onFinishElement(member, context);
+ }
+ }
+ }
+
+ protected void applyEntityCallbackAnnotations(
+ Method method,
+ AnnotationProcessorContext context) {
+
+ Annotation[] annotations = method.getAnnotations();
+
+ for (int j = 0; j < annotations.length; j++) {
+
+ AnnotationProcessor callbackProcessor = callbackProcessorFactory
+ .getProcessor(annotations[j]);
+
+ if (callbackProcessor != null) {
+ callbackProcessor.onStartElement(method, context);
+ }
+ }
+
+ // don't call 'onFinishElement' as there is no nesting within callback
+ // annotations...
+ }
+
+ /**
+ * Returns getters for public and protected methods that look like read/write bean
+ * properties, as those are the methods to look for annotations.
+ */
+ protected Collection<Method> getPropertyGetters(Class managedClass) {
+
+ Map<String, PropertyTuple> properties = new HashMap<String, PropertyTuple>();
+
+ // JPA Spec, 2.1.9.3, regarding non-entity superclasses:
+
+ // The non-entity superclass serves for inheritance of behavior only. The state of
+ // a non-entity superclass is not persistent. Any state inherited fromnon-entity
+ // superclasses is non-persistent in an inheriting entity class. This
+ // non-persistent state is not managed by the EntityManager, nor it is
+ // required to be retained across transactions. Any annotations on such
+ // superclasses are ignored.
+
+ Method[] methods = managedClass.getDeclaredMethods();
+ for (int i = 0; i < methods.length; i++) {
+
+ int modifiers = methods[i].getModifiers();
+ if (!Modifier.isProtected(modifiers) && !Modifier.isPublic(modifiers)) {
+ continue;
+ }
+
+ String name = methods[i].getName();
+ Class[] parameters = methods[i].getParameterTypes();
+ Class returnType = methods[i].getReturnType();
+ boolean isVoid = Void.TYPE.equals(returnType);
+
+ if (!isVoid && parameters.length == 0) {
+ Matcher getMatch = GETTER_PATTERN.matcher(name);
+ if (getMatch.matches()) {
+
+ String key = getMatch.group(2) + ":" + returnType.getName();
+ PropertyTuple t = properties.get(key);
+ if (t == null) {
+ t = new PropertyTuple();
+ properties.put(key, t);
+ }
+
+ t.getter = methods[i];
+ continue;
+ }
+ }
+
+ if (isVoid && parameters.length == 1) {
+ Matcher setMatch = SETTER_PATTERN.matcher(name);
+ if (setMatch.matches()) {
+
+ String key = setMatch.group(1) + ":" + parameters[0].getName();
+
+ PropertyTuple t = properties.get(key);
+ if (t == null) {
+ t = new PropertyTuple();
+ properties.put(key, t);
+ }
+
+ t.setter = methods[i];
+ }
+ }
+ }
+
+ Collection<Method> matchingMethods = new ArrayList<Method>(properties.size());
+
+ for (PropertyTuple t : properties.values()) {
+ if (t.getter != null && t.setter != null) {
+ matchingMethods.add(t.getter);
+ }
+ }
+
+ return matchingMethods;
+ }
+
+ protected Collection<Method> getEntityCallbacks(Class managedClass) {
+
+ Collection<Method> callbacks = new ArrayList<Method>(3);
+
+ Method[] methods = managedClass.getDeclaredMethods();
+ for (int i = 0; i < methods.length; i++) {
+
+ int modifiers = methods[i].getModifiers();
+ if (Modifier.isStatic(modifiers) || Modifier.isFinal(modifiers)) {
+ continue;
+ }
+
+ if (!Void.TYPE.equals(methods[i].getReturnType())) {
+ continue;
+ }
+
+ Class[] params = methods[i].getParameterTypes();
+ if (params.length != 0) {
+ continue;
+ }
+
+ callbacks.add(methods[i]);
+ }
+
+ return callbacks;
+ }
+
+ /**
+ * Comparator for TYPE level JPA annotations that first returns top-level annotations
+ * that define what kind of managed persistent class is being annotated.
+ */
+ final class AnnotationSorter implements Comparator<Annotation> {
+
+ private Map<String, Integer> weights;
+
+ AnnotationSorter(Map<String, Integer> weights) {
+ this.weights = weights;
+ }
+
+ public int compare(Annotation o1, Annotation o2) {
+ Integer w1 = weights.get(o1.annotationType().getName());
+ Integer w2 = weights.get(o2.annotationType().getName());
+
+ // nulls go last as all non-top annotations are not explicitly mentioned
+ // mapped to sorting weight
+ return Util.nullSafeCompare(false, w1, w2);
+ }
+ }
+
+ final class PropertyTuple {
+
+ Method getter;
+ Method setter;
+ }
+
+ final class Context implements AnnotationProcessorContext {
+
+ LinkedList stack = new LinkedList();
+
+ public Object peek() {
+ return stack.peek();
+ }
+
+ public Object pop() {
+ return stack.removeFirst();
+ }
+
+ public void push(Object object) {
+ stack.addFirst(object);
+ }
+
+ public void recordConflict(
+ AnnotatedElement element,
+ Class annotatedType,
+ String message) {
+
+ StringBuilder buffer = new StringBuilder();
+ buffer.append("Problem processing annotation: ").append(
+ annotatedType.getName());
+ buffer.append(", annotated element: ").append(element);
+
+ if (message != null) {
+ buffer.append(", details: ").append(message);
+ }
+
+ conflicts.addFailure(new SimpleValidationFailure(peek(), buffer.toString()));
+ }
+ }
+}
Added: incubator/cayenne/trunk/cayenne-jpa/src/main/java/org/objectstyle/cayenne/jpa/conf/EntityMapLoader.java
URL: http://svn.apache.org/viewcvs/incubator/cayenne/trunk/cayenne-jpa/src/main/java/org/objectstyle/cayenne/jpa/conf/EntityMapLoader.java?rev=396882&view=auto
==============================================================================
--- incubator/cayenne/trunk/cayenne-jpa/src/main/java/org/objectstyle/cayenne/jpa/conf/EntityMapLoader.java (added)
+++ incubator/cayenne/trunk/cayenne-jpa/src/main/java/org/objectstyle/cayenne/jpa/conf/EntityMapLoader.java Tue Apr 25 06:43:00 2006
@@ -0,0 +1,242 @@
+/* ====================================================================
+ *
+ * The ObjectStyle Group Software License, version 1.1
+ * ObjectStyle Group - http://objectstyle.org/
+ *
+ * Copyright (c) 2002-2005, Andrei (Andrus) Adamchik and individual authors
+ * of the software. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution, if any,
+ * must include the following acknowlegement:
+ * "This product includes software developed by independent contributors
+ * and hosted on ObjectStyle Group web site (http://objectstyle.org/)."
+ * Alternately, this acknowlegement may appear in the software itself,
+ * if and wherever such third-party acknowlegements normally appear.
+ *
+ * 4. The names "ObjectStyle Group" and "Cayenne" must not be used to endorse
+ * or promote products derived from this software without prior written
+ * permission. For written permission, email
+ * "andrus at objectstyle dot org".
+ *
+ * 5. Products derived from this software may not be called "ObjectStyle"
+ * or "Cayenne", nor may "ObjectStyle" or "Cayenne" appear in their
+ * names without prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE OBJECTSTYLE GROUP OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals and hosted on ObjectStyle Group web site. For more
+ * information on the ObjectStyle Group, please see
+ * <http://objectstyle.org/>.
+ */
+package org.objectstyle.cayenne.jpa.conf;
+
+import java.io.IOException;
+import java.net.URL;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.persistence.spi.PersistenceUnitInfo;
+
+import org.objectstyle.cayenne.jpa.JpaProviderException;
+import org.objectstyle.cayenne.jpa.map.JpaEntityMap;
+import org.objectstyle.cayenne.validation.ValidationFailure;
+import org.objectstyle.cayenne.validation.ValidationResult;
+
+/**
+ * Loads JPA mapping information from all sources per JPA specification.
+ * <h3>Specification Documentation, Chapter 6.2.1.6</h3>
+ * <p>
+ * The set of managed persistence classes that are managed by a persistence unit is
+ * defined by using one or more of the following:
+ * <ul>
+ * <li>One or more object/relational mapping XML files
+ * <li>One or more jar files that will be searched for classes
+ * <li>An explicit list of the classes
+ * <li>The annotated managed persistence classes contained in the root of the persistence
+ * unit (unless theexclude-unlisted-classes element is specified) [Java SE doesn't have to
+ * support that].
+ * </ul>
+ * <p>
+ * The result is undefined if multiple mapping files (including any orm.xml file)
+ * referenced within a single persistence unit contain overlapping mapping information for
+ * any given class.
+ * </p>
+ * <p>
+ * The resulting set of entities managed by the persistence unit [and contained in the
+ * returned entity map] is the union of these sources, with the mapping metadata
+ * annotations (or annotation defaults) for any given class being overridden by the XML
+ * mapping information file if there are both annotations as well as XML mappings for that
+ * class. The minimum portable level of overriding is at the level of the persistent field
+ * or property.
+ * </p>
+ *
+ * @author Andrus Adamchik
+ */
+public class EntityMapLoader {
+
+ static final String DESCRIPTOR_LOCATION = "META-INF/orm.xml";
+
+ protected JpaEntityMap entityMap;
+ protected ValidationResult conflicts;
+
+ /**
+ * Creates an EntityMapLoader for the persistence unit, merging entity information
+ * from all locations supported by the JPA specification.
+ */
+ public EntityMapLoader(PersistenceUnitInfo persistenceUnit) {
+ this.conflicts = new ValidationResult();
+ this.entityMap = loadEntityMap(persistenceUnit);
+ }
+
+ /**
+ * Returns an entity map with entity
+ */
+ public JpaEntityMap getEntityMap() {
+ return entityMap;
+ }
+
+ /**
+ * Returns a descriptor of conflicts encountered during load.
+ */
+ public ValidationResult getConflicts() {
+ return conflicts;
+ }
+
+ /**
+ * Loads a combined entity map including managed class descriptors from all supported
+ * locations.
+ */
+ protected JpaEntityMap loadEntityMap(PersistenceUnitInfo persistenceUnit)
+ throws JpaProviderException {
+
+ // algorithm is the following:
+ // 1. load all ORM descriptors and merge them together
+ // 2. load annotations for all mentioned classes
+ // 3. merge combined ORM descriptor with the annotated descriptor, overriding
+ // annotation values.
+ // 4. set defaults
+
+ try {
+ JpaEntityMap xmlMap = loadFromXML(persistenceUnit);
+ JpaEntityMap baseMap = loadFromAnnotations(persistenceUnit);
+ baseMap.mergeOverride(xmlMap);
+ return baseMap;
+ }
+ catch (JpaProviderException e) {
+ throw e;
+ }
+ catch (Exception e) {
+ throw new JpaProviderException("Error loading ORM descriptors", e);
+ }
+ }
+
+ /**
+ * Loads a combined mapping descriptor from all supported locations of the mapping
+ * files.
+ * <h3>Specification Documentation, Chapter 6.2.1.6</h3>
+ * <p>
+ * An <em>orm.xml</em> file may be specified in the META-INF directory in the root
+ * of the persistence unit or in the META-INF directory of any jar file referenced by
+ * the persistence.xml. Alternatively, or in addition, other mapping files maybe
+ * referenced by the mapping-file elements of the persistence-unit element, and may be
+ * present anywhere on the classpath. An orm.xml file or other mapping file is loaded
+ * as a resource by the persistence provider.
+ */
+ protected JpaEntityMap loadFromXML(PersistenceUnitInfo persistenceUnit)
+ throws IOException {
+
+ Set loadedLocations = new HashSet();
+ EntityMapXMLLoader loader = new EntityMapXMLLoader();
+ JpaEntityMap map = null;
+
+ // 1. load from the standard file called orm.xml
+ loadedLocations.add(DESCRIPTOR_LOCATION);
+ Enumeration<URL> standardDescriptors = persistenceUnit
+ .getClassLoader()
+ .getResources(DESCRIPTOR_LOCATION);
+
+ while (standardDescriptors.hasMoreElements()) {
+ map = loadFromXML(loader, standardDescriptors.nextElement(), map);
+ }
+
+ // 2. load from orm.xml within the jars
+ // TODO: andrus, 4/20/2006 - load from the jar files
+
+ // 3. load from explicitly specified descriptors
+ for (String descriptor : persistenceUnit.getMappingFileNames()) {
+
+ // avoid loading duplicates, such as META-INF/orm.xml that is also explicitly
+ // mentioned in the unit...
+ if (loadedLocations.add(descriptor)) {
+
+ Enumeration<URL> mappedDescriptors = persistenceUnit
+ .getClassLoader()
+ .getResources(descriptor);
+ while (mappedDescriptors.hasMoreElements()) {
+ map = loadFromXML(loader, mappedDescriptors.nextElement(), map);
+ }
+ }
+ }
+
+ // 4. load defaults
+ // TODO: andrus, 4/21/2006 - implement default loading
+
+ return map != null ? map : new JpaEntityMap();
+ }
+
+ protected JpaEntityMap loadFromAnnotations(PersistenceUnitInfo persistenceUnit) {
+
+ // TODO: implement me!
+ return new JpaEntityMap();
+ }
+
+ /**
+ * Loads a single descriptor from provided URL, merging it with an existing descriptor
+ * if it exists. Retruns a combination descriptor.
+ */
+ private JpaEntityMap loadFromXML(
+ EntityMapXMLLoader loader,
+ URL url,
+ JpaEntityMap baseMap) {
+
+ JpaEntityMap map = loader.getEntityMap(url);
+ if (baseMap == null) {
+ return map;
+ }
+
+ ValidationResult conflicts = baseMap.mergeNoOverride(map);
+ if (conflicts != null && conflicts.hasFailures()) {
+ for (Object failure : conflicts.getFailures()) {
+ this.conflicts.addFailure((ValidationFailure) failure);
+ }
+ }
+
+ return baseMap;
+ }
+}
Added: incubator/cayenne/trunk/cayenne-jpa/src/main/java/org/objectstyle/cayenne/jpa/conf/EntityMapXMLLoader.java
URL: http://svn.apache.org/viewcvs/incubator/cayenne/trunk/cayenne-jpa/src/main/java/org/objectstyle/cayenne/jpa/conf/EntityMapXMLLoader.java?rev=396882&view=auto
==============================================================================
--- incubator/cayenne/trunk/cayenne-jpa/src/main/java/org/objectstyle/cayenne/jpa/conf/EntityMapXMLLoader.java (added)
+++ incubator/cayenne/trunk/cayenne-jpa/src/main/java/org/objectstyle/cayenne/jpa/conf/EntityMapXMLLoader.java Tue Apr 25 06:43:00 2006
@@ -0,0 +1,168 @@
+/* ====================================================================
+ *
+ * The ObjectStyle Group Software License, version 1.1
+ * ObjectStyle Group - http://objectstyle.org/
+ *
+ * Copyright (c) 2002-2005, Andrei (Andrus) Adamchik and individual authors
+ * of the software. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution, if any,
+ * must include the following acknowlegement:
+ * "This product includes software developed by independent contributors
+ * and hosted on ObjectStyle Group web site (http://objectstyle.org/)."
+ * Alternately, this acknowlegement may appear in the software itself,
+ * if and wherever such third-party acknowlegements normally appear.
+ *
+ * 4. The names "ObjectStyle Group" and "Cayenne" must not be used to endorse
+ * or promote products derived from this software without prior written
+ * permission. For written permission, email
+ * "andrus at objectstyle dot org".
+ *
+ * 5. Products derived from this software may not be called "ObjectStyle"
+ * or "Cayenne", nor may "ObjectStyle" or "Cayenne" appear in their
+ * names without prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE OBJECTSTYLE GROUP OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals and hosted on ObjectStyle Group web site. For more
+ * information on the ObjectStyle Group, please see
+ * <http://objectstyle.org/>.
+ */
+package org.objectstyle.cayenne.jpa.conf;
+
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.net.URL;
+
+import javax.xml.XMLConstants;
+import javax.xml.parsers.SAXParserFactory;
+import javax.xml.transform.stream.StreamSource;
+import javax.xml.validation.Schema;
+import javax.xml.validation.SchemaFactory;
+
+import org.objectstyle.cayenne.jpa.map.JpaEntityMap;
+import org.objectstyle.cayenne.xml.XMLDecoder;
+import org.xml.sax.SAXException;
+
+/**
+ * {@link org.objectstyle.cayenne.jpa.map.JpaEntityMap} loader that reads mapping
+ * information from the XML sources compatible with the JPA ORM schema.
+ *
+ * @author Andrus Adamchik
+ */
+public class EntityMapXMLLoader {
+
+ static final String XML_CODER_MAPPING = "META-INF/cayenne/orm-coder.xml";
+ static final String ORM_SCHEMA = "META-INF/schemas/orm_1_0.xsd";
+
+ Schema schema;
+
+ public EntityMapXMLLoader() {
+ this(false);
+ }
+
+ public EntityMapXMLLoader(boolean validateDescriptors) {
+
+ // TODO: andrus, 04/18/2006 - merge validation capabilities to the XMLDecoder...
+ if (validateDescriptors) {
+ SAXParserFactory parserFactory = SAXParserFactory.newInstance();
+ parserFactory.setNamespaceAware(true);
+
+ // note that validation requires that orm.xml declares a schema like this:
+ // <orm xmlns="http://java.sun.com/xml/ns/persistence/orm"
+ // xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ // xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm
+ // http://java.sun.com/xml/ns/persistence/orm/orm_1_0.xsd">
+
+ URL schemaURL = Thread.currentThread().getContextClassLoader().getResource(
+ ORM_SCHEMA);
+
+ SchemaFactory factory = SchemaFactory
+ .newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
+ StreamSource ss = new StreamSource(schemaURL.toExternalForm());
+
+ try {
+ this.schema = factory.newSchema(ss);
+ }
+ catch (Exception e) {
+ throw new RuntimeException("Error loading ORM schema", e);
+ }
+ }
+ }
+
+ /**
+ * Loads {@link JpaEntityMap} using provided class loader to locate the XML. Returns
+ * null if no mapping is found.
+ */
+
+ public JpaEntityMap getEntityMap(URL resource) {
+
+ // TODO: andrus, 04/18/2006 XMLDecoder should support classpath locations for
+ // mapping descriptors
+ URL mappingURL = Thread.currentThread().getContextClassLoader().getResource(
+ XML_CODER_MAPPING);
+ if (mappingURL == null) {
+ throw new RuntimeException("No code mapping found: " + XML_CODER_MAPPING);
+ }
+
+ validate(resource);
+
+ try {
+
+ Reader in = new InputStreamReader(resource.openStream(), "UTF-8");
+
+ // TODO: andrus, 04/18/2006 - an inefficiency in XMLDecoder - it
+ // doesn't cache the mapping
+
+ XMLDecoder decoder = new XMLDecoder();
+ JpaEntityMap entityMap = (JpaEntityMap) decoder.decode(in, mappingURL
+ .toExternalForm());
+
+ return entityMap;
+ }
+ catch (Exception e) {
+ throw new RuntimeException("Error processing ORM mapping " + resource, e);
+ }
+ }
+
+ // TODO: andrus, 04/18/2006 - move schema validation to the XMLDecoder
+ void validate(URL resource) {
+ if (schema != null) {
+ try {
+ schema.newValidator().validate(new StreamSource(resource.openStream()));
+ }
+ catch (SAXException e) {
+ throw new RuntimeException("Error validating ORM mapping " + resource, e);
+ }
+ catch (IOException e) {
+ throw new RuntimeException("Error processing ORM mapping " + resource, e);
+ }
+ }
+ }
+}