Expression::Ptr InstanceOf::compress(const StaticContext::Ptr &context) { const Expression::Ptr me(SingleContainer::compress(context)); if(me.data() != this || m_operand->has(DisableTypingDeduction)) return me; const SequenceType::Ptr opType(m_operand->staticType()); const ItemType::Ptr itType(m_targetType->itemType()); const ItemType::Ptr ioType(opType->itemType()); if(m_targetType->cardinality().isMatch(opType->cardinality())) { if(itType->xdtTypeMatches(ioType)) return wrapLiteral(CommonValues::BooleanTrue, context, this); else if(!ioType->xdtTypeMatches(itType)) { return wrapLiteral(CommonValues::BooleanFalse, context, this); } } /* The cardinality is not guaranteed to match; it will need testing. */ else if(!ioType->xdtTypeMatches(itType)) { /* There's no way it's gonna match. The cardinality is not only * wrong, but the item type as well. */ return wrapLiteral(CommonValues::BooleanFalse, context, this); } return me; }
bool XSLTNodeTest::xdtTypeMatches(const ItemType::Ptr &other) const { if(!other->isNodeType()) return false; return *static_cast<const XSLTNodeTest *>(other.data()) == *this ? true : xdtTypeMatches(other->xdtSuperType()); }
bool BuiltinNodeType<kind>::xdtTypeMatches(const ItemType::Ptr &other) const { if(!other->isNodeType()) return false; return *static_cast<const BuiltinNodeType *>(other.data()) == *this ? true : xdtTypeMatches(other->xdtSuperType()); }
AtomicComparator::Ptr ComparisonPlatform<TSubClass, issueError, comparisonType, errorCode>:: fetchComparator(const ItemType::Ptr &t1, const ItemType::Ptr &t2, const ReportContext::Ptr &context) const { Q_ASSERT(t1); Q_ASSERT(t2); if(*BuiltinTypes::xsAnyAtomicType == *t1 || *BuiltinTypes::xsAnyAtomicType == *t2 || *BuiltinTypes::item == *t1 || *BuiltinTypes::item == *t2 || *BuiltinTypes::numeric == *t1 || *BuiltinTypes::numeric == *t2 || *CommonSequenceTypes::Empty == *t1 || *CommonSequenceTypes::Empty == *t2) { /* The static type of(at least) one of the operands could not * be narrowed further, so we do the operator * lookup at runtime. */ return AtomicComparator::Ptr(); } const AtomicComparatorLocator::Ptr locator (static_cast<const AtomicType *>(t1.data())->comparatorLocator()); if(!locator) { if(issueError) { context->error(QtXmlPatterns::tr("No comparisons can be done involving the type %1.") .arg(formatType(context->namePool(), t1)), errorCode, static_cast<const TSubClass *>(this)->actualReflection()); } return AtomicComparator::Ptr(); } const AtomicComparator::Ptr comp(static_cast<const AtomicType *>(t2.data())->accept(locator, operatorID(), static_cast<const TSubClass *>(this)->actualReflection())); if(comp) return comp; else if(issueError) { context->error(QtXmlPatterns::tr("Operator %1 is not available between atomic values of type %2 and %3.") .arg(formatKeyword(AtomicComparator::displayName(operatorID(), comparisonType)), formatType(context->namePool(), t1), formatType(context->namePool(), t2)), errorCode, static_cast<const TSubClass *>(this)->actualReflection()); } return AtomicComparator::Ptr(); }
bool AtomicType::xdtTypeMatches(const ItemType::Ptr &other) const { if(other->isAtomicType()) { if(*other == *this) return true; else return xdtTypeMatches(other->xdtSuperType()); } else return false; }
bool AbstractNodeTest::xdtTypeMatches(const ItemType::Ptr &other) const { Q_ASSERT(other); if(other->isNodeType()) { if(*other == *this) return true; else return xdtTypeMatches(other->xdtSuperType()); } else return false; }
AtomicCaster::Ptr CastingPlatform<TSubClass, issueError>::locateCaster(const ItemType::Ptr &sourceType, const ReportContext::Ptr &context, bool &castImpossible, const SourceLocationReflection *const location, const ItemType::Ptr &targetType) { Q_ASSERT(sourceType); Q_ASSERT(targetType); const AtomicCasterLocator::Ptr locator(static_cast<AtomicType *>( targetType.data())->casterLocator()); if(!locator) { if(issueError) { context->error(QtXmlPatterns::tr("No casting is possible with %1 as the target type.") .arg(formatType(context->namePool(), targetType)), ReportContext::XPTY0004, location); } else castImpossible = true; return AtomicCaster::Ptr(); } const AtomicCaster::Ptr caster(static_cast<const AtomicType *>(sourceType.data())->accept(locator, location)); if(!caster) { if(issueError) { context->error(QtXmlPatterns::tr("It is not possible to cast from %1 to %2.") .arg(formatType(context->namePool(), sourceType)) .arg(formatType(context->namePool(), targetType)), ReportContext::XPTY0004, location); } else castImpossible = true; return AtomicCaster::Ptr(); } return caster; }
Expression::Ptr InstanceOf::compress(const StaticContext::Ptr &context) { const Expression::Ptr me(SingleContainer::compress(context)); if(me != this || m_operand->has(DisableTypingDeduction)) return me; const SequenceType::Ptr opType(m_operand->staticType()); const ItemType::Ptr targetType(m_targetType->itemType()); const ItemType::Ptr operandType(opType->itemType()); if(m_targetType->cardinality().isMatch(opType->cardinality())) { if(*operandType == *CommonSequenceTypes::Empty || targetType->xdtTypeMatches(operandType)) return wrapLiteral(CommonValues::BooleanTrue, context, this); else if(!operandType->xdtTypeMatches(targetType)) return wrapLiteral(CommonValues::BooleanFalse, context, this); } /* Optimization: rule out the case where instance of will always fail. */ return me; }
Expression::Ptr TypeChecker::verifyType(const Expression::Ptr &operand, const SequenceType::Ptr &reqSeqType, const StaticContext::Ptr &context, const ReportContext::ErrorCode code, const Options options) { const ItemType::Ptr reqType(reqSeqType->itemType()); const Expression::Properties props(operand->properties()); /* If operand requires a focus, do the necessary type checking for that. */ if(props.testFlag(Expression::RequiresFocus) && options.testFlag(CheckFocus)) { const ItemType::Ptr contextType(context->contextItemType()); if(contextType) { if(props.testFlag(Expression::RequiresContextItem)) { Q_ASSERT_X(operand->expectedContextItemType(), Q_FUNC_INFO, "When the Expression sets the RequiresContextItem property, it must " "return a type in expectedContextItemType()"); const ItemType::Ptr expectedContextType(operand->expectedContextItemType()); /* Allow the empty sequence. We don't want to trigger XPTY0020 on ()/... . */ if(!expectedContextType->xdtTypeMatches(contextType) && contextType != CommonSequenceTypes::Empty) { context->error(wrongType(context->namePool(), operand->expectedContextItemType(), contextType), ReportContext::XPTY0020, operand.data()); return operand; } } } else { context->error(QtXmlPatterns::tr("The focus is undefined."), ReportContext::XPDY0002, operand.data()); return operand; } } SequenceType::Ptr operandSeqType(operand->staticType()); ItemType::Ptr operandType(operandSeqType->itemType()); /* This returns the operand if the types are identical or if operandType * is a subtype of reqType. */ if(reqType->xdtTypeMatches(operandType) || *operandType == *CommonSequenceTypes::Empty) return operand; /* Since we haven't exited yet, it means that the operandType is a super type * of reqType, and that there hence is a path down to it through the * type hierachy -- but that doesn't necessarily mean that a up-cast(down the * hierarchy) would succeed. */ Expression::Ptr result(operand); if(reqType->isAtomicType()) { const Expression::ID opID = operand->id(); if((opID == Expression::IDArgumentReference || (opID == Expression::IDCardinalityVerifier && operand->operands().first()->is(Expression::IDArgumentReference))) && *BuiltinTypes::item == *operandType) return Expression::Ptr(new ArgumentConverter(result, reqType)); if(!operandType->isAtomicType()) { result = Expression::Ptr(new Atomizer(result)); /* The atomizer might know more about the type. */ operandType = result->staticType()->itemType(); } if(reqType->xdtTypeMatches(operandType)) { /* Atomization was sufficient. Either the expected type is xs:anyAtomicType * or the type the Atomizer knows it returns, matches the required type. */ return result; } const bool compatModeEnabled = context->compatModeEnabled(); if((options.testFlag(AutomaticallyConvert) && BuiltinTypes::xsUntypedAtomic->xdtTypeMatches(operandType)) || (compatModeEnabled && BuiltinTypes::xsString->xdtTypeMatches(reqType))) { if(*reqType == *BuiltinTypes::numeric) { result = typeCheck(new UntypedAtomicConverter(result, BuiltinTypes::xsDouble, code), context, reqSeqType); } else result = typeCheck(new UntypedAtomicConverter(result, reqType, code), context, reqSeqType); /* The UntypedAtomicConverter might know more about the type, so reload. */ operandType = result->staticType()->itemType(); } else if(compatModeEnabled && *reqType == *BuiltinTypes::xsDouble) { const FunctionFactory::Ptr functions(context->functionSignatures()); Expression::List numberArgs; numberArgs.append(operand); result = functions->createFunctionCall(QXmlName(StandardNamespaces::fn, StandardLocalNames::number), numberArgs, context, operand.data())->typeCheck(context, reqSeqType); operandType = result->staticType()->itemType(); context->wrapExpressionWith(operand.data(), result); } if(reqType->xdtTypeMatches(operandType)) return result; /* Test if promotion will solve it; the xdtTypeMatches didn't * do that. */ if(options.testFlag(AutomaticallyConvert) && promotionPossible(operandType, reqType, context)) { if(options.testFlag(GeneratePromotion)) return Expression::Ptr(new UntypedAtomicConverter(result, reqType)); else return result; } if(operandType->xdtTypeMatches(reqType)) { /* For example, operandType is numeric, and reqType is xs:integer. */ return Expression::Ptr(new ItemVerifier(result, reqType, code)); } else { context->error(wrongType(context->namePool(), reqType, operandType), code, operand.data()); return result; } } else if(reqType->isNodeType()) { ReportContext::ErrorCode myCode; if(*reqType == *CommonSequenceTypes::EBV->itemType()) myCode = ReportContext::FORG0006; else myCode = code; /* empty-sequence() is considered valid because it's ok to do * for example nilled( () ). That is, to pass an empty sequence to a * function requiring for example node()?. */ if(*operandType == *CommonSequenceTypes::Empty) return result; else if(!operandType->xdtTypeMatches(reqType)) { context->error(wrongType(context->namePool(), reqType, operandType), myCode, operand.data()); return result; } /* Operand must be an item. Thus, the sequence can contain both * nodes and atomic values: we have to verify. */ return Expression::Ptr(new ItemVerifier(result, reqType, myCode)); } else { Q_ASSERT(*reqType == *CommonSequenceTypes::Empty); /* element() doesn't match empty-sequence(), but element()* does. */ if(!reqType->xdtTypeMatches(operandType) && !operandSeqType->cardinality().allowsEmpty()) { context->error(wrongType(context->namePool(), reqType, operandType), code, operand.data()); return result; } } /* This line should be reached if required type is * EBVType, and the operand is compatible. */ return result; }
bool AnyNodeType::xdtTypeMatches(const ItemType::Ptr &other) const { return other->isNodeType(); }