tools/qdoc3/jambiapiparser.cpp
changeset 0 1918ee327afb
child 4 3b1da2848fc7
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/qdoc3/jambiapiparser.cpp	Mon Jan 11 14:00:40 2010 +0000
@@ -0,0 +1,548 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights.  These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*
+    jambiapiparser.cpp
+*/
+
+#include <QtXml>
+
+#include "cppcodeparser.h"
+#include "jambiapiparser.h"
+#include "node.h"
+#include "tree.h"
+
+QT_BEGIN_NAMESPACE
+
+static const char USED_INTERNALLY[] = "";
+
+static Text textWithFixedBrief(const Text &text, const Text &beforeBrief,
+                               const Text &afterBrief)
+{
+    Text result;
+
+    const Atom *atom = text.firstAtom();
+    while (atom) {
+        if (atom->type() == Atom::BriefLeft) {
+            result << Atom::ParaLeft << beforeBrief;
+        } else if (atom->type() == Atom::BriefRight) {
+            result << afterBrief << Atom::ParaRight;
+        } else {
+            result << *atom;
+        }
+        atom = atom->next();
+    }
+
+    return result;
+}
+
+static void setPass1JambifiedDoc(Node *javaNode, const Node *cppNode, const QString &qName = "")
+{
+    Doc newDoc(cppNode->doc());
+
+    if (javaNode->type() == Node::Function) {
+        const FunctionNode *javaFunc = static_cast<const FunctionNode *>(javaNode);
+        if (cppNode->type() == Node::Function) {
+            const FunctionNode *cppFunc = static_cast<const FunctionNode *>(cppNode);
+            if (const PropertyNode *property = cppFunc->associatedProperty()) {
+                newDoc = property->doc();
+                Text text(newDoc.body());
+
+                Node *mutableCppNode = const_cast<Node *>(cppNode);
+                if (property->getters().contains(mutableCppNode)) {
+                    text = textWithFixedBrief(text, Text("Returns "), Text("."));
+                } else if (property->setters().contains(mutableCppNode)) {
+                    Text afterBrief;
+                    if (javaFunc->parameterNames().count() == 1
+                            && !javaFunc->parameterNames().first().isEmpty()) {
+                        afterBrief << " to "
+                                   << Atom(Atom::FormattingLeft, ATOM_FORMATTING_PARAMETER)
+                                   << javaFunc->parameterNames().first()
+                                   << Atom(Atom::FormattingRight, ATOM_FORMATTING_PARAMETER);
+                    }
+                    afterBrief << ".";
+                    text = textWithFixedBrief(text, Text("Sets "), afterBrief);
+                } else if (property->resetters().contains(mutableCppNode)) {
+                    text = textWithFixedBrief(text, Text("Resets "), Text("."));
+                }
+
+                newDoc.setBody(text);
+            } else {
+                QStringList javaParams = javaFunc->parameterNames();
+                QStringList cppParams = cppFunc->parameterNames();
+                newDoc.renameParameters(cppParams, javaParams);
+
+                if (cppNode->access() == Node::Private) {
+                    Text text;
+                    text << Atom::ParaLeft;
+                    if (cppFunc->reimplementedFrom()) {
+                        text << "This function is reimplemented for internal reasons.";
+                    } else {
+                        text << USED_INTERNALLY;
+                    }
+                    text << Atom::ParaRight;
+                    newDoc.setBody(text);
+                }
+            }
+        } else if (cppNode->type() == Node::Variable) {
+            Text text(newDoc.body());
+
+            if (qName == "variablegetter") {
+                text = textWithFixedBrief(text, Text("Returns "), Text("."));
+            } else if (qName == "variablesetter") {
+                Text afterBrief;
+                if (javaFunc->parameterNames().count() == 1
+                        && !javaFunc->parameterNames().first().isEmpty()) {
+                    afterBrief << " to "
+                               << Atom(Atom::FormattingLeft, ATOM_FORMATTING_PARAMETER)
+                               << javaFunc->parameterNames().first()
+                               << Atom(Atom::FormattingRight, ATOM_FORMATTING_PARAMETER);
+                }
+                afterBrief << ".";
+                text = textWithFixedBrief(text, Text("Sets "), afterBrief);
+            }
+
+            newDoc.setBody(text);
+        }
+    } else {    // ### enum value names?
+
+    }
+
+    javaNode->setDoc(newDoc, true);
+}
+
+static void setStatus(Node *javaNode, const Node *cppNode)
+{
+    if (cppNode->status() == Node::Compat) {
+        javaNode->setStatus(Node::Obsolete);
+    } else {
+        javaNode->setStatus(cppNode->status());
+    }
+}
+
+static Text findEnumText(Node *javaEnum, const QString &enumItemName)
+{
+    const Text &body = javaEnum->doc().body();
+    const Atom *atom = body.firstAtom();
+    while (atom) {
+        if (atom->type() == Atom::ListTagLeft && atom->string() == ATOM_LIST_VALUE) {
+            atom = atom->next();
+            if (atom) {
+                // ### paras?
+                if (atom->string() == enumItemName)
+                    return body.subText(Atom::ListItemLeft, Atom::ListItemRight, atom);
+            }
+        } else {
+            atom = atom->next();
+        }
+    }
+    return Text();
+}
+
+JambiApiParser::JambiApiParser(Tree *cppTree)
+    : cppTre(cppTree), javaTre(0), metJapiTag(false)
+{
+}
+
+JambiApiParser::~JambiApiParser()
+{
+}
+
+void JambiApiParser::initializeParser(const Config &config)
+{
+    CodeParser::initializeParser(config);
+}
+
+void JambiApiParser::terminateParser()
+{
+    CodeParser::terminateParser();
+}
+
+QString JambiApiParser::language()
+{
+    return "Java";
+}
+
+QString JambiApiParser::sourceFileNameFilter()
+{
+    return "*.japi";
+}
+
+void JambiApiParser::parseSourceFile(const Location &location, const QString &filePath, Tree *tree)
+{
+    javaTre = tree;
+    metJapiTag = false;
+
+    QXmlSimpleReader reader;
+    reader.setContentHandler(this);
+    reader.setErrorHandler(this);
+
+    QFile file(filePath);
+    if (!file.open(QFile::ReadOnly)) {
+        location.warning(tr("Cannot open JAPI file '%1'").arg(filePath));
+        return;
+    }
+
+    japiLocation = Location(filePath);
+    QXmlInputSource xmlSource(&file);
+    reader.parse(xmlSource);
+}
+
+void JambiApiParser::doneParsingSourceFiles(Tree * /* tree */)
+{
+    /*
+        Also import the overview documents.
+    */
+    foreach (Node *cppNode, cppTre->root()->childNodes()) {
+        if (cppNode->type() == Node::Fake) {
+            FakeNode *cppFake = static_cast<FakeNode *>(cppNode);
+            if (cppFake->subType() == Node::Page) {
+                FakeNode *javaFake = new FakeNode(javaTre->root(),
+                                                  cppFake->name(),
+                                                  cppFake->subType());
+                javaFake->setModuleName("com.trolltech.qt");    // ### hard-coded
+                javaFake->setTitle(cppFake->title());
+                javaFake->setSubTitle(cppFake->subTitle());
+                setStatus(javaFake, cppFake);
+                setPass1JambifiedDoc(javaFake, cppFake);
+            }
+        }
+    }
+
+    /*
+        Fix the docs.
+    */
+    if (javaTre) {
+        javaTre->resolveInheritance();
+        jambifyDocsPass2(javaTre->root());
+        javaTre = 0;
+    }
+}
+
+bool JambiApiParser::startElement(const QString & /* namespaceURI */,
+                                  const QString & /* localName */,
+                                  const QString &qName,
+                                  const QXmlAttributes &attributes)
+{
+    if (!metJapiTag && qName != "japi") {
+        // ### The file is not a JAPI file.
+        return true;
+    }
+    metJapiTag = true;
+
+    EnumNode *javaEnum = 0;
+    EnumNode *cppEnum = 0;
+    InnerNode *javaParent = javaTre->root();
+    InnerNode *cppParent = cppTre->root();
+
+    for (int i = 0; i < classAndEnumStack.count(); ++i) {
+        const ClassOrEnumInfo &info = classAndEnumStack.at(i);
+        if (info.cppNode) {
+            if (info.cppNode->type() == Node::Enum) {
+                Q_ASSERT(info.javaNode->type() == Node::Enum);
+                javaEnum = static_cast<EnumNode *>(info.javaNode);
+                cppEnum = static_cast<EnumNode *>(info.cppNode);
+            } else {
+                Q_ASSERT(info.javaNode->type() == Node::Class
+                         || info.javaNode->type() == Node::Namespace);
+                javaParent = static_cast<InnerNode *>(info.javaNode);
+                cppParent = static_cast<InnerNode *>(info.cppNode);
+            }
+        }
+    }
+
+    if (qName == "class" || qName == "enum") {
+        Node::Type type = (qName == "class") ? Node::Class : Node::Enum;
+
+        QString javaExtends = attributes.value("java-extends");
+        QString javaImplements = attributes.value("javaimplements");
+
+        ClassOrEnumInfo info;
+        info.tag = qName;
+        info.javaName = attributes.value("java");
+        info.cppName = attributes.value("cpp");
+        info.cppNode = cppTre->findNode(info.cppName.split("::"), type, cppParent);
+        if (!info.cppNode && type == Node::Class) {
+            type = Node::Namespace;
+            info.cppNode = cppTre->findNode(info.cppName.split("::"), type, cppParent);
+        }
+
+        if (!info.cppNode) {
+            japiLocation.warning(tr("Cannot find C++ class or enum '%1'").arg(info.cppName));
+        } else {
+            if (qName == "class") {
+                ClassNode *javaClass = new ClassNode(javaParent, info.javaName);
+                javaClass->setModuleName(attributes.value("package"));
+                if (!javaExtends.isEmpty())
+                    javaTre->addBaseClass(javaClass, Node::Public, javaExtends.split('.'),
+                                          javaExtends);
+                if (!javaImplements.isEmpty())
+                    javaTre->addBaseClass(javaClass, Node::Public, javaImplements.split('.'),
+                                          javaExtends);
+
+                info.javaNode = javaClass;
+            } else {
+                info.javaNode = new EnumNode(javaParent, info.javaName);
+            }
+            info.javaNode->setLocation(japiLocation);
+            setStatus(info.javaNode, info.cppNode);
+
+            setPass1JambifiedDoc(info.javaNode, info.cppNode);
+        }
+        classAndEnumStack.push(info);
+    } else if (qName == "method" || qName == "signal") {
+        QString javaSignature = attributes.value("java");
+        if (javaSignature.startsWith("private"))
+            return true;
+
+        QString cppSignature = attributes.value("cpp");
+
+        CppCodeParser cppParser;
+        const FunctionNode *cppNode = cppParser.findFunctionNode(cppSignature, cppTre,
+                                                                 cppParent,
+                                                                 true /* fuzzy */);
+        if (!cppNode) {
+            bool quiet = false;
+
+            /*
+                Default constructors sometimes don't exist in C++.
+            */
+            if (!quiet && javaSignature == "public " + javaParent->name() + "()")
+                quiet = true;
+
+            if (!quiet)
+                japiLocation.warning(tr("Cannot find C++ function '%1' ('%2')")
+                                     .arg(cppSignature).arg(cppParent->name()));
+        }
+
+        FunctionNode *javaNode;
+        if (makeFunctionNode(javaParent, javaSignature, &javaNode)) {
+            javaNode->setLocation(japiLocation);
+            if (qName == "signal")
+                javaNode->setMetaness(FunctionNode::Signal);
+
+            if (cppNode) {
+                setStatus(javaNode, cppNode);
+
+                int overloadNo = cppNode->parameters().count() - javaNode->parameters().count() + 1;
+                if (overloadNo == 1) {
+                    setPass1JambifiedDoc(javaNode, cppNode);
+                } else {
+                    Text text;
+
+                    text << Atom::ParaLeft << "Equivalent to "
+                         << Atom(Atom::Link, javaNode->name() + "()")
+                         << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
+                         << javaNode->name()
+                         << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK)
+                         << "(";
+
+                    for (int i = 0; i < cppNode->parameters().count(); ++i) {
+                        if (i > 0)
+                            text << ", ";
+                        if (i < javaNode->parameters().count()) {
+                            text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_PARAMETER)
+                                 << javaNode->parameters().at(i).name()
+                                 << Atom(Atom::FormattingRight, ATOM_FORMATTING_PARAMETER);
+                        } else {
+                            // ### convert to Java
+                            text << cppNode->parameters().at(i).defaultValue();
+                        }
+                    }
+
+                    text << ").";
+
+                    Doc doc;
+                    doc.setBody(text);
+                    javaNode->setDoc(doc, true);
+                }
+                javaNode->setOverload(overloadNo > 1);
+            }
+        }
+    } else if (qName == "variablesetter" || qName == "variablegetter") {
+        QString javaSignature = attributes.value("java");
+        if (javaSignature.startsWith("private"))
+            return true;
+
+        QString cppVariable = attributes.value("cpp");
+
+        VariableNode *cppNode = static_cast<VariableNode *>(cppParent->findNode(cppVariable,
+                                                                                Node::Variable));
+        FunctionNode *javaNode;
+        if (makeFunctionNode(javaParent, javaSignature, &javaNode)) {
+            javaNode->setLocation(japiLocation);
+
+            if (!cppNode) {
+#if 0
+                japiLocation.warning(tr("Cannot find C++ variable '%1' ('%2')")
+                                     .arg(cppVariable).arg(cppParent->name()));
+#endif
+                javaNode->setDoc(Doc(japiLocation, japiLocation,
+                                     USED_INTERNALLY,
+                                     QSet<QString>()), true);
+            } else {
+                setPass1JambifiedDoc(javaNode, cppNode, qName);
+                setStatus(javaNode, cppNode);
+            }
+        }
+    } else if (qName == "enum-value") {
+        QString javaName = attributes.value("java");
+        QString cppName = attributes.value("cpp");
+        QString value = attributes.value("value");
+
+        if (javaEnum) {
+            EnumItem item(javaName, value, findEnumText(javaEnum, javaName));
+            javaEnum->addItem(item);
+        }
+    }
+
+    return true;
+}
+
+bool JambiApiParser::endElement(const QString & /* namespaceURI */,
+                                const QString & /* localName */,
+                                const QString &qName)
+{
+    if (qName == "class" || qName == "enum")
+        classAndEnumStack.pop();
+    return true;
+}
+
+bool JambiApiParser::fatalError(const QXmlParseException &exception)
+{
+    japiLocation.setLineNo(exception.lineNumber());
+    japiLocation.setColumnNo(exception.columnNumber());
+    japiLocation.warning(tr("Syntax error in JAPI file (%1)").arg(exception.message()));
+    return true;
+}
+
+void JambiApiParser::jambifyDocsPass2(Node *node)
+{
+    const Doc &doc = node->doc();
+    if (!doc.isEmpty()) {
+        if (node->type() == Node::Enum) {
+            Doc newDoc(doc);
+            newDoc.simplifyEnumDoc();
+            node->setDoc(newDoc, true);
+        }
+    }
+
+    if (node->isInnerNode()) {
+        InnerNode *innerNode = static_cast<InnerNode *>(node);
+        foreach (Node *child, innerNode->childNodes())
+            jambifyDocsPass2(child);
+    }
+}
+
+bool JambiApiParser::makeFunctionNode(InnerNode *parent, const QString &synopsis,
+				      FunctionNode **funcPtr)
+{
+    Node::Access access = Node::Public;
+    FunctionNode::Metaness metaness = FunctionNode::Plain;
+    bool final = false;
+    bool statique = false;
+
+    QString mySynopsis = synopsis.simplified();
+    int oldLen;
+    do {
+        oldLen = mySynopsis.length();
+
+        if (mySynopsis.startsWith("public ")) {
+            mySynopsis.remove(0, 7);
+            access = Node::Public;
+        }
+        if (mySynopsis.startsWith("protected ")) {
+            mySynopsis.remove(0, 10);
+            access = Node::Protected;
+        }
+        if (mySynopsis.startsWith("private ")) {
+            mySynopsis.remove(0, 8);
+            access = Node::Private;
+        }
+        if (mySynopsis.startsWith("native ")) {
+            mySynopsis.remove(0, 7);
+            metaness = FunctionNode::Native;
+        }
+        if (mySynopsis.startsWith("final ")) {
+            mySynopsis.remove(0, 6);
+            final = true;
+        }
+        if (mySynopsis.startsWith("static ")) {
+            mySynopsis.remove(0, 7);
+            statique = true;
+        }
+    } while (oldLen != mySynopsis.length());
+
+    // method or constructor
+    QRegExp funcRegExp("(?:(.*) )?([A-Za-z_0-9]+)\\((.*)\\)");
+    if (!funcRegExp.exactMatch(mySynopsis))
+        return false;
+
+    QString retType = funcRegExp.cap(1);
+    QString funcName = funcRegExp.cap(2);
+    QStringList params = funcRegExp.cap(3).split(",");
+
+    FunctionNode *func = new FunctionNode(parent, funcName);
+    func->setReturnType(retType);
+    func->setAccess(access);
+    func->setStatic(statique);
+    func->setConst(final);
+    func->setMetaness(metaness);
+
+    QRegExp paramRegExp(" ?([^ ].*) ([A-Za-z_0-9]+) ?");
+
+    foreach (const QString &param, params) {
+        if (paramRegExp.exactMatch(param)) {
+            func->addParameter(Parameter(paramRegExp.cap(1), "", paramRegExp.cap(2)));
+        } else {
+            // problem
+        }
+    }
+
+    if (funcPtr) {
+        *funcPtr = func;
+    } else if (!parent) {
+        delete func;
+    }
+    return true;
+}
+
+QT_END_NAMESPACE