tools/qdoc3/qscodeparser.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   qscodeparser.cpp
       
    44 */
       
    45 
       
    46 #include <qfile.h>
       
    47 #include <qregexp.h>
       
    48 
       
    49 #include "config.h"
       
    50 #include "qscodeparser.h"
       
    51 #include "text.h"
       
    52 #include "tokenizer.h"
       
    53 #include "tree.h"
       
    54 
       
    55 QT_BEGIN_NAMESPACE
       
    56 
       
    57 #define CONFIG_QUICK                    "quick"
       
    58 #define CONFIG_REPLACES                 "replaces"
       
    59 
       
    60 #define COMMAND_BRIEF                   Doc::alias( "brief")
       
    61 #define COMMAND_CODE                    Doc::alias( "code")
       
    62 #define COMMAND_ENDCODE                 Doc::alias( "endcode")
       
    63 #define COMMAND_ENDQUICKCODE            Doc::alias( "endquickcode")
       
    64 #define COMMAND_FILE                    Doc::alias( "file")
       
    65 #define COMMAND_GROUP                   Doc::alias( "group")
       
    66 #define COMMAND_MODULE                  Doc::alias( "module")
       
    67 #define COMMAND_PAGE                    Doc::alias( "page")
       
    68 #define COMMAND_QUICKCLASS              Doc::alias( "quickclass")
       
    69 #define COMMAND_QUICKCODE               Doc::alias( "quickcode")
       
    70 #define COMMAND_QUICKENUM               Doc::alias( "quickenum")
       
    71 #define COMMAND_QUICKFN                 Doc::alias( "quickfn")
       
    72 #define COMMAND_QUICKIFY                Doc::alias( "quickify")
       
    73 #define COMMAND_QUICKPROPERTY           Doc::alias( "quickproperty")
       
    74 #define COMMAND_PROTECTED               Doc::alias( "protected")
       
    75 #define COMMAND_REPLACE                 Doc::alias( "replace")
       
    76 
       
    77 static QString balancedParens = "(?:[^()]+|\\([^()]*\\))*";
       
    78 
       
    79 QsCodeParser::QsCodeParser(Tree *cppTree)
       
    80     : cppTre(cppTree), qsTre(0), replaceRegExp("/(.+)/([^/]*)/")
       
    81 {
       
    82 }
       
    83 
       
    84 QsCodeParser::~QsCodeParser()
       
    85 {
       
    86 }
       
    87 
       
    88 void QsCodeParser::initializeParser(const Config& config)
       
    89 {
       
    90     CppCodeParser::initializeParser(config);
       
    91 
       
    92     nodeTypeMap.insert(COMMAND_QUICKCLASS, Node::Class);
       
    93     nodeTypeMap.insert(COMMAND_QUICKENUM, Node::Enum);
       
    94     nodeTypeMap.insert(COMMAND_QUICKPROPERTY, Node::Property);
       
    95     nodeTypeMap.insert(COMMAND_QUICKFN, Node::Function);
       
    96 
       
    97     QString quickDotReplaces = CONFIG_QUICK + Config::dot + CONFIG_REPLACES;
       
    98     QStringList replaces = config.getStringList(quickDotReplaces);
       
    99     QStringList::ConstIterator r = replaces.begin();
       
   100     while (r != replaces.end()) {
       
   101 	if (replaceRegExp.exactMatch(*r)) {
       
   102 	    QRegExp before(replaceRegExp.cap(1));
       
   103 	    before.setMinimal(true);
       
   104 	    QString after = replaceRegExp.cap(2);
       
   105 
       
   106 	    if (before.isValid()) {
       
   107 		replaceBefores << before;
       
   108 		replaceAfters << after;
       
   109 	    }
       
   110             else {
       
   111 		config.lastLocation().warning(
       
   112 			tr("Invalid regular expression '%1'")
       
   113 			.arg(before.pattern()));
       
   114 	    }
       
   115 	}
       
   116         else {
       
   117 	    config.lastLocation().warning(tr("Bad syntax in '%1'")
       
   118 					   .arg(quickDotReplaces));
       
   119 	}
       
   120 	++r;
       
   121     }
       
   122 }
       
   123 
       
   124 void QsCodeParser::terminateParser()
       
   125 {
       
   126     nodeTypeMap.clear();
       
   127     classesWithNoQuickDoc.clear();
       
   128     replaceBefores.clear();
       
   129     replaceAfters.clear();
       
   130     CppCodeParser::terminateParser();
       
   131 }
       
   132 
       
   133 QString QsCodeParser::language()
       
   134 {
       
   135     return "Qt Script";
       
   136 }
       
   137 
       
   138 QString QsCodeParser::headerFileNameFilter()
       
   139 {
       
   140     return "*";
       
   141 }
       
   142 
       
   143 QString QsCodeParser::sourceFileNameFilter()
       
   144 {
       
   145     return "*.qs *.qsd";
       
   146 }
       
   147 
       
   148 void QsCodeParser::parseHeaderFile(const Location& location,
       
   149                                    const QString& filePath,
       
   150                                    Tree *tree)
       
   151 {
       
   152     qsTre = tree;
       
   153 
       
   154     FILE *in = fopen(QFile::encodeName(filePath), "r");
       
   155     if (in == 0) {
       
   156 	location.error(tr("Cannot open Qt Script class list '%1'")
       
   157 			.arg(filePath));
       
   158 	return;
       
   159     }
       
   160 
       
   161     Location fileLocation(filePath);
       
   162     Tokenizer fileTokenizer(fileLocation, in);
       
   163     int tok = fileTokenizer.getToken();
       
   164     while (tok != Tok_Eoi) {
       
   165 	if (tok == Tok_Ident) {
       
   166 	    ClassNode *quickClass = new ClassNode(qsTre->root(),
       
   167 						   fileTokenizer.lexeme());
       
   168 	    quickClass->setLocation(fileTokenizer.location());
       
   169 	}
       
   170         else {
       
   171 	    fileTokenizer.location().error(tr("Unexpected token '%1' in Qt"
       
   172 					       " Script class list")
       
   173 					    .arg(fileTokenizer.lexeme()));
       
   174 	    break;
       
   175 	}
       
   176 	tok = fileTokenizer.getToken();
       
   177     }
       
   178     fclose(in);
       
   179 }
       
   180 
       
   181 void QsCodeParser::parseSourceFile(const Location& location,
       
   182                                    const QString& filePath,
       
   183                                    Tree *tree)
       
   184 {
       
   185     qsTre = tree;
       
   186     CppCodeParser::parseSourceFile(location, filePath, tree);
       
   187 }
       
   188 
       
   189 void QsCodeParser::doneParsingHeaderFiles(Tree *tree)
       
   190 {
       
   191     NodeList::ConstIterator c = tree->root()->childNodes().begin();
       
   192     while (c != tree->root()->childNodes().end()) {
       
   193 	if ((*c)->type() == Node::Class)
       
   194 	    quickifyClass((ClassNode *) *c);
       
   195 	++c;
       
   196     }
       
   197     cppTre->root()->deleteChildren(); // save memory
       
   198     tree->resolveInheritance();
       
   199     tree->resolveProperties();
       
   200 }
       
   201 
       
   202 void QsCodeParser::doneParsingSourceFiles(Tree *tree)
       
   203 {
       
   204     tree->root()->normalizeOverloads();
       
   205 
       
   206     NodeList::ConstIterator c = tree->root()->childNodes().begin();
       
   207     while (c != tree->root()->childNodes().end()) {
       
   208 	if ((*c)->type() == Node::Class) {
       
   209 	    QMap<QString, Node *>::ConstIterator cwnqd =
       
   210 		    classesWithNoQuickDoc.find((*c)->name());
       
   211 	    if (cwnqd != classesWithNoQuickDoc.end()) {
       
   212 		(*cwnqd)->location().warning(tr("No '\\%1' documentation for"
       
   213 						 " class '%2'")
       
   214 					      .arg(COMMAND_QUICKCLASS)
       
   215 					      .arg(cwnqd.key()));
       
   216 		(*cwnqd)->setDoc(Doc(), true);
       
   217 	    }
       
   218 	}
       
   219 	++c;
       
   220     }
       
   221 
       
   222     // ### check which enum types are used
       
   223 }
       
   224 
       
   225 FunctionNode *QsCodeParser::findFunctionNode(const QString& synopsis,
       
   226 					      Tree *tree)
       
   227 {
       
   228     QStringList parentPath;
       
   229     FunctionNode *clone;
       
   230     FunctionNode *func = 0;
       
   231 
       
   232     if (makeFunctionNode(synopsis, &parentPath, &clone)) {
       
   233 	func = tree->findFunctionNode(parentPath, clone);
       
   234 	delete clone;
       
   235     }
       
   236     return func;
       
   237 }
       
   238 
       
   239 QSet<QString> QsCodeParser::topicCommands()
       
   240 {
       
   241     return QSet<QString>() << COMMAND_FILE << COMMAND_GROUP << COMMAND_MODULE
       
   242 			   << COMMAND_PAGE << COMMAND_QUICKCLASS
       
   243 			   << COMMAND_QUICKENUM << COMMAND_QUICKFN
       
   244 			   << COMMAND_QUICKPROPERTY;
       
   245 }
       
   246 
       
   247 Node *QsCodeParser::processTopicCommand(const Doc& doc,
       
   248                                         const QString& command,
       
   249                                         const QString& arg)
       
   250 {
       
   251     if (command == COMMAND_QUICKFN) {
       
   252 	QStringList parentPath;
       
   253 	FunctionNode *quickFunc = 0;
       
   254 	FunctionNode *clone;
       
   255 
       
   256 	if (makeFunctionNode(arg, &parentPath, &clone)) {
       
   257 	    FunctionNode *kernelFunc = findKernelFunction(parentPath,
       
   258 							   clone->name());
       
   259 	    if (kernelFunc != 0)
       
   260 		kernelFunc->setAccess(Node::Private);
       
   261 
       
   262 	    quickFunc = qsTre->findFunctionNode(parentPath, clone);
       
   263 	    if (quickFunc == 0 && kernelFunc != 0) {
       
   264 		quickFunc = new FunctionNode(kernelFunc->parent(),
       
   265 					      kernelFunc->name());
       
   266 		quickFunc->setLocation(kernelFunc->location());
       
   267 		quickFunc->setReturnType(clone->returnType());
       
   268 		quickFunc->setParameters(clone->parameters());
       
   269 	    }
       
   270 
       
   271 	    if (quickFunc == 0) {
       
   272 		doc.location().warning(tr("Cannot find '%1' specified with '\\%2'")
       
   273 					.arg(arg).arg(command));
       
   274 	    }
       
   275             else {
       
   276 		quickFunc->setAccess(Node::Public);
       
   277 		QStringList qtParams = quickFunc->parameterNames();
       
   278 		quickFunc->borrowParameterNames(clone);
       
   279 		QStringList quickParams = quickFunc->parameterNames();
       
   280 		setQuickDoc(quickFunc, doc, qtParams, quickParams);
       
   281 	    }
       
   282 	    delete clone;
       
   283 	}
       
   284         else {
       
   285 	    doc.location().warning(tr("Cannot find '%1' specified with '\\%2'")
       
   286 				    .arg(arg).arg(command));
       
   287 	}
       
   288 	return 0;
       
   289     }
       
   290     else if (nodeTypeMap.contains(command)) {
       
   291 	QStringList subArgs = arg.split(" ");
       
   292 	QString dataType;
       
   293 
       
   294 	if (subArgs.count() == 3 && subArgs[1] == ":") {
       
   295 	    dataType = subArgs[2];
       
   296 	}
       
   297         else if (subArgs.count() != 1) {
       
   298 	    doc.location().warning(tr("Invalid syntax in '\\%1'")
       
   299 				    .arg(command));
       
   300 	}
       
   301 
       
   302 	QStringList path = subArgs[0].split(".");
       
   303 	Node *quickNode = qsTre->findNode(path, nodeTypeMap[command]);
       
   304 	if (quickNode == 0) {
       
   305 	    doc.location().warning(tr("Cannot find '%1' specified with '\\%2'")
       
   306 				    .arg(arg).arg(command));
       
   307 	}
       
   308         else {
       
   309 	    setQuickDoc(quickNode, doc);
       
   310 	    if (quickNode->type() == Node::Class) {
       
   311 		classesWithNoQuickDoc.remove(quickNode->name());
       
   312 		if (doc.briefText().isEmpty())
       
   313 		    doc.location().warning(tr("Missing '\\%1' for class '%2'")
       
   314 					    .arg(COMMAND_BRIEF)
       
   315 					    .arg(quickNode->name()));
       
   316 	    }
       
   317             else if (quickNode->type() == Node::Property) {
       
   318 		PropertyNode *quickProperty = (PropertyNode *) quickNode;
       
   319 		if (quickProperty->dataType() == "Object") {
       
   320 		    if (dataType.isEmpty()) {
       
   321 			doc.location().warning(tr("Missing data type in '\\%1'"
       
   322 						   " (assuming 'Object')")
       
   323 						.arg(command));
       
   324 		    }
       
   325                     else {
       
   326 			quickProperty->setDataType(dataType);
       
   327 		    }
       
   328 		}
       
   329                 else if (dataType != quickProperty->dataType()) {
       
   330 		    doc.location().warning(tr("Ignored contradictory data type"
       
   331 					       " in '\\%1'")
       
   332 					    .arg(command));
       
   333 		}
       
   334 	    }
       
   335 	}
       
   336 	return 0;
       
   337     }
       
   338     else {
       
   339 	return CppCodeParser::processTopicCommand(doc, command, arg);
       
   340     }
       
   341 }
       
   342 
       
   343 QSet<QString> QsCodeParser::otherMetaCommands()
       
   344 {
       
   345     return commonMetaCommands() << COMMAND_ENDQUICKCODE << COMMAND_QUICKCODE
       
   346 				<< COMMAND_QUICKIFY << COMMAND_REPLACE;
       
   347 }
       
   348 
       
   349 void QsCodeParser::processOtherMetaCommand(const Doc& doc,
       
   350                                            const QString& command,
       
   351                                            const QString& arg,
       
   352                                            Node *node)
       
   353 {
       
   354     if (command == COMMAND_PROTECTED) {
       
   355 	doc.location().warning(tr("Cannot use '\\%1' in %2")
       
   356 				.arg(COMMAND_PROTECTED).arg(language()));
       
   357     }
       
   358     else {
       
   359 	CppCodeParser::processOtherMetaCommand(doc,command,arg,node);
       
   360     }
       
   361 }
       
   362 
       
   363 ClassNode *QsCodeParser::tryClass(const QString& className)
       
   364 {
       
   365     return (ClassNode*) cppTre->findNode(QStringList(className),Node::Class);
       
   366 }
       
   367 
       
   368 FunctionNode *QsCodeParser::findKernelFunction(const QStringList& parentPath,
       
   369                                                const QString& name)
       
   370 {
       
   371     FunctionNode clone(0, name);
       
   372     clone.setReturnType("Object");
       
   373     clone.addParameter(Parameter("..."));
       
   374     return qsTre->findFunctionNode(parentPath, &clone);
       
   375 }
       
   376 
       
   377 void QsCodeParser::extractRegExp(const QRegExp& regExp,
       
   378                                  QString& source,
       
   379                                  const Doc& doc)
       
   380 {
       
   381     QRegExp blankLineRegExp(
       
   382 	    "[ \t]*(?:\n(?:[ \t]*\n)+[ \t]*|[ \n\t]*\\\\code|"
       
   383 	    "\\\\endcode[ \n\t]*)");
       
   384     QStringList paras = source.trimmed().split(blankLineRegExp);
       
   385     paras = paras.filter(regExp);
       
   386     if (paras.count() == 0) {
       
   387 	doc.location().warning(tr("Cannot find regular expression '%1'")
       
   388 				.arg(regExp.pattern()));
       
   389     }
       
   390     else if (paras.count() > 1) {
       
   391 	doc.location().warning(tr("Regular rexpression '%1' matches multiple"
       
   392 				   "times").arg(regExp.pattern()));
       
   393     }
       
   394     else {
       
   395 	source = paras.first() + "\n\n";
       
   396     }
       
   397 }
       
   398 
       
   399 void QsCodeParser::extractTarget(const QString& target,
       
   400                                  QString& source,
       
   401                                  const Doc& doc)
       
   402 {
       
   403     QRegExp targetRegExp(
       
   404 	    "(\\\\target\\s+(\\S+)[^\n]*\n"
       
   405 	    "(?:(?!\\s*\\\\code)[^\n]+\n|\\s*\\\\code.*\\\\endcode\\s*\n)*)"
       
   406 	    "(?:\\s*\n|[^\n]*$)");
       
   407     targetRegExp.setMinimal(true);
       
   408 
       
   409     int pos = 0;
       
   410     while ((pos = source.indexOf(targetRegExp, pos)) != -1) {
       
   411 	if (targetRegExp.cap(2) == target) {
       
   412 	    source = targetRegExp.cap(1) + "\n\n";
       
   413 	    return;
       
   414 	}
       
   415 	pos += targetRegExp.matchedLength();
       
   416     }
       
   417     doc.location().warning(tr("Cannot find target '%1'").arg(target));
       
   418 }
       
   419 
       
   420 void QsCodeParser::renameParameters(QString& source,
       
   421                                     const Doc& /* doc */,
       
   422                                     const QStringList& qtParams,
       
   423                                     const QStringList& quickParams)
       
   424 {
       
   425     QRegExp paramRegExp("(\\\\a\\s*\\{?\\s*)([A-Za-z0-9_]+)");
       
   426 
       
   427     int pos = 0;
       
   428     while ((pos = paramRegExp.indexIn(source, pos)) != -1) {
       
   429 	pos += paramRegExp.cap(1).length();
       
   430 	QString before = paramRegExp.cap(2);
       
   431 	int index = qtParams.indexOf(before);
       
   432 	if (index != -1) {
       
   433 	    QString after = quickParams[index];
       
   434 	    source.replace(pos, before.size(), after);
       
   435 	}
       
   436     }
       
   437 }
       
   438 
       
   439 void QsCodeParser::applyReplacementList(QString& source, const Doc& doc)
       
   440 {
       
   441     QStringList args = doc.metaCommandArgs(COMMAND_REPLACE);
       
   442     QStringList::ConstIterator a = args.begin();
       
   443     while (a != args.end()) {
       
   444 	if (replaceRegExp.exactMatch(*a)) {
       
   445 	    QRegExp before(replaceRegExp.cap(1));
       
   446 	    before.setMinimal(true);
       
   447 	    QString after = replaceRegExp.cap(2);
       
   448 
       
   449 	    if (before.isValid()) {
       
   450 		int oldLen = source.size();
       
   451 		source.replace(before, after);
       
   452 
       
   453 		// this condition is sufficient but not necessary
       
   454 		if (oldLen == source.size() && !source.contains(after))
       
   455 		    doc.location().warning(
       
   456 			    tr("Regular expression '%1' did not match anything")
       
   457 			    .arg(before.pattern()));
       
   458 	    }
       
   459             else {
       
   460 		doc.location().warning(
       
   461 			tr("Invalid regular expression '%1'")
       
   462 			.arg(before.pattern()));
       
   463 	    }
       
   464 	}
       
   465         else {
       
   466 	    doc.location().warning(tr("Bad syntax in '\\%1'")
       
   467 				    .arg(COMMAND_REPLACE));
       
   468 	}
       
   469 	++a;
       
   470     }
       
   471 
       
   472     QRegExp codeRegExp("\\\\" + COMMAND_CODE + "(.*)\\\\" + COMMAND_ENDCODE);
       
   473     codeRegExp.setMinimal(true);
       
   474 
       
   475     QRegExp quickcodeRegExp(
       
   476 	    "\\\\" + COMMAND_QUICKCODE + "(.*)\\\\" + COMMAND_ENDQUICKCODE);
       
   477     quickcodeRegExp.setMinimal(true);
       
   478 
       
   479     int quickcodePos = doc.source().indexOf(quickcodeRegExp);
       
   480     if (quickcodePos != -1) {
       
   481 	int codePos = source.indexOf(codeRegExp);
       
   482 	if (codePos == -1) {
       
   483 	    doc.location().warning(
       
   484 		    tr("Cannot find any '\\%1' snippet corresponding to '\\%2'")
       
   485 		    .arg(COMMAND_CODE).arg(COMMAND_QUICKCODE));
       
   486 	}
       
   487         else {
       
   488 	    source.replace(codeRegExp.pos(1), codeRegExp.cap(1).length(),
       
   489 			    quickcodeRegExp.cap(1));
       
   490 	    codePos = codeRegExp.pos(1) + quickcodeRegExp.cap(1).length();
       
   491 
       
   492 	    if (doc.source().indexOf(quickcodeRegExp, quickcodePos + 1) != -1) {
       
   493 		doc.location().warning(
       
   494 			tr("Cannot use '\\%1' twice in a row")
       
   495 			.arg(COMMAND_QUICKCODE));
       
   496 	    }
       
   497             else if (source.indexOf(codeRegExp, codePos + 1) != -1) {
       
   498 		doc.location().warning(tr("Ambiguous '\\%1'")
       
   499 					.arg(COMMAND_QUICKCODE));
       
   500 	    }
       
   501 	}
       
   502     }
       
   503 }
       
   504 
       
   505 void QsCodeParser::quickifyClass(ClassNode *quickClass)
       
   506 {
       
   507     QString qtClassName = quickClass->name();
       
   508     QString bare = quickClass->name();
       
   509     if (bare != "Qt" && bare != "Object") {
       
   510 	if (bare.startsWith("Q")) {
       
   511 	    bare = bare.mid(1);
       
   512 	}
       
   513         else {
       
   514 	    qtClassName.prepend("Q");
       
   515 	    classesWithNoQ.insert(bare);
       
   516 	}
       
   517     }
       
   518 
       
   519     ClassNode *qtClass = 0;
       
   520     ClassNode *wrapperClass = 0;
       
   521 
       
   522     if ((wrapperClass = tryClass("Quick" + bare)) != 0 ||
       
   523 	 (wrapperClass = tryClass("QS" + bare + "Class")) != 0) {
       
   524 	qtClass = tryClass(qtClassName);
       
   525 	if (qtClass == 0) {
       
   526 	    qtClass = wrapperClass;
       
   527 	    wrapperClass = 0;
       
   528 	}
       
   529     }
       
   530     else if ((wrapperClass = tryClass("Quick" + bare + "Ptr")) != 0) {
       
   531 	QRegExp ptrToQtType("(Q[A-Za-z0-9_]+)\\s*\\*");
       
   532 	FunctionNode *ctor =
       
   533 		wrapperClass->findFunctionNode(wrapperClass->name());
       
   534 	if (ctor != 0 && !ctor->parameters().isEmpty() &&
       
   535 	     ptrToQtType.exactMatch(ctor->parameters().first().leftType()))
       
   536 	    qtClassName = ptrToQtType.cap(1);
       
   537 	qtClass = tryClass(qtClassName);
       
   538     }
       
   539     else {
       
   540 	wrapperClass = tryClass("Q" + bare + "Ptr");
       
   541 	if (wrapperClass == 0)
       
   542 	    wrapperClass = tryClass("Quick" + bare + "Interface");
       
   543 	qtClass = tryClass(qtClassName);
       
   544     }
       
   545 
       
   546     if (qtClass == 0) {
       
   547 	if (wrapperClass == 0) {
       
   548 	    quickClass->location().warning(tr("Cannot find Qt class '%1'")
       
   549 					    .arg(qtClassName));
       
   550 	}
       
   551         else {
       
   552 	    quickClass->location().warning(tr("Cannot find Qt class '%1'"
       
   553 					       " wrapped by '%2'")
       
   554 					    .arg(qtClassName)
       
   555 					    .arg(wrapperClass->name()));
       
   556 	}
       
   557 	return;
       
   558     }
       
   559 
       
   560     QList<RelatedClass>::ConstIterator r = qtClass->baseClasses().begin();
       
   561     while (r != qtClass->baseClasses().end()) {
       
   562 	ClassNode *quickBaseClass = cpp2qs.findClassNode(qsTre,
       
   563                                                          (*r).node->name());
       
   564 	if (quickBaseClass)
       
   565 	    quickClass->addBaseClass((*r).access, quickBaseClass);
       
   566 	++r;
       
   567     }
       
   568     if (quickClass->baseClasses().isEmpty() && quickClass->name() != "Object")
       
   569 	quickClass->addBaseClass(Node::Public,
       
   570                                  cpp2qs.findClassNode(qsTre,"Object"));
       
   571 
       
   572     QSet<QString> funcBlackList;
       
   573     QSet<QString> propertyBlackList;
       
   574 
       
   575     NodeList children;
       
   576     if (wrapperClass != 0) {
       
   577 	children = wrapperClass->childNodes();
       
   578 
       
   579 	funcBlackList.insert(wrapperClass->name());
       
   580 	funcBlackList.insert("~" + wrapperClass->name());
       
   581     }
       
   582     children += qtClass->childNodes();
       
   583 
       
   584     for (int pass = 0; pass < 2; pass++) {
       
   585 	NodeList::ConstIterator c = children.begin();
       
   586 	while (c != children.end()) {
       
   587 	    if ((*c)->access() != Node::Private &&
       
   588 		 (*c)->status() == Node::Commendable) {
       
   589 		if (pass == 0) {
       
   590 		    if ((*c)->type() == Node::Enum) {
       
   591 			EnumNode *enume = (EnumNode *) *c;
       
   592 			quickifyEnum(quickClass, enume);
       
   593 		    }
       
   594                     else if ((*c)->type() == Node::Property) {
       
   595 			if (!propertyBlackList.contains((*c)->name())) {
       
   596 			    PropertyNode *property = (PropertyNode *) *c;
       
   597 			    quickifyProperty(quickClass, qtClass, property);
       
   598 			    if (!property->getters().isEmpty())
       
   599 				funcBlackList.insert(property->getters().first()->name());
       
   600 			    if (!property->setters().isEmpty())
       
   601 				funcBlackList.insert(property->setters().first()->name());
       
   602 			    if (!property->resetters().isEmpty())
       
   603 				funcBlackList.insert(property->resetters().first()->name());
       
   604 			    propertyBlackList.insert(property->name());
       
   605 			}
       
   606 		    }
       
   607 		}
       
   608                 else if ((*c)->type() == Node::Function)  {
       
   609 		    FunctionNode *func = (FunctionNode *) *c;
       
   610 		    quickifyFunction(quickClass, qtClass, func,
       
   611 				      funcBlackList.contains((*c)->name()) &&
       
   612 				      func->parameters().count() < 2);
       
   613 		}
       
   614 	    }
       
   615 	    ++c;
       
   616 	}
       
   617     }
       
   618     setQtDoc(quickClass, qtClass->doc());
       
   619     classesWithNoQuickDoc.insert(quickClass->name(), quickClass);
       
   620 }
       
   621 
       
   622 void QsCodeParser::quickifyEnum(ClassNode *quickClass, EnumNode *enume)
       
   623 {
       
   624     EnumNode *quickEnum = new EnumNode(quickClass, enume->name());
       
   625     quickEnum->setLocation(enume->location());
       
   626 #if 0 // ### not yet
       
   627     quickEnum->setAccess(Node::Protected);
       
   628 #endif
       
   629 
       
   630     QList<EnumItem>::ConstIterator it = enume->items().begin();
       
   631     while (it != enume->items().end()) {
       
   632 	QString name = (*it).name();
       
   633 	QString value = (*it).value();
       
   634 	quickEnum->addItem(EnumItem(name, value));
       
   635 	++it;
       
   636     }
       
   637     setQtDoc(quickEnum, enume->doc());
       
   638 }
       
   639 
       
   640 void QsCodeParser::quickifyFunction(ClassNode *quickClass, ClassNode *qtClass,
       
   641 				     FunctionNode *func, bool onBlackList)
       
   642 {
       
   643     if (func->metaness() == FunctionNode::Dtor)
       
   644 	return;
       
   645 
       
   646     FunctionNode *kernelFunc = findKernelFunction(
       
   647 	    QStringList() << quickClass->name(), func->name());
       
   648 
       
   649     QString quickName = func->name();
       
   650     if (func->metaness() == FunctionNode::Ctor)
       
   651 	quickName = quickClass->name();
       
   652     FunctionNode *quickFunc = new FunctionNode(quickClass, quickName);
       
   653     quickFunc->setLocation(func->location());
       
   654 
       
   655     if (onBlackList) {
       
   656 	quickFunc->setAccess(Node::Protected);
       
   657     }
       
   658     else {
       
   659 	if (kernelFunc != 0 && func->numOverloads() == 1 &&
       
   660 	     (func->parameters().count() == 0 ||
       
   661 	      func->parameters().last().defaultValue().isEmpty())) {
       
   662 	    kernelFunc->setAccess(Node::Private);
       
   663 	}
       
   664         else {
       
   665 	    if (func->metaness() == FunctionNode::Plain)
       
   666 		quickFunc->setAccess(Node::Protected);
       
   667 	}
       
   668     }
       
   669 
       
   670     quickFunc->setReturnType(cpp2qs.convertedDataType(qsTre,
       
   671 						       func->returnType()));
       
   672     if (func->metaness() != FunctionNode::Slot)
       
   673 	quickFunc->setMetaness(func->metaness());
       
   674     quickFunc->setVirtualness(FunctionNode::ImpureVirtual);
       
   675     quickFunc->setOverload(func->isOverload());
       
   676 
       
   677     QList<Parameter>::ConstIterator q = func->parameters().begin();
       
   678     while (q != func->parameters().end()) {
       
   679 	QString dataType = cpp2qs.convertedDataType(qsTre, (*q).leftType(),
       
   680 						     (*q).rightType());
       
   681 	if (dataType.isEmpty()) {
       
   682 	    dataType = "UNKNOWN";
       
   683 	    quickFunc->setAccess(Node::Private);
       
   684 	}
       
   685 	Parameter param(dataType, "", (*q).name(),
       
   686 			 (*q).defaultValue().isEmpty() ? "" : "undefined");
       
   687 	quickFunc->addParameter(param);
       
   688 	++q;
       
   689     }
       
   690 
       
   691     if (func->doc().isEmpty()) {
       
   692 	if (func->parent() != (InnerNode *) qtClass) {
       
   693 	    func = qtClass->findFunctionNode(func);
       
   694 	    if (func != 0)
       
   695 		setQtDoc(quickFunc, func->doc());
       
   696 	}
       
   697     }
       
   698     else {
       
   699 	setQtDoc(quickFunc, func->doc());
       
   700     }
       
   701 }
       
   702 
       
   703 void QsCodeParser::quickifyProperty(ClassNode *quickClass,
       
   704                                     ClassNode * /* qtClass */,
       
   705 				    PropertyNode *property)
       
   706 {
       
   707     PropertyNode *quickProperty = new PropertyNode(quickClass,
       
   708                                                    property->name());
       
   709     quickProperty->setLocation(property->location());
       
   710     quickProperty->setDataType(cpp2qs.convertedDataType(qsTre,
       
   711                                                         property->dataType()));
       
   712 #if 0
       
   713     quickProperty->setGetter(property->getter());
       
   714     quickProperty->setSetter(property->setter());
       
   715     quickProperty->setResetter(property->resetter());
       
   716 #endif
       
   717     quickProperty->setStored(property->isStored());
       
   718     quickProperty->setDesignable(property->isDesignable());
       
   719 
       
   720     setQtDoc(quickProperty, property->doc());
       
   721 }
       
   722 
       
   723 QString QsCodeParser::quickifiedDoc(const QString& source)
       
   724 {
       
   725     QString result;
       
   726     int i = 0;
       
   727 
       
   728     while (i < (int) source.length()) {
       
   729 	if (leftWordBoundary(source, i)) {
       
   730 	    if (source[i] == 'Q') {
       
   731 		if (source[i + 1] == 'C' && source.mid(i, 8) == "QCString") {
       
   732 		    i += 2;
       
   733 		}
       
   734                 else {
       
   735 		    int end = i + 1;
       
   736 		    while (isWord(source[end]))
       
   737 			++end;
       
   738 		    if (!classesWithNoQ.contains(
       
   739 				  source.mid(i + 1, end - (i + 1))))
       
   740 			result += "Q";
       
   741 		    i++;
       
   742 		}
       
   743 	    }
       
   744             else if (source[i] == 'T' && source.mid(i, 4) == "TRUE" &&
       
   745 			rightWordBoundary(source, i + 4)) {
       
   746 		result += "\\c{true}";
       
   747 		i += 4;
       
   748 	    }
       
   749             else if (source[i] == 'F' && source.mid(i, 5) == "FALSE" &&
       
   750 			rightWordBoundary(source, i + 5)) {
       
   751 		result += "\\c{false}";
       
   752 		i += 5;
       
   753 	    }
       
   754             else if (source[i] == 'c' && source.mid(i, 6) == "const ") {
       
   755 		i += 6;
       
   756 	    }
       
   757             else {
       
   758 		result += source[i++];
       
   759 	    }
       
   760 	}
       
   761         else if ((source[i] == ':' && source[i + 1] == ':') ||
       
   762 		    (source[i] == '-' && source[i + 1] == '>')) {
       
   763 	    result += '.';
       
   764 	    i += 2;
       
   765 	}
       
   766         else if (source[i] == '\\') {
       
   767 	    // ### make independent of the command name
       
   768 	    if (source.mid(i, 5) == "\\code") {
       
   769 		do {
       
   770 		    result += source[i++];
       
   771 		} while (source[i - 1] != '\n');
       
   772 
       
   773 		int begin = i;
       
   774 		int end = source.indexOf("\\endcode", i);
       
   775 		if (end != -1) {
       
   776 		    QString code = source.mid(begin, end - begin);
       
   777 		    result += cpp2qs.convertedCode(qsTre, code,
       
   778 						    classesWithNoQ);
       
   779 		    i = end;
       
   780 		}
       
   781 	    }
       
   782             else {
       
   783 		result += source[i++];
       
   784 	    }
       
   785 	}
       
   786         else {
       
   787 	    result += source[i++];
       
   788 	}
       
   789     }
       
   790 
       
   791     QList<QRegExp>::ConstIterator b = replaceBefores.begin();
       
   792     QStringList::ConstIterator a = replaceAfters.begin();
       
   793     while (a != replaceAfters.end()) {
       
   794 	result.replace(*b, *a);
       
   795 	++b;
       
   796 	++a;
       
   797     }
       
   798     return result;
       
   799 }
       
   800 
       
   801 void QsCodeParser::setQtDoc(Node *quickNode, const Doc& doc)
       
   802 {
       
   803     if (!doc.isEmpty()) {
       
   804 	Doc quickDoc(doc.location(), doc.location(),
       
   805                      quickifiedDoc(doc.source()),
       
   806 		     CppCodeParser::topicCommands() +
       
   807                      CppCodeParser::otherMetaCommands());
       
   808 	quickNode->setDoc(quickDoc, true);
       
   809     }
       
   810 }
       
   811 
       
   812 void QsCodeParser::setQuickDoc(Node *quickNode,
       
   813                                const Doc& doc,
       
   814                                const QStringList& qtParams,
       
   815                                const QStringList& quickParams)
       
   816 {
       
   817     QRegExp quickifyCommand("\\\\" + COMMAND_QUICKIFY + "([^\n]*)(?:\n|$)");
       
   818 
       
   819     if (quickNode->type() == Node::Function) {
       
   820 	FunctionNode *quickFunc = (FunctionNode *) quickNode;
       
   821 	quickFunc->setOverload(false);
       
   822     }
       
   823 
       
   824     if (doc.metaCommandsUsed().contains(COMMAND_QUICKIFY)) {
       
   825 	QString source = doc.source();
       
   826 	int pos = source.indexOf(quickifyCommand);
       
   827 	if (pos != -1) {
       
   828 	    QString quickifiedSource = quickNode->doc().source();
       
   829 	    if (!qtParams.isEmpty() && qtParams != quickParams)
       
   830 		renameParameters(quickifiedSource, doc, qtParams,
       
   831 				  quickParams);
       
   832 	    applyReplacementList(quickifiedSource, doc);
       
   833 
       
   834 	    do {
       
   835 		QString extract = quickifiedSource;
       
   836 		QString arg = quickifyCommand.cap(1).simplified();
       
   837 		if (!arg.isEmpty()) {
       
   838 		    if (arg.startsWith("/") && arg.endsWith("/") &&
       
   839 			 arg.length() > 2) {
       
   840 			QString pattern = arg.mid(1, arg.length() - 2);
       
   841 			extractRegExp(QRegExp(pattern), extract, doc);
       
   842 		    }
       
   843                     else {
       
   844 			extractTarget(arg, extract, doc);
       
   845 		    }
       
   846 		}
       
   847 		source.replace(pos, quickifyCommand.matchedLength(), extract);
       
   848 		pos += extract.length();
       
   849 	    } while ((pos = source.indexOf(quickifyCommand, pos)) != -1);
       
   850 
       
   851 	    QRegExp quickcodeRegExp(
       
   852 		    "\\\\" + COMMAND_QUICKCODE + "(.*)\\\\" +
       
   853 		    COMMAND_ENDQUICKCODE);
       
   854 	    quickcodeRegExp.setMinimal(true);
       
   855 	    source.replace(quickcodeRegExp, "");
       
   856 	}
       
   857 
       
   858 	Doc quickDoc(doc.location(),
       
   859                      doc.location(),
       
   860                      source,
       
   861                      (CppCodeParser::topicCommands() + topicCommands() +
       
   862                       CppCodeParser::otherMetaCommands()) << COMMAND_REPLACE);
       
   863 	quickNode->setDoc(quickDoc, true);
       
   864 	processOtherMetaCommands(quickDoc, quickNode);
       
   865     }
       
   866     else {
       
   867 	quickNode->setDoc(doc, true);
       
   868 	processOtherMetaCommands(doc, quickNode);
       
   869     }
       
   870 }
       
   871 
       
   872 bool QsCodeParser::makeFunctionNode(const QString& synopsis,
       
   873 				     QStringList *parentPathPtr,
       
   874 				     FunctionNode **funcPtr)
       
   875 {
       
   876     QRegExp funcRegExp(
       
   877 	    "\\s*([A-Za-z0-9_]+)\\.([A-Za-z0-9_]+)\\s*\\((" +
       
   878             balancedParens +
       
   879 	    ")\\)(?:\\s*:\\s*([A-Za-z0-9_]+))?\\s*");
       
   880     QRegExp paramRegExp(
       
   881 	    "\\s*(\\[)?\\s*(?:([A-Za-z0-9_]+)\\s*:\\s*)?"
       
   882 	    "([A-Za-z0-9_]+|\\.\\.\\.)\\s*(\\[)?[\\s\\]]*");
       
   883 
       
   884     if (!funcRegExp.exactMatch(synopsis))
       
   885 	return false;
       
   886 
       
   887     ClassNode *classe = (ClassNode*)
       
   888         qsTre->findNode(QStringList(funcRegExp.cap(1)),Node::Class);
       
   889     if (classe == 0)
       
   890 	return false;
       
   891 
       
   892     FunctionNode *clone = new FunctionNode(0, funcRegExp.cap(2));
       
   893     bool optional = false;
       
   894 
       
   895     QString paramStr = funcRegExp.cap(3);
       
   896     QStringList params = paramStr.split(",");
       
   897     QStringList::ConstIterator p = params.begin();
       
   898     while (p != params.end()) {
       
   899 	if (paramRegExp.exactMatch(*p)) {
       
   900 	    if (!paramRegExp.cap(1).isEmpty())
       
   901 		optional = true;
       
   902 	    clone->addParameter(Parameter(paramRegExp.cap(3),
       
   903                                           "",
       
   904                                           paramRegExp.cap(2),
       
   905                                           optional ? "undefined" : ""));
       
   906 	    if (!paramRegExp.cap(4).isEmpty())
       
   907 		optional = true;
       
   908 	}
       
   909         else {
       
   910 	    delete clone;
       
   911 	    return false;
       
   912 	}
       
   913 	++p;
       
   914     }
       
   915     QString returnType = funcRegExp.cap(4);
       
   916     if (!returnType.isEmpty())
       
   917 	clone->setReturnType(returnType);
       
   918     if (parentPathPtr != 0)
       
   919 	*parentPathPtr = QStringList() << classe->name();
       
   920     if (funcPtr != 0) {
       
   921 	*funcPtr = clone;
       
   922     }
       
   923     else {
       
   924 	delete clone;
       
   925     }
       
   926     return true;
       
   927 }
       
   928 
       
   929 bool QsCodeParser::isWord(QChar ch)
       
   930 {
       
   931     return ch.isLetterOrNumber() || ch == QChar('_');
       
   932 }
       
   933 
       
   934 bool QsCodeParser::leftWordBoundary(const QString& str, int pos)
       
   935 {
       
   936     return !isWord(str[pos - 1]) && isWord(str[pos]);
       
   937 }
       
   938 
       
   939 bool QsCodeParser::rightWordBoundary(const QString& str, int pos)
       
   940 {
       
   941     return isWord(str[pos - 1]) && !isWord(str[pos]);
       
   942 }
       
   943 
       
   944 QT_END_NAMESPACE