1 /****************************************************************************
3 ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
7 ** This file is part of the tools applications of the Qt Toolkit.
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
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.
40 ****************************************************************************/
48 #include "cppcodeparser.h"
49 #include "jambiapiparser.h"
55 static const char USED_INTERNALLY
[] = "";
57 static Text
textWithFixedBrief(const Text
&text
, const Text
&beforeBrief
,
58 const Text
&afterBrief
)
62 const Atom
*atom
= text
.firstAtom();
64 if (atom
->type() == Atom::BriefLeft
) {
65 result
<< Atom::ParaLeft
<< beforeBrief
;
66 } else if (atom
->type() == Atom::BriefRight
) {
67 result
<< afterBrief
<< Atom::ParaRight
;
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
)) {
94 if (javaFunc
->parameterNames().count() == 1
95 && !javaFunc
->parameterNames().first().isEmpty()) {
97 << Atom(Atom::FormattingLeft
, ATOM_FORMATTING_PARAMETER
)
98 << javaFunc
->parameterNames().first()
99 << Atom(Atom::FormattingRight
, ATOM_FORMATTING_PARAMETER
);
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
);
109 QStringList javaParams
= javaFunc
->parameterNames();
110 QStringList cppParams
= cppFunc
->parameterNames();
111 newDoc
.renameParameters(cppParams
, javaParams
);
113 if (cppNode
->access() == Node::Private
) {
115 text
<< Atom::ParaLeft
;
116 if (cppFunc
->reimplementedFrom()) {
117 text
<< "This function is reimplemented for internal reasons.";
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") {
132 if (javaFunc
->parameterNames().count() == 1
133 && !javaFunc
->parameterNames().first().isEmpty()) {
135 << Atom(Atom::FormattingLeft
, ATOM_FORMATTING_PARAMETER
)
136 << javaFunc
->parameterNames().first()
137 << Atom(Atom::FormattingRight
, ATOM_FORMATTING_PARAMETER
);
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
);
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();
166 if (atom
->type() == Atom::ListTagLeft
&& atom
->string() == ATOM_LIST_VALUE
) {
170 if (atom
->string() == enumItemName
)
171 return body
.subText(Atom::ListItemLeft
, Atom::ListItemRight
, atom
);
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()
204 QString
JambiApiParser::sourceFileNameFilter()
209 void JambiApiParser::parseSourceFile(const Location
&location
, const QString
&filePath
, Tree
*tree
)
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
));
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(),
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
);
254 javaTre
->resolveInheritance();
255 jambifyDocsPass2(javaTre
->root());
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.
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
);
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
);
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
;
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
);
309 japiLocation
.warning(tr("Cannot find C++ class or enum '%1'").arg(info
.cppName
));
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('.'),
317 if (!javaImplements
.isEmpty())
318 javaTre
->addBaseClass(javaClass
, Node::Public
, javaImplements
.split('.'),
321 info
.javaNode
= javaClass
;
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"))
336 QString cppSignature
= attributes
.value("cpp");
338 CppCodeParser cppParser
;
339 const FunctionNode
*cppNode
= cppParser
.findFunctionNode(cppSignature
, cppTre
,
346 Default constructors sometimes don't exist in C++.
348 if (!quiet
&& javaSignature
== "public " + javaParent
->name() + "()")
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
);
363 setStatus(javaNode
, cppNode
);
365 int overloadNo
= cppNode
->parameters().count() - javaNode
->parameters().count() + 1;
366 if (overloadNo
== 1) {
367 setPass1JambifiedDoc(javaNode
, cppNode
);
371 text
<< Atom::ParaLeft
<< "Equivalent to "
372 << Atom(Atom::Link
, javaNode
->name() + "()")
373 << Atom(Atom::FormattingLeft
, ATOM_FORMATTING_LINK
)
375 << Atom(Atom::FormattingRight
, ATOM_FORMATTING_LINK
)
378 for (int i
= 0; i
< cppNode
->parameters().count(); ++i
) {
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
);
386 // ### convert to Java
387 text
<< cppNode
->parameters().at(i
).defaultValue();
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"))
405 QString cppVariable
= attributes
.value("cpp");
407 VariableNode
*cppNode
= static_cast<VariableNode
*>(cppParent
->findNode(cppVariable
,
409 FunctionNode
*javaNode
;
410 if (makeFunctionNode(javaParent
, javaSignature
, &javaNode
)) {
411 javaNode
->setLocation(japiLocation
);
415 japiLocation
.warning(tr("Cannot find C++ variable '%1' ('%2')")
416 .arg(cppVariable
).arg(cppParent
->name()));
418 javaNode
->setDoc(Doc(japiLocation
, japiLocation
,
420 QSet
<QString
>()), true);
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");
432 EnumItem
item(javaName
, value
, findEnumText(javaEnum
, javaName
));
433 javaEnum
->addItem(item
);
440 bool JambiApiParser::endElement(const QString
& /* namespaceURI */,
441 const QString
& /* localName */,
442 const QString
&qName
)
444 if (qName
== "class" || qName
== "enum")
445 classAndEnumStack
.pop();
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()));
457 void JambiApiParser::jambifyDocsPass2(Node
*node
)
459 const Doc
&doc
= node
->doc();
460 if (!doc
.isEmpty()) {
461 if (node
->type() == Node::Enum
) {
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
;
481 bool statique
= false;
483 QString mySynopsis
= synopsis
.simplified();
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);
508 if (mySynopsis
.startsWith("static ")) {
509 mySynopsis
.remove(0, 7);
512 } while (oldLen
!= mySynopsis
.length());
514 // method or constructor
515 QRegExp
funcRegExp("(?:(.*) )?([A-Za-z_0-9]+)\\((.*)\\)");
516 if (!funcRegExp
.exactMatch(mySynopsis
))
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
¶m
, params
) {
533 if (paramRegExp
.exactMatch(param
)) {
534 func
->addParameter(Parameter(paramRegExp
.cap(1), "", paramRegExp
.cap(2)));
542 } else if (!parent
) {