123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453 |
- /*
- * $Id$
- * Copyright (C) 2001-2002 The Apache Software Foundation. All rights reserved.
- * For details on use and redistribution please refer to the
- * LICENSE file included with these sources.
- */
-
- package org.apache.fop.fo.expr;
-
- import org.apache.fop.fo.Property;
- import org.apache.fop.fo.ListProperty;
- import org.apache.fop.fo.LengthProperty;
- import org.apache.fop.fo.NumberProperty;
- import org.apache.fop.fo.StringProperty;
- import org.apache.fop.fo.ColorTypeProperty;
- import org.apache.fop.datatypes.*;
-
- import java.util.HashMap;
-
- /**
- * Class to parse XSL FO property expression.
- * This class is heavily based on the epxression parser in James Clark's
- * XT, an XSLT processor.
- */
- public class PropertyParser extends PropertyTokenizer {
- private PropertyInfo propInfo; // Maker and propertyList related info
-
- private static final String RELUNIT = "em";
- private static final Numeric negOne = new Numeric(new Double(-1.0));
- private static final HashMap functionTable = new HashMap();
-
- static {
- // Initialize the HashMap of XSL-defined functions
- functionTable.put("ceiling", new CeilingFunction());
- functionTable.put("floor", new FloorFunction());
- functionTable.put("round", new RoundFunction());
- functionTable.put("min", new MinFunction());
- functionTable.put("max", new MaxFunction());
- functionTable.put("abs", new AbsFunction());
- functionTable.put("rgb", new RGBColorFunction());
- functionTable.put("from-table-column", new FromTableColumnFunction());
- functionTable.put("inherited-property-value",
- new InheritedPropFunction());
- functionTable.put("from-parent", new FromParentFunction());
- functionTable.put("from-nearest-specified-value",
- new NearestSpecPropFunction());
- functionTable.put("proportional-column-width",
- new PPColWidthFunction());
- functionTable.put("label-end", new LabelEndFunction());
- functionTable.put("body-start", new BodyStartFunction());
- // NOTE: used from code generated for corresponding properties
- functionTable.put("_fop-property-value", new FopPropValFunction());
-
- /**
- * * NOT YET IMPLEMENTED!!!
- * functionTable.put("icc-color", new ICCcolorFunction());
- * functionTable.put("system-color", new SystemColorFunction());
- * functionTable.put("system-font", new SystemFontFunction());
- *
- * functionTable.put("merge-property-values", new MergePropsFunction());
- */
- }
-
-
- /**
- * Public entrypoint to the Property expression parser.
- * @param expr The specified value (attribute on the xml element).
- * @param propInfo A PropertyInfo object representing the context in
- * which the property expression is to be evaluated.
- * @return A Property object holding the parsed result.
- * @throws PropertyException If the "expr" cannot be parsed as a Property.
- */
- public static Property parse(String expr, PropertyInfo propInfo)
- throws PropertyException {
- return new PropertyParser(expr, propInfo).parseProperty();
- }
-
-
- /**
- * Private constructor. Called by the static parse() method.
- * @param propExpr The specified value (attribute on the xml element).
- * @param propInfo A PropertyInfo object representing the context in
- * which the property expression is to be evaluated.
- */
- private PropertyParser(String propExpr, PropertyInfo pInfo) {
- super(propExpr);
- this.propInfo = pInfo;
- }
-
- /**
- * Parse the property expression described in the instance variables.
- * Note: If the property expression String is empty, a StringProperty
- * object holding an empty String is returned.
- * @return A Property object holding the parsed result.
- * @throws PropertyException If the "expr" cannot be parsed as a Property.
- */
- private Property parseProperty() throws PropertyException {
- next();
- if (currentToken == TOK_EOF) {
- // if prop value is empty string, force to StringProperty
- return new StringProperty("");
- }
- ListProperty propList = null;
- while (true) {
- Property prop = parseAdditiveExpr();
- if (currentToken == TOK_EOF) {
- if (propList != null) {
- propList.addProperty(prop);
- return propList;
- } else {
- return prop;
- }
- } else {
- if (propList == null) {
- propList = new ListProperty(prop);
- } else {
- propList.addProperty(prop);
- }
- }
- // throw new PropertyException("unexpected token");
- }
- // return prop;
- }
-
- /**
- * Try to parse an addition or subtraction expression and return the
- * resulting Property.
- */
- private Property parseAdditiveExpr() throws PropertyException {
- // Evaluate and put result on the operand stack
- Property prop = parseMultiplicativeExpr();
- loop:
- for (; ;) {
- switch (currentToken) {
- case TOK_PLUS:
- next();
- prop = evalAddition(prop.getNumeric(),
- parseMultiplicativeExpr().getNumeric());
- break;
- case TOK_MINUS:
- next();
- prop =
- evalSubtraction(prop.getNumeric(),
- parseMultiplicativeExpr().getNumeric());
- break;
- default:
- break loop;
- }
- }
- return prop;
- }
-
- /**
- * Try to parse a multiply, divide or modulo expression and return
- * the resulting Property.
- */
- private Property parseMultiplicativeExpr() throws PropertyException {
- Property prop = parseUnaryExpr();
- loop:
- for (; ;) {
- switch (currentToken) {
- case TOK_DIV:
- next();
- prop = evalDivide(prop.getNumeric(),
- parseUnaryExpr().getNumeric());
- break;
- case TOK_MOD:
- next();
- prop = evalModulo(prop.getNumber(),
- parseUnaryExpr().getNumber());
- break;
- case TOK_MULTIPLY:
- next();
- prop = evalMultiply(prop.getNumeric(),
- parseUnaryExpr().getNumeric());
- break;
- default:
- break loop;
- }
- }
- return prop;
- }
-
- /**
- * Try to parse a unary minus expression and return the
- * resulting Property.
- */
- private Property parseUnaryExpr() throws PropertyException {
- if (currentToken == TOK_MINUS) {
- next();
- return evalNegate(parseUnaryExpr().getNumeric());
- }
- return parsePrimaryExpr();
- }
-
-
- /**
- * Checks that the current token is a right parenthesis
- * and throws an exception if this isn't the case.
- */
- private final void expectRpar() throws PropertyException {
- if (currentToken != TOK_RPAR) {
- throw new PropertyException("expected )");
- }
- next();
- }
-
- /**
- * Try to parse a primary expression and return the
- * resulting Property.
- * A primary expression is either a parenthesized expression or an
- * expression representing a primitive Property datatype, such as a
- * string literal, an NCname, a number or a unit expression, or a
- * function call expression.
- */
- private Property parsePrimaryExpr() throws PropertyException {
- Property prop;
- switch (currentToken) {
- case TOK_LPAR:
- next();
- prop = parseAdditiveExpr();
- expectRpar();
- return prop;
-
- case TOK_LITERAL:
- prop = new StringProperty(currentTokenValue);
- break;
-
- case TOK_NCNAME:
- // Interpret this in context of the property or do it later?
- prop = new NCnameProperty(currentTokenValue);
- break;
-
- case TOK_FLOAT:
- prop = new NumberProperty(new Double(currentTokenValue));
- break;
-
- case TOK_INTEGER:
- prop = new NumberProperty(new Integer(currentTokenValue));
- break;
-
- case TOK_PERCENT:
- /*
- * Get the length base value object from the Maker. If null, then
- * this property can't have % values. Treat it as a real number.
- */
- double pcval =
- new Double(currentTokenValue.substring(0, currentTokenValue.length() - 1)).doubleValue()
- / 100.0;
- // LengthBase lbase = this.propInfo.getPercentLengthBase();
- PercentBase pcBase = this.propInfo.getPercentBase();
- if (pcBase != null) {
- if (pcBase.getDimension() == 0) {
- prop = new NumberProperty(pcval * pcBase.getBaseValue());
- } else if (pcBase.getDimension() == 1) {
- prop = new LengthProperty(new PercentLength(pcval,
- pcBase));
- } else {
- throw new PropertyException("Illegal percent dimension value");
- }
- } else {
- // WARNING? Interpret as a decimal fraction, eg. 50% = .5
- prop = new NumberProperty(pcval);
- }
- break;
-
- case TOK_NUMERIC:
- // A number plus a valid unit name.
- int numLen = currentTokenValue.length() - currentUnitLength;
- String unitPart = currentTokenValue.substring(numLen);
- Double numPart = new Double(currentTokenValue.substring(0,
- numLen));
- Length length = null;
- if (unitPart.equals(RELUNIT)) {
- length = new FixedLength(numPart.doubleValue(),
- propInfo.currentFontSize());
- } else {
- length = new FixedLength(numPart.doubleValue(), unitPart);
- }
- if (length == null) {
- throw new PropertyException("unrecognized unit name: "
- + currentTokenValue);
- } else {
- prop = new LengthProperty(length);
- }
- break;
-
- case TOK_COLORSPEC:
- prop = new ColorTypeProperty(new ColorType(currentTokenValue));
- break;
-
- case TOK_FUNCTION_LPAR: {
- Function function =
- (Function)functionTable.get(currentTokenValue);
- if (function == null) {
- throw new PropertyException("no such function: "
- + currentTokenValue);
- }
- next();
- // Push new function (for function context: getPercentBase())
- propInfo.pushFunction(function);
- prop = function.eval(parseArgs(function.nbArgs()), propInfo);
- propInfo.popFunction();
- return prop;
- }
- default:
- throw new PropertyException("syntax error");
- }
- next();
- return prop;
- }
-
- /**
- * Parse a comma separated list of function arguments. Each argument
- * may itself be an expression. This method consumes the closing right
- * parenthesis of the argument list.
- * @param nbArgs The number of arguments expected by the function.
- * @return An array of Property objects representing the arguments
- * found.
- * @throws PropertyException If the number of arguments found isn't equal
- * to the number expected.
- */
- Property[] parseArgs(int nbArgs) throws PropertyException {
- Property[] args = new Property[nbArgs];
- Property prop;
- int i = 0;
- if (currentToken == TOK_RPAR) {
- // No args: func()
- next();
- } else {
- while (true) {
-
- prop = parseAdditiveExpr();
- if (i < nbArgs) {
- args[i++] = prop;
- }
- // ignore extra args
- if (currentToken != TOK_COMMA) {
- break;
- }
- next();
- }
- expectRpar();
- }
- if (nbArgs != i) {
- throw new PropertyException("Wrong number of args for function");
- }
- return args;
- }
-
-
- /**
- * Evaluate an addition operation. If either of the arguments is null,
- * this means that it wasn't convertible to a Numeric value.
- * @param op1 A Numeric object (Number or Length-type object)
- * @param op2 A Numeric object (Number or Length-type object)
- * @return A new NumericProperty object holding an object which represents
- * the sum of the two operands.
- * @throws PropertyException If either operand is null.
- */
- private Property evalAddition(Numeric op1,
- Numeric op2) throws PropertyException {
- if (op1 == null || op2 == null) {
- throw new PropertyException("Non numeric operand in addition");
- }
- return new NumericProperty(op1.add(op2));
- }
-
- /**
- * Evaluate a subtraction operation. If either of the arguments is null,
- * this means that it wasn't convertible to a Numeric value.
- * @param op1 A Numeric object (Number or Length-type object)
- * @param op2 A Numeric object (Number or Length-type object)
- * @return A new NumericProperty object holding an object which represents
- * the difference of the two operands.
- * @throws PropertyException If either operand is null.
- */
- private Property evalSubtraction(Numeric op1,
- Numeric op2) throws PropertyException {
- if (op1 == null || op2 == null) {
- throw new PropertyException("Non numeric operand in subtraction");
- }
- return new NumericProperty(op1.subtract(op2));
- }
-
- /**
- * Evaluate a unary minus operation. If the argument is null,
- * this means that it wasn't convertible to a Numeric value.
- * @param op A Numeric object (Number or Length-type object)
- * @return A new NumericProperty object holding an object which represents
- * the negative of the operand (multiplication by *1).
- * @throws PropertyException If the operand is null.
- */
- private Property evalNegate(Numeric op) throws PropertyException {
- if (op == null) {
- throw new PropertyException("Non numeric operand to unary minus");
- }
- return new NumericProperty(op.multiply(negOne));
- }
-
- /**
- * Evaluate a multiplication operation. If either of the arguments is null,
- * this means that it wasn't convertible to a Numeric value.
- * @param op1 A Numeric object (Number or Length-type object)
- * @param op2 A Numeric object (Number or Length-type object)
- * @return A new NumericProperty object holding an object which represents
- * the product of the two operands.
- * @throws PropertyException If either operand is null.
- */
- private Property evalMultiply(Numeric op1,
- Numeric op2) throws PropertyException {
- if (op1 == null || op2 == null) {
- throw new PropertyException("Non numeric operand in multiplication");
- }
- return new NumericProperty(op1.multiply(op2));
- }
-
-
- /**
- * Evaluate a division operation. If either of the arguments is null,
- * this means that it wasn't convertible to a Numeric value.
- * @param op1 A Numeric object (Number or Length-type object)
- * @param op2 A Numeric object (Number or Length-type object)
- * @return A new NumericProperty object holding an object which represents
- * op1 divided by op2.
- * @throws PropertyException If either operand is null.
- */
- private Property evalDivide(Numeric op1,
- Numeric op2) throws PropertyException {
- if (op1 == null || op2 == null) {
- throw new PropertyException("Non numeric operand in division");
- }
- return new NumericProperty(op1.divide(op2));
- }
-
- /**
- * Evaluate a modulo operation. If either of the arguments is null,
- * this means that it wasn't convertible to a Number value.
- * @param op1 A Number object
- * @param op2 A Number object
- * @return A new NumberProperty object holding an object which represents
- * op1 mod op2.
- * @throws PropertyException If either operand is null.
- */
- private Property evalModulo(Number op1,
- Number op2) throws PropertyException {
- if (op1 == null || op2 == null) {
- throw new PropertyException("Non number operand to modulo");
- }
- return new NumberProperty(op1.doubleValue() % op2.doubleValue());
- }
-
- }
|