/*************************************************************************** * * * 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) 2006-2007 * * Umbrello UML Modeller Authors * ***************************************************************************/ // own header #include "petaltree2uml.h" // qt/kde includes #include #include // app includes #include "petalnode.h" #include "codeimport/import_utils.h" #include "package.h" #include "classifier.h" #include "attribute.h" #include "operation.h" #include "association.h" #include "umlrole.h" #include "actor.h" #include "usecase.h" #include "component.h" #include "node.h" #include "uml.h" #include "umldoc.h" #include "umllistview.h" #include "umllistviewitem.h" namespace Import_Rose { /** * Return the given string without surrounding quotation marks. * Also remove a possible prefix "Logical View::", it is not modeled in Umbrello. */ TQString clean(const TQString& s) { if (s.isNull()) return TQString(); TQString str = s; str.remove("\""); str.remove(TQRegExp("^Logical View::")); return str; } /** * Extract the quid attribute from a petal node and return it as a Uml::IDType. */ Uml::IDType quid(const PetalNode *node) { TQString quidStr = node->findAttribute("quid").string; if (quidStr.isEmpty()) return Uml::id_None; quidStr.remove("\""); return STR2ID(quidStr); } /** * Extract the quidu attribute from a petal node. */ TQString quidu(const PetalNode *node) { TQString quiduStr = node->findAttribute("quidu").string; if (quiduStr.isEmpty()) return TQString(); quiduStr.remove("\""); return quiduStr; } /** * Determine the model type corresponding to a name. * If the given name consists only of letters, digits, underscores, and * scope separators, then return Uml::ot_Class, else return Uml::ot_Datatype. */ Uml::Object_Type typeToCreate(const TQString& name) { TQString n = name; n.remove(TQRegExp("^.*::")); // don't consider the scope prefix, it may contain spaces Uml::Object_Type t = (n.contains(TQRegExp("\\W")) ? Uml::ot_Datatype : Uml::ot_Class); return t; } /** * Transfer the Rose attribute "exportControl" to the Umbrello object given. * * @param from Pointer to PetalNode from which to read the "exportControl" attribute * @param to Pointer to UMLObject in which to set the Uml::Visibility */ void transferVisibility(const PetalNode *from, UMLObject *to) { TQString vis = from->findAttribute("exportControl").string; if (!vis.isEmpty()) { Uml::Visibility v = Uml::Visibility::fromString(clean(vis.lower())); to->setVisibility(v); } } /** * ClassifierListReader factors the common processing for attributes, operations, * and operation parameters. */ class ClassifierListReader { public: /// constructor ClassifierListReader(const char* attributeTag, const char* elementName, const char* itemTypeDesignator) : m_attributeTag(attributeTag), m_elementName(elementName), m_itemTypeDesignator(itemTypeDesignator) { } /// destructor virtual ~ClassifierListReader() {} /** * Return a UMLClassifierListItem of the specific type desired. * Abstract method to be implemented by inheriting classes. */ virtual UMLObject *createListItem() = 0; virtual void setTypeReferences(UMLObject *item, const TQString& quid, const TQString& type) { if (!quid.isEmpty()) { item->setSecondaryId(quid); } if (!type.isEmpty()) { item->setSecondaryFallback(type); } } /** * Insert the given UMLClassifierListItem at the parent Umbrello object. * Abstract method to be implemented by inheriting classes. * NB the parent Umbrello object is not included in the ClassifierListReader * class - it must be added at inheriting classes. * * @param node The PetalNode which corresponds to the parent Umbrello object. * @param o The UMLObject to insert. */ virtual void insertAtParent(const PetalNode *node, UMLObject *o) = 0; /** * Iterate over the attributes of the given PetalNode and for each recognized * attribute do the following: * - invoke createListItem() * - fill common properties such as name, unique ID, visibility, etc. into * the new UMLClassifierListItem * - invoke insertAtParent() with the new classifier list item as the argument * This is the user entry point. */ void read(const PetalNode *node, const TQString& name) { PetalNode *attributes = node->findAttribute(m_attributeTag).node; if (attributes == NULL) { #ifdef VERBOSE_DEBUGGING kDebug() << "read(" << name << "): no " << m_attributeTag << " found" << endl; #endif return; } PetalNode::NameValueList attributeList = attributes->attributes(); for (uint i = 0; i < attributeList.count(); i++) { PetalNode *attNode = attributeList[i].second.node; TQStringList initialArgs = attNode->initialArgs(); if (attNode->name() != m_elementName) { kDebug() << "read(" << name << "): expecting " << m_elementName << ", " << "found " << initialArgs[0] << endl; continue; } UMLObject *item = createListItem(); if (initialArgs.count() > 1) item->setName(clean(initialArgs[1])); item->setID(quid(attNode)); TQString quidref = quidu(attNode); TQString type = clean(attNode->findAttribute(m_itemTypeDesignator).string); setTypeReferences(item, quidref, type); transferVisibility(attNode, item); TQString doc = attNode->findAttribute("documentation").string; if (! doc.isEmpty()) item->setDoc(doc); insertAtParent(attNode, item); } } protected: const TQString m_attributeTag, m_elementName, m_itemTypeDesignator; }; class AttributesReader : public ClassifierListReader { public: AttributesReader(UMLClassifier *c) : ClassifierListReader("class_attributes", "ClassAttribute", "type") { m_classifier = c; } virtual ~AttributesReader() {} UMLObject *createListItem() { return new UMLAttribute(m_classifier); } void insertAtParent(const PetalNode *, UMLObject *item) { m_classifier->addAttribute(static_cast(item)); } protected: UMLClassifier *m_classifier; }; class ParametersReader : public ClassifierListReader { public: ParametersReader(UMLOperation *op) : ClassifierListReader("parameters", "Parameter", "type") { m_operation = op; } virtual ~ParametersReader() {} UMLObject *createListItem() { return new UMLAttribute(m_operation); } void insertAtParent(const PetalNode *, UMLObject *item) { m_operation->addParm(static_cast(item)); } protected: UMLOperation *m_operation; }; class OperationsReader : public ClassifierListReader { public: OperationsReader(UMLClassifier *c) : ClassifierListReader("operations", "Operation", "result") { m_classifier = c; } virtual ~OperationsReader() {} UMLObject *createListItem() { return new UMLOperation(m_classifier); } void insertAtParent(const PetalNode *node, UMLObject *item) { UMLOperation *op = static_cast(item); ParametersReader parmReader(op); parmReader.read(node, m_classifier->getName()); m_classifier->addOperation(op); } protected: UMLClassifier *m_classifier; }; class SuperclassesReader : public ClassifierListReader { public: SuperclassesReader(UMLClassifier *c) : ClassifierListReader("superclasses", "Inheritance_Relationship", "supplier") { m_classifier = c; } virtual ~SuperclassesReader() {} UMLObject *createListItem() { return new UMLAssociation(Uml::at_Generalization); } /** * Override parent implementation: The secondary data is not for the * UMLAssociation itself but for its role B object. */ void setTypeReferences(UMLObject *item, const TQString& quid, const TQString& type) { UMLAssociation *assoc = static_cast(item); if (!quid.isEmpty()) { assoc->getUMLRole(Uml::B)->setSecondaryId(quid); } if (!type.isEmpty()) { assoc->getUMLRole(Uml::B)->setSecondaryFallback(type); } } void insertAtParent(const PetalNode *, UMLObject *item) { UMLAssociation *assoc = static_cast(item); assoc->setObject(m_classifier, Uml::A); UMLApp::app()->getDocument()->addAssociation(assoc); } protected: UMLClassifier *m_classifier; }; class RealizationsReader : public ClassifierListReader { public: RealizationsReader(UMLClassifier *c) : ClassifierListReader("realized_interfaces", "Realize_Relationship", "supplier") { m_classifier = c; } virtual ~RealizationsReader() {} UMLObject *createListItem() { return new UMLAssociation(Uml::at_Realization); } /** * Override parent implementation: The secondary data is not for the * UMLAssociation itself but for its role B object. */ void setTypeReferences(UMLObject *item, const TQString& quid, const TQString& type) { UMLAssociation *assoc = static_cast(item); if (!quid.isEmpty()) { assoc->getUMLRole(Uml::B)->setSecondaryId(quid); } if (!type.isEmpty()) { assoc->getUMLRole(Uml::B)->setSecondaryFallback(type); } } void insertAtParent(const PetalNode *, UMLObject *item) { UMLAssociation *assoc = static_cast(item); assoc->setObject(m_classifier, Uml::A); UMLApp::app()->getDocument()->addAssociation(assoc); } protected: UMLClassifier *m_classifier; }; /** * Handle a controlled unit. * * @param node Pointer to the PetalNode which may contain a controlled unit * @param name Name of the current node * @param id QUID of the current node * @param parentPkg Pointer to the current parent UMLPackage. * @return True if the node actually contained a controlled unit. */ bool handleControlledUnit(PetalNode *node, const TQString& name, Uml::IDType id, UMLPackage *parentPkg) { if (node->findAttribute("is_unit").string != "TRUE") return false; bool is_loaded = (node->findAttribute("is_loaded").string != "FALSE"); TQString file_name = node->findAttribute("file_name").string; if (file_name.isEmpty()) { kError() << "handleControlledUnit(" << name << "): attribute file_name not found (?)" << endl; return true; } // To Be Continued. return true; } /** * Create an Umbrello object from a PetalNode of the Logical View. * * @return True for success. * Given a PetalNode for which the mapping to Umbrello is not yet * implemented umbrellify() is a no-op but also returns true. */ bool umbrellify(PetalNode *node, UMLPackage *parentPkg = NULL) { if (node == NULL) { kError() << "umbrellify: node is NULL" << endl; return false; } TQStringList args = node->initialArgs(); TQString objType = args[0]; TQString name = clean(args[1]); Uml::IDType id = quid(node); if (objType == "Class_Category") { UMLObject *o = Import_Utils::createUMLObject(Uml::ot_Package, name, parentPkg); o->setID(id); PetalNode *logical_models = node->findAttribute("logical_models").node; if (logical_models) { UMLPackage *localParent = static_cast(o); PetalNode::NameValueList atts = logical_models->attributes(); for (uint i = 0; i < atts.count(); i++) { umbrellify(atts[i].second.node, localParent); } } else if (!handleControlledUnit(node, name, id, parentPkg)) { kDebug() << "umbrellify: handling of " << objType << " " << name << " is not yet implemented" << endl; } } else if (objType == "Class") { UMLObject *o = Import_Utils::createUMLObject(Uml::ot_Class, name, parentPkg); o->setID(id); UMLClassifier *c = static_cast(o); // set stereotype TQString stereotype = clean(node->findAttribute("stereotype").string); if (!stereotype.isEmpty()) { if (stereotype.lower() == "interface") c->setBaseType(Uml::ot_Interface); else c->setStereotype(stereotype); } // insert attributes AttributesReader attReader(c); attReader.read(node, c->getName()); // insert operations OperationsReader opReader(c); opReader.read(node, c->getName()); // insert generalizations SuperclassesReader superReader(c); superReader.read(node, c->getName()); // insert realizations RealizationsReader realReader(c); realReader.read(node, c->getName()); } else if (objType == "Association") { PetalNode *roles = node->findAttribute("roles").node; if (node == NULL) { kError() << "umbrellify: cannot find roles of Association" << endl; return false; } UMLAssociation *assoc = new UMLAssociation(Uml::at_UniAssociation); PetalNode::NameValueList roleList = roles->attributes(); for (uint i = 0; i <= 1; i++) { PetalNode *roleNode = roleList[i].second.node; if (roleNode == NULL) { kError() << "umbrellify: roleNode of Association is NULL" << endl; return false; } if (roleNode->name() != "Role") { kDebug() << "umbrellify(" << name << "): expecting Role, found \"" << roleNode->name() << endl; continue; } // index 0 corresponds to Umbrello roleB // index 1 corresponds to Umbrello roleA UMLRole *role = assoc->getUMLRole((Uml::Role_Type) !i); TQStringList initialArgs = roleNode->initialArgs(); if (initialArgs.count() > 1) { TQString roleName = clean(initialArgs[1]); if (! roleName.startsWith("$UNNAMED")) role->setName(roleName); } role->setID(quid(roleNode)); TQString quidref = quidu(roleNode); TQString type = clean(roleNode->findAttribute("supplier").string); if (!quidref.isEmpty()) { role->setSecondaryId(quidref); } if (!type.isEmpty()) { role->setSecondaryFallback(type); } TQString label = clean(roleNode->findAttribute("label").string); if (!label.isEmpty()) { role->setName(label); } TQString client_cardinality = clean(roleNode->findAttribute("client_cardinality").string); if (!client_cardinality.isEmpty()) { role->setMultiplicity(client_cardinality); } TQString is_navigable = clean(roleNode->findAttribute("is_navigable").string); if (is_navigable == "FALSE") { assoc->setAssocType(Uml::at_Association); } TQString is_aggregate = clean(roleNode->findAttribute("is_aggregate").string); if (is_aggregate == "TRUE") { assoc->setAssocType(Uml::at_Aggregation); } TQString containment = clean(roleNode->findAttribute("Containment").string); if (containment == "By Value") { assoc->setAssocType(Uml::at_Composition); } TQString doc = roleNode->findAttribute("documentation").string; if (! doc.isEmpty()) role->setDoc(doc); } UMLApp::app()->getDocument()->addAssociation(assoc); } else { kDebug() << "umbrellify: object type " << objType << " is not yet implemented" << endl; } return true; } Uml::ListView_Type folderType(UMLListViewItem *parent) { Uml::ListView_Type type = Uml::lvt_Unknown; switch (parent->getType()) { case Uml::lvt_Logical_View: case Uml::lvt_Logical_Folder: type = Uml::lvt_Logical_Folder; break; case Uml::lvt_UseCase_View: case Uml::lvt_UseCase_Folder: type = Uml::lvt_UseCase_Folder; break; case Uml::lvt_Component_View: case Uml::lvt_Component_Folder: type = Uml::lvt_Component_Folder; break; case Uml::lvt_Deployment_View: case Uml::lvt_Deployment_Folder: type = Uml::lvt_Deployment_Folder; break; default: break; } return type; } /** * Create an Umbrello object from a PetalNode of the UseCase, Component, * or Deployment View. * * @return True for success. * Given a PetalNode for which the mapping to Umbrello is not yet * implemented umbrellify() is a no-op but also returns true. */ bool umbrellify(PetalNode *node, const TQString& modelsName, UMLListViewItem *parent) { if (node == NULL) { kError() << "umbrellify(" << modelsName << "): node is NULL" << endl; return false; } TQStringList args = node->initialArgs(); TQString objType = args[0]; TQString name = clean(args[1]); Uml::IDType id = quid(node); UMLObject *obj = NULL; UMLListViewItem *item = NULL; if (objType == "Class_Category") { Uml::ListView_Type lvType = folderType(parent); item = new UMLListViewItem( parent, name, lvType, id ); } else if (objType == "Class") { TQString stereotype = clean(node->findAttribute("stereotype").string); if (stereotype == "Actor") { UMLActor *act = new UMLActor(name, id); item = new UMLListViewItem(parent, name, Uml::lvt_Actor, act); obj = act; } else { kDebug() << "umbrellify(" << name << "): handling of Class stereotype " << stereotype << " is not yet implemented" << endl; } } else if (objType == "UseCase") { UMLUseCase *uc = new UMLUseCase(name, id); item = new UMLListViewItem(parent, name, Uml::lvt_UseCase, uc); obj = uc; } else if (objType == "SubSystem") { UMLComponent *comp = new UMLComponent(name, id); item = new UMLListViewItem(parent, name, Uml::lvt_Component, comp); obj = comp; } else if (objType == "Processor" || objType == "Device") { UMLNode *un = new UMLNode(name, id); un->setStereotype(objType.lower()); item = new UMLListViewItem(parent, name, Uml::lvt_Node, un); obj = un; } else { kDebug() << "umbrellify: object type " << objType << " is not yet implemented" << endl; return true; } PetalNode *models = node->findAttribute(modelsName).node; if (models) { PetalNode::NameValueList atts = models->attributes(); for (uint i = 0; i < atts.count(); i++) { if (! umbrellify(atts[i].second.node, modelsName, item)) return false; } } if (obj) { TQString doc = node->findAttribute("documentation").string; if (! doc.isEmpty()) obj->setDoc(doc); UMLDoc *theDocument = UMLApp::app()->getDocument(); theDocument->addUMLObject(obj); } return true; } /** * Auxiliary function for UseCase/Component/Deployment view import */ bool importView(PetalNode *root, const TQString& rootName, const TQString& modelsName, UMLListViewItem *lvParent) { PetalNode *viewRoot = root->findAttribute(rootName).node; if (viewRoot == NULL) { kDebug() << "importView: cannot find " << rootName << endl; return false; } PetalNode *models = viewRoot->findAttribute(modelsName).node; if (models == NULL) { kError() << "importView: cannot find " << modelsName << " of " << rootName << endl; return false; } PetalNode::NameValueList atts = models->attributes(); for (uint i = 0; i < atts.count(); i++) { umbrellify(atts[i].second.node, modelsName, lvParent); } return true; } bool petalTree2Uml(PetalNode *root) { if (root == NULL) { kError() << "petalTree2Uml: root is NULL" << endl; return false; } if (root->name() != "Design") { kError() << "petalTree2Uml: expecting root name Design" << endl; return false; } /*************************** import Logical View ********************************/ PetalNode *root_category = root->findAttribute("root_category").node; if (root_category == NULL) { kError() << "petalTree2Uml: cannot find root_category" << endl; return false; } if (root_category->name() != "Class_Category") { kError() << "petalTree2Uml: expecting root_category object Class_Category" << endl; return false; } PetalNode *logical_models = root_category->findAttribute("logical_models").node; if (logical_models == NULL) { kError() << "petalTree2Uml: cannot find logical_models" << endl; return false; } UMLDoc *umldoc = UMLApp::app()->getDocument(); umldoc->setCurrentRoot(Uml::mt_Logical); Import_Utils::assignUniqueIdOnCreation(false); PetalNode::NameValueList atts = logical_models->attributes(); for (uint i = 0; i < atts.count(); i++) { umbrellify(atts[i].second.node); } /** Shorthand for UMLApp::app()->getListView() **/ UMLListView *lv = UMLApp::app()->getListView(); /*************************** import Use Case View ********************************/ umldoc->setCurrentRoot(Uml::mt_UseCase); importView(root, "root_usecase_package", "logical_models", lv->theUseCaseView()); /*************************** import Component View *******************************/ umldoc->setCurrentRoot(Uml::mt_Component); importView(root, "root_subsystem", "physical_models", lv->theComponentView()); /*************************** import Deployment View ******************************/ umldoc->setCurrentRoot(Uml::mt_Deployment); importView(root, "process_structure", "ProcsNDevs", lv->theDeploymentView()); /*************************** wrap up ********************************/ umldoc->setCurrentRoot(Uml::mt_Logical); Import_Utils::assignUniqueIdOnCreation(true); umldoc->resolveTypes(); return true; } } // namespace Import_Rose