// System.err.println("Static Type Check on expression (requiredType = " + req + "):"); supplied.display(10);
Expression exp = supplied;
ItemType reqItemType = req.getPrimaryType();
int reqCard = req.getCardinality();
boolean allowsMany = Cardinality.allowsMany(reqCard);
ItemType suppliedItemType = null;
// item type of the supplied expression: null means not yet calculated
int suppliedCard = -1;
// cardinality of the supplied expression: -1 means not yet calculated
boolean cardOK = (reqCard == StaticProperty.ALLOWS_ZERO_OR_MORE);
// Unless the required cardinality is zero-or-more (no constraints).
// check the static cardinality of the supplied expression
if (!cardOK) {
suppliedCard = exp.getCardinality();
cardOK = Cardinality.subsumes(reqCard, suppliedCard);
}
boolean itemTypeOK = req.getPrimaryType() instanceof AnyItemType;
// Unless the required item type and content type are ITEM (no constraints)
// check the static item type against the supplied expression.
// NOTE: we don't currently do any static inference regarding the content type
if (!itemTypeOK) {
suppliedItemType = exp.getItemType();
int relation = Type.relationship(reqItemType, suppliedItemType);
itemTypeOK = relation == Type.SAME_TYPE || relation == Type.SUBSUMES;
if (!itemTypeOK) {
// Handle numeric promotion decimal -> float -> double
if (Type.isPromotable(suppliedItemType, reqItemType)) {
itemTypeOK = true;
ComputedExpression cexp;
if (allowsMany) {
cexp = new AtomicSequenceConverter(exp, (AtomicType)reqItemType);
} else {
cexp = new CastExpression(exp, (AtomicType)reqItemType, true);
}
if (exp instanceof Value) {
try {
exp = ExpressionTool.eagerEvaluate(cexp, null);
} catch (XPathException err) {
throw new StaticError(err);
}
} else {
cexp.adoptChildExpression(exp);
exp = cexp;
}
suppliedItemType = reqItemType;
}
// Handle conversion of untypedAtomic to the required type
if (suppliedItemType.getPrimitiveType() == Type.UNTYPED_ATOMIC) {
itemTypeOK = true;
ComputedExpression cexp;
if (allowsMany) {
cexp = new AtomicSequenceConverter(exp, (AtomicType)reqItemType);
} else {
cexp = new CastExpression(exp, (AtomicType)reqItemType, true);
}
if (exp instanceof Value) {
try {
exp = ExpressionTool.eagerEvaluate(cexp, null);
} catch (XPathException err) {
throw new StaticError(err);
}
} else {
cexp.adoptChildExpression(exp);
exp = cexp;
}
suppliedItemType = reqItemType;
}
}
}
// If both the cardinality and item type are statically OK, return now.
if (itemTypeOK && cardOK) {
return exp;
}
// If we haven't evaluated the cardinality of the supplied expression, do it now
if (suppliedCard == -1) {
suppliedCard = exp.getCardinality();
if (!cardOK) {
cardOK = Cardinality.subsumes(reqCard, suppliedCard);
}
}
// If an empty sequence was explicitly supplied, and empty sequence is allowed,
// then the item type doesn't matter
if (cardOK && suppliedCard==StaticProperty.EMPTY) {
return exp;
}
// If we haven't evaluated the item type of the supplied expression, do it now
if (suppliedItemType == null) {
suppliedItemType = exp.getItemType();
}
if (suppliedCard==StaticProperty.EMPTY && ((reqCard & StaticProperty.ALLOWS_ZERO) == 0) ) {
StaticError err = new StaticError(
"An empty sequence is not allowed as the " + role.getMessage(),
ExpressionTool.getLocator(supplied));
err.setErrorCode(role.getErrorCode());
err.setIsTypeError(true);
throw err;
}
// Handle the special rules for 1.0 compatibility mode
if (backwardsCompatible && !allowsMany) {
if (Type.isSubType(reqItemType, Type.STRING_TYPE)) {
if (!Type.isSubType(suppliedItemType, Type.ANY_ATOMIC_TYPE)) {
ComputedExpression cexp = new Atomizer(exp);
cexp.adoptChildExpression(exp);
exp = cexp;
}
ComputedExpression cexp2 = new ConvertToString(exp);
if (exp instanceof Value) {
try {
exp = ExpressionTool.eagerEvaluate(cexp2, null);
} catch (XPathException err) {
throw new StaticError(err);
}
} else {
cexp2.adoptChildExpression(exp);
exp = cexp2;
}
suppliedItemType = Type.STRING_TYPE;
suppliedCard = StaticProperty.EXACTLY_ONE;
cardOK = Cardinality.subsumes(reqCard, suppliedCard);
} else if (reqItemType == Type.NUMBER_TYPE || Type.isSubType(reqItemType, Type.DOUBLE_TYPE)) {
// TODO: in the Nov 2003 draft, the rules have changed so that number() is called
// only if the supplied value doesn't match the expected type. We're currently
// returning different results for round(()) depending on whether the arg value
// is known statically or not.
NumberFn fn = (NumberFn)SystemFunction.makeSystemFunction("number", env.getNamePool());
Expression[] args = new Expression[1];
args[0] = exp;
fn.setArguments(args);
if (exp instanceof Value) {
try {
exp = ExpressionTool.eagerEvaluate(fn, null);
} catch (XPathException err) {
throw new StaticError(err);
}
} else {
fn.adoptChildExpression(exp);
exp = fn;
}
suppliedItemType = Type.DOUBLE_TYPE;
suppliedCard = StaticProperty.EXACTLY_ONE;
cardOK = Cardinality.subsumes(reqCard, suppliedCard);
} else if (reqItemType instanceof AnyNodeTest ||
reqItemType instanceof AnyItemType
|| reqItemType == Type.ANY_ATOMIC_TYPE ) {
// TODO: this last condition isn't in the rules for function calls,
// but is needed for arithmetic expressions
if (Cardinality.allowsMany(suppliedCard)) {
ComputedExpression cexp = new FirstItemExpression(exp);
cexp.adoptChildExpression(exp);
exp = cexp;
}
suppliedCard = StaticProperty.ALLOWS_ZERO_OR_ONE;
cardOK = Cardinality.subsumes(reqCard, suppliedCard);
}
}
// If the required type is atomic, and the supplied type is not statically
// guaranteed to be atomic, add an Atomizer
if ((reqItemType instanceof AtomicType) &&
!(suppliedItemType instanceof AtomicType) &&
!(suppliedCard == StaticProperty.EMPTY)) {
ComputedExpression cexp = new Atomizer(exp);
exp = cexp;
suppliedItemType = exp.getItemType();
suppliedCard = exp.getCardinality();
cardOK = Cardinality.subsumes(reqCard, suppliedCard);
}
// If the required type is a subtype of ATOMIC, and the supplied type is
// capable of producing untyped atomic values, add an Untyped Atomic Converter
if (reqItemType != Type.ANY_ATOMIC_TYPE &&
(reqItemType instanceof AtomicType) &&
(suppliedItemType instanceof AtomicType) &&
(suppliedItemType == Type.ANY_ATOMIC_TYPE ||
suppliedItemType == Type.UNTYPED_ATOMIC_TYPE) &&
!(suppliedCard == StaticProperty.EMPTY)) {
ComputedExpression cexp = new UntypedAtomicConverter(exp, (AtomicType)reqItemType);
cexp.adoptChildExpression(exp);
exp = cexp;
suppliedItemType = exp.getItemType();
suppliedCard = exp.getCardinality();
cardOK = Cardinality.subsumes(reqCard, suppliedCard);
}
// Try a static type check. We only throw it out if the call cannot possibly succeed.
int relation = Type.relationship(suppliedItemType, reqItemType);
if (relation == Type.DISJOINT) {
// The item types may be disjoint, but if both the supplied and required types permit
// an empty sequence, we can't raise a static error. Raise a warning instead.
if (Cardinality.allowsZero(suppliedCard) &&
Cardinality.allowsZero(reqCard)) {
if (suppliedCard != StaticProperty.EMPTY) {
String msg = "Required item type of " + role.getMessage() +
" is " + reqItemType.toString(env.getNamePool()) +
"; supplied value has item type " +
suppliedItemType.toString(env.getNamePool()) +
". The expression can succeed only if the supplied value is an empty sequence.";
env.issueWarning(msg);
}
} else {
StaticError err = new StaticError(
"Required item type of " + role.getMessage() +
" is " + reqItemType.toString(env.getNamePool()) +
"; supplied value has item type " +
suppliedItemType.toString(env.getNamePool()),
ExpressionTool.getLocator(supplied));
err.setErrorCode(role.getErrorCode());
err.setIsTypeError(true);
throw err;
}