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

software.amazon.event.ruler.Range Maven / Gradle / Ivy

package software.amazon.event.ruler;

import java.nio.charset.StandardCharsets;
import java.util.Arrays;

/**
 * Represents a range of numeric values to match against.
 * "Numeric" means that the character repertoire is "digits"; initially, either 0-9 or 0-9a-f. In the current
 *  implementation, the number of digits in the top and bottom of the range is the same.
 */
public final class Range extends Patterns {
    /**
     * Bottom and top of the range. openBottom true means we're looking for > bottom, false means >=
     *  Similarly, openTop true means we're looking for < top, false means <= top.
     */
    final byte[] bottom;
    final boolean openBottom;
    final byte[] top;
    final boolean openTop;

    final boolean isCIDR;

    private static final int HEX_DIGIT_A_DECIMAL_VALUE = 10;

    private Range(final double bottom, final boolean openBottom, final double top, final boolean openTop) {
        super(MatchType.NUMERIC_RANGE);
        if (bottom >= top) {
            throw new IllegalArgumentException("Bottom must be less than top");
        }
        this.bottom = ComparableNumber.generate(bottom).getBytes(StandardCharsets.UTF_8);
        this.openBottom = openBottom;
        this.top = ComparableNumber.generate(top).getBytes(StandardCharsets.UTF_8);
        this.openTop = openTop;
        isCIDR = false;
    }

    Range(final byte[] bottom, final boolean openBottom, final byte[] top, final boolean openTop, final boolean isCIDR) {
        super(MatchType.NUMERIC_RANGE);
        this.bottom = bottom;
        this.top = top;
        this.openBottom = openBottom;
        this.openTop = openTop;
        this.isCIDR = isCIDR;
    }

    private Range(Range range) {
        super(MatchType.NUMERIC_RANGE);
        this.bottom = range.bottom.clone();
        this.openBottom = range.openBottom;
        this.top = range.top.clone();
        this.openTop = range.openTop;
        this.isCIDR = range.isCIDR;
    }

    public static Range lessThan(final double val) {
        return new Range(-Constants.FIVE_BILLION, false, val, true);
    }

    public static Range lessThanOrEqualTo(final double val) {
        return new Range(-Constants.FIVE_BILLION, false, val, false);
    }

    public static Range greaterThan(final double val) {
        return new Range(val, true, Constants.FIVE_BILLION, false);
    }

    public static Range greaterThanOrEqualTo(final double val) {
        return new Range(val, false, Constants.FIVE_BILLION, false);
    }

    public static Range between(final double bottom, final boolean openBottom, final double top, final boolean openTop) {
        return new Range(bottom, openBottom, top, openTop);
    }

    private static Range deepCopy(final Range range) {
        return new Range(range);
    }

    /**
     * This is necessitated by the fact that we do range comparisons of numbers, fixed-length strings of digits, and
     *  in the case where the numbers represent IP addresses, they are hex digits.  So we need to be able to say
     *  "for all digits between '3' and 'C'". This is for that.
     *
     * @param first Start one digit higher than this, for example '4'
     * @param last Stop one digit lower than this, for example 'B'
     * @return The digit list, for example [ '4', '5', '6', '7', '8', '9', '9', 'A' ] (with 'B' for longDigitSequence)
     */
    static byte[] digitSequence(byte first, byte last, boolean includeFirst, boolean includeLast) {
        assert first <= last && first <= 'F' && first >= '0' && last <= 'F';
        assert !((first == last) && !includeFirst && !includeLast);

        int i = getHexByteIndex(first);
        int j = getHexByteIndex(last);

        if ((!includeFirst) && (i < (Constants.HEX_DIGITS.length - 1))) {
            i++;
        }

        if (includeLast) {
            j++;
        }

        byte[] bytes = new byte[j - i];

        System.arraycopy(Constants.HEX_DIGITS, i, bytes, 0, j - i);

        return bytes;
    }

    private static int getHexByteIndex(byte value) {
        // ['0'-'9'] maps to [0-9] indexes
        if (value >= '0' && value <= '9') {
            return value - '0';
        }
        // ['A'-'F'] maps to [10-15] indexes
        return (value - 'A') + HEX_DIGIT_A_DECIMAL_VALUE;
    }

    @Override
    public Object clone() {
        super.clone();
        return Range.deepCopy(this);
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || !o.getClass().equals(getClass())) {
            return false;
        }
        if (!super.equals(o)) {
            return false;
        }

        Range range = (Range) o;

        return openBottom == range.openBottom &&
               openTop == range.openTop &&
               Arrays.equals(bottom, range.bottom) &&
               Arrays.equals(top, range.top);
    }

    @Override
    public int hashCode() {
        int result = super.hashCode();
        result = 31 * result + Arrays.hashCode(bottom);
        result = 31 * result + Boolean.hashCode(openBottom);
        result = 31 * result + Arrays.hashCode(top);
        result = 31 * result + Boolean.hashCode(openTop);
        return result;
    }

    public String toString() {
        return (new String(bottom, StandardCharsets.UTF_8)) + '/' + (new String(top, StandardCharsets.UTF_8))
                       + ':' + openBottom + '/' + openTop + " (" + super.toString() + ")";
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy