/**
* Copyright 2011-2017 Asakusa Framework Team.
*
* 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 com.asakusafw.testdriver.core;
import java.lang.annotation.Annotation;
import java.text.MessageFormat;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
/**
* A data model definition for testing.
* @param <T> type of data model
* @since 0.2.0
* @version 0.9.1
*/
public interface DataModelDefinition<T> {
/**
* Returns the corresponded data model class.
* @return data model class
*/
Class<T> getModelClass();
/**
* Returns this element's annotation for the specified annotation type.
* @param <A> type of annotation
* @param annotationType class of annotation
* @return this element's annotation for the specified annotation type,
* or {@code null} if does not present
* @throws IllegalArgumentException if some parameters were {@code null}
*/
<A extends Annotation> A getAnnotation(Class<A> annotationType);
/**
* returns the all public property names.
* @return property names
*/
Collection<PropertyName> getProperties();
/**
* Returns the property type.
* @param name the property name
* @return property type, or {@code null} if no such property exists
* @throws IllegalArgumentException if some parameters were {@code null}
*/
PropertyType getType(PropertyName name);
/**
* Returns the property's annotation for the specified annotation type.
* @param <A> type of annotation
* @param name target property
* @param annotationType class of annotation
* @return this property's annotation for the specified annotation type,
* or {@code null} if neither the property nor its specified annotation do not present
* @throws IllegalArgumentException if some parameters were {@code null}
*/
<A extends Annotation> A getAnnotation(PropertyName name, Class<A> annotationType);
/**
* Starts to build a new {@link DataModelReflection}.
* @return a builder object
*/
DataModelDefinition.Builder<T> newReflection();
/**
* Converts a target object into a generic reflection model.
* @param object target object
* @return the converted model
* @throws IllegalArgumentException if some parameters were {@code null}
*/
DataModelReflection toReflection(T object);
/**
* Converts a generic reflection model into a target object.
* @param reflection a reflection model
* @return the converted object
* @throws IllegalArgumentException if some parameters were {@code null}
*/
T toObject(DataModelReflection reflection);
/**
* Converts the given raw property value into a corresponded property value for {@link DataModelReflection}.
* @param value the raw property value
* @return the resolved value
* @since 0.9.1
*/
default Object resolveRawValue(Object value) {
return value;
}
/**
* Builds a {@link DataModelReflection}.
* @param <T> type of data model
* @since 0.2.0
*/
class Builder<T> {
/**
* The current definition object.
*/
protected final DataModelDefinition<T> definition;
/**
* The current properties (includes {@code null} values).
*/
protected final Map<PropertyName, Object> properties;
/**
* Creates a new instance.
* @param definition the target model definition object
* @throws IllegalArgumentException if some parameters were {@code null}
*/
public Builder(DataModelDefinition<T> definition) {
if (definition == null) {
throw new IllegalArgumentException("definition must not be null"); //$NON-NLS-1$
}
this.definition = definition;
this.properties = new HashMap<>();
}
/**
* Adds a property to the building object.
* @param name the property name
* @param value the property value (nullable)
* @return this object (for method chain)
* @throws IllegalArgumentException
* if the property is not defined (on {@code check = true}),
* or the property value has inconsistent type,
* or some parameters were {@code null}
* @throws IllegalStateException the property has been already added
*/
public Builder<T> add(PropertyName name, Object value) {
if (name == null) {
throw new IllegalArgumentException("name must not be null"); //$NON-NLS-1$
}
if (properties.containsKey(name)) {
throw new IllegalStateException(MessageFormat.format(
Messages.getString("DataModelDefinition.errorConflictProperty"), //$NON-NLS-1$
name,
definition));
}
PropertyType type = definition.getType(name);
if (type != null && value != null && type.getRepresentation().isInstance(value) == false) {
throw new IllegalArgumentException(MessageFormat.format(
Messages.getString("DataModelDefinition.errorInconsistentPropertyValue"), //$NON-NLS-1$
name,
type,
value,
definition));
}
properties.put(name, value);
return this;
}
/**
* Builds a reflection object with since added properties.
* @return the built
*/
public DataModelReflection build() {
return new DataModelReflection(properties);
}
}
}