summaryrefslogtreecommitdiffstats
path: root/vespajlib/src/main/java/com/yahoo/collections/IntRange.java
blob: 3c3815589abd9513ea262e8ba5802558fc9b91d6 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
package com.yahoo.collections;

import java.util.Objects;
import java.util.OptionalInt;

/**
 * An integer range.
 *
 * @author bratseth
 */
public class IntRange {

    private static final IntRange empty = new IntRange(OptionalInt.empty(), OptionalInt.empty());

    private final OptionalInt from, to;

    public IntRange(OptionalInt from, OptionalInt to) {
        if (from.isPresent() && to.isPresent() && from.getAsInt() > to.getAsInt())
            throw new IllegalArgumentException("from " + from.getAsInt() + " is greater than to " + to.getAsInt());
        this.from = from;
        this.to = to;
    }

    /** Returns the minimum value which is in this range, or empty if it is open downwards. */
    public OptionalInt from() { return from; }

    /** Returns the maximum value which is in this range, or empty if it is open upwards. */
    public OptionalInt to() { return to; }

    public boolean isEmpty() {
        return from.isEmpty() && to.isEmpty();
    }

    /** Returns whether the given value is in this range. */
    public boolean includes(int value) {
        if (from.isPresent() && value < from.getAsInt()) return false;
        if (to.isPresent() && value > to.getAsInt()) return false;
        return true;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if ( ! (o instanceof IntRange other)) return false;
        if ( ! this.from.equals(other.from)) return false;
        if ( ! this.to.equals(other.to)) return false;
        return true;
    }

    @Override
    public int hashCode() {
        return Objects.hash(from, to);
    }

    @Override
    public String toString() {
        if (isEmpty()) return "[]";
        if (from.equals(to)) return String.valueOf(from.getAsInt());
        return "[" + (from.isPresent() ? from.getAsInt() : "") + ", " + (to.isPresent() ? to.getAsInt() : "") + "]";
    }

    public static IntRange empty() { return empty; }

    public static IntRange from(int from) {
        return new IntRange(OptionalInt.of(from), OptionalInt.empty());
    }

    public static IntRange to(int to) {
        return new IntRange(OptionalInt.empty(), OptionalInt.of(to));
    }

    public static IntRange of(int fromTo) {
        return new IntRange(OptionalInt.of(fromTo), OptionalInt.of(fromTo));
    }

    public static IntRange of(int from, int to) {
        return new IntRange(OptionalInt.of(from), OptionalInt.of(to));
    }

    /** Returns this with a from limit which is at most the given value */
    public IntRange fromAtMost(int minLimit) {
        if (from.isEmpty()) return this;
        if (from.getAsInt() <= minLimit) return this;
        return new IntRange(OptionalInt.of(minLimit), to);
    }

    /** Returns this with a to limit which is at least the given value */
    public IntRange toAtLeast(int maxLimit) {
        if (to.isEmpty()) return this;
        if (to.getAsInt() >= maxLimit) return this;
        return new IntRange(from, OptionalInt.of(maxLimit));
    }

    /** Parses a value ("value"), value range ("[min-value?, max-value?]"), or empty. */
    public static IntRange from(String s) {
        try {
            s = s.trim();
            if (s.startsWith("[") && s.endsWith("]")) {
                String innards = s.substring(1, s.length() - 1).trim();
                if (innards.isEmpty()) return empty();
                String[] numbers = (" " + innards + " ").split(","); // pad to make sure we get 2 elements
                if (numbers.length != 2) throw new IllegalArgumentException("Expected two numbers");
                return new IntRange(parseOptionalInt(numbers[0]), parseOptionalInt(numbers[1]));
            } else {
                var fromTo = parseOptionalInt(s);
                return new IntRange(fromTo, fromTo);
            }
        }
        catch (IllegalArgumentException e) {
            throw new IllegalArgumentException("Expected a number or range on the form [min, max], but got '" + s + "'", e);
        }
    }

    private static OptionalInt parseOptionalInt(String s) {
        s = s.trim();
        if (s.isEmpty()) return OptionalInt.empty();
        return OptionalInt.of(Integer.parseInt(s));
    }

}