Merge remote branch 'origin/4.7' 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     ~QScriptValueIteratorPrivate()
89     {
90         if (!initialized)
91             return;
92         QScriptEnginePrivate *eng_p = engine();
93         if (!eng_p)
94             return;
95         QScript::APIShim shim(eng_p);
96         propertyNames.clear(); //destroying the identifiers need to be done under the APIShim guard
97     }
98
99     QScriptValuePrivate *object() const
100     {
101         return QScriptValuePrivate::get(objectValue);
102     }
103
104     QScriptEnginePrivate *engine() const
105     {
106         return QScriptEnginePrivate::get(objectValue.engine());
107     }
108
109     void ensureInitialized()
110     {
111         if (initialized)
112             return;
113         QScriptEnginePrivate *eng_p = engine();
114         QScript::APIShim shim(eng_p);
115         JSC::ExecState *exec = eng_p->globalExec();
116         JSC::PropertyNameArray propertyNamesArray(exec);
117         JSC::asObject(object()->jscValue)->getOwnPropertyNames(exec, propertyNamesArray, JSC::IncludeDontEnumProperties);
118
119         JSC::PropertyNameArray::const_iterator propertyNamesIt = propertyNamesArray.begin();
120         for(; propertyNamesIt != propertyNamesArray.end(); ++propertyNamesIt) {
121             propertyNames.append(*propertyNamesIt);
122         }
123         it = propertyNames.begin();
124         initialized = true;
125     }
126
127     QScriptValue objectValue;
128     QLinkedList<JSC::Identifier> propertyNames;
129     QLinkedList<JSC::Identifier>::iterator it;
130     QLinkedList<JSC::Identifier>::iterator current;
131     bool initialized;
132 };
133
134 /*!
135   Constructs an iterator for traversing \a object. The iterator is
136   set to be at the front of the sequence of properties (before the
137   first property).
138 */
139 QScriptValueIterator::QScriptValueIterator(const QScriptValue &object)
140     : d_ptr(0)
141 {
142     if (object.isObject()) {
143         d_ptr.reset(new QScriptValueIteratorPrivate());
144         d_ptr->objectValue = object;
145     }
146 }
147
148 /*!
149   Destroys the iterator.
150 */
151 QScriptValueIterator::~QScriptValueIterator()
152 {
153     Q_D(QScriptValueIterator);
154     if (d && d->engine()) {
155         // Make sure identifiers are removed from the correct engine.
156         QScript::APIShim shim(d->engine());
157         d->propertyNames.clear();
158     }
159 }
160
161 /*!
162   Returns true if there is at least one item ahead of the iterator
163   (i.e. the iterator is \e not at the back of the property sequence);
164   otherwise returns false.
165
166   \sa next(), hasPrevious()
167 */
168 bool QScriptValueIterator::hasNext() const
169 {
170     Q_D(const QScriptValueIterator);
171     if (!d)
172         return false;
173
174     const_cast<QScriptValueIteratorPrivate*>(d)->ensureInitialized();
175     return d->it != d->propertyNames.end();
176 }
177
178 /*!
179   Advances the iterator by one position.
180
181   Calling this function on an iterator located at the back of the
182   container leads to undefined results.
183
184   \sa hasNext(), previous(), name()
185 */
186 void QScriptValueIterator::next()
187 {
188     Q_D(QScriptValueIterator);
189     if (!d)
190         return;
191     d->ensureInitialized();
192
193     d->current = d->it;
194     ++(d->it);
195 }
196
197 /*!
198   Returns true if there is at least one item behind the iterator
199   (i.e. the iterator is \e not at the front of the property sequence);
200   otherwise returns false.
201
202   \sa previous(), hasNext()
203 */
204 bool QScriptValueIterator::hasPrevious() const
205 {
206     Q_D(const QScriptValueIterator);
207     if (!d)
208         return false;
209
210     const_cast<QScriptValueIteratorPrivate*>(d)->ensureInitialized();
211     return d->it != d->propertyNames.begin();
212 }
213
214 /*!
215   Moves the iterator back by one position.
216
217   Calling this function on an iterator located at the front of the
218   container leads to undefined results.
219
220   \sa hasPrevious(), next(), name()
221 */
222 void QScriptValueIterator::previous()
223 {
224     Q_D(QScriptValueIterator);
225     if (!d)
226         return;
227     d->ensureInitialized();
228     --(d->it);
229     d->current = d->it;
230 }
231
232 /*!
233   Moves the iterator to the front of the QScriptValue (before the
234   first property).
235
236   \sa toBack(), next()
237 */
238 void QScriptValueIterator::toFront()
239 {
240     Q_D(QScriptValueIterator);
241     if (!d)
242         return;
243     d->ensureInitialized();
244     d->it = d->propertyNames.begin();
245 }
246
247 /*!
248   Moves the iterator to the back of the QScriptValue (after the
249   last property).
250
251   \sa toFront(), previous()
252 */
253 void QScriptValueIterator::toBack()
254 {
255     Q_D(QScriptValueIterator);
256     if (!d)
257         return;
258     d->ensureInitialized();
259     d->it = d->propertyNames.end();
260 }
261
262 /*!
263   Returns the name of the last property that was jumped over using
264   next() or previous().
265
266   \sa value(), flags()
267 */
268 QString QScriptValueIterator::name() const
269 {
270     Q_D(const QScriptValueIterator);
271     if (!d || !d->initialized || !d->engine())
272         return QString();
273     return d->current->ustring();
274 }
275
276 /*!
277   \since 4.4
278
279   Returns the name of the last property that was jumped over using
280   next() or previous().
281 */
282 QScriptString QScriptValueIterator::scriptName() const
283 {
284     Q_D(const QScriptValueIterator);
285     if (!d || !d->initialized || !d->engine())
286         return QScriptString();
287     return d->engine()->toStringHandle(*d->current);
288 }
289
290 /*!
291   Returns the value of the last property that was jumped over using
292   next() or previous().
293
294   \sa setValue(), name()
295 */
296 QScriptValue QScriptValueIterator::value() const
297 {
298     Q_D(const QScriptValueIterator);
299     if (!d || !d->initialized || !d->engine())
300         return QScriptValue();
301     QScript::APIShim shim(d->engine());
302     JSC::JSValue jsValue = d->object()->property(*d->current);
303     return d->engine()->scriptValueFromJSCValue(jsValue);
304 }
305
306 /*!
307   Sets the \a value of the last property that was jumped over using
308   next() or previous().
309
310   \sa value(), name()
311 */
312 void QScriptValueIterator::setValue(const QScriptValue &value)
313 {
314     Q_D(QScriptValueIterator);
315     if (!d || !d->initialized || !d->engine())
316         return;
317     QScript::APIShim shim(d->engine());
318     JSC::JSValue jsValue = d->engine()->scriptValueToJSCValue(value);
319     d->object()->setProperty(*d->current, jsValue);
320 }
321
322 /*!
323   Returns the flags of the last property that was jumped over using
324   next() or previous().
325
326   \sa value()
327 */
328 QScriptValue::PropertyFlags QScriptValueIterator::flags() const
329 {
330     Q_D(const QScriptValueIterator);
331     if (!d || !d->initialized || !d->engine())
332         return 0;
333     QScript::APIShim shim(d->engine());
334     return d->object()->propertyFlags(*d->current);
335 }
336
337 /*!
338   Removes the last property that was jumped over using next()
339   or previous().
340
341   \sa setValue()
342 */
343 void QScriptValueIterator::remove()
344 {
345     Q_D(QScriptValueIterator);
346     if (!d || !d->initialized || !d->engine())
347         return;
348     QScript::APIShim shim(d->engine());
349     d->object()->setProperty(*d->current, JSC::JSValue());
350     d->propertyNames.erase(d->current);
351 }
352
353 /*!
354   Makes the iterator operate on \a object. The iterator is set to be
355   at the front of the sequence of properties (before the first
356   property).
357 */
358 QScriptValueIterator& QScriptValueIterator::operator=(QScriptValue &object)
359 {
360     d_ptr.reset();
361     if (object.isObject()) {
362         d_ptr.reset(new QScriptValueIteratorPrivate());
363         d_ptr->objectValue = object;
364     }
365     return *this;
366 }
367
368 QT_END_NAMESPACE