tools/qdoc3/jambiapiparser.cpp
changeset 0 1918ee327afb
child 4 3b1da2848fc7
equal deleted inserted replaced
-1:000000000000 0:1918ee327afb
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2009 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.
       
    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 ** If you have questions regarding the use of this file, please contact
       
    29 ** Nokia at qt-info@nokia.com.
       
    30 **
       
    31 **
       
    32 **
       
    33 **
       
    34 **
       
    35 **
       
    36 **
       
    37 **
       
    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 
       
    53 QT_BEGIN_NAMESPACE
       
    54 
       
    55 static const char USED_INTERNALLY[] = "";
       
    56 
       
    57 static 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 
       
    77 static 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 
       
   152 static 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 
       
   161 static 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 
       
   180 JambiApiParser::JambiApiParser(Tree *cppTree)
       
   181     : cppTre(cppTree), javaTre(0), metJapiTag(false)
       
   182 {
       
   183 }
       
   184 
       
   185 JambiApiParser::~JambiApiParser()
       
   186 {
       
   187 }
       
   188 
       
   189 void JambiApiParser::initializeParser(const Config &config)
       
   190 {
       
   191     CodeParser::initializeParser(config);
       
   192 }
       
   193 
       
   194 void JambiApiParser::terminateParser()
       
   195 {
       
   196     CodeParser::terminateParser();
       
   197 }
       
   198 
       
   199 QString JambiApiParser::language()
       
   200 {
       
   201     return "Java";
       
   202 }
       
   203 
       
   204 QString JambiApiParser::sourceFileNameFilter()
       
   205 {
       
   206     return "*.japi";
       
   207 }
       
   208 
       
   209 void 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 
       
   229 void 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 
       
   260 bool 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 
       
   440 bool 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 
       
   449 bool 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 
       
   457 void 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 
       
   475 bool 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;
       
   518 
       
   519     QString retType = funcRegExp.cap(1);
       
   520     QString funcName = funcRegExp.cap(2);
       
   521     QStringList params = funcRegExp.cap(3).split(",");
       
   522 
       
   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);
       
   529 
       
   530     QRegExp paramRegExp(" ?([^ ].*) ([A-Za-z_0-9]+) ?");
       
   531 
       
   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
       
   537         }
       
   538     }
       
   539 
       
   540     if (funcPtr) {
       
   541         *funcPtr = func;
       
   542     } else if (!parent) {
       
   543         delete func;
       
   544     }
       
   545     return true;
       
   546 }
       
   547 
       
   548 QT_END_NAMESPACE