00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014 #include "cpptree2uml.h"
00015
00016 #include <qfileinfo.h>
00017 #include <qdir.h>
00018 #include <qregexp.h>
00019 #include <kdebug.h>
00020
00021 #include "ast_utils.h"
00022 #include "urlutil.h"
00023 #include "../import_utils.h"
00024
00025
00026 #include "../../classifier.h"
00027
00028 #include "../../template.h"
00029
00030 CppTree2Uml::CppTree2Uml( const QString& fileName)
00031 : m_anon( 0 ), m_nsCnt( 0 ), m_clsCnt( 0 )
00032 {
00033 m_fileName = URLUtil::canonicalPath(fileName);
00034 }
00035
00036 CppTree2Uml::~CppTree2Uml()
00037 {
00038 }
00039
00040 void CppTree2Uml::parseTranslationUnit( TranslationUnitAST* ast )
00041 {
00042 m_currentScope.clear();
00043 m_currentNamespace[0] = NULL;
00044 m_currentClass[0] = NULL;
00045 m_nsCnt = 0;
00046 m_clsCnt = 0;
00047
00048 m_currentAccess = Uml::Visibility::Public;
00049 m_inSlots = false;
00050 m_inSignals = false;
00051 m_inStorageSpec = false;
00052 m_inTypedef = false;
00053 m_currentDeclarator = 0;
00054 m_anon = 0;
00055
00056 TreeParser::parseTranslationUnit( ast );
00057 }
00058
00059 void CppTree2Uml::parseNamespace( NamespaceAST* ast )
00060 {
00061 if (m_clsCnt > 0) {
00062 kdDebug() << "CppTree2Uml::parseNamespace: error - cannot nest namespace inside class"
00063 << endl;
00064 return;
00065 }
00066
00067 QString nsName;
00068 if( !ast->namespaceName() || ast->namespaceName()->text().isEmpty() ){
00069 QFileInfo fileInfo( m_fileName );
00070 QString shortFileName = fileInfo.baseName();
00071
00072 nsName.sprintf( "(%s_%d)", shortFileName.local8Bit().data(), m_anon++ );
00073 } else {
00074 nsName = ast->namespaceName()->text();
00075 }
00076
00077 #ifdef DEBUG_CPPTREE2UML
00078 kdDebug() << "CppTree2Uml::parseNamespace: " << nsName << endl;
00079 #endif
00080 UMLObject * o = Import_Utils::createUMLObject( Uml::ot_Package, nsName,
00081 m_currentNamespace[m_nsCnt],
00082 ast->comment());
00083 UMLPackage *ns = (UMLPackage *)o;
00084 m_currentScope.push_back( nsName );
00085 if (++m_nsCnt > STACKSIZE) {
00086 kdError() << "CppTree2Uml::parseNamespace: excessive namespace nesting" << endl;
00087 m_nsCnt = STACKSIZE;
00088 }
00089 m_currentNamespace[m_nsCnt] = ns;
00090
00091 TreeParser::parseNamespace( ast );
00092
00093 --m_nsCnt;
00094 m_currentScope.pop_back();
00095 }
00096
00097 void CppTree2Uml::parseTypedef( TypedefAST* ast )
00098 {
00099 #if 0
00100 DeclaratorAST* oldDeclarator = m_currentDeclarator;
00101
00102 if( ast && ast->initDeclaratorList() && ast->initDeclaratorList()->initDeclaratorList().count() > 0 ) {
00103 QPtrList<InitDeclaratorAST> lst( ast->initDeclaratorList()->initDeclaratorList() );
00104 m_currentDeclarator = lst.at( 0 )->declarator();
00105 }
00106
00107 m_inTypedef = true;
00108
00109 TreeParser::parseTypedef( ast );
00110
00111 m_inTypedef = false;
00112 m_currentDeclarator = oldDeclarator;
00113 #else
00114 TypeSpecifierAST* typeSpec = ast->typeSpec();
00115 InitDeclaratorListAST* declarators = ast->initDeclaratorList();
00116
00117 if( typeSpec && declarators ){
00118 QString typeId;
00119
00120 if( typeSpec->name() )
00121 typeId = typeSpec->name()->text();
00122
00123 QPtrList<InitDeclaratorAST> l( declarators->initDeclaratorList() );
00124 QPtrListIterator<InitDeclaratorAST> it( l );
00125
00126 InitDeclaratorAST* initDecl = 0;
00127 while( 0 != (initDecl = it.current()) ){
00128
00129 QString type, id;
00130 if( initDecl->declarator() ){
00131 type = typeOfDeclaration( typeSpec, initDecl->declarator() );
00132
00133 DeclaratorAST* d = initDecl->declarator();
00134 while( d->subDeclarator() ){
00135 d = d->subDeclarator();
00136 }
00137
00138 if( d->declaratorId() )
00139 id = d->declaratorId()->text();
00140 }
00141
00142 kdDebug() << "CppTree2Uml::parseTypedef: name=" << id << ", type=" << type << endl;
00143
00144
00145
00146
00147
00148
00149
00150
00151 bool isDatatype = Import_Utils::isDatatype(typeId, m_currentNamespace[m_nsCnt]);
00152
00153 if (type.contains('*') || isDatatype) {
00154 UMLObject *inner =
00155 Import_Utils::createUMLObject( Uml::ot_Class, typeId,
00156 m_currentNamespace[m_nsCnt] );
00157 UMLObject *typedefObj =
00158 Import_Utils::createUMLObject( Uml::ot_Datatype, id,
00159 m_currentNamespace[m_nsCnt] );
00160 UMLClassifier *dt = static_cast<UMLClassifier*>(typedefObj);
00161 dt->setIsReference();
00162 dt->setOriginType(static_cast<UMLClassifier*>(inner));
00163 } else {
00164 Import_Utils::createUMLObject( Uml::ot_Class, id,
00165 m_currentNamespace[m_nsCnt],
00166 "" ,
00167 "typedef" );
00168 }
00169 ++it;
00170 }
00171
00172 }
00173 #endif
00174 }
00175
00176 void CppTree2Uml::parseTemplateDeclaration( TemplateDeclarationAST* ast )
00177 {
00178 TemplateParameterListAST* parmListAST = ast->templateParameterList();
00179 if (parmListAST == NULL)
00180 return;
00181 QPtrList<TemplateParameterAST> parmList = parmListAST->templateParameterList();
00182 for (QPtrListIterator<TemplateParameterAST> it(parmList); it.current(); ++it) {
00183
00184
00185 TemplateParameterAST* tmplParmNode = it.current();
00186 TypeParameterAST* typeParmNode = tmplParmNode->typeParameter();
00187 if (typeParmNode) {
00188 NameAST* nameNode = typeParmNode->name();
00189 if (nameNode) {
00190 QString typeName = nameNode->unqualifiedName()->text();
00191 Model_Utils::NameAndType nt(typeName, NULL);
00192 m_templateParams.append(nt);
00193 } else {
00194 kdError() << "CppTree2Uml::parseTemplateDeclaration(type):"
00195 << " nameNode is NULL" << endl;
00196 }
00197 }
00198
00199 ParameterDeclarationAST* valueNode = tmplParmNode->typeValueParameter();
00200 if (valueNode) {
00201 TypeSpecifierAST* typeSpec = valueNode->typeSpec();
00202 if (typeSpec == NULL) {
00203 kdError() << "CppTree2Uml::parseTemplateDeclaration(value):"
00204 << " typeSpec is NULL" << endl;
00205 continue;
00206 }
00207 QString typeName = typeSpec->name()->text();
00208 UMLObject *t = Import_Utils::createUMLObject( Uml::ot_UMLObject, typeName,
00209 m_currentNamespace[m_nsCnt] );
00210 DeclaratorAST* declNode = valueNode->declarator();
00211 NameAST* nameNode = declNode->declaratorId();
00212 if (nameNode == NULL) {
00213 kdError() << "CppTree2Uml::parseTemplateDeclaration(value):"
00214 << " nameNode is NULL" << endl;
00215 continue;
00216 }
00217 QString paramName = nameNode->unqualifiedName()->text();
00218 Model_Utils::NameAndType nt(paramName, t);
00219 m_templateParams.append(nt);
00220 }
00221 }
00222
00223 if( ast->declaration() )
00224 TreeParser::parseDeclaration( ast->declaration() );
00225 }
00226
00227 void CppTree2Uml::parseSimpleDeclaration( SimpleDeclarationAST* ast )
00228 {
00229 TypeSpecifierAST* typeSpec = ast->typeSpec();
00230 InitDeclaratorListAST* declarators = ast->initDeclaratorList();
00231
00232 m_comment = ast->comment();
00233
00234 if( typeSpec )
00235 parseTypeSpecifier( typeSpec );
00236
00237 if( declarators ){
00238 QPtrList<InitDeclaratorAST> l = declarators->initDeclaratorList();
00239
00240 QPtrListIterator<InitDeclaratorAST> it( l );
00241 while( it.current() ){
00242 parseDeclaration( ast->functionSpecifier(), ast->storageSpecifier(), typeSpec, it.current() );
00243 ++it;
00244 }
00245 }
00246 }
00247
00248 void CppTree2Uml::parseFunctionDefinition( FunctionDefinitionAST* ast )
00249 {
00250 TypeSpecifierAST* typeSpec = ast->typeSpec();
00251 GroupAST* funSpec = ast->functionSpecifier();
00252 GroupAST* storageSpec = ast->storageSpecifier();
00253
00254 if( !ast->initDeclarator() )
00255 return;
00256
00257 DeclaratorAST* d = ast->initDeclarator()->declarator();
00258
00259 if( !d->declaratorId() )
00260 return;
00261
00262 bool isFriend = false;
00263 bool isVirtual = false;
00264 bool isStatic = false;
00265 bool isInline = false;
00266 bool isConstructor = false;
00267
00268 if( funSpec ){
00269 QPtrList<AST> l = funSpec->nodeList();
00270 QPtrListIterator<AST> it( l );
00271 while( it.current() ){
00272 QString text = it.current()->text();
00273 if( text == "virtual" ) isVirtual = true;
00274 else if( text == "inline" ) isInline = true;
00275 ++it;
00276 }
00277 }
00278
00279 if( storageSpec ){
00280 QPtrList<AST> l = storageSpec->nodeList();
00281 QPtrListIterator<AST> it( l );
00282 while( it.current() ){
00283 QString text = it.current()->text();
00284 if( text == "friend" ) isFriend = true;
00285 else if( text == "static" ) isStatic = true;
00286 ++it;
00287 }
00288 }
00289
00290 QString id = d->declaratorId()->unqualifiedName()->text().stripWhiteSpace();
00291
00292 UMLClassifier *c = m_currentClass[m_clsCnt];
00293 if (c == NULL) {
00294 kdDebug() << "CppTree2Uml::parseFunctionDefinition (" << id
00295 << "): need a surrounding class." << endl;
00296 return;
00297 }
00298
00299 QString returnType = typeOfDeclaration( typeSpec, d );
00300 UMLOperation *m = Import_Utils::makeOperation(c, id);
00301
00302
00303 if (d && returnType.isEmpty() && id.find("~") == -1)
00304 isConstructor = true;
00305
00306 parseFunctionArguments( d, m );
00307 Import_Utils::insertMethod( c, m, m_currentAccess, returnType,
00308 isStatic, false , isFriend, isConstructor, m_comment);
00309 m_comment = "";
00310
00311
00312
00313
00314
00315
00316
00317
00318 }
00319
00320 void CppTree2Uml::parseClassSpecifier( ClassSpecifierAST* ast )
00321 {
00322 Uml::Visibility oldAccess = m_currentAccess;
00323 bool oldInSlots = m_inSlots;
00324 bool oldInSignals = m_inSignals;
00325
00326 QString kind = ast->classKey()->text();
00327 m_currentAccess=Uml::Visibility::fromString(kind);
00328 m_inSlots = false;
00329 m_inSignals = false;
00330
00331 QString className;
00332 if( !ast->name() && m_currentDeclarator && m_currentDeclarator->declaratorId() ) {
00333 className = m_currentDeclarator->declaratorId()->text().stripWhiteSpace();
00334 } else if( !ast->name() ){
00335 QFileInfo fileInfo( m_fileName );
00336 QString shortFileName = fileInfo.baseName();
00337 className.sprintf( "(%s_%d)", shortFileName.local8Bit().data(), m_anon++ );
00338 } else {
00339 className = ast->name()->unqualifiedName()->text().stripWhiteSpace();
00340 }
00341
00342 kdDebug() << "CppTree2Uml::parseClassSpecifier: name=" << className << endl;
00343
00344 if( !scopeOfName( ast->name(), QStringList() ).isEmpty() ){
00345 kdDebug() << "skip private class declarations" << endl;
00346 return;
00347 }
00348
00349 if (className.isEmpty()) {
00350 className = "anon_" + QString::number(m_anon);
00351 m_anon++;
00352 }
00353 UMLObject * o = Import_Utils::createUMLObject( Uml::ot_Class, className,
00354 m_currentNamespace[m_nsCnt],
00355 ast->comment() );
00356 UMLClassifier *klass = static_cast<UMLClassifier*>(o);
00357 flushTemplateParams(klass);
00358 if ( ast->baseClause() )
00359 parseBaseClause( ast->baseClause(), klass );
00360
00361 m_currentScope.push_back( className );
00362 if (++m_clsCnt > STACKSIZE) {
00363 kdError() << "CppTree2Uml::parseNamespace: excessive class nesting" << endl;
00364 m_clsCnt = STACKSIZE;
00365 }
00366 m_currentClass[m_clsCnt] = klass;
00367 if (++m_nsCnt > STACKSIZE) {
00368 kdError() << "CppTree2Uml::parseNamespace: excessive namespace nesting" << endl;
00369 m_nsCnt = STACKSIZE;
00370 }
00371 m_currentNamespace[m_nsCnt] = (UMLPackage*)klass;
00372
00373 TreeParser::parseClassSpecifier( ast );
00374
00375 --m_nsCnt;
00376 --m_clsCnt;
00377
00378 m_currentScope.pop_back();
00379
00380 m_currentAccess = oldAccess;
00381 m_inSlots = oldInSlots;
00382 m_inSignals = oldInSignals;
00383 }
00384
00385 void CppTree2Uml::parseEnumSpecifier( EnumSpecifierAST* ast )
00386 {
00387 NameAST *nameNode = ast->name();
00388 if (nameNode == NULL)
00389 return;
00390 QString typeName = nameNode->unqualifiedName()->text().stripWhiteSpace();
00391 if (typeName.isEmpty())
00392 return;
00393 UMLObject *o = Import_Utils::createUMLObject( Uml::ot_Enum, typeName,
00394 m_currentNamespace[m_nsCnt],
00395 ast->comment() );
00396
00397 QPtrList<EnumeratorAST> l = ast->enumeratorList();
00398 QPtrListIterator<EnumeratorAST> it( l );
00399 while ( it.current() ) {
00400 QString enumLiteral = it.current()->id()->text();
00401 Import_Utils::addEnumLiteral( (UMLEnum*)o, enumLiteral );
00402 ++it;
00403 }
00404 }
00405
00406 void CppTree2Uml::parseElaboratedTypeSpecifier( ElaboratedTypeSpecifierAST* typeSpec )
00407 {
00408
00412 QString text = typeSpec->text();
00413 kdDebug() << "CppTree2Uml::parseElaboratedTypeSpecifier: text is " << text << endl;
00414 text.remove(QRegExp("^class\\s+"));
00415 UMLObject *o = Import_Utils::createUMLObject(Uml::ot_Class, text, m_currentNamespace[m_nsCnt]);
00416 flushTemplateParams( static_cast<UMLClassifier*>(o) );
00417 }
00418
00419 void CppTree2Uml::parseDeclaration( GroupAST* funSpec, GroupAST* storageSpec,
00420 TypeSpecifierAST* typeSpec, InitDeclaratorAST* decl )
00421 {
00422 if( m_inStorageSpec )
00423 return;
00424
00425 DeclaratorAST* d = decl->declarator();
00426
00427 if( !d )
00428 return;
00429
00430 if( !d->subDeclarator() && d->parameterDeclarationClause() )
00431 return parseFunctionDeclaration( funSpec, storageSpec, typeSpec, decl );
00432
00433 DeclaratorAST* t = d;
00434 while( t && t->subDeclarator() )
00435 t = t->subDeclarator();
00436
00437 QString id;
00438 if( t && t->declaratorId() && t->declaratorId()->unqualifiedName() )
00439 id = t->declaratorId()->unqualifiedName()->text();
00440
00441 if( !scopeOfDeclarator(d, QStringList()).isEmpty() ){
00442 kdDebug() << "CppTree2Uml::parseDeclaration (" << id << "): skipping."
00443 << endl;
00444 return;
00445 }
00446
00447 UMLClassifier *c = m_currentClass[m_clsCnt];
00448 if (c == NULL) {
00449 kdDebug() << "CppTree2Uml::parseDeclaration (" << id
00450 << "): need a surrounding class." << endl;
00451 return;
00452 }
00453
00454 QString typeName = typeOfDeclaration( typeSpec, d );
00455 bool isFriend = false;
00456 bool isStatic = false;
00457
00458
00459 if( storageSpec ){
00460 QPtrList<AST> l = storageSpec->nodeList();
00461 QPtrListIterator<AST> it( l );
00462 while( it.current() ){
00463 QString text = it.current()->text();
00464 if( text == "friend" ) isFriend = true;
00465 else if( text == "static" ) isStatic = true;
00466 ++it;
00467 }
00468 }
00469
00470 Import_Utils::insertAttribute( c, m_currentAccess, id, typeName,
00471 m_comment, isStatic);
00472 m_comment = "";
00473 }
00474
00475 void CppTree2Uml::parseAccessDeclaration( AccessDeclarationAST * access )
00476 {
00477 QPtrList<AST> l = access->accessList();
00478
00479 QString accessStr = l.at( 0 )->text();
00480
00481 m_currentAccess=Uml::Visibility::fromString(accessStr);
00482
00483 m_inSlots = l.count() > 1 ? l.at( 1 )->text() == "slots" : false;
00484 m_inSignals = l.count() >= 1 ? l.at( 0 )->text() == "signals" : false;
00485 }
00486
00487 void CppTree2Uml::parseFunctionDeclaration( GroupAST* funSpec, GroupAST* storageSpec,
00488 TypeSpecifierAST * typeSpec, InitDeclaratorAST * decl )
00489 {
00490 bool isFriend = false;
00491 bool isVirtual = false;
00492 bool isStatic = false;
00493 bool isInline = false;
00494 bool isPure = decl->initializer() != 0;
00495 bool isConstructor = false;
00496
00497 if( funSpec ){
00498 QPtrList<AST> l = funSpec->nodeList();
00499 QPtrListIterator<AST> it( l );
00500 while( it.current() ){
00501 QString text = it.current()->text();
00502 if( text == "virtual" ) isVirtual = true;
00503 else if( text == "inline" ) isInline = true;
00504 ++it;
00505 }
00506 }
00507
00508 if( storageSpec ){
00509 QPtrList<AST> l = storageSpec->nodeList();
00510 QPtrListIterator<AST> it( l );
00511 while( it.current() ){
00512 QString text = it.current()->text();
00513 if( text == "friend" ) isFriend = true;
00514 else if( text == "static" ) isStatic = true;
00515 ++it;
00516 }
00517 }
00518
00519 DeclaratorAST* d = decl->declarator();
00520 QString id = d->declaratorId()->unqualifiedName()->text();
00521
00522 UMLClassifier *c = m_currentClass[m_clsCnt];
00523 if (c == NULL) {
00524 kdDebug() << "CppTree2Uml::parseFunctionDeclaration (" << id
00525 << "): need a surrounding class." << endl;
00526 return;
00527 }
00528
00529 QString returnType = typeOfDeclaration( typeSpec, d );
00530 UMLOperation *m = Import_Utils::makeOperation(c, id);
00531
00532
00533 if (d && returnType.isEmpty() && id.find("~") == -1)
00534 isConstructor = true;
00535
00536 parseFunctionArguments( d, m );
00537 Import_Utils::insertMethod( c, m, m_currentAccess, returnType,
00538 isStatic, isPure, isFriend, isConstructor, m_comment);
00539 m_comment = "";
00540 }
00541
00542 void CppTree2Uml::parseFunctionArguments(DeclaratorAST* declarator,
00543 UMLOperation* method)
00544 {
00545 ParameterDeclarationClauseAST* clause = declarator->parameterDeclarationClause();
00546
00547 if( clause && clause->parameterDeclarationList() ){
00548 ParameterDeclarationListAST* params = clause->parameterDeclarationList();
00549 QPtrList<ParameterDeclarationAST> l( params->parameterList() );
00550 QPtrListIterator<ParameterDeclarationAST> it( l );
00551 while( it.current() ){
00552 ParameterDeclarationAST* param = it.current();
00553 ++it;
00554
00555 QString name;
00556 if (param->declarator())
00557 name = declaratorToString(param->declarator(), QString::null, true );
00558
00559 QString tp = typeOfDeclaration( param->typeSpec(), param->declarator() );
00560
00561 if (tp != "void")
00562 Import_Utils::addMethodParameter( method, tp, name );
00563 }
00564 }
00565 }
00566
00567 QString CppTree2Uml::typeOfDeclaration( TypeSpecifierAST* typeSpec, DeclaratorAST* declarator )
00568 {
00569 if( !typeSpec || !declarator )
00570 return QString::null;
00571
00572 QString text;
00573
00574 text += typeSpec->text();
00575
00576 QPtrList<AST> ptrOpList = declarator->ptrOpList();
00577 for( QPtrListIterator<AST> it(ptrOpList); it.current(); ++it ){
00578 text += it.current()->text();
00579 }
00580
00581 return text;
00582 }
00583
00584 void CppTree2Uml::parseBaseClause( BaseClauseAST * baseClause, UMLClassifier* klass )
00585 {
00586 QPtrList<BaseSpecifierAST> l = baseClause->baseSpecifierList();
00587 QPtrListIterator<BaseSpecifierAST> it( l );
00588 while( it.current() ){
00589 BaseSpecifierAST* baseSpecifier = it.current();
00590 ++it;
00591
00592 if (baseSpecifier->name() == NULL) {
00593 kdDebug() << "CppTree2Uml::parseBaseClause: baseSpecifier->name() is NULL"
00594 << endl;
00595 continue;
00596 }
00597
00598 QString baseName = baseSpecifier->name()->text();
00599 Import_Utils::createGeneralization( klass, baseName );
00600 }
00601 }
00602
00603 QStringList CppTree2Uml::scopeOfName( NameAST* id, const QStringList& startScope )
00604 {
00605 QStringList scope = startScope;
00606 if( id && id->classOrNamespaceNameList().count() ){
00607 if( id->isGlobal() )
00608 scope.clear();
00609 QPtrList<ClassOrNamespaceNameAST> l = id->classOrNamespaceNameList();
00610 QPtrListIterator<ClassOrNamespaceNameAST> it( l );
00611 while( it.current() ){
00612 if( it.current()->name() ){
00613 scope << it.current()->name()->text();
00614 }
00615 ++it;
00616 }
00617 }
00618
00619 return scope;
00620 }
00621
00622 QStringList CppTree2Uml::scopeOfDeclarator( DeclaratorAST* d, const QStringList& startScope )
00623 {
00624 return scopeOfName( d->declaratorId(), startScope );
00625 }
00626
00627 void CppTree2Uml::flushTemplateParams(UMLClassifier *klass) {
00628 if (m_templateParams.count()) {
00629 Model_Utils::NameAndType_ListIt it;
00630 for (it = m_templateParams.begin(); it != m_templateParams.end(); ++it) {
00631 const Model_Utils::NameAndType &nt = *it;
00632 kdDebug() << "CppTree2Uml::parseClassSpecifier: adding template param: "
00633 << nt.m_name << endl;
00634 UMLTemplate *tmpl = klass->addTemplate(nt.m_name);
00635 tmpl->setType(nt.m_type);
00636 }
00637 m_templateParams.clear();
00638 }
00639 }
00640