/*************************************************************************** javawriter.cpp - description This is the "old" code generator that does not support code editing in the Modeller but uses significantly less file space because the source code is not replicated in the XMI file. ------------------- copyright : (C) 2003 Brian Thomas brian.thomas@gsfc.nasa.gov (C) 2004-2006 Umbrello UML Modeller Authors ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ // own header #include "javawriter.h" // qt includes #include #include #include // kde includes #include // app includes #include "codegen_utils.h" #include "../umldoc.h" #include "../classifier.h" #include "../operation.h" #include "../attribute.h" #include "../association.h" #include "../template.h" #include "../umltemplatelist.h" JavaWriter::JavaWriter() { startline = m_endl + m_indentation; } JavaWriter::~JavaWriter() {} Uml::Programming_Language JavaWriter::getLanguage() { return Uml::pl_Java; } void JavaWriter::writeClass(UMLClassifier *c) { if (!c) { kDebug()<<"Cannot write class of NULL concept!\n"; return; } isInterface = c->isInterface(); TQString fileName = cleanName(c->getName().lower()); //find an appropriate name for our file fileName = findFileName(c,".java"); if (fileName.isEmpty()) { emit codeGenerated(c, false); return; } // check that we may open that file for writing TQFile file; if ( !openFile(file, fileName) ) { emit codeGenerated(c, false); return; } // Preparations // // sort attributes by Scope UMLAttributeList atl; UMLAttributeList atpub, atprot, atpriv; UMLAttributeList final_atpub, final_atprot, final_atpriv; atpub.setAutoDelete(false); final_atpub.setAutoDelete(false); atprot.setAutoDelete(false); final_atprot.setAutoDelete(false); atpriv.setAutoDelete(false); final_atpriv.setAutoDelete(false); if (!isInterface) { UMLAttributeList atl = c->getAttributeList(); for (UMLAttribute *at = atl.first(); at ; at = atl.next()) { switch(at->getVisibility()) { case Uml::Visibility::Public: if(at->getStatic()) final_atpub.append(at); else atpub.append(at); break; case Uml::Visibility::Protected: if(at->getStatic()) final_atprot.append(at); else atprot.append(at); break; case Uml::Visibility::Private: if(at->getStatic()) final_atpriv.append(at); else atpriv.append(at); break; default: break; } } } // another preparation, determine what we have UMLAssociationList associations = c->getSpecificAssocs(Uml::at_Association); // BAD! only way to get "general" associations. UMLAssociationList uniAssociations = c->getUniAssociationToBeImplemented(); UMLAssociationList aggregations = c->getAggregations(); UMLAssociationList compositions = c->getCompositions(); bool hasAssociations = aggregations.count() > 0 || associations.count() > 0 || compositions.count() > 0 || uniAssociations.count() > 0; bool hasAttributes = (atl.count() > 0); bool hasAccessorMethods = hasAttributes || hasAssociations; bool hasOperationMethods = (c->getOpList().count() > 0); // this is a bit too simplistic..some associations are for // SINGLE objects, and WONT be declared as Vectors, so this // is a bit overly inclusive bool hasVectorFields = hasAssociations ? true : false; // open text stream to file TQTextStream java(&file); //try to find a heading file (license, coments, etc) TQString str; str = getHeadingFile(".java"); if(!str.isEmpty()) { str.replace(TQRegExp("%filename%"),fileName); str.replace(TQRegExp("%filepath%"),file.name()); java<getPackage().isEmpty()) java<<"package "<getPackage()<<";"<getBaseType() == Uml::ot_Datatype) continue; TQString pkg = con->getPackage(); if (!pkg.isEmpty() && pkg != c->getPackage()) java << "import " << pkg << "." << cleanName(con->getName()) << ";" << m_endl; } writeBlankLine(java); // write the opening declaration for the class incl any documentation, // interfaces and/or inheritence issues we have writeClassDecl(c, java); // start body of class java<<" {"<getID(), java); writeAssociationDecls(uniAssociations, c->getID(), java); writeAssociationDecls(aggregations, c->getID(), java); writeAssociationDecls(compositions, c->getID(), java); // Constructors: anything we more we need to do here ? // if(!isInterface) writeConstructor(c, java); // METHODS // // write comment for section IF needed if (forceDoc() || hasAccessorMethods || hasOperationMethods) { java<getName()); // our class name // write documentation for class, if any, first if(forceDoc() || !c->getDoc().isEmpty()) { if(isInterface) writeDocumentation("Interface "+classname,c->getDoc(),"","",java); else writeDocumentation("Class "+classname,c->getDoc(),"","",java); writeBlankLine(java); } // Now write the actual class declaration TQString scope = ""; // = scopeToJavaDecl(c->getVisibility()); if (c->getVisibility() != Uml::Visibility::Public) { // We should emit a warning in here .. java doesn't like to allow // private/protected classes. The best we can do (I believe) // is to let these declarations default to "package visibility" // which is a level between traditional "private" and "protected" // scopes. To get this visibility level we just print nothing.. } else scope = "public "; java<<((c->getAbstract() && !isInterface) ? TQString("abstract ") : TQString(""))<getTemplateList(); if (template_params.count()) { java << "<"; for (UMLTemplate *t = template_params.first(); t; ) { TQString formalName = t->getName(); java << formalName; TQString typeName = t->getTypeName(); if (typeName != "class") { java << " extends " << typeName; } if ((t = template_params.next()) != NULL) java << ", "; } java << ">" << m_endl; } // write inheritances out UMLClassifier *concept; UMLClassifierList superclasses = c->findSuperClassConcepts(UMLClassifier::CLASS); int i = 0; for (concept= superclasses.first(); concept; concept = superclasses.next()) { if (i == 0) { java<< " extends "; } else { //The java generated code is wrong ! : No multiple inheritence of class java<< ", " ; } java<< cleanName(concept->getName()); i++; } UMLClassifierList superInterfaces = c->findSuperClassConcepts(UMLClassifier::INTERFACE); i = 0; for (concept= superInterfaces.first(); concept; concept = superInterfaces.next()) { if (i == 0) { if (isInterface) java<< " extends "; else java<< " implements "; } else { //The java generated code is OK ! : multiple inheritence of interface java<< ", " ; } java<< cleanName(concept->getName()); i++; } } void JavaWriter::writeAttributeDecls(UMLAttributeList &atpub, UMLAttributeList &atprot, UMLAttributeList &atpriv, TQTextStream &java ) { UMLAttribute *at; for(at=atpub.first(); at; at=atpub.next()) { TQString documentation = at->getDoc(); TQString staticValue = at->getStatic() ? "static " : ""; TQString typeName = fixTypeName(at->getTypeName()); TQString initialValue = fixInitialStringDeclValue(at->getInitialValue(), typeName); if(!documentation.isEmpty()) writeComment(documentation, m_indentation, java, true); java<getName()) <<(initialValue.isEmpty()?TQString(""):TQString(" = ") + initialValue)<<";"; } for(at=atprot.first();at;at=atprot.next()) { TQString documentation = at->getDoc(); TQString typeName = fixTypeName(at->getTypeName()); TQString staticValue = at->getStatic() ? "static " : ""; TQString initialValue = fixInitialStringDeclValue(at->getInitialValue(), typeName); if(!documentation.isEmpty()) writeComment(documentation, m_indentation, java, true); java<getName()) <<(initialValue.isEmpty()?TQString(""):TQString(" = ") + initialValue)<<";"; } for(at=atpriv.first();at;at=atpriv.next()) { TQString documentation = at->getDoc(); TQString typeName = fixTypeName(at->getTypeName()); TQString staticValue = at->getStatic() ? "static " : ""; TQString initialValue = fixInitialStringDeclValue(at->getInitialValue(), typeName); if(!documentation.isEmpty()) writeComment(documentation, m_indentation, java, true); java<getName()) <<(initialValue.isEmpty()?TQString(""):TQString(" = ") + initialValue)<<";"; } } void JavaWriter::writeAttributeMethods(UMLAttributeList &atpub, Uml::Visibility visibility, TQTextStream &java) { UMLAttribute *at; for(at=atpub.first(); at; at=atpub.next()) { TQString fieldName = cleanName(at->getName()); // force capitalizing the field name, this is silly, // from what I can tell, this IS the default behavior for // cleanName. I dunno why its not working -b.t. fieldName.stripWhiteSpace(); fieldName.replace(0,1,fieldName.at(0).upper()); writeSingleAttributeAccessorMethods(at->getTypeName(), cleanName(at->getName()), fieldName, at->getDoc(), visibility, Uml::chg_Changeable, at->getStatic(), java); } } void JavaWriter::writeComment(const TQString &comment, const TQString &myIndent, TQTextStream &java, bool javaDocStyle) { // in the case we have several line comment.. // NOTE: this part of the method has the problem of adopting UNIX newline, // need to resolve for using with MAC/WinDoze eventually I assume if (comment.contains(TQRegExp("\n"))) { if(javaDocStyle) java << myIndent << "/**" << m_endl; TQStringList lines = TQStringList::split( "\n", comment); for(uint i= 0; i < lines.count(); i++) { writeBlankLine(java); if(javaDocStyle) java< 0) java << " " << comment; if(javaDocStyle) java << m_endl << myIndent << " */"; } } void JavaWriter::writeDocumentation(TQString header, TQString body, TQString end, TQString indent, TQTextStream &java) { writeBlankLine(java); java<getObjectId(Uml::A) == id) printRoleB = true; if (a->getObjectId(Uml::B) == id) printRoleA = true; // First: we insert documentaion for association IF it has either role AND some documentation (!) if ((printRoleA || printRoleB) && !(a->getDoc().isEmpty())) writeComment(a->getDoc(), m_indentation, java); // print RoleB decl if (printRoleB) { TQString fieldClassName = cleanName(getUMLObjectName(a->getObject(Uml::B))); writeAssociationRoleDecl(fieldClassName, a->getRoleName(Uml::B), a->getMulti(Uml::B), a->getRoleDoc(Uml::B), a->getVisibility(Uml::B), java); } // print RoleA decl if (printRoleA) { TQString fieldClassName = cleanName(getUMLObjectName(a->getObject(Uml::A))); writeAssociationRoleDecl(fieldClassName, a->getRoleName(Uml::A), a->getMulti(Uml::A), a->getRoleDoc(Uml::A), a->getVisibility(Uml::A), java); } } } } void JavaWriter::writeAssociationRoleDecl(TQString fieldClassName, TQString roleName, TQString multi, TQString doc, Uml::Visibility visib, TQTextStream &java) { // ONLY write out IF there is a rolename given // otherwise its not meant to be declared in the code if (roleName.isEmpty()) return; TQString scope = scopeToJavaDecl(visib); // always put space between this and prior decl, if any writeBlankLine(java); if (!doc.isEmpty()) writeComment(doc, m_indentation, java); // declare the association based on whether it is this a single variable // or a List (Vector). One day this will be done correctly with special // multiplicity object that we don't have to figure out what it means via regex. if(multi.isEmpty() || multi.contains(TQRegExp("^[01]$"))) { TQString fieldVarName = "m_" + roleName.replace(0, 1, roleName.left(1).lower()); java<getObjectId(Uml::A) == thisClass->getID()) { // only write out IF there is a rolename given if(!a->getRoleName(Uml::B).isEmpty()) { TQString fieldClassName = getUMLObjectName(a->getObject(Uml::B)); writeAssociationRoleMethod(fieldClassName, a->getRoleName(Uml::B), a->getMulti(Uml::B), a->getRoleDoc(Uml::B), a->getVisibility(Uml::B), a->getChangeability(Uml::B), java); } } if (a->getObjectId(Uml::B) == thisClass->getID()) { // only write out IF there is a rolename given if(!a->getRoleName(Uml::A).isEmpty()) { TQString fieldClassName = getUMLObjectName(a->getObject(Uml::A)); writeAssociationRoleMethod(fieldClassName, a->getRoleName(Uml::A), a->getMulti(Uml::A), a->getRoleDoc(Uml::A), a->getVisibility(Uml::A), a->getChangeability(Uml::A), java); } } } } } void JavaWriter::writeAssociationRoleMethod (TQString fieldClassName, TQString roleName, TQString multi, TQString description, Uml::Visibility visib, Uml::Changeability_Type change, TQTextStream &java) { if(multi.isEmpty() || multi.contains(TQRegExp("^[01]$"))) { TQString fieldVarName = "m_" + roleName.replace(0, 1, roleName.left(1).lower()); writeSingleAttributeAccessorMethods(fieldClassName, fieldVarName, roleName, description, visib, change, false, java); } else { TQString fieldVarName = roleName.lower() + "Vector"; writeVectorAttributeAccessorMethods(fieldClassName, fieldVarName, roleName, description, visib, change, java); } } void JavaWriter::writeVectorAttributeAccessorMethods (TQString fieldClassName, TQString fieldVarName, TQString fieldName, TQString description, Uml::Visibility visibility, Uml::Changeability_Type changeType, TQTextStream &java) { fieldClassName = fixTypeName(fieldClassName); fieldName = Codegen_Utils::capitalizeFirstLetter(fieldName); TQString strVis = scopeToJavaDecl(visibility); // ONLY IF changeability is NOT Frozen if (changeType != Uml::chg_Frozen) { writeDocumentation("Add a "+fieldName+" object to the "+fieldVarName+" List",description,"",m_indentation,java); java<getName()); java<getName() != op2->getName()) return false; UMLAttributeList atl1 = op1->getParmList(); UMLAttributeList atl2 = op2->getParmList(); if (atl1.count() != atl2.count()) return false; UMLAttribute *at1; UMLAttribute *at2; for (at1 = atl1.first(), at2 = atl2.first(); at1 && at2 ; at1 = atl1.next(),at2 = atl2.next()) { if (at1->getTypeName() != at2->getTypeName()) return false; } return true; } bool JavaWriter::javaMethodInList(UMLOperation *umlOp, UMLOperationList &opl) { for (UMLOperation *op = opl.first(); op; op = opl.next()) { if (JavaWriter::compareJavaMethod(op, umlOp)) { return true; } } return false; } void JavaWriter::getSuperImplementedOperations(UMLClassifier *c, UMLOperationList &yetImplementedOpList ,UMLOperationList &toBeImplementedOpList, bool noClassInPath) { UMLClassifierList superClasses = c->findSuperClassConcepts(); for (UMLClassifier *concept= superClasses.first(); concept; concept = superClasses.next()) { getSuperImplementedOperations(concept, yetImplementedOpList, toBeImplementedOpList, (concept->isInterface() && noClassInPath)); UMLOperationList opl = concept->getOpList(); for (UMLOperation *op = opl.first(); op; op = opl.next()) { if (concept->isInterface() && noClassInPath) { if (!JavaWriter::javaMethodInList(op,toBeImplementedOpList)) toBeImplementedOpList.append(op); } else { if (!JavaWriter::javaMethodInList(op, yetImplementedOpList)) yetImplementedOpList.append(op); } } } } void JavaWriter::getInterfacesOperationsToBeImplemented(UMLClassifier *c, UMLOperationList &opList ) { UMLOperationList yetImplementedOpList; UMLOperationList toBeImplementedOpList; getSuperImplementedOperations(c,yetImplementedOpList, toBeImplementedOpList); for (UMLOperation *op = toBeImplementedOpList.first(); op; op = toBeImplementedOpList.next()) { if ( ! JavaWriter::javaMethodInList(op, yetImplementedOpList) && ! JavaWriter::javaMethodInList(op, opList) ) opList.append(op); } } void JavaWriter::writeOperations(UMLClassifier *c, TQTextStream &java) { UMLOperationList opl; UMLOperationList oppub,opprot,oppriv; oppub.setAutoDelete(false); opprot.setAutoDelete(false); oppriv.setAutoDelete(false); //sort operations by scope first and see if there are abstract methods opl = c->getOpList(); if (! c->isInterface()) { getInterfacesOperationsToBeImplemented(c, opl); } for (UMLOperation *op = opl.first(); op; op = opl.next()) { switch(op->getVisibility()) { case Uml::Visibility::Public: oppub.append(op); break; case Uml::Visibility::Protected: opprot.append(op); break; case Uml::Visibility::Private: oppriv.append(op); break; default: break; } } // do people REALLY want these comments? Hmm. /* if(forceSections() || oppub.count()) { writeComment("public operations",m_indentation,java); writeBlankLine(java); } */ writeOperations(oppub,java); /* if(forceSections() || opprot.count()) { writeComment("protected operations",m_indentation,java); writeBlankLine(java); } */ writeOperations(opprot,java); /* if(forceSections() || oppriv.count()) { writeComment("private operations",m_indentation,java); writeBlankLine(java); } */ writeOperations(oppriv,java); } void JavaWriter::writeOperations(UMLOperationList &oplist, TQTextStream &java) { UMLOperation *op; UMLAttributeList atl; UMLAttribute *at; int i,j; TQString str; // generate method decl for each operation given for( op=oplist.first(); op ;op=oplist.next()) { TQString returnStr = ""; // write documentation TQString methodReturnType = fixTypeName(op->getTypeName()); if(methodReturnType != "void") returnStr += "@return "+methodReturnType+"\n"; str = ""; // reset for next method str += ((op->getAbstract() && !isInterface) ? "abstract ":""); str += scopeToJavaDecl(op->getVisibility()) + ' '; str += (op->getStatic() ? "static ":""); str += methodReturnType + ' ' +cleanName(op->getName()) + "( "; atl = op->getParmList(); i= atl.count(); j=0; for (at = atl.first(); at; at = atl.next(), j++) { TQString typeName = fixTypeName(at->getTypeName()); TQString atName = cleanName(at->getName()); str += typeName + ' ' + atName + (!(at->getInitialValue().isEmpty()) ? (TQString(" = ")+at->getInitialValue()) : TQString("")) + ((j < i-1)?", ":""); returnStr += "@param "+atName+' '+at->getDoc()+"\n"; } str+= " )"; // method only gets a body IF its not abstract if (op->getAbstract() || isInterface) str+=";\n\n"; // terminate now else str+=startline+"{\n\n"+m_indentation+"}\n\n"; // empty method body // write it out writeDocumentation("", op->getDoc(), returnStr, m_indentation, java); java<getName():TQString("NULL"); } void JavaWriter::writeBlankLine(TQTextStream &java) { java<