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 //
41 //  W A R N I N G
42 //  -------------
43 //
44 // This file is not part of the Qt API.  It exists purely as an
45 // implementation detail.  This header file may change from version to
46 // version without notice, or even be removed.
47 //
48 // We mean it.
49 //
50 
51 /**
52  * @file
53  * @short This file is included by qcastingplatform_p.h.
54  * If you need includes in this file, put them in CasttingPlatform.h, outside of the namespace.
55  */
56 
57 template <typename TSubClass, const bool issueError>
castWithCaster(const Item & sourceValue,const AtomicCaster::Ptr & caster,const ReportContext::Ptr & context)58 Item CastingPlatform<TSubClass, issueError>::castWithCaster(const Item &sourceValue,
59                                                             const AtomicCaster::Ptr &caster,
60                                                             const ReportContext::Ptr &context) const
61 {
62     Q_ASSERT(sourceValue);
63     Q_ASSERT(caster);
64     Q_ASSERT(context);
65 
66     const Item retval(caster->castFrom(sourceValue, context));
67 
68     if(issueError)
69     {
70         if(retval.template as<AtomicValue>()->hasError())
71         {
72             issueCastError(retval, sourceValue, context);
73             return Item();
74         }
75         else
76             return retval;
77     }
78     else
79         return retval;
80 }
81 
82 template <typename TSubClass, const bool issueError>
cast(const Item & sourceValue,const ReportContext::Ptr & context)83 Item CastingPlatform<TSubClass, issueError>::cast(const Item &sourceValue,
84                                                   const ReportContext::Ptr &context) const
85 {
86     Q_ASSERT(sourceValue);
87     Q_ASSERT(context);
88     Q_ASSERT(targetType());
89 
90     if(m_caster)
91         return castWithCaster(sourceValue, m_caster, context);
92     else
93     {
94         bool castImpossible = false;
95         const AtomicCaster::Ptr caster(locateCaster(sourceValue.type(), context, castImpossible, static_cast<const TSubClass *>(this), targetType()));
96 
97         if(!issueError && castImpossible)
98         {
99             /* If we're supposed to issue an error(issueError) then this
100              * line will never be reached, because locateCaster() will in
101              * that case throw. */
102             return ValidationError::createError();
103         }
104         else
105             return castWithCaster(sourceValue, caster, context);
106     }
107 }
108 
109 template <typename TSubClass, const bool issueError>
prepareCasting(const ReportContext::Ptr & context,const ItemType::Ptr & sourceType)110 bool CastingPlatform<TSubClass, issueError>::prepareCasting(const ReportContext::Ptr &context,
111                                                             const ItemType::Ptr &sourceType)
112 {
113     Q_ASSERT(sourceType);
114     Q_ASSERT(context);
115 
116     if(*sourceType == *BuiltinTypes::xsAnyAtomicType ||
117        *sourceType == *BuiltinTypes::item ||
118        *sourceType == *CommonSequenceTypes::Empty ||
119        *sourceType == *BuiltinTypes::numeric)
120         return true; /* The type could not be narrowed better than xs:anyAtomicType
121                         or numeric at compile time. We'll do lookup at runtime instead. */
122 
123     bool castImpossible = false;
124     m_caster = locateCaster(sourceType, context, castImpossible, static_cast<const TSubClass *>(this), targetType());
125 
126     return !castImpossible;
127 }
128 
129 template <typename TSubClass, const bool issueError>
locateCaster(const ItemType::Ptr & sourceType,const ReportContext::Ptr & context,bool & castImpossible,const SourceLocationReflection * const location,const ItemType::Ptr & targetType)130 AtomicCaster::Ptr CastingPlatform<TSubClass, issueError>::locateCaster(const ItemType::Ptr &sourceType,
131                                                                        const ReportContext::Ptr &context,
132                                                                        bool &castImpossible,
133                                                                        const SourceLocationReflection *const location,
134                                                                        const ItemType::Ptr &targetType)
135 {
136     Q_ASSERT(sourceType);
137     Q_ASSERT(targetType);
138 
139     const AtomicCasterLocator::Ptr locator(static_cast<AtomicType *>(
140             targetType.data())->casterLocator());
141     if(!locator)
142     {
143         if(issueError)
144         {
145             context->error(QtXmlPatterns::tr("No casting is possible with %1 as the target type.")
146                                         .arg(formatType(context->namePool(), targetType)),
147                                        ReportContext::XPTY0004, location);
148         }
149         else
150             castImpossible = true;
151 
152         return AtomicCaster::Ptr();
153     }
154 
155     const AtomicCaster::Ptr caster(static_cast<const AtomicType *>(sourceType.data())->accept(locator, location));
156     if(!caster)
157     {
158         if(issueError)
159         {
160             context->error(QtXmlPatterns::tr("It is not possible to cast from %1 to %2.")
161                                             .arg(formatType(context->namePool(), sourceType))
162                                             .arg(formatType(context->namePool(), targetType)),
163                                        ReportContext::XPTY0004, location);
164         }
165         else
166             castImpossible = true;
167 
168         return AtomicCaster::Ptr();
169     }
170 
171     return caster;
172 }
173 
174 template <typename TSubClass, const bool issueError>
checkTargetType(const ReportContext::Ptr & context)175 void CastingPlatform<TSubClass, issueError>::checkTargetType(const ReportContext::Ptr &context) const
176 {
177     Q_ASSERT(context);
178 
179     const ItemType::Ptr tType(targetType());
180     Q_ASSERT(tType);
181     Q_ASSERT(tType->isAtomicType());
182     const AtomicType::Ptr asAtomic(tType);
183 
184     /* This catches casting to xs:NOTATION and xs:anyAtomicType. */
185     if(asAtomic->isAbstract())
186     {
187         context->error(QtXmlPatterns::tr("Casting to %1 is not possible because it "
188                                      "is an abstract type, and can therefore never be instantiated.")
189                                 .arg(formatType(context->namePool(), tType)),
190                           ReportContext::XPST0080,
191                           static_cast<const TSubClass*>(this));
192     }
193 }
194 
195 template <typename TSubClass, const bool issueError>
issueCastError(const Item & validationError,const Item & sourceValue,const ReportContext::Ptr & context)196 void CastingPlatform<TSubClass, issueError>::issueCastError(const Item &validationError,
197                                                             const Item &sourceValue,
198                                                             const ReportContext::Ptr &context) const
199 {
200     Q_ASSERT(validationError);
201     Q_ASSERT(context);
202     Q_ASSERT(validationError.isAtomicValue());
203     Q_ASSERT(validationError.template as<AtomicValue>()->hasError());
204 
205     const ValidationError::Ptr err(validationError.template as<ValidationError>());
206     QString msg(err->message());
207 
208     if(msg.isNull())
209     {
210         msg = QtXmlPatterns::tr("It's not possible to cast the value %1 of type %2 to %3")
211                  .arg(formatData(sourceValue.stringValue()))
212                  .arg(formatType(context->namePool(), sourceValue.type()))
213                  .arg(formatType(context->namePool(), targetType()));
214     }
215     else
216     {
217         Q_ASSERT(!msg.isEmpty());
218         msg = QtXmlPatterns::tr("Failure when casting from %1 to %2: %3")
219                  .arg(formatType(context->namePool(), sourceValue.type()))
220                  .arg(formatType(context->namePool(), targetType()))
221                  .arg(msg);
222     }
223 
224     /* If m_errorCode is FORG0001, we assume our sub-classer doesn't have a
225      * special wish about error code, so then we use the error object's code.
226      */
227     context->error(msg, m_errorCode == ReportContext::FORG0001 ? err->errorCode() : m_errorCode,
228                    static_cast<const TSubClass*>(this));
229 }
230 
231