[ASTERIXDB-2419][COMP] Replacement limit in regexp_replace()
- user model changes: yes
- storage format changes: no
- interface changes: no
Details:
- Support replacement limit parameter in regexp_replace()
Change-Id: I9bcf633ea6af1ade49daffec4784a70355c08a0e
Reviewed-on: https://asterix-gerrit.ics.uci.edu/2797
Tested-by: Jenkins <jenkins@fulliautomatix.ics.uci.edu>
Contrib: Jenkins <jenkins@fulliautomatix.ics.uci.edu>
Integration-Tests: Jenkins <jenkins@fulliautomatix.ics.uci.edu>
Reviewed-by: Till Westmann <tillw@apache.org>
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/regexp_replace/regexp_replace.6.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/regexp_replace/regexp_replace.6.query.sqlpp
new file mode 100644
index 0000000..94deadc
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/regexp_replace/regexp_replace.6.query.sqlpp
@@ -0,0 +1,26 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+{
+ 'result1':regexp_replace('abracadabra','bra','kkk',-1),
+ 'result2':regexp_replace('abracadabra','bra','kkk',0),
+ 'result3':regexp_replace('abracadabra','bra','kkk',1),
+ 'result4':regexp_replace('abracadabra_abracadabra','bra','kkk',2),
+ 'result5':regexp_replace('abracadabra_abracadabra','bra','kkk',5)
+};
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/string/regexp_replace/regexp_replace.6.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/string/regexp_replace/regexp_replace.6.adm
new file mode 100644
index 0000000..6fc838d
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/string/regexp_replace/regexp_replace.6.adm
@@ -0,0 +1 @@
+{ "result1": "abracadabra", "result2": "abracadabra", "result3": "akkkcadabra", "result4": "akkkcadakkk_abracadabra", "result5": "akkkcadakkk_akkkcadakkk" }
\ No newline at end of file
diff --git a/asterixdb/asterix-doc/src/main/markdown/builtins/2_string_common.md b/asterixdb/asterix-doc/src/main/markdown/builtins/2_string_common.md
index 6599dc3..1f9ac19 100644
--- a/asterixdb/asterix-doc/src/main/markdown/builtins/2_string_common.md
+++ b/asterixdb/asterix-doc/src/main/markdown/builtins/2_string_common.md
@@ -329,6 +329,7 @@
* Syntax:
regexp_replace(string, string_pattern, string_replacement[, string_flags])
+ regexp_replace(string, string_pattern, string_replacement[, replacement_limit])
* Checks whether the string `string` matches the given
regular expression pattern `string_pattern` (a Java regular expression pattern),
@@ -339,6 +340,7 @@
* `string_replacement` : a pattern `string` to be used as the replacement,
* `string_flag` : (Optional) a `string` with flags to be used during replace.
* The following modes are enabled with these flags: dotall (s), multiline (m), case_insensitive (i), and comments and whitespace (x).
+ * `replacement_limit`: (Optional) an `integer` specifying the maximum number of replacements to make
* Return Value:
* Returns a `string` that is obtained after the replacements,
* `missing` if any argument is a `missing` value,
diff --git a/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/functions/BuiltinFunctions.java b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/functions/BuiltinFunctions.java
index 8f24864..4afeef1 100644
--- a/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/functions/BuiltinFunctions.java
+++ b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/functions/BuiltinFunctions.java
@@ -1273,7 +1273,8 @@
addFunction(STRING_REGEXP_POSITION, StringInt32TypeComputer.INSTANCE, true);
addFunction(STRING_REGEXP_POSITION_WITH_FLAG, StringInt32TypeComputer.INSTANCE, true);
addFunction(STRING_REGEXP_REPLACE, StringStringTypeComputer.INSTANCE, true);
- addFunction(STRING_REGEXP_REPLACE_WITH_FLAG, StringStringTypeComputer.INSTANCE, true);
+ addFunction(STRING_REGEXP_REPLACE_WITH_FLAG,
+ StringIntToStringTypeComputer.INSTANCE_STRING_REGEXP_REPLACE_WITH_FLAG, true);
addFunction(STRING_REPLACE, StringStringTypeComputer.INSTANCE, true);
addFunction(STRING_REPLACE_WITH_LIMIT, StringIntToStringTypeComputer.INSTANCE_TRIPLE_STRING, true);
addFunction(STRING_REVERSE, StringStringTypeComputer.INSTANCE, true);
diff --git a/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/typecomputer/impl/StringIntToStringTypeComputer.java b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/typecomputer/impl/StringIntToStringTypeComputer.java
index 466ba4d..0f376ba 100644
--- a/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/typecomputer/impl/StringIntToStringTypeComputer.java
+++ b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/typecomputer/impl/StringIntToStringTypeComputer.java
@@ -18,6 +18,9 @@
*/
package org.apache.asterix.om.typecomputer.impl;
+import java.util.EnumSet;
+import java.util.Set;
+
import org.apache.asterix.om.exceptions.TypeMismatchException;
import org.apache.asterix.om.typecomputer.base.AbstractResultTypeComputer;
import org.apache.asterix.om.types.ATypeTag;
@@ -28,40 +31,73 @@
import org.apache.hyracks.api.exceptions.SourceLocation;
public class StringIntToStringTypeComputer extends AbstractResultTypeComputer {
- public static final StringIntToStringTypeComputer INSTANCE = new StringIntToStringTypeComputer(1);
+ public static final StringIntToStringTypeComputer INSTANCE = new StringIntToStringTypeComputer(0, 0, 1, 1);
- public static final StringIntToStringTypeComputer INSTANCE_TRIPLE_STRING = new StringIntToStringTypeComputer(3);
+ public static final StringIntToStringTypeComputer INSTANCE_TRIPLE_STRING =
+ new StringIntToStringTypeComputer(0, 2, 3, 3);
- private final int stringArgCount;
+ public static final StringIntToStringTypeComputer INSTANCE_STRING_REGEXP_REPLACE_WITH_FLAG =
+ new StringIntToStringTypeComputer(0, 3, 3, 3);
- public StringIntToStringTypeComputer(int stringArgCount) {
- this.stringArgCount = stringArgCount;
+ private final int stringArgIdxMin;
+
+ private final int stringArgIdxMax;
+
+ private final int intArgIdxMin;
+
+ private final int intArgIdxMax;
+
+ public StringIntToStringTypeComputer(int stringArgIdxMin, int stringArgIdxMax, int intArgIdxMin, int intArgIdxMax) {
+ this.stringArgIdxMin = stringArgIdxMin;
+ this.stringArgIdxMax = stringArgIdxMax;
+ this.intArgIdxMin = intArgIdxMin;
+ this.intArgIdxMax = intArgIdxMax;
}
@Override
public void checkArgType(String funcName, int argIndex, IAType type, SourceLocation sourceLoc)
throws AlgebricksException {
ATypeTag tag = type.getTypeTag();
- if (argIndex < stringArgCount) {
- if (tag != ATypeTag.STRING) {
- throw new TypeMismatchException(sourceLoc, funcName, argIndex, tag, ATypeTag.STRING);
+ boolean expectedStringType = false;
+ if (stringArgIdxMin <= argIndex && argIndex <= stringArgIdxMax) {
+ if (tag == ATypeTag.STRING) {
+ return;
}
- } else {
+ expectedStringType = true;
+ }
+
+ boolean expectedIntType = false;
+ if (intArgIdxMin <= argIndex && argIndex <= intArgIdxMax) {
switch (tag) {
case TINYINT:
case SMALLINT:
case INTEGER:
case BIGINT:
- break;
- default:
- throw new TypeMismatchException(sourceLoc, funcName, argIndex, tag, ATypeTag.TINYINT,
- ATypeTag.SMALLINT, ATypeTag.INTEGER, ATypeTag.BIGINT);
+ return;
}
+ expectedIntType = true;
}
+
+ throw new TypeMismatchException(sourceLoc, funcName, argIndex, tag,
+ getExpectedTypes(expectedStringType, expectedIntType));
}
@Override
public IAType getResultType(ILogicalExpression expr, IAType... types) throws AlgebricksException {
return BuiltinType.ASTRING;
}
+
+ private ATypeTag[] getExpectedTypes(boolean expectedStringType, boolean expectedIntType) {
+ Set<ATypeTag> expectedTypes = EnumSet.noneOf(ATypeTag.class);
+ if (expectedStringType) {
+ expectedTypes.add(ATypeTag.STRING);
+ }
+ if (expectedIntType) {
+ expectedTypes.add(ATypeTag.TINYINT);
+ expectedTypes.add(ATypeTag.SMALLINT);
+ expectedTypes.add(ATypeTag.INTEGER);
+ expectedTypes.add(ATypeTag.BIGINT);
+ }
+ return expectedTypes.toArray(new ATypeTag[0]);
+ }
}
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/AbstractQuadStringStringEval.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/AbstractQuadStringStringEval.java
index 5de007b..8a66e46 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/AbstractQuadStringStringEval.java
+++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/AbstractQuadStringStringEval.java
@@ -47,16 +47,16 @@
private ArrayBackedValueStorage resultStorage = new ArrayBackedValueStorage();
private DataOutput dout = resultStorage.getDataOutput();
- private IPointable array0 = new VoidPointable();
- private IPointable array1 = new VoidPointable();
- private IPointable array2 = new VoidPointable();
- private IPointable array3 = new VoidPointable();
+ private IPointable ptr0 = new VoidPointable();
+ private IPointable ptr1 = new VoidPointable();
+ private IPointable ptr2 = new VoidPointable();
+ private IPointable ptr3 = new VoidPointable();
private IScalarEvaluator eval0;
private IScalarEvaluator eval1;
private IScalarEvaluator eval2;
private IScalarEvaluator eval3;
- private final FunctionIdentifier funcID;
+ protected final FunctionIdentifier funcID;
protected final SourceLocation sourceLoc;
private AMutableString resultBuffer = new AMutableString("");
@@ -64,10 +64,10 @@
private ISerializerDeserializer strSerde =
SerializerDeserializerProvider.INSTANCE.getSerializerDeserializer(BuiltinType.ASTRING);
- private final UTF8StringPointable strPtr1st = new UTF8StringPointable();
- private final UTF8StringPointable strPtr2nd = new UTF8StringPointable();
- private final UTF8StringPointable strPtr3rd = new UTF8StringPointable();
- private final UTF8StringPointable strPtr4th = new UTF8StringPointable();
+ private final UTF8StringPointable strPtr0 = new UTF8StringPointable();
+ private final UTF8StringPointable strPtr1 = new UTF8StringPointable();
+ private final UTF8StringPointable strPtr2 = new UTF8StringPointable();
+ private final UTF8StringPointable strPtr3 = new UTF8StringPointable();
public AbstractQuadStringStringEval(IHyracksTaskContext context, IScalarEvaluatorFactory eval0,
IScalarEvaluatorFactory eval1, IScalarEvaluatorFactory eval2, IScalarEvaluatorFactory eval3,
@@ -83,48 +83,19 @@
@SuppressWarnings("unchecked")
@Override
public void evaluate(IFrameTupleReference tuple, IPointable result) throws HyracksDataException {
- eval0.evaluate(tuple, array0);
- eval1.evaluate(tuple, array1);
- eval2.evaluate(tuple, array2);
- eval3.evaluate(tuple, array3);
+ eval0.evaluate(tuple, ptr0);
+ eval1.evaluate(tuple, ptr1);
+ eval2.evaluate(tuple, ptr2);
+ eval3.evaluate(tuple, ptr3);
- byte[] bytes0 = array0.getByteArray();
- byte[] bytes1 = array1.getByteArray();
- byte[] bytes2 = array2.getByteArray();
- byte[] bytes3 = array3.getByteArray();
-
- int start0 = array0.getStartOffset();
- int start1 = array1.getStartOffset();
- int start2 = array2.getStartOffset();
- int start3 = array3.getStartOffset();
-
- int len0 = array0.getLength();
- int len1 = array1.getLength();
- int len2 = array2.getLength();
- int len3 = array3.getLength();
+ processArgument(0, ptr0, strPtr0);
+ processArgument(1, ptr1, strPtr1);
+ processArgument(2, ptr2, strPtr2);
+ processArgument(3, ptr3, strPtr3);
resultStorage.reset();
- // Type check.
- if (bytes0[start0] != ATypeTag.SERIALIZED_STRING_TYPE_TAG) {
- throw new TypeMismatchException(sourceLoc, funcID, 0, bytes0[start0], ATypeTag.SERIALIZED_STRING_TYPE_TAG);
- }
- if (bytes1[start1] != ATypeTag.SERIALIZED_STRING_TYPE_TAG) {
- throw new TypeMismatchException(sourceLoc, funcID, 1, bytes1[start1], ATypeTag.SERIALIZED_STRING_TYPE_TAG);
- }
- if (bytes2[start2] != ATypeTag.SERIALIZED_STRING_TYPE_TAG) {
- throw new TypeMismatchException(sourceLoc, funcID, 2, bytes2[start2], ATypeTag.SERIALIZED_STRING_TYPE_TAG);
- }
- if (bytes3[start3] != ATypeTag.SERIALIZED_STRING_TYPE_TAG) {
- throw new TypeMismatchException(sourceLoc, funcID, 3, bytes1[start3], ATypeTag.SERIALIZED_STRING_TYPE_TAG);
- }
-
- strPtr1st.set(bytes0, start0 + 1, len0);
- strPtr2nd.set(bytes1, start1 + 1, len1);
- strPtr3rd.set(bytes2, start2 + 1, len2);
- strPtr4th.set(bytes3, start3 + 1, len3);
-
try {
- String res = compute(strPtr1st, strPtr2nd, strPtr3rd, strPtr4th);
+ String res = compute(strPtr0, strPtr1, strPtr2, strPtr3);
resultBuffer.setValue(res);
strSerde.serialize(resultBuffer, dout);
} catch (IOException e) {
@@ -133,7 +104,19 @@
result.set(resultStorage);
}
+ protected void processArgument(int argIdx, IPointable argPtr, UTF8StringPointable outStrPtr)
+ throws HyracksDataException {
+ byte[] bytes = argPtr.getByteArray();
+ int start = argPtr.getStartOffset();
+ // Type check.
+ if (bytes[start] != ATypeTag.SERIALIZED_STRING_TYPE_TAG) {
+ throw new TypeMismatchException(sourceLoc, funcID, argIdx, bytes[start],
+ ATypeTag.SERIALIZED_STRING_TYPE_TAG);
+ }
+ int len = argPtr.getLength();
+ outStrPtr.set(bytes, start + 1, len);
+ }
+
protected abstract String compute(UTF8StringPointable strPtr1st, UTF8StringPointable strPtr2nd,
UTF8StringPointable strPtr3rd, UTF8StringPointable strPtr4th) throws IOException;
-
}
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/StringRegExpReplaceWithFlagsDescriptor.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/StringRegExpReplaceWithFlagDescriptor.java
similarity index 61%
rename from asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/StringRegExpReplaceWithFlagsDescriptor.java
rename to asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/StringRegExpReplaceWithFlagDescriptor.java
index d901a22..707865c 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/StringRegExpReplaceWithFlagsDescriptor.java
+++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/StringRegExpReplaceWithFlagDescriptor.java
@@ -23,6 +23,8 @@
import org.apache.asterix.om.functions.BuiltinFunctions;
import org.apache.asterix.om.functions.IFunctionDescriptor;
import org.apache.asterix.om.functions.IFunctionDescriptorFactory;
+import org.apache.asterix.om.types.ATypeTag;
+import org.apache.asterix.om.types.hierachy.ATypeHierarchy;
import org.apache.asterix.runtime.evaluators.base.AbstractScalarFunctionDynamicDescriptor;
import org.apache.asterix.runtime.evaluators.functions.utils.RegExpMatcher;
import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
@@ -30,15 +32,16 @@
import org.apache.hyracks.algebricks.runtime.base.IScalarEvaluatorFactory;
import org.apache.hyracks.api.context.IHyracksTaskContext;
import org.apache.hyracks.api.exceptions.HyracksDataException;
+import org.apache.hyracks.data.std.api.IPointable;
import org.apache.hyracks.data.std.primitive.UTF8StringPointable;
-public class StringRegExpReplaceWithFlagsDescriptor extends AbstractScalarFunctionDynamicDescriptor {
+public class StringRegExpReplaceWithFlagDescriptor extends AbstractScalarFunctionDynamicDescriptor {
private static final long serialVersionUID = 1L;
public static final IFunctionDescriptorFactory FACTORY = new IFunctionDescriptorFactory() {
@Override
public IFunctionDescriptor createFunctionDescriptor() {
- return new StringRegExpReplaceWithFlagsDescriptor();
+ return new StringRegExpReplaceWithFlagDescriptor();
}
};
@@ -50,14 +53,40 @@
@Override
public IScalarEvaluator createScalarEvaluator(IHyracksTaskContext ctx) throws HyracksDataException {
return new AbstractQuadStringStringEval(ctx, args[0], args[1], args[2], args[3],
- StringRegExpReplaceWithFlagsDescriptor.this.getIdentifier(), sourceLoc) {
+ StringRegExpReplaceWithFlagDescriptor.this.getIdentifier(), sourceLoc) {
+ private final UTF8StringPointable emptyFlags = UTF8StringPointable.generateUTF8Pointable("");
private final RegExpMatcher matcher = new RegExpMatcher();
+ private int limit;
+
+ @Override
+ protected void processArgument(int argIdx, IPointable argPtr, UTF8StringPointable outStrPtr)
+ throws HyracksDataException {
+ if (argIdx == 3) {
+ byte[] bytes = argPtr.getByteArray();
+ int start = argPtr.getStartOffset();
+ ATypeTag tt = ATypeTag.VALUE_TYPE_MAPPING[bytes[start]];
+ switch (tt) {
+ case TINYINT:
+ case SMALLINT:
+ case INTEGER:
+ case BIGINT:
+ limit = ATypeHierarchy.getIntegerValue(funcID.getName(), argIdx, bytes, start,
+ true);
+ outStrPtr.set(emptyFlags);
+ return;
+ default:
+ limit = Integer.MAX_VALUE;
+ break;
+ }
+ }
+ super.processArgument(argIdx, argPtr, outStrPtr);
+ }
@Override
protected String compute(UTF8StringPointable srcPtr, UTF8StringPointable patternPtr,
UTF8StringPointable replacePtr, UTF8StringPointable flagsPtr) throws IOException {
matcher.build(srcPtr, patternPtr, flagsPtr);
- return matcher.replace(replacePtr);
+ return matcher.replace(replacePtr, limit);
}
};
}
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/utils/RegExpMatcher.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/utils/RegExpMatcher.java
index 8d238df..050eb2e 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/utils/RegExpMatcher.java
+++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/utils/RegExpMatcher.java
@@ -166,6 +166,20 @@
* @return a new string with contained regular expressions replaced.
*/
public String replace(UTF8StringPointable replaceStrPtr) {
+ return replace(replaceStrPtr, Integer.MAX_VALUE);
+ }
+
+ /**
+ * Replaces the appearances of a regular expression defined pattern in a source string with a given
+ * replacement string.
+ *
+ * @param replaceStrPtr
+ * , the string for replacing the regular expression.
+ * @param replaceLimit
+ * , the maximum number of replacements to make
+ * @return a new string with contained regular expressions replaced.
+ */
+ public String replace(UTF8StringPointable replaceStrPtr, int replaceLimit) {
// Sets up a new replacement string if necessary.
final boolean newReplace =
replaceStrPtr != null && (replaceStr == null || lastReplaceStrPtr.compareTo(replaceStrPtr) != 0);
@@ -176,7 +190,7 @@
// Does the actual replacement.
resultBuf.setLength(0);
- while (matcher.find()) {
+ for (int i = 0; i < replaceLimit && matcher.find(); i++) {
matcher.appendReplacement(resultBuf, replaceStr);
}
matcher.appendTail(resultBuf);
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/functions/FunctionCollection.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/functions/FunctionCollection.java
index 04f4a1e..6b0b5d5 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/functions/FunctionCollection.java
+++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/functions/FunctionCollection.java
@@ -257,7 +257,7 @@
import org.apache.asterix.runtime.evaluators.functions.StringRegExpPositionDescriptor;
import org.apache.asterix.runtime.evaluators.functions.StringRegExpPositionWithFlagDescriptor;
import org.apache.asterix.runtime.evaluators.functions.StringRegExpReplaceDescriptor;
-import org.apache.asterix.runtime.evaluators.functions.StringRegExpReplaceWithFlagsDescriptor;
+import org.apache.asterix.runtime.evaluators.functions.StringRegExpReplaceWithFlagDescriptor;
import org.apache.asterix.runtime.evaluators.functions.StringRepeatDescriptor;
import org.apache.asterix.runtime.evaluators.functions.StringReplaceDescriptor;
import org.apache.asterix.runtime.evaluators.functions.StringReplaceWithLimitDescriptor;
@@ -624,7 +624,7 @@
fc.addGenerated(StringRegExpPositionDescriptor.FACTORY);
fc.addGenerated(StringRegExpPositionWithFlagDescriptor.FACTORY);
fc.addGenerated(StringRegExpReplaceDescriptor.FACTORY);
- fc.addGenerated(StringRegExpReplaceWithFlagsDescriptor.FACTORY);
+ fc.addGenerated(StringRegExpReplaceWithFlagDescriptor.FACTORY);
fc.addGenerated(StringInitCapDescriptor.FACTORY);
fc.addGenerated(StringTrimDescriptor.FACTORY);
fc.addGenerated(StringLTrimDescriptor.FACTORY);