package org.springmodules.validation.bean.annotation.javascript.taglib;
import java.io.IOException;
import java.io.StringWriter;
import java.lang.annotation.Annotation;
import org.apache.commons.collections.Predicate;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.support.MessageSourceAccessor;
import org.springframework.web.util.JavaScriptUtils;
import org.springmodules.validation.bean.context.ValidationContextUtils;
import org.springmodules.validation.bean.rule.AbstractValidationRule;
import org.springmodules.validation.valang.javascript.ValangJavaScriptTranslator;
import org.springmodules.validation.valang.parser.ParseException;
import org.springmodules.validation.valang.parser.ValangParser;
public abstract class Handler {
private final static Log logger = LogFactory.getLog(Handler.class);
public final static String CONTEXTS_ATTR = "contexts";
protected Class[] supportedAnnotationTypes;
/**
* For field level annotations.
*
* @param field
* if null, implies a class level annotation
* @param annotation
* @param messages
* @return
*/
public abstract String convertToValang(String fieldName, Annotation annotation, MessageSourceAccessor messages);
/* Helpers */
protected String buildBasicRule(String fieldName, String errMsg, String function, String applyIf,
Annotation annotation) {
if (applyIf != null && applyIf.length() > 0) {
return buildBasicRule(fieldName, errMsg, wrapInApplyIf(function, applyIf), annotation);
}
return buildBasicRule(fieldName, errMsg, function, annotation);
}
protected String buildBasicRule(String fieldName, String errMsg, String function, Annotation annotation) {
if (!isApplicableContext(annotation)) {
logger.debug("Anotation not suitable for this context.");
return null;
}
StringBuffer sb = new StringBuffer();
sb.append("new ValangValidator.Rule(");
sb.append(wrapAndEscapeJsString(fieldName));
sb.append(",'',");// valangStufNotImplementedField
sb.append(wrapAndEscapeJsString(errMsg));
sb.append(',');
sb.append(function);
sb.append(")");
return sb.toString();
}
public String wrapAndEscapeJsString(String string) {
StringBuffer sb = new StringBuffer();
sb.append('\'');
if (string == null) {
sb.append("null");
} else {
sb.append(JavaScriptUtils.javaScriptEscape(string));
}
sb.append('\'');
return sb.toString();
}
/* Helpers for context support */
protected static boolean isApplicableContext(Annotation a) {
return ValidationContextUtils.tokensSupportedByCurrentContext(extractApplicableContexts(a));
}
protected static String wrapInApplyIf(String function, String applyIf) {
return " function() {return ( ((" + applyIf + ").apply(this)) ? ((" + function + ").apply(this)) : true ) }";
}
protected boolean isDelegateAnnotations() {
return false;
}
protected Annotation[] getDelegateAnnotations(Annotation a, String fieldName) {
throw new UnsupportedOperationException("This class does not support delegate annotations");
}
protected static String valangToJS(String text) {
if (text == null || text.trim().length() == 0) {
logger.debug("No text to parse");
return null;
}
ValangParser parser = new ValangParser(text, null, null); // text -> predicate tree
ValangJavaScriptTranslator translator = new ValangJavaScriptTranslator(); // pt -> js
StringWriter sw = new StringWriter();
Predicate pred;
try {
pred = parser.parseExpression();
translator.writeJavaScriptPredicate(sw, pred);
} catch (ParseException e) {
logger.debug("Valang failed parsing.");
return null;
} catch (IOException e) {
logger.debug("Valang failed parsing.");
return null;
}
return sw.toString();
}
/**
* Extracts the names of the validation context in which the valiation rule is applicable. Expects a "contexts"
* attribute to hold an array of context names. If it holds an empty array, <code>null </code> is returned. As the
* contract of {@link AbstractValidationRule#setContextTokens(String[])} defines <code>null</code> means that the
* rule always applies regardless of the context.
*
* @param annotation
* The validation rule annoation.
* @return The names of the validation contexts in which the
*/
protected static String[] extractApplicableContexts(Annotation annotation) {
String[] contexts = (String[]) extractAnnotationAttribute(annotation, CONTEXTS_ATTR);
return (contexts.length > 0) ? contexts : null;
}
protected static Object extractAnnotationAttribute(Annotation annotation, String attributeName) {
try {
return annotation.getClass().getMethod(attributeName).invoke(annotation);
} catch (Exception e) {
throw new IllegalArgumentException("Expecting attribute '" + attributeName + "' in annotation '"
+ annotation.getClass().getName() + "'", e);
}
}
/* Helpers for registering handler */
public Handler(Class... supportedAnnotationTypes) {
this.supportedAnnotationTypes = supportedAnnotationTypes;
}
public boolean supports(Annotation annotation) {
for (Class supportedType : supportedAnnotationTypes) {
if (supportedType.isInstance(annotation)) {
return true;
}
}
return false;
}
}