source: trunk/tools/qdoc3/jambiapiparser.cpp@ 568

Last change on this file since 568 was 561, checked in by Dmitry A. Kuminov, 15 years ago

trunk: Merged in qt 4.6.1 sources.

File size: 19.1 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation ([email protected])
6**
7** This file is part of the tools applications of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
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** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you have questions regarding the use of this file, please contact
37** Nokia at [email protected].
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42/*
43 jambiapiparser.cpp
44*/
45
46#include <QtXml>
47
48#include "cppcodeparser.h"
49#include "jambiapiparser.h"
50#include "node.h"
51#include "tree.h"
52
53QT_BEGIN_NAMESPACE
54
55static const char USED_INTERNALLY[] = "";
56
57static Text textWithFixedBrief(const Text &text, const Text &beforeBrief,
58 const Text &afterBrief)
59{
60 Text result;
61
62 const Atom *atom = text.firstAtom();
63 while (atom) {
64 if (atom->type() == Atom::BriefLeft) {
65 result << Atom::ParaLeft << beforeBrief;
66 } else if (atom->type() == Atom::BriefRight) {
67 result << afterBrief << Atom::ParaRight;
68 } else {
69 result << *atom;
70 }
71 atom = atom->next();
72 }
73
74 return result;
75}
76
77static void setPass1JambifiedDoc(Node *javaNode, const Node *cppNode, const QString &qName = "")
78{
79 Doc newDoc(cppNode->doc());
80
81 if (javaNode->type() == Node::Function) {
82 const FunctionNode *javaFunc = static_cast<const FunctionNode *>(javaNode);
83 if (cppNode->type() == Node::Function) {
84 const FunctionNode *cppFunc = static_cast<const FunctionNode *>(cppNode);
85 if (const PropertyNode *property = cppFunc->associatedProperty()) {
86 newDoc = property->doc();
87 Text text(newDoc.body());
88
89 Node *mutableCppNode = const_cast<Node *>(cppNode);
90 if (property->getters().contains(mutableCppNode)) {
91 text = textWithFixedBrief(text, Text("Returns "), Text("."));
92 } else if (property->setters().contains(mutableCppNode)) {
93 Text afterBrief;
94 if (javaFunc->parameterNames().count() == 1
95 && !javaFunc->parameterNames().first().isEmpty()) {
96 afterBrief << " to "
97 << Atom(Atom::FormattingLeft, ATOM_FORMATTING_PARAMETER)
98 << javaFunc->parameterNames().first()
99 << Atom(Atom::FormattingRight, ATOM_FORMATTING_PARAMETER);
100 }
101 afterBrief << ".";
102 text = textWithFixedBrief(text, Text("Sets "), afterBrief);
103 } else if (property->resetters().contains(mutableCppNode)) {
104 text = textWithFixedBrief(text, Text("Resets "), Text("."));
105 }
106
107 newDoc.setBody(text);
108 } else {
109 QStringList javaParams = javaFunc->parameterNames();
110 QStringList cppParams = cppFunc->parameterNames();
111 newDoc.renameParameters(cppParams, javaParams);
112
113 if (cppNode->access() == Node::Private) {
114 Text text;
115 text << Atom::ParaLeft;
116 if (cppFunc->reimplementedFrom()) {
117 text << "This function is reimplemented for internal reasons.";
118 } else {
119 text << USED_INTERNALLY;
120 }
121 text << Atom::ParaRight;
122 newDoc.setBody(text);
123 }
124 }
125 } else if (cppNode->type() == Node::Variable) {
126 Text text(newDoc.body());
127
128 if (qName == "variablegetter") {
129 text = textWithFixedBrief(text, Text("Returns "), Text("."));
130 } else if (qName == "variablesetter") {
131 Text afterBrief;
132 if (javaFunc->parameterNames().count() == 1
133 && !javaFunc->parameterNames().first().isEmpty()) {
134 afterBrief << " to "
135 << Atom(Atom::FormattingLeft, ATOM_FORMATTING_PARAMETER)
136 << javaFunc->parameterNames().first()
137 << Atom(Atom::FormattingRight, ATOM_FORMATTING_PARAMETER);
138 }
139 afterBrief << ".";
140 text = textWithFixedBrief(text, Text("Sets "), afterBrief);
141 }
142
143 newDoc.setBody(text);
144 }
145 } else { // ### enum value names?
146
147 }
148
149 javaNode->setDoc(newDoc, true);
150}
151
152static void setStatus(Node *javaNode, const Node *cppNode)
153{
154 if (cppNode->status() == Node::Compat) {
155 javaNode->setStatus(Node::Obsolete);
156 } else {
157 javaNode->setStatus(cppNode->status());
158 }
159}
160
161static Text findEnumText(Node *javaEnum, const QString &enumItemName)
162{
163 const Text &body = javaEnum->doc().body();
164 const Atom *atom = body.firstAtom();
165 while (atom) {
166 if (atom->type() == Atom::ListTagLeft && atom->string() == ATOM_LIST_VALUE) {
167 atom = atom->next();
168 if (atom) {
169 // ### paras?
170 if (atom->string() == enumItemName)
171 return body.subText(Atom::ListItemLeft, Atom::ListItemRight, atom);
172 }
173 } else {
174 atom = atom->next();
175 }
176 }
177 return Text();
178}
179
180JambiApiParser::JambiApiParser(Tree *cppTree)
181 : cppTre(cppTree), javaTre(0), metJapiTag(false)
182{
183}
184
185JambiApiParser::~JambiApiParser()
186{
187}
188
189void JambiApiParser::initializeParser(const Config &config)
190{
191 CodeParser::initializeParser(config);
192}
193
194void JambiApiParser::terminateParser()
195{
196 CodeParser::terminateParser();
197}
198
199QString JambiApiParser::language()
200{
201 return "Java";
202}
203
204QString JambiApiParser::sourceFileNameFilter()
205{
206 return "*.japi";
207}
208
209void JambiApiParser::parseSourceFile(const Location &location, const QString &filePath, Tree *tree)
210{
211 javaTre = tree;
212 metJapiTag = false;
213
214 QXmlSimpleReader reader;
215 reader.setContentHandler(this);
216 reader.setErrorHandler(this);
217
218 QFile file(filePath);
219 if (!file.open(QFile::ReadOnly)) {
220 location.warning(tr("Cannot open JAPI file '%1'").arg(filePath));
221 return;
222 }
223
224 japiLocation = Location(filePath);
225 QXmlInputSource xmlSource(&file);
226 reader.parse(xmlSource);
227}
228
229void JambiApiParser::doneParsingSourceFiles(Tree * /* tree */)
230{
231 /*
232 Also import the overview documents.
233 */
234 foreach (Node *cppNode, cppTre->root()->childNodes()) {
235 if (cppNode->type() == Node::Fake) {
236 FakeNode *cppFake = static_cast<FakeNode *>(cppNode);
237 if (cppFake->subType() == Node::Page) {
238 FakeNode *javaFake = new FakeNode(javaTre->root(),
239 cppFake->name(),
240 cppFake->subType());
241 javaFake->setModuleName("com.trolltech.qt"); // ### hard-coded
242 javaFake->setTitle(cppFake->title());
243 javaFake->setSubTitle(cppFake->subTitle());
244 setStatus(javaFake, cppFake);
245 setPass1JambifiedDoc(javaFake, cppFake);
246 }
247 }
248 }
249
250 /*
251 Fix the docs.
252 */
253 if (javaTre) {
254 javaTre->resolveInheritance();
255 jambifyDocsPass2(javaTre->root());
256 javaTre = 0;
257 }
258}
259
260bool JambiApiParser::startElement(const QString & /* namespaceURI */,
261 const QString & /* localName */,
262 const QString &qName,
263 const QXmlAttributes &attributes)
264{
265 if (!metJapiTag && qName != "japi") {
266 // ### The file is not a JAPI file.
267 return true;
268 }
269 metJapiTag = true;
270
271 EnumNode *javaEnum = 0;
272 EnumNode *cppEnum = 0;
273 InnerNode *javaParent = javaTre->root();
274 InnerNode *cppParent = cppTre->root();
275
276 for (int i = 0; i < classAndEnumStack.count(); ++i) {
277 const ClassOrEnumInfo &info = classAndEnumStack.at(i);
278 if (info.cppNode) {
279 if (info.cppNode->type() == Node::Enum) {
280 Q_ASSERT(info.javaNode->type() == Node::Enum);
281 javaEnum = static_cast<EnumNode *>(info.javaNode);
282 cppEnum = static_cast<EnumNode *>(info.cppNode);
283 } else {
284 Q_ASSERT(info.javaNode->type() == Node::Class
285 || info.javaNode->type() == Node::Namespace);
286 javaParent = static_cast<InnerNode *>(info.javaNode);
287 cppParent = static_cast<InnerNode *>(info.cppNode);
288 }
289 }
290 }
291
292 if (qName == "class" || qName == "enum") {
293 Node::Type type = (qName == "class") ? Node::Class : Node::Enum;
294
295 QString javaExtends = attributes.value("java-extends");
296 QString javaImplements = attributes.value("javaimplements");
297
298 ClassOrEnumInfo info;
299 info.tag = qName;
300 info.javaName = attributes.value("java");
301 info.cppName = attributes.value("cpp");
302 info.cppNode = cppTre->findNode(info.cppName.split("::"), type, cppParent);
303 if (!info.cppNode && type == Node::Class) {
304 type = Node::Namespace;
305 info.cppNode = cppTre->findNode(info.cppName.split("::"), type, cppParent);
306 }
307
308 if (!info.cppNode) {
309 japiLocation.warning(tr("Cannot find C++ class or enum '%1'").arg(info.cppName));
310 } else {
311 if (qName == "class") {
312 ClassNode *javaClass = new ClassNode(javaParent, info.javaName);
313 javaClass->setModuleName(attributes.value("package"));
314 if (!javaExtends.isEmpty())
315 javaTre->addBaseClass(javaClass, Node::Public, javaExtends.split('.'),
316 javaExtends);
317 if (!javaImplements.isEmpty())
318 javaTre->addBaseClass(javaClass, Node::Public, javaImplements.split('.'),
319 javaExtends);
320
321 info.javaNode = javaClass;
322 } else {
323 info.javaNode = new EnumNode(javaParent, info.javaName);
324 }
325 info.javaNode->setLocation(japiLocation);
326 setStatus(info.javaNode, info.cppNode);
327
328 setPass1JambifiedDoc(info.javaNode, info.cppNode);
329 }
330 classAndEnumStack.push(info);
331 } else if (qName == "method" || qName == "signal") {
332 QString javaSignature = attributes.value("java");
333 if (javaSignature.startsWith("private"))
334 return true;
335
336 QString cppSignature = attributes.value("cpp");
337
338 CppCodeParser cppParser;
339 const FunctionNode *cppNode = cppParser.findFunctionNode(cppSignature, cppTre,
340 cppParent,
341 true /* fuzzy */);
342 if (!cppNode) {
343 bool quiet = false;
344
345 /*
346 Default constructors sometimes don't exist in C++.
347 */
348 if (!quiet && javaSignature == "public " + javaParent->name() + "()")
349 quiet = true;
350
351 if (!quiet)
352 japiLocation.warning(tr("Cannot find C++ function '%1' ('%2')")
353 .arg(cppSignature).arg(cppParent->name()));
354 }
355
356 FunctionNode *javaNode;
357 if (makeFunctionNode(javaParent, javaSignature, &javaNode)) {
358 javaNode->setLocation(japiLocation);
359 if (qName == "signal")
360 javaNode->setMetaness(FunctionNode::Signal);
361
362 if (cppNode) {
363 setStatus(javaNode, cppNode);
364
365 int overloadNo = cppNode->parameters().count() - javaNode->parameters().count() + 1;
366 if (overloadNo == 1) {
367 setPass1JambifiedDoc(javaNode, cppNode);
368 } else {
369 Text text;
370
371 text << Atom::ParaLeft << "Equivalent to "
372 << Atom(Atom::Link, javaNode->name() + "()")
373 << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
374 << javaNode->name()
375 << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK)
376 << "(";
377
378 for (int i = 0; i < cppNode->parameters().count(); ++i) {
379 if (i > 0)
380 text << ", ";
381 if (i < javaNode->parameters().count()) {
382 text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_PARAMETER)
383 << javaNode->parameters().at(i).name()
384 << Atom(Atom::FormattingRight, ATOM_FORMATTING_PARAMETER);
385 } else {
386 // ### convert to Java
387 text << cppNode->parameters().at(i).defaultValue();
388 }
389 }
390
391 text << ").";
392
393 Doc doc;
394 doc.setBody(text);
395 javaNode->setDoc(doc, true);
396 }
397 javaNode->setOverload(overloadNo > 1);
398 }
399 }
400 } else if (qName == "variablesetter" || qName == "variablegetter") {
401 QString javaSignature = attributes.value("java");
402 if (javaSignature.startsWith("private"))
403 return true;
404
405 QString cppVariable = attributes.value("cpp");
406
407 VariableNode *cppNode = static_cast<VariableNode *>(cppParent->findNode(cppVariable,
408 Node::Variable));
409 FunctionNode *javaNode;
410 if (makeFunctionNode(javaParent, javaSignature, &javaNode)) {
411 javaNode->setLocation(japiLocation);
412
413 if (!cppNode) {
414#if 0
415 japiLocation.warning(tr("Cannot find C++ variable '%1' ('%2')")
416 .arg(cppVariable).arg(cppParent->name()));
417#endif
418 javaNode->setDoc(Doc(japiLocation, japiLocation,
419 USED_INTERNALLY,
420 QSet<QString>()), true);
421 } else {
422 setPass1JambifiedDoc(javaNode, cppNode, qName);
423 setStatus(javaNode, cppNode);
424 }
425 }
426 } else if (qName == "enum-value") {
427 QString javaName = attributes.value("java");
428 QString cppName = attributes.value("cpp");
429 QString value = attributes.value("value");
430
431 if (javaEnum) {
432 EnumItem item(javaName, value, findEnumText(javaEnum, javaName));
433 javaEnum->addItem(item);
434 }
435 }
436
437 return true;
438}
439
440bool JambiApiParser::endElement(const QString & /* namespaceURI */,
441 const QString & /* localName */,
442 const QString &qName)
443{
444 if (qName == "class" || qName == "enum")
445 classAndEnumStack.pop();
446 return true;
447}
448
449bool JambiApiParser::fatalError(const QXmlParseException &exception)
450{
451 japiLocation.setLineNo(exception.lineNumber());
452 japiLocation.setColumnNo(exception.columnNumber());
453 japiLocation.warning(tr("Syntax error in JAPI file (%1)").arg(exception.message()));
454 return true;
455}
456
457void JambiApiParser::jambifyDocsPass2(Node *node)
458{
459 const Doc &doc = node->doc();
460 if (!doc.isEmpty()) {
461 if (node->type() == Node::Enum) {
462 Doc newDoc(doc);
463 newDoc.simplifyEnumDoc();
464 node->setDoc(newDoc, true);
465 }
466 }
467
468 if (node->isInnerNode()) {
469 InnerNode *innerNode = static_cast<InnerNode *>(node);
470 foreach (Node *child, innerNode->childNodes())
471 jambifyDocsPass2(child);
472 }
473}
474
475bool JambiApiParser::makeFunctionNode(InnerNode *parent, const QString &synopsis,
476 FunctionNode **funcPtr)
477{
478 Node::Access access = Node::Public;
479 FunctionNode::Metaness metaness = FunctionNode::Plain;
480 bool final = false;
481 bool statique = false;
482
483 QString mySynopsis = synopsis.simplified();
484 int oldLen;
485 do {
486 oldLen = mySynopsis.length();
487
488 if (mySynopsis.startsWith("public ")) {
489 mySynopsis.remove(0, 7);
490 access = Node::Public;
491 }
492 if (mySynopsis.startsWith("protected ")) {
493 mySynopsis.remove(0, 10);
494 access = Node::Protected;
495 }
496 if (mySynopsis.startsWith("private ")) {
497 mySynopsis.remove(0, 8);
498 access = Node::Private;
499 }
500 if (mySynopsis.startsWith("native ")) {
501 mySynopsis.remove(0, 7);
502 metaness = FunctionNode::Native;
503 }
504 if (mySynopsis.startsWith("final ")) {
505 mySynopsis.remove(0, 6);
506 final = true;
507 }
508 if (mySynopsis.startsWith("static ")) {
509 mySynopsis.remove(0, 7);
510 statique = true;
511 }
512 } while (oldLen != mySynopsis.length());
513
514 // method or constructor
515 QRegExp funcRegExp("(?:(.*) )?([A-Za-z_0-9]+)\\((.*)\\)");
516 if (!funcRegExp.exactMatch(mySynopsis))
517 return false;