package org.bricolages.streaming.filter;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import lombok.*;

class TextOp extends SingleColumnOp {
    static final void register() {
        Op.registerOperator("text", (def) ->
            new TextOp(def, def.mapParameters(Parameters.class))
        );
    }

    @Getter
    @Setter
    static final class Parameters {
        long maxByteLength;
        boolean dropIfOverflow;
        boolean createOverflowFlag;
        String pattern;
    }

    final long maxByteLength;
    final boolean dropIfOverflow;
    final boolean createOverflowFlag;
    final Pattern pattern;

    TextOp(OperatorDefinition def, Parameters params) {
        this(def, params.maxByteLength, params.dropIfOverflow, params.createOverflowFlag, params.pattern);
    }

    TextOp(OperatorDefinition def, long maxByteLength, boolean dropIfOverflow, boolean createOverflowFlag, String pattern) {
        super(def);
        this.maxByteLength = maxByteLength;
        this.dropIfOverflow = dropIfOverflow;
        this.createOverflowFlag = createOverflowFlag;
        this.pattern = (pattern == null) ? null : Pattern.compile(pattern);
    }

    static final Charset DATA_FILE_CHARSET = StandardCharsets.UTF_8;

    @Override
    protected Object applyValue(Object value, Record record) throws FilterException {
        String str = removeAfterNullChar(castStringForce(value));
        if (str == null) return null;
        if (maxByteLength > 0) {
            boolean overflow = (str.getBytes(DATA_FILE_CHARSET).length > maxByteLength);
            if (createOverflowFlag) {
                record.put(overflowFlagName(), overflow);
            }
            if (overflow && dropIfOverflow) return null;
        }
        if (pattern != null) {
            if (!pattern.matcher(str).lookingAt()) return null;
        }
        return str;
    }

    String overflowFlagName() {
        return targetColumnName() + "_overflow";
    }

    String castStringForce(Object value) {
        if (value == null) return null;
        if (!(value instanceof String)) return null;
        return (String)value;
    }

    static final Pattern AFTER_NULL_CHAR_PATTERN = Pattern.compile("([^\\\\]|^)(\\\\\\\\)*(\\\\u0000)");

    String removeAfterNullChar(String str) {
        if (str == null) return null;
        Matcher matcher = AFTER_NULL_CHAR_PATTERN.matcher(str);
        if (matcher.find()) {
            return str.substring(0, matcher.start(3));
        }
        return str;
    }
}

@@ -113,40 +113,8 @@
 ts;%0A
-import java.util.regex.Matcher;%0A
 impo
@@ -1425,20 +1425,16 @@
 fterNull
-Char
 (castStr
@@ -2184,115 +2184,8 @@
   %7D%0A
-%0A    static final Pattern AFTER_NULL_CHAR_PATTERN = Pattern.compile(%22(%5B%5E%5C%5C%5C%5C%5D%7C%5E)(%5C%5C%5C%5C%5C%5C%5C%5C)*(%5C%5C%5C%5Cu0000)%22);%0A%0A
     
@@ -2206,20 +2206,16 @@
 fterNull
-Char
 (String 
@@ -2271,148 +2271,351 @@
     
-Matcher matcher = AFTER_NULL_CHAR_PATTERN.matcher(str);%0A        if (matcher.find()) %7B%0A            return str.substring(0, matcher.start(3));
+int idx = 0;%0A        while (idx %3C str.length()) %7B%0A            idx = str.indexOf('%5C%5C', idx);%0A            if (idx %3C 0) break;%0A            if (str.substring(idx, idx + 6).equals(%22%5C%5Cu0000%22)) %7B%0A                // Drop rest characters%0A                return str.substring(0, idx);%0A            %7D%0A            idx += 2;  // skip any character after '%5C'
 %0A   

Use loop instead of regex