You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdesdk/umbrello/umbrello/codeimport/kdevcppparser/cpptree2uml.cpp

641 lines
22 KiB

/***************************************************************************
* Based on tdevelop-3.0 languages/cpp/store_walker.cpp by Roberto Raggi *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* copyright (C) 2004-2007 *
* Umbrello UML Modeller Authors <uml-devel@uml.sf.net> *
***************************************************************************/
// own header
#include "cpptree2uml.h"
// qt/kde includes
#include <tqfileinfo.h>
#include <tqdir.h>
#include <tqregexp.h>
#include <kdebug.h>
// app includes
#include "ast_utils.h"
#include "urlutil.h"
#include "../import_utils.h"
// FIXME: The sole reason for the next 2 includes is parseTypedef().
// Make capsule methods in ClassImport, and remove these includes.
#include "../../classifier.h"
// FIXME The next include is motivated by template params
#include "../../template.h"
CppTree2Uml::CppTree2Uml( const TQString& fileName)
: m_anon( 0 ), m_nsCnt( 0 ), m_clsCnt( 0 )
{
m_fileName = URLUtil::canonicalPath(fileName);
}
CppTree2Uml::~CppTree2Uml()
{
}
void CppTree2Uml::parseTranslationUnit( TranslationUnitAST* ast )
{
m_currentScope.clear();
m_currentNamespace[0] = NULL; // index 0 is reserved (always NULL)
m_currentClass[0] = NULL; // index 0 is reserved (always NULL)
m_nsCnt = 0;
m_clsCnt = 0;
m_currentAccess = Uml::Visibility::Public;
m_inSlots = false;
m_inSignals = false;
m_inStorageSpec = false;
m_inTypedef = false;
m_currentDeclarator = 0;
m_anon = 0;
TreeParser::parseTranslationUnit( ast );
}
void CppTree2Uml::parseNamespace( NamespaceAST* ast )
{
if (m_clsCnt > 0) {
kDebug() << "CppTree2Uml::parseNamespace: error - cannot nest namespace inside class"
<< endl;
return;
}
TQString nsName;
if( !ast->namespaceName() || ast->namespaceName()->text().isEmpty() ){
TQFileInfo fileInfo( m_fileName );
TQString shortFileName = fileInfo.baseName();
nsName.sprintf( "(%s_%d)", shortFileName.local8Bit().data(), m_anon++ );
} else {
nsName = ast->namespaceName()->text();
}
#ifdef DEBUG_CPPTREE2UML
kDebug() << "CppTree2Uml::parseNamespace: " << nsName << endl;
#endif
UMLObject * o = Import_Utils::createUMLObject( Uml::ot_Package, nsName,
m_currentNamespace[m_nsCnt],
ast->comment());
UMLPackage *ns = (UMLPackage *)o;
m_currentScope.push_back( nsName );
if (++m_nsCnt > STACKSIZE) {
kdError() << "CppTree2Uml::parseNamespace: excessive namespace nesting" << endl;
m_nsCnt = STACKSIZE;
}
m_currentNamespace[m_nsCnt] = ns;
TreeParser::parseNamespace( ast );
--m_nsCnt;
m_currentScope.pop_back();
}
void CppTree2Uml::parseTypedef( TypedefAST* ast )
{
#if 0
DeclaratorAST* oldDeclarator = m_currentDeclarator;
if( ast && ast->initDeclaratorList() && ast->initDeclaratorList()->initDeclaratorList().count() > 0 ) {
TQPtrList<InitDeclaratorAST> lst( ast->initDeclaratorList()->initDeclaratorList() );
m_currentDeclarator = lst.at( 0 )->declarator();
}
m_inTypedef = true;
TreeParser::parseTypedef( ast );
m_inTypedef = false;
m_currentDeclarator = oldDeclarator;
#else
TypeSpecifierAST* typeSpec = ast->typeSpec();
InitDeclaratorListAST* declarators = ast->initDeclaratorList();
if( typeSpec && declarators ){
TQString typeId;
if( typeSpec->name() )
typeId = typeSpec->name()->text();
TQPtrList<InitDeclaratorAST> l( declarators->initDeclaratorList() );
TQPtrListIterator<InitDeclaratorAST> it( l );
InitDeclaratorAST* initDecl = 0;
while( 0 != (initDecl = it.current()) ){
TQString type, id;
if( initDecl->declarator() ){
type = typeOfDeclaration( typeSpec, initDecl->declarator() );
DeclaratorAST* d = initDecl->declarator();
while( d->subDeclarator() ){
d = d->subDeclarator();
}
if( d->declaratorId() )
id = d->declaratorId()->text();
}
//#ifdef DEBUG_CPPTREE2UML
kDebug() << "CppTree2Uml::parseTypedef: name=" << id << ", type=" << type << endl;
//#endif
/* @todo Trace typedefs back to their root type for deciding
whether to build a Datatype (for pointers.) */
/* check out if the ID type is a Datatype
ex: typedef unsigned int uint;
where unsigned int is a known datatype
I'm not sure if setIsReference() should be run
*/
bool isDatatype = Import_Utils::isDatatype(typeId, m_currentNamespace[m_nsCnt]);
if (type.contains('*') || isDatatype) {
UMLObject *inner =
Import_Utils::createUMLObject( Uml::ot_Class, typeId,
m_currentNamespace[m_nsCnt] );
UMLObject *typedefObj =
Import_Utils::createUMLObject( Uml::ot_Datatype, id,
m_currentNamespace[m_nsCnt] );
UMLClassifier *dt = static_cast<UMLClassifier*>(typedefObj);
dt->setIsReference();
dt->setOriginType(static_cast<UMLClassifier*>(inner));
} else {
Import_Utils::createUMLObject( Uml::ot_Class, id,
m_currentNamespace[m_nsCnt],
"" /* doc */,
"typedef" /* stereotype */);
}
++it;
}
}
#endif
}
void CppTree2Uml::parseTemplateDeclaration( TemplateDeclarationAST* ast )
{
TemplateParameterListAST* parmListAST = ast->templateParameterList();
if (parmListAST == NULL)
return;
TQPtrList<TemplateParameterAST> parmList = parmListAST->templateParameterList();
for (TQPtrListIterator<TemplateParameterAST> it(parmList); it.current(); ++it) {
// The template is either a typeParameter or a typeValueParameter.
TemplateParameterAST* tmplParmNode = it.current();
TypeParameterAST* typeParmNode = tmplParmNode->typeParameter();
if (typeParmNode) {
NameAST* nameNode = typeParmNode->name();
if (nameNode) {
TQString typeName = nameNode->unqualifiedName()->text();
Model_Utils::NameAndType nt(typeName, NULL);
m_templateParams.append(nt);
} else {
kdError() << "CppTree2Uml::parseTemplateDeclaration(type):"
<< " nameNode is NULL" << endl;
}
}
ParameterDeclarationAST* valueNode = tmplParmNode->typeValueParameter();
if (valueNode) {
TypeSpecifierAST* typeSpec = valueNode->typeSpec();
if (typeSpec == NULL) {
kdError() << "CppTree2Uml::parseTemplateDeclaration(value):"
<< " typeSpec is NULL" << endl;
continue;
}
TQString typeName = typeSpec->name()->text();
UMLObject *t = Import_Utils::createUMLObject( Uml::ot_UMLObject, typeName,
m_currentNamespace[m_nsCnt] );
DeclaratorAST* declNode = valueNode->declarator();
NameAST* nameNode = declNode->declaratorId();
if (nameNode == NULL) {
kdError() << "CppTree2Uml::parseTemplateDeclaration(value):"
<< " nameNode is NULL" << endl;
continue;
}
TQString paramName = nameNode->unqualifiedName()->text();
Model_Utils::NameAndType nt(paramName, t);
m_templateParams.append(nt);
}
}
if( ast->declaration() )
TreeParser::parseDeclaration( ast->declaration() );
}
void CppTree2Uml::parseSimpleDeclaration( SimpleDeclarationAST* ast )
{
TypeSpecifierAST* typeSpec = ast->typeSpec();
InitDeclaratorListAST* declarators = ast->initDeclaratorList();
m_comment = ast->comment();
if( typeSpec )
parseTypeSpecifier( typeSpec );
if( declarators ){
TQPtrList<InitDeclaratorAST> l = declarators->initDeclaratorList();
TQPtrListIterator<InitDeclaratorAST> it( l );
while( it.current() ){
parseDeclaration( ast->functionSpecifier(), ast->storageSpecifier(), typeSpec, it.current() );
++it;
}
}
}
void CppTree2Uml::parseFunctionDefinition( FunctionDefinitionAST* ast )
{
TypeSpecifierAST* typeSpec = ast->typeSpec();
GroupAST* funSpec = ast->functionSpecifier();
GroupAST* storageSpec = ast->storageSpecifier();
if( !ast->initDeclarator() )
return;
DeclaratorAST* d = ast->initDeclarator()->declarator();
if( !d->declaratorId() )
return;
bool isFriend = false;
bool isVirtual = false;
bool isStatic = false;
bool isInline = false;
bool isConstructor = false;
if( funSpec ){
TQPtrList<AST> l = funSpec->nodeList();
TQPtrListIterator<AST> it( l );
while( it.current() ){
TQString text = it.current()->text();
if( text == "virtual" ) isVirtual = true;
else if( text == "inline" ) isInline = true;
++it;
}
}
if( storageSpec ){
TQPtrList<AST> l = storageSpec->nodeList();
TQPtrListIterator<AST> it( l );
while( it.current() ){
TQString text = it.current()->text();
if( text == "friend" ) isFriend = true;
else if( text == "static" ) isStatic = true;
++it;
}
}
TQString id = d->declaratorId()->unqualifiedName()->text().stripWhiteSpace();
UMLClassifier *c = m_currentClass[m_clsCnt];
if (c == NULL) {
kDebug() << "CppTree2Uml::parseFunctionDefinition (" << id
<< "): need a surrounding class." << endl;
return;
}
TQString returnType = typeOfDeclaration( typeSpec, d );
UMLOperation *m = Import_Utils::makeOperation(c, id);
// if a class has no return type, it could be a constructor or
// a destructor
if (d && returnType.isEmpty() && id.find("~") == -1)
isConstructor = true;
parseFunctionArguments( d, m );
Import_Utils::insertMethod( c, m, m_currentAccess, returnType,
isStatic, false /*isAbstract*/, isFriend, isConstructor, m_comment);
m_comment = "";
/* For reference, Kdevelop does some more:
method->setFileName( m_fileName );
if( m_inSignals )
method->setSignal( true );
if( m_inSlots )
method->setSlot( true );
*/
}
void CppTree2Uml::parseClassSpecifier( ClassSpecifierAST* ast )
{
Uml::Visibility oldAccess = m_currentAccess;
bool oldInSlots = m_inSlots;
bool oldInSignals = m_inSignals;
TQString kind = ast->classKey()->text();
m_currentAccess=Uml::Visibility::fromString(kind);
m_inSlots = false;
m_inSignals = false;
TQString className;
if( !ast->name() && m_currentDeclarator && m_currentDeclarator->declaratorId() ) {
className = m_currentDeclarator->declaratorId()->text().stripWhiteSpace();
} else if( !ast->name() ){
TQFileInfo fileInfo( m_fileName );
TQString shortFileName = fileInfo.baseName();
className.sprintf( "(%s_%d)", shortFileName.local8Bit().data(), m_anon++ );
} else {
className = ast->name()->unqualifiedName()->text().stripWhiteSpace();
}
//#ifdef DEBUG_CPPTREE2UML
kDebug() << "CppTree2Uml::parseClassSpecifier: name=" << className << endl;
//#endif
if( !scopeOfName( ast->name(), TQStringList() ).isEmpty() ){
kDebug() << "skip private class declarations" << endl;
return;
}
if (className.isEmpty()) {
className = "anon_" + TQString::number(m_anon);
m_anon++;
}
UMLObject * o = Import_Utils::createUMLObject( Uml::ot_Class, className,
m_currentNamespace[m_nsCnt],
ast->comment() );
UMLClassifier *klass = static_cast<UMLClassifier*>(o);
flushTemplateParams(klass);
if ( ast->baseClause() )
parseBaseClause( ast->baseClause(), klass );
m_currentScope.push_back( className );
if (++m_clsCnt > STACKSIZE) {
kdError() << "CppTree2Uml::parseNamespace: excessive class nesting" << endl;
m_clsCnt = STACKSIZE;
}
m_currentClass[m_clsCnt] = klass;
if (++m_nsCnt > STACKSIZE) {
kdError() << "CppTree2Uml::parseNamespace: excessive namespace nesting" << endl;
m_nsCnt = STACKSIZE;
}
m_currentNamespace[m_nsCnt] = (UMLPackage*)klass;
TreeParser::parseClassSpecifier( ast );
--m_nsCnt;
--m_clsCnt;
m_currentScope.pop_back();
m_currentAccess = oldAccess;
m_inSlots = oldInSlots;
m_inSignals = oldInSignals;
}
void CppTree2Uml::parseEnumSpecifier( EnumSpecifierAST* ast )
{
NameAST *nameNode = ast->name();
if (nameNode == NULL)
return; // skip constants
TQString typeName = nameNode->unqualifiedName()->text().stripWhiteSpace();
if (typeName.isEmpty())
return; // skip constants
UMLObject *o = Import_Utils::createUMLObject( Uml::ot_Enum, typeName,
m_currentNamespace[m_nsCnt],
ast->comment() );
TQPtrList<EnumeratorAST> l = ast->enumeratorList();
TQPtrListIterator<EnumeratorAST> it( l );
while ( it.current() ) {
TQString enumLiteral = it.current()->id()->text();
Import_Utils::addEnumLiteral( (UMLEnum*)o, enumLiteral );
++it;
}
}
void CppTree2Uml::parseElaboratedTypeSpecifier( ElaboratedTypeSpecifierAST* typeSpec )
{
// This is invoked for forward declarations.
/// @todo Refine - Currently only handles class forward declarations.
/// - Using typeSpec->text() is probably not good, decode
/// the kind() instead.
TQString text = typeSpec->text();
kDebug() << "CppTree2Uml::parseElaboratedTypeSpecifier: text is " << text << endl;
text.remove(TQRegExp("^class\\s+"));
UMLObject *o = Import_Utils::createUMLObject(Uml::ot_Class, text, m_currentNamespace[m_nsCnt]);
flushTemplateParams( static_cast<UMLClassifier*>(o) );
}
void CppTree2Uml::parseDeclaration( GroupAST* funSpec, GroupAST* storageSpec,
TypeSpecifierAST* typeSpec, InitDeclaratorAST* decl )
{
if( m_inStorageSpec )
return;
DeclaratorAST* d = decl->declarator();
if( !d )
return;
if( !d->subDeclarator() && d->parameterDeclarationClause() )
return parseFunctionDeclaration( funSpec, storageSpec, typeSpec, decl );
DeclaratorAST* t = d;
while( t && t->subDeclarator() )
t = t->subDeclarator();
TQString id;
if( t && t->declaratorId() && t->declaratorId()->unqualifiedName() )
id = t->declaratorId()->unqualifiedName()->text();
if( !scopeOfDeclarator(d, TQStringList()).isEmpty() ){
kDebug() << "CppTree2Uml::parseDeclaration (" << id << "): skipping."
<< endl;
return;
}
UMLClassifier *c = m_currentClass[m_clsCnt];
if (c == NULL) {
kDebug() << "CppTree2Uml::parseDeclaration (" << id
<< "): need a surrounding class." << endl;
return;
}
TQString typeName = typeOfDeclaration( typeSpec, d );
bool isFriend = false;
bool isStatic = false;
//bool isInitialized = decl->initializer() != 0;
if( storageSpec ){
TQPtrList<AST> l = storageSpec->nodeList();
TQPtrListIterator<AST> it( l );
while( it.current() ){
TQString text = it.current()->text();
if( text == "friend" ) isFriend = true;
else if( text == "static" ) isStatic = true;
++it;
}
}
Import_Utils::insertAttribute( c, m_currentAccess, id, typeName,
m_comment, isStatic);
m_comment = "";
}
void CppTree2Uml::parseAccessDeclaration( AccessDeclarationAST * access )
{
TQPtrList<AST> l = access->accessList();
TQString accessStr = l.at( 0 )->text();
m_currentAccess=Uml::Visibility::fromString(accessStr);
m_inSlots = l.count() > 1 ? l.at( 1 )->text() == "slots" : false;
m_inSignals = l.count() >= 1 ? l.at( 0 )->text() == "signals" : false;
}
void CppTree2Uml::parseFunctionDeclaration( GroupAST* funSpec, GroupAST* storageSpec,
TypeSpecifierAST * typeSpec, InitDeclaratorAST * decl )
{
bool isFriend = false;
bool isVirtual = false;
bool isStatic = false;
bool isInline = false;
bool isPure = decl->initializer() != 0;
bool isConstructor = false;
if( funSpec ){
TQPtrList<AST> l = funSpec->nodeList();
TQPtrListIterator<AST> it( l );
while( it.current() ){
TQString text = it.current()->text();
if( text == "virtual" ) isVirtual = true;
else if( text == "inline" ) isInline = true;
++it;
}
}
if( storageSpec ){
TQPtrList<AST> l = storageSpec->nodeList();
TQPtrListIterator<AST> it( l );
while( it.current() ){
TQString text = it.current()->text();
if( text == "friend" ) isFriend = true;
else if( text == "static" ) isStatic = true;
++it;
}
}
DeclaratorAST* d = decl->declarator();
TQString id = d->declaratorId()->unqualifiedName()->text();
UMLClassifier *c = m_currentClass[m_clsCnt];
if (c == NULL) {
kDebug() << "CppTree2Uml::parseFunctionDeclaration (" << id
<< "): need a surrounding class." << endl;
return;
}
TQString returnType = typeOfDeclaration( typeSpec, d );
UMLOperation *m = Import_Utils::makeOperation(c, id);
// if a class has no return type, it could be a constructor or
// a destructor
if (d && returnType.isEmpty() && id.find("~") == -1)
isConstructor = true;
parseFunctionArguments( d, m );
Import_Utils::insertMethod( c, m, m_currentAccess, returnType,
isStatic, isPure, isFriend, isConstructor, m_comment);
m_comment = "";
}
void CppTree2Uml::parseFunctionArguments(DeclaratorAST* declarator,
UMLOperation* method)
{
ParameterDeclarationClauseAST* clause = declarator->parameterDeclarationClause();
if( clause && clause->parameterDeclarationList() ){
ParameterDeclarationListAST* params = clause->parameterDeclarationList();
TQPtrList<ParameterDeclarationAST> l( params->parameterList() );
TQPtrListIterator<ParameterDeclarationAST> it( l );
while( it.current() ){
ParameterDeclarationAST* param = it.current();
++it;
TQString name;
if (param->declarator())
name = declaratorToString(param->declarator(), TQString(), true );
TQString tp = typeOfDeclaration( param->typeSpec(), param->declarator() );
if (tp != "void")
Import_Utils::addMethodParameter( method, tp, name );
}
}
}
TQString CppTree2Uml::typeOfDeclaration( TypeSpecifierAST* typeSpec, DeclaratorAST* declarator )
{
if( !typeSpec || !declarator )
return TQString();
TQString text;
text += typeSpec->text();
TQPtrList<AST> ptrOpList = declarator->ptrOpList();
for( TQPtrListIterator<AST> it(ptrOpList); it.current(); ++it ){
text += it.current()->text();
}
return text;
}
void CppTree2Uml::parseBaseClause( BaseClauseAST * baseClause, UMLClassifier* klass )
{
TQPtrList<BaseSpecifierAST> l = baseClause->baseSpecifierList();
TQPtrListIterator<BaseSpecifierAST> it( l );
while( it.current() ){
BaseSpecifierAST* baseSpecifier = it.current();
++it;
if (baseSpecifier->name() == NULL) {
kDebug() << "CppTree2Uml::parseBaseClause: baseSpecifier->name() is NULL"
<< endl;
continue;
}
TQString baseName = baseSpecifier->name()->text();
Import_Utils::createGeneralization( klass, baseName );
}
}
TQStringList CppTree2Uml::scopeOfName( NameAST* id, const TQStringList& startScope )
{
TQStringList scope = startScope;
if( id && id->classOrNamespaceNameList().count() ){
if( id->isGlobal() )
scope.clear();
TQPtrList<ClassOrNamespaceNameAST> l = id->classOrNamespaceNameList();
TQPtrListIterator<ClassOrNamespaceNameAST> it( l );
while( it.current() ){
if( it.current()->name() ){
scope << it.current()->name()->text();
}
++it;
}
}
return scope;
}
TQStringList CppTree2Uml::scopeOfDeclarator( DeclaratorAST* d, const TQStringList& startScope )
{
return scopeOfName( d->declaratorId(), startScope );
}
void CppTree2Uml::flushTemplateParams(UMLClassifier *klass) {
if (m_templateParams.count()) {
Model_Utils::NameAndType_ListIt it;
for (it = m_templateParams.begin(); it != m_templateParams.end(); ++it) {
const Model_Utils::NameAndType &nt = *it;
kDebug() << "CppTree2Uml::parseClassSpecifier: adding template param: "
<< nt.m_name << endl;
UMLTemplate *tmpl = klass->addTemplate(nt.m_name);
tmpl->setType(nt.m_type);
}
m_templateParams.clear();
}
}