Merge branch '4.7' of scm.dev.nokia.troll.no:qt/qt-qml into 4.7
[qt:qt.git] / src / script / api / qscriptvalueiterator.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
6 **
7 ** This file is part of the QtScript module of the Qt Toolkit.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL-ONLY$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser
12 ** General Public License version 2.1 as published by the Free Software
13 ** Foundation and appearing in the file LICENSE.LGPL included in the
14 ** packaging of this file.  Please review the following information to
15 ** ensure the GNU Lesser General Public License version 2.1 requirements
16 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 **
18 ** If you have questions regarding the use of this file, please contact
19 ** Nokia at qt-info@nokia.com.
20 ** $QT_END_LICENSE$
21 **
22 ****************************************************************************/
23
24 #include "config.h"
25 #include "qscriptvalueiterator.h"
26
27 #include "qscriptstring.h"
28 #include "qscriptengine.h"
29 #include "qscriptengine_p.h"
30 #include "qscriptvalue_p.h"
31 #include "qlinkedlist.h"
32
33
34 #include "JSObject.h"
35 #include "PropertyNameArray.h"
36 #include "JSArray.h"
37 #include "JSFunction.h"
38
39 QT_BEGIN_NAMESPACE
40
41 /*!
42   \since 4.3
43   \class QScriptValueIterator
44
45   \brief The QScriptValueIterator class provides a Java-style iterator for QScriptValue.
46
47   \ingroup script
48
49
50   The QScriptValueIterator constructor takes a QScriptValue as
51   argument.  After construction, the iterator is located at the very
52   beginning of the sequence of properties. Here's how to iterate over
53   all the properties of a QScriptValue:
54
55   \snippet doc/src/snippets/code/src_script_qscriptvalueiterator.cpp 0
56
57   The next() advances the iterator. The name(), value() and flags()
58   functions return the name, value and flags of the last item that was
59   jumped over.
60
61   If you want to remove properties as you iterate over the
62   QScriptValue, use remove(). If you want to modify the value of a
63   property, use setValue().
64
65   Note that QScriptValueIterator only iterates over the QScriptValue's
66   own properties; i.e. it does not follow the prototype chain. You can
67   use a loop like this to follow the prototype chain:
68
69   \snippet doc/src/snippets/code/src_script_qscriptvalueiterator.cpp 1
70
71   Note that QScriptValueIterator will not automatically skip over
72   properties that have the QScriptValue::SkipInEnumeration flag set;
73   that flag only affects iteration in script code.  If you want, you
74   can skip over such properties with code like the following:
75
76   \snippet doc/src/snippets/code/src_script_qscriptvalueiterator.cpp 2
77
78   \sa QScriptValue::property()
79 */
80
81 class QScriptValueIteratorPrivate
82 {
83 public:
84     QScriptValueIteratorPrivate()
85         : initialized(false)
86     {}
87
88     QScriptValuePrivate *object() const
89     {
90         return QScriptValuePrivate::get(objectValue);
91     }
92
93     QScriptEnginePrivate *engine() const
94     {
95         return QScriptEnginePrivate::get(objectValue.engine());
96     }
97
98     void ensureInitialized()
99     {
100         if (initialized)
101             return;
102         QScriptEnginePrivate *eng_p = engine();
103         QScript::APIShim shim(eng_p);
104         JSC::ExecState *exec = eng_p->globalExec();
105         JSC::PropertyNameArray propertyNamesArray(exec);
106         JSC::asObject(object()->jscValue)->getOwnPropertyNames(exec, propertyNamesArray, JSC::IncludeDontEnumProperties);
107
108         JSC::PropertyNameArray::const_iterator propertyNamesIt = propertyNamesArray.begin();
109         for(; propertyNamesIt != propertyNamesArray.end(); ++propertyNamesIt) {
110             propertyNames.append(*propertyNamesIt);
111         }
112         it = propertyNames.begin();
113         initialized = true;
114     }
115
116     QScriptValue objectValue;
117     QLinkedList<JSC::Identifier> propertyNames;
118     QLinkedList<JSC::Identifier>::iterator it;
119     QLinkedList<JSC::Identifier>::iterator current;
120     bool initialized;
121 };
122
123 /*!
124   Constructs an iterator for traversing \a object. The iterator is
125   set to be at the front of the sequence of properties (before the
126   first property).
127 */
128 QScriptValueIterator::QScriptValueIterator(const QScriptValue &object)
129     : d_ptr(0)
130 {
131     if (object.isObject()) {
132         d_ptr.reset(new QScriptValueIteratorPrivate());
133         d_ptr->objectValue = object;
134     }
135 }
136
137 /*!
138   Destroys the iterator.
139 */
140 QScriptValueIterator::~QScriptValueIterator()
141 {
142     Q_D(QScriptValueIterator);
143     if (d && d->engine()) {
144         // Make sure identifiers are removed from the correct engine.
145         QScript::APIShim shim(d->engine());
146         d->propertyNames.clear();
147     }
148 }
149
150 /*!
151   Returns true if there is at least one item ahead of the iterator
152   (i.e. the iterator is \e not at the back of the property sequence);
153   otherwise returns false.
154
155   \sa next(), hasPrevious()
156 */
157 bool QScriptValueIterator::hasNext() const
158 {
159     Q_D(const QScriptValueIterator);
160     if (!d)
161         return false;
162
163     const_cast<QScriptValueIteratorPrivate*>(d)->ensureInitialized();
164     return d->it != d->propertyNames.end();
165 }
166
167 /*!
168   Advances the iterator by one position.
169
170   Calling this function on an iterator located at the back of the
171   container leads to undefined results.
172
173   \sa hasNext(), previous(), name()
174 */
175 void QScriptValueIterator::next()
176 {
177     Q_D(QScriptValueIterator);
178     if (!d)
179         return;
180     d->ensureInitialized();
181
182     d->current = d->it;
183     ++(d->it);
184 }
185
186 /*!
187   Returns true if there is at least one item behind the iterator
188   (i.e. the iterator is \e not at the front of the property sequence);
189   otherwise returns false.
190
191   \sa previous(), hasNext()
192 */
193 bool QScriptValueIterator::hasPrevious() const
194 {
195     Q_D(const QScriptValueIterator);
196     if (!d)
197         return false;
198
199     const_cast<QScriptValueIteratorPrivate*>(d)->ensureInitialized();
200     return d->it != d->propertyNames.begin();
201 }
202
203 /*!
204   Moves the iterator back by one position.
205
206   Calling this function on an iterator located at the front of the
207   container leads to undefined results.
208
209   \sa hasPrevious(), next(), name()
210 */
211 void QScriptValueIterator::previous()
212 {
213     Q_D(QScriptValueIterator);
214     if (!d)
215         return;
216     d->ensureInitialized();
217     --(d->it);
218     d->current = d->it;
219 }
220
221 /*!
222   Moves the iterator to the front of the QScriptValue (before the
223   first property).
224
225   \sa toBack(), next()
226 */
227 void QScriptValueIterator::toFront()
228 {
229     Q_D(QScriptValueIterator);
230     if (!d)
231         return;
232     d->ensureInitialized();
233     d->it = d->propertyNames.begin();
234 }
235
236 /*!
237   Moves the iterator to the back of the QScriptValue (after the
238   last property).
239
240   \sa toFront(), previous()
241 */
242 void QScriptValueIterator::toBack()
243 {
244     Q_D(QScriptValueIterator);
245     if (!d)
246         return;
247     d->ensureInitialized();
248     d->it = d->propertyNames.end();
249 }
250
251 /*!
252   Returns the name of the last property that was jumped over using
253   next() or previous().
254
255   \sa value(), flags()
256 */
257 QString QScriptValueIterator::name() const
258 {
259     Q_D(const QScriptValueIterator);
260     if (!d || !d->initialized || !d->engine())
261         return QString();
262     return d->current->ustring();
263 }
264
265 /*!
266   \since 4.4
267
268   Returns the name of the last property that was jumped over using
269   next() or previous().
270 */
271 QScriptString QScriptValueIterator::scriptName() const
272 {
273     Q_D(const QScriptValueIterator);
274     if (!d || !d->initialized || !d->engine())
275         return QScriptString();
276     return d->engine()->toStringHandle(*d->current);
277 }
278
279 /*!
280   Returns the value of the last property that was jumped over using
281   next() or previous().
282
283   \sa setValue(), name()
284 */
285 QScriptValue QScriptValueIterator::value() const
286 {
287     Q_D(const QScriptValueIterator);
288     if (!d || !d->initialized || !d->engine())
289         return QScriptValue();
290     JSC::JSValue jsValue = d->object()->property(*d->current);
291     return d->engine()->scriptValueFromJSCValue(jsValue);
292 }
293
294 /*!
295   Sets the \a value of the last property that was jumped over using
296   next() or previous().
297
298   \sa value(), name()
299 */
300 void QScriptValueIterator::setValue(const QScriptValue &value)
301 {
302     Q_D(QScriptValueIterator);
303     if (!d || !d->initialized || !d->engine())
304         return;
305     JSC::JSValue jsValue = d->engine()->scriptValueToJSCValue(value);
306     d->object()->setProperty(*d->current, jsValue);
307 }
308
309 /*!
310   Returns the flags of the last property that was jumped over using
311   next() or previous().
312
313   \sa value()
314 */
315 QScriptValue::PropertyFlags QScriptValueIterator::flags() const
316 {
317     Q_D(const QScriptValueIterator);
318     if (!d || !d->initialized || !d->engine())
319         return 0;
320     return d->object()->propertyFlags(*d->current);
321 }
322
323 /*!
324   Removes the last property that was jumped over using next()
325   or previous().
326
327   \sa setValue()
328 */
329 void QScriptValueIterator::remove()
330 {
331     Q_D(QScriptValueIterator);
332     if (!d || !d->initialized || !d->engine())
333         return;
334     d->object()->setProperty(*d->current, JSC::JSValue());
335     d->propertyNames.erase(d->current);
336 }
337
338 /*!
339   Makes the iterator operate on \a object. The iterator is set to be
340   at the front of the sequence of properties (before the first
341   property).
342 */
343 QScriptValueIterator& QScriptValueIterator::operator=(QScriptValue &object)
344 {
345     d_ptr.reset();
346     if (object.isObject()) {
347         d_ptr.reset(new QScriptValueIteratorPrivate());
348         d_ptr->objectValue = object;
349     }
350     return *this;
351 }
352
353 QT_END_NAMESPACE