OSDN Git Service

Update license.
[qt-creator-jp/qt-creator-jp.git] / src / libs / qmljs / qmljsdocument.cpp
1 /**************************************************************************
2 **
3 ** This file is part of Qt Creator
4 **
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
6 **
7 ** Contact: Nokia Corporation (info@qt.nokia.com)
8 **
9 **
10 ** GNU Lesser General Public License Usage
11 **
12 ** This file may be used under the terms of the GNU Lesser General Public
13 ** License version 2.1 as published by the Free Software Foundation and
14 ** appearing in the file LICENSE.LGPL included in the packaging of this file.
15 ** Please review the following information to ensure the GNU Lesser General
16 ** Public License version 2.1 requirements will be met:
17 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
18 **
19 ** In addition, as a special exception, Nokia gives you certain additional
20 ** rights. These rights are described in the Nokia Qt LGPL Exception
21 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
22 **
23 ** Other Usage
24 **
25 ** Alternatively, this file may be used in accordance with the terms and
26 ** conditions contained in a signed written agreement between you and Nokia.
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 #include "qmljsdocument.h"
34 #include "qmljsbind.h"
35 #include <qmljs/parser/qmljsast_p.h>
36 #include <qmljs/parser/qmljslexer_p.h>
37 #include <qmljs/parser/qmljsparser_p.h>
38 #include <qmljs/parser/qmljsnodepool_p.h>
39 #include <qmljs/parser/qmljsastfwd_p.h>
40 #include <QtCore/QDir>
41
42 using namespace QmlJS;
43 using namespace QmlJS::AST;
44
45 /*!
46     \class QmlJS::Document
47     \brief A Qml or JavaScript document.
48     \sa QmlJS::Snapshot
49
50     Documents are usually created by the \l{QmlJSEditor::Internal::ModelManager}
51     and stored in a \l{QmlJS::Snapshot}. They allow access to data such as
52     the file path, source code, abstract syntax tree and the \l{QmlJS::Bind}
53     instance for the document.
54
55     To make sure unused and outdated documents are removed correctly, Document
56     instances are usually accessed through a shared pointer, see \l{Document::Ptr}.
57 */
58
59 /*!
60     \class QmlJS::LibraryInfo
61     \brief A Qml library.
62     \sa QmlJS::Snapshot
63
64     A LibraryInfo is created when the \l{QmlJSEditor::Internal::ModelManager} finds
65     a Qml library and parses the qmldir file. The instance holds information about
66     which Components the library provides and which plugins to load.
67
68     The ModelManager will try to extract detailed information about the types
69     defined in the plugins this library loads. Once it is done, the data will
70     be available through the metaObjects() function.
71 */
72
73 /*!
74     \class QmlJS::Snapshot
75     \brief A set of Document::Ptr and LibraryInfo instances.
76     \sa QmlJS::Document QmlJS::LibraryInfo
77
78     A Snapshot holds and offers access to a set of Document and LibraryInfo instances.
79
80     Usually Snapshots are copies of the snapshot maintained and updated by the
81     \l{QmlJSEditor::Internal::ModelManager} that updates its instance as parsing
82     threads finish and new information becomes available.
83 */
84
85
86 Document::Document(const QString &fileName)
87     : _engine(0)
88     , _pool(0)
89     , _ast(0)
90     , _bind(0)
91     , _isQmlDocument(false)
92     , _editorRevision(0)
93     , _parsedCorrectly(false)
94     , _fileName(QDir::cleanPath(fileName))
95 {
96     QFileInfo fileInfo(fileName);
97     _path = QDir::cleanPath(fileInfo.absolutePath());
98
99     // ### Should use mime type
100     if (fileInfo.suffix() == QLatin1String("qml")
101             || fileInfo.suffix() == QLatin1String("qmlproject")) {
102         _isQmlDocument = true;
103         _componentName = fileInfo.baseName();
104
105         if (! _componentName.isEmpty()) {
106             // ### TODO: check the component name.
107
108             if (! _componentName.at(0).isUpper())
109                 _componentName.clear();
110         }
111     }
112 }
113
114 Document::~Document()
115 {
116     if (_bind)
117         delete _bind;
118
119     if (_engine)
120         delete _engine;
121
122     if (_pool)
123         delete _pool;
124 }
125
126 Document::Ptr Document::create(const QString &fileName)
127 {
128     Document::Ptr doc(new Document(fileName));
129     return doc;
130 }
131
132 bool Document::isQmlDocument() const
133 {
134     return _isQmlDocument;
135 }
136
137 bool Document::isJSDocument() const
138 {
139     return ! _isQmlDocument;
140 }
141
142 AST::UiProgram *Document::qmlProgram() const
143 {
144     return cast<UiProgram *>(_ast);
145 }
146
147 AST::Program *Document::jsProgram() const
148 {
149     return cast<Program *>(_ast);
150 }
151
152 AST::ExpressionNode *Document::expression() const
153 {
154     if (_ast)
155         return _ast->expressionCast();
156
157     return 0;
158 }
159
160 AST::Node *Document::ast() const
161 {
162     return _ast;
163 }
164
165 const QmlJS::Engine *Document::engine() const
166 {
167     return _engine;
168 }
169
170 QList<DiagnosticMessage> Document::diagnosticMessages() const
171 {
172     return _diagnosticMessages;
173 }
174
175 QString Document::source() const
176 {
177     return _source;
178 }
179
180 void Document::setSource(const QString &source)
181 {
182     _source = source;
183 }
184
185 int Document::editorRevision() const
186 {
187     return _editorRevision;
188 }
189
190 void Document::setEditorRevision(int revision)
191 {
192     _editorRevision = revision;
193 }
194
195 QString Document::fileName() const
196 {
197     return _fileName;
198
199 }
200
201 QString Document::path() const
202 {
203     return _path;
204 }
205
206 QString Document::componentName() const
207 {
208     return _componentName;
209 }
210
211 bool Document::parse_helper(int startToken)
212 {
213     Q_ASSERT(! _engine);
214     Q_ASSERT(! _pool);
215     Q_ASSERT(! _ast);
216     Q_ASSERT(! _bind);
217
218     _engine = new Engine();
219     _pool = new NodePool(_fileName, _engine);
220
221     Lexer lexer(_engine);
222     Parser parser(_engine);
223
224     QString source = _source;
225     if (startToken == QmlJSGrammar::T_FEED_JS_PROGRAM)
226         extractPragmas(&source);
227
228     lexer.setCode(source, /*line = */ 1);
229
230     switch (startToken) {
231     case QmlJSGrammar::T_FEED_UI_PROGRAM:
232         _parsedCorrectly = parser.parse();
233         break;
234     case QmlJSGrammar::T_FEED_JS_PROGRAM:
235         _parsedCorrectly = parser.parseProgram();
236         break;
237     case QmlJSGrammar::T_FEED_JS_EXPRESSION:
238         _parsedCorrectly = parser.parseExpression();
239         break;
240     default:
241         Q_ASSERT(0);
242     }
243
244     _ast = parser.rootNode();
245     _diagnosticMessages = parser.diagnosticMessages();
246
247     _bind = new Bind(this, &_diagnosticMessages);
248
249     return _parsedCorrectly;
250 }
251
252 bool Document::parse()
253 {
254     if (isQmlDocument())
255         return parseQml();
256
257     return parseJavaScript();
258 }
259
260 bool Document::parseQml()
261 {
262     return parse_helper(QmlJSGrammar::T_FEED_UI_PROGRAM);
263 }
264
265 bool Document::parseJavaScript()
266 {
267     return parse_helper(QmlJSGrammar::T_FEED_JS_PROGRAM);
268 }
269
270 bool Document::parseExpression()
271 {
272     return parse_helper(QmlJSGrammar::T_FEED_JS_EXPRESSION);
273 }
274
275 Bind *Document::bind() const
276 {
277     return _bind;
278 }
279
280 // this is essentially a copy of QDeclarativeScriptParser::extractPragmas
281 void Document::extractPragmas(QString *source)
282 {
283     const QChar forwardSlash(QLatin1Char('/'));
284     const QChar star(QLatin1Char('*'));
285     const QChar newline(QLatin1Char('\n'));
286     const QChar dot(QLatin1Char('.'));
287     const QChar semicolon(QLatin1Char(';'));
288     const QChar space(QLatin1Char(' '));
289     const QString pragma(QLatin1String(".pragma "));
290
291     const QChar *pragmaData = pragma.constData();
292
293     QString &script = *source;
294     const QChar *data = script.constData();
295     const int length = script.count();
296     for (int ii = 0; ii < length; ++ii) {
297         const QChar &c = data[ii];
298
299         if (c.isSpace())
300             continue;
301
302         if (c == forwardSlash) {
303             ++ii;
304             if (ii >= length)
305                 return;
306
307             const QChar &c = data[ii];
308             if (c == forwardSlash) {
309                 // Find next newline
310                 while (ii < length && data[++ii] != newline) {};
311             } else if (c == star) {
312                 // Find next star
313                 while (true) {
314                     while (ii < length && data[++ii] != star) {};
315                     if (ii + 1 >= length)
316                         return;
317
318                     if (data[ii + 1] == forwardSlash) {
319                         ++ii;
320                         break;
321                     }
322                 }
323             } else {
324                 return;
325             }
326         } else if (c == dot) {
327             // Could be a pragma!
328             if (ii + pragma.length() >= length ||
329                 0 != ::memcmp(data + ii, pragmaData, sizeof(QChar) * pragma.length()))
330                 return;
331
332             int pragmaStatementIdx = ii;
333
334             ii += pragma.length();
335
336             while (ii < length && data[ii].isSpace()) { ++ii; }
337
338             int startIdx = ii;
339
340             while (ii < length && data[ii].isLetter()) { ++ii; }
341
342             int endIdx = ii;
343
344             if (ii != length && data[ii] != forwardSlash && !data[ii].isSpace() && data[ii] != semicolon)
345                 return;
346
347             QString p(data + startIdx, endIdx - startIdx);
348
349             if (p != QLatin1String("library"))
350                 return;
351
352             for (int jj = pragmaStatementIdx; jj < endIdx; ++jj) script[jj] = space;
353
354         } else {
355             return;
356         }
357     }
358 }
359
360 LibraryInfo::LibraryInfo()
361     : _valid(false)
362     , _dumpStatus(DumpNotStartedOrRunning)
363 {
364 }
365
366 LibraryInfo::LibraryInfo(const QmlDirParser &parser)
367     : _valid(true)
368     , _components(parser.components())
369     , _plugins(parser.plugins())
370     , _dumpStatus(DumpNotStartedOrRunning)
371 {
372 }
373
374 LibraryInfo::~LibraryInfo()
375 {
376 }
377
378 Snapshot::Snapshot()
379 {
380 }
381
382 Snapshot::~Snapshot()
383 {
384 }
385
386 void Snapshot::insert(const Document::Ptr &document)
387 {
388     if (document && (document->qmlProgram() || document->jsProgram())) {
389         const QString fileName = document->fileName();
390         const QString path = document->path();
391
392         remove(fileName);
393         _documentsByPath[path].append(document);
394         _documents.insert(fileName, document);
395     }
396 }
397
398 void Snapshot::insertLibraryInfo(const QString &path, const LibraryInfo &info)
399 {
400     _libraries.insert(QDir::cleanPath(path), info);
401 }
402
403 void Snapshot::remove(const QString &fileName)
404 {
405     Document::Ptr doc = _documents.value(fileName);
406     if (!doc.isNull()) {
407         const QString &path = doc->path();
408
409         QList<Document::Ptr> docs = _documentsByPath.value(path);
410         docs.removeAll(doc);
411         _documentsByPath[path] = docs;
412
413         _documents.remove(fileName);
414     }
415 }
416
417 Document::Ptr Snapshot::documentFromSource(const QString &code,
418                                            const QString &fileName) const
419 {
420     Document::Ptr newDoc = Document::create(fileName);
421
422     if (Document::Ptr thisDocument = document(fileName)) {
423         newDoc->_editorRevision = thisDocument->_editorRevision;
424     }
425
426     newDoc->setSource(code);
427     return newDoc;
428 }
429
430 Document::Ptr Snapshot::document(const QString &fileName) const
431 {
432     return _documents.value(QDir::cleanPath(fileName));
433 }
434
435 QList<Document::Ptr> Snapshot::documentsInDirectory(const QString &path) const
436 {
437     return _documentsByPath.value(QDir::cleanPath(path));
438 }
439
440 LibraryInfo Snapshot::libraryInfo(const QString &path) const
441 {
442     return _libraries.value(QDir::cleanPath(path));
443 }