umbrello API Documentation

adaimport.cpp

00001 /***************************************************************************
00002  *                                                                         *
00003  *   This program is free software; you can redistribute it and/or modify  *
00004  *   it under the terms of the GNU General Public License as published by  *
00005  *   the Free Software Foundation; either version 2 of the License, or     *
00006  *   (at your option) any later version.                                   *
00007  *                                                                         *
00008  *   copyright (C) 2005-2007                                               *
00009  *   Umbrello UML Modeller Authors <uml-devel@uml.sf.net>                  *
00010  ***************************************************************************/
00011 
00012 // own header
00013 #include "adaimport.h"
00014 
00015 #include <stdio.h>
00016 // qt/kde includes
00017 #include <qregexp.h>
00018 #include <kdebug.h>
00019 // app includes
00020 #include "import_utils.h"
00021 #include "../uml.h"
00022 #include "../umldoc.h"
00023 #include "../package.h"
00024 #include "../folder.h"
00025 #include "../classifier.h"
00026 #include "../enum.h"
00027 #include "../operation.h"
00028 #include "../attribute.h"
00029 #include "../association.h"
00030 
00031 AdaImport::AdaImport() : NativeImportBase("--") {
00032    initVars();
00033 }
00034 
00035 AdaImport::~AdaImport() {
00036 }
00037 
00038 void AdaImport::initVars() {
00039     m_inGenericFormalPart = false;
00040     m_classesDefinedInThisScope.clear();
00041     m_renaming.clear();
00042 }
00043 
00046 QStringList AdaImport::split(const QString& lin) {
00047     QStringList list;
00048     QString listElement;
00049     bool inString = false;
00050     bool seenSpace = false;
00051     QString line = lin.stripWhiteSpace();
00052     uint len = line.length();
00053     for (uint i = 0; i < len; i++) {
00054         const QChar& c = line[i];
00055         if (inString) {
00056             listElement += c;
00057             if (c == '"') {
00058                 if (i < len - 1 && line[i + 1] == '"') {
00059                     i++;     // escaped quotation mark
00060                     continue;
00061                 }
00062                 list.append(listElement);
00063                 listElement = QString();
00064                 inString = false;
00065             }
00066         } else if (c == '"') {
00067             inString = true;
00068             if (!listElement.isEmpty())
00069                 list.append(listElement);
00070             listElement = QString(c);
00071             seenSpace = false;
00072         } else if (c == '\'') {
00073             if (i < len - 2 && line[i + 2] == '\'') {
00074                 // character constant
00075                 if (!listElement.isEmpty())
00076                     list.append(listElement);
00077                 listElement = line.mid(i, 3);
00078                 i += 2;
00079                 list.append(listElement);
00080                 listElement = QString();
00081                 continue;
00082             }
00083             listElement += c;
00084             seenSpace = false;
00085         } else if (c.isSpace()) {
00086             if (seenSpace)
00087                 continue;
00088             seenSpace = true;
00089             if (!listElement.isEmpty()) {
00090                 list.append(listElement);
00091                 listElement = QString();
00092             }
00093         } else {
00094             listElement += c;
00095             seenSpace = false;
00096         }
00097     }
00098     if (!listElement.isEmpty())
00099         list.append(listElement);
00100     return list;
00101 }
00102 
00103 void AdaImport::fillSource(const QString& word) {
00104     QString lexeme;
00105     const uint len = word.length();
00106     for (uint i = 0; i < len; i++) {
00107         QChar c = word[i];
00108         if (c.isLetterOrNumber() || c == '_' || c == '.' || c == '#') {
00109             lexeme += c;
00110         } else {
00111             if (!lexeme.isEmpty()) {
00112                 m_source.append(lexeme);
00113                 lexeme = QString();
00114             }
00115             if (c == ':' && word[i + 1] == '=') {
00116                 m_source.append(":=");
00117                 i++;
00118             } else {
00119                 m_source.append(QString(c));
00120             }
00121         }
00122     }
00123     if (!lexeme.isEmpty())
00124         m_source.append(lexeme);
00125 }
00126 
00127 QString AdaImport::expand(const QString& name) {
00128     QRegExp pfxRegExp("^(\\w+)\\.");
00129     pfxRegExp.setCaseSensitive(false);
00130     int pos = pfxRegExp.search(name);
00131     if (pos == -1)
00132         return name;
00133     QString result = name;
00134     QString pfx = pfxRegExp.cap(1);
00135     if (m_renaming.contains(pfx)) {
00136         result.remove(pfxRegExp);
00137         result.prepend(m_renaming[pfx] + '.');
00138     }
00139     return result;
00140 }
00141 
00142 void AdaImport::parseStems(const QStringList& stems) {
00143     if (stems.isEmpty())
00144         return;
00145     QString base = stems.first();
00146     uint i = 0;
00147     while (1) {
00148         QString filename = base + ".ads";
00149         if (! m_parsedFiles.contains(filename)) {
00150             // Save current m_source and m_srcIndex.
00151             QStringList source(m_source);
00152             uint srcIndex = m_srcIndex;
00153             m_source.clear();
00154             parseFile(filename);
00155             // Restore m_source and m_srcIndex.
00156             m_source = source;
00157             m_srcIndex = srcIndex;
00158             // Also reset m_currentAccess.
00159             // CHECK: need to reset more stuff?
00160             m_currentAccess = Uml::Visibility::Public;
00161         }
00162         if (++i >= stems.count())
00163             break;
00164         base += '-' + stems[i];
00165     }
00166 }
00167 
00168 bool AdaImport::parseStmt() {
00169     const uint srcLength = m_source.count();
00170     QString keyword = m_source[m_srcIndex];
00171     UMLDoc *umldoc = UMLApp::app()->getDocument();
00172     //kDebug() << '"' << keyword << '"' << endl;
00173     if (keyword == "with") {
00174         if (m_inGenericFormalPart) {
00175             // mapping of generic formal subprograms or packages is not yet implemented
00176             return false;
00177         }
00178         while (++m_srcIndex < srcLength && m_source[m_srcIndex] != ";") {
00179             QStringList components = QStringList::split(".", m_source[m_srcIndex].lower());
00180             const QString& prefix = components.first();
00181             if (prefix == "system" || prefix == "ada" || prefix == "gnat" ||
00182                 prefix == "interfaces" || prefix == "text_io" ||
00183                 prefix == "unchecked_conversion" ||
00184                 prefix == "unchecked_deallocation") {
00185                 if (advance() != ",")
00186                     break;
00187                 continue;
00188             }
00189             parseStems(components);
00190             if (advance() != ",")
00191                 break;
00192         }
00193         return true;
00194     }
00195     if (keyword == "generic") {
00196         m_inGenericFormalPart = true;
00197         return true;
00198     }
00199     if (keyword == "package") {
00200         const QString& name = advance();
00201         QStringList parentPkgs = QStringList::split(".", name.lower());
00202         parentPkgs.pop_back();  // exclude the current package
00203         parseStems(parentPkgs);
00204         UMLObject *ns = NULL;
00205         if (advance() == "is") {
00206             ns = Import_Utils::createUMLObject(Uml::ot_Package, name,
00207                                                m_scope[m_scopeIndex], m_comment);
00208             if (m_source[m_srcIndex + 1] == "new") {
00209                 m_srcIndex++;
00210                 QString pkgName = advance();
00211                 UMLObject *gp = Import_Utils::createUMLObject(Uml::ot_Package, pkgName,
00212                                                               m_scope[m_scopeIndex]);
00213                 gp->setStereotype("generic");
00214                 // Add binding from instantiator to instantiatee
00215                 UMLAssociation *assoc = new UMLAssociation(Uml::at_Dependency, ns, gp);
00216                 assoc->setUMLPackage(umldoc->getRootFolder(Uml::mt_Logical));
00217                 assoc->setStereotype("bind");
00218                 // Work around missing display of stereotype in AssociationWidget:
00219                 assoc->setName(assoc->getStereotype(true));
00220                 umldoc->addAssociation(assoc);
00221                 skipStmt();
00222             } else {
00223                 m_scope[++m_scopeIndex] = static_cast<UMLPackage*>(ns);
00224             }
00225         } else if (m_source[m_srcIndex] == "renames") {
00226             m_renaming[name] = advance();
00227         } else {
00228             kError() << "AdaImport::parseStmt: unexpected: " << m_source[m_srcIndex] << endl;
00229             skipStmt("is");
00230         }
00231         if (m_inGenericFormalPart) {
00232             ns->setStereotype("generic");
00233             m_inGenericFormalPart = false;
00234         }
00235         return true;
00236     }
00237     if (m_inGenericFormalPart)
00238         return false;  // skip generic formal parameter (not yet implemented)
00239     if (keyword == "subtype") {
00240         QString name = advance();
00241         advance();  // "is"
00242         QString base = expand(advance());
00243         base.remove("Standard.", false);
00244         UMLObject *type = umldoc->findUMLObject(base, Uml::ot_UMLObject, m_scope[m_scopeIndex]);
00245         if (type == NULL) {
00246             type = Import_Utils::createUMLObject(Uml::ot_Datatype, base, m_scope[m_scopeIndex]);
00247         }
00248         UMLObject *subtype = Import_Utils::createUMLObject(type->getBaseType(), name,
00249                                                            m_scope[m_scopeIndex], m_comment);
00250         UMLAssociation *assoc = new UMLAssociation(Uml::at_Dependency, subtype, type);
00251         assoc->setUMLPackage(umldoc->getRootFolder(Uml::mt_Logical));
00252         assoc->setStereotype("subtype");
00253         // Work around missing display of stereotype in AssociationWidget:
00254         assoc->setName(assoc->getStereotype(true));
00255         umldoc->addAssociation(assoc);
00256         skipStmt();
00257         return true;
00258     }
00259     if (keyword == "type") {
00260         QString name = advance();
00261         QString next = advance();
00262         if (next == "(") {
00263             kDebug() << "AdaImport::parseStmt(" << name << "): "
00264                 << "discriminant handling is not yet implemented" << endl;
00265             // @todo Find out how to map discriminated record to UML.
00266             //       For now, we just create a pro forma empty record.
00267             Import_Utils::createUMLObject(Uml::ot_Class, name, m_scope[m_scopeIndex],
00268                                           m_comment, "record");
00269             skipStmt("end");
00270             if ((next = advance()) == "case")
00271                 m_srcIndex += 2;  // skip "case" ";"
00272             skipStmt();
00273             return true;
00274         }
00275         if (next == ";") {
00276             // forward declaration
00277             Import_Utils::createUMLObject(Uml::ot_Class, name, m_scope[m_scopeIndex],
00278                                           m_comment);
00279             return true;
00280         }
00281         if (next != "is") {
00282             kError() << "AdaImport::parseStmt: expecting \"is\"" << endl;
00283             return false;
00284         }
00285         next = advance();
00286         if (next == "(") {
00287             // enum type
00288             UMLObject *ns = Import_Utils::createUMLObject(Uml::ot_Enum,
00289                             name, m_scope[m_scopeIndex], m_comment);
00290             UMLEnum *enumType = static_cast<UMLEnum*>(ns);
00291             while ((next = advance()) != ")") {
00292                 Import_Utils::addEnumLiteral(enumType, next, m_comment);
00293                 m_comment = QString();
00294                 if (advance() != ",")
00295                     break;
00296             }
00297             skipStmt();
00298             return true;
00299         }
00300         bool isTaggedType = false;
00301         if (next == "abstract") {
00302             m_isAbstract = true;
00303             next = advance();
00304         }
00305         if (next == "tagged") {
00306             isTaggedType = true;
00307             next = advance();
00308         }
00309         if (next == "limited" ||
00310             next == "task" ||
00311             next == "protected" ||
00312             next == "synchronized") {
00313             next = advance();  // we can't (yet?) represent that
00314         }
00315         if (next == "private" ||
00316             next == "interface" ||
00317             next == "record" ||
00318             (next == "null" &&
00319              m_source[m_srcIndex+1] == "record")) {
00320             Uml::Object_Type t = (next == "interface" ? Uml::ot_Interface : Uml::ot_Class);
00321             UMLObject *ns = Import_Utils::createUMLObject(t, name, m_scope[m_scopeIndex], m_comment);
00322             if (t == Uml::ot_Interface) {
00323                 while ((next = advance()) == "and") {
00324                     UMLClassifier *klass = static_cast<UMLClassifier*>(ns);
00325                     QString base = expand(advance());
00326                     UMLObject *p = Import_Utils::createUMLObject(Uml::ot_Interface, base, m_scope[m_scopeIndex]);
00327                     UMLClassifier *parent = static_cast<UMLClassifier*>(p);
00328                     Import_Utils::createGeneralization(klass, parent);
00329                 }
00330             } else {
00331                 ns->setAbstract(m_isAbstract);
00332             }
00333             m_isAbstract = false;
00334             if (isTaggedType) {
00335                 if (! m_classesDefinedInThisScope.contains(ns))
00336                     m_classesDefinedInThisScope.append(ns);
00337             } else {
00338                 ns->setStereotype("record");
00339             }
00340             if (next == "record")
00341                 m_klass = static_cast<UMLClassifier*>(ns);
00342             else
00343                 skipStmt();
00344             return true;
00345         }
00346         if (next == "new") {
00347             QString base = expand(advance());
00348             QStringList baseInterfaces;
00349             while ((next = advance()) == "and") {
00350                 baseInterfaces.append(expand(advance()));
00351             }
00352             const bool isExtension = (next == "with");
00353             Uml::Object_Type t;
00354             if (isExtension || m_isAbstract) {
00355                 t = Uml::ot_Class;
00356             } else {
00357                 base.remove("Standard.", false);
00358                 UMLObject *known = umldoc->findUMLObject(base, Uml::ot_UMLObject, m_scope[m_scopeIndex]);
00359                 t = (known ? known->getBaseType() : Uml::ot_Datatype);
00360             }
00361             UMLObject *ns = Import_Utils::createUMLObject(t, base, NULL);
00362             UMLClassifier *parent = static_cast<UMLClassifier*>(ns);
00363             ns = Import_Utils::createUMLObject(t, name, m_scope[m_scopeIndex], m_comment);
00364             if (isExtension) {
00365                 next = advance();
00366                 if (next == "null" || next == "record") {
00367                     UMLClassifier *klass = static_cast<UMLClassifier*>(ns);
00368                     Import_Utils::createGeneralization(klass, parent);
00369                     if (next == "record") {
00370                         // Set the m_klass for attributes.
00371                         m_klass = klass;
00372                     }
00373                     if (baseInterfaces.count()) {
00374                         t = Uml::ot_Interface;
00375                         QStringList::Iterator end(baseInterfaces.end());
00376                         for (QStringList::Iterator bi(baseInterfaces.begin()); bi != end; ++bi) {
00377                              ns = Import_Utils::createUMLObject(t, *bi, m_scope[m_scopeIndex]);
00378                              parent = static_cast<UMLClassifier*>(ns);
00379                              Import_Utils::createGeneralization(klass, parent);
00380                         }
00381                     }
00382                 }
00383             }
00384             skipStmt();
00385             return true;
00386         }
00387         // Datatypes: TO BE DONE
00388         return false;
00389     }
00390     if (keyword == "private") {
00391         m_currentAccess = Uml::Visibility::Private;
00392         return true;
00393     }
00394     if (keyword == "end") {
00395         if (m_klass) {
00396             if (advance() != "record") {
00397                 kError() << "end: expecting \"record\" at "
00398                           << m_source[m_srcIndex] << endl;
00399             }
00400             m_klass = NULL;
00401         } else if (m_scopeIndex) {
00402             if (advance() != ";") {
00403                 QString scopeName = m_scope[m_scopeIndex]->getFullyQualifiedName();
00404                 if (scopeName.lower() != m_source[m_srcIndex].lower())
00405                     kError() << "end: expecting " << scopeName << ", found "
00406                               << m_source[m_srcIndex] << endl;
00407             }
00408             m_scopeIndex--;
00409             m_currentAccess = Uml::Visibility::Public;   // @todo make a stack for this
00410         } else {
00411             kError() << "importAda: too many \"end\"" << endl;
00412         }
00413         skipStmt();
00414         return true;
00415     }
00416     // subprogram
00417     if (keyword == "not")
00418         keyword = advance();
00419     if (keyword == "overriding")
00420         keyword = advance();
00421     if (keyword == "function" || keyword == "procedure") {
00422         const QString& name = advance();
00423         QString returnType;
00424         if (advance() != "(") {
00425             // Unlike an Ada package, a UML package does not support
00426             // subprograms.
00427             // In order to map those, we would need to create a UML
00428             // class with stereotype <<utility>> for the Ada package.
00429             kDebug() << "ignoring parameterless " << keyword << " " << name << endl;
00430             skipStmt();
00431             return true;
00432         }
00433         UMLClassifier *klass = NULL;
00434         UMLOperation *op = NULL;
00435         const uint MAX_PARNAMES = 16;
00436         while (m_srcIndex < srcLength && m_source[m_srcIndex] != ")") {
00437             QString parName[MAX_PARNAMES];
00438             uint parNameCount = 0;
00439             do {
00440                 if (parNameCount >= MAX_PARNAMES) {
00441                     kError() << "MAX_PARNAMES is exceeded at " << name << endl;
00442                     break;
00443                 }
00444                 parName[parNameCount++] = advance();
00445             } while (advance() == ",");
00446             if (m_source[m_srcIndex] != ":") {
00447                 kError() << "importAda: expecting ':'" << endl;
00448                 skipStmt();
00449                 break;
00450             }
00451             const QString &direction = advance();
00452             QString typeName;
00453             Uml::Parameter_Direction dir = Uml::pd_In;
00454             if (direction == "access") {
00455                 // Oops, we have to improvise here because there
00456                 // is no such thing as "access" in UML.
00457                 // So we use the next best thing, "inout".
00458                 // Better ideas, anyone?
00459                 dir = Uml::pd_InOut;
00460                 typeName = advance();
00461             } else if (direction == "in") {
00462                 if (m_source[m_srcIndex + 1] == "out") {
00463                     dir = Uml::pd_InOut;
00464                     m_srcIndex++;
00465                 }
00466                 typeName = advance();
00467             } else if (direction == "out") {
00468                 dir = Uml::pd_Out;
00469                 typeName = advance();
00470             } else {
00471                 typeName = direction;  // In Ada, the default direction is "in"
00472             }
00473             typeName.remove("Standard.", false);
00474             typeName = expand(typeName);
00475             if (op == NULL) {
00476                 // In Ada, the first parameter indicates the class.
00477                 UMLObject *type = Import_Utils::createUMLObject(Uml::ot_Class, typeName, m_scope[m_scopeIndex]);
00478                 Uml::Object_Type t = type->getBaseType();
00479                 if ((t != Uml::ot_Interface &&
00480                      (t != Uml::ot_Class || type->getStereotype() == "record")) ||
00481                     !m_classesDefinedInThisScope.contains(type)) {
00482                     // Not an instance bound method - we cannot represent it.
00483                     skipStmt(")");
00484                     break;
00485                 }
00486                 klass = static_cast<UMLClassifier*>(type);
00487                 op = Import_Utils::makeOperation(klass, name);
00488                 // The controlling parameter is suppressed.
00489                 parNameCount--;
00490                 if (parNameCount) {
00491                     for (uint i = 0; i < parNameCount; i++)
00492                         parName[i] = parName[i + 1];
00493                 }
00494             }
00495             for (uint i = 0; i < parNameCount; i++) {
00496                 UMLAttribute *att = Import_Utils::addMethodParameter(op, typeName, parName[i]);
00497                 att->setParmKind(dir);
00498             }
00499             if (advance() != ";")
00500                 break;
00501         }
00502         if (keyword == "function") {
00503             if (advance() != "return") {
00504                 if (klass)
00505                     kError() << "importAda: expecting \"return\" at function "
00506                         << name << endl;
00507                 return false;
00508             }
00509             returnType = expand(advance());
00510             returnType.remove("Standard.", false);
00511         }
00512         bool isAbstract = false;
00513         if (advance() == "is" && advance() == "abstract")
00514             isAbstract = true;
00515         if (klass != NULL && op != NULL)
00516             Import_Utils::insertMethod(klass, op, m_currentAccess, returnType,
00517                                        false, isAbstract, false, false, m_comment);
00518         skipStmt();
00519         return true;
00520     }
00521     if (keyword == "task" || keyword == "protected") {
00522         // Can task and protected objects/types be mapped to UML?
00523         bool isType = false;
00524         QString name = advance();
00525         if (name == "type") {
00526             isType = true;
00527             name = advance();
00528         }
00529         QString next = advance();
00530         if (next == "(") {
00531             skipStmt(")");  // skip discriminant
00532             next = advance();
00533         }
00534         if (next == "is")
00535             skipStmt("end");
00536         skipStmt();
00537         return true;
00538     }
00539     if (keyword == "for") {    // rep spec
00540         QString typeName = advance();
00541         QString next = advance();
00542         if (next == "'") {
00543             advance();  // skip qualifier
00544             next = advance();
00545         }
00546         if (next == "use") {
00547             if (advance() == "record")
00548                 skipStmt("end");
00549         } else {
00550             kError() << "importAda: expecting \"use\" at rep spec of "
00551                       << typeName << endl;
00552         }
00553         skipStmt();
00554         return true;
00555     }
00556     // At this point we're only interested in attribute declarations.
00557     if (m_klass == NULL || keyword == "null") {
00558         skipStmt();
00559         return true;
00560     }
00561     const QString& name = keyword;
00562     if (advance() != ":") {
00563         kError() << "adaImport: expecting \":\" at " << name << " "
00564                   << m_source[m_srcIndex] << endl;
00565         skipStmt();
00566         return true;
00567     }
00568     QString nextToken = advance();
00569     if (nextToken == "aliased")
00570         nextToken = advance();
00571     QString typeName = expand(nextToken);
00572     QString initialValue;
00573     if (advance() == ":=") {
00574         initialValue = advance();
00575         QString token;
00576         while ((token = advance()) != ";") {
00577             initialValue.append(' ' + token);
00578         }
00579     }
00580     UMLObject *o = Import_Utils::insertAttribute(m_klass, m_currentAccess, name,
00581                                                  typeName, m_comment);
00582     UMLAttribute *attr = static_cast<UMLAttribute*>(o);
00583     attr->setInitialValue(initialValue);
00584     skipStmt();
00585     return true;
00586 }
00587 
00588 
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:07:54 2007 by doxygen 1.4.1 written by Dimitri van Heesch, © 1997-2003