/*
* This code is distributed under The GNU Lesser General Public License (LGPLv3)
* Please visit GNU site for LGPLv3 http://www.gnu.org/copyleft/lesser.html
*
* Copyright Denis Pavlov 2009
* Web: http://www.genericdtoassembler.org
* SVN: https://svn.code.sf.net/p/geda-genericdto/code/trunk/
* SVN (mirror): http://geda-genericdto.googlecode.com/svn/trunk/
*/
package com.inspiresoftware.lib.dto.geda.assembler;
import com.inspiresoftware.lib.dto.geda.annotations.Dto;
import com.inspiresoftware.lib.dto.geda.assembler.extension.DataReader;
import com.inspiresoftware.lib.dto.geda.assembler.extension.DataWriter;
import com.inspiresoftware.lib.dto.geda.dsl.Registry;
import com.inspiresoftware.lib.dto.geda.exception.AnnotationMissingBindingException;
import com.inspiresoftware.lib.dto.geda.exception.AnnotationMissingBindingException.MissingBindingType;
import com.inspiresoftware.lib.dto.geda.exception.AnnotationValidatingBindingException;
/**
* Small utility class that validates pipes.
* <p/>
* User: Denis Pavlov
* Date: Jan 26, 2010
* Time: 3:43:30 PM
*/
final class PipeValidator {
private PipeValidator() {
// prevent instanciation
}
/**
* Validates that read and write pipes for dto to entity are non null.
*
* @param dtoRead method for reading data from DTO field
* @param dtoWrite method for writting data to DTO field
* @param dtoField dto field
* @param entityRead method for reading data from Entity field
* @param entityWrite method for writting data to Entity field
* @param entityField entity field
* @throws AnnotationMissingBindingException if any of pipe is null (exception is thrown with
* a bit more clarification then the generic one).
*/
static void validatePipeNonNull(final DataReader dtoRead,
final DataWriter dtoWrite,
final String dtoField,
final DataReader entityRead,
final DataWriter entityWrite,
final String entityField)
throws AnnotationMissingBindingException {
validateReadPipeNonNull(dtoWrite, dtoField, entityRead, entityField);
validateWritePipeNonNull(dtoRead, dtoField, entityWrite, entityField);
}
/**
* Validates that read and write pipes for dto to entity are non null.
*
* @param dtoWrite method for writting data to DTO field
* @param dtoWriteField dto field
* @param entityRead method for reading data from Entity field
* @param entityReadField entity field
* @throws AnnotationMissingBindingException if any of pipe is null (exception is thrown with
* a bit more clarification then the generic one).
*/
static void validateReadPipeNonNull(final DataWriter dtoWrite,
final String dtoWriteField,
final DataReader entityRead,
final String entityReadField)
throws AnnotationMissingBindingException {
validatePipeNonNull(entityRead, MissingBindingType.ENTITY_READ, entityReadField);
validatePipeNonNull(dtoWrite, MissingBindingType.DTO_WRITE, dtoWriteField);
}
/**
* Validates that read and write pipes for dto to entity match types.
*
* @param dtoRead method for reading data from DTO field
* @param dtoReadField dto field
* @param entityWrite method for writting data to Entity field
* @param entityWriteField entity field
* @throws AnnotationMissingBindingException if any of pipe is null (exception is thrown with
* a bit more clarification then the generic one).
*/
static void validateWritePipeNonNull(final DataReader dtoRead,
final String dtoReadField,
final DataWriter entityWrite,
final String entityWriteField)
throws AnnotationMissingBindingException {
validatePipeNonNull(dtoRead, MissingBindingType.DTO_READ, dtoReadField);
validatePipeNonNull(entityWrite, MissingBindingType.ENTITY_WRITE, entityWriteField);
}
/**
* Validate that method is not null.
*
* @param meth method to check
* @param type type of reder/writer
* @param fieldName field name
* @throws AnnotationMissingBindingException if reader/writer is missing
*/
static void validatePipeNonNull(final Object meth, final MissingBindingType type, final String fieldName)
throws AnnotationMissingBindingException {
if (meth == null) {
throw new AnnotationMissingBindingException(type, fieldName);
}
}
/**
* Validates thate read and write pipes for dto to entity match types.
*
* @param registry DSL registry
* @param dtoRead method for reading data from DTO field
* @param dtoWrite method for writing data to DTO field
* @param dtoField dto field
* @param entityRead method for reading data from Entity field
* @param entityWrite method for writing data to Entity field
* @param entityField entity field
* @throws AnnotationValidatingBindingException if arguments do not match (exception is thrown with
* a bit more clarification then the generic one).
*/
static void validatePipeTypes(final Registry registry,
final DataReader dtoRead,
final DataWriter dtoWrite,
final String dtoField,
final DataReader entityRead,
final DataWriter entityWrite,
final String entityField)
throws AnnotationValidatingBindingException {
validateReadPipeTypes(registry, dtoWrite, dtoField, entityRead, entityField);
validateWritePipeTypes(registry, dtoRead, dtoField, entityWrite, entityField);
}
private static boolean sameDataType(final Class< ? > data1, final Class< ? > data2) {
return data1.equals(data2)
|| (data1.isPrimitive() && !data2.isPrimitive() && samePrimitiveDataType(data2, data1))
|| (!data1.isPrimitive() && data2.isPrimitive() && samePrimitiveDataType(data1, data2));
}
private static boolean samePrimitiveDataType(final Class< ? > wrapper, final Class< ? > primitive) {
try {
return wrapper.getDeclaredField("TYPE").get(null).equals(primitive);
} catch (Throwable thr) {
return false;
}
}
/**
* Validates that read and write pipes for dto to entity match types.
*
* @param registry DSL registry
* @param dtoWrite method for writing data to DTO field
* @param dtoField dto field
* @param entityRead method for reading data from Entity field
* @param entityField entity field
* @throws AnnotationValidatingBindingException if arguments do not match (exception is thrown with
* a bit more clarification then the generic one).
*/
static void validateReadPipeTypes(final Registry registry,
final DataWriter dtoWrite,
final String dtoField,
final DataReader entityRead,
final String entityField)
throws AnnotationValidatingBindingException {
final Class< ? > dtoWriteClass = dtoWrite.getParameterType();
final Class< ? > entityReadClass = entityRead.getReturnType();
if (
// if it is interface we cannot find out until we get an annotated class instance.
!dtoWriteClass.isInterface()
// check if it is a nested dto
&& dtoWriteClass.getAnnotation(Dto.class) == null
// check if it is a nested dto in DSL
&& (registry == null || registry.has(dtoWriteClass) == null)
// Object checking is for generics - they are too much effort just let it go
&& !entityReadClass.equals(Object.class) && !dtoWriteClass.equals(Object.class)
// check the same types
&& !sameDataType(dtoWriteClass, entityReadClass)
) {
throw new AnnotationValidatingBindingException(
dtoField, dtoWrite.getClass().getCanonicalName(), dtoWriteClass.getSimpleName(),
entityField, entityRead.getClass().getCanonicalName(), entityReadClass.getSimpleName(), false);
}
}
/**
* Validates that read and write pipes for dto to entity match types.
*
* @param registry DSL registry
* @param dtoRead method for reading data from DTO field
* @param dtoField dto field
* @param entityWrite method for writing data to Entity field
* @param entityField entity field
*
* @throws AnnotationValidatingBindingException if arguments do not match (exception is thrown with
* a bit more clarification then the generic one).
*/
static void validateWritePipeTypes(final Registry registry,
final DataReader dtoRead,
final String dtoField,
final DataWriter entityWrite,
final String entityField)
throws AnnotationValidatingBindingException {
final Class< ? > dtoReadClass = dtoRead.getReturnType();
final Class< ? > entityWriteClass = entityWrite.getParameterType();
if (
// if it is interface we cannot find out until we get an annotated class instance.
!dtoReadClass.isInterface()
// check if it is a nested dto
&& dtoReadClass.getAnnotation(Dto.class) == null
// check if it is a nested dto in DSL
&& (registry == null || registry.has(dtoReadClass) == null)
// Object checking is for generics - they are too much effort just let it go
&& !entityWriteClass.equals(Object.class) && !dtoReadClass.equals(Object.class)
// check the same types
&& !sameDataType(dtoReadClass, entityWriteClass)
) {
throw new AnnotationValidatingBindingException(
dtoField, dtoRead.getClass().getCanonicalName(), dtoReadClass.getSimpleName(),
entityField, entityWrite.getClass().getCanonicalName(), entityWriteClass.getSimpleName(), true);
}
}
}