tools/qdoc3/codemarker.cpp
changeset 0 1918ee327afb
child 3 41300fa6a67c
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 #include <QMetaObject>
       
    43 #include <QDebug>
       
    44 #include "codemarker.h"
       
    45 #include "config.h"
       
    46 #include "node.h"
       
    47 
       
    48 #include <stdio.h>
       
    49 
       
    50 QT_BEGIN_NAMESPACE
       
    51 
       
    52 QString CodeMarker::defaultLang;
       
    53 QList<CodeMarker *> CodeMarker::markers;
       
    54 
       
    55 /*!
       
    56   When a code marker constructs itself, it puts itself into
       
    57   the static list of code markers. All the code markers in
       
    58   the static list get initialized in initialize(), which is
       
    59   not called until after the qdoc configuration file has
       
    60   been read.
       
    61  */
       
    62 CodeMarker::CodeMarker()
       
    63     : slow(false)
       
    64 {
       
    65     markers.prepend(this);
       
    66 }
       
    67 
       
    68 /*!
       
    69   When a code marker destroys itself, it removes itself from
       
    70   the static list of code markers.
       
    71  */
       
    72 CodeMarker::~CodeMarker()
       
    73 {
       
    74     markers.removeAll(this);
       
    75 }
       
    76 
       
    77 /*!
       
    78   The only thing a code market initializes is its \e{slow}
       
    79   flag. The \e{slow} flag indicates whether the operations
       
    80   that slow down qdoc are to be performed or not. It is
       
    81   turned off by default. 
       
    82  */
       
    83 void CodeMarker::initializeMarker(const Config &config)
       
    84 {
       
    85     slow = config.getBool(QLatin1String(CONFIG_SLOW));
       
    86 }
       
    87 
       
    88 /*!
       
    89   Terminating a code marker is trivial.
       
    90  */
       
    91 void CodeMarker::terminateMarker()
       
    92 {
       
    93     // nothing.
       
    94 }
       
    95 
       
    96 /*!
       
    97   All the code markers in the static list are initialized
       
    98   here, after the qdoc configuration file has been loaded.
       
    99  */
       
   100 void CodeMarker::initialize(const Config& config)
       
   101 {
       
   102     defaultLang = config.getString(QLatin1String(CONFIG_LANGUAGE));
       
   103     QList<CodeMarker *>::ConstIterator m = markers.begin();
       
   104     while (m != markers.end()) {
       
   105 	(*m)->initializeMarker(config);
       
   106 	++m;
       
   107     }
       
   108 }
       
   109 
       
   110 /*!
       
   111   All the code markers in the static list are terminated here.
       
   112  */
       
   113 void CodeMarker::terminate()
       
   114 {
       
   115     QList<CodeMarker *>::ConstIterator m = markers.begin();
       
   116     while (m != markers.end()) {
       
   117 	(*m)->terminateMarker();
       
   118 	++m;
       
   119     }
       
   120 }
       
   121 
       
   122 CodeMarker *CodeMarker::markerForCode(const QString& code)
       
   123 {
       
   124     CodeMarker *defaultMarker = markerForLanguage(defaultLang);
       
   125     if (defaultMarker != 0 && defaultMarker->recognizeCode(code))
       
   126 	return defaultMarker;
       
   127 
       
   128     QList<CodeMarker *>::ConstIterator m = markers.begin();
       
   129     while (m != markers.end()) {
       
   130 	if ((*m)->recognizeCode(code))
       
   131 	    return *m;
       
   132 	++m;
       
   133     }
       
   134     return defaultMarker;
       
   135 }
       
   136 
       
   137 CodeMarker *CodeMarker::markerForFileName(const QString& fileName)
       
   138 {
       
   139     CodeMarker *defaultMarker = markerForLanguage(defaultLang);
       
   140     int dot = -1;
       
   141     while ((dot = fileName.lastIndexOf(QLatin1Char('.'), dot)) != -1) {
       
   142 	QString ext = fileName.mid(dot + 1);
       
   143 	if (defaultMarker != 0 && defaultMarker->recognizeExtension(ext))
       
   144 	    return defaultMarker;
       
   145 	QList<CodeMarker *>::ConstIterator m = markers.begin();
       
   146 	while (m != markers.end()) {
       
   147 	    if ((*m)->recognizeExtension(ext))
       
   148 		return *m;
       
   149 	    ++m;
       
   150 	}
       
   151         --dot;
       
   152     }
       
   153     return defaultMarker;
       
   154 }
       
   155 
       
   156 CodeMarker *CodeMarker::markerForLanguage(const QString& lang)
       
   157 {
       
   158     QList<CodeMarker *>::ConstIterator m = markers.begin();
       
   159     while (m != markers.end()) {
       
   160 	if ((*m)->recognizeLanguage(lang))
       
   161 	    return *m;
       
   162 	++m;
       
   163     }
       
   164     return 0;
       
   165 }
       
   166 
       
   167 const Node *CodeMarker::nodeForString(const QString& string)
       
   168 {
       
   169     if (sizeof(const Node *) == sizeof(uint)) {
       
   170         return reinterpret_cast<const Node *>(string.toUInt());
       
   171     }
       
   172     else {
       
   173         return reinterpret_cast<const Node *>(string.toULongLong());
       
   174     }
       
   175 }
       
   176 
       
   177 QString CodeMarker::stringForNode(const Node *node)
       
   178 {
       
   179     if (sizeof(const Node *) == sizeof(ulong)) {
       
   180         return QString::number(reinterpret_cast<ulong>(node));
       
   181     }
       
   182     else {
       
   183         return QString::number(reinterpret_cast<qulonglong>(node));
       
   184     }
       
   185 }
       
   186 
       
   187 static const QString samp  = QLatin1String("&amp;");
       
   188 static const QString slt   = QLatin1String("&lt;");
       
   189 static const QString sgt   = QLatin1String("&gt;");
       
   190 static const QString squot = QLatin1String("&quot;");
       
   191 
       
   192 QString CodeMarker::protect(const QString& str)
       
   193 {
       
   194     int n = str.length();
       
   195     QString marked;
       
   196     marked.reserve(n * 2 + 30);
       
   197     const QChar *data = str.constData();
       
   198     for (int i = 0; i != n; ++i) {
       
   199         switch (data[i].unicode()) {
       
   200             case '&': marked += samp;  break;
       
   201             case '<': marked += slt;   break;
       
   202             case '>': marked += sgt;   break;
       
   203             case '"': marked += squot; break;
       
   204             default : marked += data[i];
       
   205         }
       
   206     }
       
   207     return marked;
       
   208 }
       
   209 
       
   210 QString CodeMarker::typified(const QString &string)
       
   211 {
       
   212     QString result;
       
   213     QString pendingWord;
       
   214 
       
   215     for (int i = 0; i <= string.size(); ++i) {
       
   216         QChar ch;
       
   217         if (i != string.size())
       
   218             ch = string.at(i);
       
   219 
       
   220         QChar lower = ch.toLower();
       
   221         if ((lower >= QLatin1Char('a') && lower <= QLatin1Char('z'))
       
   222             || ch.digitValue() >= 0 || ch == QLatin1Char('_')
       
   223                 || ch == QLatin1Char(':')) {
       
   224             pendingWord += ch;
       
   225         }
       
   226         else {
       
   227             if (!pendingWord.isEmpty()) {
       
   228                 bool isProbablyType = (pendingWord != QLatin1String("const"));
       
   229                 if (isProbablyType)
       
   230                     result += QLatin1String("<@type>");
       
   231                 result += pendingWord;
       
   232                 if (isProbablyType)
       
   233                     result += QLatin1String("</@type>");
       
   234             }
       
   235             pendingWord.clear();
       
   236 
       
   237             switch (ch.unicode()) {
       
   238             case '\0':
       
   239                 break;
       
   240             case '&':
       
   241                 result += QLatin1String("&amp;");
       
   242                 break;
       
   243             case '<':
       
   244                 result += QLatin1String("&lt;");
       
   245                 break;
       
   246             case '>':
       
   247                 result += QLatin1String("&gt;");
       
   248                 break;
       
   249             default:
       
   250                 result += ch;
       
   251             }
       
   252         }
       
   253     }
       
   254     return result;
       
   255 }
       
   256 
       
   257 QString CodeMarker::taggedNode(const Node* node)
       
   258 {
       
   259     QString tag;
       
   260 
       
   261     switch (node->type()) {
       
   262     case Node::Namespace:
       
   263         tag = QLatin1String("@namespace");
       
   264         break;
       
   265     case Node::Class:
       
   266         tag = QLatin1String("@class");
       
   267         break;
       
   268     case Node::Enum:
       
   269         tag = QLatin1String("@enum");
       
   270         break;
       
   271     case Node::Typedef:
       
   272         tag = QLatin1String("@typedef");
       
   273         break;
       
   274     case Node::Function:
       
   275         tag = QLatin1String("@function");
       
   276         break;
       
   277     case Node::Property:
       
   278         tag = QLatin1String("@property");
       
   279         break;
       
   280     default:
       
   281         tag = QLatin1String("@unknown");
       
   282         break;
       
   283     }
       
   284     return QLatin1Char('<') + tag + QLatin1Char('>') + protect(node->name())
       
   285         + QLatin1String("</") + tag + QLatin1Char('>');
       
   286 }
       
   287 
       
   288 #ifdef QDOC_QML
       
   289 QString CodeMarker::taggedQmlNode(const Node* node)
       
   290 {
       
   291     QString tag;
       
   292     switch (node->type()) {
       
   293     case Node::QmlProperty:
       
   294         tag = QLatin1String("@property");
       
   295         break;
       
   296     case Node::QmlSignal:
       
   297         tag = QLatin1String("@signal");
       
   298         break;
       
   299     case Node::QmlMethod:
       
   300         tag = QLatin1String("@method");
       
   301         break;
       
   302     default:
       
   303         tag = QLatin1String("@unknown");
       
   304         break;
       
   305     }
       
   306     return QLatin1Char('<') + tag + QLatin1Char('>') + protect(node->name())
       
   307         + QLatin1String("</") + tag + QLatin1Char('>');
       
   308 }
       
   309 #endif
       
   310 
       
   311 QString CodeMarker::linkTag(const Node *node, const QString& body)
       
   312 {
       
   313     return QLatin1String("<@link node=\"") + stringForNode(node)
       
   314         + QLatin1String("\">") + body + QLatin1String("</@link>");
       
   315 }
       
   316 
       
   317 QString CodeMarker::sortName(const Node *node)
       
   318 {
       
   319     QString nodeName = node->name();
       
   320     int numDigits = 0;
       
   321     for (int i = nodeName.size() - 1; i > 0; --i) {
       
   322         if (nodeName.at(i).digitValue() == -1)
       
   323             break;
       
   324         ++numDigits;
       
   325     }
       
   326 
       
   327     // we want 'qint8' to appear before 'qint16'
       
   328     if (numDigits > 0) {
       
   329         for (int i = 0; i < 4 - numDigits; ++i)
       
   330             nodeName.insert(nodeName.size()-numDigits-1, QLatin1String("0"));
       
   331     }
       
   332 
       
   333     if (node->type() == Node::Function) {
       
   334         const FunctionNode *func = static_cast<const FunctionNode *>(node);
       
   335         QString sortNo;
       
   336         if (func->metaness() == FunctionNode::Ctor) {
       
   337             sortNo = QLatin1String("C");
       
   338         }
       
   339         else if (func->metaness() == FunctionNode::Dtor) {
       
   340             sortNo = QLatin1String("D");
       
   341         }
       
   342         else {
       
   343             if (nodeName.startsWith(QLatin1String("operator"))
       
   344                     && nodeName.length() > 8
       
   345                     && !nodeName[8].isLetterOrNumber())
       
   346                 sortNo = QLatin1String("F");
       
   347             else
       
   348                 sortNo = QLatin1String("E");
       
   349         }
       
   350         return sortNo + nodeName + QLatin1Char(' ')
       
   351              + QString::number(func->overloadNumber(), 36);
       
   352     }
       
   353 
       
   354     if (node->type() == Node::Class)
       
   355         return QLatin1Char('A') + nodeName;
       
   356 
       
   357     if (node->type() == Node::Property || node->type() == Node::Variable)
       
   358         return QLatin1Char('E') + nodeName;
       
   359 
       
   360     return QLatin1Char('B') + nodeName;
       
   361 }
       
   362 
       
   363 void CodeMarker::insert(FastSection &fastSection,
       
   364                         Node *node,
       
   365                         SynopsisStyle style,
       
   366                         Status status)
       
   367 {
       
   368     bool irrelevant = false;
       
   369     bool inheritedMember = false;
       
   370     if (!node->relates()) {
       
   371         if (node->parent() != (const InnerNode*)fastSection.innerNode) {
       
   372             if (node->type() != Node::QmlProperty)
       
   373                 inheritedMember = true;
       
   374         }
       
   375     }
       
   376 
       
   377     if (node->access() == Node::Private) {
       
   378 	irrelevant = true;
       
   379     }
       
   380     else if (node->type() == Node::Function) {
       
   381 	FunctionNode *func = (FunctionNode *) node;
       
   382 	irrelevant = (inheritedMember
       
   383 		      && (func->metaness() == FunctionNode::Ctor ||
       
   384 			  func->metaness() == FunctionNode::Dtor));
       
   385     }
       
   386     else if (node->type() == Node::Class || node->type() == Node::Enum
       
   387 		    || node->type() == Node::Typedef) {
       
   388 	irrelevant = (inheritedMember && style != SeparateList);
       
   389         if (!irrelevant && style == Detailed && node->type() == Node::Typedef) {
       
   390             const TypedefNode* typedeffe = static_cast<const TypedefNode*>(node);
       
   391             if (typedeffe->associatedEnum())
       
   392                 irrelevant = true;
       
   393         }
       
   394     }
       
   395 
       
   396     if (!irrelevant) {
       
   397         if (status == Compat) {
       
   398             irrelevant = (node->status() != Node::Compat);
       
   399 	}
       
   400         else if (status == Obsolete) {
       
   401             irrelevant = (node->status() != Node::Obsolete);
       
   402 	}
       
   403         else {
       
   404             irrelevant = (node->status() == Node::Compat ||
       
   405                           node->status() == Node::Obsolete);
       
   406 	}
       
   407     }
       
   408 
       
   409     if (!irrelevant) {
       
   410 	if (!inheritedMember || style == SeparateList) {
       
   411 	    QString key = sortName(node);
       
   412             if (!fastSection.memberMap.contains(key))
       
   413 		fastSection.memberMap.insert(key, node);
       
   414 	}
       
   415         else {
       
   416 	    if (node->parent()->type() == Node::Class) {
       
   417 		if (fastSection.inherited.isEmpty()
       
   418                     || fastSection.inherited.last().first != node->parent()) {
       
   419 		    QPair<ClassNode *, int> p((ClassNode *)node->parent(), 0);
       
   420 		    fastSection.inherited.append(p);
       
   421 		}
       
   422 		fastSection.inherited.last().second++;
       
   423 	    }
       
   424 	}
       
   425     }
       
   426 }
       
   427 
       
   428 /*!
       
   429   Returns true if \a node represents a reimplemented member function.
       
   430   If it is, then it is inserted in the reimplemented member map in the
       
   431   section \a fs. And, the test is only performed if \a status is \e OK.
       
   432   Otherwise, false is returned.
       
   433  */
       
   434 bool CodeMarker::insertReimpFunc(FastSection& fs, Node* node, Status status)
       
   435 {
       
   436     if (node->access() == Node::Private)
       
   437         return false;
       
   438 
       
   439     const FunctionNode* fn = static_cast<const FunctionNode*>(node);
       
   440     if ((fn->reimplementedFrom() != 0) && (status == Okay)) {
       
   441         bool inherited = (!fn->relates() && (fn->parent() != (const InnerNode*)fs.innerNode));
       
   442         if (!inherited) {
       
   443             QString key = sortName(fn);
       
   444             if (!fs.reimpMemberMap.contains(key)) {
       
   445                 fs.reimpMemberMap.insert(key,node);
       
   446                 return true;
       
   447             }
       
   448         }
       
   449     }
       
   450     return false;
       
   451  }
       
   452 
       
   453 /*!
       
   454   If \a fs is not empty, convert it to a Section and append
       
   455   the new Section to \a sectionList.
       
   456  */
       
   457 void CodeMarker::append(QList<Section>& sectionList, const FastSection& fs)
       
   458 {
       
   459     if (!fs.isEmpty()) {
       
   460 	Section section(fs.name,fs.singularMember,fs.pluralMember);
       
   461 	section.members = fs.memberMap.values();
       
   462         section.reimpMembers = fs.reimpMemberMap.values();
       
   463 	section.inherited = fs.inherited;
       
   464 	sectionList.append(section);
       
   465     }
       
   466 }
       
   467 
       
   468 static QString encode(const QString &string)
       
   469 {
       
   470 #if 0
       
   471     QString result = string;
       
   472 
       
   473     for (int i = string.size() - 1; i >= 0; --i) {
       
   474         uint ch = string.at(i).unicode();
       
   475         if (ch > 0xFF)
       
   476             ch = '?';
       
   477         if ((ch - '0') >= 10 && (ch - 'a') >= 26 && (ch - 'A') >= 26
       
   478             && ch != '/' && ch != '(' && ch != ')' && ch != ',' && ch != '*'
       
   479             && ch != '&' && ch != '_' && ch != '<' && ch != '>' && ch != ':'
       
   480             && ch != '~')
       
   481             result.replace(i, 1, QString("%") + QString("%1").arg(ch, 2, 16));
       
   482     }
       
   483     return result;
       
   484 #else
       
   485     return string;
       
   486 #endif
       
   487 }
       
   488 
       
   489 QStringList CodeMarker::macRefsForNode(const Node *node)
       
   490 {
       
   491     QString result = QLatin1String("cpp/");
       
   492     switch (node->type()) {
       
   493     case Node::Class:
       
   494         {
       
   495             const ClassNode *classe = static_cast<const ClassNode *>(node);
       
   496 #if 0
       
   497             if (!classe->templateStuff().isEmpty()) {
       
   498                  result += QLatin1String("tmplt/");
       
   499             }
       
   500             else
       
   501 #endif
       
   502             {
       
   503                  result += QLatin1String("cl/");
       
   504             }
       
   505             result += macName(classe); // ### Maybe plainName?
       
   506         }
       
   507         break;
       
   508     case Node::Enum:
       
   509         {
       
   510             QStringList stringList;
       
   511             stringList << encode(result + QLatin1String("tag/") +
       
   512                                  macName(node));
       
   513             foreach (const QString &enumName, node->doc().enumItemNames()) {
       
   514                 // ### Write a plainEnumValue() and use it here
       
   515                 stringList << encode(result + QLatin1String("econst/") +
       
   516                                      macName(node->parent(), enumName));
       
   517             }
       
   518             return stringList;
       
   519         }
       
   520     case Node::Typedef:
       
   521         result += QLatin1String("tdef/") + macName(node);
       
   522         break;
       
   523     case Node::Function:
       
   524         {
       
   525             bool isMacro = false;
       
   526             const FunctionNode *func = static_cast<const FunctionNode *>(node);
       
   527 
       
   528             // overloads are too clever for the Xcode documentation browser
       
   529             if (func->isOverload()) 
       
   530                 return QStringList();
       
   531 
       
   532             if (func->metaness() == FunctionNode::MacroWithParams
       
   533                 || func->metaness() == FunctionNode::MacroWithoutParams) {
       
   534                 result += QLatin1String("macro/");
       
   535                 isMacro = true;
       
   536 #if 0
       
   537             }
       
   538             else if (!func->templateStuff().isEmpty()) {
       
   539                 result += QLatin1String("ftmplt/");
       
   540 #endif
       
   541             }
       
   542             else if (func->isStatic()) {
       
   543                 result += QLatin1String("clm/");
       
   544             }
       
   545             else if (!func->parent()->name().isEmpty()) {
       
   546                 result += QLatin1String("instm/");
       
   547             }
       
   548             else {
       
   549                 result += QLatin1String("func/");
       
   550             }
       
   551 
       
   552             result += macName(func);
       
   553             if (result.endsWith(QLatin1String("()")))
       
   554                 result.chop(2);
       
   555 #if 0
       
   556             // this code is too clever for the Xcode documentation
       
   557             // browser and/or pbhelpindexer
       
   558             if (!isMacro) {
       
   559                 result += "/" + QLatin1String(QMetaObject::normalizedSignature(func->returnType().toLatin1().constData())) + "/(";
       
   560                 const QList<Parameter> &params = func->parameters();
       
   561                 for (int i = 0; i < params.count(); ++i) {
       
   562                     QString type = params.at(i).leftType() +
       
   563                         params.at(i).rightType();
       
   564                     type = QLatin1String(QMetaObject::normalizedSignature(type.toLatin1().constData()));
       
   565                     if (i != 0)
       
   566                         result += ",";
       
   567                     result += type;
       
   568                 }
       
   569                 result += ")";
       
   570             }
       
   571 #endif
       
   572         }
       
   573         break;
       
   574     case Node::Variable:
       
   575          result += QLatin1String("data/") + macName(node);
       
   576          break;
       
   577     case Node::Property:
       
   578          {
       
   579              NodeList list = static_cast<const PropertyNode*>(node)->functions();
       
   580              QStringList stringList;
       
   581              foreach (const Node *node, list) {
       
   582                 stringList += macRefsForNode(node);
       
   583              }
       
   584              return stringList;
       
   585          }
       
   586     case Node::Namespace:
       
   587     case Node::Fake:
       
   588     case Node::Target:
       
   589     default:
       
   590         return QStringList();
       
   591     }
       
   592 
       
   593     return QStringList(encode(result));
       
   594 }
       
   595 
       
   596 QString CodeMarker::macName(const Node *node, const QString &name)
       
   597 {
       
   598     QString myName = name;
       
   599     if (myName.isEmpty()) {
       
   600         myName = node->name();
       
   601         node = node->parent();
       
   602     }
       
   603 
       
   604     if (node->name().isEmpty()) {
       
   605         return QLatin1Char('/') + myName;
       
   606     }
       
   607     else {
       
   608         return plainFullName(node) + QLatin1Char('/') + myName;
       
   609     }
       
   610 }
       
   611 
       
   612 #ifdef QDOC_QML
       
   613 /*!
       
   614   Get the list of documentation sections for the children of
       
   615   the specified QmlClassNode.
       
   616  */
       
   617 QList<Section> CodeMarker::qmlSections(const QmlClassNode* , SynopsisStyle )
       
   618 {
       
   619     return QList<Section>();
       
   620 }
       
   621 #endif
       
   622 
       
   623 QT_END_NAMESPACE