pbwest      02/05/06 22:37:16

  Modified:    src/org/apache/fop/fo/expr Tag: FOP_0-20-0_Alt-Design
                        PropertyParser.java PropertyTokenizer.java
  Log:
  Initial version of experimental property expression parsing.
  
  Revision  Changes    Path
  No                   revision
  
  
  No                   revision
  
  
  1.5.2.1   +435 -287  xml-fop/src/org/apache/fop/fo/expr/PropertyParser.java
  
  Index: PropertyParser.java
  ===================================================================
  RCS file: /home/cvs/xml-fop/src/org/apache/fop/fo/expr/PropertyParser.java,v
  retrieving revision 1.5
  retrieving revision 1.5.2.1
  diff -u -r1.5 -r1.5.2.1
  --- PropertyParser.java       30 Jul 2001 20:29:21 -0000      1.5
  +++ PropertyParser.java       7 May 2002 05:37:16 -0000       1.5.2.1
  @@ -1,5 +1,5 @@
   /*
  - * $Id: PropertyParser.java,v 1.5 2001/07/30 20:29:21 tore Exp $
  + * $Id: PropertyParser.java,v 1.5.2.1 2002/05/07 05:37:16 pbwest Exp $
    * Copyright (C) 2001 The Apache Software Foundation. All rights reserved.
    * For details on use and redistribution please refer to the
    * LICENSE file included with these sources.
  @@ -7,140 +7,195 @@
   
   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 org.apache.fop.fo.PropertyConsts;
  +import org.apache.fop.fo.Properties;
  +import org.apache.fop.fo.PropNames;
  +
  +import org.apache.fop.fo.expr.PropertyValue;
  +import org.apache.fop.fo.expr.PropertyValueList;
  +import org.apache.fop.datatypes.Numeric;
  +import org.apache.fop.datatypes.Literal;
  +import org.apache.fop.datatypes.NCName;
  +import org.apache.fop.datatypes.Percentage;
  +import org.apache.fop.datatypes.Ems;
  +import org.apache.fop.datatypes.IntegerType;
  +import org.apache.fop.datatypes.Length;
  +import org.apache.fop.datatypes.Time;
  +import org.apache.fop.datatypes.Frequency;
  +import org.apache.fop.datatypes.Angle;
  +import org.apache.fop.datatypes.Bool;
  +import org.apache.fop.datatypes.Inherit;
  +import org.apache.fop.datatypes.Auto;
  +import org.apache.fop.datatypes.None;
  +import org.apache.fop.datatypes.ColorType;
  +import org.apache.fop.datatypes.StringType;
  +import org.apache.fop.datatypes.MimeType;
  +import org.apache.fop.datatypes.UriType;
  +import org.apache.fop.datatypes.FromParent;
  +import org.apache.fop.datatypes.FromNearestSpecified;
  +//import org.apache.fop.datatypes.*;
   
  -import java.util.Hashtable;
  +import java.util.HashMap;
   
   /**
    * Class to parse XSL FO property expression.
  - * This class is heavily based on the epxression parser in James Clark's
  + * This class is heavily based on the expression parser in James Clark's
    * XT, an XSLT processor.
  + *
  + * PropertyParser objects are re-usable.  The constructor simply creates the
  + * object.  To parse an expression, the public method <i>Parse</i> is
  + * called.
    */
   public class PropertyParser extends PropertyTokenizer {
  -    private PropertyInfo propInfo;    // Maker and propertyList related info
  -
  -    static private final String RELUNIT = "em";
  -    static private final Numeric negOne = new Numeric(new Double(-1.0));
  -    static final private Hashtable functionTable = new Hashtable();
  -
  -    static {
  -        // Initialize the Hashtable 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.
  +     * This is an attempt to ensure that the restriction on the application of
  +     * from-parent() and from-nearest-specified-value() functions to shorthand
  +     * properties (Section 5.10.4 Property Value Functions) is maintained.
  +     * I.e. if a shorthand property is the subject of one of these functions,
  +     * ist is valid only if "...the expression only consists of the
  +     * [from-parent or from-nearest-specified-value] function with an argument
  +     * matching the property being computed..."
        */
  -    public static Property parse(String expr, PropertyInfo propInfo)
  -            throws PropertyException {
  -        return new PropertyParser(expr, propInfo).parseProperty();
  -    }
  -
  -
  +    //private int elementsSeen = 0;
       /**
  -     * 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.
  +     * Used in conjunction with <i>elementsSeen</i>.
        */
  -    private PropertyParser(String propExpr, PropertyInfo pInfo) {
  -        super(propExpr);
  -        this.propInfo = pInfo;
  +    //private String restrictedValueFunctSeen = null;
  +
  +    public PropertyParser() {
  +        super();
       }
   
       /**
        * Parse the property expression described in the instance variables.
  +     * <p>
        * 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("");
  +     * @param property an <tt>int</tt> containing the property index.
  +     * which the property expression is to be evaluated.
  +     * @param expr The specified value (attribute on the xml element).
  +     * @return A PropertyValue holding the parsed result.
  +     * @throws PropertyException If the "expr" cannot be parsed as a
  +     * PropertyValue.
  +     */
  +    public PropertyValue parse(int property, String expr)
  +        throws PropertyException
  +    {
  +        synchronized (this) {
  +            // make sure this parser is available
  +            if (expr != null) // the parser is currently active
  +                throw new PropertyException
  +                        ("PropertyParser is currently active.");
  +            initialize(property, expr);
           }
  -        ListProperty propList = null;
  +
  +        next();
  +        if (currentToken == EOF)
  +            // prop value is empty
  +            throw new PropertyException
  +                    ("No token recognized in :" + expr + ":");
  +
  +        PropertyValueList propList = new PropertyValueList(property);
           while (true) {
  -            Property prop = parseAdditiveExpr();
  -            if (currentToken == TOK_EOF) {
  -                if (propList != null) {
  -                    propList.addProperty(prop);
  +            PropertyValue prop = parseAdditiveExpr();
  +            if (currentToken == EOF) {
  +                // end of the expression - add to list and go
  +                if (propList.size() != 0) {
  +                    propList.add(prop);
                       return propList;
  -                } else
  +                } else { // list is empty
                       return prop;
  -            } else {
  -                if (propList == null) {
  -                    propList = new ListProperty(prop);
  -                } else {
  -                    propList.addProperty(prop);
                   }
               }
  -            // throw new PropertyException("unexpected token");
  +            // throw away commas separating arguments.  These can occur
  +            // in font-family and voice-family.  Commas are regarded here
  +            // as separators of list and sublist elements.
  +            // See 7.16.5 "text-shadow" in the 1.0 Recommendation for an
  +            // example of sublists.
  +            if (currentToken == COMMA) {
  +                next();
  +                propList.add(prop);
  +            } else { // whitespace separates list elements; make a sublist
  +                propList.add(parseSublist(prop));
  +                if (currentToken == EOF)
  +                    return propList;
  +            }
  +        }
  +    }
  +
  +    /**
  +     * <p>Parse a property values sublist - a list of whitespace separated
  +     * <tt>PropertyValue</tt>s.
  +     * </p><p>
  +     * Property value expressions for various properties may contaiin lists
  +     * of values, which may be separated by whitespace or by commas.  See,
  +     * e.g., 7.6.17 "voice-family" and 7.8.2 "font-family".  The shorthands
  +     * may also contain lists of elements, generally (or exclusively)
  +     * whitespace separated.  7.16.5 "text-shadow" allows whitespace
  +     * separated length doubles or triples to be specified for individual
  +     * shadow effects, with multiple shadow effects, each separated by
  +     * commmas.
  +     * @param initialValue a <tt>PropertyValue</tt> to assign as the initial
  +     * value of the sublist.  The detection of this value, which is
  +     * whitespace separated from a subsequent value,  has been the
  +     * trigger for the creation of the sublist.
  +     * @return a <tt>PropertyValueList</tt> containing the sublist.  The
  +     * indicatior for the end of the sublist is the end of the expression,
  +     * or a comma.
  +     */
  +    PropertyValueList parseSublist(PropertyValue initialValue)
  +        throws PropertyException
  +    {
  +        PropertyValueList sublist = new PropertyValueList(property);
  +        sublist.add(initialValue);
  +        while (true) {
  +            PropertyValue prop = parseAdditiveExpr();
  +            if (currentToken == EOF) {
  +                // end of the expression - add to sublist and go
  +                sublist.add(prop);
  +                return sublist;
  +            }
  +            // Comma separates next element - end of sublist
  +            if (currentToken == COMMA) {
  +                next();
  +                sublist.add(prop);
  +                return sublist;
  +            } else { // whitespace separates next elements; add to sublist
  +                sublist.add(prop);
  +            }
  +        }
  +    }
  +
  +    /**
  +     * Reset the parser by resetting the tokenizer to null (or equivalent)
  +     * values.
  +     */
  +    public void resetParser() {
  +        synchronized (this) {
  +            //elementsSeen = 0;
  +            //restrictedValueFunctSeen = null;
  +            reset();
           }
  -        // return prop;
       }
   
       /**
        * Try to parse an addition or subtraction expression and return the
  -     * resulting Property.
  +     * resulting PropertyValue.
        */
  -    private Property parseAdditiveExpr() throws PropertyException {
  +    private PropertyValue parseAdditiveExpr() throws PropertyException {
           // Evaluate and put result on the operand stack
  -        Property prop = parseMultiplicativeExpr();
  +        PropertyValue prop = parseMultiplicativeExpr();
           loop:
           for (; ; ) {
               switch (currentToken) {
  -            case TOK_PLUS:
  +            case PLUS:
                   next();
  -                prop = evalAddition(prop.getNumeric(),
  -                                    parseMultiplicativeExpr().getNumeric());
  +                ((Numeric)prop).add((Numeric)parseMultiplicativeExpr());
                   break;
  -            case TOK_MINUS:
  +            case MINUS:
                   next();
  -                prop =
  -                    evalSubtraction(prop.getNumeric(),
  -                                    parseMultiplicativeExpr().getNumeric());
  +                ((Numeric)prop).subtract((Numeric)parseMultiplicativeExpr());
                   break;
               default:
                   break loop;
  @@ -151,27 +206,24 @@
   
       /**
        * Try to parse a multiply, divide or modulo expression and return
  -     * the resulting Property.
  +     * the resulting PropertyValue.
        */
  -    private Property parseMultiplicativeExpr() throws PropertyException {
  -        Property prop = parseUnaryExpr();
  +    private PropertyValue parseMultiplicativeExpr() throws PropertyException {
  +        PropertyValue prop = parseUnaryExpr();
           loop:
           for (; ; ) {
               switch (currentToken) {
  -            case TOK_DIV:
  +            case DIV:
                   next();
  -                prop = evalDivide(prop.getNumeric(),
  -                                  parseUnaryExpr().getNumeric());
  +                ((Numeric)prop).divide((Numeric)parseUnaryExpr());
                   break;
  -            case TOK_MOD:
  +            case MOD:
                   next();
  -                prop = evalModulo(prop.getNumber(),
  -                                  parseUnaryExpr().getNumber());
  +                ((Numeric)prop).mod((Numeric)parseUnaryExpr());
                   break;
  -            case TOK_MULTIPLY:
  +            case MULTIPLY:
                   next();
  -                prop = evalMultiply(prop.getNumeric(),
  -                                    parseUnaryExpr().getNumeric());
  +                ((Numeric)prop).multiply((Numeric)parseUnaryExpr());
                   break;
               default:
                   break loop;
  @@ -182,12 +234,12 @@
   
       /**
        * Try to parse a unary minus expression and return the
  -     * resulting Property.
  +     * resulting PropertyValue.
        */
  -    private Property parseUnaryExpr() throws PropertyException {
  -        if (currentToken == TOK_MINUS) {
  +    private PropertyValue parseUnaryExpr() throws PropertyException {
  +        if (currentToken == MINUS) {
               next();
  -            return evalNegate(parseUnaryExpr().getNumeric());
  +            return ((Numeric)parseUnaryExpr()).negate();
           }
           return parsePrimaryExpr();
       }
  @@ -198,111 +250,286 @@
        * and throws an exception if this isn't the case.
        */
       private final void expectRpar() throws PropertyException {
  -        if (currentToken != TOK_RPAR)
  +        if (currentToken != RPAR)
               throw new PropertyException("expected )");
           next();
       }
   
       /**
        * Try to parse a primary expression and return the
  -     * resulting Property.
  +     * resulting PropertyValue.
        * A primary expression is either a parenthesized expression or an
  -     * expression representing a primitive Property datatype, such as a
  +     * expression representing a primitive PropertyValue 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;
  +    private PropertyValue parsePrimaryExpr() throws PropertyException {
  +        PropertyValue prop;
  +        //if (restrictedValueFunctSeen != null)
  +        //throw new PropertyException
  +        //(restrictedValueFunctSeen
  +        //+ " already seen with shorthand argument");
           switch (currentToken) {
  -        case TOK_LPAR:
  +        case LPAR:
               next();
               prop = parseAdditiveExpr();
               expectRpar();
  +            // Do this here, rather than breaking, because expectRpar()
  +            // consumes the right parenthesis and calls next().
  +            //elementsSeen++;
               return prop;
   
  -        case TOK_LITERAL:
  -            prop = new StringProperty(currentTokenValue);
  +        case LITERAL:
  +            prop = new Literal(property, currentTokenValue);
               break;
   
  -        case TOK_NCNAME:
  +        case NCNAME:
               // Interpret this in context of the property or do it later?
  -            prop = new NCnameProperty(currentTokenValue);
  +            prop = new NCName(property, currentTokenValue);
               break;
   
  -        case TOK_FLOAT:
  -            prop = new NumberProperty(new Double(currentTokenValue));
  +        case FLOAT:
  +            // Do I need to differentiate here between floats and integers?
  +            prop = new Numeric
  +                    (property, (new Double(currentTokenValue)).doubleValue());
               break;
   
  -        case TOK_INTEGER:
  -            prop = new NumberProperty(new Integer(currentTokenValue));
  +        case INTEGER:
  +            prop = IntegerType.makeInteger
  +                    (property, (new Long(currentTokenValue)).longValue());
               break;
   
  -        case TOK_PERCENT:
  +        case 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.
  +             * Generate a Percentage object with the percentage number.
  +             * The constructor converts this to a straight multiplicative
  +             * factor by dividing by 100.
                */
  -            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);
  -            }
  +            prop = Percentage.makePercentage
  +                    (property, (new Double(currentTokenValue)).doubleValue());
               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 Length(numPart.doubleValue(),
  -                                    propInfo.currentFontSize());
  -            } else
  -                length = new Length(numPart.doubleValue(), unitPart);
  -            if (length == null) {
  -                throw new PropertyException("unrecognized unit name: "
  -                                            + currentTokenValue);
  -            } else
  -                prop = new LengthProperty(length);
  +        case ABSOLUTE_LENGTH:
  +            prop = Length.makeLength(property,
  +                              (new Double(currentTokenValue)).doubleValue(),
  +                              currentUnit);
  +            break;
  +        case TIME:
  +            prop = Time.makeTime(property,
  +                              (new Double(currentTokenValue)).doubleValue(),
  +                              currentUnit);
  +            break;
  +        case FREQ:
  +            prop = Frequency.makeFrequency(property,
  +                              (new Double(currentTokenValue)).doubleValue(),
  +                              currentUnit);
  +            break;
  +        case ANGLE:
  +            prop = Angle.makeAngle(property,
  +                              (new Double(currentTokenValue)).doubleValue(),
  +                              currentUnit);
  +            break;
  +        case RELATIVE_LENGTH:
  +            prop = Ems.makeEms(property,
  +                     (new Double(currentTokenValue)).doubleValue());
  +            break;
  +
  +        case COLORSPEC:
  +            prop = new ColorType(property, currentTokenValue);
  +            break;
  +
  +        case BOOL:
  +            prop = new Bool(property, currentTokenValue);
  +            break;
  +
  +        case AUTO:
  +            prop = new Auto(property);
  +            break;
  +
  +        case NONE:
  +            prop = new None(property);
  +            break;
  +
  +        case INHERIT:
  +            throw new PropertyException("INHERIT not supported");
  +            //break;
  +
  +        case URI:
  +            prop = new UriType(property, currentTokenValue);
               break;
   
  -        case TOK_COLORSPEC:
  -            prop = new ColorTypeProperty(new ColorType(currentTokenValue));
  +        case MIMETYPE:
  +            prop = new MimeType(property, currentTokenValue);
               break;
   
  -        case TOK_FUNCTION_LPAR: {
  -            Function function =
  -                (Function)functionTable.get(currentTokenValue);
  -            if (function == null) {
  +        case FUNCTION_LPAR: {
  +            // N.B. parseArgs() invokes expectRpar at the end of argument
  +            // processing, so, like LPAR processing, next() is not called
  +            // and the return from this method must be premature
  +            prop = null;
  +            // Numeric functions
  +            if (currentTokenValue.equals("floor")) {
  +                PropertyValue[] args = parseArgs(1);
  +                prop = new Numeric
  +                        (property, ((Numeric)args[0]).floor());
  +            }
  +            else if (currentTokenValue.equals("ceiling")) {
  +                PropertyValue[] args = parseArgs(1);
  +                prop = new Numeric
  +                        (property, ((Numeric)args[0]).ceiling());
  +            }
  +            else if (currentTokenValue.equals("round")) {
  +                PropertyValue[] args = parseArgs(1);
  +                prop = new Numeric
  +                        (property, ((Numeric)args[0]).round());
  +            }
  +            else if (currentTokenValue.equals("min")) {
  +                PropertyValue[] args = parseArgs(2);
  +                prop = new Numeric
  +                        (property, ((Numeric)args[0]).min((Numeric)args[1]));
  +            }
  +            else if (currentTokenValue.equals("max")) {
  +                PropertyValue[] args = parseArgs(2);
  +                prop = new Numeric
  +                        (property, ((Numeric)args[0]).max((Numeric)args[1]));
  +            }
  +            else if (currentTokenValue.equals("abs")) {
  +                PropertyValue[] args = parseArgs(1);
  +                prop = new Numeric
  +                        (property, ((Numeric)args[0]).abs());
  +            }
  +
  +            // Color functions
  +            else if (currentTokenValue.equals("rgb")) {
  +                PropertyValue[] args = parseArgs(3);
  +                prop = new ColorType
  +                        (property, ((Numeric)args[0]).asInt(),
  +                         ((Numeric)args[1]).asInt(),
  +                         ((Numeric)args[2]).asInt());
  +            }
  +            else if (currentTokenValue.equals("rgb-icc")) {
  +                PropertyValue[] args = parseArgs(6);
  +                throw new PropertyException
  +                        ("rgb-icc function is not supported.");
  +            }
  +            else if (currentTokenValue.equals("system-color")) {
  +                PropertyValue[] args = parseArgs(1);
  +                prop = new ColorType
  +                        (property, ((StringType)args[0]).getString());
  +            }
  +
  +            // Font function
  +            else if (currentTokenValue.equals("system-font")) {
  +                PropertyValue[] args = parseArgs(1, 2);
  +                throw new PropertyException
  +                        ("system-font function is not supported.");
  +            }
  +
  +            // Property value functions
  +            else if (currentTokenValue.equals("inherited-property-value")) {
  +                int propindex = property;
  +                PropertyValue[] args = parseArgs(0, 1);
  +                if (args.length != 0)
  +                    propindex = PropertyConsts.getPropertyIndex(
  +                            ((StringType)args[0]).getString());
  +                if (PropertyConsts.inheritance(propindex) == Properties.NO)
  +                    throw new PropertyException
  +                            ("inherited-property-value: "
  +                             + PropNames.getPropertyName(propindex)
  +                             + " is not inherited.");
  +                prop = new Inherit(property, propindex);
  +            }
  +            else if (currentTokenValue.equals("label-end")) {
  +                PropertyValue[] args = parseArgs(0);
  +                throw new PropertyException
  +                        ("label-end function is not supported.");
  +            }
  +            else if (currentTokenValue.equals("body-start")) {
  +                PropertyValue[] args = parseArgs(0);
  +                throw new PropertyException
  +                        ("body-start function is not supported.");
  +            }
  +            // N.B. see comments on classes FromNearestSpecified and
  +            // FromParent for explanation of this section
  +            else if (currentTokenValue.equals("from-parent") ||
  +                     currentTokenValue.equals("from-nearest-specified-value"))
  +            {
  +                // Preset the return value in case of a shorthand property
  +                if (currentTokenValue.equals("from-parent"))
  +                    prop = new FromParent(property);
  +                else
  +                    prop = new FromNearestSpecified(property);
  +
  +                PropertyValue[] args = parseArgs(0, 1);
  +                if (args.length == 0) {
  +                    if (! PropertyConsts.isShorthand(property)) {
  +                        // develop the function value and return it as
  +                        // a property.
  +                        throw new PropertyException
  +                                (currentTokenValue +
  +                                     " function is not supported.");
  +                    }
  +                    // else a shorthand - do nothing; prop has been set
  +                    // to the appropriate pseudo-propertyValue
  +                } else { // one argument - it must be a property name
  +                    if ( ! (args[0] instanceof NCName))
  +                        throw new PropertyException
  +                                (currentTokenValue + " function requires"
  +                                     + " property name arg.");
  +                    // else arg[0] is an NCName
  +                    NCName ncname = (NCName)args[0];
  +                    String propname = ncname.getNCName();
  +                    int nameindex = PropertyConsts.getPropertyIndex(propname);
  +                    if (PropertyConsts.isShorthand(nameindex)) {
  +                        // the argument is a shorthand property -
  +                        // it must be the same as the property being
  +                        // assigned to.
  +                        // see 5.10.4 Property Value Functions
  +                        if ( ! (nameindex == property))
  +                            throw new PropertyException
  +                                    (currentTokenValue +
  +                                     " argument " + propname +
  +                                     " does not match property " +
  +                                     PropNames.getPropertyName(property));
  +                        // else perform shorthand processing
  +                        // i.e. do nothing; prop has been set to the correct
  +                        // pseudo-propertyValue
  +                    }
  +                    else {   // An NCName but not a shorthand
  +                        // Perform normal from-parent processing
  +                        throw new PropertyException
  +                                (currentTokenValue +
  +                                 " function is not supported.");
  +                    }
  +                }
  +            }
  +            else if (currentTokenValue.equals("from-table-column")) {
  +                PropertyValue[] args = parseArgs(0, 1);
  +                throw new PropertyException
  +                        ("from-table-column function is not supported.");
  +            }
  +            else if (currentTokenValue.equals("proportional-column-width")) {
  +                PropertyValue[] args = parseArgs(1);
  +                throw new PropertyException
  +                        ("proportional-column-width "
  +                         + "function is not supported.");
  +            }
  +            else if (currentTokenValue.equals("merge-property-values")) {
  +                PropertyValue[] args = parseArgs(0, 1);
  +                throw new PropertyException
  +                        ("merge-property-values function is not supported.");
  +            }
  +            else
                   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();
  +            //elementsSeen++;
               return prop;
           }
           default:
               throw new PropertyException("syntax error");
           }
           next();
  +        //elementsSeen++;
           return prop;
       }
   
  @@ -311,132 +538,53 @@
        * 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;
  +     * @return <tt>PropertyValueList</tt> of <tt>PropertyValue</tt> objects
  +     * representing the arguments found.
  +     * @exception PropertyException
  +     */
  +    PropertyValue[] parseArgs(int nbArgs) throws PropertyException {
  +        return parseArgs(nbArgs, nbArgs);
  +    }
  +
  +    /**
  +     * 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 minArgs The minimum number of arguments expected by the function.
  +     * @param maxArgs The maximum number of arguments expected by the function.
  +     * @return <tt>PropertyValueList</tt> of <tt>PropertyValue</tt> objects
  +     * representing the arguments found.  N.B.  The actual number of arguments
  +     * returned is guaranteed to be between minArgs and maxArgs, inclusive,
  +     * but the actual list of args found is terminated by the end of the
  +     * array, or the first null element.
  +     * @exception PropertyException
  +     */
  +    PropertyValue[] parseArgs(int minArgs, int maxArgs)
  +        throws PropertyException
  +    {
  +        PropertyValue[] args = new PropertyValue[maxArgs];
  +        PropertyValue prop;
           int i = 0;
  -        if (currentToken == TOK_RPAR) {
  +        if (currentToken == RPAR) {
               // No args: func()
               next();
           } else {
               while (true) {
  -
                   prop = parseAdditiveExpr();
  -                if (i < nbArgs) {
  +                if (i < maxArgs) {
                       args[i++] = prop;
                   }
                   // ignore extra args
  -                if (currentToken != TOK_COMMA)
  +                if (currentToken != COMMA)
                       break;
                   next();
               }
               expectRpar();
           }
  -        if (nbArgs != i) {
  +        if (minArgs > i || i > maxArgs) {
               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());
       }
   
   }
  
  
  
  1.4.4.1   +297 -88   xml-fop/src/org/apache/fop/fo/expr/PropertyTokenizer.java
  
  Index: PropertyTokenizer.java
  ===================================================================
  RCS file: /home/cvs/xml-fop/src/org/apache/fop/fo/expr/PropertyTokenizer.java,v
  retrieving revision 1.4
  retrieving revision 1.4.4.1
  diff -u -r1.4 -r1.4.4.1
  --- PropertyTokenizer.java    30 Jul 2001 20:29:21 -0000      1.4
  +++ PropertyTokenizer.java    7 May 2002 05:37:16 -0000       1.4.4.1
  @@ -1,5 +1,5 @@
   /*
  - * $Id: PropertyTokenizer.java,v 1.4 2001/07/30 20:29:21 tore Exp $
  + * $Id: PropertyTokenizer.java,v 1.4.4.1 2002/05/07 05:37:16 pbwest Exp $
    * Copyright (C) 2001 The Apache Software Foundation. All rights reserved.
    * For details on use and redistribution please refer to the
    * LICENSE file included with these sources.
  @@ -7,7 +7,10 @@
   
   package org.apache.fop.fo.expr;
   
  -
  +import org.apache.fop.datatypes.Numeric;
  +import org.apache.fop.datatypes.Length;
  +import org.apache.fop.datatypes.Frequency;
  +import org.apache.fop.datatypes.Time;
   
   /**
    * Class to tokenize XSL FO property expression.
  @@ -16,44 +19,92 @@
    */
   class PropertyTokenizer {
   
  -    static final int TOK_EOF = 0;
  -    static final int TOK_NCNAME = TOK_EOF + 1;
  -    static final int TOK_MULTIPLY = TOK_NCNAME + 1;
  -    static final int TOK_LPAR = TOK_MULTIPLY + 1;
  -    static final int TOK_RPAR = TOK_LPAR + 1;
  -    static final int TOK_LITERAL = TOK_RPAR + 1;
  -    static final int TOK_NUMBER = TOK_LITERAL + 1;
  -    static final int TOK_FUNCTION_LPAR = TOK_NUMBER + 1;
  -    static final int TOK_PLUS = TOK_FUNCTION_LPAR + 1;
  -    static final int TOK_MINUS = TOK_PLUS + 1;
  -    static final int TOK_MOD = TOK_MINUS + 1;
  -    static final int TOK_DIV = TOK_MOD + 1;
  -    static final int TOK_NUMERIC = TOK_DIV + 1;
  -    static final int TOK_COMMA = TOK_NUMERIC + 1;
  -    static final int TOK_PERCENT = TOK_COMMA + 1;
  -    static final int TOK_COLORSPEC = TOK_PERCENT + 1;
  -    static final int TOK_FLOAT = TOK_COLORSPEC + 1;
  -    static final int TOK_INTEGER = TOK_FLOAT + 1;
  +    /*
  +     * Maintain the numbering of this list in (X)Emacs by issuing
  +     * a shell command on the region with replacement (M-1 M-|).  Use
  +     * the perl command:
  +     * perl -p -e 'BEGIN{$n=0};$n++ if s/= [0-9]+/= $n/'
  +     *
  +     * in vi, set mark `a' at the last line and
  +     * !'aperl... etc
  +     */
  +    static final int
  +                 EOF = 0
  +             ,NCNAME = 1
  +           ,MULTIPLY = 2
  +               ,LPAR = 3
  +               ,RPAR = 4
  +            ,LITERAL = 5
  +      ,FUNCTION_LPAR = 6
  +               ,PLUS = 7
  +              ,MINUS = 8
  +                ,MOD = 9
  +                ,DIV = 10
  +              ,COMMA = 11
  +            ,PERCENT = 12
  +          ,COLORSPEC = 13
  +              ,FLOAT = 14
  +            ,INTEGER = 15
  +    ,ABSOLUTE_LENGTH = 16
  +    ,RELATIVE_LENGTH = 17
  +               ,TIME = 18
  +               ,FREQ = 19
  +              ,ANGLE = 20
  +            ,INHERIT = 21
  +               ,AUTO = 22
  +               ,NONE = 23
  +               ,BOOL = 24
  +                ,URI = 25
  +           ,MIMETYPE = 26
  +            // NO_UNIT is a transient token for internal use only.  It is
  +            // never set as the end result of parsing a token.
  +            ,NO_UNIT = 27
  +            //,NSPREFIX = 28
  +            //,WHITESPACE = 29
  +                     ;
   
  -    int currentToken = TOK_EOF;
  +    /*
  +     * Absolute unit type constants
  +     */
  +    int currentToken = EOF;
       String currentTokenValue = null;
       protected int currentUnitLength = 0;
  +    protected int currentUnit;
  +    protected String uri;
   
       private int currentTokenStartIndex = 0;
  -    private /* final */ String expr;
  +    private String expr = null;
       private int exprIndex = 0;
       private int exprLength;
  -    private boolean recognizeOperator = false;
  +    protected int property;
   
  +    protected PropertyTokenizer() {}
   
       /**
  -     * Construct a new PropertyTokenizer object to tokenize the passed
  -     * String.
  -     * @param s The Property expressio to tokenize.
  +     * Initialize this tokenizer to tokenize the passed
  +     * String as a value of the passed property.
  +     * It is assumed that the subclass has made any necessary
  +     * synchronization arrangements.
  +     * @param property an <tt>int</tt> containing the property index.
  +     * @param s The Property expression to tokenize.
        */
  -    PropertyTokenizer(String s) {
  -        this.expr = s;
  -        this.exprLength = s.length();
  +    protected void initialize(int property, String s) {
  +        expr = s;
  +        exprLength = s.length();
  +        this.property = property;
  +    }
  +
  +    /**
  +     * Reset the tokenizer to null (or equivalent) values.
  +     * Synchronization is achieved in the subclass.
  +     */
  +    protected void reset() {
  +        expr = null;
  +        exprIndex = 0;
  +        exprLength = 0;
  +        currentToken = EOF;
  +        currentTokenValue = null;
  +        property = 0;
       }
   
       /**
  @@ -61,19 +112,17 @@
        * This sets the following package visible variables:
        * currentToken  An enumerated value identifying the recognized token
        * currentTokenValue  A String containing the token contents
  -     * currentUnitLength  If currentToken = TOK_NUMERIC, the number of
  -     * characters in the unit name.
  +     * currentUnit  If currentToken = ABSOLUTE_LENGTH, TIME or FREQUENCY,
  +     * an enumerated value identifying the unit.
        * @throws PropertyException If un unrecognized token is encountered.
        */
       void next() throws PropertyException {
           currentTokenValue = null;
           currentTokenStartIndex = exprIndex;
  -        boolean currentMaybeOperator = recognizeOperator;
           boolean bSawDecimal;
  -        recognizeOperator = true;
           for (; ; ) {
               if (exprIndex >= exprLength) {
  -                currentToken = TOK_EOF;
  +                currentToken = EOF;
                   return;
               }
               char c = expr.charAt(exprIndex++);
  @@ -82,26 +131,29 @@
               case '\t':
               case '\r':
               case '\n':
  -                currentTokenStartIndex = exprIndex;
  +                // Whitespace characters are valid within strings.
  +                // in font family names, sequences of whitespace are
  +                // compressed into a single space. (Rec 7.8.2)
  +                //scanWhitespace();
  +                //currentToken = WHITESPACE;
  +                //currentTokenValue = expr.substring(currentTokenStartIndex,
  +                //                                   exprIndex);
  +                //return;
                   break;
               case ',':
  -                recognizeOperator = false;
  -                currentToken = TOK_COMMA;
  +                currentToken = COMMA;
                   return;
               case '+':
  -                recognizeOperator = false;
  -                currentToken = TOK_PLUS;
  +                currentToken = PLUS;
                   return;
               case '-':
  -                recognizeOperator = false;
  -                currentToken = TOK_MINUS;
  +                currentToken = MINUS;
                   return;
               case '(':
  -                currentToken = TOK_LPAR;
  -                recognizeOperator = false;
  +                currentToken = LPAR;
                   return;
               case ')':
  -                currentToken = TOK_RPAR;
  +                currentToken = RPAR;
                   return;
               case '"':
               case '\'':
  @@ -112,19 +164,10 @@
                   }
                   currentTokenValue = expr.substring(currentTokenStartIndex
                                                      + 1, exprIndex++);
  -                currentToken = TOK_LITERAL;
  +                currentToken = LITERAL;
                   return;
               case '*':
  -                /*
  -                 * if (currentMaybeOperator) {
  -                 * recognizeOperator = false;
  -                 */
  -                currentToken = TOK_MULTIPLY;
  -                /*
  -                 * }
  -                 * else
  -                 * throw new PropertyException("illegal operator *");
  -                 */
  +                currentToken = MULTIPLY;
                   return;
               case '0':
               case '1':
  @@ -148,15 +191,16 @@
                   } else
                       bSawDecimal = false;
                   if (exprIndex < exprLength && expr.charAt(exprIndex) == '%') {
  +                    currentToken = PERCENT;
  +                    currentTokenValue = expr.substring(currentTokenStartIndex,
  +                                                       exprIndex);
                       exprIndex++;
  -                    currentToken = TOK_PERCENT;
  +                    return;
                   } else {
                       // Check for possible unit name following number
  -                    currentUnitLength = exprIndex;
  -                    scanName();
  -                    currentUnitLength = exprIndex - currentUnitLength;
  -                    currentToken = (currentUnitLength > 0) ? TOK_NUMERIC
  -                                   : (bSawDecimal ? TOK_FLOAT : TOK_INTEGER);
  +                    currentToken = scanUnitName();
  +                    if (currentToken == NO_UNIT)
  +                        currentToken = bSawDecimal ? FLOAT : INTEGER;
                   }
                   currentTokenValue = expr.substring(currentTokenStartIndex,
                                                      exprIndex);
  @@ -170,14 +214,12 @@
                       if (exprIndex < exprLength
                               && expr.charAt(exprIndex) == '%') {
                           exprIndex++;
  -                        currentToken = TOK_PERCENT;
  +                        currentToken = PERCENT;
                       } else {
                           // Check for possible unit name following number
  -                        currentUnitLength = exprIndex;
  -                        scanName();
  -                        currentUnitLength = exprIndex - currentUnitLength;
  -                        currentToken = (currentUnitLength > 0) ? TOK_NUMERIC
  -                                       : TOK_FLOAT;
  +                        currentToken = scanUnitName();
  +                        if (currentToken == NO_UNIT)
  +                            currentToken = FLOAT;
                       }
                       currentTokenValue = expr.substring(currentTokenStartIndex,
                                                          exprIndex);
  @@ -188,44 +230,98 @@
               case '#':    // Start of color value
                   if (exprIndex < exprLength
                           && isHexDigit(expr.charAt(exprIndex))) {
  +                    int len;
                       ++exprIndex;
                       scanHexDigits();
  -                    currentToken = TOK_COLORSPEC;
  +                    currentToken = COLORSPEC;
                       currentTokenValue = expr.substring(currentTokenStartIndex,
                                                          exprIndex);
                       // Probably should have some multiple of 3 for length!
  -                    return;
  -                } else
  +                    len = exprIndex - currentTokenStartIndex;
  +                    if (len == 4 || len == 7) return;
  +                    throw new PropertyException("color not 3 or 6 hex digits");
  +                } else {
                       throw new PropertyException("illegal character '#'");
  +                }
   
               default:
                   --exprIndex;
                   scanName();
                   if (exprIndex == currentTokenStartIndex)
  -                    throw new PropertyException("illegal character");
  +                    // Not a name - must be a <string>
  +                    throw new PropertyException
  +                            ("illegal character '"
  +                             + expr.charAt(exprIndex) + "'");
                   currentTokenValue = expr.substring(currentTokenStartIndex,
  -        exprIndex);
  -                // if (currentMaybeOperator) {
  +                                                   exprIndex);
                   if (currentTokenValue.equals("mod")) {
  -                    currentToken = TOK_MOD;
  +                    currentToken = MOD;
                       return;
  -                } else if (currentTokenValue.equals("div")) {
  -                    currentToken = TOK_DIV;
  +                }
  +                if (currentTokenValue.equals("div")) {
  +                    currentToken = DIV;
                       return;
                   }
  -                /*
  -                 * else
  -                 * throw new PropertyException("unrecognized operator name");
  -                 * recognizeOperator = false;
  -                 * return;
  -                 * }
  -                 */
  +                if (currentTokenValue.equals("inherit")) {
  +                    currentToken = INHERIT;
  +                    return;
  +                }
  +                if (currentTokenValue.equals("auto")) {
  +                    currentToken = AUTO;
  +                    return;
  +                }
  +                if (currentTokenValue.equals("none")) {
  +                    currentToken = NONE;
  +                    return;
  +                }
  +                if (currentTokenValue.equals("true")
  +                    || currentTokenValue.equals("false")) {
  +                    currentToken = BOOL;
  +                    return;
  +                }
  +                // Quick and dirty url "parsing".  Assume that a
  +                // URI-SPECIFICATION must be the only component of a
  +                // property value expression
  +                if (currentTokenValue.equals("url")
  +                    && expr.charAt(exprIndex) == '(') {
  +                    if (! scanUrl()) {
  +                        throw new PropertyException
  +                                ("Invalid url expression :" +
  +                                 expr.substring(exprIndex));
  +                    }
  +                    currentToken = URI;
  +                    return;
  +                }
  +                if (currentTokenValue.equals("content-type")) {
  +                    // content-type attribute value.  Must be followed
  +                    // by a mime type
  +                    if (expr.charAt(exprIndex) == ':') {
  +                        int mimeptr = ++exprIndex;
  +                        scanMimeType();
  +                        currentToken = MIMETYPE;
  +                        currentTokenValue =
  +                                expr.substring(mimeptr, exprIndex);
  +                        return;
  +                    }
  +                    // else it's just a name
  +                }
  +                if (currentTokenValue.equals("namespace-prefix")) {
  +                    // content-type attribute value.  Must be followed
  +                    // by a declared namespace-prefix or null
  +                    if (expr.charAt(exprIndex) == ':') {
  +                        int nsptr = ++exprIndex;
  +                        scanName();   // Allowed to be empty
  +                        currentToken = NCNAME;
  +                        currentTokenValue =
  +                                expr.substring(nsptr, exprIndex);
  +                        return;
  +                    }
  +                    // else it's just a name
  +                }
                   if (followingParen()) {
  -                    currentToken = TOK_FUNCTION_LPAR;
  -                    recognizeOperator = false;
  +                    currentToken = FUNCTION_LPAR;
                   } else {
  -                    currentToken = TOK_NCNAME;
  -                    recognizeOperator = false;
  +                    currentToken = NCNAME;
                   }
                   return;
               }
  @@ -233,6 +329,67 @@
       }
   
       /**
  +     * Attempt to recognize a valid UnitName token in the input expression.
  +     * @return token value appropriate to UnitName: ABSOLUTE_LENGTH,
  +     * RELATIVE_LENGTH or NO_UNIT.
  +     * @exception PropertyException if an NCName not a UnitName recognized.
  +     */
  +    private int scanUnitName() throws PropertyException {
  +        String unit;
  +        currentUnitLength = exprIndex;
  +        scanName();
  +        if ((currentUnitLength = exprIndex - currentUnitLength) > 0) {
  +            unit = expr.substring(currentUnitLength, exprIndex);
  +            if (unit.equals("em")) return RELATIVE_LENGTH;
  +            if (unit.equals("cm")) {
  +                currentUnit = Length.CM;
  +                return ABSOLUTE_LENGTH;
  +            }
  +            if (unit.equals("mm")) {
  +                currentUnit = Length.MM;
  +                return ABSOLUTE_LENGTH;
  +            }
  +            if (unit.equals("in")) {
  +                currentUnit = Length.IN;
  +                return ABSOLUTE_LENGTH;
  +            }
  +            if (unit.equals("pt")) {
  +                currentUnit = Length.PT;
  +                return ABSOLUTE_LENGTH;
  +            }
  +            if (unit.equals("pc")) {
  +                currentUnit = Length.PC;
  +                return ABSOLUTE_LENGTH;
  +            }
  +            if (unit.equals("px")) {
  +                currentUnit = Length.PX;
  +                return ABSOLUTE_LENGTH;
  +            }
  +            if (unit.equals("s")) {
  +                currentUnit = Time.SEC;
  +                return TIME;
  +            }
  +            if (unit.equals("ms")) {
  +                currentUnit = Time.MSEC;
  +                return TIME;
  +            }
  +            if (unit.equals("Hz")) {
  +                currentUnit = Frequency.HZ;
  +                return FREQ;
  +            }
  +            if (unit.equals("kHz")) {
  +                currentUnit = Frequency.KHZ;
  +                return FREQ;
  +            }
  +            // Not a UnitName
  +            throw new PropertyException
  +                    ("NCName following a number is not a UnitName");
  +        } else { // No NCName found
  +            return NO_UNIT;
  +        }
  +    }
  +
  +    /**
        * Attempt to recognize a valid NAME token in the input expression.
        */
       private void scanName() {
  @@ -251,6 +408,15 @@
       }
   
       /**
  +     * Scan to the end of a sequence of whitespace characters in the
  +     * input expression.
  +     */
  +    private void scanWhitespace() {
  +        while (exprIndex < exprLength && isSpace(expr.charAt(exprIndex)))
  +            exprIndex++;
  +    }
  +
  +    /**
        * Attempt to recognize a valid sequence of hexadecimal digits in the
        * input expression.
        */
  @@ -260,7 +426,26 @@
       }
   
       /**
  -     * Return a boolean value indicating whether the following non-whitespace
  +     * Attempt to recognize a mime-type.  Working definition here:
  +     * NCName/NCName (NCName as recognized by scanName()).
  +     */
  +    private void scanMimeType() throws PropertyException {
  +        int part1 = exprIndex;
  +        scanName();
  +        if (part1 != exprIndex) {
  +            if (expr.charAt(exprIndex) == '/') {
  +                int part2 = ++exprIndex;
  +                scanName();
  +                if (part2 != exprIndex)
  +                    return;
  +            }
  +        }
  +        throw new PropertyException("Mime type expected; found:" +
  +                                    expr.substring(part1));
  +    }
  +
  +    /**
  +     * @return a boolean value indicating whether the following non-whitespace
        * character is an opening parenthesis.
        */
       private boolean followingParen() {
  @@ -281,6 +466,30 @@
           return false;
       }
   
  +    /**
  +     * Primitive URI extractor.  Assumes that the only contents of a
  +     * URI-SPECIFICATION property type is a complete uri-specification.
  +     * No checking is done on the syntactical validity of the URI.
  +     * @return a boolean indicating whether the remainder of the
  +     * characters form the body of a <tt>url(...)</tt> specification.
  +     * As a side-effect, sets the <tt>protected</tt> field <i>uri</i>
  +     * and sets <i>exprIndex</i> past the end of the expression, when
  +     * returning a <tt>true</tt> value.
  +     */
  +    private boolean scanUrl() {
  +        char ch;
  +        String str = expr.substring(exprIndex).trim();
  +        if (str.charAt(str.length() - 1) != ')') return false;
  +        // Remove closing parenthesis and trim
  +        str = str.substring(0, str.length() - 1).trim();
  +        if ((ch = str.charAt(0)) == '"' || ch == '\'') {
  +            if (str.charAt(str.length() - 1) != ch) return false;
  +            str = str.substring(1, str.length() - 1);
  +        }
  +        uri = str.trim();
  +        exprIndex = expr.length();
  +        return true;
  +    }
   
       static private final String nameStartChars =
           "_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
  
  
  

---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to