src/declarative/qml/qdeclarativeimport.cpp
branchGCC_SURGE
changeset 31 5daf16870df6
parent 30 5dc02b23752f
child 33 3e2da88830cd
equal deleted inserted replaced
27:93b982ccede2 31:5daf16870df6
       
     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 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 "qdeclarativeimport_p.h"
       
    43 
       
    44 #include <QtCore/qdebug.h>
       
    45 #include <QtCore/qdir.h>
       
    46 #include <QtCore/qfileinfo.h>
       
    47 #include <QtCore/qpluginloader.h>
       
    48 #include <QtCore/qlibraryinfo.h>
       
    49 #include <QtDeclarative/qdeclarativeextensioninterface.h>
       
    50 #include <private/qdeclarativeglobal_p.h>
       
    51 #include <private/qdeclarativetypenamecache_p.h>
       
    52 #include <private/qdeclarativeengine_p.h>
       
    53 
       
    54 QT_BEGIN_NAMESPACE
       
    55 
       
    56 DEFINE_BOOL_CONFIG_OPTION(qmlImportTrace, QML_IMPORT_TRACE)
       
    57 DEFINE_BOOL_CONFIG_OPTION(qmlCheckTypes, QML_CHECK_TYPES)
       
    58 
       
    59 static bool greaterThan(const QString &s1, const QString &s2)
       
    60 {
       
    61     return s1 > s2;
       
    62 }
       
    63 
       
    64 typedef QMap<QString, QString> StringStringMap;
       
    65 Q_GLOBAL_STATIC(StringStringMap, qmlEnginePluginsWithRegisteredTypes); // stores the uri
       
    66 
       
    67 class QDeclarativeImportedNamespace 
       
    68 {
       
    69 public:
       
    70     QStringList uris;
       
    71     QStringList urls;
       
    72     QList<int> majversions;
       
    73     QList<int> minversions;
       
    74     QList<bool> isLibrary;
       
    75     QList<QDeclarativeDirComponents> qmlDirComponents;
       
    76 
       
    77 
       
    78     bool find_helper(int i, const QByteArray& type, int *vmajor, int *vminor,
       
    79                                  QDeclarativeType** type_return, QUrl* url_return,
       
    80                                  QUrl *base = 0, bool *typeRecursionDetected = 0);
       
    81     bool find(const QByteArray& type, int *vmajor, int *vminor, QDeclarativeType** type_return,
       
    82               QUrl* url_return, QUrl *base = 0, QString *errorString = 0);
       
    83 };
       
    84 
       
    85 class QDeclarativeImportsPrivate {
       
    86 public:
       
    87     QDeclarativeImportsPrivate();
       
    88     ~QDeclarativeImportsPrivate();
       
    89 
       
    90     bool importExtension(const QString &absoluteFilePath, const QString &uri, 
       
    91                          QDeclarativeImportDatabase *database, QDeclarativeDirComponents* components, 
       
    92                          QString *errorString);
       
    93 
       
    94     QString resolvedUri(const QString &dir_arg, QDeclarativeImportDatabase *database);
       
    95     bool add(const QDeclarativeDirComponents &qmldircomponentsnetwork, 
       
    96              const QString& uri_arg, const QString& prefix, 
       
    97              int vmaj, int vmin, QDeclarativeScriptParser::Import::Type importType, 
       
    98              QDeclarativeImportDatabase *database, QString *errorString);
       
    99     bool find(const QByteArray& type, int *vmajor, int *vminor, 
       
   100               QDeclarativeType** type_return, QUrl* url_return, QString *errorString);
       
   101 
       
   102     QDeclarativeImportedNamespace *findNamespace(const QString& type);
       
   103 
       
   104     QUrl base;
       
   105     int ref;
       
   106 
       
   107     QSet<QString> qmlDirFilesForWhichPluginsHaveBeenLoaded;
       
   108     QDeclarativeImportedNamespace unqualifiedset;
       
   109     QHash<QString,QDeclarativeImportedNamespace* > set;
       
   110 };
       
   111 
       
   112 QDeclarativeImports::QDeclarativeImports(const QDeclarativeImports &copy) 
       
   113 : d(copy.d)
       
   114 {
       
   115     ++d->ref;
       
   116 }
       
   117 
       
   118 QDeclarativeImports &
       
   119 QDeclarativeImports::operator =(const QDeclarativeImports &copy)
       
   120 {
       
   121     ++copy.d->ref;
       
   122     if (--d->ref == 0)
       
   123         delete d;
       
   124     d = copy.d;
       
   125     return *this;
       
   126 }
       
   127 
       
   128 QDeclarativeImports::QDeclarativeImports() 
       
   129 : d(new QDeclarativeImportsPrivate)
       
   130 {
       
   131 }
       
   132 
       
   133 QDeclarativeImports::~QDeclarativeImports()
       
   134 {
       
   135     if (--d->ref == 0)
       
   136         delete d;
       
   137 }
       
   138 
       
   139 /*!
       
   140   Sets the base URL to be used for all relative file imports added.
       
   141 */
       
   142 void QDeclarativeImports::setBaseUrl(const QUrl& url)
       
   143 {
       
   144     d->base = url;
       
   145 }
       
   146 
       
   147 /*!
       
   148   Returns the base URL to be used for all relative file imports added.
       
   149 */
       
   150 QUrl QDeclarativeImports::baseUrl() const
       
   151 {
       
   152     return d->base;
       
   153 }
       
   154 
       
   155 static QDeclarativeTypeNameCache *
       
   156 cacheForNamespace(QDeclarativeEngine *engine, const QDeclarativeImportedNamespace &set, 
       
   157                   QDeclarativeTypeNameCache *cache)
       
   158 {
       
   159     if (!cache)
       
   160         cache = new QDeclarativeTypeNameCache(engine);
       
   161 
       
   162     QList<QDeclarativeType *> types = QDeclarativeMetaType::qmlTypes();
       
   163 
       
   164     for (int ii = 0; ii < set.uris.count(); ++ii) {
       
   165         QByteArray base = set.uris.at(ii).toUtf8() + '/';
       
   166         int major = set.majversions.at(ii);
       
   167         int minor = set.minversions.at(ii);
       
   168 
       
   169         foreach (QDeclarativeType *type, types) {
       
   170             if (type->qmlTypeName().startsWith(base) &&
       
   171                 type->qmlTypeName().lastIndexOf('/') == (base.length() - 1) &&
       
   172                 type->availableInVersion(major,minor))
       
   173             {
       
   174                 QString name = QString::fromUtf8(type->qmlTypeName().mid(base.length()));
       
   175 
       
   176                 cache->add(name, type);
       
   177             }
       
   178         }
       
   179     }
       
   180 
       
   181     return cache;
       
   182 }
       
   183 
       
   184 void QDeclarativeImports::cache(QDeclarativeTypeNameCache *cache, QDeclarativeEngine *engine) const
       
   185 {
       
   186     const QDeclarativeImportedNamespace &set = d->unqualifiedset;
       
   187 
       
   188     for (QHash<QString,QDeclarativeImportedNamespace* >::ConstIterator iter = d->set.begin();
       
   189          iter != d->set.end(); ++iter) {
       
   190 
       
   191         QDeclarativeTypeNameCache::Data *d = cache->data(iter.key());
       
   192         if (d) {
       
   193             if (!d->typeNamespace)
       
   194                 cacheForNamespace(engine, *(*iter), d->typeNamespace);
       
   195         } else {
       
   196             QDeclarativeTypeNameCache *nc = cacheForNamespace(engine, *(*iter), 0);
       
   197             cache->add(iter.key(), nc);
       
   198             nc->release();
       
   199         }
       
   200     }
       
   201 
       
   202     cacheForNamespace(engine, set, cache);
       
   203 }
       
   204 bool QDeclarativeImportedNamespace::find_helper(int i, const QByteArray& type, int *vmajor, int *vminor,
       
   205                                  QDeclarativeType** type_return, QUrl* url_return,
       
   206                                  QUrl *base, bool *typeRecursionDetected)
       
   207 {
       
   208     int vmaj = majversions.at(i);
       
   209     int vmin = minversions.at(i);
       
   210 
       
   211     QByteArray qt = uris.at(i).toUtf8();
       
   212     qt += '/';
       
   213     qt += type;
       
   214 
       
   215     QDeclarativeType *t = QDeclarativeMetaType::qmlType(qt,vmaj,vmin);
       
   216     if (t) {
       
   217         if (vmajor) *vmajor = vmaj;
       
   218         if (vminor) *vminor = vmin;
       
   219         if (type_return)
       
   220             *type_return = t;
       
   221         return true;
       
   222     }
       
   223 
       
   224     QUrl url = QUrl(urls.at(i) + QLatin1Char('/') + QString::fromUtf8(type) + QLatin1String(".qml"));
       
   225     QDeclarativeDirComponents qmldircomponents = qmlDirComponents.at(i);
       
   226 
       
   227     bool typeWasDeclaredInQmldir = false;
       
   228     if (!qmldircomponents.isEmpty()) {
       
   229         const QString typeName = QString::fromUtf8(type);
       
   230         foreach (const QDeclarativeDirParser::Component &c, qmldircomponents) {
       
   231             if (c.typeName == typeName) {
       
   232                 typeWasDeclaredInQmldir = true;
       
   233 
       
   234                 // importing version -1 means import ALL versions
       
   235                 if ((vmaj == -1) || (c.majorVersion < vmaj || (c.majorVersion == vmaj && vmin >= c.minorVersion))) {
       
   236                     QUrl candidate = url.resolved(QUrl(c.fileName));
       
   237                     if (c.internal && base) {
       
   238                         if (base->resolved(QUrl(c.fileName)) != candidate)
       
   239                             continue; // failed attempt to access an internal type
       
   240                     }
       
   241                     if (base && *base == candidate) {
       
   242                         if (typeRecursionDetected)
       
   243                             *typeRecursionDetected = true;
       
   244                         continue; // no recursion
       
   245                     }
       
   246                     if (url_return)
       
   247                         *url_return = candidate;
       
   248                     return true;
       
   249                 }
       
   250             }
       
   251         }
       
   252     }
       
   253 
       
   254     if (!typeWasDeclaredInQmldir  && !isLibrary.at(i)) {
       
   255         // XXX search non-files too! (eg. zip files, see QT-524)
       
   256         QFileInfo f(QDeclarativeEnginePrivate::urlToLocalFileOrQrc(url));
       
   257         if (f.exists()) {
       
   258             if (base && *base == url) { // no recursion
       
   259                 if (typeRecursionDetected)
       
   260                     *typeRecursionDetected = true;
       
   261             } else {
       
   262                 if (url_return)
       
   263                     *url_return = url;
       
   264                 return true;
       
   265             }
       
   266         }
       
   267     }
       
   268     return false;
       
   269 }
       
   270 
       
   271 QDeclarativeImportsPrivate::QDeclarativeImportsPrivate() 
       
   272 : ref(1)
       
   273 {
       
   274 }
       
   275 
       
   276 QDeclarativeImportsPrivate::~QDeclarativeImportsPrivate()
       
   277 {
       
   278     foreach (QDeclarativeImportedNamespace* s, set.values())
       
   279         delete s;
       
   280 }
       
   281 
       
   282 bool QDeclarativeImportsPrivate::importExtension(const QString &absoluteFilePath, const QString &uri, 
       
   283                                                   QDeclarativeImportDatabase *database, 
       
   284                                                   QDeclarativeDirComponents* components, QString *errorString) 
       
   285 {
       
   286     QFile file(absoluteFilePath);
       
   287     QString filecontent;
       
   288     if (file.open(QFile::ReadOnly)) {
       
   289         filecontent = QString::fromUtf8(file.readAll());
       
   290         if (qmlImportTrace())
       
   291             qDebug() << "QDeclarativeImportDatabase::add: loaded" << absoluteFilePath;
       
   292     } else {
       
   293         if (errorString)
       
   294             *errorString = QDeclarativeImportDatabase::tr("module \"%1\" definition \"%2\" not readable").arg(uri).arg(absoluteFilePath);
       
   295         return false;
       
   296     }
       
   297     QDir dir = QFileInfo(file).dir();
       
   298 
       
   299     QDeclarativeDirParser qmldirParser;
       
   300     qmldirParser.setSource(filecontent);
       
   301     qmldirParser.parse();
       
   302 
       
   303     if (! qmlDirFilesForWhichPluginsHaveBeenLoaded.contains(absoluteFilePath)) {
       
   304         qmlDirFilesForWhichPluginsHaveBeenLoaded.insert(absoluteFilePath);
       
   305 
       
   306 
       
   307         foreach (const QDeclarativeDirParser::Plugin &plugin, qmldirParser.plugins()) {
       
   308 
       
   309             QString resolvedFilePath = database->resolvePlugin(dir, plugin.path, plugin.name);
       
   310 
       
   311             if (!resolvedFilePath.isEmpty()) {
       
   312                 if (!database->importPlugin(resolvedFilePath, uri, errorString)) {
       
   313                     if (errorString)
       
   314                         *errorString = QDeclarativeImportDatabase::tr("plugin cannot be loaded for module \"%1\": %2").arg(uri).arg(*errorString);
       
   315                     return false;
       
   316                 }
       
   317             } else {
       
   318                 if (errorString)
       
   319                     *errorString = QDeclarativeImportDatabase::tr("module \"%1\" plugin \"%2\" not found").arg(uri).arg(plugin.name);
       
   320                 return false;
       
   321             }
       
   322         }
       
   323     }
       
   324 
       
   325     if (components)
       
   326         *components = qmldirParser.components();
       
   327 
       
   328     return true;
       
   329 }
       
   330 
       
   331 QString QDeclarativeImportsPrivate::resolvedUri(const QString &dir_arg, QDeclarativeImportDatabase *database)
       
   332 {
       
   333     QString dir = dir_arg;
       
   334     if (dir.endsWith(QLatin1Char('/')) || dir.endsWith(QLatin1Char('\\')))
       
   335         dir.chop(1);
       
   336 
       
   337     QStringList paths = database->fileImportPath;
       
   338     qSort(paths.begin(), paths.end(), greaterThan); // Ensure subdirs preceed their parents.
       
   339 
       
   340     QString stableRelativePath = dir;
       
   341     foreach( QString path, paths) {
       
   342         if (dir.startsWith(path)) {
       
   343             stableRelativePath = dir.mid(path.length()+1);
       
   344             break;
       
   345         }
       
   346     }
       
   347     stableRelativePath.replace(QLatin1Char('/'), QLatin1Char('.'));
       
   348     stableRelativePath.replace(QLatin1Char('\\'), QLatin1Char('.'));
       
   349     return stableRelativePath;
       
   350 }
       
   351 
       
   352 bool QDeclarativeImportsPrivate::add(const QDeclarativeDirComponents &qmldircomponentsnetwork, 
       
   353                                      const QString& uri_arg, const QString& prefix, int vmaj, int vmin, 
       
   354                                      QDeclarativeScriptParser::Import::Type importType, 
       
   355                                      QDeclarativeImportDatabase *database, QString *errorString)
       
   356 {
       
   357     QDeclarativeDirComponents qmldircomponents = qmldircomponentsnetwork;
       
   358     QString uri = uri_arg;
       
   359     QDeclarativeImportedNamespace *s;
       
   360     if (prefix.isEmpty()) {
       
   361         s = &unqualifiedset;
       
   362     } else {
       
   363         s = set.value(prefix);
       
   364         if (!s)
       
   365             set.insert(prefix,(s=new QDeclarativeImportedNamespace));
       
   366     }
       
   367 
       
   368     QString url = uri;
       
   369     if (importType == QDeclarativeScriptParser::Import::Library) {
       
   370         url.replace(QLatin1Char('.'), QLatin1Char('/'));
       
   371         bool found = false;
       
   372         QString dir;
       
   373 
       
   374 
       
   375         foreach (const QString &p, database->fileImportPath) {
       
   376             dir = p+QLatin1Char('/')+url;
       
   377 
       
   378             QFileInfo fi(dir+QLatin1String("/qmldir"));
       
   379             const QString absoluteFilePath = fi.absoluteFilePath();
       
   380 
       
   381             if (fi.isFile()) {
       
   382                 found = true;
       
   383 
       
   384                 url = QUrl::fromLocalFile(fi.absolutePath()).toString();
       
   385                 uri = resolvedUri(dir, database);
       
   386                 if (!importExtension(absoluteFilePath, uri, database, &qmldircomponents, errorString))
       
   387                     return false;
       
   388                 break;
       
   389             }
       
   390         }
       
   391 
       
   392         if (!found) {
       
   393             found = QDeclarativeMetaType::isModule(uri.toUtf8(), vmaj, vmin);
       
   394             if (!found) {
       
   395                 if (errorString) {
       
   396                     bool anyversion = QDeclarativeMetaType::isModule(uri.toUtf8(), -1, -1);
       
   397                     if (anyversion)
       
   398                         *errorString = QDeclarativeImportDatabase::tr("module \"%1\" version %2.%3 is not installed").arg(uri_arg).arg(vmaj).arg(vmin);
       
   399                     else
       
   400                         *errorString = QDeclarativeImportDatabase::tr("module \"%1\" is not installed").arg(uri_arg);
       
   401                 }
       
   402                 return false;
       
   403             }
       
   404         }
       
   405     } else {
       
   406 
       
   407         if (importType == QDeclarativeScriptParser::Import::File && qmldircomponents.isEmpty()) {
       
   408             QUrl importUrl = base.resolved(QUrl(uri + QLatin1String("/qmldir")));
       
   409             QString localFileOrQrc = QDeclarativeEnginePrivate::urlToLocalFileOrQrc(importUrl);
       
   410             if (!localFileOrQrc.isEmpty()) {
       
   411                 QString dir = QDeclarativeEnginePrivate::urlToLocalFileOrQrc(base.resolved(QUrl(uri)));
       
   412                 if (dir.isEmpty() || !QDir().exists(dir)) {
       
   413                     if (errorString)
       
   414                         *errorString = QDeclarativeImportDatabase::tr("\"%1\": no such directory").arg(uri_arg);
       
   415                     return false; // local import dirs must exist
       
   416                 }
       
   417                 uri = resolvedUri(QDeclarativeEnginePrivate::urlToLocalFileOrQrc(base.resolved(QUrl(uri))), database);
       
   418                 if (uri.endsWith(QLatin1Char('/')))
       
   419                     uri.chop(1);
       
   420                 if (QFile::exists(localFileOrQrc)) {
       
   421                     if (!importExtension(localFileOrQrc,uri,database,&qmldircomponents,errorString))
       
   422                         return false;
       
   423                 }
       
   424             } else {
       
   425                 if (prefix.isEmpty()) {
       
   426                     // directory must at least exist for valid import
       
   427                     QString localFileOrQrc = QDeclarativeEnginePrivate::urlToLocalFileOrQrc(base.resolved(QUrl(uri)));
       
   428                     if (localFileOrQrc.isEmpty() || !QDir().exists(localFileOrQrc)) {
       
   429                         if (errorString) {
       
   430                             if (localFileOrQrc.isEmpty())
       
   431                                 *errorString = QDeclarativeImportDatabase::tr("import \"%1\" has no qmldir and no namespace").arg(uri);
       
   432                             else
       
   433                                 *errorString = QDeclarativeImportDatabase::tr("\"%1\": no such directory").arg(uri);
       
   434                         }
       
   435                         return false;
       
   436                     }
       
   437                 }
       
   438             }
       
   439         }
       
   440 
       
   441         url = base.resolved(QUrl(url)).toString();
       
   442         if (url.endsWith(QLatin1Char('/')))
       
   443             url.chop(1);
       
   444     }
       
   445 
       
   446     if (vmaj > -1 && vmin > -1 && !qmldircomponents.isEmpty()) {
       
   447         QList<QDeclarativeDirParser::Component>::ConstIterator it = qmldircomponents.begin();
       
   448         for (; it != qmldircomponents.end(); ++it) {
       
   449             if (it->majorVersion > vmaj || (it->majorVersion == vmaj && it->minorVersion >= vmin))
       
   450                 break;
       
   451         }
       
   452         if (it == qmldircomponents.end()) {
       
   453             *errorString = QDeclarativeImportDatabase::tr("module \"%1\" version %2.%3 is not installed").arg(uri_arg).arg(vmaj).arg(vmin);
       
   454             return false;
       
   455         }
       
   456     }
       
   457 
       
   458     s->uris.prepend(uri);
       
   459     s->urls.prepend(url);
       
   460     s->majversions.prepend(vmaj);
       
   461     s->minversions.prepend(vmin);
       
   462     s->isLibrary.prepend(importType == QDeclarativeScriptParser::Import::Library);
       
   463     s->qmlDirComponents.prepend(qmldircomponents);
       
   464     return true;
       
   465 }
       
   466 
       
   467 bool QDeclarativeImportsPrivate::find(const QByteArray& type, int *vmajor, int *vminor, QDeclarativeType** type_return,
       
   468                                       QUrl* url_return, QString *errorString)
       
   469 {
       
   470     QDeclarativeImportedNamespace *s = 0;
       
   471     int slash = type.indexOf('/');
       
   472     if (slash >= 0) {
       
   473         QString namespaceName = QString::fromUtf8(type.left(slash));
       
   474         s = set.value(namespaceName);
       
   475         if (!s) {
       
   476             if (errorString)
       
   477                 *errorString = QDeclarativeImportDatabase::tr("- %1 is not a namespace").arg(namespaceName);
       
   478             return false;
       
   479         }
       
   480         int nslash = type.indexOf('/',slash+1);
       
   481         if (nslash > 0) {
       
   482             if (errorString)
       
   483                 *errorString = QDeclarativeImportDatabase::tr("- nested namespaces not allowed");
       
   484             return false;
       
   485         }
       
   486     } else {
       
   487         s = &unqualifiedset;
       
   488     }
       
   489     QByteArray unqualifiedtype = slash < 0 ? type : type.mid(slash+1); // common-case opt (QString::mid works fine, but slower)
       
   490     if (s) {
       
   491         if (s->find(unqualifiedtype,vmajor,vminor,type_return,url_return, &base, errorString))
       
   492             return true;
       
   493         if (s->urls.count() == 1 && !s->isLibrary[0] && url_return && s != &unqualifiedset) {
       
   494             // qualified, and only 1 url
       
   495             *url_return = QUrl(s->urls[0]+QLatin1Char('/')).resolved(QUrl(QString::fromUtf8(unqualifiedtype) + QLatin1String(".qml")));
       
   496             return true;
       
   497         }
       
   498     }
       
   499 
       
   500     return false;
       
   501 }
       
   502 
       
   503 QDeclarativeImportedNamespace *QDeclarativeImportsPrivate::findNamespace(const QString& type)
       
   504 {
       
   505     return set.value(type);
       
   506 }
       
   507 
       
   508 bool QDeclarativeImportedNamespace::find(const QByteArray& type, int *vmajor, int *vminor, QDeclarativeType** type_return,
       
   509           QUrl* url_return, QUrl *base, QString *errorString)
       
   510 {
       
   511     bool typeRecursionDetected = false;
       
   512     for (int i=0; i<urls.count(); ++i) {
       
   513         if (find_helper(i, type, vmajor, vminor, type_return, url_return, base, &typeRecursionDetected)) {
       
   514             if (qmlCheckTypes()) {
       
   515                 // check for type clashes
       
   516                 for (int j = i+1; j<urls.count(); ++j) {
       
   517                     if (find_helper(j, type, vmajor, vminor, 0, 0, base)) {
       
   518                         if (errorString) {
       
   519                             QString u1 = urls.at(i);
       
   520                             QString u2 = urls.at(j);
       
   521                             if (base) {
       
   522                                 QString b = base->toString();
       
   523                                 int slash = b.lastIndexOf(QLatin1Char('/'));
       
   524                                 if (slash >= 0) {
       
   525                                     b = b.left(slash+1);
       
   526                                     QString l = b.left(slash);
       
   527                                     if (u1.startsWith(b))
       
   528                                         u1 = u1.mid(b.count());
       
   529                                     else if (u1 == l)
       
   530                                         u1 = QDeclarativeImportDatabase::tr("local directory");
       
   531                                     if (u2.startsWith(b))
       
   532                                         u2 = u2.mid(b.count());
       
   533                                     else if (u2 == l)
       
   534                                         u2 = QDeclarativeImportDatabase::tr("local directory");
       
   535                                 }
       
   536                             }
       
   537 
       
   538                             if (u1 != u2)
       
   539                                 *errorString
       
   540                                         = QDeclarativeImportDatabase::tr("is ambiguous. Found in %1 and in %2")
       
   541                                 .arg(u1).arg(u2);
       
   542                             else
       
   543                                 *errorString
       
   544                                         = QDeclarativeImportDatabase::tr("is ambiguous. Found in %1 in version %2.%3 and %4.%5")
       
   545                                           .arg(u1)
       
   546                                           .arg(majversions.at(i)).arg(minversions.at(i))
       
   547                                           .arg(majversions.at(j)).arg(minversions.at(j));
       
   548                         }
       
   549                         return false;
       
   550                     }
       
   551                 }
       
   552             }
       
   553             return true;
       
   554         }
       
   555     }
       
   556     if (errorString) {
       
   557         if (typeRecursionDetected)
       
   558             *errorString = QDeclarativeImportDatabase::tr("is instantiated recursively");
       
   559         else
       
   560             *errorString = QDeclarativeImportDatabase::tr("is not a type");
       
   561     }
       
   562     return false;
       
   563 }
       
   564 
       
   565 QDeclarativeImportDatabase::QDeclarativeImportDatabase(QDeclarativeEngine *e)
       
   566 : engine(e)
       
   567 {
       
   568     filePluginPath << QLatin1String(".");
       
   569 
       
   570     // Search order is applicationDirPath(), $QML_IMPORT_PATH, QLibraryInfo::ImportsPath
       
   571 
       
   572     addImportPath(QLibraryInfo::location(QLibraryInfo::ImportsPath));
       
   573 
       
   574     // env import paths
       
   575     QByteArray envImportPath = qgetenv("QML_IMPORT_PATH");
       
   576     if (!envImportPath.isEmpty()) {
       
   577 #if defined(Q_OS_WIN) || defined(Q_OS_SYMBIAN)
       
   578         QLatin1Char pathSep(';');
       
   579 #else
       
   580         QLatin1Char pathSep(':');
       
   581 #endif
       
   582         QStringList paths = QString::fromLatin1(envImportPath).split(pathSep, QString::SkipEmptyParts);
       
   583         for (int ii = paths.count() - 1; ii >= 0; --ii)
       
   584             addImportPath(paths.at(ii));
       
   585     }
       
   586 
       
   587     addImportPath(QCoreApplication::applicationDirPath());
       
   588 }
       
   589 
       
   590 QDeclarativeImportDatabase::~QDeclarativeImportDatabase()
       
   591 {
       
   592 }
       
   593 
       
   594 /*!
       
   595   \internal
       
   596 
       
   597   Adds information to \a imports such that subsequent calls to resolveType()
       
   598   will resolve types qualified by \a prefix by considering types found at the given \a uri.
       
   599 
       
   600   The uri is either a directory (if importType is FileImport), or a URI resolved using paths
       
   601   added via addImportPath() (if importType is LibraryImport).
       
   602 
       
   603   The \a prefix may be empty, in which case the import location is considered for
       
   604   unqualified types.
       
   605 
       
   606   The base URL must already have been set with Import::setBaseUrl().
       
   607 */
       
   608 bool QDeclarativeImportDatabase::addToImport(QDeclarativeImports* imports, 
       
   609                                              const QDeclarativeDirComponents &qmldircomponentsnetwork, 
       
   610                                              const QString& uri, const QString& prefix, int vmaj, int vmin, 
       
   611                                              QDeclarativeScriptParser::Import::Type importType, 
       
   612                                              QString *errorString) 
       
   613 {
       
   614     if (qmlImportTrace())
       
   615         qDebug().nospace() << "QDeclarativeImportDatabase::addToImport " << imports << " " << uri << " " 
       
   616                            << vmaj << '.' << vmin << " " 
       
   617                            << (importType==QDeclarativeScriptParser::Import::Library? "Library" : "File") 
       
   618                            << " as " << prefix;
       
   619 
       
   620     bool ok = imports->d->add(qmldircomponentsnetwork, uri, prefix, vmaj, vmin, importType, this, errorString);
       
   621     return ok;
       
   622 }
       
   623 
       
   624 /*!
       
   625   \internal
       
   626 
       
   627   Using the given \a imports, the given (namespace qualified) \a type is resolved to either
       
   628   a QDeclarativeImportedNamespace stored at \a ns_return,
       
   629   a QDeclarativeType stored at \a type_return, or
       
   630   a component located at \a url_return.
       
   631 
       
   632   If any return pointer is 0, the corresponding search is not done.
       
   633 
       
   634   \sa addToImport()
       
   635 */
       
   636 bool QDeclarativeImportDatabase::resolveType(const QDeclarativeImports& imports, const QByteArray& type, 
       
   637                                              QDeclarativeType** type_return, QUrl* url_return, int *vmaj, int *vmin,
       
   638                                              QDeclarativeImportedNamespace** ns_return, QString *errorString) const
       
   639 {
       
   640     QDeclarativeImportedNamespace* ns = imports.d->findNamespace(QString::fromUtf8(type));
       
   641     if (ns) {
       
   642         if (ns_return)
       
   643             *ns_return = ns;
       
   644         return true;
       
   645     }
       
   646     if (type_return || url_return) {
       
   647         if (imports.d->find(type,vmaj,vmin,type_return,url_return, errorString)) {
       
   648             if (qmlImportTrace()) {
       
   649                 if (type_return && *type_return && url_return && !url_return->isEmpty())
       
   650                     qDebug() << "QDeclarativeImportDatabase::resolveType" << type << '=' << (*type_return)->typeName() << *url_return;
       
   651                 if (type_return && *type_return)
       
   652                     qDebug() << "QDeclarativeImportDatabase::resolveType" << type << '=' << (*type_return)->typeName();
       
   653                 if (url_return && !url_return->isEmpty())
       
   654                     qDebug() << "QDeclarativeImportDatabase::resolveType" << type << '=' << *url_return;
       
   655             }
       
   656             return true;
       
   657         }
       
   658     }
       
   659     return false;
       
   660 }
       
   661 
       
   662 /*!
       
   663   \internal
       
   664 
       
   665   Searching \e only in the namespace \a ns (previously returned in a call to
       
   666   resolveType(), \a type is found and returned to either
       
   667   a QDeclarativeType stored at \a type_return, or
       
   668   a component located at \a url_return.
       
   669 
       
   670   If either return pointer is 0, the corresponding search is not done.
       
   671 */
       
   672 bool QDeclarativeImportDatabase::resolveTypeInNamespace(QDeclarativeImportedNamespace* ns, const QByteArray& type, 
       
   673                                                         QDeclarativeType** type_return, QUrl* url_return, 
       
   674                                                         int *vmaj, int *vmin) const
       
   675 {
       
   676     return ns->find(type,vmaj,vmin,type_return,url_return);
       
   677 }
       
   678 
       
   679 /*!
       
   680   \internal
       
   681 
       
   682   Returns the result of the merge of \a baseName with \a path, \a suffixes, and \a prefix.
       
   683   The \a prefix must contain the dot.
       
   684 
       
   685   \a qmldirPath is the location of the qmldir file.
       
   686  */
       
   687 QString QDeclarativeImportDatabase::resolvePlugin(const QDir &qmldirPath, const QString &qmldirPluginPath, 
       
   688                                                   const QString &baseName, const QStringList &suffixes,
       
   689                                                   const QString &prefix)
       
   690 {
       
   691     QStringList searchPaths = filePluginPath;
       
   692     bool qmldirPluginPathIsRelative = QDir::isRelativePath(qmldirPluginPath);
       
   693     if (!qmldirPluginPathIsRelative)
       
   694         searchPaths.prepend(qmldirPluginPath);
       
   695 
       
   696     foreach (const QString &pluginPath, searchPaths) {
       
   697 
       
   698         QString resolvedPath;
       
   699 
       
   700         if (pluginPath == QLatin1String(".")) {
       
   701             if (qmldirPluginPathIsRelative)
       
   702                 resolvedPath = qmldirPath.absoluteFilePath(qmldirPluginPath);
       
   703             else
       
   704                 resolvedPath = qmldirPath.absolutePath();
       
   705         } else {
       
   706             resolvedPath = pluginPath;
       
   707         }
       
   708 
       
   709         // hack for resources, should probably go away
       
   710         if (resolvedPath.startsWith(QLatin1Char(':')))
       
   711             resolvedPath = QCoreApplication::applicationDirPath();
       
   712 
       
   713         QDir dir(resolvedPath);
       
   714         foreach (const QString &suffix, suffixes) {
       
   715             QString pluginFileName = prefix;
       
   716 
       
   717             pluginFileName += baseName;
       
   718             pluginFileName += suffix;
       
   719 
       
   720             QFileInfo fileInfo(dir, pluginFileName);
       
   721 
       
   722             if (fileInfo.exists())
       
   723                 return fileInfo.absoluteFilePath();
       
   724         }
       
   725     }
       
   726 
       
   727     if (qmlImportTrace())
       
   728         qDebug() << "QDeclarativeImportDatabase::resolvePlugin: Could not resolve plugin" << baseName 
       
   729                  << "in" << qmldirPath.absolutePath();
       
   730 
       
   731     return QString();
       
   732 }
       
   733 
       
   734 /*!
       
   735   \internal
       
   736 
       
   737   Returns the result of the merge of \a baseName with \a dir and the platform suffix.
       
   738 
       
   739   \table
       
   740   \header \i Platform \i Valid suffixes
       
   741   \row \i Windows     \i \c .dll
       
   742   \row \i Unix/Linux  \i \c .so
       
   743   \row \i AIX  \i \c .a
       
   744   \row \i HP-UX       \i \c .sl, \c .so (HP-UXi)
       
   745   \row \i Mac OS X    \i \c .dylib, \c .bundle, \c .so
       
   746   \row \i Symbian     \i \c .dll
       
   747   \endtable
       
   748 
       
   749   Version number on unix are ignored.
       
   750 */
       
   751 QString QDeclarativeImportDatabase::resolvePlugin(const QDir &qmldirPath, const QString &qmldirPluginPath, 
       
   752                                                   const QString &baseName)
       
   753 {
       
   754 #if defined(Q_OS_WIN32) || defined(Q_OS_WINCE)
       
   755     return resolvePlugin(qmldirPath, qmldirPluginPath, baseName,
       
   756                          QStringList()
       
   757 # ifdef QT_DEBUG
       
   758                          << QLatin1String("d.dll") // try a qmake-style debug build first
       
   759 # endif
       
   760                          << QLatin1String(".dll"));
       
   761 #elif defined(Q_OS_SYMBIAN)
       
   762     return resolvePlugin(qmldirPath, qmldirPluginPath, baseName,
       
   763                          QStringList()
       
   764                          << QLatin1String(".dll")
       
   765                          << QLatin1String(".qtplugin"));
       
   766 #else
       
   767 
       
   768 # if defined(Q_OS_DARWIN)
       
   769 
       
   770     return resolvePlugin(qmldirPath, qmldirPluginPath, baseName,
       
   771                          QStringList()
       
   772 # ifdef QT_DEBUG
       
   773                          << QLatin1String("_debug.dylib") // try a qmake-style debug build first
       
   774                          << QLatin1String(".dylib")
       
   775 # else
       
   776                          << QLatin1String(".dylib")
       
   777                          << QLatin1String("_debug.dylib") // try a qmake-style debug build after
       
   778 # endif
       
   779                          << QLatin1String(".so")
       
   780                          << QLatin1String(".bundle"),
       
   781                          QLatin1String("lib"));
       
   782 # else  // Generic Unix
       
   783     QStringList validSuffixList;
       
   784 
       
   785 #  if defined(Q_OS_HPUX)
       
   786 /*
       
   787     See "HP-UX Linker and Libraries User's Guide", section "Link-time Differences between PA-RISC and IPF":
       
   788     "In PA-RISC (PA-32 and PA-64) shared libraries are suffixed with .sl. In IPF (32-bit and 64-bit),
       
   789     the shared libraries are suffixed with .so. For compatibility, the IPF linker also supports the .sl suffix."
       
   790  */
       
   791     validSuffixList << QLatin1String(".sl");
       
   792 #   if defined __ia64
       
   793     validSuffixList << QLatin1String(".so");
       
   794 #   endif
       
   795 #  elif defined(Q_OS_AIX)
       
   796     validSuffixList << QLatin1String(".a") << QLatin1String(".so");
       
   797 #  elif defined(Q_OS_UNIX)
       
   798     validSuffixList << QLatin1String(".so");
       
   799 #  endif
       
   800 
       
   801     // Examples of valid library names:
       
   802     //  libfoo.so
       
   803 
       
   804     return resolvePlugin(qmldirPath, qmldirPluginPath, baseName, validSuffixList, QLatin1String("lib"));
       
   805 # endif
       
   806 
       
   807 #endif
       
   808 }
       
   809 
       
   810 QStringList QDeclarativeImportDatabase::pluginPathList() const
       
   811 {
       
   812     return filePluginPath;
       
   813 }
       
   814 
       
   815 void QDeclarativeImportDatabase::setPluginPathList(const QStringList &paths)
       
   816 {
       
   817     filePluginPath = paths;
       
   818 }
       
   819 
       
   820 void QDeclarativeImportDatabase::addPluginPath(const QString& path)
       
   821 {
       
   822     if (qmlImportTrace())
       
   823         qDebug() << "QDeclarativeImportDatabase::addPluginPath" << path;
       
   824 
       
   825     QUrl url = QUrl(path);
       
   826     if (url.isRelative() || url.scheme() == QString::fromLocal8Bit("file")) {
       
   827         QDir dir = QDir(path);
       
   828         filePluginPath.prepend(dir.canonicalPath());
       
   829     } else {
       
   830         filePluginPath.prepend(path);
       
   831     }
       
   832 }
       
   833 
       
   834 void QDeclarativeImportDatabase::addImportPath(const QString& path)
       
   835 {
       
   836     if (qmlImportTrace())
       
   837         qDebug() << "QDeclarativeImportDatabase::addImportPath" << path;
       
   838 
       
   839     if (path.isEmpty())
       
   840         return;
       
   841 
       
   842     QUrl url = QUrl(path);
       
   843     QString cPath;
       
   844 
       
   845     if (url.isRelative() || url.scheme() == QString::fromLocal8Bit("file")) {
       
   846         QDir dir = QDir(path);
       
   847         cPath = dir.canonicalPath();
       
   848     } else {
       
   849         cPath = path;
       
   850     }
       
   851 
       
   852     if (!cPath.isEmpty()
       
   853         && !fileImportPath.contains(cPath))
       
   854         fileImportPath.prepend(cPath);
       
   855 }
       
   856 
       
   857 QStringList QDeclarativeImportDatabase::importPathList() const
       
   858 {
       
   859     return fileImportPath;
       
   860 }
       
   861 
       
   862 void QDeclarativeImportDatabase::setImportPathList(const QStringList &paths)
       
   863 {
       
   864     fileImportPath = paths;
       
   865 }
       
   866 
       
   867 
       
   868 bool QDeclarativeImportDatabase::importPlugin(const QString &filePath, const QString &uri, QString *errorString)
       
   869 {
       
   870     if (qmlImportTrace())
       
   871         qDebug() << "QDeclarativeImportDatabase::importPlugin" << uri << "from" << filePath;
       
   872 
       
   873     QFileInfo fileInfo(filePath);
       
   874     const QString absoluteFilePath = fileInfo.absoluteFilePath();
       
   875 
       
   876     bool engineInitialized = initializedPlugins.contains(absoluteFilePath);
       
   877     bool typesRegistered = qmlEnginePluginsWithRegisteredTypes()->contains(absoluteFilePath);
       
   878 
       
   879     if (typesRegistered) {
       
   880         Q_ASSERT_X(qmlEnginePluginsWithRegisteredTypes()->value(absoluteFilePath) == uri,
       
   881                    "QDeclarativeImportDatabase::importExtension",
       
   882                    "Internal error: Plugin imported previously with different uri");
       
   883     }
       
   884 
       
   885     if (!engineInitialized || !typesRegistered) {
       
   886         QPluginLoader loader(absoluteFilePath);
       
   887 
       
   888         if (!loader.load()) {
       
   889             if (errorString)
       
   890                 *errorString = loader.errorString();
       
   891             return false;
       
   892         }
       
   893 
       
   894         if (QDeclarativeExtensionInterface *iface = qobject_cast<QDeclarativeExtensionInterface *>(loader.instance())) {
       
   895 
       
   896             const QByteArray bytes = uri.toUtf8();
       
   897             const char *moduleId = bytes.constData();
       
   898             if (!typesRegistered) {
       
   899 
       
   900                 // ### this code should probably be protected with a mutex.
       
   901                 qmlEnginePluginsWithRegisteredTypes()->insert(absoluteFilePath, uri);
       
   902                 iface->registerTypes(moduleId);
       
   903             }
       
   904             if (!engineInitialized) {
       
   905                 // things on the engine (eg. adding new global objects) have to be done for every engine.
       
   906 
       
   907                 // protect against double initialization
       
   908                 initializedPlugins.insert(absoluteFilePath);
       
   909                 iface->initializeEngine(engine, moduleId);
       
   910             }
       
   911         } else {
       
   912             if (errorString)
       
   913                 *errorString = loader.errorString();
       
   914             return false;
       
   915         }
       
   916     }
       
   917 
       
   918     return true;
       
   919 }
       
   920 
       
   921 
       
   922 QT_END_NAMESPACE