blob: 3e5ad738df086f07ad92ab4d42f80e5478051fea [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.fo.expr;
import org.apache.fop.datatypes.Length;
import org.apache.fop.datatypes.Numeric;
import org.apache.fop.datatypes.PercentBaseContext;
import org.apache.fop.fo.properties.Property;
import org.apache.fop.fo.properties.TableColLength;
import org.apache.fop.util.CompareUtil;
/**
* This class represent a node in a property expression tree.
* It is created when an operation involve relative expression and is used
* to delay evaluation of the operation until the time where getNumericValue()
* or getValue() is called.
*/
public class RelativeNumericProperty extends Property implements Length {
/** ADDITION */
public static final int ADDITION = 1;
/** SUBTRACTION */
public static final int SUBTRACTION = 2;
/** MULTIPLY */
public static final int MULTIPLY = 3;
/** DIVIDE */
public static final int DIVIDE = 4;
/** MODULO */
public static final int MODULO = 5;
/** NEGATE */
public static final int NEGATE = 6;
/** ABS */
public static final int ABS = 7;
/** MAX */
public static final int MAX = 8;
/** MIN */
public static final int MIN = 9;
// Used in the toString() method, indexed by operation id.
private static String operations = " +-*/%";
/**
* The operation identifier.
*/
private int operation;
/**
* The first (or only) operand.
*/
private Numeric op1;
/**
* The second operand.
*/
private Numeric op2;
/**
* The dimension of the result.
*/
private int dimension;
/**
* Constructor for a two argument operation.
* @param operation the operation opcode: ADDITION, SUBTRACTION, ...
* @param op1 the first operand.
* @param op2 the second operand
*/
public RelativeNumericProperty(int operation, Numeric op1, Numeric op2) {
this.operation = operation;
this.op1 = op1;
this.op2 = op2;
// Calculate the dimension. We can do now.
switch (operation) {
case MULTIPLY:
dimension = op1.getDimension() + op2.getDimension();
break;
case DIVIDE:
dimension = op1.getDimension() - op2.getDimension();
break;
default:
dimension = op1.getDimension();
}
}
/**
* Constructor for a one argument operation.
* @param operation the operation opcode: NEGATE, ABS
* @param op the operand.
*/
public RelativeNumericProperty(int operation, Numeric op) {
this.operation = operation;
this.op1 = op;
this.dimension = op.getDimension();
}
/**
* Return a resolved (calculated) Numeric with the value of the expression.
* @param context Evaluation context
* @return the resolved {@link Numeric} corresponding to the value of the expression
* @throws PropertyException when an exception occur during evaluation.
*/
private Numeric getResolved(PercentBaseContext context) throws PropertyException {
switch (operation) {
case ADDITION:
return NumericOp.addition2(op1, op2, context);
case SUBTRACTION:
return NumericOp.subtraction2(op1, op2, context);
case MULTIPLY:
return NumericOp.multiply2(op1, op2, context);
case DIVIDE:
return NumericOp.divide2(op1, op2, context);
case MODULO:
return NumericOp.modulo2(op1, op2, context);
case NEGATE:
return NumericOp.negate2(op1, context);
case ABS:
return NumericOp.abs2(op1, context);
case MAX:
return NumericOp.max2(op1, op2, context);
case MIN:
return NumericOp.min2(op1, op2, context);
default:
throw new PropertyException("Unknown expr operation " + operation);
}
}
/**
* Return the resolved (calculated) value of the expression.
* {@inheritDoc}
*/
public double getNumericValue() {
try {
return getResolved(null).getNumericValue(null);
} catch (PropertyException pe) {
throw new RuntimeException(pe);
}
}
/**
* {@inheritDoc}
*/
public double getNumericValue(PercentBaseContext context) {
try {
return getResolved(context).getNumericValue(context);
} catch (PropertyException pe) {
throw new RuntimeException(pe);
}
}
/**
* Return the dimension of the expression
* @return numeric value as dimension
*/
public int getDimension() {
return dimension;
}
/**
* Return false since an expression is only created when there is relative
* numerics involved.
* @return true if expression is absolute
*/
public boolean isAbsolute() {
return false;
}
/**
* Cast this numeric as a Length.
* @return numeric value as length
*/
@Override
public Length getLength() {
if (dimension == 1) {
return this;
}
log.error("Can't create length with dimension " + dimension);
return null;
}
/** @return numeric value */
@Override
public Numeric getNumeric() {
return this;
}
/**
* {@inheritDoc}
*/
public int getValue() {
return (int) getNumericValue();
}
/**
* {@inheritDoc}
*/
public int getValue(PercentBaseContext context) {
return (int) getNumericValue(context);
}
/**
* Return the number of table units which are included in this length
* specification. This will always be 0 unless the property specification
* used the proportional-column-width() function (only on table column FOs).
* <p>
* If this value is not 0, the actual value of the Length cannot be known
* without looking at all of the columns in the table to determine the value
* of a "table-unit".
*
* @return The number of table units which are included in this length
* specification.
*/
public double getTableUnits() {
double tu1 = 0.0;
double tu2 = 0.0;
if (op1 instanceof RelativeNumericProperty) {
tu1 = ((RelativeNumericProperty) op1).getTableUnits();
} else if (op1 instanceof TableColLength) {
tu1 = ((TableColLength) op1).getTableUnits();
}
if (op2 instanceof RelativeNumericProperty) {
tu2 = ((RelativeNumericProperty) op2).getTableUnits();
} else if (op2 instanceof TableColLength) {
tu2 = ((TableColLength) op2).getTableUnits();
}
if (tu1 != 0.0 && tu2 != 0.0) {
switch (operation) {
case ADDITION:
return tu1 + tu2;
case SUBTRACTION:
return tu1 - tu2;
case MULTIPLY:
return tu1 * tu2;
case DIVIDE:
return tu1 / tu2;
case MODULO:
return tu1 % tu2;
case MIN:
return Math.min(tu1, tu2);
case MAX:
return Math.max(tu1, tu2);
default:
assert false;
}
} else if (tu1 != 0.0) {
switch (operation) {
case NEGATE:
return -tu1;
case ABS:
return Math.abs(tu1);
default:
return tu1;
}
} else if (tu2 != 0.0) {
return tu2;
}
return 0.0;
}
/**
* Return a string represention of the expression. Only used for debugging.
* @return the string representation.
*/
@Override
public String toString() {
switch (operation) {
case ADDITION: case SUBTRACTION:
case DIVIDE: case MULTIPLY: case MODULO:
return "(" + op1 + " " + operations.charAt(operation) + op2 + ")";
case NEGATE:
return "-" + op1;
case MAX:
return "max(" + op1 + ", " + op2 + ")";
case MIN:
return "min(" + op1 + ", " + op2 + ")";
case ABS:
return "abs(" + op1 + ")";
default:
return "unknown operation " + operation;
}
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + dimension;
result = prime * result + CompareUtil.getHashCode(op1);
result = prime * result + CompareUtil.getHashCode(op2);
result = prime * result + operation;
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof RelativeNumericProperty)) {
return false;
}
RelativeNumericProperty other = (RelativeNumericProperty) obj;
return dimension == other.dimension
&& CompareUtil.equal(op1, other.op1)
&& CompareUtil.equal(op2, other.op2)
&& operation == other.operation;
}
}