/**
* Copyright 2011-2014 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.rule;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.asakusafw.testdriver.core.DataModelDefinition;
import com.asakusafw.testdriver.core.PropertyName;
import com.asakusafw.testdriver.core.PropertyType;
import com.asakusafw.testdriver.core.VerifyRule;
/**
* Builds condition based {@link VerifyRule}s.
* @since 0.2.0
*/
public class VerifyRuleBuilder {
private final DataModelDefinition<?> definition;
private final Set<DataModelCondition> dataModelConditions;
private final Map<PropertyName, Property> propertyConditions;
/**
* Creates a new instance.
* @param definition the definition of target data model type
* @throws IllegalArgumentException if some parameters were {@code null}
*/
public VerifyRuleBuilder(DataModelDefinition<?> definition) {
if (definition == null) {
throw new IllegalArgumentException("definition must not be null"); //$NON-NLS-1$
}
this.definition = definition;
this.dataModelConditions = new HashSet<DataModelCondition>();
this.propertyConditions = new LinkedHashMap<PropertyName, VerifyRuleBuilder.Property>();
}
/**
* Adds a rule to accept if actual data is absent.
* Note that if one of the added rules is accepted, this model object will be also accepted.
* @return this object (for method chain)
*/
public VerifyRuleBuilder acceptIfAbsent() {
this.dataModelConditions.add(DataModelCondition.IGNORE_ABSENT);
return this;
}
/**
* Adds a rule to accept if expected data is absent.
* Note that if one of the added rules is accepted, this model object will be also accepted.
* @return this object (for method chain)
*/
public VerifyRuleBuilder acceptIfUnexpected() {
this.dataModelConditions.add(DataModelCondition.IGNORE_UNEXPECTED);
return this;
}
/**
* Returns the sub rule builder for the specified property.
* Note that this model will be only accepted if all properties are accepted.
* @param name the property name
* @return the rule builder for the property
* @throws IllegalArgumentException if the property does not exist,
* or if some parameters were {@code null}
*/
public Property property(String name) {
if (name == null) {
throw new IllegalArgumentException("name must not be null"); //$NON-NLS-1$
}
String[] words = name.split("_|-|\\s+");
PropertyName propertyName = PropertyName.newInstance(words);
PropertyType type = definition.getType(propertyName);
if (type == null) {
throw new IllegalArgumentException(MessageFormat.format(
"\"{0}\"にプロパティ\"{1}\"は定義されていません",
definition.getModelClass().getName(),
propertyName));
}
Property subBuilder = propertyConditions.get(propertyName);
if (subBuilder == null) {
subBuilder = new Property(propertyName, type);
propertyConditions.put(propertyName, subBuilder);
}
return subBuilder;
}
/**
* Returns a {@link VerifyRule} from since added rules.
* @return the created {@link VerifyRule}
*/
public VerifyRule toVerifyRule() {
List<PropertyName> keys = new ArrayList<PropertyName>();
List<PropertyCondition<?>> properties = new ArrayList<PropertyCondition<?>>();
for (Map.Entry<PropertyName, Property> entry : propertyConditions.entrySet()) {
Property property = entry.getValue();
if (property.key) {
keys.add(entry.getKey());
}
if (property.predicates.isEmpty() == false) {
@SuppressWarnings({ "unchecked", "rawtypes" })
PropertyCondition<?> cond = new PropertyCondition(
entry.getKey(),
definition.getType(entry.getKey()).getRepresentation(),
property.predicates);
properties.add(cond);
}
}
return new VerifyRuleInterpretor(keys, dataModelConditions, properties);
}
/**
* Builds verify conditions for individual properties.
* @since 0.2.0
*/
public static class Property {
private final PropertyName name;
private final PropertyType type;
boolean key;
final List<ValuePredicate<?>> predicates;
Property(PropertyName name, PropertyType type) {
assert name != null;
assert type != null;
this.name = name;
this.type = type;
this.key = false;
this.predicates = new ArrayList<ValuePredicate<?>>();
}
/**
* Returns the name of this property.
* @return the name
*/
public PropertyName getName() {
return name;
}
/**
* Returns the type of this property.
* @return the type
*/
public PropertyType getType() {
return type;
}
/**
* Make this property as a key.
* @return this object (for method chain)
*/
public Property asKey() {
this.key = true;
return this;
}
/**
* Adds acceptable predicate between expected value and actual value to this property.
* Note that if one of the added rules is accepted, this property will be also accepted.
* @param predicate acceptable predicate
* @return this object (for method chain)
* @throws IllegalArgumentException if some parameters were {@code null}
*/
public Property accept(ValuePredicate<?> predicate) {
if (predicate == null) {
throw new IllegalArgumentException("predicate must not be null"); //$NON-NLS-1$
}
this.predicates.add(predicate);
return this;
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("Property [name=");
builder.append(name);
builder.append(", type=");
builder.append(type);
builder.append(", key=");
builder.append(key);
builder.append(", predicates=");
builder.append(predicates);
builder.append("]");
return builder.toString();
}
}
}