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