package org.springmodules.validation.bean.conf.loader.xml;
import java.beans.PropertyDescriptor;
import java.util.ArrayList;
import java.util.List;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import junit.framework.TestCase;
import org.easymock.ArgumentsMatcher;
import org.easymock.MockControl;
import org.springframework.beans.BeanUtils;
import org.springmodules.validation.bean.conf.CascadeValidation;
import org.springmodules.validation.bean.conf.MutableBeanValidationConfiguration;
import org.springmodules.validation.bean.conf.ValidationConfigurationException;
import org.springmodules.validation.bean.conf.loader.xml.handler.ClassValidationElementHandler;
import org.springmodules.validation.bean.conf.loader.xml.handler.PropertyValidationElementHandler;
import org.springmodules.validation.bean.rule.PropertyValidationRule;
import org.springmodules.validation.bean.rule.ValidationMethodValidationRule;
import org.springmodules.validation.bean.rule.ValidationRule;
import org.springmodules.validation.bean.context.ValidationContextUtils;
import org.springmodules.validation.util.cel.ognl.OgnlConditionExpressionParser;
import org.springmodules.validation.util.fel.parser.OgnlFunctionExpressionParser;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
/**
* Tests for {@link org.springmodules.validation.bean.conf.loader.xml.DefaultXmlBeanValidationConfigurationLoader}.
*
* @author Uri Boness
*/
public class DefaultXmlBeanValidationConfigurationLoaderTests extends TestCase {
private DefaultXmlBeanValidationConfigurationLoader loader;
private MockControl registryControl;
private ValidationRuleElementHandlerRegistry registry;
private MockControl configurationControl;
private MutableBeanValidationConfiguration configuration;
private MockControl propertyHandlerControl;
private PropertyValidationElementHandler propertyHandler;
private MockControl classHandlerControl;
private ClassValidationElementHandler classHandler;
private MockControl ruleControl;
private ValidationRule rule;
protected void setUp() throws Exception {
registryControl = MockControl.createControl(ValidationRuleElementHandlerRegistry.class);
registry = (ValidationRuleElementHandlerRegistry) registryControl.getMock();
configurationControl = MockControl.createControl(MutableBeanValidationConfiguration.class);
configuration = (MutableBeanValidationConfiguration) configurationControl.getMock();
ruleControl = MockControl.createControl(ValidationRule.class);
rule = (ValidationRule) ruleControl.getMock();
propertyHandlerControl = MockControl.createControl(PropertyValidationElementHandler.class);
propertyHandler = (PropertyValidationElementHandler) propertyHandlerControl.getMock();
classHandlerControl = MockControl.createControl(ClassValidationElementHandler.class);
classHandler = (ClassValidationElementHandler) classHandlerControl.getMock();
loader = new DefaultXmlBeanValidationConfigurationLoader(registry);
}
public void testHandlePropertyDefinition_WithNoRulesAndNoCascading() throws Exception {
Element propertyDefinition = element("property",
new String[]{"name"},
new String[]{"name"}
);
replay();
loader.handlePropertyDefinition(propertyDefinition, Object.class, configuration);
verify();
}
public void testHandlePropertyDefinition_WithNoRulesButWithCascading() throws Exception {
Element propertyDefinition = element("property",
new String[]{"name", "cascade"},
new String[]{"name", "true"}
);
configuration.addCascadeValidation(new CascadeValidation("name"));
replay();
loader.handlePropertyDefinition(propertyDefinition, TestBean.class, configuration);
verify();
}
public void testHandlePropertyDefinition_WithARuleAndCascading() throws Exception {
Document document = document();
Element propertyDefinition = element(document, "property",
new String[]{"name", "cascade"},
new String[]{"name", "true"}
);
Element ruleDefinition = element(document, "rule");
propertyDefinition.appendChild(ruleDefinition);
PropertyDescriptor descriptor = BeanUtils.getPropertyDescriptor(TestBean.class, "name");
configuration.addCascadeValidation(new CascadeValidation("name"));
registryControl.expectAndReturn(registry.findPropertyHandler(ruleDefinition, TestBean.class, descriptor), propertyHandler);
propertyHandler.handle(ruleDefinition, "name", configuration);
final PropertyValidationRule propertyRule = new PropertyValidationRule("name", rule);
loader = new DefaultXmlBeanValidationConfigurationLoader(registry) {
protected PropertyValidationRule createPropertyRule(String propertyName, ValidationRule rule) {
return propertyRule;
}
};
replay();
loader.handlePropertyDefinition(propertyDefinition, TestBean.class, configuration);
verify();
}
public void testHandlePropertyDefinition_WithoutAppropriateHandler() throws Exception {
Document document = document();
Element propertyDefinition = element(document, "property",
new String[]{"name", "cascade"},
new String[]{"name", "true"}
);
Element ruleDefinition = element(document, "rule");
propertyDefinition.appendChild(ruleDefinition);
PropertyDescriptor descriptor = BeanUtils.getPropertyDescriptor(TestBean.class, "name");
configuration.addCascadeValidation(new CascadeValidation("name"));
registryControl.expectAndReturn(registry.findPropertyHandler(ruleDefinition, TestBean.class, descriptor), null);
replay();
try {
loader.handlePropertyDefinition(propertyDefinition, TestBean.class, configuration);
fail("An ValidationConfigurationException is expected to be thrown if of the configured rules has no handler");
} catch (ValidationConfigurationException xce) {
// expected
}
verify();
}
public void testHandleGlobalDefinition_WithNoRules() throws Exception {
Element globalDefinition = element("global");
replay();
loader.handleGlobalDefinition(globalDefinition, TestBean.class, configuration);
verify();
}
public void testHandleGlobalDefinition_WithARule() throws Exception {
Document document = document();
Element globalDefinition = element(document, "global");
Element ruleDefinition = element(document, "rule");
globalDefinition.appendChild(ruleDefinition);
registryControl.expectAndReturn(registry.findClassHandler(ruleDefinition, TestBean.class), classHandler);
classHandler.handle(ruleDefinition, configuration);
replay();
loader.handleGlobalDefinition(globalDefinition, TestBean.class, configuration);
verify();
}
public void testHandleGlobalDefinition_WithoutAppropriateHandler() throws Exception {
Document document = document();
Element globalDefinition = element(document, "global");
Element ruleDefinition = element(document, "rule");
globalDefinition.appendChild(ruleDefinition);
registryControl.expectAndReturn(registry.findClassHandler(ruleDefinition, TestBean.class), null);
replay();
try {
loader.handleGlobalDefinition(globalDefinition, TestBean.class, configuration);
fail("An ValidationConfigurationException is expected to be thrown if no proper element handler " +
"could be found for a rule definition");
} catch (ValidationConfigurationException xce) {
// expected
}
verify();
}
public void testHandleMethodDefinition() throws Exception {
Document document = document();
Element methodElement = element(document, "method",
new String[]{"name", "code", "message", "args", "apply-if"},
new String[]{"validate", "thecode", "themessage", "a, b, c", "true"}
);
loader = new DefaultXmlBeanValidationConfigurationLoader() {
protected ValidationMethodValidationRule createMethodValidationRule(
Class clazz,
String methodName,
String errorCode,
String message,
String argsString,
String contextsString,
String applyIfString) {
assertEquals(TestBean.class, clazz);
assertEquals("validate", methodName);
assertEquals("thecode", errorCode);
assertEquals("themessage", message);
assertEquals("a, b, c", argsString);
assertEquals("true", applyIfString);
assertEquals("", contextsString);
return super.createMethodValidationRule(clazz, methodName, errorCode, message, argsString, contextsString, applyIfString);
}
};
loader.setFunctionExpressionParser(new OgnlFunctionExpressionParser());
loader.setConditionExpressionParser(new OgnlConditionExpressionParser());
loader.handleMethodDefinition(methodElement, TestBean.class, configuration);
}
public void testHandleMethodDefinition_WithProperty() throws Exception {
Document document = document();
Element methodElement = element(document, "method",
new String[]{"name", "for-property", "code", "message", "args", "contexts", "apply-if"},
new String[]{"validate", "name", "thecode", "themessage", "a, b, c", "bla", "true"}
);
// a hack to ensure the createMethodValidationRule method
// is actually called. In the call an element is added to
// this list and after the test this list is asserted to be
// non-empty
final List list = new ArrayList();
loader = new DefaultXmlBeanValidationConfigurationLoader() {
protected ValidationMethodValidationRule createMethodValidationRule(
Class clazz,
String methodName,
String errorCode,
String message,
String argsString,
String contextStrings,
String applyIfString) {
list.add("entered");
assertEquals(TestBean.class, clazz);
assertEquals("validate", methodName);
assertEquals("thecode", errorCode);
assertEquals("themessage", message);
assertEquals("a, b, c", argsString);
assertEquals("bla", contextStrings);
assertEquals("true", applyIfString);
return super.createMethodValidationRule(clazz, methodName, errorCode, message, argsString, contextStrings, applyIfString);
}
};
configuration.addPropertyRule("name", null);
configurationControl.setMatcher(new ArgumentsMatcher() {
public boolean matches(Object[] objects, Object[] objects1) {
return objects.length == 2 && objects[0].equals(objects1[0]);
}
public String toString(Object[] objects) {
return "";
}
});
loader.setFunctionExpressionParser(new OgnlFunctionExpressionParser());
loader.setConditionExpressionParser(new OgnlConditionExpressionParser());
replay();
loader.handleMethodDefinition(methodElement, TestBean.class, configuration);
verify();
assertFalse("createMethodValidationRule on configuration was not called", list.isEmpty());
}
public void testCreateMethodValidationRule() throws Exception {
loader.setFunctionExpressionParser(new OgnlFunctionExpressionParser());
loader.setConditionExpressionParser(new OgnlConditionExpressionParser());
ValidationMethodValidationRule rule = loader.createMethodValidationRule(TestBean.class, "validate", "code", "message", "'a', 'b', 'c'", "bla", "true");
TestBean testBean = new TestBean() {
public boolean validate() {
return false;
}
};
assertEquals("code", rule.getErrorCode());
assertEquals("message", rule.getDefaultErrorMessage());
ValidationContextUtils.setContext("bla");
assertTrue(rule.isApplicable(testBean));
ValidationContextUtils.clearContext();
Object[] args = rule.getErrorArguments(testBean);
assertEquals(3, args.length);
assertEquals(new Character('a'), args[0]);
assertEquals(new Character('b'), args[1]);
assertEquals(new Character('c'), args[2]);
assertFalse(rule.getCondition().check(testBean));
}
//=============================================== Helper Methods ===================================================
protected void replay() {
registryControl.replay();
configurationControl.replay();
propertyHandlerControl.replay();
classHandlerControl.replay();
ruleControl.replay();
}
protected void verify() {
registryControl.verify();
configurationControl.verify();
propertyHandlerControl.verify();
classHandlerControl.verify();
ruleControl.verify();
}
protected Document document() throws Exception {
DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
return builder.newDocument();
}
protected Element element(Document doc, String name) throws Exception {
return doc.createElement(name);
}
protected Element element(String name) throws Exception {
return element(name, new String[0], new String[0]);
}
protected Element element(String name, String[] attrNames, String[] attrValues) throws Exception {
return element(document(), name, attrNames, attrValues);
}
protected Element element(Document doc, String name, String[] attrNames, String[] attrValues) throws Exception {
if (attrNames.length != attrValues.length) {
throw new IllegalArgumentException("attribute names and attribute values have different sizes");
}
Element element = doc.createElement(name);
for (int i = 0; i < attrNames.length; i++) {
element.setAttribute(attrNames[i], attrValues[i]);
}
return element;
}
//=============================================== Inner Classes ===================================================
public class TestBean {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public boolean validate() {
return true;
}
}
}