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

net.sf.jsefa.common.annotation.TypeMappingFactory Maven / Gradle / Ivy

/*
 * Copyright 2007 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package net.sf.jsefa.common.annotation;

import static net.sf.jsefa.common.annotation.AnnotationParameterNames.CONVERTER_TYPE;
import static net.sf.jsefa.common.annotation.AnnotationParameterNames.FORMAT;
import static net.sf.jsefa.common.annotation.AnnotationParameterNames.LIST_ITEM;
import static net.sf.jsefa.common.annotation.AnnotationParameterNames.OBJECT_TYPE;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.Collection;
import java.util.Map;

import net.sf.jsefa.common.accessor.ObjectAccessorProvider;
import net.sf.jsefa.common.converter.SimpleTypeConverter;
import net.sf.jsefa.common.converter.provider.SimpleTypeConverterProvider;
import net.sf.jsefa.common.mapping.SimpleTypeMapping;
import net.sf.jsefa.common.mapping.TypeMapping;
import net.sf.jsefa.common.mapping.TypeMappingException;
import net.sf.jsefa.common.mapping.TypeMappingRegistry;
import net.sf.jsefa.common.util.ReflectionUtil;
import net.sf.jsefa.common.validator.provider.ValidatorProvider;

/**
 * Abstract super class for factories which can create {@link TypeMapping}s from annotated classes.
 * 

* All subclasses should be thread-safe. * * @author Norman Lahme-Huetig * * @param the type of the data type name * @param the type of the type mapping registry * */ public abstract class TypeMappingFactory> { private final SimpleTypeConverterProvider simpleTypeConverterProvider; private final ValidatorProvider validatorProvider; private final ObjectAccessorProvider objectAccessorProvider; private final R typeMappingRegistry; private final ValidatorFactory validatorFactory; /** * Constructs a new TypeMappingFactory. * * @param typeMappingRegistry the type mapping registry. New types will be registered using that registry. * @param simpleTypeConverterProvider the simple type converter provider to use * @param validatorProvider the validator provider to use * @param objectAccessorProvider the object accessor provider to use */ public TypeMappingFactory(R typeMappingRegistry, SimpleTypeConverterProvider simpleTypeConverterProvider, ValidatorProvider validatorProvider, ObjectAccessorProvider objectAccessorProvider) { this.typeMappingRegistry = typeMappingRegistry; this.simpleTypeConverterProvider = simpleTypeConverterProvider; this.validatorProvider = validatorProvider; this.objectAccessorProvider = objectAccessorProvider; this.validatorFactory = new ValidatorFactory(validatorProvider, objectAccessorProvider); } /** * Creates a type mapping for the given object type, registers it with the type mapping registry and returns * its data type name. The first two steps are omitted if a type mapping is already registered for the given * object type. * * @param objectType the object type to create a type mapping for. * @return the name of the created or found data type. */ public abstract N createIfAbsent(Class objectType); /** * Returns the type mapping registry. * * @return the type mapping registry. */ public final R getTypeMappingRegistry() { return typeMappingRegistry; } /** * Called before creating a new type mapping with the given data type name for the given object type. Returns * true, if the type mapping registry has no entry for the data type name and if it is the first time this * method is called with the given argument; otherwise false. *

* The purpose of this method is to prevent from creating duplicates and from falling in an endless loop in * case of a cycle in the type mapping graph. *

* In case the method returns true, a {@link TypeMappingPlaceholder} is registered so that object type * information can be retrieved during the construction of a type mapping. That type mapping is replaced by the * real type mapping after finishing its construction. * * @param objectType the object type * @param dataTypeName the data type name * * @return true, if no type mapping with the given name already exists or is already under construction. */ protected final boolean prepareToCreate(Class objectType, N dataTypeName) { if (this.typeMappingRegistry.get(dataTypeName) != null) { return false; } this.typeMappingRegistry.register(new TypeMappingPlaceholder(objectType, dataTypeName)); return true; } /** * Returns the object accessor provider. * * @return the object accessor provider. */ protected final ObjectAccessorProvider getObjectAccessorProvider() { return objectAccessorProvider; } /** * Returns the simple type converter provider. * * @return the simple type converter provider. */ protected final SimpleTypeConverterProvider getSimpleTypeConverterProvider() { return simpleTypeConverterProvider; } /** * Returns the validator provider. * * @return the validator provider */ protected final ValidatorProvider getValidatorProvider() { return validatorProvider; } /** * Creates a simple type converter. * * @param objectType the object type to create a converter for * @param field the field to create a converter for. May be null. * @param annotation the annotation providing parameters for constructing the converter. May be null. * @return a simple type converter */ @SuppressWarnings("unchecked") protected final SimpleTypeConverter createSimpleTypeConverter(Class objectType, Field field, Annotation annotation) { String[] format = null; SimpleTypeConverter itemTypeConverter = null; if (annotation != null) { format = AnnotationDataProvider.get(annotation, FORMAT); if (hasCollectionType(objectType)) { Annotation itemAnnotation = AnnotationDataProvider.get(annotation, LIST_ITEM); N itemDataTypeName = getAnnotatedDataTypeName(itemAnnotation, field.getDeclaringClass()); if (itemDataTypeName != null) { assertTypeMappingIsSimple(itemDataTypeName); assertNoCollectionType(getTypeMappingRegistry().get(itemDataTypeName).getObjectType()); itemTypeConverter = ((SimpleTypeMapping) getTypeMappingRegistry().get(itemDataTypeName)) .getSimpleTypeConverter(); } else { Class itemObjectType = getCollectionItemType(itemAnnotation, field, true); assertHasSimpleType(itemObjectType); assertNoCollectionType(itemObjectType); itemTypeConverter = createSimpleTypeConverter(itemObjectType, null, itemAnnotation); } } if (AnnotationDataProvider.get(annotation, CONVERTER_TYPE) != null) { Class converterType = AnnotationDataProvider.get(annotation, CONVERTER_TYPE); return getSimpleTypeConverterProvider().getForConverterType(converterType, objectType, format, itemTypeConverter); } } if (getSimpleTypeConverterProvider().hasConverterFor(objectType)) { return getSimpleTypeConverterProvider().getForObjectType(objectType, format, itemTypeConverter); } throw new TypeMappingException("Could not create a simple type converter for " + objectType); } /** * Returns the type of the items of a collection. * * @param annotation the annotation of the field * @param field the field * @param fromFieldDeclarationAllowed true, if the field is a collection which contains only values of one type so * that the type may be deduced from the actual parameter type of the parameterized type of the field; false * otherwise. * @return an object type */ protected final Class getCollectionItemType(Annotation annotation, Field field, boolean fromFieldDeclarationAllowed) { Class objectType = AnnotationDataProvider.get(annotation, OBJECT_TYPE); if (objectType == null && fromFieldDeclarationAllowed) { objectType = ReflectionUtil.getActualTypeParameter(field, 0); } return objectType; } /** * Returns the type of the values of a map. * * @param annotation the annotation of the field * @param field the field * @param fromFieldDeclarationAllowed true, if the field is a map which contains only values of one type so * that the type may be deduced from the actual parameter type of the parameterized type of the field; false * otherwise. * @return an object type */ protected final Class getMapValueType(Annotation annotation, Field field, boolean fromFieldDeclarationAllowed) { Class objectType = AnnotationDataProvider.get(annotation, OBJECT_TYPE); if (objectType == null && fromFieldDeclarationAllowed) { objectType = ReflectionUtil.getActualTypeParameter(field, 1); } return objectType; } /** * Returns the type of the keys of a map. * * @param annotation the annotation of the field * @param field the field * @return an object type */ protected final Class getMapKeyType(Annotation annotation, Field field) { Class objectType = AnnotationDataProvider.get(annotation, OBJECT_TYPE); if (objectType == null) { objectType = ReflectionUtil.getActualTypeParameter(field, 0); } return objectType; } /** * Returns the data type name as declared through the given annotation. * * @param annotation the annotation * @param annotationContextClass the context class of the annotation. This class may be needed to interpret the * annotation data. * @return a data type name */ protected abstract N getAnnotatedDataTypeName(Annotation annotation, Class annotationContextClass); /** * Returns the validator factory. * * @return the validator factory */ protected final ValidatorFactory getValidatorFactory() { return this.validatorFactory; } /** * Returns true if and only if the given object type is a simple type. * * @param objectType the object type * @return true, if the object type is a simple type; false otherwise. */ protected final boolean hasSimpleType(Class objectType) { return getSimpleTypeConverterProvider().hasConverterFor(objectType); } /** * Returns true if and only if the given object type is a collection type. * * @param objectType the object type * @return true, if the given object type is a collection type; false otherwise. */ protected final boolean hasCollectionType(Class objectType) { return Collection.class.isAssignableFrom(objectType); } /** * Returns true if and only if the given object type is a map type. * * @param objectType the object type * @return true, if the given object type is a map type; false otherwise. */ protected final boolean hasMapType(Class objectType) { return Map.class.isAssignableFrom(objectType); } /** * Asserts that a type mapping exists for the given data type name. * * @param dataTypeName the data type name. * @throws AnnotationException if the assertion fails. */ protected final void assertTypeMappingExists(N dataTypeName) { if (getTypeMappingRegistry().get(dataTypeName) == null) { throw new AnnotationException("No type mapping registered for data type name " + dataTypeName); } } /** * Asserts that a type mapping exists for the given data type name and is simple. * * @param dataTypeName the data type name. * @throws AnnotationException if the assertion fails. */ protected final void assertTypeMappingIsSimple(N dataTypeName) { assertTypeMappingExists(dataTypeName); if (!(getTypeMappingRegistry().get(dataTypeName) instanceof SimpleTypeMapping)) { throw new AnnotationException("The dataTypeName " + dataTypeName + " does not denote a simple type mapping"); } } /** * Asserts that a given object type is not a collection class. * * @param objectType the object type to check * @throws AnnotationException if the assertion fails. */ protected final void assertNoCollectionType(Class objectType) { if (hasCollectionType(objectType)) { throw new AnnotationException("No collections allowed here!"); } } /** * Asserts that a given object type is a simple type. * * @param objectType the object type to check * @throws AnnotationException if the assertion fails. */ protected final void assertHasSimpleType(Class objectType) { if (!hasSimpleType(objectType)) { throw new AnnotationException("Only simple types are allowed here!"); } } /** * A placeholder for a type mapping used during the construction of a type mapping, i. e. between calling * {@link TypeMappingFactory#prepareToCreate(Class, Object)} and the registration of the type mapping. *

* This is used to obtain the object type for a given data type name during the construction phase. * * @author Norman Lahme-Huetig * */ protected final class TypeMappingPlaceholder extends TypeMapping { /** * Constructs a new TypeMappingPlaceholder. * * @param objectType the object type * @param dataTypeName the data type name */ protected TypeMappingPlaceholder(Class objectType, N dataTypeName) { super(objectType, dataTypeName); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy