umbrello API Documentation

xmlschemawriter.cpp

00001 /***************************************************************************
00002     copyright            : (C) 2003 Brian Thomas brian.thomas@gsfc.nasa.gov
00003       (C) 2004-2006  Umbrello UML Modeller Authors <uml-devel@uml.sf.net>
00004 ***************************************************************************/
00005 
00006 /***************************************************************************
00007  *                                                                         *
00008  *   This program is free software; you can redistribute it and/or modify  *
00009  *   it under the terms of the GNU General Public License as published by  *
00010  *   the Free Software Foundation; either version 2 of the License, or     *
00011  *   (at your option) any later version.                                   *
00012  *                                                                         *
00013  ***************************************************************************/
00014 
00015 #include "xmlschemawriter.h"
00016 
00017 #include <kdebug.h>
00018 
00019 #include <klocale.h>
00020 #include <kmessagebox.h>
00021 #include <qfile.h>
00022 #include <qtextstream.h>
00023 #include <qregexp.h>
00024 
00025 #include "../umldoc.h"
00026 #include "../classifier.h"
00027 #include "../operation.h"
00028 #include "../umlnamespace.h"
00029 
00030 // Constructor
00031 XMLSchemaWriter::XMLSchemaWriter()
00032 {
00033 
00034     packageNamespaceTag = "tns";
00035     packageNamespaceURI = "http://foo.example.com/";
00036     schemaNamespaceTag = "xs";
00037     schemaNamespaceURI = "http://www.w3.org/2001/XMLSchema";
00038 }
00039 
00040 // form of..."the Destructor"!!
00041 XMLSchemaWriter::~XMLSchemaWriter() {
00042 }
00043 
00047 Uml::Programming_Language XMLSchemaWriter::getLanguage() {
00048     return Uml::pl_XMLSchema;
00049 }
00050 
00051 // main method for invoking..
00052 void XMLSchemaWriter::writeClass(UMLClassifier *c)
00053 {
00054 
00055     if (!c) {
00056         kDebug()<<"Cannot write class of NULL classifier!\n";
00057         return;
00058     }
00059 
00060     // find an appropriate name for our file
00061     QString fileName = findFileName(c,".xsd");
00062 
00063     if (fileName.isEmpty()) {
00064         emit codeGenerated(c, false);
00065         return;
00066     }
00067 
00068     // check that we may open that file for writing
00069     QFile file;
00070     if ( !openFile(file, fileName) ) {
00071         emit codeGenerated(c, false);
00072         return;
00073     }
00074 
00075     QTextStream XMLschema(&file);
00076 
00077     // set package namespace tag appropriately
00078     if(!c->getPackage().isEmpty())
00079         packageNamespaceTag = c->getPackage();
00080 
00081     // START WRITING
00082 
00083     // 0. FIRST THING: open the xml processing instruction. This MUST be
00084     // the first thing in the file
00085     XMLschema<<"<?xml version=\"1.0\"?>"<<m_endl;
00086 
00087     // 1. create the header
00088     QString headerText = getHeadingFile(".xsd");
00089     if(!headerText.isEmpty()) {
00090         headerText.replace(QRegExp("%filename%"),fileName);
00091         headerText.replace(QRegExp("%filepath%"),file.name());
00092     }
00093     if(!headerText.isEmpty())
00094         XMLschema<<headerText<<m_endl;
00095 
00096     // 2. Open schema element node with appropriate namespace decl
00097     XMLschema<<"<"<<makeSchemaTag("schema");
00098     // common namespaces we know will be in the file..
00099     XMLschema<<" targetNamespace=\""<<packageNamespaceURI+packageNamespaceTag<<"\""<<m_endl;
00100     XMLschema<<" xmlns:"<<schemaNamespaceTag<<"=\""<<schemaNamespaceURI<<"\"";
00101     XMLschema<<" xmlns:"<<packageNamespaceTag<<"=\""<<packageNamespaceURI+packageNamespaceTag<<"\"";
00102 
00103     XMLschema<<">"<<m_endl; // close opening declaration
00104 
00105     m_indentLevel++;
00106 
00107     // 3? IMPORT statements -- do we need to do anything here? I suppose if
00108     // our document has more than one package, which is possible, we are missing
00109     // the correct import statements. Leave that for later at this time.
00110     /*
00111     //only import classes in a different package as this class
00112     UMLPackageList imports;
00113     findObjectsRelated(c,imports);
00114     for(UMLPackage *con = imports.first(); con ; con = imports.next())
00115         if(con->getPackage() != c->getPackage())
00116                 XMLschema<<"import "<<con->getPackage()<<"."<<cleanName(con->getName())<<";"<<m_endl;
00117     */
00118 
00119     // 4. BODY of the schema.
00120     // start the writing by sending this classifier, the "root" for this particular
00121     // schema, to writeClassifier method, which will subsequently call itself on all
00122     // related classifiers and thus populate the schema.
00123     writeClassifier(c, XMLschema);
00124 
00125     // 5. What remains is to make the root node declaration
00126     XMLschema<<m_endl;
00127     writeElementDecl(getElementName(c), getElementTypeName(c), XMLschema);
00128 
00129     // 6. Finished: now we may close schema decl
00130     m_indentLevel--;
00131     XMLschema<<getIndent()<<"</"<<makeSchemaTag("schema")<<">"<<m_endl; // finished.. close schema node
00132 
00133     // bookeeping for code generation
00134     emit codeGenerated(c, true);
00135 
00136     // tidy up. no dangling open files please..
00137     file.close();
00138 
00139     // need to clear HERE, NOT in the destructor because we want each
00140     // schema that we write to have all related classes.
00141     writtenClassifiers.clear();
00142 }
00143 
00144 void XMLSchemaWriter::writeElementDecl( const QString &elementName, const QString &elementTypeName, QTextStream &XMLschema)
00145 {
00146     if(forceDoc())
00147         writeComment(elementName+" is the root element, declared here.", XMLschema);
00148 
00149     XMLschema<<getIndent()<<"<"<<makeSchemaTag("element")
00150     <<" name=\""<<elementName<<"\""
00151     <<" type=\""<<makePackageTag(elementTypeName)<<"\""
00152     <<"/>"<<m_endl;
00153 
00154 }
00155 
00156 void XMLSchemaWriter::writeClassifier (UMLClassifier *c, QTextStream &XMLschema)
00157 {
00158 
00159     // NO doing this 2 or more times.
00160     if(hasBeenWritten(c))
00161         return;
00162 
00163     XMLschema<<m_endl;
00164 
00165     // write documentation for class, if any, first
00166     if(forceDoc() || !c->getDoc().isEmpty())
00167         writeComment(c->getDoc(),XMLschema);
00168 
00169     if(c->getAbstract() || c->isInterface() )
00170         writeAbstractClassifier(c,XMLschema); // if its an interface or abstract class
00171     else
00172         writeConcreteClassifier(c,XMLschema);
00173 
00174 }
00175 
00176 UMLAttributeList XMLSchemaWriter::findAttributes (UMLClassifier *c)
00177 {
00178     // sort attributes by Scope
00179     UMLAttributeList attribs;
00180     attribs.setAutoDelete(false);
00181 
00182     if (!c->isInterface()) {
00183         UMLAttributeList atl = c->getAttributeList();
00184         for(UMLAttribute *at=atl.first(); at ; at=atl.next()) {
00185             switch(at->getVisibility())
00186             {
00187               case Uml::Visibility::Public:
00188               case Uml::Visibility::Protected:
00189                 attribs.append(at);
00190                 break;
00191               case Uml::Visibility::Private:
00192                 // DO NOTHING! no way to print in the schema
00193                 break;
00194               default:
00195                 break;
00196             }
00197         }
00198     }
00199     return attribs;
00200 }
00201 
00202 // We have to do 2 things with abstract classifiers (e.g. abstract classes and interfaces)
00203 // which is to:
00204 // 1) declare it as a complexType so it may be inherited (I can see an option here: to NOT write
00205 //    this complexType declaration IF the classifier itself isnt inherited by or is inheriting
00206 //    from anything because no other element will use this complexType).
00207 // 2) Create a group so that elements, which obey the abstract class /interface may be placed in
00208 //    aggregation with other elements (again, and option here to NOT write the group if no other
00209 //    element use the interface in element aggregation)
00210 //
00211 
00212 void XMLSchemaWriter::writeAbstractClassifier (UMLClassifier *c, QTextStream &XMLschema)
00213 {
00214 
00215     // preparations
00216     UMLClassifierList subclasses = c->findSubClassConcepts(); // list of what inherits from us
00217     UMLClassifierList superclasses = c->findSuperClassConcepts(); // list of what we inherit from
00218 
00219     // write the main declaration
00220     writeConcreteClassifier (c, XMLschema);
00221     writeGroupClassifierDecl (c, subclasses, XMLschema);
00222 
00223     markAsWritten(c);
00224 
00225     // now go back and make sure all sub-classing nodes are declared
00226     if(subclasses.count() > 0)
00227     {
00228 
00229         QString elementName = getElementName(c);
00230         UMLAttributeList attribs = findAttributes(c);
00231         QStringList attribGroups = findAttributeGroups(c);
00232 
00233         writeAttributeGroupDecl(elementName, attribs, XMLschema);
00234 
00235         // now write out inheriting classes, as needed
00236         for(UMLClassifier * classifier = subclasses.first(); classifier; classifier = subclasses.next())
00237             writeClassifier(classifier, XMLschema);
00238     }
00239 
00240     // write out any superclasses as needed
00241     for(UMLClassifier *classifier = superclasses.first(); classifier; classifier = superclasses.next())
00242         writeClassifier(classifier, XMLschema);
00243 
00244 }
00245 
00246 void XMLSchemaWriter::writeGroupClassifierDecl (UMLClassifier *c,
00247         UMLClassifierList subclasses,
00248         QTextStream &XMLschema)
00249 {
00250 
00251     // name of class, subclassing classifiers
00252     QString elementTypeName = getElementGroupTypeName(c);
00253 
00254     // start Writing node but only if it has subclasses? Nah..right now put in empty group
00255     XMLschema<<getIndent()<<"<"<<makeSchemaTag("group")<<" name=\""<<elementTypeName<<"\">"<<m_endl;
00256     m_indentLevel++;
00257 
00258     XMLschema<<getIndent()<<"<"<<makeSchemaTag("choice")<<">"<<m_endl;
00259     m_indentLevel++;
00260 
00261     for(UMLClassifier *classifier = subclasses.first(); classifier; classifier = subclasses.next()) {
00262         writeAssociationRoleDecl(classifier, "1", XMLschema);
00263     }
00264 
00265     m_indentLevel--;
00266     XMLschema<<getIndent()<<"</"<<makeSchemaTag("choice")<<">"<<m_endl;
00267 
00268     m_indentLevel--;
00269 
00270     // finish node
00271     XMLschema<<getIndent()<<"</"<<makeSchemaTag("group")<<">"<<m_endl;
00272 
00273 }
00274 
00275 void XMLSchemaWriter::writeComplexTypeClassifierDecl (UMLClassifier *c,
00276         UMLAssociationList associations,
00277         UMLAssociationList aggregations,
00278         UMLAssociationList compositions,
00279         UMLClassifierList superclasses,
00280         QTextStream &XMLschema)
00281 {
00282 
00283     // Preparations
00284     //
00285 
00286     // sort attributes by Scope
00287     UMLAttributeList attribs = findAttributes(c);
00288     QStringList attribGroups = findAttributeGroups(c);
00289 
00290     // test for relevant associations
00291     bool hasAssociations = determineIfHasChildNodes(c);
00292     bool hasSuperclass = superclasses.count()> 0;
00293     bool hasAttributes = attribs.count() > 0 || attribGroups.count() > 0;
00294 
00295     // START WRITING
00296 
00297     // start body of element
00298     QString elementTypeName = getElementTypeName(c);
00299 
00300     XMLschema<<getIndent()<<"<"<<makeSchemaTag("complexType")<<" name=\""<<elementTypeName<<"\"";
00301 
00302     if(hasAssociations || hasAttributes || hasSuperclass)
00303     {
00304 
00305         XMLschema<<">"<<m_endl;
00306 
00307         m_indentLevel++;
00308 
00309         if(hasSuperclass)
00310         {
00311             QString superClassName = getElementTypeName(superclasses.first());
00312             XMLschema<<getIndent()<<"<"<<makeSchemaTag("complexContent")<<">"<<m_endl;
00313 
00314             //PROBLEM: we only treat ONE superclass for inheritence.. bah.
00315             m_indentLevel++;
00316             XMLschema<<getIndent()<<"<"<<makeSchemaTag("extension")<<" base=\""<<makePackageTag(superClassName)
00317             <<"\"";
00318             if(hasAssociations || hasAttributes )
00319                 XMLschema<<">"<<m_endl;
00320             else
00321                 XMLschema<<"/>"<<m_endl;
00322 
00323             m_indentLevel++;
00324         }
00325 
00326         if(hasAssociations)
00327         {
00328             // Child Elements (from most associations)
00329             //
00330             bool didFirstOne = false;
00331             didFirstOne = writeAssociationDecls(associations, true, didFirstOne, c->getID(), XMLschema);
00332             didFirstOne = writeAssociationDecls(aggregations, false, didFirstOne, c->getID(), XMLschema);
00333             didFirstOne = writeAssociationDecls(compositions, false, didFirstOne, c->getID(), XMLschema);
00334 
00335             if (didFirstOne) {
00336                 m_indentLevel--;
00337                 XMLschema<<getIndent()<<"</"<<makeSchemaTag("sequence")<<">"<<m_endl;
00338             }
00339         }
00340 
00341         // ATTRIBUTES
00342         //
00343         if(hasAttributes)
00344         {
00345             writeAttributeDecls(attribs, XMLschema);
00346             for(uint i= 0; i < attribGroups.count(); i++)
00347                 XMLschema<<getIndent()<<"<"<<makeSchemaTag("attributeGroup")<<" ref=\""
00348                 <<makePackageTag(attribGroups[i])<<"\"/>"<<m_endl;
00349         }
00350 
00351         if(hasSuperclass)
00352         {
00353             m_indentLevel--;
00354 
00355             if(hasAssociations || hasAttributes )
00356                 XMLschema<<getIndent()<<"</"<<makeSchemaTag("extension")<<">"<<m_endl;
00357 
00358             m_indentLevel--;
00359             XMLschema<<getIndent()<<"</"<<makeSchemaTag("complexContent")<<">"<<m_endl;
00360         }
00361 
00362         // close this element decl
00363         m_indentLevel--;
00364         XMLschema<<getIndent()<<"</"<<makeSchemaTag("complexType")<<">"<<m_endl;
00365 
00366     } else
00367         XMLschema<<"/>"<<m_endl; // empty node. just close this element decl
00368 
00369 }
00370 
00371 void XMLSchemaWriter::writeConcreteClassifier (UMLClassifier *c, QTextStream &XMLschema)
00372 {
00373 
00374     // preparations.. gather information about this classifier
00375     //
00376     UMLClassifierList superclasses = c->findSuperClassConcepts(); // list of what inherits from us
00377     UMLClassifierList subclasses = c->findSubClassConcepts(); // list of what we inherit from
00378     UMLAssociationList aggregations = c->getAggregations();
00379     UMLAssociationList compositions = c->getCompositions();
00380     // BAD! only way to get "general" associations.
00381     UMLAssociationList associations = c->getSpecificAssocs(Uml::at_Association);
00382 
00383     // write the main declaration
00384     writeComplexTypeClassifierDecl(c, associations, aggregations, compositions, superclasses, XMLschema);
00385 
00386     markAsWritten(c);
00387 
00388     // Now write out any child def's
00389     writeChildObjsInAssociation(c, associations, XMLschema);
00390     writeChildObjsInAssociation(c, aggregations, XMLschema);
00391     writeChildObjsInAssociation(c, compositions, XMLschema);
00392 
00393     // write out any superclasses as needed
00394     for(UMLClassifier *classifier = superclasses.first(); classifier; classifier = superclasses.next())
00395         writeClassifier(classifier, XMLschema);
00396 
00397     // write out any subclasses as needed
00398     for(UMLClassifier *classifier = subclasses.first(); classifier; classifier = subclasses.next())
00399         writeClassifier(classifier, XMLschema);
00400 }
00401 
00402 // these exist for abstract classes only (which become xs:group nodes)
00403 QStringList XMLSchemaWriter::findAttributeGroups (UMLClassifier *c)
00404 {
00405     // we need to look for any class we inherit from. IF these
00406     // have attributes, then we need to notice
00407     QStringList list;
00408     UMLClassifierList superclasses = c->findSuperClassConcepts(); // list of what inherits from us
00409     for(UMLClassifier *classifier = superclasses.first(); classifier; classifier = superclasses.next())
00410     {
00411         if(classifier->getAbstract())
00412         {
00413             // only classes have attributes..
00414             if (!classifier->isInterface()) {
00415                 UMLAttributeList attribs = c->getAttributeList();
00416                 if (attribs.count() > 0)
00417                     list.append(getElementName(classifier)+"AttribGroupType");
00418             }
00419         }
00420     }
00421     return list;
00422 }
00423 
00424 bool XMLSchemaWriter::determineIfHasChildNodes( UMLClassifier *c)
00425 {
00426     UMLObjectList aggList = findChildObjsInAssociations (c, c->getAggregations());
00427     UMLObjectList compList = findChildObjsInAssociations (c, c->getCompositions());
00428     UMLAssociationList associations = c->getSpecificAssocs(Uml::at_Association); // BAD! only way to get "general" associations.
00429     UMLObjectList assocList = findChildObjsInAssociations (c, associations);
00430     return aggList.count() > 0 || compList.count() > 0 || assocList.count() > 0;
00431 }
00432 
00433 void XMLSchemaWriter::writeChildObjsInAssociation (UMLClassifier *c,
00434         UMLAssociationList assoc,
00435         QTextStream &XMLschema)
00436 {
00437 
00438     UMLObjectList list = findChildObjsInAssociations (c, assoc);
00439     for(UMLObject * obj = list.first(); obj; obj = list.next())
00440     {
00441         UMLClassifier * thisClassifier = dynamic_cast<UMLClassifier*>(obj);
00442         if(thisClassifier)
00443             writeClassifier(thisClassifier, XMLschema);
00444     }
00445 }
00446 
00447 bool XMLSchemaWriter::hasBeenWritten(UMLClassifier *c) {
00448     if (writtenClassifiers.contains(c))
00449         return true;
00450     else
00451         return false;
00452 }
00453 
00454 void XMLSchemaWriter::markAsWritten(UMLClassifier *c) {
00455     writtenClassifiers.append(c);
00456 }
00457 
00458 void XMLSchemaWriter::writeAttributeDecls(UMLAttributeList &attribs, QTextStream &XMLschema )
00459 {
00460 
00461     UMLAttribute *at;
00462     for(at=attribs.first(); at; at=attribs.next())
00463     {
00464         writeAttributeDecl(at,XMLschema);
00465     }
00466 
00467 }
00468 
00469 void XMLSchemaWriter::writeAttributeDecl(UMLAttribute *attrib, QTextStream &XMLschema )
00470 {
00471 
00472     QString documentation = attrib->getDoc();
00473     QString typeName = fixTypeName(attrib->getTypeName());
00474     bool isStatic = attrib->getStatic();
00475     QString initialValue = fixInitialStringDeclValue(attrib->getInitialValue(), typeName);
00476 
00477     if(!documentation.isEmpty())
00478         writeComment(documentation, XMLschema);
00479 
00480     XMLschema<<getIndent()<<"<"<<makeSchemaTag("attribute")
00481     <<" name=\""<<cleanName(attrib->getName())<<"\""
00482     <<" type=\""<<typeName<<"\"";
00483 
00484     // default value?
00485     if(!initialValue.isEmpty())
00486     {
00487         // IF its static, then we use "fixed", otherwise, we use "default" decl.
00488         // For the default decl, we _must_ use "optional" decl
00489         if(isStatic)
00490             XMLschema<<" use=\"required\" fixed=\""<<initialValue<<"\"";
00491         else
00492             XMLschema<<" use=\"optional\" default=\""<<initialValue<<"\"";
00493     }
00494 
00495     // finish decl
00496     XMLschema<<"/>"<<m_endl;
00497 
00498 }
00499 
00500 void XMLSchemaWriter::writeAttributeGroupDecl (const QString &elementName, UMLAttributeList &attribs, QTextStream &XMLschema )
00501 {
00502 
00503     if (attribs.count()> 0) {
00504 
00505         // write a little documentation
00506         writeComment("attributes for element "+elementName,XMLschema);
00507 
00508         // open attribute group
00509         XMLschema<<getIndent()<<"<"<<makeSchemaTag("attributeGroup")<<" name=\""<<elementName+"AttribGroupType"<<"\">"<<m_endl;
00510 
00511         m_indentLevel++;
00512 
00513         for( UMLAttribute *at=attribs.first(); at; at=attribs.next())
00514         {
00515             writeAttributeDecl(at,XMLschema);
00516         }
00517 
00518         m_indentLevel--;
00519 
00520         // close attrib group node
00521         XMLschema<<getIndent()<<"</"<<makeSchemaTag("attributeGroup")<<">"<<m_endl;
00522     }
00523 }
00524 
00525 void XMLSchemaWriter::writeComment( const QString &comment, QTextStream &XMLschema )
00526 {
00527     // in the case we have several line comment..
00528     // NOTE: this part of the method has the problem of adopting UNIX newline,
00529     // need to resolve for using with MAC/WinDoze eventually I assume
00530     QString indent = getIndent();
00531     XMLschema<<indent<<"<!-- ";
00532     if (comment.contains(QRegExp("\n"))) {
00533         XMLschema<<m_endl;
00534         QStringList lines = QStringList::split( "\n", comment);
00535         for(uint i= 0; i < lines.count(); i++)
00536             XMLschema<<indent<<"     "<<lines[i]<<m_endl;
00537 
00538         XMLschema<<indent<<"-->"<<m_endl;
00539     } else {
00540         // this should be more fancy in the future, breaking it up into 80 char
00541         // lines so that it doesn't look too bad
00542         XMLschema<<comment<<" -->"<<m_endl;
00543     }
00544 }
00545 
00546 // all that matters here is roleA, the role served by the children of this class
00547 // in any composition or aggregation association. In full associations, I have only
00548 // considered the case of "self" association, so it shouldn't matter if we use role A or
00549 // B to find the child class as long as we don't use BOTH roles. I bet this will fail
00550 // badly for someone using a plain association between 2 different classes. THAT should
00551 // be done, but isnt yet (this is why I have left role b code in for now). -b.t.
00552 bool XMLSchemaWriter::writeAssociationDecls(UMLAssociationList associations,
00553         bool noRoleNameOK, bool didFirstOne, Uml::IDType id, QTextStream &XMLschema)
00554 {
00555 
00556     if( !associations.isEmpty() )
00557     {
00558         bool printRoleA = false, printRoleB = false;
00559 
00560         for(UMLAssociation *a = associations.first(); a; a = associations.next())
00561         {
00562             // it may seem counter intuitive, but you want to insert the role of the
00563             // *other* class into *this* class.
00564 
00565             if (a->getObjectId(Uml::A) == id && a->getVisibility(Uml::B) != Uml::Visibility::Private)
00566                 printRoleB = true;
00567 
00568             if (a->getObjectId(Uml::B) == id && a->getVisibility(Uml::A) != Uml::Visibility::Private)
00569                 printRoleA = true;
00570 
00571             // First: we insert documentaion for association IF it has either role
00572             // AND some documentation (!)
00573             if ((printRoleA || printRoleB) && !(a->getDoc().isEmpty()))
00574                 writeComment(a->getDoc(), XMLschema);
00575 
00576             // opening for sequence
00577             if(!didFirstOne && (printRoleA || printRoleB))
00578             {
00579                 didFirstOne = true;
00580                 XMLschema<<getIndent()<<"<"<<makeSchemaTag("sequence")<<">"<<m_endl;
00581                 m_indentLevel++;
00582             }
00583 
00584             // print RoleB decl
00585             /*
00586             // As mentioned in the header comment for this method: this block of code is
00587             // commented out for now as it will only be needed if/when plain associations
00588             // between different classes are to be treated
00589             if (printRoleB)
00590             {
00591                 UMLClassifier *classifierB = dynamic_cast<UMLClassifier*>(a->getObjectB());
00592                 if (classifierB) {
00593                         // ONLY write out IF there is a rolename given
00594                         // otherwise its not meant to be declared
00595                         if (!a->getRoleNameB().isEmpty() || noRoleNameOK)
00596                                 writeAssociationRoleDecl(classifierB, a->getMultiB(), XMLschema);
00597                 }
00598             }
00599             */
00600 
00601             // print RoleA decl
00602             if (printRoleA)
00603             {
00604                 UMLClassifier *classifierA = dynamic_cast<UMLClassifier*>(a->getObject(Uml::A));
00605                 if (classifierA) {
00606                     // ONLY write out IF there is a rolename given
00607                     // otherwise its not meant to be declared
00608                     if (!a->getRoleName(Uml::A).isEmpty() || noRoleNameOK )
00609                         writeAssociationRoleDecl(classifierA, a->getMulti(Uml::A), XMLschema);
00610                 }
00611             }
00612         }
00613 
00614     }
00615 
00616     return didFirstOne;
00617 }
00618 
00619 UMLObjectList XMLSchemaWriter::findChildObjsInAssociations (UMLClassifier *c, UMLAssociationList associations)
00620 {
00621     Uml::IDType id = c->getID();
00622     UMLObjectList list;
00623     for(UMLAssociation *a = associations.first(); a; a = associations.next())
00624     {
00625         if (a->getObjectId(Uml::A) == id
00626                 && a->getVisibility(Uml::B) != Uml::Visibility::Private
00627                 && !a->getRoleName(Uml::B).isEmpty()
00628            )
00629             list.append(a->getObject(Uml::B));
00630 
00631         if (a->getObjectId(Uml::B) == id
00632                 && a->getVisibility(Uml::A) != Uml::Visibility::Private
00633                 && !a->getRoleName(Uml::A).isEmpty()
00634            )
00635             list.append(a->getObject(Uml::A));
00636     }
00637     return list;
00638 }
00639 
00640 void XMLSchemaWriter::writeAssociationRoleDecl( UMLClassifier *c, const QString &multi, QTextStream &XMLschema)
00641 {
00642 
00643     bool isAbstract = c->getAbstract();
00644     bool isInterface = c->isInterface();
00645 
00646     QString elementName = getElementName(c);
00647     QString doc = c->getDoc();
00648 
00649     if (!doc.isEmpty())
00650         writeComment(doc, XMLschema);
00651 
00652 
00653     // Min/Max Occurs is based on whether it is this a single element
00654     // or a List (maxoccurs>1). One day this will be done correctly with special
00655     // multiplicity object that we don't have to figure out what it means via regex.
00656     QString minOccurs = "0";
00657     QString maxOccurs = "unbounded";
00658     if (multi.isEmpty())
00659     {
00660         // in this case, association will only specify ONE element can exist
00661         // as a child
00662         minOccurs = "1";
00663         maxOccurs = "1";
00664     }
00665     else
00666     {
00667         QStringList values = QStringList::split( QRegExp("[^\\d{1,}|\\*]"), multi);
00668 
00669         // could use some improvement here.. for sequences like "0..1,3..5,10" we
00670         // don't capture the whole "richness" of the multi. Instead we translate it
00671         // now to minOccurs="0" maxOccurs="10"
00672         if (values.count() > 0)
00673         {
00674             // populate both with the actual value as long as our value isnt an asterix
00675             // In that case, use special value (from above)
00676             if(values[0].contains(QRegExp("\\d{1,}")))
00677                 minOccurs = values[0]; // use first number in sequence
00678 
00679             if(values[values.count()-1].contains(QRegExp("\\d{1,}")))
00680                 maxOccurs = values[values.count()-1]; // use only last number in sequence
00681         }
00682     }
00683 
00684     // Now declare the class in the association as an element or group.
00685     //
00686     // In a semi-arbitrary way, we have decided to make abstract classes into
00687     // "groups" and concrete classes into "complexTypes".
00688     //
00689     // This is because about the only thing you can do with an abstract
00690     // class (err. node) is inherit from it with a "concrete" element. Therefore,
00691     // in this manner, we need a group node for abstract classes to lay out the child
00692     // element choices so that the child, concrete class may be plugged into whatever spot
00693     // it parent could go. The tradeoff is that "group" nodes may not be extended, so the
00694     // choices that you lay out here are it (e.g. no more nodes may inherit" from this group)
00695     //
00696     // The flipside for concrete classes is that we want to use them as elements in our document.
00697     // Unfortunately, about all you can do with complexTypes in terms of inheritance, is to
00698     // use these as the basis for a new node type. This is NOT full inheritence because the new
00699     // class (err. element node) wont be able to go into the document where it parent went without
00700     // you heavily editing the schema.
00701     //
00702     // Therefore, IF a group is abstract, but has no inheriting sub-classes, there are no choices, and its nigh
00703     // on pointless to declare it as a group, in this special case, abstract classes get declared
00704     // as complexTypes.
00705     //
00706     // Of course, in OO methodology, you should be able to inherit from
00707     // any class, but schema just don't allow use to have full inheritence using either groups
00708     // or complexTypes. Thus we have taken a middle rode. If someone wants to key me into a
00709     // better way to represent this, I'd be happy to listen. =b.t.
00710     //
00711     // UPDATE: partial solution to the above: as of 13-Mar-2003 we now write BOTH a complexType
00712     //         AND a group declaration for interfaces AND classes which are inherited from.
00713     //
00714     if ((isAbstract || isInterface ) && c->findSubClassConcepts().count() > 0)
00715         XMLschema<<getIndent()<<"<"<<makeSchemaTag("group")
00716         <<" ref=\""<<makePackageTag(getElementGroupTypeName(c))<<"\"";
00717     else
00718         XMLschema<<getIndent()<<"<"<<makeSchemaTag("element")
00719         <<" name=\""<<getElementName(c)<<"\""
00720         <<" type=\""<<makePackageTag(getElementTypeName(c))<<"\"";
00721 
00722     // min/max occurs
00723     if (minOccurs != "1")
00724         XMLschema<<" minOccurs=\""<<minOccurs<<"\"";
00725 
00726     if (maxOccurs != "1")
00727         XMLschema<<" maxOccurs=\""<<maxOccurs<<"\"";
00728 
00729     // tidy up the node
00730     XMLschema<<"/>"<<m_endl;
00731 
00732 }
00733 
00734 // IF the type is "string" we need to declare it as
00735 // the XMLSchema Object "String" (there is no string primative in XMLSchema).
00736 // Same thing again for "bool" to "boolean"
00737 QString XMLSchemaWriter::fixTypeName(const QString& string)
00738 {
00739     //  string.replace(QRegExp("^string$"),schemaNamespaceTag+":string");
00740     //  string.replace(QRegExp("^bool$"),schemaNamespaceTag+":boolean");
00741     return schemaNamespaceTag + ':' + string;
00742 }
00743 
00744 QString XMLSchemaWriter::fixInitialStringDeclValue(QString value, const QString &type)
00745 {
00746     // check for strings only
00747     if (!value.isEmpty() && type == "xs:string") {
00748         if (!value.startsWith("\""))
00749             value.remove(0,1);
00750         if (!value.endsWith("\""))
00751             value.remove(value.length(),1);
00752     }
00753     return value;
00754 }
00755 
00756 QString XMLSchemaWriter::getElementName(UMLClassifier *c)
00757 {
00758     return cleanName(c->getName());
00759 }
00760 
00761 QString XMLSchemaWriter::getElementTypeName(UMLClassifier *c)
00762 {
00763     QString elementName = getElementName(c);
00764     return elementName + "ComplexType";
00765 }
00766 
00767 QString XMLSchemaWriter::getElementGroupTypeName(UMLClassifier *c)
00768 {
00769     QString elementName = getElementName(c);
00770     return elementName + "GroupType";
00771 }
00772 
00773 QString XMLSchemaWriter::makePackageTag (QString tagName) {
00774     tagName.prepend( packageNamespaceTag + ':');
00775     return tagName;
00776 }
00777 
00778 QString XMLSchemaWriter::makeSchemaTag (QString tagName) {
00779     tagName.prepend( schemaNamespaceTag + ':');
00780     return tagName;
00781 }
00782 
00783 const QStringList XMLSchemaWriter::reservedKeywords() const {
00784 
00785     static QStringList keywords;
00786 
00787     if (keywords.isEmpty()) {
00788         keywords << "ATTLIST"
00789         << "CDATA"
00790         << "DOCTYPE"
00791         << "ELEMENT"
00792         << "ENTITIES"
00793         << "ENTITY"
00794         << "ID"
00795         << "IDREF"
00796         << "IDREFS"
00797         << "NMTOKEN"
00798         << "NMTOKENS"
00799         << "NOTATION"
00800         << "PUBLIC"
00801         << "SHORTREF"
00802         << "SYSTEM"
00803         << "USEMAP";
00804     }
00805 
00806     return keywords;
00807 }
00808 
00809 #include "xmlschemawriter.moc"
KDE Logo
This file is part of the documentation for umbrello Version 3.1.0.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Tue Jun 26 08:08:03 2007 by doxygen 1.4.1 written by Dimitri van Heesch, © 1997-2003