Merge branch 'master' of scm.dev.nokia.troll.no:qt/oslo-staging-1 into master-integration
[qt-netbsd.git] / tools / qdoc3 / jambiapiparser.cpp
blob3aba1b09973b9e51d4bf67a7defb50dc92c7093a
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 tools applications 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.
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.
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.
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
38 ** $QT_END_LICENSE$
40 ****************************************************************************/
43 jambiapiparser.cpp
46 #include <QtXml>
48 #include "cppcodeparser.h"
49 #include "jambiapiparser.h"
50 #include "node.h"
51 #include "tree.h"
53 QT_BEGIN_NAMESPACE
55 static const char USED_INTERNALLY[] = "";
57 static Text textWithFixedBrief(const Text &text, const Text &beforeBrief,
58 const Text &afterBrief)
60 Text result;
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;
71 atom = atom->next();
74 return result;
77 static void setPass1JambifiedDoc(Node *javaNode, const Node *cppNode, const QString &qName = "")
79 Doc newDoc(cppNode->doc());
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());
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);
101 afterBrief << ".";
102 text = textWithFixedBrief(text, Text("Sets "), afterBrief);
103 } else if (property->resetters().contains(mutableCppNode)) {
104 text = textWithFixedBrief(text, Text("Resets "), Text("."));
107 newDoc.setBody(text);
108 } else {
109 QStringList javaParams = javaFunc->parameterNames();
110 QStringList cppParams = cppFunc->parameterNames();
111 newDoc.renameParameters(cppParams, javaParams);
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;
121 text << Atom::ParaRight;
122 newDoc.setBody(text);
125 } else if (cppNode->type() == Node::Variable) {
126 Text text(newDoc.body());
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);
139 afterBrief << ".";
140 text = textWithFixedBrief(text, Text("Sets "), afterBrief);
143 newDoc.setBody(text);
145 } else { // ### enum value names?
149 javaNode->setDoc(newDoc, true);
152 static void setStatus(Node *javaNode, const Node *cppNode)
154 if (cppNode->status() == Node::Compat) {
155 javaNode->setStatus(Node::Obsolete);
156 } else {
157 javaNode->setStatus(cppNode->status());
161 static Text findEnumText(Node *javaEnum, const QString &enumItemName)
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);
173 } else {
174 atom = atom->next();
177 return Text();
180 JambiApiParser::JambiApiParser(Tree *cppTree)
181 : cppTre(cppTree), javaTre(0), metJapiTag(false)
185 JambiApiParser::~JambiApiParser()
189 void JambiApiParser::initializeParser(const Config &config)
191 CodeParser::initializeParser(config);
194 void JambiApiParser::terminateParser()
196 CodeParser::terminateParser();
199 QString JambiApiParser::language()
201 return "Java";
204 QString JambiApiParser::sourceFileNameFilter()
206 return "*.japi";
209 void JambiApiParser::parseSourceFile(const Location &location, const QString &filePath, Tree *tree)
211 javaTre = tree;
212 metJapiTag = false;
214 QXmlSimpleReader reader;
215 reader.setContentHandler(this);
216 reader.setErrorHandler(this);
218 QFile file(filePath);
219 if (!file.open(QFile::ReadOnly)) {
220 location.warning(tr("Cannot open JAPI file '%1'").arg(filePath));
221 return;
224 japiLocation = Location(filePath);
225 QXmlInputSource xmlSource(&file);
226 reader.parse(xmlSource);
229 void JambiApiParser::doneParsingSourceFiles(Tree * /* tree */)
232 Also import the overview documents.
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);
251 Fix the docs.
253 if (javaTre) {
254 javaTre->resolveInheritance();
255 jambifyDocsPass2(javaTre->root());
256 javaTre = 0;
260 bool JambiApiParser::startElement(const QString & /* namespaceURI */,
261 const QString & /* localName */,
262 const QString &qName,
263 const QXmlAttributes &attributes)
265 if (!metJapiTag && qName != "japi") {
266 // ### The file is not a JAPI file.
267 return true;
269 metJapiTag = true;
271 EnumNode *javaEnum = 0;
272 EnumNode *cppEnum = 0;
273 InnerNode *javaParent = javaTre->root();
274 InnerNode *cppParent = cppTre->root();
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);
292 if (qName == "class" || qName == "enum") {
293 Node::Type type = (qName == "class") ? Node::Class : Node::Enum;
295 QString javaExtends = attributes.value("java-extends");
296 QString javaImplements = attributes.value("javaimplements");
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);
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);
321 info.javaNode = javaClass;
322 } else {
323 info.javaNode = new EnumNode(javaParent, info.javaName);
325 info.javaNode->setLocation(japiLocation);
326 setStatus(info.javaNode, info.cppNode);
328 setPass1JambifiedDoc(info.javaNode, info.cppNode);
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;
336 QString cppSignature = attributes.value("cpp");
338 CppCodeParser cppParser;
339 const FunctionNode *cppNode = cppParser.findFunctionNode(cppSignature, cppTre,
340 cppParent,
341 true /* fuzzy */);
342 if (!cppNode) {
343 bool quiet = false;
346 Default constructors sometimes don't exist in C++.
348 if (!quiet && javaSignature == "public " + javaParent->name() + "()")
349 quiet = true;
351 if (!quiet)
352 japiLocation.warning(tr("Cannot find C++ function '%1' ('%2')")
353 .arg(cppSignature).arg(cppParent->name()));
356 FunctionNode *javaNode;
357 if (makeFunctionNode(javaParent, javaSignature, &javaNode)) {
358 javaNode->setLocation(japiLocation);
359 if (qName == "signal")
360 javaNode->setMetaness(FunctionNode::Signal);
362 if (cppNode) {
363 setStatus(javaNode, cppNode);
365 int overloadNo = cppNode->parameters().count() - javaNode->parameters().count() + 1;
366 if (overloadNo == 1) {
367 setPass1JambifiedDoc(javaNode, cppNode);
368 } else {
369 Text text;
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 << "(";
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();
391 text << ").";
393 Doc doc;
394 doc.setBody(text);
395 javaNode->setDoc(doc, true);
397 javaNode->setOverload(overloadNo > 1);
400 } else if (qName == "variablesetter" || qName == "variablegetter") {
401 QString javaSignature = attributes.value("java");
402 if (javaSignature.startsWith("private"))
403 return true;
405 QString cppVariable = attributes.value("cpp");
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);
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);
426 } else if (qName == "enum-value") {
427 QString javaName = attributes.value("java");
428 QString cppName = attributes.value("cpp");
429 QString value = attributes.value("value");
431 if (javaEnum) {
432 EnumItem item(javaName, value, findEnumText(javaEnum, javaName));
433 javaEnum->addItem(item);
437 return true;
440 bool JambiApiParser::endElement(const QString & /* namespaceURI */,
441 const QString & /* localName */,
442 const QString &qName)
444 if (qName == "class" || qName == "enum")
445 classAndEnumStack.pop();
446 return true;
449 bool JambiApiParser::fatalError(const QXmlParseException &exception)
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;
457 void JambiApiParser::jambifyDocsPass2(Node *node)
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);
468 if (node->isInnerNode()) {
469 InnerNode *innerNode = static_cast<InnerNode *>(node);
470 foreach (Node *child, innerNode->childNodes())
471 jambifyDocsPass2(child);
475 bool JambiApiParser::makeFunctionNode(InnerNode *parent, const QString &synopsis,
476 FunctionNode **funcPtr)
478 Node::Access access = Node::Public;
479 FunctionNode::Metaness metaness = FunctionNode::Plain;
480 bool final = false;
481 bool statique = false;
483 QString mySynopsis = synopsis.simplified();
484 int oldLen;
485 do {
486 oldLen = mySynopsis.length();
488 if (mySynopsis.startsWith("public ")) {
489 mySynopsis.remove(0, 7);
490 access = Node::Public;
492 if (mySynopsis.startsWith("protected ")) {
493 mySynopsis.remove(0, 10);
494 access = Node::Protected;
496 if (mySynopsis.startsWith("private ")) {
497 mySynopsis.remove(0, 8);
498 access = Node::Private;
500 if (mySynopsis.startsWith("native ")) {
501 mySynopsis.remove(0, 7);
502 metaness = FunctionNode::Native;
504 if (mySynopsis.startsWith("final ")) {
505 mySynopsis.remove(0, 6);
506 final = true;
508 if (mySynopsis.startsWith("static ")) {
509 mySynopsis.remove(0, 7);
510 statique = true;
512 } while (oldLen != mySynopsis.length());
514 // method or constructor
515 QRegExp funcRegExp("(?:(.*) )?([A-Za-z_0-9]+)\\((.*)\\)");
516 if (!funcRegExp.exactMatch(mySynopsis))
517 return false;
519 QString retType = funcRegExp.cap(1);
520 QString funcName = funcRegExp.cap(2);
521 QStringList params = funcRegExp.cap(3).split(",");
523 FunctionNode *func = new FunctionNode(parent, funcName);
524 func->setReturnType(retType);
525 func->setAccess(access);
526 func->setStatic(statique);
527 func->setConst(final);
528 func->setMetaness(metaness);
530 QRegExp paramRegExp(" ?([^ ].*) ([A-Za-z_0-9]+) ?");
532 foreach (const QString &param, params) {
533 if (paramRegExp.exactMatch(param)) {
534 func->addParameter(Parameter(paramRegExp.cap(1), "", paramRegExp.cap(2)));
535 } else {
536 // problem
540 if (funcPtr) {
541 *funcPtr = func;
542 } else if (!parent) {
543 delete func;
545 return true;
548 QT_END_NAMESPACE