pbwest      2003/07/05 12:14:07

  Added:       src/java/org/apache/fop/fo/expr Tag: FOP_0-20-0_Alt-Design
                        package.html PropertyException.java
                        PropertyNotImplementedException.java
                        DataTypeNotImplementedException.java
                        PropertyTokenizer.java PropertyParser.java
                        SystemFontFunction.java
                        FunctionNotImplementedException.java
  Log:
  Moved from src to src/java.
  
  Revision  Changes    Path
  No                   revision
  
  
  No                   revision
  
  
  1.1.2.1   +6 -0      xml-fop/src/java/org/apache/fop/fo/expr/Attic/package.html
  
  
  
  
  1.1.2.1   +34 -23    xml-fop/src/java/org/apache/fop/fo/expr/PropertyException.java
  
  Index: PropertyException.java
  ===================================================================
  RCS file: /home/cvs/xml-fop/src/java/org/apache/fop/fo/expr/PropertyException.java,v
  retrieving revision 1.1
  retrieving revision 1.1.2.1
  diff -u -r1.1 -r1.1.2.1
  --- PropertyException.java    11 Mar 2003 13:05:26 -0000      1.1
  +++ PropertyException.java    5 Jul 2003 19:14:06 -0000       1.1.2.1
  @@ -1,7 +1,8 @@
   /*
    * $Id$
  + * 
    * ============================================================================
  - *                    The Apache Software License, Version 1.1
  + *                   The Apache Software License, Version 1.1
    * ============================================================================
    * 
    * Copyright (C) 1999-2003 The Apache Software Foundation. All rights reserved.
  @@ -9,7 +10,7 @@
    * Redistribution and use in source and binary forms, with or without modifica-
    * tion, are permitted provided that the following conditions are met:
    * 
  - * 1. Redistributions of source code must retain the above copyright notice,
  + * 1. Redistributions of  source code must  retain the above copyright  notice,
    *    this list of conditions and the following disclaimer.
    * 
    * 2. Redistributions in binary form must reproduce the above copyright notice,
  @@ -17,42 +18,52 @@
    *    and/or other materials provided with the distribution.
    * 
    * 3. The end-user documentation included with the redistribution, if any, must
  - *    include the following acknowledgment: "This product includes software
  - *    developed by the Apache Software Foundation (http://www.apache.org/)."
  - *    Alternately, this acknowledgment may appear in the software itself, if
  + *    include  the following  acknowledgment:  "This product includes  software
  + *    developed  by the  Apache Software Foundation  (http://www.apache.org/)."
  + *    Alternately, this  acknowledgment may  appear in the software itself,  if
    *    and wherever such third-party acknowledgments normally appear.
    * 
  - * 4. The names "FOP" and "Apache Software Foundation" must not be used to
  - *    endorse or promote products derived from this software without prior
  + * 4. The names "FOP" and  "Apache Software Foundation"  must not be used to
  + *    endorse  or promote  products derived  from this  software without  prior
    *    written permission. For written permission, please contact
    *    [EMAIL PROTECTED]
    * 
  - * 5. Products derived from this software may not be called "Apache", nor may
  - *    "Apache" appear in their name, without prior written permission of the
  + * 5. Products  derived from this software may not  be called "Apache", nor may
  + *    "Apache" appear  in their name,  without prior written permission  of the
    *    Apache Software Foundation.
    * 
    * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
    * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
  - * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
  - * APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
  - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLU-
  - * DING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
  - * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
  - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  + * FITNESS  FOR A PARTICULAR  PURPOSE ARE  DISCLAIMED.  IN NO  EVENT SHALL  THE
  + * APACHE SOFTWARE  FOUNDATION  OR ITS CONTRIBUTORS  BE LIABLE FOR  ANY DIRECT,
  + * INDIRECT, INCIDENTAL, SPECIAL,  EXEMPLARY, OR CONSEQUENTIAL  DAMAGES (INCLU-
  + * DING, BUT NOT LIMITED TO, PROCUREMENT  OF SUBSTITUTE GOODS OR SERVICES; LOSS
  + * OF USE, DATA, OR  PROFITS; OR BUSINESS  INTERRUPTION)  HOWEVER CAUSED AND ON
  + * ANY  THEORY OF LIABILITY,  WHETHER  IN CONTRACT,  STRICT LIABILITY,  OR TORT
  + * (INCLUDING  NEGLIGENCE OR  OTHERWISE) ARISING IN  ANY WAY OUT OF THE  USE OF
    * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  - * ============================================================================
    * 
  - * This software consists of voluntary contributions made by many individuals
  - * on behalf of the Apache Software Foundation and was originally created by
  - * James Tauber <[EMAIL PROTECTED]>. For more information on the Apache
  + * This software  consists of voluntary contributions made  by many individuals
  + * on  behalf of the Apache Software  Foundation and was  originally created by
  + * James Tauber <[EMAIL PROTECTED]>. For more  information on the Apache 
    * Software Foundation, please see <http://www.apache.org/>.
  - */ 
  + *  
  + */
  +
   package org.apache.fop.fo.expr;
   
  -public class PropertyException extends Exception {
  +import org.apache.fop.apps.FOPException;
  +
  +public class PropertyException extends FOPException {
  +    private static final String tag = "$Name$";
  +    private static final String revision = "$Revision$";
  +
       public PropertyException(String detail) {
           super(detail);
  +    }
  +
  +    public PropertyException(Throwable e) {
  +        super(e);
       }
   
   }
  
  
  
  1.1.2.1   +67 -0     
xml-fop/src/java/org/apache/fop/fo/expr/Attic/PropertyNotImplementedException.java
  
  
  
  
  1.1.2.1   +67 -0     
xml-fop/src/java/org/apache/fop/fo/expr/Attic/DataTypeNotImplementedException.java
  
  
  
  
  1.1.2.1   +357 -134  xml-fop/src/java/org/apache/fop/fo/expr/PropertyTokenizer.java
  
  Index: PropertyTokenizer.java
  ===================================================================
  RCS file: /home/cvs/xml-fop/src/java/org/apache/fop/fo/expr/PropertyTokenizer.java,v
  retrieving revision 1.1
  retrieving revision 1.1.2.1
  diff -u -r1.1 -r1.1.2.1
  --- PropertyTokenizer.java    11 Mar 2003 13:05:26 -0000      1.1
  +++ PropertyTokenizer.java    5 Jul 2003 19:14:07 -0000       1.1.2.1
  @@ -1,7 +1,8 @@
   /*
    * $Id$
  + * 
    * ============================================================================
  - *                    The Apache Software License, Version 1.1
  + *                   The Apache Software License, Version 1.1
    * ============================================================================
    * 
    * Copyright (C) 1999-2003 The Apache Software Foundation. All rights reserved.
  @@ -9,7 +10,7 @@
    * Redistribution and use in source and binary forms, with or without modifica-
    * tion, are permitted provided that the following conditions are met:
    * 
  - * 1. Redistributions of source code must retain the above copyright notice,
  + * 1. Redistributions of  source code must  retain the above copyright  notice,
    *    this list of conditions and the following disclaimer.
    * 
    * 2. Redistributions in binary form must reproduce the above copyright notice,
  @@ -17,40 +18,43 @@
    *    and/or other materials provided with the distribution.
    * 
    * 3. The end-user documentation included with the redistribution, if any, must
  - *    include the following acknowledgment: "This product includes software
  - *    developed by the Apache Software Foundation (http://www.apache.org/)."
  - *    Alternately, this acknowledgment may appear in the software itself, if
  + *    include  the following  acknowledgment:  "This product includes  software
  + *    developed  by the  Apache Software Foundation  (http://www.apache.org/)."
  + *    Alternately, this  acknowledgment may  appear in the software itself,  if
    *    and wherever such third-party acknowledgments normally appear.
    * 
  - * 4. The names "FOP" and "Apache Software Foundation" must not be used to
  - *    endorse or promote products derived from this software without prior
  + * 4. The names "FOP" and  "Apache Software Foundation"  must not be used to
  + *    endorse  or promote  products derived  from this  software without  prior
    *    written permission. For written permission, please contact
    *    [EMAIL PROTECTED]
    * 
  - * 5. Products derived from this software may not be called "Apache", nor may
  - *    "Apache" appear in their name, without prior written permission of the
  + * 5. Products  derived from this software may not  be called "Apache", nor may
  + *    "Apache" appear  in their name,  without prior written permission  of the
    *    Apache Software Foundation.
    * 
    * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
    * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
  - * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
  - * APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
  - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLU-
  - * DING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
  - * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
  - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  + * FITNESS  FOR A PARTICULAR  PURPOSE ARE  DISCLAIMED.  IN NO  EVENT SHALL  THE
  + * APACHE SOFTWARE  FOUNDATION  OR ITS CONTRIBUTORS  BE LIABLE FOR  ANY DIRECT,
  + * INDIRECT, INCIDENTAL, SPECIAL,  EXEMPLARY, OR CONSEQUENTIAL  DAMAGES (INCLU-
  + * DING, BUT NOT LIMITED TO, PROCUREMENT  OF SUBSTITUTE GOODS OR SERVICES; LOSS
  + * OF USE, DATA, OR  PROFITS; OR BUSINESS  INTERRUPTION)  HOWEVER CAUSED AND ON
  + * ANY  THEORY OF LIABILITY,  WHETHER  IN CONTRACT,  STRICT LIABILITY,  OR TORT
  + * (INCLUDING  NEGLIGENCE OR  OTHERWISE) ARISING IN  ANY WAY OUT OF THE  USE OF
    * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  - * ============================================================================
    * 
  - * This software consists of voluntary contributions made by many individuals
  - * on behalf of the Apache Software Foundation and was originally created by
  - * James Tauber <[EMAIL PROTECTED]>. For more information on the Apache
  + * This software  consists of voluntary contributions made  by many individuals
  + * on  behalf of the Apache Software  Foundation and was  originally created by
  + * James Tauber <[EMAIL PROTECTED]>. For more  information on the Apache 
    * Software Foundation, please see <http://www.apache.org/>.
  - */ 
  -package org.apache.fop.fo.expr;
  + *  
  + */
   
  +package org.apache.fop.fo.expr;
   
  +import org.apache.fop.datatypes.Frequency;
  +import org.apache.fop.datatypes.Length;
  +import org.apache.fop.datatypes.Time;
   
   /**
    * Class to tokenize XSL FO property expression.
  @@ -59,44 +63,107 @@
    */
   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;
  -
  -    protected int currentToken = TOK_EOF;
  -    protected String currentTokenValue = null;
  -    protected int currentUnitLength = 0;
  +    private static final String tag = "$Name$";
  +    private static final String revision = "$Revision$";
  +
  +    /*
  +     * 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
  +              ,SLASH = 27
  +            // NO_UNIT is a transient token for internal use only.  It is
  +            // never set as the end result of parsing a token.
  +            ,NO_UNIT = 28
  +            //,NSPREFIX = 29
  +            //,WHITESPACE = 30
  +                     ;
  +
  +    /*
  +     * Absolute unit type constants
  +     */
  +    int currentToken = EOF;
  +    String currentTokenValue = null;
  +    protected int currentUnitIndex = 0;
  +    protected int currentUnit;
  +    protected String unitString;
  +    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() {}
  +
  +    /**
  +     * 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.
  +     */
  +    protected void initialize(int property, String s) {
  +        expr = s;
  +        exprLength = s.length();
  +        this.property = property;
  +        //System.out.println("-----Tokenizer initialized: " + expr);
  +    }
   
  +    /**
  +     * 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;
  +        //System.out.println("-----Tokenizer reset.");
  +    }
   
       /**
  -     * Construct a new PropertyTokenizer object to tokenize the passed
  -     * String.
  -     * @param s The Property expressio to tokenize.
  +     * Get the current expression
  +     * @return - the expression.
        */
  -    PropertyTokenizer(String s) {
  -        this.expr = s;
  -        this.exprLength = s.length();
  +    public String getExpr() {
  +        return expr;
       }
   
       /**
  @@ -104,19 +171,18 @@
        * 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 {
  +        //System.out.println("expr:" + expr + ":  exprIndex: " + exprIndex);
           currentTokenValue = null;
           currentTokenStartIndex = exprIndex;
  -        boolean currentMaybeOperator = recognizeOperator;
           boolean bSawDecimal;
  -        recognizeOperator = true;
  -        for (; ;) {
  +        for (; ; ) {
               if (exprIndex >= exprLength) {
  -                currentToken = TOK_EOF;
  +                currentToken = EOF;
                   return;
               }
               char c = expr.charAt(exprIndex++);
  @@ -125,26 +191,30 @@
               case '\t':
               case '\r':
               case '\n':
  +                // 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;
                   currentTokenStartIndex = exprIndex;
                   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 '\'':
  @@ -155,19 +225,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':
  @@ -188,22 +249,21 @@
                           exprIndex++;
                           scanDigits();
                       }
  -                } else {
  +                } else
                       bSawDecimal = false;
  -                }
  +                currentUnitIndex = exprIndex;
                   if (exprIndex < exprLength && expr.charAt(exprIndex) == '%') {
  +                    currentToken = PERCENT;
  +                    unitString = "%";
                       exprIndex++;
  -                    currentToken = TOK_PERCENT;
                   } 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);
  +                                                   currentUnitIndex);
                   return;
   
               case '.':
  @@ -211,20 +271,19 @@
                           && isDigit(expr.charAt(exprIndex))) {
                       ++exprIndex;
                       scanDigits();
  +                    currentUnitIndex = exprIndex;
                       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);
  +                                                       currentUnitIndex);
                       return;
                   }
                   throw new PropertyException("illegal character '.'");
  @@ -232,46 +291,102 @@
               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;
  +                    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 '#'");
                   }
   
  +            case '/':
  +                currentToken = SLASH;
  +                return;
  +
               default:
                   --exprIndex;
                   scanName();
  -                if (exprIndex == currentTokenStartIndex) {
  -                    throw new PropertyException("illegal character");
  -                }
  +                if (exprIndex == currentTokenStartIndex)
  +                    // 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;
  +                }
  +                if (currentTokenValue.equals("div")) {
  +                    currentToken = DIV;
  +                    return;
  +                }
  +                if (currentTokenValue.equals("inherit")) {
  +                    currentToken = INHERIT;
                       return;
  -                } else if (currentTokenValue.equals("div")) {
  -                    currentToken = TOK_DIV;
  +                }
  +                if (currentTokenValue.equals("auto")) {
  +                    currentToken = AUTO;
  +                    return;
  +                }
  +                if (currentTokenValue.equals("none")) {
  +                    currentToken = NONE;
  +                    return;
  +                }
  +                if (currentTokenValue.equals("true")
  +                    || currentTokenValue.equals("false")) {
  +                    currentToken = BOOL;
                       return;
                   }
  -                /*
  -                 * else
  -                 * throw new PropertyException("unrecognized operator name");
  -                 * recognizeOperator = false;
  -                 * 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;
               }
  @@ -279,37 +394,122 @@
       }
   
       /**
  +     * 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 {
  +        currentUnitIndex = exprIndex;
  +        scanName();
  +        if (currentUnitIndex < exprIndex) {
  +            unitString = expr.substring(currentUnitIndex, exprIndex);
  +            if (unitString.equals("em")) return RELATIVE_LENGTH;
  +            if (unitString.equals("cm")) {
  +                currentUnit = Length.CM;
  +                return ABSOLUTE_LENGTH;
  +            }
  +            if (unitString.equals("mm")) {
  +                currentUnit = Length.MM;
  +                return ABSOLUTE_LENGTH;
  +            }
  +            if (unitString.equals("in")) {
  +                currentUnit = Length.IN;
  +                return ABSOLUTE_LENGTH;
  +            }
  +            if (unitString.equals("pt")) {
  +                currentUnit = Length.PT;
  +                return ABSOLUTE_LENGTH;
  +            }
  +            if (unitString.equals("pc")) {
  +                currentUnit = Length.PC;
  +                return ABSOLUTE_LENGTH;
  +            }
  +            if (unitString.equals("px")) {
  +                currentUnit = Length.PX;
  +                return ABSOLUTE_LENGTH;
  +            }
  +            if (unitString.equals("s")) {
  +                currentUnit = Time.SEC;
  +                return TIME;
  +            }
  +            if (unitString.equals("ms")) {
  +                currentUnit = Time.MSEC;
  +                return TIME;
  +            }
  +            if (unitString.equals("Hz")) {
  +                currentUnit = Frequency.HZ;
  +                return FREQ;
  +            }
  +            if (unitString.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() {
  -        if (exprIndex < exprLength && isNameStartChar(expr.charAt(exprIndex))) {
  +        if (exprIndex < exprLength && isNameStartChar(expr.charAt(exprIndex)))
               while (++exprIndex < exprLength
  -                   && isNameChar(expr.charAt(exprIndex))) { }
  -        }
  +                   && isNameChar(expr.charAt(exprIndex)));
       }
   
       /**
  -     * Attempt to recognize a valid sequence of decimal DIGITS in the
  +     * Attempt to recognize a valid sequence of decimal digits in the
        * input expression.
        */
       private void scanDigits() {
  -        while (exprIndex < exprLength && isDigit(expr.charAt(exprIndex))) {
  +        while (exprIndex < exprLength && isDigit(expr.charAt(exprIndex)))
               exprIndex++;
  -        }
       }
   
       /**
  -     * Attempt to recognize a valid sequence of hexadecimal DIGITS in the
  +     * 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.
        */
       private void scanHexDigits() {
  -        while (exprIndex < exprLength && isHexDigit(expr.charAt(exprIndex))) {
  +        while (exprIndex < exprLength && isHexDigit(expr.charAt(exprIndex)))
               exprIndex++;
  +    }
  +
  +    /**
  +     * 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
  +     * @return a boolean value indicating whether the following non-whitespace
        * character is an opening parenthesis.
        */
       private boolean followingParen() {
  @@ -330,12 +530,36 @@
           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;
  +    }
   
  -    private static final String NAME_START_CHARS =
  +    static private final String nameStartChars =
           "_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
  -    private static final String NAME_CHARS = ".-0123456789";
  -    private static final String DIGITS = "0123456789";
  -    private static final String HEX_CHARS = DIGITS + "abcdefABCDEF";
  +    static private final String nameChars = ".-0123456789";
  +    static private final String digits = "0123456789";
  +    static private final String hexchars = digits + "abcdefABCDEF";
   
       /**
        * Return a boolean value indicating whether the argument is a
  @@ -343,7 +567,7 @@
        * @param c The character to check
        */
       private static final boolean isDigit(char c) {
  -        return DIGITS.indexOf(c) >= 0;
  +        return digits.indexOf(c) >= 0;
       }
   
       /**
  @@ -352,7 +576,7 @@
        * @param c The character to check
        */
       private static final boolean isHexDigit(char c) {
  -        return HEX_CHARS.indexOf(c) >= 0;
  +        return hexchars.indexOf(c) >= 0;
       }
   
       /**
  @@ -377,7 +601,7 @@
        * @param c The character to check
        */
       private static final boolean isNameStartChar(char c) {
  -        return NAME_START_CHARS.indexOf(c) >= 0 || c >= 0x80;
  +        return nameStartChars.indexOf(c) >= 0 || c >= 0x80;
       }
   
       /**
  @@ -386,9 +610,8 @@
        * @param c The character to check
        */
       private static final boolean isNameChar(char c) {
  -        return NAME_START_CHARS.indexOf(c) >= 0 || NAME_CHARS.indexOf(c) >= 0
  +        return nameStartChars.indexOf(c) >= 0 || nameChars.indexOf(c) >= 0
                  || c >= 0x80;
       }
   
   }
  -
  
  
  
  1.1.2.1   +839 -343  xml-fop/src/java/org/apache/fop/fo/expr/PropertyParser.java
  
  Index: PropertyParser.java
  ===================================================================
  RCS file: /home/cvs/xml-fop/src/java/org/apache/fop/fo/expr/PropertyParser.java,v
  retrieving revision 1.1
  retrieving revision 1.1.2.1
  diff -u -r1.1 -r1.1.2.1
  --- PropertyParser.java       11 Mar 2003 13:05:26 -0000      1.1
  +++ PropertyParser.java       5 Jul 2003 19:14:07 -0000       1.1.2.1
  @@ -1,7 +1,8 @@
   /*
    * $Id$
  + * 
    * ============================================================================
  - *                    The Apache Software License, Version 1.1
  + *                   The Apache Software License, Version 1.1
    * ============================================================================
    * 
    * Copyright (C) 1999-2003 The Apache Software Foundation. All rights reserved.
  @@ -9,7 +10,7 @@
    * Redistribution and use in source and binary forms, with or without modifica-
    * tion, are permitted provided that the following conditions are met:
    * 
  - * 1. Redistributions of source code must retain the above copyright notice,
  + * 1. Redistributions of  source code must  retain the above copyright  notice,
    *    this list of conditions and the following disclaimer.
    * 
    * 2. Redistributions in binary form must reproduce the above copyright notice,
  @@ -17,181 +18,364 @@
    *    and/or other materials provided with the distribution.
    * 
    * 3. The end-user documentation included with the redistribution, if any, must
  - *    include the following acknowledgment: "This product includes software
  - *    developed by the Apache Software Foundation (http://www.apache.org/)."
  - *    Alternately, this acknowledgment may appear in the software itself, if
  + *    include  the following  acknowledgment:  "This product includes  software
  + *    developed  by the  Apache Software Foundation  (http://www.apache.org/)."
  + *    Alternately, this  acknowledgment may  appear in the software itself,  if
    *    and wherever such third-party acknowledgments normally appear.
    * 
  - * 4. The names "FOP" and "Apache Software Foundation" must not be used to
  - *    endorse or promote products derived from this software without prior
  + * 4. The names "FOP" and  "Apache Software Foundation"  must not be used to
  + *    endorse  or promote  products derived  from this  software without  prior
    *    written permission. For written permission, please contact
    *    [EMAIL PROTECTED]
    * 
  - * 5. Products derived from this software may not be called "Apache", nor may
  - *    "Apache" appear in their name, without prior written permission of the
  + * 5. Products  derived from this software may not  be called "Apache", nor may
  + *    "Apache" appear  in their name,  without prior written permission  of the
    *    Apache Software Foundation.
    * 
    * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
    * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
  - * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
  - * APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
  - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLU-
  - * DING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
  - * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
  - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  + * FITNESS  FOR A PARTICULAR  PURPOSE ARE  DISCLAIMED.  IN NO  EVENT SHALL  THE
  + * APACHE SOFTWARE  FOUNDATION  OR ITS CONTRIBUTORS  BE LIABLE FOR  ANY DIRECT,
  + * INDIRECT, INCIDENTAL, SPECIAL,  EXEMPLARY, OR CONSEQUENTIAL  DAMAGES (INCLU-
  + * DING, BUT NOT LIMITED TO, PROCUREMENT  OF SUBSTITUTE GOODS OR SERVICES; LOSS
  + * OF USE, DATA, OR  PROFITS; OR BUSINESS  INTERRUPTION)  HOWEVER CAUSED AND ON
  + * ANY  THEORY OF LIABILITY,  WHETHER  IN CONTRACT,  STRICT LIABILITY,  OR TORT
  + * (INCLUDING  NEGLIGENCE OR  OTHERWISE) ARISING IN  ANY WAY OUT OF THE  USE OF
    * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  - * ============================================================================
    * 
  - * This software consists of voluntary contributions made by many individuals
  - * on behalf of the Apache Software Foundation and was originally created by
  - * James Tauber <[EMAIL PROTECTED]>. For more information on the Apache
  + * This software  consists of voluntary contributions made  by many individuals
  + * on  behalf of the Apache Software  Foundation and was  originally created by
  + * James Tauber <[EMAIL PROTECTED]>. For more  information on the Apache 
    * Software Foundation, please see <http://www.apache.org/>.
  - */ 
  + *  
  + */
  +
   package org.apache.fop.fo.expr;
   
  +import org.apache.fop.datatypes.Angle;
  +import org.apache.fop.datatypes.Auto;
  +import org.apache.fop.datatypes.Bool;
   import org.apache.fop.datatypes.ColorType;
  -import org.apache.fop.datatypes.FixedLength;
  +import org.apache.fop.datatypes.Ems;
  +import org.apache.fop.datatypes.Frequency;
  +import org.apache.fop.datatypes.IntegerType;
   import org.apache.fop.datatypes.Length;
  -import org.apache.fop.datatypes.PercentBase;
  -import org.apache.fop.datatypes.PercentLength;
  -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 java.util.HashMap;
  +import org.apache.fop.datatypes.Literal;
  +import org.apache.fop.datatypes.MimeType;
  +import org.apache.fop.datatypes.NCName;
  +import org.apache.fop.datatypes.None;
  +import org.apache.fop.datatypes.Numeric;
  +import org.apache.fop.datatypes.Percentage;
  +import org.apache.fop.datatypes.PropertyValue;
  +import org.apache.fop.datatypes.PropertyValueList;
  +import org.apache.fop.datatypes.Slash;
  +import org.apache.fop.datatypes.StringType;
  +import org.apache.fop.datatypes.Time;
  +import org.apache.fop.datatypes.UriType;
  +import org.apache.fop.datatypes.indirect.FromNearestSpecified;
  +import org.apache.fop.datatypes.indirect.FromParent;
  +import org.apache.fop.datatypes.indirect.Inherit;
  +import org.apache.fop.datatypes.indirect.InheritedValue;
  +import org.apache.fop.fo.FONode;
  +import org.apache.fop.fo.FOTree;
  +import org.apache.fop.fo.PropNames;
  +import org.apache.fop.fo.PropertyConsts;
  +import org.apache.fop.fo.properties.Property;
   
   /**
    * 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
  -
  -    private static final String RELUNIT = "em";
  -    private static final Numeric NEGATIVE_ONE = new Numeric(new Double(-1.0));
  -    private static final HashMap FUNCTION_TABLE = new HashMap();
  -
  -    static {
  -        // Initialize the HashMap of XSL-defined functions
  -        FUNCTION_TABLE.put("ceiling", new CeilingFunction());
  -        FUNCTION_TABLE.put("floor", new FloorFunction());
  -        FUNCTION_TABLE.put("round", new RoundFunction());
  -        FUNCTION_TABLE.put("min", new MinFunction());
  -        FUNCTION_TABLE.put("max", new MaxFunction());
  -        FUNCTION_TABLE.put("abs", new AbsFunction());
  -        FUNCTION_TABLE.put("rgb", new RGBColorFunction());
  -        FUNCTION_TABLE.put("from-table-column", new FromTableColumnFunction());
  -        FUNCTION_TABLE.put("inherited-property-value",
  -                          new InheritedPropFunction());
  -        FUNCTION_TABLE.put("from-parent", new FromParentFunction());
  -        FUNCTION_TABLE.put("from-nearest-specified-value",
  -                          new NearestSpecPropFunction());
  -        FUNCTION_TABLE.put("proportional-column-width",
  -                          new PPColWidthFunction());
  -        FUNCTION_TABLE.put("label-end", new LabelEndFunction());
  -        FUNCTION_TABLE.put("body-start", new BodyStartFunction());
  -        // NOTE: used from code generated for corresponding properties
  -        FUNCTION_TABLE.put("_fop-property-value", new FopPropValFunction());
  -
  -        /**
  -         * * NOT YET IMPLEMENTED!!!
  -         * FUNCTION_TABLE.put("icc-color", new ICCcolorFunction());
  -         * FUNCTION_TABLE.put("system-color", new SystemColorFunction());
  -         * FUNCTION_TABLE.put("system-font", new SystemFontFunction());
  -         *
  -         * FUNCTION_TABLE.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 static final String tag = "$Name$";
  +    private static final String revision = "$Revision$";
   
  -    /**
  -     * 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;
  +    /** The FO tree which has initiated this parser */
  +    private FOTree foTree;
  +    /** The FONode which has initiated this parser */
  +    private FONode node;
  +
  +    public PropertyParser(FOTree foTree) {
  +        super();
  +        this.foTree = foTree;
       }
   
       /**
        * Parse the property expression described in the instance variables.
  -     * Note: If the property expression String is empty, a StringProperty
  +     * 
  +     * <p>The <tt>PropertyValue</tt> returned by this function has the
  +     * following characteristics:
  +     * If the expression resolves to a single element that object is returned
  +     * directly in an object which implements <PropertyValue</tt>.
  +     *
  +     * <p>If the expression cannot be resolved into a single object, the set
  +     * to which it resolves is returned in a <tt>PropertyValueList</tt> object
  +     * (which itself implements <tt>PropertyValue</tt>).
  +     *
  +     * <p>The <tt>PropertyValueList</tt> contains objects whose corresponding
  +     * elements in the original expression were separated by <em>commas</em>.
  +     *
  +     * <p>Objects whose corresponding elements in the original expression
  +     * were separated by spaces are composed into a sublist contained in
  +     * another <tt>PropertyValueList</tt>.  If all of the elements in the
  +     * expression were separated by spaces, the returned
  +     * <tt>PropertyValueList</tt> will contain one element, a
  +     * <tt>PropertyValueList</tt> containing objects representing each of
  +     * the space-separated elements in the original expression.
  +     *
  +     * <p>E.g., if a <b>font-family</b> property is assigned the string
  +     * <em>Palatino, New Century Schoolbook, serif</em>, the returned value
  +     * will look like this:
  +     * <pre>
  +     * PropertyValueList(NCName('Palatino')
  +     *                   PropertyValueList(NCName('New')
  +     *                                     NCName('Century')
  +     *                                     NCName('Schoolbook') )
  +     *                   NCName('serif') )
  +     * </pre>
  +     * <p>If the property had been assigned the string
  +     * <em>Palatino, "New Century Schoolbook", serif</em>, the returned value
  +     * would look like this:
  +     * <pre>
  +     * PropertyValueList(NCName('Palatino')
  +     *                   NCName('New Century Schoolbook')
  +     *                   NCName('serif') )
  +     * </pre>
  +     * <p>If a <b>background-position</b> property is assigned the string
  +     * <em>top center</em>, the returned value will look like this:
  +     * <pre>
  +     * PropertyValueList(PropertyValueList(NCName('top')
  +     *                                     NCName('center') ) )
  +     * </pre>
  +     *
  +     * <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 node - the <tt>FONode</tt> for which the property expression
  +     * is being resolved.
  +     * @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(FONode node, int property, String expr)
  +        throws PropertyException
  +    {
  +        //System.out.println("-----Entering parse:"
  +        // + PropNames.getPropertyName(property) + " " + expr);
  +        synchronized (this) {
  +            // make sure this parser is available
  +            if (getExpr() != null) // the parser is currently active
  +                throw new PropertyException
  +                        ("PropertyParser is currently active: " + getExpr());
  +            initialize(property, expr);
  +            this.node = node;
           }
  -        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);
  +                    reset();
                       return propList;
  -                } else {
  +                } else { // list is empty
  +                    reset();
                       return prop;
                   }
  -            } else {
  -                if (propList == null) {
  -                    propList = new ListProperty(prop);
  -                } else {
  -                    propList.addProperty(prop);
  +            }
  +            // 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) {
  +                    reset();
  +                    return propList;
                   }
               }
  -            // throw new PropertyException("unexpected token");
           }
  -        // return prop;
  +    }
  +
  +    /**
  +     * <p>Parse a property values sublist - a list of whitespace separated
  +     * <tt>PropertyValue</tt>s.
  +     * <p>
  +     * Property value expressions for various properties may contain 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();
  +        }
  +    }
  +
  +    /**
  +     * Generate an arithmetic error string.
  +     * @return arithmetic error message.
  +     */
  +    private String arithErrorStr() {
  +        return "Arithmetic operator not followed by Numeric or integer: "
  +                + getExpr();
  +    }
  +
  +
  +    /**
  +     * Generate an function numeric argument error string.
  +     * @return function numeric argument error message.
  +     */
  +    private String funcNumericErrorStr() {
  +        return "Function requires Numeric or integer argument: "
  +                + getExpr();
       }
   
       /**
        * 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();
  -        loop:
  -        while (true) {
  -            switch (currentToken) {
  -            case TOK_PLUS:
  -                next();
  -                prop = evalAddition(prop.getNumeric(),
  -                                    parseMultiplicativeExpr().getNumeric());
  -                break;
  -            case TOK_MINUS:
  -                next();
  -                prop =
  -                    evalSubtraction(prop.getNumeric(),
  -                                    parseMultiplicativeExpr().getNumeric());
  -                break;
  +        //System.out.println("parseAdd");
  +        PropertyValue prop = parseMultiplicativeExpr();
  +        PropertyValue pv;
  +        outer:
  +        for (; ; ) {
  +            inner:
  +            switch (prop.getType()) {
  +            case PropertyValue.NUMERIC: {
  +                switch (currentToken) {
  +                case PLUS:
  +                    next();
  +                    pv = parseMultiplicativeExpr();
  +                    switch (pv.getType()) {
  +                    case PropertyValue.NUMERIC:
  +                        ((Numeric)prop).add((Numeric)pv);
  +                        break inner;
  +                    case PropertyValue.INTEGER:
  +                        ((Numeric)prop).add((double)
  +                                            (((IntegerType)pv).getInt()));
  +                        break inner;
  +                    default:
  +                        throw new PropertyException(arithErrorStr());
  +                    }
  +                case MINUS:
  +                    next();
  +                    pv = parseMultiplicativeExpr();
  +                    switch (pv.getType()) {
  +                    case PropertyValue.NUMERIC:
  +                        ((Numeric)prop).subtract((Numeric)pv);
  +                        break inner;
  +                    case PropertyValue.INTEGER:
  +                        ((Numeric)prop).subtract((double)
  +                                             (((IntegerType)pv).getInt()));
  +                        break inner;
  +                    default:
  +                        throw new PropertyException(arithErrorStr());
  +                    }
  +                default:
  +                    break outer;
  +                }
  +            }
  +            case PropertyValue.INTEGER: {
  +                int intVal = ((IntegerType)prop).getInt();
  +                switch (currentToken) {
  +                case PLUS:
  +                    next();
  +                    pv = parseMultiplicativeExpr();
  +                    switch (pv.getType()) {
  +                    case PropertyValue.NUMERIC:
  +                        prop = ((Numeric)pv).add((double)intVal);
  +                        break inner;
  +                    case PropertyValue.INTEGER:
  +                        ((IntegerType)prop).setInt(intVal +
  +                                            ((IntegerType)pv).getInt());
  +                        break inner;
  +                    default:
  +                        throw new PropertyException(arithErrorStr());
  +                    }
  +                case MINUS:
  +                    next();
  +                    pv = parseMultiplicativeExpr();
  +                    switch (pv.getType()) {
  +                    case PropertyValue.NUMERIC:
  +                        ((Numeric)pv).add((double)(-intVal));
  +                        prop = ((Numeric)pv).negate();
  +                        break inner;
  +                    case PropertyValue.INTEGER:
  +                        ((IntegerType)prop).setInt(intVal +
  +                                            ((IntegerType)pv).getInt());
  +                        break inner;
  +                    default:
  +                        throw new PropertyException(arithErrorStr());
  +                    }
  +                default:
  +                    break outer;
  +                }
  +            }
               default:
  -                break loop;
  +                break outer;
               }
           }
           return prop;
  @@ -199,30 +383,125 @@
   
       /**
        * 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();
  -        loop:
  -        while (true) {
  -            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;
  +    private PropertyValue parseMultiplicativeExpr() throws PropertyException {
  +        //System.out.println("parseMult");
  +        PropertyValue prop = parseUnaryExpr();
  +        PropertyValue pv;
  +        outer:
  +        // Outer loop exists to handle a sequence of multiplicative operations
  +        // e.g. 5 * 4 / 2
  +        // break outer; will terminate the multiplicative expression parsing
  +        // break inner; will look for another trailing multiplicative
  +        // operator.
  +        for (; ; ) {
  +            inner:
  +            switch (prop.getType()) {
  +            case PropertyValue.NUMERIC:
  +                switch (currentToken) {
  +                case DIV:
  +                    next();
  +                    pv = parseUnaryExpr();
  +                    switch (pv.getType()) {
  +                    case PropertyValue.INTEGER:
  +                        ((Numeric)prop).divide
  +                                        ((double)(((IntegerType)pv).getInt()));
  +                        break inner;
  +                    case PropertyValue.NUMERIC:
  +                        ((Numeric)prop).divide((Numeric)pv);
  +                        break inner;
  +                    default:
  +                        throw new PropertyException(arithErrorStr());
  +                    }
  +                case MOD:
  +                    next();
  +                    pv = parseUnaryExpr();
  +                    switch (pv.getType()) {
  +                    case PropertyValue.INTEGER:
  +                        ((Numeric)prop).mod
  +                                        ((double)(((IntegerType)pv).getInt()));
  +                        break inner;
  +                    case PropertyValue.NUMERIC:
  +                        ((Numeric)prop).mod((Numeric)pv);
  +                        break inner;
  +                    default:
  +                        throw new PropertyException(arithErrorStr());
  +                    }
  +                case MULTIPLY:
  +                    next();
  +                    pv = parseUnaryExpr();
  +                    switch (pv.getType()) {
  +                    case PropertyValue.INTEGER:
  +                        ((Numeric)prop).multiply
  +                                        ((double)(((IntegerType)pv).getInt()));
  +                        break inner;
  +                    case PropertyValue.NUMERIC:
  +                        ((Numeric)prop).multiply((Numeric)pv);
  +                        break inner;
  +                    default:
  +                        throw new PropertyException(arithErrorStr());
  +                    }
  +                default:
  +                    break outer;
  +                }
  +                // N.B. The above case cannot fall through to here
  +            case PropertyValue.INTEGER:
  +                // This code treats all multiplicative operations as implicit
  +                // operations on doubles.  It might be reasonable to allow
  +                // an integer multiply.
  +                int intVal = ((IntegerType)prop).getInt();
  +                switch (currentToken) {
  +                case DIV:
  +                    next();
  +                    pv = parseUnaryExpr();
  +                    switch (pv.getType()) {
  +                    case PropertyValue.INTEGER:
  +                        prop = new Numeric(property,
  +                            (double)intVal / ((IntegerType)pv).getInt());
  +                        break inner;
  +                    case PropertyValue.NUMERIC:
  +                        prop = (new Numeric(property, (double)intVal))
  +                                                        .divide((Numeric)pv);
  +                        break inner;
  +                    default:
  +                        throw new PropertyException(arithErrorStr());
  +                    }
  +                case MOD:
  +                    next();
  +                    pv = parseUnaryExpr();
  +                    switch (pv.getType()) {
  +                    case PropertyValue.INTEGER:
  +                        prop = new Numeric(property,
  +                                ((double)intVal) % ((IntegerType)pv).getInt());
  +                        break inner;
  +                    case PropertyValue.NUMERIC:
  +                        prop = (new Numeric(property, (double)intVal))
  +                                                        .mod((Numeric)pv);
  +                        break inner;
  +                    default:
  +                        throw new PropertyException(arithErrorStr());
  +                    }
  +                case MULTIPLY:
  +                    next();
  +                    pv = parseUnaryExpr();
  +                    switch (pv.getType()) {
  +                    case PropertyValue.INTEGER:
  +                        prop = new Numeric(property,
  +                            ((double)intVal) * ((IntegerType)pv).getInt());
  +                        break inner;
  +                    case PropertyValue.NUMERIC:
  +                        prop = (new Numeric(property, (double)intVal))
  +                                                   .multiply((Numeric)pv);
  +                        break inner;
  +                    default:
  +                        throw new PropertyException(arithErrorStr());
  +                    }
  +                default:
  +                    break outer;
  +                }
               default:
  -                break loop;
  +                break outer;
               }
           }
           return prop;
  @@ -230,12 +509,22 @@
   
       /**
        * 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 {
  +        //System.out.println("Unary entry");
  +        if (currentToken == MINUS) {
               next();
  -            return evalNegate(parseUnaryExpr().getNumeric());
  +            PropertyValue pv = parseUnaryExpr();
  +            switch (pv.getType()) {
  +            case PropertyValue.NUMERIC:
  +                return ((Numeric)pv).negate();
  +            case PropertyValue.INTEGER:
  +                ((IntegerType)pv).setInt( -((IntegerType)pv).getInt());
  +                return pv;
  +            default:
  +                throw new PropertyException(arithErrorStr());
  +            }
           }
           return parsePrimaryExpr();
       }
  @@ -246,107 +535,400 @@
        * 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;
  +        //System.out.println("Primary currentToken:" + currentToken + " "
  +        //                   + currentTokenValue);
           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().
               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, Double.parseDouble(currentTokenValue));
               break;
   
  -        case TOK_INTEGER:
  -            prop = new NumberProperty(new Integer(currentTokenValue));
  +        case INTEGER:
  +            prop = new IntegerType
  +                    (property, Integer.parseInt(currentTokenValue));
               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, Double.parseDouble(currentTokenValue));
               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);
  -            }
  +        case ABSOLUTE_LENGTH:
  +            prop = Length.makeLength(property,
  +                              Double.parseDouble(currentTokenValue),
  +                              currentUnit);
  +            break;
  +        case TIME:
  +            prop = new Time(property, currentUnit,
  +                            Double.parseDouble(currentTokenValue));
  +            break;
  +        case FREQ:
  +            prop = new Frequency(property, currentUnit,
  +                                 Double.parseDouble(currentTokenValue));
  +            break;
  +        case ANGLE:
  +            prop = new Angle(property, currentUnit,
  +                             Double.parseDouble(currentTokenValue));
  +            break;
  +        case RELATIVE_LENGTH:
  +            prop = Ems.makeEms(node, property,
  +                               Double.parseDouble(currentTokenValue));
               break;
   
  -        case TOK_COLORSPEC:
  -            prop = new ColorTypeProperty(new ColorType(currentTokenValue));
  +        case COLORSPEC:
  +            prop = new ColorType(property, currentTokenValue);
               break;
   
  -        case TOK_FUNCTION_LPAR: {
  -            Function function =
  -                (Function)FUNCTION_TABLE.get(currentTokenValue);
  -            if (function == null) {
  -                throw new PropertyException("no such function: "
  -                                            + currentTokenValue);
  -            }
  +        case BOOL:
  +            prop = new Bool(property, currentTokenValue);
  +            break;
  +
  +        case AUTO:
  +            prop = new Auto(property);
  +            break;
  +
  +        case NONE:
  +            prop = new None(property);
  +            break;
  +
  +        case INHERIT:
  +            prop = new Inherit(property);
  +            //throw new PropertyException("INHERIT not supported");
  +            break;
  +
  +        case URI:
  +            prop = new UriType(property, currentTokenValue);
  +            break;
  +
  +        case MIMETYPE:
  +            prop = new MimeType(property, currentTokenValue);
  +            break;
  +
  +        case SLASH:
  +            prop = new Slash(property);
  +            break;
  +
  +        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;
  +            int funcType = PropertyValue.NO_TYPE;
  +            String function = currentTokenValue;
               next();
  -            // Push new function (for function context: getPercentBase())
  -            propInfo.pushFunction(function);
  -            prop = function.eval(parseArgs(function.nbArgs()), propInfo);
  -            propInfo.popFunction();
  +            do {
  +                // Numeric functions
  +                if (function.equals("floor")) {
  +                    PropertyValue[] args = parseArgs(1);
  +                    switch (args[0].getType()) {
  +                    case PropertyValue.INTEGER:
  +                        args[0] =
  +                            new Numeric
  +                                (property, ((IntegerType)args[0]).getInt());
  +                    case PropertyValue.NUMERIC:
  +                        prop = new Numeric
  +                                (property, ((Numeric)args[0]).floor());
  +                        break;
  +                    default:
  +                        throw new PropertyException(funcNumericErrorStr());
  +                    }
  +                    break;
  +                }
  +                if (function.equals("ceiling")) {
  +                    PropertyValue[] args = parseArgs(1);
  +                    switch (args[0].getType()) {
  +                    case PropertyValue.INTEGER:
  +                        args[0] =
  +                            new Numeric
  +                                (property, ((IntegerType)args[0]).getInt());
  +                    case PropertyValue.NUMERIC:
  +                        prop = new Numeric
  +                                (property, ((Numeric)args[0]).ceiling());
  +                        break;
  +                    default:
  +                        throw new PropertyException(funcNumericErrorStr());
  +                    }
  +                    break;
  +                }
  +                if (function.equals("round")) {
  +                    PropertyValue[] args = parseArgs(1);
  +                    switch (args[0].getType()) {
  +                    case PropertyValue.INTEGER:
  +                        args[0] =
  +                            new Numeric
  +                                (property, ((IntegerType)args[0]).getInt());
  +                    case PropertyValue.NUMERIC:
  +                        prop = new Numeric
  +                                (property, ((Numeric)args[0]).round());
  +                        break;
  +                    default:
  +                        throw new PropertyException(funcNumericErrorStr());
  +                    }
  +                    break;
  +                }
  +                if (function.equals("min")) {
  +                    PropertyValue[] args = parseArgs(2);
  +                    switch (args[0].getType()) {
  +                    case PropertyValue.INTEGER:
  +                        args[0] =
  +                            new Numeric
  +                                (property, ((IntegerType)args[0]).getInt());
  +                    case PropertyValue.NUMERIC:
  +                        prop = ((Numeric)args[0]).min((Numeric)args[1]);
  +                        break;
  +                    default:
  +                        throw new PropertyException(funcNumericErrorStr());
  +                    }
  +                    break;
  +                }
  +                if (function.equals("max")) {
  +                    PropertyValue[] args = parseArgs(2);
  +                    switch (args[0].getType()) {
  +                    case PropertyValue.INTEGER:
  +                        args[0] =
  +                            new Numeric
  +                                (property, ((IntegerType)args[0]).getInt());
  +                    case PropertyValue.NUMERIC:
  +                        prop = ((Numeric)args[0]).max((Numeric)args[1]);
  +                        break;
  +                    default:
  +                        throw new PropertyException(funcNumericErrorStr());
  +                    }
  +                    break;
  +                }
  +                if (function.equals("abs")) {
  +                    PropertyValue[] args = parseArgs(1);
  +                    switch (args[0].getType()) {
  +                    case PropertyValue.INTEGER:
  +                        args[0] =
  +                            new Numeric
  +                                (property, ((IntegerType)args[0]).getInt());
  +                    case PropertyValue.NUMERIC:
  +                        prop = ((Numeric)args[0]).abs();
  +                        break;
  +                    default:
  +                        throw new PropertyException(funcNumericErrorStr());
  +                    }
  +                    break;
  +                }
  +
  +                // Color functions
  +                if (function.equals("rgb")) {
  +                    // Currently arguments must all be integers.
  +                    PropertyValue[] args = parseArgs(3);
  +                    switch (args[0].getType()) {
  +                    case PropertyValue.INTEGER:
  +                        prop = new ColorType
  +                                (property, ((IntegerType)args[0]).getInt(),
  +                                 ((IntegerType)args[1]).getInt(),
  +                                 ((IntegerType)args[2]).getInt());
  +                        break;
  +                    case PropertyValue.NUMERIC:
  +                        prop = new ColorType
  +                                (property, ((Numeric)args[0]).asInt(),
  +                                 ((Numeric)args[1]).asInt(),
  +                                 ((Numeric)args[2]).asInt());
  +                        break;
  +                    default:
  +                        throw new PropertyException(funcNumericErrorStr());
  +                    }
  +                    break;
  +                }
  +                if (function.equals("rgb-icc")) {
  +                    PropertyValue[] args = parseArgs(6);
  +                    throw new FunctionNotImplementedException("rgb-icc");
  +                    //break;
  +                }
  +                if (function.equals("system-color")) {
  +                    PropertyValue[] args = parseArgs(1);
  +                    prop = new ColorType
  +                            (property, ((StringType)args[0]).getString());
  +                    break;
  +                }
  +
  +                // Font function
  +                if (function.equals("system-font")) {
  +                    PropertyValue[] args = parseArgs(1, 2);
  +                    if (args.length == 1) {
  +                        prop = SystemFontFunction.systemFontCharacteristic
  +                                (property,
  +                                 ((StringType)args[0]).getString());
  +                    } else {
  +                        // 2 args
  +                        prop = SystemFontFunction.systemFontCharacteristic
  +                                (property,
  +                                 ((StringType)args[0]).getString(),
  +                                 ((StringType)args[1]).getString());
  +                    }
  +                    break;
  +                }
  +
  +                // Property value functions
  +                if (function.equals("label-end")) {
  +                    PropertyValue[] args = parseArgs(0);
  +                    throw new FunctionNotImplementedException("label-end");
  +                    //break;
  +                }
  +                if (function.equals("body-start")) {
  +                    PropertyValue[] args = parseArgs(0);
  +                    throw new FunctionNotImplementedException("body-start");
  +                    //break;
  +                }
  +                if (function.equals("inherited-property-value")) {
  +                    int propindex = property;
  +                    PropertyValue[] args = parseArgs(0, 1);
  +                    if (args.length != 0)
  +                        propindex = PropNames.getPropertyIndex(
  +                                ((StringType)args[0]).getString());
  +
  +                    // If it's a compound, return an InheritedValue object
  +                    if (PropertyConsts.pconsts.isCompound(propindex)) {
  +                        prop = new InheritedValue(property, propindex);
  +                        break;
  +                    }
  +                    // Is it an inherited property?
  +                    if (PropertyConsts.pconsts.inheritance(propindex)
  +                                                            == Property.NO)
  +                        throw new PropertyException
  +                                ("inherited-property-value: "
  +                                 + PropNames.getPropertyName(propindex)
  +                                 + " is not inherited.");
  +                    // Not a compound, and inherited - try to resolve it
  +                    prop = node.fromParent(property, propindex);
  +                    break;
  +                }
  +                // N.B. see comments on classes FromNearestSpecified and
  +                // FromParent for explanation of this section
  +                if (function.equals("from-parent"))
  +                    funcType = PropertyValue.FROM_PARENT;
  +                if (function.equals("from-nearest-specified-value"))
  +                    funcType = PropertyValue.FROM_NEAREST_SPECIFIED;
  +                if (funcType == PropertyValue.FROM_PARENT
  +                    || funcType == PropertyValue.FROM_NEAREST_SPECIFIED)
  +                {
  +                    // Preset the return value in case of a shorthand property
  +                    switch (funcType) {
  +                    case PropertyValue.FROM_PARENT:
  +                        prop = new FromParent(property);
  +                    case PropertyValue.FROM_NEAREST_SPECIFIED:
  +                        prop = new FromNearestSpecified(property);
  +                    }
  +
  +                    PropertyValue[] args = parseArgs(0, 1);
  +                    if (args.length == 0) {
  +                        if (! (PropertyConsts.pconsts.isShorthand(property)
  +                           || PropertyConsts.pconsts.isCompound(property))) {
  +                            // develop the function value and return it as
  +                            // a property.
  +                            switch (funcType) {
  +                            case PropertyValue.FROM_PARENT:
  +                                prop = node.fromParent(property);
  +                            case PropertyValue.FROM_NEAREST_SPECIFIED:
  +                                prop = node.fromNearestSpecified(property);
  +                            }
  +                        }
  +                        // else a shorthand/compound - 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
  +                                    (function + " function requires"
  +                                     + " property name arg.");
  +                        // else arg[0] is an NCName
  +                        NCName ncname = (NCName)args[0];
  +                        String propname = ncname.getNCName();
  +                        int nameindex =
  +                                PropNames.getPropertyIndex(propname);
  +                        if (PropertyConsts.pconsts.isShorthand(nameindex)
  +                            || PropertyConsts.pconsts.isCompound(nameindex)) {
  +                            // the argument is a shorthand/compound 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
  +                                        (function +
  +                                         " argument " + propname +
  +                                         " does not match property " +
  +                                         PropNames.getPropertyName(property));
  +                            // else perform shorthand/compound processing
  +                            // i.e. do nothing;
  +                            // prop has been set to the correct
  +                            // pseudo-propertyValue
  +                        }
  +                        else {   // An NCName but not a shorthand/compound
  +                            // Perform normal from-? processing
  +                            switch (funcType) {
  +                            case PropertyValue.FROM_PARENT:
  +                                prop = node.fromParent(property, nameindex);
  +                            case PropertyValue.FROM_NEAREST_SPECIFIED:
  +                                prop = node.fromNearestSpecified
  +                                                        (property, nameindex);
  +                            }
  +                        }
  +                    }
  +                    break;
  +                }
  +                if (function.equals("from-table-column")) {
  +                    PropertyValue[] args = parseArgs(0, 1);
  +                    throw new FunctionNotImplementedException
  +                            ("from-table-column");
  +                    //break;
  +                }
  +                if (function.equals("proportional-column-width")) {
  +                    PropertyValue[] args = parseArgs(1);
  +                    throw new FunctionNotImplementedException
  +                            ("proportional-column-width");
  +                    //break;
  +                }
  +                if (function.equals("merge-property-values")) {
  +                    PropertyValue[] args = parseArgs(0, 1);
  +                    throw new FunctionNotImplementedException
  +                            ("merge-property-values");
  +                    //break;
  +                }
  +                throw new PropertyException("no such function: "
  +                                                        + function);
  +            } while (false);
               return prop;
           }
           default:
  @@ -361,139 +943,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(NEGATIVE_ONE));
  -    }
  -
  -    /**
  -     * 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.1.2.1   +94 -0     
xml-fop/src/java/org/apache/fop/fo/expr/Attic/SystemFontFunction.java
  
  
  
  
  1.1.2.1   +67 -0     
xml-fop/src/java/org/apache/fop/fo/expr/Attic/FunctionNotImplementedException.java
  
  
  
  

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

Reply via email to