aboutsummaryrefslogtreecommitdiffstats
path: root/config-provisioning/src/main/java/com/yahoo/config/provision/IntRange.java
blob: daf51865e95f0afbd4731762bcfaad20ab8558be (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
121
122
123
124
125
126
127
128
129
130
131
132
133
134
// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.config.provision;

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; }

    /** Returns true if both from and to is open (not present). */
    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;
    }

    /** Returns the given value adjusted minimally to fit within this range. */
    public int fit(int value) {
        if (from.isPresent() && value < from.getAsInt()) return from.getAsInt();
        if (to.isPresent() && value > to.getAsInt()) return to.getAsInt();
        return value;
    }

    @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) {
        try {
            s = s.trim();
            if (s.isEmpty()) return OptionalInt.empty();
            return OptionalInt.of(Integer.parseInt(s));
        }
        catch (IllegalArgumentException e) {
            throw new IllegalArgumentException("'" + s + "' is not an integer");
        }
    }

}