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

Last change on this file since 1028 was 846, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

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