package org.jboss.seam.remoting.validation; import java.util.ArrayList; import java.util.HashMap; import java.util.Map; import java.util.regex.Pattern; import javax.validation.constraints.Pattern.Flag; /** * some basic incompatibility between Java and JavaScript Regular Expression syntax is handled here * however since Java regular expression is relatively more powerful than JavaScript counterpart * it is developer's responsibility to make sure the provided Regular Expression is a valid * JS expression as well. * the main features that are not supported by JavaScript regular expression engines are: * <ul> * <li> No Unicode categories or blocks [just unicode characters]</li> * <li> No Character class Union, Intersection or substraction [simply no nesting and no &&] </li> * <li> No Possessive Quantifiers * </ul> * * @author Amir Sadri */ public class RegexpConsideration implements SpecialConsideration { private final static Pattern BACKSLASH_REPLACEMENT = Pattern.compile("\\\\\\\\"); private final static Pattern PERMANENT_START_REPLACEMENT = Pattern.compile("\\\\A"); private final static Pattern PERMANENT_END_REPLACEMENT = Pattern.compile("\\\\Z"); public HashMap<String, Object> reassessParameters(Map<String, Object> constraintParams) { HashMap<String, Object> params = new HashMap<String, Object>(); params.put("regexp", convertRegex((String) constraintParams.get("regexp"))); Flag[] flags = (Flag[]) constraintParams.get("flags"); int[] values = new int[flags.length]; for (int i = 0; i < values.length; i++) values[i] = flags[i].getValue(); ArrayList<String> changedFlags = convertRegexFlags(values); if (changedFlags.size() > 0) params.put("flags", changedFlags); return params; } public String reassessConstraintName(String constraintName) { return constraintName; } public final static String convertRegex(String regex) { regex = BACKSLASH_REPLACEMENT.matcher(regex).replaceAll("\\"); //////// JavaScript doesn't support \A and \Z anchors, so we just replace them with '^' and '$' //////// although this strategy could easily get broken when multi-line flag is on regex = PERMANENT_START_REPLACEMENT.matcher(regex).replaceAll("^"); regex = PERMANENT_END_REPLACEMENT.matcher(regex).replaceAll("$"); ///// Matcher.matches()[which is what I am assuming we are all used to] ///// treats Regexps a little different than JS test() method does, so here I just try to make sure ///// that we are in the same page in both client and server side if (regex.charAt(0) != '^') regex = "^" + regex; if (regex.charAt(regex.length() - 1) != '$') regex += "$"; return regex; } public final static ArrayList<String> convertRegexFlags(int[] flags) { ArrayList<String> jsFlags = new ArrayList<String>(); for (int flag : flags) { switch (flag) { case java.util.regex.Pattern.CASE_INSENSITIVE: jsFlags.add("i"); break; case java.util.regex.Pattern.MULTILINE: jsFlags.add("m"); break; } } return jsFlags; } }