Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.databene.benerator.anno.AnnotationMapper Maven / Gradle / Ivy
Go to download
benerator is a framework for creating realistic and valid high-volume test data, used for
testing (unit/integration/load) and showcase setup.
Metadata constraints are imported from systems and/or configuration files. Data can imported from
and exported to files and systems, anonymized or generated from scratch. Domain packages provide
reusable generators for creating domain-specific data as names and addresses internationalizable
in language and region. It is strongly customizable with plugins and configuration options.
/*
* (c) Copyright 2010-2011 by Volker Bergmann. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, is permitted under the terms of the
* GNU General Public License (GPL).
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* WITHOUT A WARRANTY OF ANY KIND. ALL EXPRESS OR IMPLIED CONDITIONS,
* REPRESENTATIONS AND WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE
* HEREBY EXCLUDED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 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.
*/
package org.databene.benerator.anno;
import java.io.File;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.HashSet;
import java.util.Set;
import javax.validation.constraints.AssertFalse;
import javax.validation.constraints.AssertTrue;
import javax.validation.constraints.DecimalMax;
import javax.validation.constraints.DecimalMin;
import javax.validation.constraints.Digits;
import javax.validation.constraints.Future;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Null;
import javax.validation.constraints.Past;
import javax.validation.constraints.Pattern;
import javax.validation.constraints.Size;
import org.databene.benerator.Generator;
import org.databene.benerator.engine.BeneratorContext;
import org.databene.benerator.engine.DescriptorBasedGenerator;
import org.databene.benerator.factory.ArrayTypeGeneratorFactory;
import org.databene.benerator.factory.CoverageGeneratorFactory;
import org.databene.benerator.factory.DescriptorUtil;
import org.databene.benerator.factory.EquivalenceGeneratorFactory;
import org.databene.benerator.factory.GeneratorFactory;
import org.databene.benerator.factory.GentleDefaultsProvider;
import org.databene.benerator.factory.InstanceGeneratorFactory;
import org.databene.benerator.factory.MeanDefaultsProvider;
import org.databene.benerator.factory.SerialGeneratorFactory;
import org.databene.benerator.factory.StochasticGeneratorFactory;
import org.databene.benerator.wrapper.LastFlagGenerator;
import org.databene.benerator.wrapper.NShotGeneratorProxy;
import org.databene.benerator.wrapper.WrapperFactory;
import org.databene.commons.BeanUtil;
import org.databene.commons.CollectionUtil;
import org.databene.commons.ConfigurationError;
import org.databene.commons.ParseException;
import org.databene.commons.ProgrammerError;
import org.databene.commons.StringUtil;
import org.databene.commons.TimeUtil;
import org.databene.commons.context.ContextAware;
import org.databene.formats.script.ScriptUtil;
import org.databene.formats.util.DataFileUtil;
import org.databene.model.data.ArrayElementDescriptor;
import org.databene.model.data.ArrayTypeDescriptor;
import org.databene.model.data.ComplexTypeDescriptor;
import org.databene.model.data.DataModel;
import org.databene.model.data.DefaultDescriptorProvider;
import org.databene.model.data.InstanceDescriptor;
import org.databene.model.data.Mode;
import org.databene.model.data.SimpleTypeDescriptor;
import org.databene.model.data.TypeDescriptor;
import org.databene.model.data.Uniqueness;
import org.databene.platform.db.DBSystem;
import org.databene.platform.db.DefaultDBSystem;
import org.databene.platform.java.BeanDescriptorProvider;
import org.databene.platform.java.Entity2JavaConverter;
import org.databene.script.DatabeneScriptParser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Maps Java annotations to descriptor objects.
* Created: 29.04.2010 06:59:02
* @since 0.6.1
* @author Volker Bergmann
*/
public class AnnotationMapper extends DefaultDescriptorProvider {
private static final Logger LOGGER = LoggerFactory.getLogger(AnnotationMapper.class);
private static final Set STANDARD_METHODS;
private static final Package BENERATOR_ANNO_PACKAGE = Unique.class.getPackage();
private static final Package BEANVAL_ANNO_PACKAGE = Max.class.getPackage();
@SuppressWarnings("unchecked")
private static final Set> EXPLICITLY_MAPPED_ANNOTATIONS = CollectionUtil.toSet(
Bean.class,
Database.class,
Descriptor.class,
InvocationCount.class,
Factory.class, Equivalence.class, Coverage.class, Serial.class,
Defaults.class, Gentle.class, Mean.class,
ThreadPoolSize.class);
static {
STANDARD_METHODS = new HashSet();
for (Method method : Annotation.class.getMethods())
STANDARD_METHODS.add(method.getName());
}
private DataModel dataModel;
private PathResolver pathResolver;
private ArrayTypeGeneratorFactory arrayTypeGeneratorFactory;
public AnnotationMapper(DataModel dataModel, PathResolver pathResolver) {
super("anno", dataModel);
this.dataModel = dataModel;
this.dataModel.addDescriptorProvider(this);
this.arrayTypeGeneratorFactory = new ArrayTypeGeneratorFactory();
this.pathResolver = pathResolver;
}
// interface -------------------------------------------------------------------------------------------------------
/** Parses @{@link Database} and @{@link Bean} annotations attached to a class,
* initializes the related objects and puts them into the {@link BeneratorContext} */
public void parseClassAnnotations(Annotation[] annotations, BeneratorContext context) {
for (Annotation annotation : annotations) {
if (annotation instanceof Database)
parseDatabase((Database) annotation, context);
else if (annotation instanceof Bean)
parseBean((Bean) annotation, context);
}
}
/** scans test class attributes for attributes with @{@link Source} annotation
* and initializes them with a value from the referred source object */
public Generator> createAndInitAttributeGenerator(Field attribute, BeneratorContext context) {
Source sourceAnno = attribute.getAnnotation(Source.class);
if (sourceAnno != null)
return createAndInitAttributeSourceGenerator(sourceAnno, attribute, context);
else
return null;
}
public Generator createAndInitMethodParamsGenerator(Method testMethod, BeneratorContext context) {
return createAndInitMethodParamsGenerator(new MethodDescriptor(testMethod), context);
}
public Generator createAndInitMethodParamsGenerator(MethodDescriptor testMethod, BeneratorContext context) {
applyMethodGeneratorFactory(testMethod, context);
// Evaluate @Bean and @Database annotations attached to the test method
if (testMethod.getAnnotation(Bean.class) != null)
parseBean(testMethod.getAnnotation(Bean.class), context);
if (testMethod.getAnnotation(Database.class) != null)
parseDatabase(testMethod.getAnnotation(Database.class), context);
// map the native Java bean informations to an array descriptor
ArrayTypeDescriptor type = createMethodParamsType(testMethod);
InstanceDescriptor instance = createMethodParamsInstanceDescriptor(testMethod, type);
Uniqueness uniqueness = (instance.isUnique() != null && instance.isUnique() ? Uniqueness.ORDERED : Uniqueness.NONE);
// create and return generator
Generator generator = createGenerator(type, testMethod, uniqueness, context);
generator.init(context);
return generator;
}
// helper methods --------------------------------------------------------------------------------------------------
protected ArrayTypeDescriptor createMethodParamsType(MethodDescriptor testMethod) {
ArrayTypeDescriptor nativeType = createNativeParamsDescriptor(testMethod);
return createConfiguredParamsDescriptor(testMethod, nativeType);
}
protected InstanceDescriptor createMethodParamsInstanceDescriptor(
MethodDescriptor testMethod, ArrayTypeDescriptor type) {
InstanceDescriptor instance = new InstanceDescriptor(testMethod.getName(), this, type);
for (Annotation annotation : testMethod.getAnnotations())
mapParamAnnotation(annotation, instance, testMethod.getDeclaringClass());
return instance;
}
protected ArrayTypeDescriptor createNativeParamsDescriptor(MethodDescriptor testMethod) {
ArrayTypeDescriptor nativeDescriptor = new ArrayTypeDescriptor(testMethod.getName() + "_native", this);
Class>[] paramTypes = testMethod.getParameterTypes();
for (int i = 0; i < paramTypes.length; i++) {
TypeDescriptor elementType = dataModel.getTypeDescriptor(paramTypes[i].getName());
BeanDescriptorProvider beanDescriptorProvider = dataModel.getBeanDescriptorProvider();
ArrayElementDescriptor elementDescriptor = new ArrayElementDescriptor(i, beanDescriptorProvider, elementType);
if (elementDescriptor.isNullable() == null) { // assure an explicit setting for nullability
if (BeanUtil.isPrimitiveType(paramTypes[i].getName()))
elementDescriptor.setNullable(false); // primitives can never be null
else if (elementDescriptor.getNullQuota() != null &&
((Double) elementDescriptor.getDeclaredDetailValue("nullQuota")) == 0.)
elementDescriptor.setNullable(false); // if nullQuota == 0, then set nullable to false
else
elementDescriptor.setNullable(true);
}
nativeDescriptor.addElement(elementDescriptor);
}
return nativeDescriptor;
}
private ArrayTypeDescriptor createConfiguredParamsDescriptor(
MethodDescriptor testMethod, ArrayTypeDescriptor nativeDescriptor) {
ArrayTypeDescriptor type = new ArrayTypeDescriptor(
testMethod.getName() + "_configured", this, nativeDescriptor);
Class>[] parameterTypes = testMethod.getParameterTypes();
Annotation[][] paramAnnos = testMethod.getParameterAnnotations();
for (int i = 0; i < parameterTypes.length; i++) {
ArrayElementDescriptor parentElement = nativeDescriptor.getElement(i);
if (containsConfig(paramAnnos[i])) {
TypeDescriptor parentElementType = parentElement.getTypeDescriptor();
TypeDescriptor elementType = DescriptorUtil.deriveType(
parentElementType.getName(), parentElementType);
ArrayElementDescriptor element = new ArrayElementDescriptor(i, this, elementType);
element.setParent(parentElement);
for (Annotation annotation : paramAnnos[i])
mapParamAnnotation(annotation, element, testMethod.getDeclaringClass());
type.addElement(element);
}
}
return type;
}
private static boolean containsConfig(Annotation[] annotations) {
for (Annotation annotation : annotations) {
Package annoPkg = annotation.annotationType().getPackage();
if ((annoPkg == BENERATOR_ANNO_PACKAGE || annoPkg == BEANVAL_ANNO_PACKAGE) &&
!explicitlyMappedAnnotation(annotation))
return true;
}
return false;
}
protected static boolean explicitlyMappedAnnotation(Annotation annotation) {
return EXPLICITLY_MAPPED_ANNOTATIONS.contains(annotation.annotationType());
}
protected void applyMethodGeneratorFactory(MethodDescriptor testMethod, BeneratorContext context) {
boolean configured = applyGeneratorFactory(testMethod.getAnnotations(), context);
if (!configured)
applyClassGeneratorFactory(testMethod.getDeclaringClass().getAnnotations(), context);
applyMethodDefaultsProvider(testMethod, context);
}
private void applyClassGeneratorFactory(Annotation[] annotations, BeneratorContext context) {
boolean configured = applyGeneratorFactory(annotations, context);
if (!configured)
context.setGeneratorFactory(context.getGeneratorFactory());
}
protected boolean applyGeneratorFactory(Annotation[] annotations, BeneratorContext context) {
boolean configured = false;
for (Annotation annotation : annotations) {
if (annotation instanceof Factory) {
GeneratorFactory factory = BeanUtil.newInstance(((Factory) annotation).value());
factory.setDefaultsProvider(context.getDefaultsProvider());
context.setGeneratorFactory(factory);
return true;
} else if (annotation instanceof Equivalence) {
context.setGeneratorFactory(new EquivalenceGeneratorFactory());
return true;
} else if (annotation instanceof Coverage) {
context.setGeneratorFactory(new CoverageGeneratorFactory());
return true;
} else if (annotation instanceof Stochastic) {
context.setGeneratorFactory(new StochasticGeneratorFactory());
return true;
} else if (annotation instanceof Serial) {
context.setGeneratorFactory(new SerialGeneratorFactory());
return true;
}
}
return configured;
}
protected void applyMethodDefaultsProvider(MethodDescriptor testMethod, BeneratorContext context) {
// check if the method is annotated with an individual DefaultsProvider...
boolean configured = applyDefaultsProvider(testMethod.getAnnotations(), context);
// ... otherwise check for a class-wide DefaultsProvider annotation...
if (!configured)
applyDefaultsProvider(testMethod.getDeclaringClass().getAnnotations(), context);
// ...otherwise the GeneratorFactory's DefaultProvider is used
}
private static boolean applyDefaultsProvider(Annotation[] annotations, BeneratorContext context) {
for (Annotation annotation : annotations) {
if (annotation instanceof Defaults) {
context.setDefaultsProvider(BeanUtil.newInstance(((Defaults) annotation).value()));
return true;
} else if (annotation instanceof Gentle) {
context.setDefaultsProvider(new GentleDefaultsProvider());
return true;
} else if (annotation instanceof Mean) {
context.setDefaultsProvider(new MeanDefaultsProvider());
return true;
}
}
return false;
}
/*
@SuppressWarnings("unchecked")
private Generator createMethodSourceGenerator(org.databene.benerator.anno.Source source,
Method testMethod, ArrayTypeDescriptor type, BeneratorContext context) {
Generator baseGenerator = (Generator) arrayTypeGeneratorFactory.createGenerator(
type, testMethod.getName(), false, Uniqueness.NONE, context);
return baseGenerator;
}
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
private Generator> createAndInitAttributeSourceGenerator(
org.databene.benerator.anno.Source source, Field attribute, BeneratorContext context) {
String attName = attribute.getName();
TypeDescriptor typeDescriptor = createTypeDescriptor(attribute.getType());
InstanceDescriptor descriptor = new InstanceDescriptor(attName, this, typeDescriptor);
Class> testClass = attribute.getDeclaringClass();
mapParamAnnotation(source, descriptor, testClass);
Offset offset = attribute.getAnnotation(Offset.class);
if (offset != null)
mapParamAnnotation(offset, descriptor, testClass);
Generator generator = InstanceGeneratorFactory.createSingleInstanceGenerator(
descriptor, Uniqueness.NONE, context);
generator = WrapperFactory.applyConverter(generator, new Entity2JavaConverter());
generator.init(context);
return generator;
}
/*
@SuppressWarnings("unchecked")
private Generator createGeneratorGenerator(org.databene.benerator.anno.Generator annotation,
Method testMethod, ArrayTypeDescriptor type, BeneratorContext context) {
return (Generator) arrayTypeGeneratorFactory.createGenerator(
type, testMethod.getName(), false, Uniqueness.NONE, context);
}
*/
@SuppressWarnings("unchecked")
protected Generator createGenerator(ArrayTypeDescriptor type,
MethodDescriptor testMethod, Uniqueness uniqueness, BeneratorContext context) {
Generator generator;
Descriptor descriptorBasedAnno = testMethod.getAnnotation(Descriptor.class);
if (descriptorBasedAnno != null) {
context.setGeneratorFactory(new StochasticGeneratorFactory());
generator = createDescriptorBasedGenerator(descriptorBasedAnno, testMethod, context);
} else {
generator = (Generator) arrayTypeGeneratorFactory.createGenerator(
type, testMethod.getName(), false, uniqueness, context);
}
Class> generatedType = generator.getGeneratedType();
if (generatedType != Object[].class && generatedType != Object.class)
throw new ProgrammerError("Expected Generator or Generator, but found Generator<" +
generatedType.getClass().getSimpleName() + '>');
// evaluate @TestFeed annotation
InvocationCount testCount = testMethod.getAnnotation(InvocationCount.class);
if (testCount != null)
generator = new NShotGeneratorProxy(generator, testCount.value());
// apply LastInstanceDetector
generator = WrapperFactory.applyLastProductDetector(generator);
int indexOfLast = indexOfLast(testMethod);
if (indexOfLast >= 0)
generator = new LastFlagGenerator(generator, indexOfLast);
return generator;
}
private static int indexOfLast(MethodDescriptor testMethod) {
Annotation[][] paramsAnnotations = testMethod.getParameterAnnotations();
for (int i = 0; i < paramsAnnotations.length; i++) {
for (Annotation paramAnnotation : paramsAnnotations[i])
if (paramAnnotation.annotationType() == Last.class)
return i;
}
return -1;
}
@SuppressWarnings({ "unchecked", "rawtypes" })
private static Generator createDescriptorBasedGenerator(Descriptor annotation, MethodDescriptor testMethod, BeneratorContext context) {
String filename = null;
try {
if (annotation.file().length() > 0)
filename = annotation.file();
else
filename = testMethod.getDeclaringClass().getName().replace('.', File.separatorChar) + ".ben.xml";
String testName;
if (annotation.name().length() > 0)
testName = annotation.name();
else
testName = testMethod.getName();
return (Generator) new DescriptorBasedGenerator(filename, testName, context);
} catch (IOException e) {
throw new RuntimeException("Error opening file " + filename, e);
}
}
private static void parseDatabase(Database annotation, BeneratorContext context) {
DBSystem db;
if (!StringUtil.isEmpty(annotation.environment()))
db = new DefaultDBSystem(annotation.id(), annotation.environment(), context.getDataModel());
else
db = new DefaultDBSystem(annotation.id(), annotation.url(), annotation.driver(),
annotation.user(), annotation.password(), context.getDataModel());
if (!StringUtil.isEmpty(annotation.catalog()))
db.setCatalog(annotation.catalog());
if (!StringUtil.isEmpty(annotation.schema()))
db.setSchema(annotation.schema());
db.setLazy(true);
context.setGlobal(db.getId(), db);
}
private static void parseBean(Bean annotation, BeneratorContext context) {
Object bean = instantiateBean(annotation, context);
applyProperties(annotation.properties(), bean, context);
context.setGlobal(annotation.id(), bean);
if (bean instanceof ContextAware)
((ContextAware) bean).setContext(context);
}
private static Object instantiateBean(Bean beanAnno, BeneratorContext context) {
String beanSpec = beanAnno.spec();
Class> beanClass = beanAnno.type();
if (!StringUtil.isEmpty(beanSpec)) {
try {
if (beanClass != Object.class)
throw new ConfigurationError("'type' and 'spec' exclude each other in a @Bean");
return DatabeneScriptParser.parseBeanSpec(beanSpec).evaluate(context);
} catch (ParseException e) {
throw new ConfigurationError("Error parsing bean spec: " + beanSpec, e);
}
} else if (beanClass != Object.class) {
return BeanUtil.newInstance(beanClass);
} else
throw new ConfigurationError("@Bean is missing 'type' or 'spec' attribute");
}
private static void applyProperties(Property[] properties, Object bean, BeneratorContext context) {
for (Property property : properties) {
Object value = resolveProperty(property, bean, context);
BeanUtil.setPropertyValue(bean, property.name(), value, true, true);
}
}
private static Object resolveProperty(Property property, Object bean, BeneratorContext context) {
if (!StringUtil.isEmpty(property.value())) {
if (!StringUtil.isEmpty(property.ref()))
throw new ConfigurationError("'value' and 'ref' exclude each other in a @Property");
Object value = ScriptUtil.evaluate(property.value(), context);
if (value instanceof String)
value = StringUtil.unescape((String) value);
return value;
} else if (!StringUtil.isEmpty(property.ref())) {
return context.get(property.ref());
} else
throw new ConfigurationError("@Property is missing 'value' or 'ref' attribute");
}
/*
private Generator createParamsGenerator(
Method testMethod, InstanceDescriptor descriptor, BeneratorContext context) {
Uniqueness uniqueness = DescriptorUtil.getUniqueness(descriptor, context);
Generator generator = arrayTypeGeneratorFactory.createSimpleArrayGenerator(descriptor.getName(),
(ArrayTypeDescriptor) descriptor.getTypeDescriptor(), uniqueness, context);
return generator;
}
*/
private void mapParamAnnotation(Annotation annotation, InstanceDescriptor descriptor, Class> testClass) {
Package annoPackage = annotation.annotationType().getPackage();
if (BENERATOR_ANNO_PACKAGE.equals(annoPackage))
mapBeneratorParamAnnotation(annotation, descriptor, testClass);
else if (BEANVAL_ANNO_PACKAGE.equals(annoPackage))
mapBeanValidationParameter(annotation, descriptor);
}
private void mapBeneratorParamAnnotation(Annotation annotation, InstanceDescriptor instanceDescriptor, Class> testClass) {
if (LOGGER.isDebugEnabled())
LOGGER.debug("mapDetails(" + annotation + ", " + instanceDescriptor + ")");
try {
Class> annotationType = annotation.annotationType();
if (annotationType == Unique.class)
instanceDescriptor.setDetailValue("unique", true);
else if (annotationType == Granularity.class)
instanceDescriptor.getLocalType(false).setDetailValue("granularity", String.valueOf(DescriptorUtil.convertType(((Granularity) annotation).value(), (SimpleTypeDescriptor) instanceDescriptor.getLocalType(false))));
else if (annotationType == DecimalGranularity.class)
instanceDescriptor.getLocalType(false).setDetailValue("granularity", String.valueOf(DescriptorUtil.convertType(((DecimalGranularity) annotation).value(), (SimpleTypeDescriptor) instanceDescriptor.getLocalType(false))));
else if (annotationType == SizeDistribution.class)
instanceDescriptor.getLocalType(false).setDetailValue("lengthDistribution", ((SizeDistribution) annotation).value());
else if (annotationType == Pattern.class)
mapPatternAnnotation((Pattern) annotation, instanceDescriptor);
else if (annotationType == Size.class)
mapSizeAnnotation((Size) annotation, instanceDescriptor);
else if (annotationType == Source.class)
mapSourceAnnotation((Source) annotation, instanceDescriptor, testClass);
else if (annotationType == Values.class)
mapValuesAnnotation((Values) annotation, instanceDescriptor);
else if (annotationType == Offset.class)
mapOffsetAnnotation((Offset) annotation, instanceDescriptor);
else if (annotationType == MinDate.class)
mapMinDateAnnotation((MinDate) annotation, instanceDescriptor);
else if (annotationType == MaxDate.class)
mapMaxDateAnnotation((MaxDate) annotation, instanceDescriptor);
else if (annotationType == Last.class)
instanceDescriptor.setMode(Mode.ignored);
else if (!explicitlyMappedAnnotation(annotation))
mapAnyValueTypeAnnotation(annotation, instanceDescriptor);
} catch (Exception e) {
throw new ConfigurationError("Error mapping annotation settings", e);
}
}
private static void mapSizeAnnotation(Size size, InstanceDescriptor instanceDescriptor) {
setDetail("minLength", size.min(), instanceDescriptor);
setDetail("maxLength", size.max(), instanceDescriptor);
}
private static void mapPatternAnnotation(Pattern pattern, InstanceDescriptor instanceDescriptor) {
if (!StringUtil.isEmpty(pattern.regexp()))
setDetail("pattern", pattern.regexp(), instanceDescriptor);
}
private void mapSourceAnnotation(Source source, InstanceDescriptor instanceDescriptor, Class> testClass) throws Exception {
mapSourceUriOrValue(source.value(), instanceDescriptor, testClass);
mapSourceUriOrValue(source.uri(), instanceDescriptor, testClass);
mapSourceSetting(source.segment(), "segment", instanceDescriptor);
mapSourceSetting(source.id(), "source", instanceDescriptor);
mapSourceSetting(source.dataset(), "dataset", instanceDescriptor);
mapSourceSetting(source.nesting(), "nesting", instanceDescriptor);
mapSourceSetting(source.encoding(), "encoding", instanceDescriptor);
mapSourceSetting(source.filter(), "filter", instanceDescriptor);
mapSourceSetting(source.selector(), "selector", instanceDescriptor);
mapSourceSetting(source.separator(), "separator", instanceDescriptor);
mapSourceSetting(source.emptyMarker(), "emptyMarker", instanceDescriptor);
mapSourceSetting(source.nullMarker(), "nullMarker", instanceDescriptor);
mapFormatted(source, instanceDescriptor);
setDetail("rowBased", source.rowBased(), instanceDescriptor);
}
private void mapSourceUriOrValue(String value, InstanceDescriptor instanceDescriptor, Class> testClass) {
if (DataFileUtil.isExcelOrCsvDocument(value))
value = pathResolver.getPathFor(value, testClass);
mapSourceSetting(value, "source", instanceDescriptor);
}
private static void mapSourceSetting(String value, String detailName, InstanceDescriptor instanceDescriptor) {
if (!StringUtil.isEmpty(value))
setDetail(detailName, value, instanceDescriptor);
}
private static void mapFormatted(Source source, InstanceDescriptor instanceDescriptor) {
switch (source.format()) {
case formatted: case raw: setDetail("format", source.format().name(), instanceDescriptor); break;
case globalDefault: break;
default: throw new UnsupportedOperationException("Not a supported Format: " + source.format());
}
}
private static void mapValuesAnnotation(Values annotation, InstanceDescriptor instanceDescriptor) throws Exception {
Method method = annotation.annotationType().getMethod("value");
String[] values = (String[]) method.invoke(annotation);
StringBuilder builder = new StringBuilder();
for (int i = 0; i < values.length; i++) {
if (i > 0)
builder.append(',');
builder.append("'").append(values[i].replace("'", "\\'")).append("'");
}
((SimpleTypeDescriptor) instanceDescriptor.getLocalType(false)).setValues(builder.toString());
}
private static void mapOffsetAnnotation(Offset annotation, InstanceDescriptor instanceDescriptor) throws Exception {
if (annotation.value() != 0)
instanceDescriptor.getLocalType().setOffset(annotation.value());
}
private static void mapMinDateAnnotation(MinDate annotation, InstanceDescriptor instanceDescriptor) throws Exception {
TypeDescriptor localType = instanceDescriptor.getLocalType();
if (!(localType instanceof SimpleTypeDescriptor))
throw new ConfigurationError("@MinDate can only be applied to Date types");
((SimpleTypeDescriptor) localType).setMin(annotation.value());
}
private static void mapMaxDateAnnotation(MaxDate annotation, InstanceDescriptor instanceDescriptor) throws Exception {
TypeDescriptor localType = instanceDescriptor.getLocalType();
if (!(localType instanceof SimpleTypeDescriptor))
throw new ConfigurationError("@MaxDate can only be applied to Date types");
((SimpleTypeDescriptor) localType).setMax(annotation.value());
}
private static void mapAnyValueTypeAnnotation(Annotation annotation, InstanceDescriptor instanceDescriptor) throws Exception {
Method method = annotation.annotationType().getMethod("value");
Object value = normalize(method.invoke(annotation));
String detailName = StringUtil.uncapitalize(annotation.annotationType().getSimpleName());
setDetail(detailName, value, instanceDescriptor);
}
private static void mapBeanValidationParameter(Annotation annotation, InstanceDescriptor element) {
SimpleTypeDescriptor typeDescriptor = (SimpleTypeDescriptor) element.getLocalType(false);
if (annotation instanceof AssertFalse)
typeDescriptor.setTrueQuota(0.);
else if (annotation instanceof AssertTrue)
typeDescriptor.setTrueQuota(1.);
else if (annotation instanceof DecimalMax)
typeDescriptor.setMax(String.valueOf(DescriptorUtil.convertType(((DecimalMax) annotation).value(), typeDescriptor)));
else if (annotation instanceof DecimalMin)
typeDescriptor.setMin(String.valueOf(DescriptorUtil.convertType(((DecimalMin) annotation).value(), typeDescriptor)));
else if (annotation instanceof Digits) {
Digits digits = (Digits) annotation;
typeDescriptor.setGranularity(String.valueOf(Math.pow(10, - digits.fraction())));
} else if (annotation instanceof Future)
typeDescriptor.setMin(new SimpleDateFormat("yyyy-MM-dd").format(TimeUtil.tomorrow()));
else if (annotation instanceof Max)
typeDescriptor.setMax(String.valueOf(((Max) annotation).value()));
else if (annotation instanceof Min)
typeDescriptor.setMin(String.valueOf(((Min) annotation).value()));
else if (annotation instanceof NotNull) {
element.setNullable(false);
element.setNullQuota(0.);
} else if (annotation instanceof Null) {
element.setNullable(true);
element.setNullQuota(1.);
} else if (annotation instanceof Past)
typeDescriptor.setMax(new SimpleDateFormat("yyyy-MM-dd").format(TimeUtil.yesterday()));
else if (annotation instanceof Pattern)
typeDescriptor.setPattern(String.valueOf(((Pattern) annotation).regexp()));
else if (annotation instanceof Size) {
Size size = (Size) annotation;
typeDescriptor.setMinLength(size.min());
typeDescriptor.setMaxLength(size.max());
}
}
private static void setDetail(String detailName, Object detailValue, InstanceDescriptor instanceDescriptor) {
if (instanceDescriptor.supportsDetail(detailName))
instanceDescriptor.setDetailValue(detailName, detailValue);
else
instanceDescriptor.getLocalType().setDetailValue(detailName, detailValue);
}
private static Object normalize(Object value) {
if (value == null)
return null;
if (value instanceof String && ((String) value).length() == 0)
return null;
if (value.getClass().isArray() && Array.getLength(value) == 0)
return null;
return value;
}
protected TypeDescriptor createTypeDescriptor(Class> type) {
String abstractType = dataModel.getBeanDescriptorProvider().abstractType(type);
TypeDescriptor baseTypeDescriptor = dataModel.getTypeDescriptor(abstractType);
TypeDescriptor typeDescriptor;
if (baseTypeDescriptor instanceof SimpleTypeDescriptor) {
typeDescriptor = new SimpleTypeDescriptor(type.getName(), this, (SimpleTypeDescriptor) baseTypeDescriptor);
} else if (baseTypeDescriptor instanceof ComplexTypeDescriptor) {
typeDescriptor = new ComplexTypeDescriptor(type.getName(), this, (ComplexTypeDescriptor) baseTypeDescriptor);
} else
throw new ConfigurationError("Cannot handle descriptor: " + baseTypeDescriptor);
return typeDescriptor;
}
}