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

Last change on this file since 348 was 2, checked in by Dmitry A. Kuminov, 16 years ago

Initially imported qt-all-opensource-src-4.5.1 from Trolltech.

File size: 19.1 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4** Contact: Qt Software Information ([email protected])
5**
6** This file is part of the tools applications of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial Usage
10** Licensees holding valid Qt Commercial licenses may use this file in
11** accordance with the Qt Commercial License Agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and Nokia.
14**
15** GNU Lesser General Public License Usage
16** Alternatively, this file may be used under the terms of the GNU Lesser
17** General Public License version 2.1 as published by the Free Software
18** Foundation and appearing in the file LICENSE.LGPL included in the
19** packaging of this file. Please review the following information to
20** ensure the GNU Lesser General Public License version 2.1 requirements
21** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
22**
23** In addition, as a special exception, Nokia gives you certain
24** additional rights. These rights are described in the Nokia Qt LGPL
25** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
26** 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 are unsure which license is appropriate for your use, please
37** contact the sales department 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() == FakeNode::Page) {
238 FakeNode *javaFake = new FakeNode(javaTre->root(), cppFake->name(),
239 cppFake->subType());
240 javaFake->setModuleName("com.trolltech.qt"); // ### hard-coded
241 javaFake->setTitle(cppFake->title());
242 javaFake->setSubTitle(cppFake->subTitle());
243 setStatus(javaFake, cppFake);
244 setPass1JambifiedDoc(javaFake, cppFake);
245 }
246 }
247 }
248
249 /*
250 Fix the docs.
251 */
252 if (javaTre) {
253 javaTre->resolveInheritance();
254 jambifyDocsPass2(javaTre->root());
255 javaTre = 0;
256 }
257}
258
259bool JambiApiParser::startElement(const QString & /* namespaceURI */,
260 const QString & /* localName */,
261 const QString &qName,
262 const QXmlAttributes &attributes)
263{
264 if (!metJapiTag && qName != "japi") {
265 // ### The file is not a JAPI file.
266 return true;
267 }
268 metJapiTag = true;
269
270 EnumNode *javaEnum = 0;
271 EnumNode *cppEnum = 0;
272 InnerNode *javaParent = javaTre->root();
273 InnerNode *cppParent = cppTre->root();
274
275 for (int i = 0; i < classAndEnumStack.count(); ++i) {
276 const ClassOrEnumInfo &info = classAndEnumStack.at(i);
277 if (info.cppNode) {
278 if (info.cppNode->type() == Node::Enum) {
279 Q_ASSERT(info.javaNode->type() == Node::Enum);
280 javaEnum = static_cast<EnumNode *>(info.javaNode);
281 cppEnum = static_cast<EnumNode *>(info.cppNode);
282 } else {
283 Q_ASSERT(info.javaNode->type() == Node::Class
284 || info.javaNode->type() == Node::Namespace);
285 javaParent = static_cast<InnerNode *>(info.javaNode);
286 cppParent = static_cast<InnerNode *>(info.cppNode);
287 }
288 }
289 }
290
291 if (qName == "class" || qName == "enum") {
292 Node::Type type = (qName == "class") ? Node::Class : Node::Enum;
293
294 QString javaExtends = attributes.value("java-extends");
295 QString javaImplements = attributes.value("javaimplements");
296
297 ClassOrEnumInfo info;
298 info.tag = qName;
299 info.javaName = attributes.value("java");
300 info.cppName = attributes.value("cpp");
301 info.cppNode = cppTre->findNode(info.cppName.split("::"), type, cppParent);
302 if (!info.cppNode && type == Node::Class) {
303 type = Node::Namespace;
304 info.cppNode = cppTre->findNode(info.cppName.split("::"), type, cppParent);
305 }
306
307 if (!info.cppNode) {
308 japiLocation.warning(tr("Cannot find C++ class or enum '%1'").arg(info.cppName));
309 } else {
310 if (qName == "class") {
311 ClassNode *javaClass = new ClassNode(javaParent, info.javaName);
312 javaClass->setModuleName(attributes.value("package"));
313 if (!javaExtends.isEmpty())
314 javaTre->addBaseClass(javaClass, Node::Public, javaExtends.split('.'),
315 javaExtends);
316 if (!javaImplements.isEmpty())
317 javaTre->addBaseClass(javaClass, Node::Public, javaImplements.split('.'),
318 javaExtends);
319
320 info.javaNode = javaClass;
321 } else {
322 info.javaNode = new EnumNode(javaParent, info.javaName);
323 }
324 info.javaNode->setLocation(japiLocation);
325 setStatus(info.javaNode, info.cppNode);
326
327 setPass1JambifiedDoc(info.javaNode, info.cppNode);
328 }
329 classAndEnumStack.push(info);
330 } else if (qName == "method" || qName == "signal") {
331 QString javaSignature = attributes.value("java");
332 if (javaSignature.startsWith("private"))
333 return true;
334
335 QString cppSignature = attributes.value("cpp");
336
337 CppCodeParser cppParser;
338 const FunctionNode *cppNode = cppParser.findFunctionNode(cppSignature, cppTre,
339 cppParent,
340 true /* fuzzy */);
341 if (!cppNode) {
342 bool quiet = false;
343
344 /*
345 Default constructors sometimes don't exist in C++.
346 */
347 if (!quiet && javaSignature == "public " + javaParent->name() + "()")
348 quiet = true;
349
350 if (!quiet)
351 japiLocation.warning(tr("Cannot find C++ function '%1' ('%2')")
352 .arg(cppSignature).arg(cppParent->name()));
353 }
354
355 FunctionNode *javaNode;
356 if (makeFunctionNode(javaParent, javaSignature, &javaNode)) {
357 javaNode->setLocation(japiLocation);
358 if (qName == "signal")
359 javaNode->setMetaness(FunctionNode::Signal);
360
361 if (cppNode) {
362 setStatus(javaNode, cppNode);
363
364 int overloadNo = cppNode->parameters().count() - javaNode->parameters().count() + 1;
365 if (overloadNo == 1) {
366 setPass1JambifiedDoc(javaNode, cppNode);
367 } else {
368 Text text;
369
370 text << Atom::ParaLeft << "Equivalent to "
371 << Atom(Atom::Link, javaNode->name() + "()")
372 << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
373 << javaNode->name()
374 << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK)
375 << "(";
376
377 for (int i = 0; i < cppNode->parameters().count(); ++i) {
378 if (i > 0)
379 text << ", ";
380 if (i < javaNode->parameters().count()) {
381 text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_PARAMETER)
382 << javaNode->parameters().at(i).name()
383 << Atom(Atom::FormattingRight, ATOM_FORMATTING_PARAMETER);
384 } else {
385 // ### convert to Java
386 text << cppNode->parameters().at(i).defaultValue();
387 }
388 }
389
390 text << ").";
391
392 Doc doc;
393 doc.setBody(text);
394 javaNode->setDoc(doc, true);
395 }
396 javaNode->setOverload(overloadNo > 1);
397 }
398 }
399 } else if (qName == "variablesetter" || qName == "variablegetter") {
400 QString javaSignature = attributes.value("java");
401 if (javaSignature.startsWith("private"))
402 return true;
403
404 QString cppVariable = attributes.value("cpp");
405
406 VariableNode *cppNode = static_cast<VariableNode *>(cppParent->findNode(cppVariable,
407 Node::Variable));
408 FunctionNode *javaNode;
409 if (makeFunctionNode(javaParent, javaSignature, &javaNode)) {
410 javaNode->setLocation(japiLocation);
411
412 if (!cppNode) {
413#if 0
414 japiLocation.warning(tr("Cannot find C++ variable '%1' ('%2')")
415 .arg(cppVariable).arg(cppParent->name()));
416#endif
417 javaNode->setDoc(Doc(japiLocation, japiLocation,
418 USED_INTERNALLY,
419 QSet<QString>()), true);
420 } else {
421 setPass1JambifiedDoc(javaNode, cppNode, qName);
422 setStatus(javaNode, cppNode);
423 }
424 }
425 } else if (qName == "enum-value") {
426 QString javaName = attributes.value("java");
427 QString cppName = attributes.value("cpp");
428 QString value = attributes.value("value");
429
430 if (javaEnum) {
431 EnumItem item(javaName, value, findEnumText(javaEnum, javaName));
432 javaEnum->addItem(item);
433 }
434 }
435
436 return true;
437}
438
439bool JambiApiParser::endElement(const QString & /* namespaceURI */,
440 const QString & /* localName */,
441 const QString &qName)
442{
443 if (qName == "class" || qName == "enum")
444 classAndEnumStack.pop();
445 return true;
446}
447
448bool JambiApiParser::fatalError(const QXmlParseException &exception)
449{
450 japiLocation.setLineNo(exception.lineNumber());
451 japiLocation.setColumnNo(exception.columnNumber());
452 japiLocation.warning(tr("Syntax error in JAPI file (%1)").arg(exception.message()));
453 return true;
454}
455
456void JambiApiParser::jambifyDocsPass2(Node *node)
457{
458 const Doc &doc = node->doc();
459 if (!doc.isEmpty()) {
460 if (node->type() == Node::Enum) {
461 Doc newDoc(doc);
462 newDoc.simplifyEnumDoc();
463 node->setDoc(newDoc, true);
464 }
465 }
466
467 if (node->isInnerNode()) {
468 InnerNode *innerNode = static_cast<InnerNode *>(node);
469 foreach (Node *child, innerNode->childNodes())
470 jambifyDocsPass2(child);
471 }
472}
473
474bool JambiApiParser::makeFunctionNode(InnerNode *parent, const QString &synopsis,
475 FunctionNode **funcPtr)
476{
477 Node::Access access = Node::Public;
478 FunctionNode::Metaness metaness = FunctionNode::Plain;
479 bool final = false;
480 bool statique = false;
481
482 QString mySynopsis = synopsis.simplified();
483 int oldLen;
484 do {
485 oldLen = mySynopsis.length();
486
487 if (mySynopsis.startsWith("public ")) {
488 mySynopsis.remove(0, 7);
489 access = Node::Public;
490 }
491 if (mySynopsis.startsWith("protected ")) {
492 mySynopsis.remove(0, 10);
493 access = Node::Protected;
494 }
495 if (mySynopsis.startsWith("private ")) {
496 mySynopsis.remove(0, 8);
497 access = Node::Private;
498 }
499 if (mySynopsis.startsWith("native ")) {
500 mySynopsis.remove(0, 7);
501 metaness = FunctionNode::Native;
502 }
503 if (mySynopsis.startsWith("final ")) {
504 mySynopsis.remove(0, 6);
505 final = true;
506 }
507 if (mySynopsis.startsWith("static ")) {
508 mySynopsis.remove(0, 7);
509 statique = true;
510 }
511 } while (oldLen != mySynopsis.length());
512
513 // method or constructor
514 QRegExp funcRegExp("(?:(.*) )?([A-Za-z_0-9]+)\\((.*)\\)");
515 if (!funcRegExp.exactMatch(mySynopsis))
516 return false;
517
518 QString retType = funcRegExp.cap(1);
519 QString funcName = funcRegExp.cap(2);
520 QStringList params = funcRegExp.cap(3).split(",");
521
522 FunctionNode *func = new FunctionNode(parent, funcName);
523 func->setReturnType(retType);
524 func->setAccess(access);
525 func->setStatic(statique);
526 func->setConst(final);
527 func->setMetaness(metaness);
528
529 QRegExp paramRegExp(" ?([^ ].*) ([A-Za-z_0-9]+) ?");
530
531 foreach (const QString &param, params) {
532 if (paramRegExp.exactMatch(param)) {
533 func->addParameter(Parameter(paramRegExp.cap(1), "", paramRegExp.cap(2)));
534 } else {
535 // problem
536 }
537 }
538
539 if (funcPtr) {
540 *funcPtr = func;
541 } else if (!parent) {
542 delete func;
543 }
544 return true;
545}
546
547QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.