cb63aca393cb3df0857b6ca39e577b7dfa327a6a
[profile/ivi/qtxmlpatterns.git] / src / xmlpatterns / type / qtypechecker.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
5 **
6 ** This file is part of the QtXmlPatterns module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
16 **
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
20 **
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
28 **
29 ** Other Usage
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
32 **
33 **
34 **
35 **
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"
51 #include "quntypedatomicconverter_p.h"
52
53 #include "qtypechecker_p.h"
54
55 QT_BEGIN_NAMESPACE
56
57 using namespace QPatternist;
58
59 QString TypeChecker::wrongType(const NamePool::Ptr &np,
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
67 Expression::Ptr TypeChecker::applyFunctionConversion(const Expression::Ptr &operand,
68                                                      const SequenceType::Ptr &reqType,
69                                                      const StaticContext::Ptr &context,
70                                                      const ReportContext::ErrorCode code,
71                                                      const Options options)
72 {
73     Q_ASSERT_X(!ReportContext::codeToString(code).isEmpty(), Q_FUNC_INFO,
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
84 bool TypeChecker::promotionPossible(const ItemType::Ptr &fromType,
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
116 Expression::Ptr TypeChecker::typeCheck(Expression *const op,
117                                        const StaticContext::Ptr &context,
118                                        const SequenceType::Ptr &reqType)
119 {
120     return Expression::Ptr(op->typeCheck(context, reqType));
121 }
122
123 Expression::Ptr TypeChecker::verifyType(const Expression::Ptr &operand,
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             {
140                 Q_ASSERT_X(operand->expectedContextItemType(), Q_FUNC_INFO,
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 ||
180             (opID == Expression::IDCardinalityVerifier && operand->operands().first()->is(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
255         ReportContext::ErrorCode myCode;
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
296 QT_END_NAMESPACE