Merge remote branch 'origin/4.6' into qt-4.7-from-4.6
[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 }
143
144 /*!
145   Returns true if there is at least one item ahead of the iterator
146   (i.e. the iterator is \e not at the back of the property sequence);
147   otherwise returns false.
148
149   \sa next(), hasPrevious()
150 */
151 bool QScriptValueIterator::hasNext() const
152 {
153     Q_D(const QScriptValueIterator);
154     if (!d)
155         return false;
156
157     const_cast<QScriptValueIteratorPrivate*>(d)->ensureInitialized();
158     return d->it != d->propertyNames.end();
159 }
160
161 /*!
162   Advances the iterator by one position.
163
164   Calling this function on an iterator located at the back of the
165   container leads to undefined results.
166
167   \sa hasNext(), previous(), name()
168 */
169 void QScriptValueIterator::next()
170 {
171     Q_D(QScriptValueIterator);
172     if (!d)
173         return;
174     d->ensureInitialized();
175
176     d->current = d->it;
177     ++(d->it);
178 }
179
180 /*!
181   Returns true if there is at least one item behind the iterator
182   (i.e. the iterator is \e not at the front of the property sequence);
183   otherwise returns false.
184
185   \sa previous(), hasNext()
186 */
187 bool QScriptValueIterator::hasPrevious() const
188 {
189     Q_D(const QScriptValueIterator);
190     if (!d)
191         return false;
192
193     const_cast<QScriptValueIteratorPrivate*>(d)->ensureInitialized();
194     return d->it != d->propertyNames.begin();
195 }
196
197 /*!
198   Moves the iterator back by one position.
199
200   Calling this function on an iterator located at the front of the
201   container leads to undefined results.
202
203   \sa hasPrevious(), next(), name()
204 */
205 void QScriptValueIterator::previous()
206 {
207     Q_D(QScriptValueIterator);
208     if (!d)
209         return;
210     d->ensureInitialized();
211     --(d->it);
212     d->current = d->it;
213 }
214
215 /*!
216   Moves the iterator to the front of the QScriptValue (before the
217   first property).
218
219   \sa toBack(), next()
220 */
221 void QScriptValueIterator::toFront()
222 {
223     Q_D(QScriptValueIterator);
224     if (!d)
225         return;
226     d->ensureInitialized();
227     d->it = d->propertyNames.begin();
228 }
229
230 /*!
231   Moves the iterator to the back of the QScriptValue (after the
232   last property).
233
234   \sa toFront(), previous()
235 */
236 void QScriptValueIterator::toBack()
237 {
238     Q_D(QScriptValueIterator);
239     if (!d)
240         return;
241     d->ensureInitialized();
242     d->it = d->propertyNames.end();
243 }
244
245 /*!
246   Returns the name of the last property that was jumped over using
247   next() or previous().
248
249   \sa value(), flags()
250 */
251 QString QScriptValueIterator::name() const
252 {
253     Q_D(const QScriptValueIterator);
254     if (!d || !d->initialized || !d->engine())
255         return QString();
256     return d->current->ustring();
257 }
258
259 /*!
260   \since 4.4
261
262   Returns the name of the last property that was jumped over using
263   next() or previous().
264 */
265 QScriptString QScriptValueIterator::scriptName() const
266 {
267     Q_D(const QScriptValueIterator);
268     if (!d || !d->initialized || !d->engine())
269         return QScriptString();
270     return d->engine()->toStringHandle(*d->current);
271 }
272
273 /*!
274   Returns the value of the last property that was jumped over using
275   next() or previous().
276
277   \sa setValue(), name()
278 */
279 QScriptValue QScriptValueIterator::value() const
280 {
281     Q_D(const QScriptValueIterator);
282     if (!d || !d->initialized || !d->engine())
283         return QScriptValue();
284     JSC::JSValue jsValue = d->object()->property(*d->current);
285     return d->engine()->scriptValueFromJSCValue(jsValue);
286 }
287
288 /*!
289   Sets the \a value of the last property that was jumped over using
290   next() or previous().
291
292   \sa value(), name()
293 */
294 void QScriptValueIterator::setValue(const QScriptValue &value)
295 {
296     Q_D(QScriptValueIterator);
297     if (!d || !d->initialized || !d->engine())
298         return;
299     JSC::JSValue jsValue = d->engine()->scriptValueToJSCValue(value);
300     d->object()->setProperty(*d->current, jsValue);
301 }
302
303 /*!
304   Returns the flags of the last property that was jumped over using
305   next() or previous().
306
307   \sa value()
308 */
309 QScriptValue::PropertyFlags QScriptValueIterator::flags() const
310 {
311     Q_D(const QScriptValueIterator);
312     if (!d || !d->initialized || !d->engine())
313         return 0;
314     return d->object()->propertyFlags(*d->current);
315 }
316
317 /*!
318   Removes the last property that was jumped over using next()
319   or previous().
320
321   \sa setValue()
322 */
323 void QScriptValueIterator::remove()
324 {
325     Q_D(QScriptValueIterator);
326     if (!d || !d->initialized || !d->engine())
327         return;
328     d->object()->setProperty(*d->current, JSC::JSValue());
329     d->propertyNames.erase(d->current);
330 }
331
332 /*!
333   Makes the iterator operate on \a object. The iterator is set to be
334   at the front of the sequence of properties (before the first
335   property).
336 */
337 QScriptValueIterator& QScriptValueIterator::operator=(QScriptValue &object)
338 {
339     d_ptr.reset();
340     if (object.isObject()) {
341         d_ptr.reset(new QScriptValueIteratorPrivate());
342         d_ptr->objectValue = object;
343     }
344     return *this;
345 }
346
347 QT_END_NAMESPACE