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