1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
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 The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/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 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "qabstractfloat_p.h"
41#include "qarithmeticexpression_p.h"
42#include "qbuiltintypes_p.h"
43#include "qcommonsequencetypes_p.h"
44#include "qcommonvalues_p.h"
45#include "qdecimal_p.h"
46#include "qgenericsequencetype_p.h"
47#include "qinteger_p.h"
48#include "qoptimizerblocks_p.h"
49#include "qsequencefns_p.h"
50#include "quntypedatomicconverter_p.h"
51
52#include "qaggregatefns_p.h"
53
54QT_BEGIN_NAMESPACE
55
56using namespace QPatternist;
57
58Item CountFN::evaluateSingleton(const DynamicContext::Ptr &context) const
59{
60 return Integer::fromValue(num: m_operands.first()->evaluateSequence(context)->count());
61}
62
63Expression::Ptr CountFN::typeCheck(const StaticContext::Ptr &context,
64 const SequenceType::Ptr &reqType)
65{
66 if(*CommonSequenceTypes::EBV->itemType() == *reqType->itemType())
67 {
68 return ByIDCreator::create(id: IDExistsFN, operands: operands(), context, r: this)->typeCheck(context, reqType);
69 }
70 else
71 return FunctionCall::typeCheck(context, reqType);
72}
73
74Expression::Ptr CountFN::compress(const StaticContext::Ptr &context)
75{
76 const Expression::Ptr me(FunctionCall::compress(context));
77 if(me != this)
78 return me;
79
80 const Cardinality card(m_operands.first()->staticType()->cardinality());
81 if(card.isExactlyOne())
82 return wrapLiteral(item: CommonValues::IntegerOne, context, r: this);
83 else if(card.isEmpty())
84 {
85 /* One might think that if the operand is (), that compress() would have
86 * evaluated us and therefore this line never be reached, but "()" can
87 * be combined with the DisableElimination flag. */
88 return wrapLiteral(item: CommonValues::IntegerZero, context, r: this);
89 }
90 else if(card.isExact())
91 return wrapLiteral(item: Integer::fromValue(num: card.minimum()), context, r: this);
92 else
93 return me;
94}
95
96Expression::Ptr AddingAggregate::typeCheck(const StaticContext::Ptr &context,
97 const SequenceType::Ptr &reqType)
98{
99 const Expression::Ptr me(FunctionCall::typeCheck(context, reqType));
100 ItemType::Ptr t1(m_operands.first()->staticType()->itemType());
101
102 if(*CommonSequenceTypes::Empty == *t1)
103 return me;
104 else if(*BuiltinTypes::xsAnyAtomicType == *t1 ||
105 *BuiltinTypes::numeric == *t1)
106 return me;
107 else if(BuiltinTypes::xsUntypedAtomic->xdtTypeMatches(other: t1))
108 {
109 m_operands.replace(i: 0, t: Expression::Ptr(new UntypedAtomicConverter(m_operands.first(),
110 BuiltinTypes::xsDouble)));
111 t1 = m_operands.first()->staticType()->itemType();
112 }
113 else if(!BuiltinTypes::numeric->xdtTypeMatches(other: t1) &&
114 !BuiltinTypes::xsDayTimeDuration->xdtTypeMatches(other: t1) &&
115 !BuiltinTypes::xsYearMonthDuration->xdtTypeMatches(other: t1))
116 {
117 /* Translator, don't translate the type names. */
118 context->error(message: QtXmlPatterns::tr(sourceText: "The first argument to %1 cannot be "
119 "of type %2. It must be a numeric "
120 "type, xs:yearMonthDuration or "
121 "xs:dayTimeDuration.")
122 .arg(a: formatFunction(np: context->namePool(), func: signature()))
123 .arg(a: formatType(np: context->namePool(),
124 type: m_operands.first()->staticType())),
125 errorCode: ReportContext::FORG0006, reflection: this);
126 }
127
128 if(!m_operands.first()->staticType()->cardinality().allowsMany())
129 return m_operands.first();
130
131 /* We know fetchMathematician won't attempt a rewrite of the operand, so this is safe. */
132 m_mather = ArithmeticExpression::fetchMathematician(t1&: m_operands.first(), t2&: m_operands.first(),
133 op: AtomicMathematician::Add, issueError: true, context,
134 reflection: this,
135 code: ReportContext::FORG0006);
136 return me;
137}
138
139Item AvgFN::evaluateSingleton(const DynamicContext::Ptr &context) const
140{
141 const Item::Iterator::Ptr it(m_operands.first()->evaluateSequence(context));
142 Item sum(it->next());
143
144 xsInteger count = 0;
145 while(sum)
146 {
147 ++count;
148 const Item next(it->next());
149 if(!next)
150 break;
151
152 sum = ArithmeticExpression::flexiblyCalculate(op1: sum, op: AtomicMathematician::Add,
153 op2: next, mather: m_adder, context,
154 reflection: this,
155 code: ReportContext::FORG0006);
156 };
157
158 if(!sum)
159 return Item();
160
161 /* Note that we use the same m_mather which was used for adding,
162 * can be worth to think about. */
163 return ArithmeticExpression::flexiblyCalculate(op1: sum, op: AtomicMathematician::Div,
164 op2: Integer::fromValue(num: count),
165 mather: m_divider, context,
166 reflection: this,
167 code: ReportContext::FORG0006);
168}
169
170Expression::Ptr AvgFN::typeCheck(const StaticContext::Ptr &context,
171 const SequenceType::Ptr &reqType)
172{
173 const Expression::Ptr me(FunctionCall::typeCheck(context, reqType));
174 ItemType::Ptr t1(m_operands.first()->staticType()->itemType());
175
176 if(*CommonSequenceTypes::Empty == *t1)
177 return me;
178 else if(*BuiltinTypes::xsAnyAtomicType == *t1 ||
179 *BuiltinTypes::numeric == *t1)
180 return me;
181 else if(BuiltinTypes::xsUntypedAtomic->xdtTypeMatches(other: t1))
182 {
183 m_operands.replace(i: 0, t: Expression::Ptr(new UntypedAtomicConverter(m_operands.first(),
184 BuiltinTypes::xsDouble)));
185 t1 = m_operands.first()->staticType()->itemType();
186 }
187 else if(!BuiltinTypes::numeric->xdtTypeMatches(other: t1) &&
188 !BuiltinTypes::xsDayTimeDuration->xdtTypeMatches(other: t1) &&
189 !BuiltinTypes::xsYearMonthDuration->xdtTypeMatches(other: t1))
190 {
191 /* Translator, don't translate the type names. */
192 context->error(message: QtXmlPatterns::tr(sourceText: "The first argument to %1 cannot be "
193 "of type %2. It must be of type %3, "
194 "%4, or %5.")
195 .arg(a: signature())
196 .arg(a: formatType(np: context->namePool(), type: m_operands.first()->staticType()))
197 .arg(a: formatType(np: context->namePool(), type: BuiltinTypes::numeric))
198 .arg(a: formatType(np: context->namePool(), type: BuiltinTypes::xsYearMonthDuration))
199 .arg(a: formatType(np: context->namePool(), type: BuiltinTypes::xsDayTimeDuration)),
200 errorCode: ReportContext::FORG0006, reflection: this);
201 }
202
203 if(!m_operands.first()->staticType()->cardinality().allowsMany())
204 return m_operands.first();
205
206 /* We use CommonValues::IntegerOne here because it is an arbitrary Expression
207 * of type xs:integer */
208 Expression::Ptr op2(wrapLiteral(item: CommonValues::IntegerOne, context, r: this));
209 m_adder = ArithmeticExpression::fetchMathematician(t1&: m_operands.first(), t2&: m_operands.first(),
210 op: AtomicMathematician::Add, issueError: true, context, reflection: this);
211 m_divider = ArithmeticExpression::fetchMathematician(t1&: m_operands.first(), t2&: op2,
212 op: AtomicMathematician::Div, issueError: true, context, reflection: this);
213 return me;
214}
215
216SequenceType::Ptr AvgFN::staticType() const
217{
218 const SequenceType::Ptr opt(m_operands.first()->staticType());
219 ItemType::Ptr t(opt->itemType());
220
221 if(BuiltinTypes::xsUntypedAtomic->xdtTypeMatches(other: t))
222 t = BuiltinTypes::xsDouble; /* xsUntypedAtomics are converted to xsDouble. */
223 else if(BuiltinTypes::xsInteger->xdtTypeMatches(other: t))
224 t = BuiltinTypes::xsDecimal;
225
226 /* else, it means the type is xsDayTimeDuration, xsYearMonthDuration,
227 * xsDouble, xsFloat or xsAnyAtomicType, which we use as is. */
228 return makeGenericSequenceType(itemType: BuiltinTypes::xsAnyAtomicType->xdtTypeMatches(other: t) ? t : ItemType::Ptr(BuiltinTypes::xsAnyAtomicType),
229 cardinality: opt->cardinality().toWithoutMany());
230}
231
232Item SumFN::evaluateSingleton(const DynamicContext::Ptr &context) const
233{
234 const Item::Iterator::Ptr it(m_operands.first()->evaluateSequence(context));
235 Item sum(it->next());
236
237 while(sum)
238 {
239 const Item next(it->next());
240 if(!next)
241 break;
242
243 sum = ArithmeticExpression::flexiblyCalculate(op1: sum, op: AtomicMathematician::Add,
244 op2: next, mather: m_mather, context, reflection: this,
245 code: ReportContext::FORG0006);
246 };
247
248 if(!sum)
249 {
250 if(m_operands.count() == 1)
251 return CommonValues::IntegerZero;
252 else
253 return m_operands.last()->evaluateSingleton(context);
254 }
255
256 return sum;
257}
258
259Expression::Ptr SumFN::typeCheck(const StaticContext::Ptr &context,
260 const SequenceType::Ptr &reqType)
261{
262 const Expression::Ptr me(AddingAggregate::typeCheck(context, reqType));
263
264 if(*CommonSequenceTypes::Empty == *m_operands.first()->staticType()->itemType())
265 {
266 if(m_operands.count() == 1)
267 return wrapLiteral(item: CommonValues::IntegerZero, context, r: this);
268 else
269 return m_operands.at(i: 1);
270 }
271
272 if(m_operands.count() == 1)
273 return me;
274
275 const ItemType::Ptr t(m_operands.at(i: 1)->staticType()->itemType());
276
277 if(!BuiltinTypes::numeric->xdtTypeMatches(other: t) &&
278 !BuiltinTypes::xsAnyAtomicType->xdtTypeMatches(other: t) &&
279 *CommonSequenceTypes::Empty != *t &&
280 !BuiltinTypes::xsDayTimeDuration->xdtTypeMatches(other: t) &&
281 !BuiltinTypes::xsYearMonthDuration->xdtTypeMatches(other: t))
282 {
283 context->error(message: QtXmlPatterns::tr(sourceText: "The second argument to %1 cannot be "
284 "of type %2. It must be of type %3, "
285 "%4, or %5.")
286 .arg(a: formatFunction(np: context->namePool(), func: signature()))
287 .arg(a: formatType(np: context->namePool(), type: m_operands.at(i: 1)->staticType()))
288 .arg(a: formatType(np: context->namePool(), type: BuiltinTypes::numeric))
289 .arg(a: formatType(np: context->namePool(), type: BuiltinTypes::xsYearMonthDuration))
290 .arg(a: formatType(np: context->namePool(), type: BuiltinTypes::xsDayTimeDuration)),
291 errorCode: ReportContext::FORG0006, reflection: this);
292 return me;
293 }
294
295 return me;
296}
297
298SequenceType::Ptr SumFN::staticType() const
299{
300 const SequenceType::Ptr t(m_operands.first()->staticType());
301
302 if(m_operands.count() == 1)
303 {
304 return makeGenericSequenceType(itemType: t->itemType() | BuiltinTypes::xsInteger,
305 cardinality: Cardinality::exactlyOne());
306 }
307 else
308 {
309 return makeGenericSequenceType(itemType: t->itemType() | m_operands.at(i: 1)->staticType()->itemType(),
310 cardinality: t->cardinality().toWithoutMany());
311 }
312}
313
314QT_END_NAMESPACE
315

source code of qtxmlpatterns/src/xmlpatterns/functions/qaggregatefns.cpp