Qt 4.8
qtypechecker.cpp
Go to the documentation of this file.
1 /****************************************************************************
2 **
3 ** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
5 **
6 ** This file is part of the QtXmlPatterns module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and Digia. For licensing terms and
14 ** conditions see http://qt.digia.com/licensing. For further information
15 ** use the contact form at http://qt.digia.com/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 **
25 ** In addition, as a special exception, Digia gives you certain additional
26 ** rights. These rights are described in the Digia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 **
29 ** GNU General Public License Usage
30 ** Alternatively, this file may be used under the terms of the GNU
31 ** General Public License version 3.0 as published by the Free Software
32 ** Foundation and appearing in the file LICENSE.GPL included in the
33 ** packaging of this file. Please review the following information to
34 ** ensure the GNU General Public License version 3.0 requirements will be
35 ** met: http://www.gnu.org/copyleft/gpl.html.
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41 
42 #include "qargumentconverter_p.h"
43 #include "qatomictype_p.h"
44 #include "qatomizer_p.h"
45 #include "qbuiltintypes_p.h"
46 #include "qcardinalityverifier_p.h"
47 #include "qcommonsequencetypes_p.h"
48 #include "qfunctionfactory_p.h"
49 #include "qitemverifier_p.h"
50 #include "qpatternistlocale_p.h"
52 
53 #include "qtypechecker_p.h"
54 
56 
57 using namespace QPatternist;
58 
60  const ItemType::Ptr &reqType,
61  const ItemType::Ptr &opType)
62 {
63  return QtXmlPatterns::tr("Required type is %1, but %2 was found.")
64  .arg(formatType(np, reqType), formatType(np, opType));
65 }
66 
68  const SequenceType::Ptr &reqType,
69  const StaticContext::Ptr &context,
70  const ReportContext::ErrorCode code,
71  const Options options)
72 {
74  "This test ensures 'code' exists, otherwise codeToString() would assert.");
75  Q_ASSERT(operand);
76  Q_ASSERT(reqType);
77  Q_ASSERT(context);
78 
79  /* Do it in two steps: verify type, and then cardinality. */
80  const Expression::Ptr cardVerified(CardinalityVerifier::verifyCardinality(operand, reqType->cardinality(), context, code));
81  return verifyType(cardVerified, reqType, context, code, options);
82 }
83 
85  const ItemType::Ptr &toType,
86  const StaticContext::Ptr &context)
87 {
88  /* These types can be promoted to xs:string. xs:untypedAtomic should be
89  * cast when interpreting it formally, but implementing it as a promotion
90  * gives the same result(and is faster). */
91  if(*toType == *BuiltinTypes::xsString &&
92  (BuiltinTypes::xsUntypedAtomic->xdtTypeMatches(fromType) ||
93  BuiltinTypes::xsAnyURI->xdtTypeMatches(fromType)))
94  return true;
95 
96  if(*toType == *BuiltinTypes::xsDouble &&
97  BuiltinTypes::numeric->xdtTypeMatches(fromType))
98  {
99  /* Any numeric can be promoted to xs:double. */
100  return true;
101  }
102 
103  /* xs:decimal/xs:integer can be promoted to xs:float. */
104  if(*toType == *BuiltinTypes::xsFloat && BuiltinTypes::xsDecimal->xdtTypeMatches(fromType))
105 
106  {
107  context->warning(QtXmlPatterns::tr("Promoting %1 to %2 may cause loss of precision.")
108  .arg(formatType(context->namePool(), fromType))
109  .arg(formatType(context->namePool(), BuiltinTypes::xsFloat)));
110  return true;
111  }
112 
113  return false;
114 }
115 
117  const StaticContext::Ptr &context,
118  const SequenceType::Ptr &reqType)
119 {
120  return Expression::Ptr(op->typeCheck(context, reqType));
121 }
122 
124  const SequenceType::Ptr &reqSeqType,
125  const StaticContext::Ptr &context,
126  const ReportContext::ErrorCode code,
127  const Options options)
128 {
129  const ItemType::Ptr reqType(reqSeqType->itemType());
130  const Expression::Properties props(operand->properties());
131 
132  /* If operand requires a focus, do the necessary type checking for that. */
133  if(props.testFlag(Expression::RequiresFocus) && options.testFlag(CheckFocus))
134  {
135  const ItemType::Ptr contextType(context->contextItemType());
136  if(contextType)
137  {
138  if(props.testFlag(Expression::RequiresContextItem))
139  {
141  "When the Expression sets the RequiresContextItem property, it must "
142  "return a type in expectedContextItemType()");
143  const ItemType::Ptr expectedContextType(operand->expectedContextItemType());
144 
145  /* Allow the empty sequence. We don't want to trigger XPTY0020 on ()/... . */
146  if(!expectedContextType->xdtTypeMatches(contextType) && contextType != CommonSequenceTypes::Empty)
147  {
148  context->error(wrongType(context->namePool(), operand->expectedContextItemType(), contextType),
149  ReportContext::XPTY0020, operand.data());
150  return operand;
151  }
152  }
153  }
154  else
155  {
156  context->error(QtXmlPatterns::tr("The focus is undefined."), ReportContext::XPDY0002, operand.data());
157  return operand;
158  }
159  }
160 
161  SequenceType::Ptr operandSeqType(operand->staticType());
162  ItemType::Ptr operandType(operandSeqType->itemType());
163 
164  /* This returns the operand if the types are identical or if operandType
165  * is a subtype of reqType. */
166  if(reqType->xdtTypeMatches(operandType) || *operandType == *CommonSequenceTypes::Empty)
167  return operand;
168 
169  /* Since we haven't exited yet, it means that the operandType is a super type
170  * of reqType, and that there hence is a path down to it through the
171  * type hierachy -- but that doesn't necessarily mean that a up-cast(down the
172  * hierarchy) would succeed. */
173 
174  Expression::Ptr result(operand);
175 
176  if(reqType->isAtomicType())
177  {
178  const Expression::ID opID = operand->id();
179  if((opID == Expression::IDArgumentReference ||
181  && *BuiltinTypes::item == *operandType)
182  return Expression::Ptr(new ArgumentConverter(result, reqType));
183 
184  if(!operandType->isAtomicType())
185  {
186  result = Expression::Ptr(new Atomizer(result));
187  /* The atomizer might know more about the type. */
188  operandType = result->staticType()->itemType();
189  }
190 
191  if(reqType->xdtTypeMatches(operandType))
192  {
193  /* Atomization was sufficient. Either the expected type is xs:anyAtomicType
194  * or the type the Atomizer knows it returns, matches the required type. */
195  return result;
196  }
197 
198  const bool compatModeEnabled = context->compatModeEnabled();
199 
200  if((options.testFlag(AutomaticallyConvert) && BuiltinTypes::xsUntypedAtomic->xdtTypeMatches(operandType)) ||
201  (compatModeEnabled && BuiltinTypes::xsString->xdtTypeMatches(reqType)))
202  {
203  if(*reqType == *BuiltinTypes::numeric)
204  {
205  result = typeCheck(new UntypedAtomicConverter(result, BuiltinTypes::xsDouble, code),
206  context, reqSeqType);
207  }
208  else
209  result = typeCheck(new UntypedAtomicConverter(result, reqType, code), context, reqSeqType);
210 
211  /* The UntypedAtomicConverter might know more about the type, so reload. */
212  operandType = result->staticType()->itemType();
213  }
214  else if(compatModeEnabled && *reqType == *BuiltinTypes::xsDouble)
215  {
216  const FunctionFactory::Ptr functions(context->functionSignatures());
217  Expression::List numberArgs;
218  numberArgs.append(operand);
219 
220  result = functions->createFunctionCall(QXmlName(StandardNamespaces::fn, StandardLocalNames::number),
221  numberArgs,
222  context,
223  operand.data())->typeCheck(context, reqSeqType);
224  operandType = result->staticType()->itemType();
225  context->wrapExpressionWith(operand.data(), result);
226  }
227 
228  if(reqType->xdtTypeMatches(operandType))
229  return result;
230 
231  /* Test if promotion will solve it; the xdtTypeMatches didn't
232  * do that. */
233  if(options.testFlag(AutomaticallyConvert) && promotionPossible(operandType, reqType, context))
234  {
235  if(options.testFlag(GeneratePromotion))
236  return Expression::Ptr(new UntypedAtomicConverter(result, reqType));
237  else
238  return result;
239  }
240 
241  if(operandType->xdtTypeMatches(reqType))
242  {
243  /* For example, operandType is numeric, and reqType is xs:integer. */
244  return Expression::Ptr(new ItemVerifier(result, reqType, code));
245  }
246  else
247  {
248  context->error(wrongType(context->namePool(), reqType, operandType), code, operand.data());
249  return result;
250  }
251  }
252  else if(reqType->isNodeType())
253  {
254 
256 
257  if(*reqType == *CommonSequenceTypes::EBV->itemType())
258  myCode = ReportContext::FORG0006;
259  else
260  myCode = code;
261 
262  /* empty-sequence() is considered valid because it's ok to do
263  * for example nilled( () ). That is, to pass an empty sequence to a
264  * function requiring for example node()?. */
265  if(*operandType == *CommonSequenceTypes::Empty)
266  return result;
267  else if(!operandType->xdtTypeMatches(reqType))
268  {
269  context->error(wrongType(context->namePool(), reqType, operandType), myCode, operand.data());
270  return result;
271  }
272 
273  /* Operand must be an item. Thus, the sequence can contain both
274  * nodes and atomic values: we have to verify. */
275  return Expression::Ptr(new ItemVerifier(result, reqType, myCode));
276  }
277  else
278  {
279  Q_ASSERT(*reqType == *CommonSequenceTypes::Empty);
280 
281  /* element() doesn't match empty-sequence(), but element()* does. */
282  if(!reqType->xdtTypeMatches(operandType) &&
283  !operandSeqType->cardinality().allowsEmpty())
284  {
285  context->error(wrongType(context->namePool(), reqType, operandType),
286  code, operand.data());
287  return result;
288  }
289  }
290 
291  /* This line should be reached if required type is
292  * EBVType, and the operand is compatible. */
293  return result;
294 }
295 
static QString codeToString(const ReportContext::ErrorCode errorCode)
virtual bool compatModeEnabled() const =0
#define QT_END_NAMESPACE
This macro expands to.
Definition: qglobal.h:90
Base class for all AST nodes in an XPath/XQuery/XSL-T expression.
static Expression::Ptr typeCheck(Expression *const op, const StaticContext::Ptr &context, const SequenceType::Ptr &reqType)
QString formatType(const NamePool::Ptr &np, const T &type)
Formats ItemType and SequenceType.
virtual NamePool::Ptr namePool() const =0
virtual Expression::Ptr typeCheck(const StaticContext::Ptr &context, const SequenceType::Ptr &reqType)
Definition: qexpression.cpp:70
bool testFlag(Enum f) const
Returns true if the flag is set, otherwise false.
Definition: qglobal.h:2345
The QString class provides a Unicode character string.
Definition: qstring.h:83
virtual ItemType::Ptr expectedContextItemType() const
#define Q_ASSERT(cond)
Definition: qglobal.h:1823
When set, the function conversion rules are applied.
static Expression::Ptr verifyCardinality(const Expression::Ptr &operand, const Cardinality &card, const StaticContext::Ptr &context, const ReportContext::ErrorCode code=ReportContext::XPTY0004)
static const AtomicType::Ptr xsString
virtual ID id() const
void append(const T &t)
Inserts value at the end of the list.
Definition: qlist.h:507
T * data() const
Returns a pointer to the shared data object.
Definition: qshareddata.h:145
#define QT_BEGIN_NAMESPACE
This macro expands to.
Definition: qglobal.h:89
void error(const QString &message, const ReportContext::ErrorCode errorCode, const QSourceLocation &sourceLocation)
static bool isEmpty(const char *str)
static const ItemType::Ptr item
The namespace for the internal API of QtXmlPatterns.
static const AtomicType::Ptr xsDouble
virtual Cardinality cardinality() const =0
Verifies that the items in a sequence an Expression evaluates is of a certain ItemType.
virtual QExplicitlySharedDataPointer< FunctionFactory > functionSignatures() const =0
virtual Expression::List operands() const =0
static const AtomicType::Ptr xsUntypedAtomic
Performs atomization. Effectively, it is an implementation of the fn:data() function.
Definition: qatomizer_p.h:76
T & first()
Returns a reference to the first item in the list.
Definition: qlist.h:282
QExplicitlySharedDataPointer< Expression > Ptr
A smart pointer wrapping mutable Expression instances.
virtual SequenceType::Ptr staticType() const =0
UntypedAtomicConverter for ArgumentReference, if needed.
static const EmptySequenceType::Ptr Empty
#define Q_ASSERT_X(cond, where, what)
Definition: qglobal.h:1837
QString arg(qlonglong a, int fieldwidth=0, int base=10, const QChar &fillChar=QLatin1Char(' ')) const Q_REQUIRED_RESULT
Definition: qstring.cpp:7186
The QXmlName class represents the name of an XML node, in an efficient, namespace-aware way...
Definition: qxmlname.h:58
Contains functions used for formatting arguments, such as keywords and paths, in translated strings...
static Expression::Ptr verifyType(const Expression::Ptr &operand, const SequenceType::Ptr &reqSeqType, const StaticContext::Ptr &context, const ReportContext::ErrorCode code, const Options options)
Implements the type checking and promotion part of the Function Conversion Rules. ...
static Expression::Ptr applyFunctionConversion(const Expression::Ptr &operand, const SequenceType::Ptr &reqType, const StaticContext::Ptr &context, const ReportContext::ErrorCode code=ReportContext::XPTY0004, const Options=Options(AutomaticallyConvert|CheckFocus))
Builds a pipeline of artificial AST nodes that ensures operand conforms to the type reqType by applyi...
static const AtomicType::Ptr xsFloat
virtual Properties properties() const
static const AtomicType::Ptr numeric
virtual ItemType::Ptr contextItemType() const =0
The static type of the context item.
static const SequenceType::Ptr EBV
Casts every item in a sequence obtained from evaluating an Expression, to a requested atomic type...
static const AtomicType::Ptr xsDecimal
static QString wrongType(const NamePool::Ptr &np, const ItemType::Ptr &reqType, const ItemType::Ptr &opType)
Centralizes a message-string to reduce work for translators and increase consistency.
void warning(const QString &message, const QSourceLocation &sourceLocation=QSourceLocation())
The QFlags class provides a type-safe way of storing OR-combinations of enum values.
Definition: qglobal.h:2313
virtual ItemType::Ptr itemType() const =0
void wrapExpressionWith(const SourceLocationReflection *const existingNode, const QExplicitlySharedDataPointer< Expression > &newNode)
Ensures source locations are handled in such a manner that existingNode wraps newNode.
bool is(const ID id) const
static const AtomicType::Ptr xsAnyURI
static QByteArray toType(const QByteArray &t)
Definition: qaxdump.cpp:110
static bool promotionPossible(const ItemType::Ptr &fromType, const ItemType::Ptr &toType, const StaticContext::Ptr &context)
Whether the focus should be checked or not.
#define Q_FUNC_INFO
Definition: qglobal.h:1871