KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > fop > fo > expr > PropertyParser


1 /*
2  * $Id: PropertyParser.java,v 1.6.2.4 2003/02/25 12:56:58 jeremias Exp $
3  * ============================================================================
4  * The Apache Software License, Version 1.1
5  * ============================================================================
6  *
7  * Copyright (C) 1999-2003 The Apache Software Foundation. All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without modifica-
10  * tion, are permitted provided that the following conditions are met:
11  *
12  * 1. Redistributions of source code must retain the above copyright notice,
13  * this list of conditions and the following disclaimer.
14  *
15  * 2. Redistributions in binary form must reproduce the above copyright notice,
16  * this list of conditions and the following disclaimer in the documentation
17  * and/or other materials provided with the distribution.
18  *
19  * 3. The end-user documentation included with the redistribution, if any, must
20  * include the following acknowledgment: "This product includes software
21  * developed by the Apache Software Foundation (http://www.apache.org/)."
22  * Alternately, this acknowledgment may appear in the software itself, if
23  * and wherever such third-party acknowledgments normally appear.
24  *
25  * 4. The names "FOP" and "Apache Software Foundation" must not be used to
26  * endorse or promote products derived from this software without prior
27  * written permission. For written permission, please contact
28  * apache@apache.org.
29  *
30  * 5. Products derived from this software may not be called "Apache", nor may
31  * "Apache" appear in their name, without prior written permission of the
32  * Apache Software Foundation.
33  *
34  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
35  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
36  * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
37  * APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
38  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLU-
39  * DING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
40  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
41  * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
42  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
43  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
44  * ============================================================================
45  *
46  * This software consists of voluntary contributions made by many individuals
47  * on behalf of the Apache Software Foundation and was originally created by
48  * James Tauber <jtauber@jtauber.com>. For more information on the Apache
49  * Software Foundation, please see <http://www.apache.org/>.
50  */

51 package org.apache.fop.fo.expr;
52
53 import org.apache.fop.fo.Property;
54 import org.apache.fop.fo.ListProperty;
55 import org.apache.fop.fo.LengthProperty;
56 import org.apache.fop.fo.NumberProperty;
57 import org.apache.fop.fo.StringProperty;
58 import org.apache.fop.fo.ColorTypeProperty;
59 import org.apache.fop.datatypes.*;
60
61 import java.util.HashMap JavaDoc;
62
63 /**
64  * Class to parse XSL FO property expression.
65  * This class is heavily based on the epxression parser in James Clark's
66  * XT, an XSLT processor.
67  */

68 public class PropertyParser extends PropertyTokenizer {
69     private PropertyInfo propInfo; // Maker and propertyList related info
70

71     private static final String JavaDoc RELUNIT = "em";
72     private static final Numeric negOne = new Numeric(new Double JavaDoc(-1.0));
73     private static final HashMap JavaDoc functionTable = new HashMap JavaDoc();
74
75     static {
76         // Initialize the HashMap of XSL-defined functions
77
functionTable.put("ceiling", new CeilingFunction());
78         functionTable.put("floor", new FloorFunction());
79         functionTable.put("round", new RoundFunction());
80         functionTable.put("min", new MinFunction());
81         functionTable.put("max", new MaxFunction());
82         functionTable.put("abs", new AbsFunction());
83         functionTable.put("rgb", new RGBColorFunction());
84         functionTable.put("from-table-column", new FromTableColumnFunction());
85         functionTable.put("inherited-property-value",
86                           new InheritedPropFunction());
87         functionTable.put("from-parent", new FromParentFunction());
88         functionTable.put("from-nearest-specified-value",
89                           new NearestSpecPropFunction());
90         functionTable.put("proportional-column-width",
91                           new PPColWidthFunction());
92         functionTable.put("label-end", new LabelEndFunction());
93         functionTable.put("body-start", new BodyStartFunction());
94         // NOTE: used from code generated for corresponding properties
95
functionTable.put("_fop-property-value", new FopPropValFunction());
96
97         /**
98          * * NOT YET IMPLEMENTED!!!
99          * functionTable.put("rgb-icc", new RgbICCFunction());
100          * functionTable.put("system-color", new SystemColorFunction());
101          * functionTable.put("system-font", new SystemFontFunction());
102          *
103          * functionTable.put("merge-property-values", new MergePropsFunction());
104          */

105     }
106
107
108     /**
109      * Public entrypoint to the Property expression parser.
110      * @param expr The specified value (attribute on the xml element).
111      * @param propInfo A PropertyInfo object representing the context in
112      * which the property expression is to be evaluated.
113      * @return A Property object holding the parsed result.
114      * @throws PropertyException If the "expr" cannot be parsed as a Property.
115      */

116     public static Property parse(String JavaDoc expr, PropertyInfo propInfo)
117             throws PropertyException {
118         return new PropertyParser(expr, propInfo).parseProperty();
119     }
120
121
122     /**
123      * Private constructor. Called by the static parse() method.
124      * @param propExpr The specified value (attribute on the xml element).
125      * @param propInfo A PropertyInfo object representing the context in
126      * which the property expression is to be evaluated.
127      */

128     private PropertyParser(String JavaDoc propExpr, PropertyInfo pInfo) {
129         super(propExpr);
130         this.propInfo = pInfo;
131     }
132
133     /**
134      * Parse the property expression described in the instance variables.
135      * Note: If the property expression String is empty, a StringProperty
136      * object holding an empty String is returned.
137      * @return A Property object holding the parsed result.
138      * @throws PropertyException If the "expr" cannot be parsed as a Property.
139      */

140     private Property parseProperty() throws PropertyException {
141         next();
142         if (currentToken == TOK_EOF) {
143             // if prop value is empty string, force to StringProperty
144
return new StringProperty("");
145         }
146         ListProperty propList = null;
147         while (true) {
148             Property prop = parseAdditiveExpr();
149             if (currentToken == TOK_EOF) {
150                 if (propList != null) {
151                     propList.addProperty(prop);
152                     return propList;
153                 } else
154                     return prop;
155             } else {
156                 if (propList == null) {
157                     propList = new ListProperty(prop);
158                 } else {
159                     propList.addProperty(prop);
160                 }
161             }
162             // throw new PropertyException("unexpected token");
163
}
164         // return prop;
165
}
166
167     /**
168      * Try to parse an addition or subtraction expression and return the
169      * resulting Property.
170      */

171     private Property parseAdditiveExpr() throws PropertyException {
172         // Evaluate and put result on the operand stack
173
Property prop = parseMultiplicativeExpr();
174         loop:
175         for (; ; ) {
176             switch (currentToken) {
177             case TOK_PLUS:
178                 next();
179                 prop = evalAddition(prop.getNumeric(),
180                                     parseMultiplicativeExpr().getNumeric());
181                 break;
182             case TOK_MINUS:
183                 next();
184                 prop =
185                     evalSubtraction(prop.getNumeric(),
186                                     parseMultiplicativeExpr().getNumeric());
187                 break;
188             default:
189                 break loop;
190             }
191         }
192         return prop;
193     }
194
195     /**
196      * Try to parse a multiply, divide or modulo expression and return
197      * the resulting Property.
198      */

199     private Property parseMultiplicativeExpr() throws PropertyException {
200         Property prop = parseUnaryExpr();
201         loop:
202         for (; ; ) {
203             switch (currentToken) {
204             case TOK_DIV:
205                 next();
206                 prop = evalDivide(prop.getNumeric(),
207                                   parseUnaryExpr().getNumeric());
208                 break;
209             case TOK_MOD:
210                 next();
211                 prop = evalModulo(prop.getNumber(),
212                                   parseUnaryExpr().getNumber());
213                 break;
214             case TOK_MULTIPLY:
215                 next();
216                 prop = evalMultiply(prop.getNumeric(),
217                                     parseUnaryExpr().getNumeric());
218                 break;
219             default:
220                 break loop;
221             }
222         }
223         return prop;
224     }
225
226     /**
227      * Try to parse a unary minus expression and return the
228      * resulting Property.
229      */

230     private Property parseUnaryExpr() throws PropertyException {
231         if (currentToken == TOK_MINUS) {
232             next();
233             return evalNegate(parseUnaryExpr().getNumeric());
234         }
235         return parsePrimaryExpr();
236     }
237
238
239     /**
240      * Checks that the current token is a right parenthesis
241      * and throws an exception if this isn't the case.
242      */

243     private final void expectRpar() throws PropertyException {
244         if (currentToken != TOK_RPAR)
245             throw new PropertyException("expected )");
246         next();
247     }
248
249     /**
250      * Try to parse a primary expression and return the
251      * resulting Property.
252      * A primary expression is either a parenthesized expression or an
253      * expression representing a primitive Property datatype, such as a
254      * string literal, an NCname, a number or a unit expression, or a
255      * function call expression.
256      */

257     private Property parsePrimaryExpr() throws PropertyException {
258         Property prop;
259         switch (currentToken) {
260         case TOK_LPAR:
261             next();
262             prop = parseAdditiveExpr();
263             expectRpar();
264             return prop;
265
266         case TOK_LITERAL:
267             prop = new StringProperty(currentTokenValue);
268             break;
269
270         case TOK_NCNAME:
271             // Interpret this in context of the property or do it later?
272
prop = new NCnameProperty(currentTokenValue);
273             break;
274
275         case TOK_FLOAT:
276             prop = new NumberProperty(new Double JavaDoc(currentTokenValue));
277             break;
278
279         case TOK_INTEGER:
280             prop = new NumberProperty(new Integer JavaDoc(currentTokenValue));
281             break;
282
283         case TOK_PERCENT:
284             /*
285              * Get the length base value object from the Maker. If null, then
286              * this property can't have % values. Treat it as a real number.
287              */

288             double pcval =
289                 new Double JavaDoc(currentTokenValue.substring(0, currentTokenValue.length() - 1)).doubleValue()
290                 / 100.0;
291             // LengthBase lbase = this.propInfo.getPercentLengthBase();
292
PercentBase pcBase = this.propInfo.getPercentBase();
293             if (pcBase != null) {
294                 if (pcBase.getDimension() == 0) {
295                     prop = new NumberProperty(pcval * pcBase.getBaseValue());
296                 } else if (pcBase.getDimension() == 1) {
297                     prop = new LengthProperty(new PercentLength(pcval,
298                                                                 pcBase));
299                 } else {
300                     throw new PropertyException("Illegal percent dimension value");
301                 }
302             } else {
303                 // WARNING? Interpret as a decimal fraction, eg. 50% = .5
304
prop = new NumberProperty(pcval);
305             }
306             break;
307
308         case TOK_NUMERIC:
309             // A number plus a valid unit name.
310
int numLen = currentTokenValue.length() - currentUnitLength;
311             String JavaDoc unitPart = currentTokenValue.substring(numLen);
312             Double JavaDoc numPart = new Double JavaDoc(currentTokenValue.substring(0,
313                     numLen));
314             Length length = null;
315             if (unitPart.equals(RELUNIT)) {
316                 length = new FixedLength(numPart.doubleValue(),
317                                     propInfo.currentFontSize());
318             } else
319                 length = new FixedLength(numPart.doubleValue(), unitPart);
320             if (length == null) {
321                 throw new PropertyException("unrecognized unit name: "
322                                             + currentTokenValue);
323             } else
324                 prop = new LengthProperty(length);
325             break;
326
327         case TOK_COLORSPEC:
328             prop = new ColorTypeProperty(new ColorType(currentTokenValue));
329             break;
330
331         case TOK_FUNCTION_LPAR: {
332             Function function =
333                 (Function)functionTable.get(currentTokenValue);
334             if (function == null) {
335                 throw new PropertyException("no such function: "
336                                             + currentTokenValue);
337             }
338             next();
339             // Push new function (for function context: getPercentBase())
340
propInfo.pushFunction(function);
341             prop = function.eval(parseArgs(function.nbArgs()), propInfo);
342             propInfo.popFunction();
343             return prop;
344         }
345         default:
346             throw new PropertyException("syntax error");
347         }
348         next();
349         return prop;
350     }
351
352     /**
353      * Parse a comma separated list of function arguments. Each argument
354      * may itself be an expression. This method consumes the closing right
355      * parenthesis of the argument list.
356      * @param nbArgs The number of arguments expected by the function.
357      * @return An array of Property objects representing the arguments
358      * found.
359      * @throws PropertyException If the number of arguments found isn't equal
360      * to the number expected.
361      */

362     Property[] parseArgs(int nbArgs) throws PropertyException {
363         Property[] args = new Property[nbArgs];
364         Property prop;
365         int i = 0;
366         if (currentToken == TOK_RPAR) {
367             // No args: func()
368
next();
369         } else {
370             while (true) {
371
372                 prop = parseAdditiveExpr();
373                 if (i < nbArgs) {
374                     args[i++] = prop;
375                 }
376                 // ignore extra args
377
if (currentToken != TOK_COMMA)
378                     break;
379                 next();
380             }
381             expectRpar();
382         }
383         if (nbArgs != i) {
384             throw new PropertyException("Wrong number of args for function");
385         }
386         return args;
387     }
388
389
390     /**
391      * Evaluate an addition operation. If either of the arguments is null,
392      * this means that it wasn't convertible to a Numeric value.
393      * @param op1 A Numeric object (Number or Length-type object)
394      * @param op2 A Numeric object (Number or Length-type object)
395      * @return A new NumericProperty object holding an object which represents
396      * the sum of the two operands.
397      * @throws PropertyException If either operand is null.
398      */

399     private Property evalAddition(Numeric op1,
400                                   Numeric op2) throws PropertyException {
401         if (op1 == null || op2 == null)
402             throw new PropertyException("Non numeric operand in addition");
403         return new NumericProperty(op1.add(op2));
404     }
405
406     /**
407      * Evaluate a subtraction operation. If either of the arguments is null,
408      * this means that it wasn't convertible to a Numeric value.
409      * @param op1 A Numeric object (Number or Length-type object)
410      * @param op2 A Numeric object (Number or Length-type object)
411      * @return A new NumericProperty object holding an object which represents
412      * the difference of the two operands.
413      * @throws PropertyException If either operand is null.
414      */

415     private Property evalSubtraction(Numeric op1,
416                                      Numeric op2) throws PropertyException {
417         if (op1 == null || op2 == null)
418             throw new PropertyException("Non numeric operand in subtraction");
419         return new NumericProperty(op1.subtract(op2));
420     }
421
422     /**
423      * Evaluate a unary minus operation. If the argument is null,
424      * this means that it wasn't convertible to a Numeric value.
425      * @param op A Numeric object (Number or Length-type object)
426      * @return A new NumericProperty object holding an object which represents
427      * the negative of the operand (multiplication by *1).
428      * @throws PropertyException If the operand is null.
429      */

430     private Property evalNegate(Numeric op) throws PropertyException {
431         if (op == null)
432             throw new PropertyException("Non numeric operand to unary minus");
433         return new NumericProperty(op.multiply(negOne));
434     }
435
436     /**
437      * Evaluate a multiplication operation. If either of the arguments is null,
438      * this means that it wasn't convertible to a Numeric value.
439      * @param op1 A Numeric object (Number or Length-type object)
440      * @param op2 A Numeric object (Number or Length-type object)
441      * @return A new NumericProperty object holding an object which represents
442      * the product of the two operands.
443      * @throws PropertyException If either operand is null.
444      */

445     private Property evalMultiply(Numeric op1,
446                                   Numeric op2) throws PropertyException {
447         if (op1 == null || op2 == null)
448             throw new PropertyException("Non numeric operand in multiplication");
449         return new NumericProperty(op1.multiply(op2));
450     }
451
452
453     /**
454      * Evaluate a division operation. If either of the arguments is null,
455      * this means that it wasn't convertible to a Numeric value.
456      * @param op1 A Numeric object (Number or Length-type object)
457      * @param op2 A Numeric object (Number or Length-type object)
458      * @return A new NumericProperty object holding an object which represents
459      * op1 divided by op2.
460      * @throws PropertyException If either operand is null.
461      */

462     private Property evalDivide(Numeric op1,
463                                 Numeric op2) throws PropertyException {
464         if (op1 == null || op2 == null)
465             throw new PropertyException("Non numeric operand in division");
466         return new NumericProperty(op1.divide(op2));
467     }
468
469     /**
470      * Evaluate a modulo operation. If either of the arguments is null,
471      * this means that it wasn't convertible to a Number value.
472      * @param op1 A Number object
473      * @param op2 A Number object
474      * @return A new NumberProperty object holding an object which represents
475      * op1 mod op2.
476      * @throws PropertyException If either operand is null.
477      */

478     private Property evalModulo(Number JavaDoc op1,
479                                 Number JavaDoc op2) throws PropertyException {
480         if (op1 == null || op2 == null)
481             throw new PropertyException("Non number operand to modulo");
482         return new NumberProperty(op1.doubleValue() % op2.doubleValue());
483     }
484
485 }
486
Popular Tags