/* * Copyright (c) 2012 Data Harmonisation Panel * * All rights reserved. This program and the accompanying materials are made * available under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 3 of the License, * or (at your option) any later version. * * You should have received a copy of the GNU Lesser General Public License * along with this distribution. If not, see <http://www.gnu.org/licenses/>. * * Contributors: * HUMBOLDT EU Integrated Project #030962 * Data Harmonisation Panel <http://www.dhpanel.eu> */ package eu.esdihumboldt.hale.common.schema.model.constraint; import java.lang.reflect.Constructor; import java.util.HashMap; import java.util.LinkedList; import java.util.Map; import java.util.Queue; import de.fhg.igd.slf4jplus.ALogger; import de.fhg.igd.slf4jplus.ALoggerFactory; import eu.esdihumboldt.hale.common.schema.model.Constraint; import eu.esdihumboldt.hale.common.schema.model.Definition; /** * Helper for creating default constraints and dealing with the * {@link Constraint} annotation. Caches immutable default constraints that have * a default constructor. * * @see Constraint * @see Definition#getConstraint(Class) * * @author Simon Templer */ public abstract class ConstraintUtil { private static final ALogger log = ALoggerFactory.getLogger(ConstraintUtil.class); private static final Map<Class<?>, Object> cachedDefaults = new HashMap<Class<?>, Object>(); /** * Get the default constraint for the given constraint type. * * @param <T> the constraint type * * @param constraintType the concrete constraint type, i.e. a type annotated * with {@link Constraint} and defining a default constructor * and/or a constructor taking a {@link Definition} as an * argument * @param definition the definition the constraint will be associated to * @return the default constraint of the given type * @throws IllegalArgumentException if the given type is no constraint type * or creating the default constraint fails * * @see Constraint * @see Definition#getConstraint(Class) */ @SuppressWarnings("unchecked") public static <T> T getDefaultConstraint(Class<T> constraintType, Definition<?> definition) throws IllegalArgumentException { if (!constraintType.isAnnotationPresent(Constraint.class)) { throw new IllegalArgumentException("The type " + constraintType.getName() + " is no constraint type."); } Object cached = cachedDefaults.get(constraintType); if (cached != null) { return (T) cached; } // try the definition constructors Queue<Class<? extends Definition<?>>> defTypes = new LinkedList<Class<? extends Definition<?>>>(); defTypes.add((Class<? extends Definition<?>>) definition.getClass()); while (!defTypes.isEmpty()) { Class<? extends Definition<?>> defType = defTypes.poll(); try { // try creation Constructor<T> defConstructor = constraintType.getConstructor(defType); T constraint = defConstructor.newInstance(definition); return constraint; } catch (Throwable e) { // ignore, try other constructor log.debug("Could not create default constraint using a definition constructor", e); } // add supertype to queue Class<?> superType = defType.getSuperclass(); if (superType != null && Definition.class.isAssignableFrom(superType)) { defTypes.add((Class<? extends Definition<?>>) superType); } // add interfaces to queue for (Class<?> intfc : defType.getInterfaces()) { // TODO also check if interface has been checked already? if (Definition.class.isAssignableFrom(intfc)) { defTypes.add((Class<? extends Definition<?>>) intfc); } } } // try the default constructor try { Constructor<T> defConstructor = constraintType.getConstructor(); T constraint = defConstructor.newInstance(); if (!isMutableConstraint(constraintType)) { // constraint may be cached cachedDefaults.put(constraintType, constraint); } return constraint; } catch (Throwable e) { // ignore, try other constructor log.debug("Could not create default constraint using the default constructor", e); } throw new IllegalArgumentException( "Could not create a default constraint for constraint type " + constraintType.getSimpleName() + ". Ensure that a concrete constraint type is given and " + "that the implementation adheres to the contract specified by Constraint"); } /** * Determine the constraint type in the hierarchy of the given type, i.e. * the type that is marked with {@link Constraint} * * @param type the type to determine the constraint type for * @return the constraint type * @throws IllegalArgumentException if no constraint type exists in the type * hierarchy */ public static Class<?> getConstraintType(Class<?> type) throws IllegalArgumentException { while (!type.isAnnotationPresent(Constraint.class)) { if (type.equals(Object.class)) { throw new IllegalArgumentException("The type " + type.getName() + " has no constraint type in its hierarchy."); } type = type.getSuperclass(); } return type; } /** * Determine if the constraint type in the hierarchy of the given type is * mutable. * * @param type the type to determine the constraint type for * @return if the constraint is mutable * @throws IllegalArgumentException if no constraint type exists in the type * hierarchy */ public static boolean isMutableConstraint(Class<?> type) { type = getConstraintType(type); Constraint constraint = type.getAnnotation(Constraint.class); return constraint.mutable(); } }