package org.apache.fop.fo.expr;
import org.apache.fop.fo.PropertyConsts;
import org.apache.fop.fo.properties.Property;
import org.apache.fop.fo.PropNames;
import org.apache.fop.fo.FOTree;
import org.apache.fop.fo.FONode;
import org.apache.fop.fo.expr.SystemFontFunction;
import org.apache.fop.datatypes.PropertyValue;
import org.apache.fop.datatypes.PropertyValueList;
import org.apache.fop.datatypes.Numeric;
import org.apache.fop.datatypes.Literal;
import org.apache.fop.datatypes.NCName;
import org.apache.fop.datatypes.Percentage;
import org.apache.fop.datatypes.Ems;
import org.apache.fop.datatypes.IntegerType;
import org.apache.fop.datatypes.Length;
import org.apache.fop.datatypes.Time;
import org.apache.fop.datatypes.Frequency;
import org.apache.fop.datatypes.Angle;
import org.apache.fop.datatypes.Bool;
import org.apache.fop.datatypes.Auto;
import org.apache.fop.datatypes.None;
import org.apache.fop.datatypes.Slash;
import org.apache.fop.datatypes.ColorType;
import org.apache.fop.datatypes.StringType;
import org.apache.fop.datatypes.MimeType;
import org.apache.fop.datatypes.UriType;
import org.apache.fop.datatypes.indirect.Inherit;
import org.apache.fop.datatypes.indirect.InheritedValue;
import org.apache.fop.datatypes.indirect.FromParent;
import org.apache.fop.datatypes.indirect.FromNearestSpecified;
import java.util.HashMap;
Parse
public class PropertyParser extends PropertyTokenizer {
private static final String tag = "$Name$";
private static final String revision = "$Revision$";
private FOTree foTree;
private FONode node;
public PropertyParser(FOTree foTree) {
super();
this.foTree = foTree;
}
commasfont-familyPalatino, New Century Schoolbook, serifPalatino, "New Century Schoolbook", serifbackground-positiontop center@paramnode@paramproperty@paramexpr@return@throwsPropertyException
public PropertyValue parse(FONode node, int property, String expr)
throws PropertyException
{
synchronized (this) {
if (getExpr() != null) throw new PropertyException
("PropertyParser is currently active: " + getExpr());
initialize(property, expr);
this.node = node;
}
next();
if (currentToken == EOF)
throw new PropertyException
("No token recognized in :" + expr + ":");
PropertyValueList propList = new PropertyValueList(property);
while (true) {
PropertyValue prop = parseAdditiveExpr();
if (currentToken == EOF) {
if (propList.size() != 0) {
propList.add(prop);
reset();
return propList;
} else { reset();
return prop;
}
}
if (currentToken == COMMA) {
next();
propList.add(prop);
} else { propList.add(parseSublist(prop));
if (currentToken == EOF) {
reset();
return propList;
}
}
}
}
@paraminitialValue@return
PropertyValueList parseSublist(PropertyValue initialValue)
throws PropertyException
{
PropertyValueList sublist = new PropertyValueList(property);
sublist.add(initialValue);
while (true) {
PropertyValue prop = parseAdditiveExpr();
if (currentToken == EOF) {
sublist.add(prop);
return sublist;
}
if (currentToken == COMMA) {
next();
sublist.add(prop);
return sublist;
} else { sublist.add(prop);
}
}
}
public void resetParser() {
synchronized (this) {
reset();
}
}
@return
private String arithErrorStr() {
return "Arithmetic operator not followed by Numeric or integer: "
+ getExpr();
}
@return
private String funcNumericErrorStr() {
return "Function requires Numeric or integer argument: "
+ getExpr();
}
private PropertyValue parseAdditiveExpr() throws PropertyException {
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 outer;
}
}
return prop;
}
private PropertyValue parseMultiplicativeExpr() throws PropertyException {
PropertyValue prop = parseUnaryExpr();
PropertyValue pv;
outer:
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;
}
case PropertyValue.INTEGER:
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 outer;
}
}
return prop;
}
private PropertyValue parseUnaryExpr() throws PropertyException {
if (currentToken == MINUS) {
next();
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();
}
private final void expectRpar() throws PropertyException {
if (currentToken != RPAR)
throw new PropertyException("expected )");
next();
}
private PropertyValue parsePrimaryExpr() throws PropertyException {
PropertyValue prop;
switch (currentToken) {
case LPAR:
next();
prop = parseAdditiveExpr();
expectRpar();
return prop;
case LITERAL:
prop = new Literal(property, currentTokenValue);
break;
case NCNAME:
prop = new NCName(property, currentTokenValue);
break;
case FLOAT:
prop = new Numeric
(property, Double.parseDouble(currentTokenValue));
break;
case INTEGER:
prop = new IntegerType
(property, Integer.parseInt(currentTokenValue));
break;
case PERCENT:
prop = Percentage.makePercentage
(property, Double.parseDouble(currentTokenValue));
break;
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 COLORSPEC:
prop = new ColorType(property, currentTokenValue);
break;
case BOOL:
prop = new Bool(property, currentTokenValue);
break;
case AUTO:
prop = new Auto(property);
break;
case NONE:
prop = new None(property);
break;
case INHERIT:
prop = new Inherit(property);
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: {
prop = null;
int funcType = PropertyValue.NO_TYPE;
String function = currentTokenValue;
next();
do {
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;
}
if (function.equals("rgb")) {
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");
}
if (function.equals("system-color")) {
PropertyValue[] args = parseArgs(1);
prop = new ColorType
(property, ((StringType)args[0]).getString());
break;
}
if (function.equals("system-font")) {
PropertyValue[] args = parseArgs(1, 2);
if (args.length == 1) {
prop = SystemFontFunction.systemFontCharacteristic
(property,
((StringType)args[0]).getString());
} else {
prop = SystemFontFunction.systemFontCharacteristic
(property,
((StringType)args[0]).getString(),
((StringType)args[1]).getString());
}
break;
}
if (function.equals("label-end")) {
PropertyValue[] args = parseArgs(0);
throw new FunctionNotImplementedException("label-end");
}
if (function.equals("body-start")) {
PropertyValue[] args = parseArgs(0);
throw new FunctionNotImplementedException("body-start");
}
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 (PropertyConsts.pconsts.isCompound(propindex)) {
prop = new InheritedValue(property, propindex);
break;
}
if (PropertyConsts.pconsts.inheritance(propindex)
== Property.NO)
throw new PropertyException
("inherited-property-value: "
+ PropNames.getPropertyName(propindex)
+ " is not inherited.");
prop = node.fromParent(property, propindex);
break;
}
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)
{
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))) {
switch (funcType) {
case PropertyValue.FROM_PARENT:
prop = node.fromParent(property);
case PropertyValue.FROM_NEAREST_SPECIFIED:
prop = node.fromNearestSpecified(property);
}
}
} else { if ( ! (args[0] instanceof NCName))
throw new PropertyException
(function + " function requires"
+ " property name arg.");
NCName ncname = (NCName)args[0];
String propname = ncname.getNCName();
int nameindex =
PropNames.getPropertyIndex(propname);
if (PropertyConsts.pconsts.isShorthand(nameindex)
|| PropertyConsts.pconsts.isCompound(nameindex)) {
if ( ! (nameindex == property))
throw new PropertyException
(function +
" argument " + propname +
" does not match property " +
PropNames.getPropertyName(property));
}
else { 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");
}
if (function.equals("proportional-column-width")) {
PropertyValue[] args = parseArgs(1);
throw new FunctionNotImplementedException
("proportional-column-width");
}
if (function.equals("merge-property-values")) {
PropertyValue[] args = parseArgs(0, 1);
throw new FunctionNotImplementedException
("merge-property-values");
}
throw new PropertyException("no such function: "
+ function);
} while (false);
return prop;
}
default:
throw new PropertyException("syntax error");
}
next();
return prop;
}
@paramnbArgs@return@exceptionPropertyException
PropertyValue[] parseArgs(int nbArgs) throws PropertyException {
return parseArgs(nbArgs, nbArgs);
}
@paramminArgs@parammaxArgs@return@exceptionPropertyException
PropertyValue[] parseArgs(int minArgs, int maxArgs)
throws PropertyException
{
PropertyValue[] args = new PropertyValue[maxArgs];
PropertyValue prop;
int i = 0;
if (currentToken == RPAR) {
next();
} else {
while (true) {
prop = parseAdditiveExpr();
if (i < maxArgs) {
args[i++] = prop;
}
if (currentToken != COMMA)
break;
next();
}
expectRpar();
}
if (minArgs > i || i > maxArgs) {
throw new PropertyException("Wrong number of args for function");
}
return args;
}
}