blob: 84fb0156a68ac5b48e347e0d142e96f8984c5b6b [file] [log] [blame]
/*
* 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 + "]";
}
}