/*************************************************************************** * * * 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 * ***************************************************************************/ // own header #include "import_utils.h" // qt/kde includes #include #include #include #include #include // 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 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(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(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(o); UMLClassifier *c = dynamic_cast(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(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(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(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(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