aboutsummaryrefslogtreecommitdiffstats
path: root/src/java/org/apache/fop/traits/MinOptMax.java
blob: 84fb0156a68ac5b48e347e0d142e96f8984c5b6b (plain)
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
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
/*
 * 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.
 */

/* $Id$ */

package org.apache.fop.traits;

import java.io.Serializable;

/**
 * This class holds the resolved (as mpoints) form of a
 * {@link org.apache.fop.fo.properties.LengthRangeProperty} or
 * {@link org.apache.fop.fo.properties.SpaceProperty} type property value.
 * <p/>
 * Instances of this class are immutable. All arithmetic methods like {@link #plus(MinOptMax) plus},
 * {@link #minus(MinOptMax) minus} or {@link #mult(int) mult} return a different instance. So it is
 * possible to pass around instances without copying.
 * <p/>
 * <code>MinOptMax</code> values are used during layout calculations.
 */
public final class MinOptMax implements Serializable {

    private static final long serialVersionUID = -4791524475122206142L;

    /**
     * The zero <code>MinOptMax</code> instance with <code>min == opt == max == 0</code>.
     */
    public static final MinOptMax ZERO = getInstance(0);

    private final int min;
    private final int opt;
    private final int max;

    /**
     * Returns an instance of <code>MinOptMax</code> with the given values.
     *
     * @param min the minimum value
     * @param opt the optimum value
     * @param max the maximum value
     * @return the corresponding instance
     * @throws IllegalArgumentException if <code>min > opt || max < opt</code>.
     */
    public static MinOptMax getInstance(int min, int opt, int max) throws IllegalArgumentException {
        if (min > opt) {
            throw new IllegalArgumentException("min (" + min + ") > opt (" + opt + ")");
        }
        if (max < opt) {
            throw new IllegalArgumentException("max (" + max + ") < opt (" + opt + ")");
        }
        return new MinOptMax(min, opt, max);
    }

    /**
     * Returns an instance of <code>MinOptMax</code> with one fixed value for all three
     * properties (min, opt, max).
     *
     * @param value the value for min, opt and max
     * @return the corresponding instance
     * @see #isStiff()
     */
    public static MinOptMax getInstance(int value) {
        return new MinOptMax(value, value, value);
    }

    // Private constructor without consistency checks
    private MinOptMax(int min, int opt, int max) {
        assert min <= opt && opt <= max;
        this.min = min;
        this.opt = opt;
        this.max = max;
    }

    /**
     * Returns the minimum value of this <code>MinOptMax</code>.
     *
     * @return the minimum value of this <code>MinOptMax</code>.
     */
    public int getMin() {
        return min;
    }

    /**
     * Returns the optimum value of this <code>MinOptMax</code>.
     *
     * @return the optimum value of this <code>MinOptMax</code>.
     */
    public int getOpt() {
        return opt;
    }

    /**
     * Returns the maximum value of this <code>MinOptMax</code>.
     *
     * @return the maximum value of this <code>MinOptMax</code>.
     */
    public int getMax() {
        return max;
    }

    /**
     * Returns the shrinkability of this <code>MinOptMax</code> which is the absolute difference
     * between <code>min</code> and <code>opt</code>.
     *
     * @return the shrinkability of this <code>MinOptMax</code> which is always non-negative.
     */
    public int getShrink() {
        return opt - min;
    }

    /**
     * Returns the stretchability of this <code>MinOptMax</code> which is the absolute difference
     * between <code>opt</code> and <code>max</code>.
     *
     * @return the stretchability of this <code>MinOptMax</code> which is always non-negative.
     */
    public int getStretch() {
        return max - opt;
    }

    /**
     * Returns the sum of this <code>MinOptMax</code> and the given <code>MinOptMax</code>.
     *
     * @param operand the second operand of the sum (the first is this instance itself),
     * @return the sum of this <code>MinOptMax</code> and the given <code>MinOptMax</code>.
     */
    public MinOptMax plus(MinOptMax operand) {
        return new MinOptMax(min + operand.min, opt + operand.opt, max + operand.max);
    }


    /**
     * Adds the given value to all three components of this instance and returns the result.
     *
     * @param value value to add to the min, opt, max components
     * @return the result of the addition
     */
    public MinOptMax plus(int value) {
        return new MinOptMax(min + value, opt + value, max + value);
    }

    /**
     * Returns the difference of this <code>MinOptMax</code> and the given
     * <code>MinOptMax</code>. This instance must be a compound of the operand and another
     * <code>MinOptMax</code>, that is, there must exist a <code>MinOptMax</code> <i>m</i>
     * such that <code>this.equals(m.plus(operand))</code>. In other words, the operand
     * must have less shrink and stretch than this instance.
     *
     * @param operand the value to be subtracted
     * @return the difference of this <code>MinOptMax</code> and the given
     * <code>MinOptMax</code>.
     * @throws ArithmeticException if this instance has strictly less shrink or stretch
     * than the operand
     */
    public MinOptMax minus(MinOptMax operand) throws ArithmeticException {
        checkCompatibility(getShrink(), operand.getShrink(), "shrink");
        checkCompatibility(getStretch(), operand.getStretch(), "stretch");
        return new MinOptMax(min - operand.min, opt - operand.opt, max - operand.max);
    }

    private void checkCompatibility(int thisElasticity, int operandElasticity, String msge) {
        if (thisElasticity < operandElasticity) {
            throw new ArithmeticException(
                    "Cannot subtract a MinOptMax from another MinOptMax that has less " + msge
                            + " (" + thisElasticity + " < " + operandElasticity + ")");
        }
    }

    /**
     * Subtracts the given value from all three components of this instance and returns the result.
     *
     * @param value value to subtract from the min, opt, max components
     * @return the result of the subtraction
     */
    public MinOptMax minus(int value) {
        return new MinOptMax(min - value, opt - value, max - value);
    }

    /**
     * Do not use, backwards compatibility only. Returns an instance with the
     * given value added to the minimal value.
     *
     * @param minOperand the minimal value to be added.
     * @return an instance with the given value added to the minimal value.
     * @throws IllegalArgumentException if
     * <code>min + minOperand > opt || max < opt</code>.
     */
    public MinOptMax plusMin(int minOperand) throws IllegalArgumentException {
        return getInstance(min + minOperand, opt, max);
    }

    /**
     * Do not use, backwards compatibility only. Returns an instance with the
     * given value subtracted to the minimal value.
     *
     * @param minOperand the minimal value to be subtracted.
     * @return an instance with the given value subtracted to the minimal value.
     * @throws IllegalArgumentException if
     * <code>min - minOperand > opt || max < opt</code>.
     */
    public MinOptMax minusMin(int minOperand) throws IllegalArgumentException {
        return getInstance(min - minOperand, opt, max);
    }

    /**
     * Do not use, backwards compatibility only. Returns an instance with the
     * given value added to the maximal value.
     *
     * @param maxOperand the maximal value to be added.
     * @return an instance with the given value added to the maximal value.
     * @throws IllegalArgumentException if
     * <code>min > opt || max < opt + maxOperand</code>.
     */
    public MinOptMax plusMax(int maxOperand) throws IllegalArgumentException {
        return getInstance(min, opt, max + maxOperand);
    }

    /**
     * Do not use, backwards compatibility only. Returns an instance with the
     * given value subtracted to the maximal value.
     *
     * @param maxOperand the maximal value to be subtracted.
     * @return an instance with the given value subtracted to the maximal value.
     * @throws IllegalArgumentException if
     * <code>min > opt || max < opt - maxOperand</code>.
     */
    public MinOptMax minusMax(int maxOperand) throws IllegalArgumentException {
        return getInstance(min, opt, max - maxOperand);
    }

    /**
     * Returns the product of this <code>MinOptMax</code> and the given factor.
     *
     * @param factor the factor
     * @return the product of this <code>MinOptMax</code> and the given factor
     * @throws IllegalArgumentException if the factor is negative
     */
    public MinOptMax mult(int factor) throws IllegalArgumentException {
        if (factor < 0) {
            throw new IllegalArgumentException("factor < 0; was: " + factor);
        } else if (factor == 1) {
            return this;
        } else {
            return getInstance(min * factor, opt * factor, max * factor);
        }
    }

    /**
     * Determines whether this <code>MinOptMax</code> represents a non-zero dimension, which means
     * that not all values (min, opt, max) are zero.
     *
     * @return <code>true</code> if this <code>MinOptMax</code> represents a non-zero dimension;
     *         <code>false</code> otherwise.
     */
    public boolean isNonZero() {
        return min != 0 || max != 0;
    }

    /**
     * Determines whether this <code>MinOptMax</code> doesn't allow for shrinking or stretching,
     * which means that all values (min, opt, max) are the same.
     *
     * @return <code>true</code> if whether this <code>MinOptMax</code> doesn't allow for shrinking
     *         or stretching; <code>false</code> otherwise.
     * @see #isElastic()
     */
    public boolean isStiff() {
        return min == max;
    }

    /**
     * Determines whether this <code>MinOptMax</code> allows for shrinking or stretching, which
     * means that at least one of the min or max values isn't equal to the opt value.
     *
     * @return <code>true</code> if this <code>MinOptMax</code> allows for shrinking or stretching;
     *         <code>false</code> otherwise.
     * @see #isStiff()
     */
    public boolean isElastic() {
        return min != opt || opt != max;
    }

    /**
     * Extends the minimum length to the given length if necessary, and adjusts opt and max
     * accordingly.
     *
     * @param newMin the new minimum length
     * @return a <code>MinOptMax</code> instance with the minimum length extended
     */
    public MinOptMax extendMinimum(int newMin) {
        if (min < newMin) {
            int newOpt = Math.max(newMin, opt);
            int newMax = Math.max(newOpt, max);
            return getInstance(newMin, newOpt, newMax);
        } else {
            return this;
        }
    }

    /**
     * {@inheritDoc}
     */
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null || getClass() != obj.getClass()) {
            return false;
        }

        MinOptMax minOptMax = (MinOptMax) obj;

        return opt == minOptMax.opt && max == minOptMax.max && min == minOptMax.min;
    }

    /**
     * {@inheritDoc}
     */
    public int hashCode() {
        int result = min;
        result = 31 * result + opt;
        result = 31 * result + max;
        return result;
    }

    /**
     * {@inheritDoc}
     */
    public String toString() {
        return "MinOptMax[min = " + min + ", opt = " + opt + ", max = " + max + "]";
    }
}