/*************************************************************************** * * * 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 * ***************************************************************************/ /* This code generated by: * Author : thomas * Date : Thu Jun 19 2003 */ // own header #include "codegenerator.h" // system includes #include //to get the user name // qt includes #include #include #include #include // kde includes #include #include #include #include #include // app includes #include "dialogs/overwritedialogue.h" #include "dialogs/codeviewerdialog.h" #include "codegenerators/simplecodegenerator.h" #include "attribute.h" #include "association.h" #include "classifier.h" #include "classifiercodedocument.h" #include "codedocument.h" #include "codegenerationpolicy.h" #include "operation.h" #include "uml.h" #include "umldoc.h" #include "umlobject.h" #include "umlattributelist.h" #include "umloperationlist.h" #include "model_utils.h" // Constructors/Destructors // CodeGenerator::CodeGenerator () : TQObject (UMLApp::app()->getDocument()) { initFields(); } // FIX // hmm. this should be pure virtual so that implemented in sub-class CodeGenerator::CodeGenerator (TQDomElement & element ) : TQObject (UMLApp::app()->getDocument()) { initFields(); loadFromXMI(element); // hmm. cant call this here.. its 'pure' virtual } CodeGenerator::~CodeGenerator ( ) { // destroy all owned codedocuments CodeDocument *doc; for (CodeDocumentListIt it(m_codedocumentVector); (doc = it.current()) != NULL; ++it) delete doc; m_codedocumentVector.clear(); } // // Methods // // Accessor methods // TQString CodeGenerator::getUniqueID(CodeDocument * codeDoc) { TQString id = codeDoc->getID(); // does this document already exist? then just return its present id if (!id.isEmpty() && findCodeDocumentByID(id)) return id; // approach now differs by whether or not its a classifier code document ClassifierCodeDocument * classDoc = dynamic_cast(codeDoc); if(classDoc) { UMLClassifier *c = classDoc->getParentClassifier(); id = ID2STR(c->getID()); // this is supposed to be unique already.. } else { TQString prefix = "doc"; TQString id = prefix + "_0"; int number = lastIDIndex; for ( ; findCodeDocumentByID(id); number++) { id = prefix + '_' + TQString::number(number); } lastIDIndex = number; } return id; } CodeDocument * CodeGenerator::findCodeDocumentByID( const TQString &tag ) { //if we already know to which file this class was written/should be written, just return it. CodeDocument * doc = (CodeDocument*)NULL; if((doc = m_codeDocumentDictionary.find(tag))) return doc; return doc; } bool CodeGenerator::addCodeDocument ( CodeDocument * doc ) { TQString tag = doc->getID(); // assign a tag if one doesn't already exist if(tag.isEmpty()) { tag = getUniqueID(doc); doc->setID(tag); } if(m_codeDocumentDictionary.find(tag)) return false; // return false, we already have some object with this tag in the list else m_codeDocumentDictionary.insert(tag, doc); m_codedocumentVector.append(doc); return true; } /** * Remove a CodeDocument object from m_codedocumentVector List */ bool CodeGenerator::removeCodeDocument ( CodeDocument * remove_object ) { TQString tag = remove_object->getID(); if(!(tag.isEmpty())) m_codeDocumentDictionary.remove(tag); else return false; m_codedocumentVector.remove(remove_object); return true; } /** * Get the list of CodeDocument objects held by m_codedocumentVector * @return TQPtrList list of CodeDocument objects held by * m_codedocumentVector */ CodeDocumentList * CodeGenerator::getCodeDocumentList ( ) { return &m_codedocumentVector; } // the vanilla version CodeViewerDialog * CodeGenerator::getCodeViewerDialog ( TQWidget* parent, CodeDocument *doc, Settings::CodeViewerState state) { return new CodeViewerDialog(parent, doc, state); } // Other methods // void CodeGenerator::loadFromXMI (TQDomElement & qElement ) { // don't do anything for simple (compatability) code generators if(dynamic_cast(this)) return; //now look for our particular child element TQDomNode node = qElement.firstChild(); TQDomElement element = node.toElement(); TQString langType = Model_Utils::progLangToString( getLanguage() ); if (qElement.tagName() != "codegenerator" || qElement.attribute("language", "UNKNOWN") != langType) return; // got our code generator element, now load // codedocuments TQDomNode codeDocNode = qElement.firstChild(); TQDomElement codeDocElement = codeDocNode.toElement(); while( !codeDocElement.isNull() ) { TQString docTag = codeDocElement.tagName(); if( docTag == "codedocument" || docTag == "classifiercodedocument" ) { TQString id = codeDocElement.attribute( "id", "-1" ); CodeDocument * codeDoc = findCodeDocumentByID(id); if(codeDoc) codeDoc->loadFromXMI(codeDocElement); else { kWarning()<<" loadFromXMI: missing code document w/ id:"<first(); codeDoc; codeDoc= docList->next()) codeDoc->saveToXMI(doc, docElement); root.appendChild( docElement ); } /** * Initialize this code generator from its parent UMLDoc. When this is called, it will * (re-)generate the list of code documents for this project (generator) by checking * for new objects/attributes which have been added or changed in the documnet. One or more * CodeDocuments will be created/overwritten/amended as is appropriate for the given language. * * In this 'generic' version a ClassifierCodeDocument will exist for each and * every classifier that exists in our UMLDoc. IF when this is called, a code document * doesn't exist for the given classifier, then we will created and add a new code * document to our generator. * * IF you want to add non-classifier related code documents at this step, * you will need to overload this method in the appropriate * code generatator (see JavaCodeGenerator for an example of this). */ void CodeGenerator::initFromParentDocument( ) { // Walk through the document converting classifiers into // classifier code documents as needed (e.g only if doesn't exist) UMLClassifierList concepts = UMLApp::app()->getDocument()->getClassesAndInterfaces(); for (UMLClassifier *c = concepts.first(); c; c = concepts.next()) { // Doesn't exist? Then build one. CodeDocument * codeDoc = findCodeDocumentByClassifier(c); if (!codeDoc) { codeDoc = newClassifierCodeDocument(c); addCodeDocument(codeDoc); // this will also add a unique tag to the code document } } } /** * Force a synchronize of this code generator, and its present contents, to that of the parent UMLDocument. * "UserGenerated" code will be preserved, but Autogenerated contents will be updated/replaced * or removed as is apppropriate. */ void CodeGenerator::syncCodeToDocument ( ) { for (CodeDocument * doc = m_codedocumentVector.first(); doc; doc=m_codedocumentVector.next()) doc->synchronize(); } // in this 'vanilla' version, we only worry about adding classifier // documents void CodeGenerator::checkAddUMLObject (UMLObject * obj) { if (!obj) return; UMLClassifier * c = dynamic_cast(obj); if(c) { CodeDocument * cDoc = newClassifierCodeDocument(c); addCodeDocument(cDoc); } } void CodeGenerator::checkRemoveUMLObject (UMLObject * obj) { if (!obj) return; UMLClassifier * c = dynamic_cast(obj); if(c) { ClassifierCodeDocument * cDoc = (ClassifierCodeDocument*) findCodeDocumentByClassifier(c); if (cDoc) removeCodeDocument(cDoc); } } /** * @return CodeDocument * @param classifier */ CodeDocument * CodeGenerator::findCodeDocumentByClassifier ( UMLClassifier * classifier ) { return findCodeDocumentByID(ID2STR(classifier->getID())); } /** * Write out all code documents to file as appropriate. */ void CodeGenerator::writeCodeToFile ( ) { writeListedCodeDocsToFile(&m_codedocumentVector); } void CodeGenerator::writeCodeToFile ( UMLClassifierList & concepts) { CodeDocumentList docs; docs.setAutoDelete(false); for (UMLClassifier *concept= concepts.first(); concept; concept= concepts.next()) { CodeDocument * doc = findCodeDocumentByClassifier(concept); if(doc) docs.append(doc); } writeListedCodeDocsToFile(&docs); } // Main method. Will write out passed code documents to file as appropriate. void CodeGenerator::writeListedCodeDocsToFile ( CodeDocumentList * docs ) { // iterate thru all code documents for (CodeDocument *doc = docs->first(); doc; doc = docs->next()) { // we need this so we know when to emit a 'codeGenerated' signal ClassifierCodeDocument * cdoc = dynamic_cast(doc); bool codeGenSuccess = false; // we only write the document, if so requested if(doc->getWriteOutCode()) { TQString filename = findFileName(doc); // check that we may open that file for writing TQFile file; if ( openFile(file,filename) ) { TQTextStream stream(&file); stream<toString()<getParentClassifier(), codeGenSuccess); } } /** * Create a new Code document belonging to this package. * @return CodeDocument */ CodeDocument * CodeGenerator::newCodeDocument ( ) { CodeDocument * newCodeDoc = new CodeDocument (); return newCodeDoc; } /** * @return TQString * @param file */ TQString CodeGenerator::getHeadingFile( const TQString &file ) { return UMLApp::app()->getCommonPolicy()->getHeadingFile(file); } /** * @return TQString * @param codeDoc * @param name */ TQString CodeGenerator::overwritableName(const TQString& name, const TQString &extension ) { CodeGenerationPolicy *pol = UMLApp::app()->getCommonPolicy(); TQDir outputDirectory = pol->getOutputDirectory(); TQString filename = name + extension; if (!outputDirectory.exists(filename)) { return filename; } int suffix; OverwriteDialogue overwriteDialog( name, outputDirectory.absPath(), m_applyToAllRemaining, kapp -> mainWidget() ); switch (pol->getOverwritePolicy()) { //if it exists, check the OverwritePolicy we should use case CodeGenerationPolicy::Ok: //ok to overwrite file filename = name + extension; break; case CodeGenerationPolicy::Ask: //ask if we can overwrite switch(overwriteDialog.exec()) { case KDialogBase::Yes: //overwrite file if ( overwriteDialog.applyToAllRemaining() ) { pol->setOverwritePolicy(CodeGenerationPolicy::Ok); filename = name + extension; } else { m_applyToAllRemaining = false; } break; case KDialogBase::No: //generate similar name suffix = 1; while (1) { filename = name + "__" + TQString::number(suffix) + extension; if (!outputDirectory.exists(filename)) break; suffix++; } if ( overwriteDialog.applyToAllRemaining() ) { pol->setOverwritePolicy(CodeGenerationPolicy::Never); } else { m_applyToAllRemaining = false; } break; case KDialogBase::Cancel: //don't output anything if ( overwriteDialog.applyToAllRemaining() ) { pol->setOverwritePolicy(CodeGenerationPolicy::Cancel); } else { m_applyToAllRemaining = false; } return TQString(); break; } break; case CodeGenerationPolicy::Never: //generate similar name suffix = 1; while (1) { filename = name + "__" + TQString::number(suffix) + extension; if (!outputDirectory.exists(filename)) break; suffix++; } break; case CodeGenerationPolicy::Cancel: //don't output anything return TQString(); break; } return filename; } /** * @return bool * @param file * @param name */ bool CodeGenerator::openFile (TQFile & file, const TQString &fileName ) { //open files for writing. if(fileName.isEmpty()) { kWarning() << "cannot find a file name" << endl; return false; } else { TQDir outputDirectory = UMLApp::app()->getCommonPolicy()->getOutputDirectory(); file.setName(outputDirectory.absFilePath(fileName)); if(!file.open(IO_WriteOnly)) { KMessageBox::sorry(0,i18n("Cannot open file %1 for writing. Please make sure the folder exists and you have permissions to write to it.").arg(file.name()),i18n("Cannot Open File")); return false; } return true; } } /** * @return TQString * @param name */ TQString CodeGenerator::cleanName ( const TQString &name ) { TQString retval = name; retval.replace(TQRegExp("\\W"), "_"); return retval; } TQString CodeGenerator::findFileName ( CodeDocument * codeDocument ) { //else, determine the "natural" file name TQString name; // Get the path name TQString path = codeDocument->getPath(); // if path is given add this as a directory to the file name if (!path.isEmpty()) { path.replace(TQRegExp("::"), "/"); // Simple hack! name = path + '/' + codeDocument->getFileName(); path = '/' + path; } else { name = codeDocument->getFileName(); } // Convert all "::" to "/" : Platform-specific path separator name.replace(TQRegExp("::"), "/"); // Simple hack! // if a path name exists check the existence of the path directory if (!path.isEmpty()) { TQDir outputDirectory = UMLApp::app()->getCommonPolicy()->getOutputDirectory(); TQDir pathDir(outputDirectory.absPath() + path); // does our complete output directory exist yet? if not, try to create it if (!pathDir.exists()) { // ugh. dir separator here is UNIX specific.. TQStringList dirs = TQStringList::split("/",pathDir.absPath()); TQString currentDir = ""; TQStringList::iterator end(dirs.end()); for (TQStringList::iterator dir(dirs.begin()); dir != end; ++dir) { currentDir += '/' + *dir; if (! (pathDir.exists(currentDir) || pathDir.mkdir(currentDir) ) ) { KMessageBox::error(0, i18n("Cannot create the folder:\n") + pathDir.absPath() + i18n("\nPlease check the access rights"), i18n("Cannot Create Folder")); return NULL; } } } } name.simplifyWhiteSpace(); name.replace(TQRegExp(" "),"_"); return overwritableName( name, codeDocument->getFileExtension() ); } void CodeGenerator::findObjectsRelated(UMLClassifier *c, UMLPackageList &cList) { UMLPackage *temp; UMLAssociationList associations = c->getAssociations(); for (UMLAssociation *a = associations.first(); a; a = associations.next()) { temp = 0; switch (a->getAssocType()) { case Uml::at_Generalization: case Uml::at_Realization: // only the "b" end is seen by the "a" end, not other way around { UMLObject *objB = a->getObject(Uml::B); if (objB != c) temp = (UMLPackage*)objB; } break; case Uml::at_Dependency: case Uml::at_UniAssociation: { UMLObject *objA = a->getObject(Uml::A); UMLObject *objB = a->getObject(Uml::B); if (objA == c) temp = static_cast(objB); } break; case Uml::at_Aggregation: case Uml::at_Composition: case Uml::at_Association: { UMLObject *objA = a->getObject(Uml::A); UMLObject *objB = a->getObject(Uml::B); if (objA == c && objB->getBaseType() != Uml::ot_Datatype) temp = static_cast(objB); } break; default: /* all others.. like for state diagrams..we currently don't use */ break; } // now add in list ONLY if its not already there if(temp && !cList.containsRef(temp)) cList.append(temp); } //operations UMLOperationList opl(c->getOpList()); for(UMLOperation *op = opl.first(); op ; op = opl.next()) { temp =0; //check return value temp =(UMLClassifier*) op->getType(); if (temp && temp->getBaseType() != Uml::ot_Datatype && !cList.containsRef(temp)) cList.append(temp); //check parameters UMLAttributeList atl = op->getParmList(); for (UMLAttribute *at = atl.first(); at; at = atl.next()) { temp = (UMLClassifier*)at->getType(); if (temp && temp->getBaseType() != Uml::ot_Datatype && !cList.containsRef(temp)) cList.append(temp); } } //attributes if (!c->isInterface()) { UMLAttributeList atl = c->getAttributeList(); for (UMLAttribute *at = atl.first(); at; at = atl.next()) { temp=0; temp = (UMLClassifier*) at->getType(); if (temp && temp->getBaseType() != Uml::ot_Datatype && !cList.containsRef(temp)) cList.append(temp); } } } /** * Format an output document. * @return TQString * @param text * @param lineprefix * @param linewidth */ TQString CodeGenerator::formatDoc(const TQString &text, const TQString &linePrefix, int lineWidth) { TQString output; const TQString endLine = UMLApp::app()->getCommonPolicy()->getNewLineEndingChars(); TQStringList lines = TQStringList::split(endLine, text); for (TQStringList::ConstIterator lit = lines.begin(); lit != lines.end(); ++lit) { TQString input = *lit; input.remove( TQRegExp("\\s+$") ); if (input.length() < (uint)lineWidth) { output += linePrefix + input + endLine; continue; } int index; while ((index = input.findRev(" ", lineWidth)) >= 0) { output += linePrefix + input.left(index) + endLine; // add line input.remove(0, index + 1); //and remove processed string, including // white space } if (!input.isEmpty()) output += linePrefix + input + endLine; } return output; } void CodeGenerator::initFields() { m_document = UMLApp::app()->getDocument(); m_codeDocumentDictionary.setAutoDelete(false); m_codedocumentVector.setAutoDelete(false); m_applyToAllRemaining = true; lastIDIndex = 0; // initial population of our project generator // CANT Be done here because we would call pure virtual method // of newClassifierDocument (bad!). // We should only call from the child // initFromParentDocument(); } void CodeGenerator::connect_newcodegen_slots() { UMLDoc *doc = UMLApp::app()->getDocument(); connect(doc, TQT_SIGNAL(sigObjectCreated(UMLObject*)), this, TQT_SLOT(checkAddUMLObject(UMLObject*))); connect(doc, TQT_SIGNAL(sigObjectRemoved(UMLObject*)), this, TQT_SLOT(checkRemoveUMLObject(UMLObject*))); CodeGenerationPolicy *commonPolicy = UMLApp::app()->getCommonPolicy(); connect(commonPolicy, TQT_SIGNAL(modifiedCodeContent()), this, TQT_SLOT(syncCodeToDocument())); } // these are utility methods for accessing the default // code gen policy object and should go away when we // finally implement the CodeGenDialog class -b.t. void CodeGenerator::setForceDoc(bool f) { UMLApp::app()->getCommonPolicy()->setCodeVerboseDocumentComments(f); } bool CodeGenerator::forceDoc() const { return UMLApp::app()->getCommonPolicy()->getCodeVerboseDocumentComments(); } void CodeGenerator::setForceSections(bool f) { UMLApp::app()->getCommonPolicy()->setCodeVerboseSectionComments(f); } bool CodeGenerator::forceSections() const { return UMLApp::app()->getCommonPolicy()->getCodeVerboseSectionComments(); } TQStringList CodeGenerator::defaultDatatypes() { return TQStringList(); //empty by default, override in your code generator } bool CodeGenerator::isReservedKeyword(const TQString & keyword) { const TQStringList keywords = reservedKeywords(); return keywords.contains(keyword); } const TQStringList CodeGenerator::reservedKeywords() const { static TQStringList emptyList; return emptyList; } void CodeGenerator::createDefaultStereotypes() { //empty by default, override in your code generator //e.g. m_document->createDefaultStereotypes("constructor"); } #include "codegenerator.moc"