All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.bval.jsr.util.LookBehindRegexHolder Maven / Gradle / Ivy

There is a newer version: 10.0.0-M3
Show newest version
/*
 * 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.
 */
package org.apache.bval.jsr.util;

import java.util.function.IntUnaryOperator;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.bval.util.Validate;

/**
 * Utility class to manage regular expressions that require simulated infinite
 * lookbehind, e.g. to determine whether a sequence of escape characters is
 * complete unto itself.
 */
public class LookBehindRegexHolder {
    public static final int DEFAULT_INITIAL_MAXIMUM_LENGTH = 256;
    public static final int DEFAULT_EXPANSION_BLOCK_SIZE = 128;

    private final String regex;
    private final int expansionBlockSize;
    private final IntUnaryOperator computeInjectedRepetition;

    private volatile int maximumLength;
    private Pattern pattern;

    /**
     * Create a new {@link LookBehindRegexHolder} instance with the default
     * initial maximum length and expansion block size.
     * 
     * @param regex
     *            assumed to contain a {@code %d} Java format sequence
     * @param computeInjectedRepetition
     *            function to compute the injected number of repetitions to
     *            inject at {@code %d} in {@code regex}
     */
    public LookBehindRegexHolder(String regex, IntUnaryOperator computeInjectedRepetition) {
        this(regex, DEFAULT_INITIAL_MAXIMUM_LENGTH, DEFAULT_EXPANSION_BLOCK_SIZE, computeInjectedRepetition);
    }

    /**
     * Create a new {@link LookBehindRegexHolder} instance.
     * 
     * @param regex
     *            assumed to contain a {@code %d} Java format sequence
     * @param initialMaximumLength
     *            initial guess
     * @param expansionBlockSize
     *            number of bytes by which to increase the maximum length when a
     *            {@link Matcher} is requested for a larger message size
     * @param computeInjectedRepetition
     *            function to compute the injected number of repetitions to
     *            inject at {@code %d} in {@code regex}
     */
    public LookBehindRegexHolder(String regex, int initialMaximumLength, int expansionBlockSize,
        IntUnaryOperator computeInjectedRepetition) {
        super();
        Validate.isTrue(regex != null && !regex.trim().isEmpty(), "empty regex");
        Validate.isTrue(initialMaximumLength > 0, "invalid initial maximum length %d", initialMaximumLength);
        Validate.isTrue(expansionBlockSize > 0, "Invalid expansion block size %d", expansionBlockSize);
        Validate.notNull(computeInjectedRepetition, "missing %s to compute injected repetition",
            IntUnaryOperator.class.getSimpleName());
        this.regex = regex;
        this.expansionBlockSize = expansionBlockSize;
        this.computeInjectedRepetition = computeInjectedRepetition;
        accommodate(initialMaximumLength);
    }

    /**
     * Get a {@link Matcher} against the specified {@link CharSequence}.
     * 
     * @param s
     * @return {@link Matcher}
     */
    public Matcher matcher(CharSequence s) {
        if (s.length() > maximumLength) {
            accommodate(s.length());
        }
        return pattern.matcher(s);
    }

    int getMaximumLength() {
        return this.maximumLength;
    }

    String getPattern() {
        return pattern.pattern();
    }

    private synchronized void accommodate(int maximumLength) {
        if (this.maximumLength < maximumLength) {
            if (this.maximumLength == 0) {
                this.maximumLength = maximumLength;
            } else {
                int difference = maximumLength - this.maximumLength;
                int addBlocks = difference / expansionBlockSize + 1;
                this.maximumLength += addBlocks * expansionBlockSize;
            }
            this.pattern =
                Pattern.compile(String.format(regex, computeInjectedRepetition.applyAsInt(this.maximumLength)));
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy