All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.vaadin.addon.jpacontainer.metadata.MetadataFactory Maven / Gradle / Ivy

The newest version!
/*
 * JPAContainer
 * Copyright (C) 2010 Oy IT Mill Ltd
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * 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 Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see .
 */
package com.vaadin.addon.jpacontainer.metadata;

import java.beans.Introspector;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.Map;

import javax.persistence.Embeddable;
import javax.persistence.Embedded;
import javax.persistence.EmbeddedId;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
import javax.persistence.MappedSuperclass;
import javax.persistence.OneToMany;
import javax.persistence.OneToOne;
import javax.persistence.Transient;
import javax.persistence.Version;

import com.vaadin.addon.jpacontainer.metadata.PersistentPropertyMetadata.AccessType;

/**
 * Factory for creating and populating {@link ClassMetadata} and
 * {@link EntityClassMetadata} instances.
 * 
 * @author Petter Holmström (IT Mill)
 * @since 1.0
 */
public class MetadataFactory {

	private static MetadataFactory INSTANCE;
	private Map, ClassMetadata> metadataMap = new HashMap, ClassMetadata>();

	protected MetadataFactory() {
		// NOP
	}

	/**
	 * Gets the singleton instance of this factory.
	 * 
	 * @return the factory instance (never null).
	 */
	public static MetadataFactory getInstance() {
		if (INSTANCE == null) {
			INSTANCE = new MetadataFactory();
		}
		return INSTANCE;
	}

	/**
	 * Extracts the entity class metadata from mappedClass. The
	 * access type (field or method) will be determined from the location of the
	 * {@link Id} or {@link EmbeddedId} annotation. If both of these are
	 * missing, this method will fail. This method will also fail if
	 * mappedClass lacks the {@link Entity} annotation.
	 * 
	 * @param mappedClass
	 *            the mapped class (must not be null).
	 * @return the class metadata.
	 * @throws IllegalArgumentException
	 *             if no metadata could be extracted.
	 */
	public  EntityClassMetadata getEntityClassMetadata(
			Class mappedClass) throws IllegalArgumentException {
		assert mappedClass != null : "mappedClass must not be null";
		if (mappedClass.getAnnotation(Entity.class) == null) {
			throw new IllegalArgumentException("The class is not an entity");
		}
		PersistentPropertyMetadata.AccessType accessType = determineAccessType(mappedClass);
		if (accessType == null) {
			throw new IllegalArgumentException(
					"The access type could not be determined");
		} else {
			return (EntityClassMetadata) getClassMetadata(mappedClass,
					accessType);
		}
	}

	/**
	 * Extracts the class metadata from mappedClass. If
	 * mappedClass is {@link Embeddable}, the result will be an
	 * instance of {@link ClassMetadata}. If mappedClass is an
	 * {@link Entity}, the result will be an instance of
	 * {@link EntityClassMetadata}.
	 * 

* accessType instructs the factory where to look for * annotations and which defaults to assume if there are no annotations. * * @param mappedClass * the mapped class (must not be null). * @param accessType * the location where to look for annotations (must not be null). * @return the class metadata. * @throws IllegalArgumentException * if no metadata could be extracted. */ @SuppressWarnings("unchecked") public ClassMetadata getClassMetadata(Class mappedClass, PersistentPropertyMetadata.AccessType accessType) throws IllegalArgumentException { assert mappedClass != null : "mappedClass must not be null"; assert accessType != null : "accessType must not be null"; // Check if we already have the metadata in cache ClassMetadata metadata = (ClassMetadata) metadataMap .get(mappedClass); if (metadata != null) { return metadata; } // Check if we are dealing with an entity class or an embeddable class Entity entity = mappedClass.getAnnotation(Entity.class); Embeddable embeddable = mappedClass.getAnnotation(Embeddable.class); if (entity != null) { // We have an entity class String entityName = entity.name().length() == 0 ? mappedClass .getSimpleName() : entity.name(); metadata = new EntityClassMetadata(mappedClass, entityName); // Put the metadata instance in the cache in case it is referenced // from loadProperties() metadataMap.put(mappedClass, metadata); loadProperties(mappedClass, metadata, accessType); // Locate the version and identifier properties EntityClassMetadata entityMetadata = (EntityClassMetadata) metadata; for (PersistentPropertyMetadata pm : entityMetadata .getPersistentProperties()) { if (pm.getAnnotation(Version.class) != null) { entityMetadata.setVersionPropertyName(pm.getName()); } else if (pm.getAnnotation(Id.class) != null || pm.getAnnotation(EmbeddedId.class) != null) { entityMetadata.setIdentifierPropertyName(pm.getName()); } if (entityMetadata.hasIdentifierProperty() && entityMetadata.hasVersionProperty()) { // No use continuing the loop if both the version // and the identifier property have already been found. break; } } } else if (embeddable != null) { // We have an embeddable class metadata = new ClassMetadata(mappedClass); // Put the metadata instance in the cache in case it is referenced // from loadProperties() metadataMap.put(mappedClass, metadata); loadProperties(mappedClass, metadata, accessType); } else { throw new IllegalArgumentException( "The class is nether an entity nor embeddable"); } return metadata; } protected void loadProperties(Class type, ClassMetadata metadata, PersistentPropertyMetadata.AccessType accessType) { // Also check superclass for metadata Class superclass = type.getSuperclass(); if (superclass != null && (superclass.getAnnotation(MappedSuperclass.class) != null || superclass .getAnnotation(Entity.class) != null) || superclass.getAnnotation(Embeddable.class) != null) { loadProperties(superclass, metadata, accessType); } if (accessType == PersistentPropertyMetadata.AccessType.FIELD) { extractPropertiesFromFields(type, metadata); } else { extractPropertiesFromMethods(type, metadata); } } protected PersistentPropertyMetadata.AccessType determineAccessType( Class type) { // Start by looking for annotated fields for (Field f : type.getDeclaredFields()) { if (f.getAnnotation(Id.class) != null || f.getAnnotation(EmbeddedId.class) != null) { return AccessType.FIELD; } } // Then look for annotated getter methods for (Method m : type.getDeclaredMethods()) { if (m.getAnnotation(Id.class) != null || m.getAnnotation(EmbeddedId.class) != null) { return AccessType.METHOD; } } // Nothing found? Try with the superclass! Class superclass = type.getSuperclass(); if (superclass != null && (superclass.getAnnotation(MappedSuperclass.class) != null || superclass .getAnnotation(Entity.class) != null)) { return determineAccessType(superclass); } // The access type could not be determined; return null; } protected boolean isReference(AccessibleObject ab) { return (ab.getAnnotation(OneToOne.class) != null || ab .getAnnotation(ManyToOne.class) != null); } protected boolean isCollection(AccessibleObject ab) { return (ab.getAnnotation(OneToMany.class) != null || ab .getAnnotation(ManyToMany.class) != null); } protected boolean isEmbedded(AccessibleObject ab) { return (ab.getAnnotation(Embedded.class) != null || ab .getAnnotation(EmbeddedId.class) != null); } protected void extractPropertiesFromFields(Class type, ClassMetadata metadata) { for (Field f : type.getDeclaredFields()) { int mod = f.getModifiers(); if (!Modifier.isFinal(mod) && !Modifier.isStatic(mod) && !Modifier.isTransient(mod) && f.getAnnotation(Transient.class) == null) { if (isEmbedded(f)) { ClassMetadata cm = getClassMetadata(f.getType(), AccessType.FIELD); metadata.addProperties(new PersistentPropertyMetadata(f .getName(), cm, PersistentPropertyMetadata.PropertyKind.EMBEDDED, f)); } else if (isReference(f)) { ClassMetadata cm = getClassMetadata(f.getType(), AccessType.FIELD); metadata.addProperties(new PersistentPropertyMetadata(f .getName(), cm, PersistentPropertyMetadata.PropertyKind.REFERENCE, f)); } else if (isCollection(f)) { metadata.addProperties(new PersistentPropertyMetadata(f .getName(), f.getType(), PersistentPropertyMetadata.PropertyKind.COLLECTION, f)); } else { metadata.addProperties(new PersistentPropertyMetadata(f .getName(), convertPrimitiveType(f.getType()), PersistentPropertyMetadata.PropertyKind.SIMPLE, f)); } } } // Find the transient properties for (Method m : type.getDeclaredMethods()) { int mod = m.getModifiers(); // Synthetic methods are excluded (#4590). // In theory, this could filter out too much in some special cases, // in which case the subclass could re-declare the accessor methods // with the correct annotations as a workaround. if (m.getName().startsWith("get") && !Modifier.isStatic(mod) && !m.isSynthetic() && m.getReturnType() != Void.TYPE) { Method setter = null; try { // Check if we have a setter setter = type.getDeclaredMethod("set" + m.getName().substring(3), m.getReturnType()); } catch (NoSuchMethodException ignoreit) { } String name = Introspector.decapitalize(m.getName() .substring(3)); if (metadata.getProperty(name) == null) { // No previous property has been added with the same name metadata.addProperties(new PropertyMetadata(name, m .getReturnType(), m, setter)); } } } } private Class convertPrimitiveType(Class type) { // Vaadin fields don't work with primitive values, use wrapper types for // primitives if (type.isPrimitive()) { if (type.equals(Boolean.TYPE)) { type = Boolean.class; } else if (type.equals(Integer.TYPE)) { type = Integer.class; } else if (type.equals(Float.TYPE)) { type = Float.class; } else if (type.equals(Double.TYPE)) { type = Double.class; } else if (type.equals(Byte.TYPE)) { type = Byte.class; } else if (type.equals(Character.TYPE)) { type = Character.class; } else if (type.equals(Short.TYPE)) { type = Short.class; } else if (type.equals(Long.TYPE)) { type = Long.class; } } return type; } protected void extractPropertiesFromMethods(Class type, ClassMetadata metadata) { for (Method m : type.getDeclaredMethods()) { int mod = m.getModifiers(); // Synthetic methods are excluded (#4590) - otherwise you could e.g. // have a synthetic and a concrete id getter (with different // declared return types) in TestClasses.Integer_ConcreteId_M, and // the synthetic method could override the concrete one and its // annotations. // In theory, this could filter out too much in some special cases, // in which case the subclass could re-declare the accessor methods // with the correct annotations as a workaround. if (m.getName().startsWith("get") && !Modifier.isStatic(mod) && !m.isSynthetic() && m.getReturnType() != Void.TYPE) { Method setter = null; try { // Check if we have a setter setter = type.getDeclaredMethod("set" + m.getName().substring(3), m.getReturnType()); } catch (NoSuchMethodException ignoreit) { // No setter <=> transient property } String name = Introspector.decapitalize(m.getName() .substring(3)); if (setter != null && m.getAnnotation(Transient.class) == null) { // Persistent property if (isEmbedded(m)) { ClassMetadata cm = getClassMetadata( m.getReturnType(), AccessType.METHOD); metadata.addProperties(new PersistentPropertyMetadata( name, cm, PersistentPropertyMetadata.PropertyKind.EMBEDDED, m, setter)); } else if (isReference(m)) { ClassMetadata cm = getClassMetadata( m.getReturnType(), AccessType.METHOD); metadata.addProperties(new PersistentPropertyMetadata( name, cm, PersistentPropertyMetadata.PropertyKind.REFERENCE, m, setter)); } else if (isCollection(m)) { metadata.addProperties(new PersistentPropertyMetadata( name, m.getReturnType(), PersistentPropertyMetadata.PropertyKind.COLLECTION, m, setter)); } else { metadata.addProperties(new PersistentPropertyMetadata( name, m.getReturnType(), PersistentPropertyMetadata.PropertyKind.SIMPLE, m, setter)); } } else { // Transient property metadata.addProperties(new PropertyMetadata(name, m .getReturnType(), m, setter)); } } } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy