From 9cd061eb5ec604b55973c91b04c1dff80201e74f Mon Sep 17 00:00:00 2001 From: wenshao Date: Sat, 26 Jan 2013 00:31:20 +0800 Subject: [PATCH] SQL Eval support like & not like --- .../sql/visitor/SQLEvalVisitorUtils.java | 280 +++++++++++------- .../druid/bvt/sql/eval/EvalLikeTest.java | 19 ++ .../alibaba/druid/bvt/sql/eval/LikeTest.java | 58 ++++ 3 files changed, 258 insertions(+), 99 deletions(-) create mode 100644 src/test/java/com/alibaba/druid/bvt/sql/eval/EvalLikeTest.java create mode 100644 src/test/java/com/alibaba/druid/bvt/sql/eval/LikeTest.java diff --git a/src/main/java/com/alibaba/druid/sql/visitor/SQLEvalVisitorUtils.java b/src/main/java/com/alibaba/druid/sql/visitor/SQLEvalVisitorUtils.java index e75dcc5e70..34d1442384 100644 --- a/src/main/java/com/alibaba/druid/sql/visitor/SQLEvalVisitorUtils.java +++ b/src/main/java/com/alibaba/druid/sql/visitor/SQLEvalVisitorUtils.java @@ -189,7 +189,7 @@ public class SQLEvalVisitorUtils { } String strValue = param0Value.toString(); - int intValue = _int(param1Value); + int intValue = castToInteger(param1Value); String result = strValue.substring(0, intValue); @@ -211,7 +211,7 @@ public class SQLEvalVisitorUtils { } String strValue = param0Value.toString(); - int intValue = _int(param1Value); + int intValue = castToInteger(param1Value); String result = strValue.substring(strValue.length() - intValue, strValue.length()); @@ -326,8 +326,8 @@ public class SQLEvalVisitorUtils { return false; } - int intValue0 = _int(param0Value); - int intValue1 = _int(param1Value); + int intValue0 = castToInteger(param0Value); + int intValue1 = castToInteger(param1Value); int result = intValue0 % intValue1; @@ -351,7 +351,7 @@ public class SQLEvalVisitorUtils { } else if (paramValue instanceof Long) { result = Math.abs(((Long) paramValue).longValue()); } else { - result = _decimal(paramValue).abs(); + result = castToDecimal(paramValue).abs(); } x.putAttribute(EVAL_VALUE, result); @@ -368,7 +368,7 @@ public class SQLEvalVisitorUtils { return false; } - double doubleValue = _double(paramValue); + double doubleValue = castToDouble(paramValue); double result = Math.acos(doubleValue); if (Double.isNaN(result)) { @@ -389,7 +389,7 @@ public class SQLEvalVisitorUtils { return false; } - double doubleValue = _double(paramValue); + double doubleValue = castToDouble(paramValue); double result = Math.asin(doubleValue); if (Double.isNaN(result)) { @@ -410,7 +410,7 @@ public class SQLEvalVisitorUtils { return false; } - double doubleValue = _double(paramValue); + double doubleValue = castToDouble(paramValue); double result = Math.atan(doubleValue); if (Double.isNaN(result)) { @@ -434,8 +434,8 @@ public class SQLEvalVisitorUtils { return false; } - double doubleValue0 = _double(param0Value); - double doubleValue1 = _double(param1Value); + double doubleValue0 = castToDouble(param0Value); + double doubleValue1 = castToDouble(param1Value); double result = Math.atan2(doubleValue0, doubleValue1); if (Double.isNaN(result)) { @@ -456,7 +456,7 @@ public class SQLEvalVisitorUtils { return false; } - double doubleValue = _double(paramValue); + double doubleValue = castToDouble(paramValue); int result = (int) Math.ceil(doubleValue); if (Double.isNaN(result)) { @@ -477,7 +477,7 @@ public class SQLEvalVisitorUtils { return false; } - double doubleValue = _double(paramValue); + double doubleValue = castToDouble(paramValue); int result = (int) Math.cos(doubleValue); if (Double.isNaN(result)) { @@ -498,7 +498,7 @@ public class SQLEvalVisitorUtils { return false; } - double doubleValue = _double(paramValue); + double doubleValue = castToDouble(paramValue); int result = (int) Math.sin(doubleValue); if (Double.isNaN(result)) { @@ -519,7 +519,7 @@ public class SQLEvalVisitorUtils { return false; } - double doubleValue = _double(paramValue); + double doubleValue = castToDouble(paramValue); int result = (int) Math.log(doubleValue); if (Double.isNaN(result)) { @@ -540,7 +540,7 @@ public class SQLEvalVisitorUtils { return false; } - double doubleValue = _double(paramValue); + double doubleValue = castToDouble(paramValue); int result = (int) Math.log10(doubleValue); if (Double.isNaN(result)) { @@ -561,7 +561,7 @@ public class SQLEvalVisitorUtils { return false; } - double doubleValue = _double(paramValue); + double doubleValue = castToDouble(paramValue); int result = (int) Math.tan(doubleValue); if (Double.isNaN(result)) { @@ -582,7 +582,7 @@ public class SQLEvalVisitorUtils { return false; } - double doubleValue = _double(paramValue); + double doubleValue = castToDouble(paramValue); int result = (int) Math.sqrt(doubleValue); if (Double.isNaN(result)) { @@ -606,8 +606,8 @@ public class SQLEvalVisitorUtils { return false; } - double doubleValue0 = _double(param0Value); - double doubleValue1 = _double(param1Value); + double doubleValue0 = castToDouble(param0Value); + double doubleValue1 = castToDouble(param1Value); double result = Math.pow(doubleValue0, doubleValue1); if (Double.isNaN(result)) { @@ -678,7 +678,7 @@ public class SQLEvalVisitorUtils { if (!x.getValueExpr().getAttributes().containsKey(EVAL_VALUE)) { return false; } - + value = x.getValueExpr().getAttribute(EVAL_VALUE); } else { value = null; @@ -837,18 +837,31 @@ public class SQLEvalVisitorUtils { break; case RegExp: case RLike: { - String pattern = _string(right.getAttributes().get(EVAL_VALUE)); - String input = _string(left.getAttributes().get(EVAL_VALUE)); + String pattern = castToString(right.getAttributes().get(EVAL_VALUE)); + String input = castToString(left.getAttributes().get(EVAL_VALUE)); boolean matchResult = Pattern.matches(pattern, input); x.putAttribute(EVAL_VALUE, matchResult); } break; case NotRegExp: case NotRLike: { - String pattern = _string(right.getAttributes().get(EVAL_VALUE)); - String input = _string(left.getAttributes().get(EVAL_VALUE)); + String pattern = castToString(right.getAttributes().get(EVAL_VALUE)); + String input = castToString(left.getAttributes().get(EVAL_VALUE)); boolean matchResult = !Pattern.matches(pattern, input); x.putAttribute(EVAL_VALUE, matchResult); + } + case Like: { + String pattern = castToString(right.getAttributes().get(EVAL_VALUE)); + String input = castToString(left.getAttributes().get(EVAL_VALUE)); + boolean matchResult = like(input, pattern); + x.putAttribute(EVAL_VALUE, matchResult); + } + break; + case NotLike: { + String pattern = castToString(right.getAttributes().get(EVAL_VALUE)); + String input = castToString(left.getAttributes().get(EVAL_VALUE)); + boolean matchResult = !like(input, pattern); + x.putAttribute(EVAL_VALUE, matchResult); } break; default: @@ -883,7 +896,7 @@ public class SQLEvalVisitorUtils { return false; } - public static Boolean _bool(Object val) { + public static Boolean castToBoolean(Object val) { if (val == null) { return null; } @@ -899,7 +912,7 @@ public class SQLEvalVisitorUtils { throw new IllegalArgumentException(); } - public static String _string(Object val) { + public static String castToString(Object val) { Object value = val; if (value == null) { @@ -909,7 +922,7 @@ public class SQLEvalVisitorUtils { return value.toString(); } - public static Byte _byte(Object val) { + public static Byte castToByte(Object val) { if (val == null) { return null; } @@ -921,7 +934,7 @@ public class SQLEvalVisitorUtils { return ((Number) val).byteValue(); } - public static Short _short(Object val) { + public static Short castToShort(Object val) { if (val == null) { return null; } @@ -933,7 +946,7 @@ public class SQLEvalVisitorUtils { return ((Number) val).shortValue(); } - public static Integer _int(Object val) { + public static Integer castToInteger(Object val) { if (val == null) { return null; } @@ -945,7 +958,7 @@ public class SQLEvalVisitorUtils { return ((Number) val).intValue(); } - public static Long _long(Object val) { + public static Long castToLong(Object val) { if (val == null) { return null; } @@ -957,7 +970,7 @@ public class SQLEvalVisitorUtils { return ((Number) val).longValue(); } - public static Float _float(Object val) { + public static Float castToFloat(Object val) { if (val == null) { return null; } @@ -969,7 +982,7 @@ public class SQLEvalVisitorUtils { return ((Number) val).floatValue(); } - public static Double _double(Object val) { + public static Double castToDouble(Object val) { if (val == null) { return null; } @@ -981,7 +994,7 @@ public class SQLEvalVisitorUtils { return ((Number) val).doubleValue(); } - public static BigInteger _bigInt(Object val) { + public static BigInteger castToBigInteger(Object val) { if (val == null) { return null; } @@ -997,7 +1010,7 @@ public class SQLEvalVisitorUtils { return BigInteger.valueOf(((Number) val).longValue()); } - public static Date _date(Object val) { + public static Date castToDate(Object val) { if (val == null) { return null; } @@ -1011,13 +1024,13 @@ public class SQLEvalVisitorUtils { } if (val instanceof String) { - return _date((String) val); + return castToDate((String) val); } throw new DruidRuntimeException("can cast to date"); } - public static Date _date(String text) { + public static Date castToDate(String text) { if (text == null || text.length() == 0) { return null; } @@ -1037,7 +1050,7 @@ public class SQLEvalVisitorUtils { } } - public static BigDecimal _decimal(Object val) { + public static BigDecimal castToDecimal(Object val) { if (val == null) { return null; } @@ -1061,7 +1074,7 @@ public class SQLEvalVisitorUtils { return BigDecimal.valueOf(((Number) val).longValue()); } - public static Object _sum(Object a, Object b) { + public static Object sum(Object a, Object b) { if (a == null) { return b; } @@ -1071,27 +1084,27 @@ public class SQLEvalVisitorUtils { } if (a instanceof BigDecimal || b instanceof BigDecimal) { - return _decimal(a).add(_decimal(b)); + return castToDecimal(a).add(castToDecimal(b)); } if (a instanceof BigInteger || b instanceof BigInteger) { - return _bigInt(a).add(_bigInt(b)); + return castToBigInteger(a).add(castToBigInteger(b)); } if (a instanceof Long || b instanceof Long) { - return _long(a) + _long(b); + return castToLong(a) + castToLong(b); } if (a instanceof Integer || b instanceof Integer) { - return _int(a) + _int(b); + return castToInteger(a) + castToInteger(b); } if (a instanceof Short || b instanceof Short) { - return _short(a) + _short(b); + return castToShort(a) + castToShort(b); } if (a instanceof Byte || b instanceof Byte) { - return _byte(a) + _byte(b); + return castToByte(a) + castToByte(b); } throw new IllegalArgumentException(); @@ -1103,27 +1116,27 @@ public class SQLEvalVisitorUtils { } if (a instanceof BigDecimal || b instanceof BigDecimal) { - return _decimal(a).divide(_decimal(b)); + return castToDecimal(a).divide(castToDecimal(b)); } if (a instanceof BigInteger || b instanceof BigInteger) { - return _bigInt(a).divide(_bigInt(b)); + return castToBigInteger(a).divide(castToBigInteger(b)); } if (a instanceof Long || b instanceof Long) { - return _long(a) / _long(b); + return castToLong(a) / castToLong(b); } if (a instanceof Integer || b instanceof Integer) { - return _int(a) / _int(b); + return castToInteger(a) / castToInteger(b); } if (a instanceof Short || b instanceof Short) { - return _short(a) / _short(b); + return castToShort(a) / castToShort(b); } if (a instanceof Byte || b instanceof Byte) { - return _byte(a) / _byte(b); + return castToByte(a) / castToByte(b); } throw new IllegalArgumentException(); @@ -1139,32 +1152,32 @@ public class SQLEvalVisitorUtils { } if (a instanceof BigDecimal || b instanceof BigDecimal) { - return _decimal(a).compareTo(_decimal(b)) > 0; + return castToDecimal(a).compareTo(castToDecimal(b)) > 0; } if (a instanceof BigInteger || b instanceof BigInteger) { - return _bigInt(a).compareTo(_bigInt(b)) > 0; + return castToBigInteger(a).compareTo(castToBigInteger(b)) > 0; } if (a instanceof Long || b instanceof Long) { - return _long(a) > _long(b); + return castToLong(a) > castToLong(b); } if (a instanceof Integer || b instanceof Integer) { - return _int(a) > _int(b); + return castToInteger(a) > castToInteger(b); } if (a instanceof Short || b instanceof Short) { - return _short(a) > _short(b); + return castToShort(a) > castToShort(b); } if (a instanceof Byte || b instanceof Byte) { - return _byte(a) > _byte(b); + return castToByte(a) > castToByte(b); } if (a instanceof Date || b instanceof Date) { - Date d1 = _date(a); - Date d2 = _date(b); + Date d1 = castToDate(a); + Date d2 = castToDate(b); if (d1 == d2) { return false; @@ -1202,32 +1215,32 @@ public class SQLEvalVisitorUtils { } if (a instanceof BigDecimal || b instanceof BigDecimal) { - return _decimal(a).compareTo(_decimal(b)) < 0; + return castToDecimal(a).compareTo(castToDecimal(b)) < 0; } if (a instanceof BigInteger || b instanceof BigInteger) { - return _bigInt(a).compareTo(_bigInt(b)) < 0; + return castToBigInteger(a).compareTo(castToBigInteger(b)) < 0; } if (a instanceof Long || b instanceof Long) { - return _long(a) < _long(b); + return castToLong(a) < castToLong(b); } if (a instanceof Integer || b instanceof Integer) { - return _int(a) < _int(b); + return castToInteger(a) < castToInteger(b); } if (a instanceof Short || b instanceof Short) { - return _short(a) < _short(b); + return castToShort(a) < castToShort(b); } if (a instanceof Byte || b instanceof Byte) { - return _byte(a) < _byte(b); + return castToByte(a) < castToByte(b); } if (a instanceof Date || b instanceof Date) { - Date d1 = _date(a); - Date d2 = _date(b); + Date d1 = castToDate(a); + Date d2 = castToDate(b); if (d1 == d2) { return false; @@ -1269,32 +1282,32 @@ public class SQLEvalVisitorUtils { } if (a instanceof BigDecimal || b instanceof BigDecimal) { - return _decimal(a).compareTo(_decimal(b)) == 0; + return castToDecimal(a).compareTo(castToDecimal(b)) == 0; } if (a instanceof BigInteger || b instanceof BigInteger) { - return _bigInt(a).compareTo(_bigInt(b)) == 0; + return castToBigInteger(a).compareTo(castToBigInteger(b)) == 0; } if (a instanceof Long || b instanceof Long) { - return _long(a) == _long(b); + return castToLong(a) == castToLong(b); } if (a instanceof Integer || b instanceof Integer) { - return _int(a) == _int(b); + return castToInteger(a) == castToInteger(b); } if (a instanceof Short || b instanceof Short) { - return _short(a) == _short(b); + return castToShort(a) == castToShort(b); } if (a instanceof Byte || b instanceof Byte) { - return _byte(a) == _byte(b); + return castToByte(a) == castToByte(b); } if (a instanceof Date || b instanceof Date) { - Date d1 = _date(a); - Date d2 = _date(b); + Date d1 = castToDate(a); + Date d2 = castToDate(b); if (d1 == d2) { return true; @@ -1306,9 +1319,9 @@ public class SQLEvalVisitorUtils { return d1.equals(d2); } - + if (a instanceof String || b instanceof String) { - return _string(a).equals(_string(b)); + return castToString(a).equals(castToString(b)); } throw new IllegalArgumentException(); @@ -1324,39 +1337,39 @@ public class SQLEvalVisitorUtils { } if (a instanceof BigDecimal || b instanceof BigDecimal) { - return _decimal(a).add(_decimal(b)); + return castToDecimal(a).add(castToDecimal(b)); } if (a instanceof BigInteger || b instanceof BigInteger) { - return _bigInt(a).add(_bigInt(b)); + return castToBigInteger(a).add(castToBigInteger(b)); } if (a instanceof Double || b instanceof Double) { - return _double(a) + _double(b); + return castToDouble(a) + castToDouble(b); } if (a instanceof Float || b instanceof Float) { - return _float(a) + _float(b); + return castToFloat(a) + castToFloat(b); } if (a instanceof Long || b instanceof Long) { - return _long(a) + _long(b); + return castToLong(a) + castToLong(b); } if (a instanceof Integer || b instanceof Integer) { - return _int(a) + _int(b); + return castToInteger(a) + castToInteger(b); } if (a instanceof Short || b instanceof Short) { - return _short(a) + _short(b); + return castToShort(a) + castToShort(b); } if (a instanceof Byte || b instanceof Byte) { - return _byte(a) + _byte(b); + return castToByte(a) + castToByte(b); } if (a instanceof String || b instanceof String) { - return _string(a) + _string(b); + return castToString(a) + castToString(b); } throw new IllegalArgumentException(); @@ -1372,35 +1385,35 @@ public class SQLEvalVisitorUtils { } if (a instanceof BigDecimal || b instanceof BigDecimal) { - return _decimal(a).subtract(_decimal(b)); + return castToDecimal(a).subtract(castToDecimal(b)); } if (a instanceof BigInteger || b instanceof BigInteger) { - return _bigInt(a).subtract(_bigInt(b)); + return castToBigInteger(a).subtract(castToBigInteger(b)); } if (a instanceof Double || b instanceof Double) { - return _double(a) - _double(b); + return castToDouble(a) - castToDouble(b); } if (a instanceof Float || b instanceof Float) { - return _float(a) - _float(b); + return castToFloat(a) - castToFloat(b); } if (a instanceof Long || b instanceof Long) { - return _long(a) - _long(b); + return castToLong(a) - castToLong(b); } if (a instanceof Integer || b instanceof Integer) { - return _int(a) - _int(b); + return castToInteger(a) - castToInteger(b); } if (a instanceof Short || b instanceof Short) { - return _short(a) - _short(b); + return castToShort(a) - castToShort(b); } if (a instanceof Byte || b instanceof Byte) { - return _byte(a) - _byte(b); + return castToByte(a) - castToByte(b); } throw new IllegalArgumentException(); @@ -1412,29 +1425,98 @@ public class SQLEvalVisitorUtils { } if (a instanceof BigDecimal || b instanceof BigDecimal) { - return _decimal(a).multiply(_decimal(b)); + return castToDecimal(a).multiply(castToDecimal(b)); } if (a instanceof BigInteger || b instanceof BigInteger) { - return _bigInt(a).multiply(_bigInt(b)); + return castToBigInteger(a).multiply(castToBigInteger(b)); } if (a instanceof Long || b instanceof Long) { - return _long(a) * _long(b); + return castToLong(a) * castToLong(b); } if (a instanceof Integer || b instanceof Integer) { - return _int(a) * _int(b); + return castToInteger(a) * castToInteger(b); } if (a instanceof Short || b instanceof Short) { - return _short(a) * _short(b); + return castToShort(a) * castToShort(b); } if (a instanceof Byte || b instanceof Byte) { - return _byte(a) * _byte(b); + return castToByte(a) * castToByte(b); } throw new IllegalArgumentException(); } + + public static boolean like(String input, String pattern) { + if (pattern == null) { + throw new IllegalArgumentException("pattern is null"); + } + + StringBuilder regexprBuilder = new StringBuilder(pattern.length() + 4); + + final int STAT_NOTSET = 0; + final int STAT_RANGE = 1; + final int STAT_LITERAL = 2; + + int stat = STAT_NOTSET; + + int blockStart = -1; + for (int i = 0; i < pattern.length(); ++i) { + char ch = pattern.charAt(i); + + if (stat == STAT_LITERAL // + && (ch == '%' || ch == '_' || ch == '[')) { + String block = pattern.substring(blockStart, i); + regexprBuilder.append("\\Q"); + regexprBuilder.append(block); + regexprBuilder.append("\\E"); + blockStart = -1; + stat = STAT_NOTSET; + } + + if (ch == '%') { + regexprBuilder.append(".*"); + } else if (ch == '_') { + regexprBuilder.append('.'); + } else if (ch == '[') { + if (stat == STAT_RANGE) { + throw new IllegalArgumentException("illegal pattern : " + pattern); + } + stat = STAT_RANGE; + blockStart = i; + } else if (ch == ']') { + if (stat != STAT_RANGE) { + throw new IllegalArgumentException("illegal pattern : " + pattern); + } + String block = pattern.substring(blockStart, i + 1); + regexprBuilder.append(block); + + blockStart = -1; + } else { + if (stat == STAT_NOTSET) { + stat = STAT_LITERAL; + blockStart = i; + } + + if (stat == STAT_LITERAL && i == pattern.length() - 1) { + String block = pattern.substring(blockStart, i + 1); + regexprBuilder.append("\\Q"); + regexprBuilder.append(block); + regexprBuilder.append("\\E"); + } + } + } + if ("%".equals(pattern) || "%%".equals(pattern)) { + return true; + } + // %xxxx + + String regexpr = regexprBuilder.toString(); + return Pattern.matches(regexpr, input); + } + } diff --git a/src/test/java/com/alibaba/druid/bvt/sql/eval/EvalLikeTest.java b/src/test/java/com/alibaba/druid/bvt/sql/eval/EvalLikeTest.java new file mode 100644 index 0000000000..cfeeee3f63 --- /dev/null +++ b/src/test/java/com/alibaba/druid/bvt/sql/eval/EvalLikeTest.java @@ -0,0 +1,19 @@ +package com.alibaba.druid.bvt.sql.eval; + +import junit.framework.TestCase; + +import org.junit.Assert; + +import com.alibaba.druid.sql.visitor.SQLEvalVisitorUtils; +import com.alibaba.druid.util.JdbcConstants; + + +public class EvalLikeTest extends TestCase { + public void test_like() throws Exception { + Assert.assertEquals(true, SQLEvalVisitorUtils.evalExpr(JdbcConstants.MYSQL, "'a' LIKE '[a-d]'")); + } + + public void test_not_like() throws Exception { + Assert.assertEquals(false, SQLEvalVisitorUtils.evalExpr(JdbcConstants.MYSQL, "'a' NOT LIKE '[a-d]'")); + } +} diff --git a/src/test/java/com/alibaba/druid/bvt/sql/eval/LikeTest.java b/src/test/java/com/alibaba/druid/bvt/sql/eval/LikeTest.java new file mode 100644 index 0000000000..15df766806 --- /dev/null +++ b/src/test/java/com/alibaba/druid/bvt/sql/eval/LikeTest.java @@ -0,0 +1,58 @@ +package com.alibaba.druid.bvt.sql.eval; + +import junit.framework.TestCase; + +import org.junit.Assert; + +import com.alibaba.druid.sql.visitor.SQLEvalVisitorUtils; + +public class LikeTest extends TestCase { + + public void test_any_0() throws Exception { + Assert.assertTrue(SQLEvalVisitorUtils.like("abcdefg", "%")); + } + + public void test_any_1() throws Exception { + Assert.assertTrue(SQLEvalVisitorUtils.like("abcdefg", "%%")); + } + + public void test_any_2() throws Exception { + Assert.assertTrue(SQLEvalVisitorUtils.like("abcdefg", "%b%")); + } + + public void test_any_3() throws Exception { + Assert.assertTrue(SQLEvalVisitorUtils.like("abcdefg", "a%")); + } + + public void test_any_4() throws Exception { + Assert.assertTrue(SQLEvalVisitorUtils.like("abcdefg", "%g")); + } + + public void test_single_0() throws Exception { + Assert.assertTrue(SQLEvalVisitorUtils.like("a", "_")); + } + + public void test_single_1() throws Exception { + Assert.assertTrue(SQLEvalVisitorUtils.like("ab", "__")); + } + + public void test_single_2() throws Exception { + Assert.assertTrue(SQLEvalVisitorUtils.like("abcd", "a__d")); + } + + public void test_single_3() throws Exception { + Assert.assertTrue(SQLEvalVisitorUtils.like("abcdf", "a__d_")); + } + + public void test_range_0() throws Exception { + Assert.assertTrue(SQLEvalVisitorUtils.like("abcdf", "abcd[a-f]")); + } + + public void test_range_1() throws Exception { + Assert.assertFalse(SQLEvalVisitorUtils.like("abcdf", "abcd[a-e]")); + } + + public void test_range_2() throws Exception { + Assert.assertTrue(SQLEvalVisitorUtils.like("ab", "a[abf]")); + } +}