Qt 4.8
qacceltreebuilder.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 
48 template <bool FromDocument>
50  const QUrl &baseURI,
51  const NamePool::Ptr &np,
52  ReportContext *const context,
53  Features features) : m_preNumber(-1)
54  , m_isPreviousAtomic(false)
55  , m_hasCharacters(false)
56  , m_isCharactersCompressed(false)
57  , m_namePool(np)
58  , m_document(new AccelTree(docURI, baseURI))
59  , m_skippedDocumentNodes(0)
60  , m_documentURI(docURI)
61  , m_context(context)
62  , m_features(features)
63 {
64  Q_ASSERT(m_namePool);
65 
66  /* TODO Perhaps we can merge m_ancestors and m_size
67  * into one, and store a struct for the two instead? */
68  m_ancestors.reserve(DefaultNodeStackSize);
69  m_ancestors.push(-1);
70 
71  m_size.reserve(DefaultNodeStackSize);
72  m_size.push(0);
73 }
74 
75 template <bool FromDocument>
77 {
78  if(m_hasCharacters)
79  {
80  /* We create a node even if m_characters is empty.
81  * Remember that `text {""}' creates one text node
82  * with string value "". */
83 
84  ++m_preNumber;
85  m_document->basicData.append(AccelTree::BasicNodeData(currentDepth(),
86  currentParent(),
88  m_isCharactersCompressed ? AccelTree::IsCompressed : 0));
89  m_document->data.insert(m_preNumber, m_characters);
90  ++m_size.top();
91 
92  m_characters.clear(); /* We don't want it added twice. */
93  m_hasCharacters = false;
94 
95  if(m_isCharactersCompressed)
96  m_isCharactersCompressed = false;
97  }
98 }
99 
100 template <bool FromDocument>
102 {
103  Q_ASSERT(it);
104 
105  if(it.isAtomicValue())
106  {
107  if(m_isPreviousAtomic)
108  {
109  m_characters += QLatin1Char(' ');
110  m_characters += it.stringValue();
111  }
112  else
113  {
114  m_isPreviousAtomic = true;
115  const QString sv(it.stringValue());
116 
117  if(!sv.isEmpty())
118  {
119  m_characters += sv;
120  m_hasCharacters = true;
121  }
122  }
123  }
124  else
125  sendAsNode(it);
126 }
127 
128 template <bool FromDocument>
130 {
131  startElement(name, 1, 1);
132 }
133 
134 template <bool FromDocument>
136 {
137  startStructure();
138 
139  AccelTree::BasicNodeData data(currentDepth(), currentParent(), QXmlNodeModelIndex::Element, -1, name);
140  m_document->basicData.append(data);
141  if (m_features & SourceLocationsFeature)
142  m_document->sourcePositions.insert(m_document->maximumPreNumber(), qMakePair(line, column));
143 
144  ++m_preNumber;
145  m_ancestors.push(m_preNumber);
146 
147  ++m_size.top();
148  m_size.push(0);
149 
150  /* With node constructors, we can receive names for which we have no namespace
151  * constructors, such as in the query '<xs:space/>'. Since the 'xs' prefix has no
152  * NamespaceConstructor in this case, we synthesize the namespace.
153  *
154  * In case we're constructing from an XML document we avoid the call because
155  * although it's redundant, it's on extra virtual call for each element. */
156  if(!FromDocument)
157  namespaceBinding(QXmlName(name.namespaceURI(), 0, name.prefix()));
158 
159  m_isPreviousAtomic = false;
160 }
161 
162 template <bool FromDocument>
164 {
165  startStructure();
166  const AccelTree::PreNumber index = m_ancestors.pop();
167  AccelTree::BasicNodeData &data = m_document->basicData[index];
168 
169  /* Sub trees needs to be included in upper trees, so we add the count of this element
170  * to our parent. */
171  m_size[m_size.count() - 2] += m_size.top();
172 
173  data.setSize(m_size.pop());
174  m_isPreviousAtomic = false;
175 }
176 
177 template <bool FromDocument>
179 {
180  /* Attributes adds a namespace binding, so lets synthesize one.
181  *
182  * We optimize by checking whether we have a namespace for which a binding would
183  * be generated. Happens relatively rarely. */
184  if(name.hasPrefix())
185  namespaceBinding(QXmlName(name.namespaceURI(), 0, name.prefix()));
186 
187  m_document->basicData.append(AccelTree::BasicNodeData(currentDepth(), currentParent(), QXmlNodeModelIndex::Attribute, 0, name));
188  ++m_preNumber;
189  ++m_size.top();
190 
191  m_isPreviousAtomic = false;
192 
193  if(name.namespaceURI() == StandardNamespaces::xml && name.localName() == StandardLocalNames::id)
194  {
195  const QString normalized(value.toString().simplified());
196 
197  if(QXmlUtils::isNCName(normalized))
198  {
199  const QXmlName::LocalNameCode id = m_namePool->allocateLocalName(normalized);
200 
201  const int oldSize = m_document->m_IDs.count();
202  m_document->m_IDs.insert(id, currentParent());
203  /* We don't run the value through m_attributeCompress here, because
204  * the likelyhood of it deing identical to another attribute is
205  * very small. */
206  m_document->data.insert(m_preNumber, normalized);
207 
213  if(oldSize == m_document->m_IDs.count() && m_context) // TODO
214  {
215  Q_ASSERT(m_context);
216  m_context->error(QtXmlPatterns::tr("An %1-attribute with value %2 has already been declared.")
217  .arg(formatKeyword("xml:id"),
218  formatData(normalized)),
219  FromDocument ? ReportContext::FODC0002 : ReportContext::XQDY0091,
220  this);
221  }
222  }
223  else if(m_context) // TODO
224  {
225  Q_ASSERT(m_context);
226 
227  /* If we're building from an XML Document(e.g, we're fed from QXmlStreamReader, we raise FODC0002,
228  * otherwise XQDY0091. */
229  m_context->error(QtXmlPatterns::tr("An %1-attribute must have a "
230  "valid %2 as value, which %3 isn't.").arg(formatKeyword("xml:id"),
231  formatType(m_namePool, BuiltinTypes::xsNCName),
232  formatData(value.toString())),
233  FromDocument ? ReportContext::FODC0002 : ReportContext::XQDY0091,
234  this);
235  }
236  }
237  else
238  m_document->data.insert(m_preNumber, *m_attributeCompress.insert(value.toString()));
239 }
240 
241 template <bool FromDocument>
243 {
244 
245  /* If a text node constructor appears by itself, a node needs to
246  * be created. Therefore, we set m_hasCharacters
247  * if we're the only node.
248  * However, if the text node appears as a child of a document or element
249  * node it is discarded if it's empty.
250  */
251  if(m_hasCharacters && m_isCharactersCompressed)
252  {
253  m_characters = CompressedWhitespace::decompress(m_characters);
254  m_isCharactersCompressed = false;
255  }
256 
257  m_characters += ch;
258 
259  m_isPreviousAtomic = false;
260  m_hasCharacters = !m_characters.isEmpty() || m_preNumber == -1; /* -1 is our start value. */
261 }
262 
263 template <bool FromDocument>
265 {
266  Q_ASSERT(!ch.isEmpty());
267  Q_ASSERT(ch.toString().trimmed().isEmpty());
268 
269  /* This gets problematic due to how QXmlStreamReader works(which
270  * is the only one we get whitespaceOnly() events from). Namely, text intermingled
271  * with CDATA gets reported as individual Characters events, and
272  * QXmlStreamReader::isWhitespace() can return differently for each of those. However,
273  * it will occur very rarely, so this workaround of 1) mistakenly compressing 2) decompressing 3)
274  * appending, will happen infrequently.
275  */
276  if(m_hasCharacters)
277  {
278  if(m_isCharactersCompressed)
279  {
280  m_characters = CompressedWhitespace::decompress(m_characters);
281  m_isCharactersCompressed = false;
282  }
283 
284  m_characters.append(ch.toString());
285  }
286  else
287  {
288  /* We haven't received a text node previously. */
289  m_characters = CompressedWhitespace::compress(ch);
290  m_isCharactersCompressed = true;
291  m_isPreviousAtomic = false;
292  m_hasCharacters = true;
293  }
294 }
295 
296 template <bool FromDocument>
298  const QString &data)
299 {
300  startStructure();
301  ++m_preNumber;
302  m_document->data.insert(m_preNumber, data);
303 
304  m_document->basicData.append(AccelTree::BasicNodeData(currentDepth(),
305  currentParent(),
307  0,
308  target));
309  ++m_size.top();
310  m_isPreviousAtomic = false;
311 }
312 
313 template <bool FromDocument>
315 {
316  startStructure();
317  m_document->basicData.append(AccelTree::BasicNodeData(currentDepth(), currentParent(), QXmlNodeModelIndex::Comment, 0));
318  ++m_preNumber;
319  m_document->data.insert(m_preNumber, content);
320  ++m_size.top();
321 }
322 
323 template <bool FromDocument>
325 {
326  /* Note, because attribute() sometimes generate namespaceBinding() calls, this function
327  * can be called after attributes, in contrast to what the class documentation says. This is ok,
328  * as long as we're not dealing with public API. */
329 
330  /* If we've received attributes, it means the element's size have changed and m_preNumber have advanced,
331  * so "reverse back" to the actual element. */
332  const AccelTree::PreNumber pn = m_preNumber - m_size.top();
333 
334  QVector<QXmlName> &nss = m_document->namespaces[pn];
335 
336  /* "xml" hasn't been declared for each node, AccelTree::namespaceBindings() adds it, so avoid it
337  * such that we don't get duplicates. */
338  if(nb.prefix() == StandardPrefixes::xml)
339  return;
340 
341  /* If we already have the binding, skip it. */
342  const int len = nss.count();
343  for(int i = 0; i < len; ++i)
344  {
345  if(nss.at(i).prefix() == nb.prefix())
346  return;
347  }
348 
349  nss.append(nb);
350 }
351 
352 template <bool FromDocument>
354 {
355  /* If we have already received nodes, we can't add a document node. */
356  if(m_preNumber == -1) /* -1 is our start value. */
357  {
358  m_size.push(0);
359  m_document->basicData.append(AccelTree::BasicNodeData(0, -1, QXmlNodeModelIndex::Document, -1));
360  ++m_preNumber;
361  m_ancestors.push(m_preNumber);
362  }
363  else
364  ++m_skippedDocumentNodes;
365 
366  m_isPreviousAtomic = false;
367 }
368 
369 template <bool FromDocument>
371 {
372  if(m_skippedDocumentNodes == 0)
373  {
374  /* Create text nodes, if we've received any. We do this only if we're the
375  * top node because if we're getting this event as being a child of an element,
376  * text nodes or atomic values can appear after us, and which must get
377  * merged with the previous text.
378  *
379  * We call startStructure() before we pop the ancestor, such that the text node becomes
380  * a child of this document node. */
381  startStructure();
382 
383  m_document->basicData.first().setSize(m_size.pop());
384  m_ancestors.pop();
385  }
386  else
387  --m_skippedDocumentNodes;
388 
389  m_isPreviousAtomic = false;
390 }
391 
392 template <bool FromDocument>
394 {
395  Q_UNUSED(value);
396  // TODO
397 }
398 
399 template <bool FromDocument>
401 {
402  /* Create a text node, if we have received text in some way. */
403  startStructure();
404  m_document->printStats(m_namePool);
405 
406  return m_document;
407 }
408 
409 template <bool FromDocument>
411 {
412  Q_UNUSED(baseURI);
413  return NodeBuilder::Ptr(new AccelTreeBuilder(QUrl(), baseURI, m_namePool, m_context));
414 }
415 
416 template <bool FromDocument>
418 {
419 }
420 
421 template <bool FromDocument>
423 {
424 }
425 
426 template <bool FromDocument>
428 {
429  return this;
430 }
431 
432 template <bool FromDocument>
434 {
435  if(m_documentURI.isEmpty())
436  return QSourceLocation(QUrl(QLatin1String("AnonymousNodeTree")));
437  else
438  return QSourceLocation(m_documentURI);
439 }
440 
The QVariant class acts like a union for the most common Qt data types.
Definition: qvariant.h:92
QString toString() const
Returns a copy of the string reference as a QString object.
Definition: qstring.cpp:8653
NamespaceCode LocalNameCode
Definition: qxmlname.h:84
QString formatKeyword(const QString &keyword)
#define it(className, varName)
int count(const T &t) const
Returns the number of occurrences of value in the vector.
Definition: qvector.h:742
QString formatType(const NamePool::Ptr &np, const T &type)
Formats ItemType and SequenceType.
static Expression::Ptr create(Expression *const expr, const YYLTYPE &sourceLocator, const ParserContext *const parseInfo)
Builds an AccelTree from a stream of XML/Item events received through the NodeBuilder interface...
Definition: qacceltree_p.h:69
QLatin1String(DBUS_INTERFACE_DBUS))) Q_GLOBAL_STATIC_WITH_ARGS(QString
The QUrl class provides a convenient interface for working with URLs.
Definition: qurl.h:61
The QString class provides a Unicode character string.
Definition: qstring.h:83
#define Q_ASSERT(cond)
Definition: qglobal.h:1823
QString prefix(const QXmlNamePool &query) const
Returns the prefix.
Definition: qxmlname.cpp:370
bool hasPrefix() const
Returns true if this QXmlName has a non-empty prefix.
Definition: qnamepool_p.h:493
QString trimmed() const Q_REQUIRED_RESULT
Returns a string that has whitespace removed from the start and the end.
Definition: qstring.cpp:4506
bool isEmpty() const
Returns true if the string has no characters; otherwise returns false.
Definition: qstring.h:704
const char * name
void append(const T &t)
Inserts value at the end of the vector.
Definition: qvector.h:573
bool isEmpty() const
Returns true if the string reference has no characters; otherwise returns false.
Definition: qstring.h:1169
The QSourceLocation class identifies a location in a resource by URI, line, and column.
QString stringValue() const
Returns the string value of this Item.
Definition: qitem_p.h:302
static const char * data(const QByteArray &arr)
__int64 qint64
Definition: qglobal.h:942
static QString formatData(const QString &data)
int count() const
Definition: qstring.h:103
The QStringRef class provides a thin wrapper around QString substrings.
Definition: qstring.h:1099
const T & at(int i) const
Returns the item at index position i in the vector.
Definition: qvector.h:350
QString localName(const QXmlNamePool &query) const
Returns the local name.
Definition: qxmlname.cpp:387
NamespaceCode namespaceURI() const
Definition: qnamepool_p.h:503
bool isAtomicValue() const
Determines whether this item is an atomic value, or a node.
Definition: qitem_p.h:335
The QXmlName class represents the name of an XML node, in an efficient, namespace-aware way...
Definition: qxmlname.h:58
Represents an item in the XPath 2.0 Data Model.
Definition: qitem_p.h:182
Stores an XML document using the XPath Accelerator scheme, also known as pre/post numbering...
Definition: qacceltree_p.h:92
QString simplified() const Q_REQUIRED_RESULT
Returns a string that has whitespace removed from the start and the end, and that has each sequence o...
Definition: qstring.cpp:4415
Q_OUTOFLINE_TEMPLATE QPair< T1, T2 > qMakePair(const T1 &x, const T2 &y)
Definition: qpair.h:102
quint16 index
Houses data for a node, and that all node kinds have.
Definition: qacceltree_p.h:121
A callback for reporting errors.
#define Q_UNUSED(x)
Indicates to the compiler that the parameter with the specified name is not used in the body of a fun...
Definition: qglobal.h:1729
static bool isNCName(const QStringRef &ncName)
Determines whether c is a valid instance of production [4]NCName in the XML 1.0 Namespaces specificat...
Definition: qxmlutils.cpp:377
The QLatin1Char class provides an 8-bit ASCII/Latin-1 character.
Definition: qchar.h:55
void setSize(const PreNumber aSize)
Definition: qacceltree_p.h:164
Base class for all instances that represents something at a certain location.