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.
465 lines
18 KiB
465 lines
18 KiB
/***************************************************************************
|
|
* *
|
|
* 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) 2005-2007 *
|
|
* Umbrello UML Modeller Authors <uml-devel@uml.sf.net> *
|
|
***************************************************************************/
|
|
|
|
// own header
|
|
#include "import_utils.h"
|
|
// qt/kde includes
|
|
#include <tqmap.h>
|
|
#include <tqregexp.h>
|
|
#include <kmessagebox.h>
|
|
#include <kdebug.h>
|
|
#include <klocale.h>
|
|
// app includes
|
|
#include "../uml.h"
|
|
#include "../umldoc.h"
|
|
#include "../umllistview.h"
|
|
#include "../umllistviewitem.h"
|
|
#include "../umlobject.h"
|
|
#include "../package.h"
|
|
#include "../folder.h"
|
|
#include "../enum.h"
|
|
#include "../classifier.h"
|
|
#include "../operation.h"
|
|
#include "../attribute.h"
|
|
#include "../template.h"
|
|
#include "../association.h"
|
|
#include "../object_factory.h"
|
|
|
|
#include <stdlib.h>
|
|
|
|
namespace Import_Utils {
|
|
|
|
/**
|
|
* Flag manipulated by createUMLObject().
|
|
* Global state is generally bad, I know.
|
|
* It would be cleaner to make this into a return value from
|
|
* createUMLObject().
|
|
*/
|
|
bool bNewUMLObjectWasCreated = false;
|
|
|
|
/**
|
|
* Related classifier for creation of dependencies on template
|
|
* parameters in createUMLObject().
|
|
*/
|
|
UMLClassifier * gRelatedClassifier = NULL;
|
|
|
|
/**
|
|
* On encountering a scoped typename string where the scopes
|
|
* have not yet been seen, we synthesize UML objects for the
|
|
* unknown scopes (using a question dialog to the user to decide
|
|
* whether to treat a scope as a class or as a package.)
|
|
* However, such an unknown scope is put at the global level.
|
|
* I.e. before calling createUMLObject() we set this flag to true.
|
|
*/
|
|
bool bPutAtGlobalScope = false;
|
|
|
|
/**
|
|
* The include path list (see addIncludePath() and includePathList())
|
|
*/
|
|
TQStringList incPathList;
|
|
|
|
void putAtGlobalScope(bool yesno) {
|
|
bPutAtGlobalScope = yesno;
|
|
}
|
|
|
|
void setRelatedClassifier(UMLClassifier *c) {
|
|
gRelatedClassifier = c;
|
|
}
|
|
|
|
void assignUniqueIdOnCreation(bool yesno) {
|
|
Object_Factory::assignUniqueIdOnCreation(yesno);
|
|
}
|
|
|
|
bool newUMLObjectWasCreated() {
|
|
return bNewUMLObjectWasCreated;
|
|
}
|
|
|
|
TQString formatComment(const TQString &comment) {
|
|
if (comment.isEmpty())
|
|
return comment;
|
|
|
|
TQStringList lines = TQStringList::split("\n", comment);
|
|
TQString& first = lines.first();
|
|
TQRegExp wordex("\\w");
|
|
if (first.startsWith("/*")) {
|
|
int wordpos = wordex.search(first);
|
|
if (wordpos != -1)
|
|
first = first.mid(wordpos); // remove comment start
|
|
else
|
|
lines.pop_front(); // nothing interesting on this line
|
|
}
|
|
TQString& last = lines.last();
|
|
int endpos = last.find("*/");
|
|
if (endpos != -1) {
|
|
if (last.contains(wordex))
|
|
last = last.mid(0, endpos - 1); // remove comment end
|
|
else
|
|
lines.pop_back(); // nothing interesting on this line
|
|
}
|
|
if (! lines.count())
|
|
return "";
|
|
|
|
TQStringList::Iterator end(lines.end());
|
|
for (TQStringList::Iterator lit(lines.begin()); lit != end; ++lit) {
|
|
(*lit).remove(TQRegExp("^\\s+"));
|
|
(*lit).remove(TQRegExp("^\\*+\\s?"));
|
|
}
|
|
return lines.join("\n");
|
|
}
|
|
|
|
/*
|
|
UMLObject* findUMLObject(TQString name,
|
|
Uml::Object_Type type) {
|
|
// Why an extra wrapper? See comment at addMethodParameter()
|
|
UMLObject * o = umldoc->findUMLObject(name, type);
|
|
return o;
|
|
}
|
|
*/
|
|
|
|
UMLObject *createUMLObject(Uml::Object_Type type,
|
|
const TQString& inName,
|
|
UMLPackage *parentPkg,
|
|
const TQString& comment,
|
|
const TQString& stereotype) {
|
|
TQString name = inName;
|
|
UMLDoc *umldoc = UMLApp::app()->getDocument();
|
|
UMLFolder *logicalView = umldoc->getRootFolder(Uml::mt_Logical);
|
|
const Uml::Programming_Language pl = UMLApp::app()->getActiveLanguage();
|
|
if (parentPkg == NULL) {
|
|
// kDebug() << "Import_Utils::createUMLObject(" << name
|
|
// << "): parentPkg is NULL, assuming Logical View" << endl;
|
|
parentPkg = logicalView;
|
|
}
|
|
UMLObject * o = umldoc->findUMLObject(name, type, parentPkg);
|
|
bNewUMLObjectWasCreated = false;
|
|
if (o == NULL) {
|
|
// Strip possible adornments and look again.
|
|
int isConst = name.contains(TQRegExp("^const "));
|
|
name.remove(TQRegExp("^const\\s+"));
|
|
TQString typeName(name);
|
|
const int isAdorned = typeName.contains( TQRegExp("[^\\w:\\. ]") );
|
|
const int isPointer = typeName.contains('*');
|
|
const int isRef = typeName.contains('&');
|
|
typeName.remove(TQRegExp("[^\\w:\\. ].*$"));
|
|
typeName = typeName.simplifyWhiteSpace();
|
|
UMLObject *origType = umldoc->findUMLObject(typeName, Uml::ot_UMLObject, parentPkg);
|
|
if (origType == NULL) {
|
|
// Still not found. Create the stripped down type.
|
|
if (bPutAtGlobalScope)
|
|
parentPkg = logicalView;
|
|
// Find, or create, the scopes.
|
|
TQStringList components;
|
|
if (typeName.contains("::")) {
|
|
components = TQStringList::split("::", typeName);
|
|
} else if (typeName.contains(".")) {
|
|
components = TQStringList::split(".", typeName);
|
|
}
|
|
if (components.count() > 1) {
|
|
typeName = components.back();
|
|
components.pop_back();
|
|
while ( components.count() ) {
|
|
TQString scopeName = components.front();
|
|
components.pop_front();
|
|
o = umldoc->findUMLObject(scopeName, Uml::ot_UMLObject, parentPkg);
|
|
if (o) {
|
|
parentPkg = static_cast<UMLPackage*>(o);
|
|
continue;
|
|
}
|
|
int wantNamespace = KMessageBox::Yes;
|
|
if (pl == Uml::pl_Cpp) {
|
|
/* We know std and TQt are namespaces */
|
|
if (scopeName != "std" && scopeName != "TQt") {
|
|
wantNamespace = KMessageBox::questionYesNo(NULL,
|
|
i18n("Is the scope %1 a namespace or a class?").arg(scopeName),
|
|
i18n("C++ Import Requests Your Help"),
|
|
i18n("Namespace"), i18n("Class"));
|
|
}
|
|
}
|
|
Uml::Object_Type ot = (wantNamespace == KMessageBox::Yes ? Uml::ot_Package : Uml::ot_Class);
|
|
o = Object_Factory::createUMLObject(ot, scopeName, parentPkg);
|
|
parentPkg = static_cast<UMLPackage*>(o);
|
|
UMLListView *listView = UMLApp::app()->getListView();
|
|
UMLListViewItem *lvitem = listView->findUMLObject(o);
|
|
listView->setCurrentItem(lvitem);
|
|
}
|
|
// All scope qualified datatypes live in the global scope.
|
|
bPutAtGlobalScope = true;
|
|
}
|
|
Uml::Object_Type t = type;
|
|
if (type == Uml::ot_UMLObject || isAdorned)
|
|
t = Uml::ot_Class;
|
|
origType = Object_Factory::createUMLObject(t, typeName, parentPkg, false);
|
|
bNewUMLObjectWasCreated = true;
|
|
bPutAtGlobalScope = false;
|
|
}
|
|
if (isConst || isAdorned) {
|
|
// Create the full given type (including adornments.)
|
|
if (isConst)
|
|
name.prepend("const ");
|
|
o = Object_Factory::createUMLObject(Uml::ot_Datatype, name,
|
|
umldoc->getDatatypeFolder(),
|
|
false); //solicitNewName
|
|
UMLClassifier *dt = static_cast<UMLClassifier*>(o);
|
|
UMLClassifier *c = dynamic_cast<UMLClassifier*>(origType);
|
|
if (c)
|
|
dt->setOriginType(c);
|
|
else
|
|
kError() << "createUMLObject(" << name << "): "
|
|
<< "origType " << typeName << " is not a UMLClassifier"
|
|
<< endl;
|
|
if (isRef || isPointer)
|
|
dt->setIsReference();
|
|
/*
|
|
if (isPointer) {
|
|
UMLObject *pointerDecl = Object_Factory::createUMLObject(Uml::ot_Datatype, type);
|
|
UMLClassifier *dt = static_cast<UMLClassifier*>(pointerDecl);
|
|
dt->setOriginType(classifier);
|
|
dt->setIsReference();
|
|
classifier = dt;
|
|
} */
|
|
} else {
|
|
o = origType;
|
|
}
|
|
} else if (parentPkg && !bPutAtGlobalScope) {
|
|
UMLPackage *existingPkg = o->getUMLPackage();
|
|
if (existingPkg != umldoc->getDatatypeFolder()) {
|
|
if (existingPkg)
|
|
existingPkg->removeObject(o);
|
|
else
|
|
kError() << "createUMLObject(" << name << "): "
|
|
<< "o->getUMLPackage() was NULL" << endl;
|
|
o->setUMLPackage(parentPkg);
|
|
parentPkg->addObject(o);
|
|
}
|
|
}
|
|
TQString strippedComment = formatComment(comment);
|
|
if (! strippedComment.isEmpty()) {
|
|
o->setDoc(strippedComment);
|
|
}
|
|
if (!stereotype.isEmpty()) {
|
|
o->setStereotype(stereotype);
|
|
}
|
|
if (gRelatedClassifier == NULL || gRelatedClassifier == o)
|
|
return o;
|
|
TQRegExp templateInstantiation("^[\\w:\\.]+\\s*<(.*)>");
|
|
int pos = templateInstantiation.search(name);
|
|
if (pos == -1)
|
|
return o;
|
|
// Create dependencies on template parameters.
|
|
TQString caption = templateInstantiation.cap(1);
|
|
TQStringList params = TQStringList::split(TQRegExp("[^\\w:\\.]+"), caption);
|
|
if (!params.count())
|
|
return o;
|
|
TQStringList::Iterator end(params.end());
|
|
for (TQStringList::Iterator it(params.begin()); it != end; ++it) {
|
|
UMLObject *p = umldoc->findUMLObject(*it, Uml::ot_UMLObject, parentPkg);
|
|
if (p == NULL || p->getBaseType() == Uml::ot_Datatype)
|
|
continue;
|
|
const Uml::Association_Type at = Uml::at_Dependency;
|
|
UMLAssociation *assoc = umldoc->findAssociation(at, gRelatedClassifier, p);
|
|
if (assoc)
|
|
continue;
|
|
assoc = new UMLAssociation(at, gRelatedClassifier, p);
|
|
assoc->setUMLPackage(umldoc->getRootFolder(Uml::mt_Logical));
|
|
umldoc->addAssociation(assoc);
|
|
}
|
|
return o;
|
|
}
|
|
|
|
UMLOperation* makeOperation(UMLClassifier *parent, const TQString &name) {
|
|
UMLOperation *op = Object_Factory::createOperation(parent, name);
|
|
return op;
|
|
}
|
|
|
|
UMLObject* insertAttribute(UMLClassifier *owner,
|
|
Uml::Visibility scope,
|
|
const TQString& name,
|
|
UMLClassifier *attrType,
|
|
const TQString& comment /* ="" */,
|
|
bool isStatic /* =false */) {
|
|
Uml::Object_Type ot = owner->getBaseType();
|
|
Uml::Programming_Language pl = UMLApp::app()->getActiveLanguage();
|
|
if (! (ot == Uml::ot_Class || ot == Uml::ot_Interface && pl == Uml::pl_Java)) {
|
|
kDebug() << "insertAttribute: Don't know what to do with "
|
|
<< owner->getName() << " (object type " << ot << ")" << endl;
|
|
return NULL;
|
|
}
|
|
UMLObject *o = owner->findChildObject(name, Uml::ot_Attribute);
|
|
if (o) {
|
|
return o;
|
|
}
|
|
|
|
UMLAttribute *attr = owner->addAttribute(name, attrType, scope);
|
|
attr->setStatic(isStatic);
|
|
TQString strippedComment = formatComment(comment);
|
|
if (! strippedComment.isEmpty()) {
|
|
attr->setDoc(strippedComment);
|
|
}
|
|
|
|
UMLApp::app()->getDocument()->setModified(true);
|
|
return attr;
|
|
}
|
|
|
|
UMLObject* insertAttribute(UMLClassifier *owner, Uml::Visibility scope,
|
|
const TQString& name,
|
|
const TQString& type,
|
|
const TQString& comment /* ="" */,
|
|
bool isStatic /* =false */) {
|
|
UMLObject *attrType = owner->findTemplate(type);
|
|
if (attrType == NULL) {
|
|
bPutAtGlobalScope = true;
|
|
gRelatedClassifier = owner;
|
|
attrType = createUMLObject(Uml::ot_UMLObject, type, owner);
|
|
gRelatedClassifier = NULL;
|
|
bPutAtGlobalScope = false;
|
|
}
|
|
return insertAttribute (owner, scope, name,
|
|
static_cast<UMLClassifier*>(attrType),
|
|
comment, isStatic);
|
|
}
|
|
|
|
void insertMethod(UMLClassifier *klass, UMLOperation* &op,
|
|
Uml::Visibility scope, const TQString& type,
|
|
bool isStatic, bool isAbstract,
|
|
bool isFriend, bool isConstructor,
|
|
const TQString& comment) {
|
|
op->setVisibility(scope);
|
|
if (!type.isEmpty() // return type may be missing (constructor/destructor)
|
|
&& type != "void") {
|
|
if (type == klass->getName()) {
|
|
op->setType(klass);
|
|
} else {
|
|
UMLObject *typeObj = klass->findTemplate(type);
|
|
if (typeObj == NULL) {
|
|
bPutAtGlobalScope = true;
|
|
gRelatedClassifier = klass;
|
|
typeObj = createUMLObject(Uml::ot_UMLObject, type, klass);
|
|
gRelatedClassifier = NULL;
|
|
bPutAtGlobalScope = false;
|
|
op->setType(typeObj);
|
|
}
|
|
}
|
|
}
|
|
|
|
op->setStatic(isStatic);
|
|
op->setAbstract(isAbstract);
|
|
|
|
// if the operation is friend, add it as a stereotype
|
|
if (isFriend)
|
|
op->setStereotype("friend");
|
|
// if the operation is a constructor, add it as a stereotype
|
|
if (isConstructor)
|
|
op->setStereotype("constructor");
|
|
|
|
TQString strippedComment = formatComment(comment);
|
|
if (! strippedComment.isEmpty()) {
|
|
op->setDoc(strippedComment);
|
|
}
|
|
|
|
UMLAttributeList params = op->getParmList();
|
|
UMLOperation *exist = klass->checkOperationSignature(op->getName(), params);
|
|
if (exist) {
|
|
// copy contents to existing operation
|
|
exist->setVisibility(scope);
|
|
exist->setStatic(isStatic);
|
|
exist->setAbstract(isAbstract);
|
|
if (! strippedComment.isEmpty())
|
|
exist->setDoc(strippedComment);
|
|
UMLAttributeList exParams = exist->getParmList();
|
|
UMLAttribute *param, *exParam = exParams.first();
|
|
for (UMLAttributeListIt it(params); (param = it.current()) != NULL;
|
|
++it, exParam = exParams.next()) {
|
|
exParam->setName(param->getName());
|
|
exParam->setVisibility(param->getVisibility());
|
|
exParam->setStatic(param->getStatic());
|
|
exParam->setAbstract(param->getAbstract());
|
|
exParam->setDoc(param->getDoc());
|
|
exParam->setInitialValue(param->getInitialValue());
|
|
exParam->setParmKind(param->getParmKind());
|
|
}
|
|
// delete incoming UMLOperation and pass out the existing one
|
|
delete op;
|
|
op = exist;
|
|
} else {
|
|
klass->addOperation(op);
|
|
}
|
|
}
|
|
|
|
UMLAttribute* addMethodParameter(UMLOperation *method,
|
|
const TQString& type,
|
|
const TQString& name) {
|
|
UMLClassifier *owner = static_cast<UMLClassifier*>(method->parent());
|
|
UMLObject *typeObj = owner->findTemplate(type);
|
|
if (typeObj == NULL) {
|
|
bPutAtGlobalScope = true;
|
|
gRelatedClassifier = owner;
|
|
typeObj = createUMLObject(Uml::ot_UMLObject, type, owner);
|
|
gRelatedClassifier = NULL;
|
|
bPutAtGlobalScope = false;
|
|
}
|
|
UMLAttribute *attr = Object_Factory::createAttribute(method, name, typeObj);
|
|
method->addParm(attr);
|
|
return attr;
|
|
}
|
|
|
|
void addEnumLiteral(UMLEnum *enumType, const TQString &literal, const TQString &comment) {
|
|
UMLObject *el = enumType->addEnumLiteral(literal);
|
|
el->setDoc(comment);
|
|
}
|
|
|
|
void createGeneralization(UMLClassifier *child, UMLClassifier *parent) {
|
|
// if the child is an interface, so is the parent.
|
|
if (child->isInterface())
|
|
parent->setBaseType(Uml::ot_Interface);
|
|
Uml::Association_Type association = Uml::at_Generalization;
|
|
|
|
if (parent->isInterface() && !child->isInterface()) {
|
|
// if the parent is an interface, but the child is not, then
|
|
// this is really realization.
|
|
//
|
|
association = Uml::at_Realization;
|
|
}
|
|
UMLAssociation *assoc = new UMLAssociation(association, child, parent);
|
|
UMLDoc *umldoc = UMLApp::app()->getDocument();
|
|
assoc->setUMLPackage(umldoc->getRootFolder(Uml::mt_Logical));
|
|
umldoc->addAssociation(assoc);
|
|
}
|
|
|
|
void createGeneralization(UMLClassifier *child, const TQString &parentName) {
|
|
UMLObject *parentObj = createUMLObject( Uml::ot_Class, parentName );
|
|
UMLClassifier *parent = static_cast<UMLClassifier*>(parentObj);
|
|
createGeneralization(child, parent);
|
|
}
|
|
|
|
TQStringList includePathList() {
|
|
TQStringList includePathList(incPathList);
|
|
char *umbrello_incpath = getenv( "UMBRELLO_INCPATH" );
|
|
if (umbrello_incpath) {
|
|
includePathList += TQStringList::split( ':', umbrello_incpath );
|
|
}
|
|
return includePathList;
|
|
}
|
|
|
|
void addIncludePath(const TQString& path) {
|
|
if (! incPathList.contains(path))
|
|
incPathList.append(path);
|
|
}
|
|
|
|
|
|
bool isDatatype(const TQString& name, UMLPackage *parentPkg) {
|
|
UMLDoc *umldoc = UMLApp::app()->getDocument();
|
|
UMLObject * o = umldoc->findUMLObject(name, Uml::ot_Datatype, parentPkg);
|
|
return (o!=NULL);
|
|
}
|
|
|
|
} // end namespace Import_Utils
|
|
|