Initial import from the monolithic Qt.
[profile/ivi/qtdeclarative.git] / src / declarative / qml / qdeclarativetypeloader.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2011 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 QtDeclarative module of the Qt Toolkit.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** No Commercial Usage
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
14 ** this package.
15 **
16 ** GNU Lesser General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU Lesser
18 ** General Public License version 2.1 as published by the Free Software
19 ** Foundation and appearing in the file LICENSE.LGPL included in the
20 ** packaging of this file.  Please review the following information to
21 ** ensure the GNU Lesser General Public License version 2.1 requirements
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23 **
24 ** In addition, as a special exception, Nokia gives you certain additional
25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27 **
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
30 **
31 **
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qdeclarativetypeloader_p.h"
43
44 #include <private/qdeclarativeengine_p.h>
45 #include <private/qdeclarativecompiler_p.h>
46 #include <private/qdeclarativecomponent_p.h>
47 #include <private/qdeclarativeglobal_p.h>
48 #include <private/qdeclarativedebugtrace_p.h>
49
50 #include <QtDeclarative/qdeclarativecomponent.h>
51 #include <QtCore/qdebug.h>
52 #include <QtCore/qdir.h>
53 #include <QtCore/qfile.h>
54
55 QT_BEGIN_NAMESPACE
56
57 /*!
58 \class QDeclarativeDataBlob
59 \brief The QDeclarativeDataBlob encapsulates a data request that can be issued to a QDeclarativeDataLoader.
60 \internal
61
62 QDeclarativeDataBlob's are loaded by a QDeclarativeDataLoader.  The user creates the QDeclarativeDataBlob
63 and then calls QDeclarativeDataLoader::load() or QDeclarativeDataLoader::loadWithStaticData() to load it.
64 The QDeclarativeDataLoader invokes callbacks on the QDeclarativeDataBlob as data becomes available.
65 */
66
67 /*!
68 \enum QDeclarativeDataBlob::Status
69
70 This enum describes the status of the data blob.
71
72 \list
73 \o Null The blob has not yet been loaded by a QDeclarativeDataLoader
74 \o Loading The blob is loading network data.  The QDeclarativeDataBlob::setData() callback has not yet been
75 invoked or has not yet returned.
76 \o WaitingForDependencies The blob is waiting for dependencies to be done before continueing.  This status
77 only occurs after the QDeclarativeDataBlob::setData() callback has been made, and when the blob has outstanding
78 dependencies.
79 \o Complete The blob's data has been loaded and all dependencies are done.
80 \o Error An error has been set on this blob.
81 \endlist
82 */
83
84 /*!
85 \enum QDeclarativeDataBlob::Type
86
87 This enum describes the type of the data blob.
88
89 \list
90 \o QmlFile This is a QDeclarativeTypeData
91 \o JavaScriptFile This is a QDeclarativeScriptData
92 \o QmldirFile This is a QDeclarativeQmldirData
93 \endlist
94 */
95
96 /*!
97 Create a new QDeclarativeDataBlob for \a url and of the provided \a type.
98 */
99 QDeclarativeDataBlob::QDeclarativeDataBlob(const QUrl &url, Type type)
100 : m_type(type), m_status(Null), m_progress(0), m_url(url), m_finalUrl(url), m_manager(0),
101   m_redirectCount(0), m_inCallback(false), m_isDone(false)
102 {
103 }
104
105 /*!  \internal */
106 QDeclarativeDataBlob::~QDeclarativeDataBlob()
107 {
108     Q_ASSERT(m_waitingOnMe.isEmpty());
109
110     cancelAllWaitingFor();
111 }
112
113 /*!
114 Returns the type provided to the constructor.
115 */
116 QDeclarativeDataBlob::Type QDeclarativeDataBlob::type() const
117 {
118     return m_type;
119 }
120
121 /*!
122 Returns the blob's status.
123 */
124 QDeclarativeDataBlob::Status QDeclarativeDataBlob::status() const
125 {
126     return m_status;
127 }
128
129 /*!
130 Returns true if the status is Null.
131 */
132 bool QDeclarativeDataBlob::isNull() const
133 {
134     return m_status == Null;
135 }
136
137 /*!
138 Returns true if the status is Loading.
139 */
140 bool QDeclarativeDataBlob::isLoading() const
141 {
142     return m_status == Loading;
143 }
144
145 /*!
146 Returns true if the status is WaitingForDependencies.
147 */
148 bool QDeclarativeDataBlob::isWaiting() const
149 {
150     return m_status == WaitingForDependencies;
151 }
152
153 /*!
154 Returns true if the status is Complete.
155 */
156 bool QDeclarativeDataBlob::isComplete() const
157 {
158     return m_status == Complete;
159 }
160
161 /*!
162 Returns true if the status is Error.
163 */
164 bool QDeclarativeDataBlob::isError() const
165 {
166     return m_status == Error;
167 }
168
169 /*!
170 Returns true if the status is Complete or Error.
171 */
172 bool QDeclarativeDataBlob::isCompleteOrError() const
173 {
174     return isComplete() || isError();
175 }
176
177 /*!
178 Returns the data download progress from 0 to 1.
179 */
180 qreal QDeclarativeDataBlob::progress() const
181 {
182     return m_progress;
183 }
184
185 /*!
186 Returns the blob url passed to the constructor.  If a network redirect
187 happens while fetching the data, this url remains the same.
188
189 \sa finalUrl()
190 */
191 QUrl QDeclarativeDataBlob::url() const
192 {
193     return m_url;
194 }
195
196 /*!
197 Returns the final url of the data.  Initially this is the same as
198 url(), but if a network redirect happens while fetching the data, this url
199 is updated to reflect the new location.
200 */
201 QUrl QDeclarativeDataBlob::finalUrl() const
202 {
203     return m_finalUrl;
204 }
205
206 /*!
207 Return the errors on this blob.
208 */
209 QList<QDeclarativeError> QDeclarativeDataBlob::errors() const
210 {
211     return m_errors;
212 }
213
214 /*!
215 Mark this blob as having \a errors.
216
217 All outstanding dependencies will be cancelled.  Requests to add new dependencies 
218 will be ignored.  Entry into the Error state is irreversable, although you can change the 
219 specific errors by additional calls to setError.
220 */
221 void QDeclarativeDataBlob::setError(const QDeclarativeError &errors)
222 {
223     QList<QDeclarativeError> l;
224     l << errors;
225     setError(l);
226 }
227
228 /*!
229 \overload
230 */
231 void QDeclarativeDataBlob::setError(const QList<QDeclarativeError> &errors)
232 {
233     m_status = Error;
234     m_errors = errors;
235
236     cancelAllWaitingFor();
237
238     if (!m_inCallback)
239         tryDone();
240 }
241
242 /*! 
243 Wait for \a blob to become complete or to error.  If \a blob is already 
244 complete or in error, or this blob is already complete, this has no effect.
245 */
246 void QDeclarativeDataBlob::addDependency(QDeclarativeDataBlob *blob)
247 {
248     Q_ASSERT(status() != Null);
249
250     if (!blob ||
251         blob->status() == Error || blob->status() == Complete ||
252         status() == Error || status() == Complete ||
253         m_waitingFor.contains(blob))
254         return;
255
256     blob->addref();
257     m_status = WaitingForDependencies;
258     m_waitingFor.append(blob);
259     blob->m_waitingOnMe.append(this);
260 }
261
262 /*!
263 \fn void QDeclarativeDataBlob::dataReceived(const QByteArray &data)
264
265 Invoked when data for the blob is received.  Implementors should use this callback
266 to determine a blob's dependencies.  Within this callback you may call setError()
267 or addDependency().
268 */
269
270 /*!
271 Invoked once data has either been received or a network error occurred, and all 
272 dependencies are complete.
273
274 You can set an error in this method, but you cannot add new dependencies.  Implementors
275 should use this callback to finalize processing of data.
276
277 The default implementation does nothing.
278 */
279 void QDeclarativeDataBlob::done()
280 {
281 }
282
283 /*!
284 Invoked if there is a network error while fetching this blob.
285
286 The default implementation sets an appropriate QDeclarativeError.
287 */
288 void QDeclarativeDataBlob::networkError(QNetworkReply::NetworkError networkError)
289 {
290     Q_UNUSED(networkError);
291
292     QDeclarativeError error;
293     error.setUrl(m_finalUrl);
294
295     const char *errorString = 0;
296     switch (networkError) {
297         default:
298             errorString = "Network error";
299             break;
300         case QNetworkReply::ConnectionRefusedError:
301             errorString = "Connection refused";
302             break;
303         case QNetworkReply::RemoteHostClosedError:
304             errorString = "Remote host closed the connection";
305             break;
306         case QNetworkReply::HostNotFoundError:
307             errorString = "Host not found";
308             break;
309         case QNetworkReply::TimeoutError:
310             errorString = "Timeout";
311             break;
312         case QNetworkReply::ProxyConnectionRefusedError:
313         case QNetworkReply::ProxyConnectionClosedError:
314         case QNetworkReply::ProxyNotFoundError:
315         case QNetworkReply::ProxyTimeoutError:
316         case QNetworkReply::ProxyAuthenticationRequiredError:
317         case QNetworkReply::UnknownProxyError:
318             errorString = "Proxy error";
319             break;
320         case QNetworkReply::ContentAccessDenied:
321             errorString = "Access denied";
322             break;
323         case QNetworkReply::ContentNotFoundError:
324             errorString = "File not found";
325             break;
326         case QNetworkReply::AuthenticationRequiredError:
327             errorString = "Authentication required";
328             break;
329     };
330
331     error.setDescription(QLatin1String(errorString));
332
333     setError(error);
334 }
335
336 /*! 
337 Called if \a blob, which was previously waited for, has an error.
338
339 The default implementation does nothing.
340 */
341 void QDeclarativeDataBlob::dependencyError(QDeclarativeDataBlob *blob)
342 {
343     Q_UNUSED(blob);
344 }
345
346 /*!
347 Called if \a blob, which was previously waited for, has completed.
348
349 The default implementation does nothing.
350 */
351 void QDeclarativeDataBlob::dependencyComplete(QDeclarativeDataBlob *blob)
352 {
353     Q_UNUSED(blob);
354 }
355
356 /*! 
357 Called when all blobs waited for have completed.  This occurs regardless of 
358 whether they are in error, or complete state.  
359
360 The default implementation does nothing.
361 */
362 void QDeclarativeDataBlob::allDependenciesDone()
363 {
364 }
365
366 /*!
367 Called when the download progress of this blob changes.  \a progress goes
368 from 0 to 1.
369 */
370 void QDeclarativeDataBlob::downloadProgressChanged(qreal progress)
371 {
372     Q_UNUSED(progress);
373 }
374
375 void QDeclarativeDataBlob::tryDone()
376 {
377     if (status() != Loading && m_waitingFor.isEmpty() && !m_isDone) {
378         if (status() != Error)
379             m_status = Complete;
380
381         m_isDone = true;
382         done();
383         notifyAllWaitingOnMe();
384     }
385 }
386
387 void QDeclarativeDataBlob::cancelAllWaitingFor()
388 {
389     while (m_waitingFor.count()) {
390         QDeclarativeDataBlob *blob = m_waitingFor.takeLast();
391
392         Q_ASSERT(blob->m_waitingOnMe.contains(this));
393
394         blob->m_waitingOnMe.removeOne(this);
395
396         blob->release();
397     }
398 }
399
400 void QDeclarativeDataBlob::notifyAllWaitingOnMe()
401 {
402     while (m_waitingOnMe.count()) {
403         QDeclarativeDataBlob *blob = m_waitingOnMe.takeLast();
404
405         Q_ASSERT(blob->m_waitingFor.contains(this));
406
407         blob->notifyComplete(this);
408     }
409 }
410
411 void QDeclarativeDataBlob::notifyComplete(QDeclarativeDataBlob *blob)
412 {
413     Q_ASSERT(m_waitingFor.contains(blob));
414     Q_ASSERT(blob->status() == Error || blob->status() == Complete);
415
416     m_inCallback = true;
417
418     if (blob->status() == Error) {
419         dependencyError(blob);
420     } else if (blob->status() == Complete) {
421         dependencyComplete(blob);
422     }
423
424     m_waitingFor.removeOne(blob);
425     blob->release();
426
427     if (!isError() && m_waitingFor.isEmpty()) 
428         allDependenciesDone();
429
430     m_inCallback = false;
431
432     tryDone();
433 }
434
435 /*!
436 \class QDeclarativeDataLoader
437 \brief The QDeclarativeDataLoader class abstracts loading files and their dependecies over the network.
438 \internal
439
440 The QDeclarativeDataLoader class is provided for the exclusive use of the QDeclarativeTypeLoader class.
441
442 Clients create QDeclarativeDataBlob instances and submit them to the QDeclarativeDataLoader class
443 through the QDeclarativeDataLoader::load() or QDeclarativeDataLoader::loadWithStaticData() methods.
444 The loader then fetches the data over the network or from the local file system in an efficient way.
445 QDeclarativeDataBlob is an abstract class, so should always be specialized.
446
447 Once data is received, the QDeclarativeDataBlob::dataReceived() method is invoked on the blob.  The
448 derived class should use this callback to process the received data.  Processing of the data can 
449 result in an error being set (QDeclarativeDataBlob::setError()), or one or more dependencies being
450 created (QDeclarativeDataBlob::addDependency()).  Dependencies are other QDeclarativeDataBlob's that
451 are required before processing can fully complete.
452
453 To complete processing, the QDeclarativeDataBlob::done() callback is invoked.  done() is called when
454 one of these three preconditions are met.
455
456 1.  The QDeclarativeDataBlob has no dependencies.
457 2.  The QDeclarativeDataBlob has an error set.
458 3.  All the QDeclarativeDataBlob's dependencies are themselves "done()".
459
460 Thus QDeclarativeDataBlob::done() will always eventually be called, even if the blob has an error set.
461 */
462
463 /*!
464 Create a new QDeclarativeDataLoader for \a engine.
465 */
466 QDeclarativeDataLoader::QDeclarativeDataLoader(QDeclarativeEngine *engine)
467 : m_engine(engine)
468 {
469 }
470
471 /*! \internal */
472 QDeclarativeDataLoader::~QDeclarativeDataLoader()
473 {
474     for (NetworkReplies::Iterator iter = m_networkReplies.begin(); iter != m_networkReplies.end(); ++iter) 
475         (*iter)->release();
476 }
477
478 /*!
479 Load the provided \a blob from the network or filesystem.
480 */
481 void QDeclarativeDataLoader::load(QDeclarativeDataBlob *blob)
482 {
483     Q_ASSERT(blob->status() == QDeclarativeDataBlob::Null);
484     Q_ASSERT(blob->m_manager == 0);
485
486     blob->m_status = QDeclarativeDataBlob::Loading;
487
488     if (blob->m_url.isEmpty()) {
489         QDeclarativeError error;
490         error.setDescription(QLatin1String("Invalid null URL"));
491         blob->setError(error);
492         return;
493     }
494
495     QString lf = QDeclarativeEnginePrivate::urlToLocalFileOrQrc(blob->m_url);
496
497     if (!lf.isEmpty()) {
498         if (!QDeclarative_isFileCaseCorrect(lf)) {
499             QDeclarativeError error;
500             error.setUrl(blob->m_url);
501             error.setDescription(QLatin1String("File name case mismatch"));
502             blob->setError(error);
503             return;
504         }
505         QFile file(lf);
506         if (file.open(QFile::ReadOnly)) {
507             QByteArray data = file.readAll();
508
509             blob->m_progress = 1.;
510             blob->downloadProgressChanged(1.);
511
512             setData(blob, data);
513         } else {
514             blob->networkError(QNetworkReply::ContentNotFoundError);
515         }
516
517     } else {
518
519         blob->m_manager = this;
520         QNetworkReply *reply = m_engine->networkAccessManager()->get(QNetworkRequest(blob->m_url));
521         QObject::connect(reply, SIGNAL(downloadProgress(qint64,qint64)), 
522                          this, SLOT(networkReplyProgress(qint64,qint64)));
523         QObject::connect(reply, SIGNAL(finished()), 
524                          this, SLOT(networkReplyFinished()));
525         m_networkReplies.insert(reply, blob);
526
527         blob->addref();
528     }
529 }
530
531 #define DATALOADER_MAXIMUM_REDIRECT_RECURSION 16
532
533 void QDeclarativeDataLoader::networkReplyFinished()
534 {
535     QNetworkReply *reply = static_cast<QNetworkReply *>(sender());
536     reply->deleteLater();
537
538     QDeclarativeDataBlob *blob = m_networkReplies.take(reply);
539
540     Q_ASSERT(blob);
541
542     blob->m_redirectCount++;
543
544     if (blob->m_redirectCount < DATALOADER_MAXIMUM_REDIRECT_RECURSION) {
545         QVariant redirect = reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
546         if (redirect.isValid()) {
547             QUrl url = reply->url().resolved(redirect.toUrl());
548             blob->m_finalUrl = url;
549
550             QNetworkReply *reply = m_engine->networkAccessManager()->get(QNetworkRequest(url));
551             QObject::connect(reply, SIGNAL(finished()), this, SLOT(networkReplyFinished()));
552             m_networkReplies.insert(reply, blob);
553             return;
554         }
555     }
556
557     if (reply->error()) {
558         blob->networkError(reply->error());
559     } else {
560         QByteArray data = reply->readAll();
561         setData(blob, data);
562     }
563
564     blob->release();
565 }
566
567 void QDeclarativeDataLoader::networkReplyProgress(qint64 bytesReceived, qint64 bytesTotal)
568 {
569     QNetworkReply *reply = static_cast<QNetworkReply *>(sender());
570     QDeclarativeDataBlob *blob = m_networkReplies.value(reply);
571
572     Q_ASSERT(blob);
573
574     if (bytesTotal != 0) {
575         blob->m_progress = bytesReceived / bytesTotal;
576         blob->downloadProgressChanged(blob->m_progress);
577     }
578 }
579
580 /*!
581 Load the provided \a blob with \a data.  The blob's URL is not used by the data loader in this case.
582 */
583 void QDeclarativeDataLoader::loadWithStaticData(QDeclarativeDataBlob *blob, const QByteArray &data)
584 {
585     Q_ASSERT(blob->status() == QDeclarativeDataBlob::Null);
586     Q_ASSERT(blob->m_manager == 0);
587     
588     blob->m_status = QDeclarativeDataBlob::Loading;
589
590     setData(blob, data);
591 }
592
593 /*!
594 Return the QDeclarativeEngine associated with this loader
595 */
596 QDeclarativeEngine *QDeclarativeDataLoader::engine() const
597 {
598     return m_engine;
599 }
600
601 void QDeclarativeDataLoader::setData(QDeclarativeDataBlob *blob, const QByteArray &data)
602 {
603     blob->m_inCallback = true;
604
605     blob->dataReceived(data);
606
607     if (!blob->isError() && !blob->isWaiting())
608         blob->allDependenciesDone();
609
610     if (blob->status() != QDeclarativeDataBlob::Error) 
611         blob->m_status = QDeclarativeDataBlob::WaitingForDependencies;
612
613     blob->m_inCallback = false;
614
615     blob->tryDone();
616 }
617
618 /*!
619 \class QDeclarativeTypeLoader
620 */
621 QDeclarativeTypeLoader::QDeclarativeTypeLoader(QDeclarativeEngine *engine)
622 : QDeclarativeDataLoader(engine)
623 {
624 }
625
626 QDeclarativeTypeLoader::~QDeclarativeTypeLoader()
627 {
628     clearCache();
629 }
630
631 /*!
632 \enum QDeclarativeTypeLoader::Option
633
634 This enum defines the options that control the way type data is handled.
635
636 \value None             The default value, indicating that no other options
637                         are enabled.
638 \value PreserveParser   The parser used to handle the type data is preserved
639                         after the data has been parsed.
640 */
641
642 /*!
643 Returns a QDeclarativeTypeData for the specified \a url.  The QDeclarativeTypeData may be cached.
644 */
645 QDeclarativeTypeData *QDeclarativeTypeLoader::get(const QUrl &url)
646 {
647     Q_ASSERT(!url.isRelative() && 
648             (QDeclarativeEnginePrivate::urlToLocalFileOrQrc(url).isEmpty() || 
649              !QDir::isRelativePath(QDeclarativeEnginePrivate::urlToLocalFileOrQrc(url))));
650
651     QDeclarativeTypeData *typeData = m_typeCache.value(url);
652
653     if (!typeData) {
654         typeData = new QDeclarativeTypeData(url, None, this);
655         m_typeCache.insert(url, typeData);
656         QDeclarativeDataLoader::load(typeData);
657     }
658
659     typeData->addref();
660     return typeData;
661 }
662
663 /*!
664 Returns a QDeclarativeTypeData for the given \a data with the provided base \a url.  The 
665 QDeclarativeTypeData will not be cached.
666
667 The specified \a options control how the loader handles type data.
668 */
669 QDeclarativeTypeData *QDeclarativeTypeLoader::get(const QByteArray &data, const QUrl &url, Options options)
670 {
671     QDeclarativeTypeData *typeData = new QDeclarativeTypeData(url, options, this);
672     QDeclarativeDataLoader::loadWithStaticData(typeData, data);
673     return typeData;
674 }
675
676 /*!
677 Return a QDeclarativeScriptData for \a url.  The QDeclarativeScriptData may be cached.
678 */
679 QDeclarativeScriptData *QDeclarativeTypeLoader::getScript(const QUrl &url)
680 {
681     Q_ASSERT(!url.isRelative() && 
682             (QDeclarativeEnginePrivate::urlToLocalFileOrQrc(url).isEmpty() || 
683              !QDir::isRelativePath(QDeclarativeEnginePrivate::urlToLocalFileOrQrc(url))));
684
685     QDeclarativeScriptData *scriptData = m_scriptCache.value(url);
686
687     if (!scriptData) {
688         scriptData = new QDeclarativeScriptData(url);
689         m_scriptCache.insert(url, scriptData);
690         QDeclarativeDataLoader::load(scriptData);
691     }
692
693     scriptData->addref();
694     return scriptData;
695 }
696
697 /*!
698 Return a QDeclarativeQmldirData for \a url.  The QDeclarativeQmldirData may be cached.
699 */
700 QDeclarativeQmldirData *QDeclarativeTypeLoader::getQmldir(const QUrl &url)
701 {
702     Q_ASSERT(!url.isRelative() && 
703             (QDeclarativeEnginePrivate::urlToLocalFileOrQrc(url).isEmpty() || 
704              !QDir::isRelativePath(QDeclarativeEnginePrivate::urlToLocalFileOrQrc(url))));
705
706     QDeclarativeQmldirData *qmldirData = m_qmldirCache.value(url);
707
708     if (!qmldirData) {
709         qmldirData = new QDeclarativeQmldirData(url);
710         m_qmldirCache.insert(url, qmldirData);
711         QDeclarativeDataLoader::load(qmldirData);
712     }
713
714     qmldirData->addref();
715     return qmldirData;
716 }
717
718 void QDeclarativeTypeLoader::clearCache()
719 {
720     for (TypeCache::Iterator iter = m_typeCache.begin(); iter != m_typeCache.end(); ++iter) 
721         (*iter)->release();
722     for (ScriptCache::Iterator iter = m_scriptCache.begin(); iter != m_scriptCache.end(); ++iter) 
723         (*iter)->release();
724     for (QmldirCache::Iterator iter = m_qmldirCache.begin(); iter != m_qmldirCache.end(); ++iter) 
725         (*iter)->release();
726
727     m_typeCache.clear();
728     m_scriptCache.clear();
729     m_qmldirCache.clear();
730 }
731
732
733 QDeclarativeTypeData::QDeclarativeTypeData(const QUrl &url, QDeclarativeTypeLoader::Options options, 
734                                            QDeclarativeTypeLoader *manager)
735 : QDeclarativeDataBlob(url, QmlFile), m_options(options), m_typesResolved(false), 
736   m_compiledData(0), m_typeLoader(manager)
737 {
738 }
739
740 QDeclarativeTypeData::~QDeclarativeTypeData()
741 {
742     for (int ii = 0; ii < m_scripts.count(); ++ii) 
743         m_scripts.at(ii).script->release();
744     for (int ii = 0; ii < m_qmldirs.count(); ++ii) 
745         m_qmldirs.at(ii)->release();
746     for (int ii = 0; ii < m_types.count(); ++ii) 
747         if (m_types.at(ii).typeData) m_types.at(ii).typeData->release();
748     if (m_compiledData)
749         m_compiledData->release();
750 }
751
752 QDeclarativeTypeLoader *QDeclarativeTypeData::typeLoader() const
753 {
754     return m_typeLoader;
755 }
756
757 const QDeclarativeImports &QDeclarativeTypeData::imports() const
758 {
759     return m_imports;
760 }
761
762 const QDeclarativeScriptParser &QDeclarativeTypeData::parser() const
763 {
764     return scriptParser;
765 }
766
767 const QList<QDeclarativeTypeData::TypeReference> &QDeclarativeTypeData::resolvedTypes() const
768 {
769     return m_types;
770 }
771
772 const QList<QDeclarativeTypeData::ScriptReference> &QDeclarativeTypeData::resolvedScripts() const
773 {
774     return m_scripts;
775 }
776
777 QDeclarativeCompiledData *QDeclarativeTypeData::compiledData() const
778 {
779     if (m_compiledData) 
780         m_compiledData->addref();
781
782     return m_compiledData;
783 }
784
785 void QDeclarativeTypeData::registerCallback(TypeDataCallback *callback)
786 {
787     Q_ASSERT(!m_callbacks.contains(callback));
788     m_callbacks.append(callback);
789 }
790
791 void QDeclarativeTypeData::unregisterCallback(TypeDataCallback *callback)
792 {
793     Q_ASSERT(m_callbacks.contains(callback));
794     m_callbacks.removeOne(callback);
795     Q_ASSERT(!m_callbacks.contains(callback));
796 }
797
798 void QDeclarativeTypeData::done()
799 {
800     addref();
801
802     // Check all script dependencies for errors
803     for (int ii = 0; !isError() && ii < m_scripts.count(); ++ii) {
804         const ScriptReference &script = m_scripts.at(ii);
805         Q_ASSERT(script.script->isCompleteOrError());
806         if (script.script->isError()) {
807             QList<QDeclarativeError> errors = script.script->errors();
808             QDeclarativeError error;
809             error.setUrl(finalUrl());
810             error.setLine(script.location.line);
811             error.setColumn(script.location.column);
812             error.setDescription(QDeclarativeTypeLoader::tr("Script %1 unavailable").arg(script.script->url().toString()));
813             errors.prepend(error);
814             setError(errors);
815         }
816     }
817
818     // Check all type dependencies for errors
819     for (int ii = 0; !isError() && ii < m_types.count(); ++ii) {
820         const TypeReference &type = m_types.at(ii);
821         Q_ASSERT(!type.typeData || type.typeData->isCompleteOrError());
822         if (type.typeData && type.typeData->isError()) {
823             QString typeName = scriptParser.referencedTypes().at(ii)->name;
824
825             QList<QDeclarativeError> errors = type.typeData->errors();
826             QDeclarativeError error;
827             error.setUrl(finalUrl());
828             error.setLine(type.location.line);
829             error.setColumn(type.location.column);
830             error.setDescription(QDeclarativeTypeLoader::tr("Type %1 unavailable").arg(typeName));
831             errors.prepend(error);
832             setError(errors);
833         }
834     }
835
836     // Compile component
837     if (!isError()) 
838         compile();
839
840     if (!(m_options & QDeclarativeTypeLoader::PreserveParser))
841         scriptParser.clear();
842
843     // Notify callbacks
844     while (!m_callbacks.isEmpty()) {
845         TypeDataCallback *callback = m_callbacks.takeFirst();
846         callback->typeDataReady(this);
847     }
848
849     release();
850 }
851
852 void QDeclarativeTypeData::dataReceived(const QByteArray &data)
853 {
854     if (!scriptParser.parse(data, finalUrl())) {
855         setError(scriptParser.errors());
856         return;
857     }
858
859     m_imports.setBaseUrl(finalUrl());
860
861     foreach (const QDeclarativeScriptParser::Import &import, scriptParser.imports()) {
862         if (import.type == QDeclarativeScriptParser::Import::File && import.qualifier.isEmpty()) {
863             QUrl importUrl = finalUrl().resolved(QUrl(import.uri + QLatin1String("/qmldir")));
864             if (QDeclarativeEnginePrivate::urlToLocalFileOrQrc(importUrl).isEmpty()) {
865                 QDeclarativeQmldirData *data = typeLoader()->getQmldir(importUrl);
866                 addDependency(data);
867                 m_qmldirs << data;
868             }
869         } else if (import.type == QDeclarativeScriptParser::Import::Script) {
870             QUrl scriptUrl = finalUrl().resolved(QUrl(import.uri));
871             QDeclarativeScriptData *data = typeLoader()->getScript(scriptUrl);
872             addDependency(data);
873
874             ScriptReference ref;
875             ref.location = import.location.start;
876             ref.qualifier = import.qualifier;
877             ref.script = data;
878             m_scripts << ref;
879
880         }
881     }
882
883     if (!finalUrl().scheme().isEmpty()) {
884         QUrl importUrl = finalUrl().resolved(QUrl(QLatin1String("qmldir")));
885         if (QDeclarativeEnginePrivate::urlToLocalFileOrQrc(importUrl).isEmpty()) {
886             QDeclarativeQmldirData *data = typeLoader()->getQmldir(importUrl);
887             addDependency(data);
888             m_qmldirs << data;
889         }
890     }
891 }
892
893 void QDeclarativeTypeData::allDependenciesDone()
894 {
895     if (!m_typesResolved) {
896         resolveTypes();
897         m_typesResolved = true;
898     }
899 }
900
901 void QDeclarativeTypeData::downloadProgressChanged(qreal p)
902 {
903     for (int ii = 0; ii < m_callbacks.count(); ++ii) {
904         TypeDataCallback *callback = m_callbacks.at(ii);
905         callback->typeDataProgress(this, p);
906     }
907 }
908
909 void QDeclarativeTypeData::compile()
910 {
911     Q_ASSERT(m_compiledData == 0);
912     QDeclarativeDebugTrace::startRange(QDeclarativeDebugTrace::Compiling);
913
914     m_compiledData = new QDeclarativeCompiledData(typeLoader()->engine());
915     m_compiledData->url = m_imports.baseUrl();
916     m_compiledData->name = m_compiledData->url.toString();
917     QDeclarativeDebugTrace::rangeData(QDeclarativeDebugTrace::Compiling, m_compiledData->name);
918
919     QDeclarativeCompiler compiler;
920     if (!compiler.compile(typeLoader()->engine(), this, m_compiledData)) {
921         setError(compiler.errors());
922         m_compiledData->release();
923         m_compiledData = 0;
924     }
925     QDeclarativeDebugTrace::endRange(QDeclarativeDebugTrace::Compiling);
926 }
927
928 void QDeclarativeTypeData::resolveTypes()
929 {
930     QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(m_typeLoader->engine());
931     QDeclarativeImportDatabase *importDatabase = &ep->importDatabase;
932
933     // For local urls, add an implicit import "." as first (most overridden) lookup. 
934     // This will also trigger the loading of the qmldir and the import of any native 
935     // types from available plugins.
936     if (QDeclarativeQmldirData *qmldir = qmldirForUrl(finalUrl().resolved(QUrl(QLatin1String("./qmldir"))))) {
937         m_imports.addImport(importDatabase, QLatin1String("."),
938                             QString(), -1, -1, QDeclarativeScriptParser::Import::File, 
939                             qmldir->dirComponents(), 0);
940     } else {
941         m_imports.addImport(importDatabase, QLatin1String("."), 
942                             QString(), -1, -1, QDeclarativeScriptParser::Import::File, 
943                             QDeclarativeDirComponents(), 0);
944     }
945
946     foreach (const QDeclarativeScriptParser::Import &import, scriptParser.imports()) {
947         QDeclarativeDirComponents qmldircomponentsnetwork;
948         if (import.type == QDeclarativeScriptParser::Import::Script)
949             continue;
950
951         if (import.type == QDeclarativeScriptParser::Import::File && import.qualifier.isEmpty()) {
952             QUrl qmldirUrl = finalUrl().resolved(QUrl(import.uri + QLatin1String("/qmldir")));
953             if (QDeclarativeQmldirData *qmldir = qmldirForUrl(qmldirUrl)) 
954                 qmldircomponentsnetwork = qmldir->dirComponents();
955         }
956
957         int vmaj = -1;
958         int vmin = -1;
959
960         if (!import.version.isEmpty()) {
961             int dot = import.version.indexOf(QLatin1Char('.'));
962             if (dot < 0) {
963                 vmaj = import.version.toInt();
964                 vmin = 0;
965             } else {
966                 vmaj = import.version.left(dot).toInt();
967                 vmin = import.version.mid(dot+1).toInt();
968             }
969         }
970
971         QString errorString;
972         if (!m_imports.addImport(importDatabase, import.uri, import.qualifier,
973                                  vmaj, vmin, import.type, qmldircomponentsnetwork, &errorString)) {
974             QDeclarativeError error;
975             error.setUrl(m_imports.baseUrl());
976             error.setDescription(errorString);
977             error.setLine(import.location.start.line);
978             error.setColumn(import.location.start.column);
979
980             setError(error);
981             return;
982         }
983     }
984
985     foreach (QDeclarativeScriptParser::TypeReference *parserRef, scriptParser.referencedTypes()) {
986         QByteArray typeName = parserRef->name.toUtf8();
987
988         TypeReference ref;
989
990         QUrl url;
991         int majorVersion;
992         int minorVersion;
993         QDeclarativeImportedNamespace *typeNamespace = 0;
994         QString errorString;
995
996         if (!m_imports.resolveType(typeName, &ref.type, &url, &majorVersion, &minorVersion,
997                                    &typeNamespace, &errorString) || typeNamespace) {
998             // Known to not be a type:
999             //  - known to be a namespace (Namespace {})
1000             //  - type with unknown namespace (UnknownNamespace.SomeType {})
1001             QDeclarativeError error;
1002             error.setUrl(m_imports.baseUrl());
1003             QString userTypeName = parserRef->name;
1004             userTypeName.replace(QLatin1Char('/'),QLatin1Char('.'));
1005             if (typeNamespace)
1006                 error.setDescription(QDeclarativeTypeLoader::tr("Namespace %1 cannot be used as a type").arg(userTypeName));
1007             else
1008                 error.setDescription(QDeclarativeTypeLoader::tr("%1 %2").arg(userTypeName).arg(errorString));
1009
1010             if (!parserRef->refObjects.isEmpty()) {
1011                 QDeclarativeParser::Object *obj = parserRef->refObjects.first();
1012                 error.setLine(obj->location.start.line);
1013                 error.setColumn(obj->location.start.column);
1014             }
1015             
1016             setError(error);
1017             return;
1018         }
1019
1020         if (ref.type) {
1021             ref.majorVersion = majorVersion;
1022             ref.minorVersion = minorVersion;
1023             foreach (QDeclarativeParser::Object *obj, parserRef->refObjects) {
1024                // store namespace for DOM
1025                obj->majorVersion = majorVersion;
1026                obj->minorVersion = minorVersion;
1027             }
1028         } else {
1029             ref.typeData = typeLoader()->get(url);
1030             addDependency(ref.typeData);
1031         }
1032
1033         if (parserRef->refObjects.count())
1034             ref.location = parserRef->refObjects.first()->location.start;
1035
1036         m_types << ref;
1037     }
1038 }
1039
1040 QDeclarativeQmldirData *QDeclarativeTypeData::qmldirForUrl(const QUrl &url)
1041 {
1042     for (int ii = 0; ii < m_qmldirs.count(); ++ii) {
1043         if (m_qmldirs.at(ii)->url() == url)
1044             return m_qmldirs.at(ii);
1045     }
1046     return 0;
1047 }
1048
1049 QDeclarativeScriptData::QDeclarativeScriptData(const QUrl &url)
1050 : QDeclarativeDataBlob(url, JavaScriptFile), m_pragmas(QDeclarativeParser::Object::ScriptBlock::None)
1051 {
1052 }
1053
1054 QDeclarativeParser::Object::ScriptBlock::Pragmas QDeclarativeScriptData::pragmas() const
1055 {
1056     return m_pragmas;
1057 }
1058
1059 QString QDeclarativeScriptData::scriptSource() const
1060 {
1061     return m_source;
1062 }
1063
1064 void QDeclarativeScriptData::dataReceived(const QByteArray &data)
1065 {
1066     m_source = QString::fromUtf8(data);
1067     m_pragmas = QDeclarativeScriptParser::extractPragmas(m_source);
1068 }
1069
1070 QDeclarativeQmldirData::QDeclarativeQmldirData(const QUrl &url)
1071 : QDeclarativeDataBlob(url, QmldirFile)
1072 {
1073 }
1074
1075 const QDeclarativeDirComponents &QDeclarativeQmldirData::dirComponents() const
1076 {
1077     return m_components;
1078 }
1079
1080 void QDeclarativeQmldirData::dataReceived(const QByteArray &data)
1081 {
1082     QDeclarativeDirParser parser;
1083     parser.setSource(QString::fromUtf8(data));
1084     parser.parse();
1085     m_components = parser.components();
1086 }
1087
1088 QT_END_NAMESPACE
1089