summaryrefslogtreecommitdiffstats
path: root/umbrello/umbrello/codeimport/adaimport.cpp
diff options
context:
space:
mode:
authortoma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2009-11-25 17:56:58 +0000
committertoma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2009-11-25 17:56:58 +0000
commitbd9e6617827818fd043452c08c606f07b78014a0 (patch)
tree425bb4c3168f9c02f10150f235d2cb998dcc6108 /umbrello/umbrello/codeimport/adaimport.cpp
downloadtdesdk-bd9e6617827818fd043452c08c606f07b78014a0.tar.gz
tdesdk-bd9e6617827818fd043452c08c606f07b78014a0.zip
Copy the KDE 3.5 branch to branches/trinity for new KDE 3.5 features.
BUG:215923 git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdesdk@1054174 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'umbrello/umbrello/codeimport/adaimport.cpp')
-rw-r--r--umbrello/umbrello/codeimport/adaimport.cpp588
1 files changed, 588 insertions, 0 deletions
diff --git a/umbrello/umbrello/codeimport/adaimport.cpp b/umbrello/umbrello/codeimport/adaimport.cpp
new file mode 100644
index 00000000..54ac3907
--- /dev/null
+++ b/umbrello/umbrello/codeimport/adaimport.cpp
@@ -0,0 +1,588 @@
+/***************************************************************************
+ * *
+ * 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) 2005-2007 *
+ * Umbrello UML Modeller Authors <uml-devel@uml.sf.net> *
+ ***************************************************************************/
+
+// own header
+#include "adaimport.h"
+
+#include <stdio.h>
+// qt/kde includes
+#include <qregexp.h>
+#include <kdebug.h>
+// app includes
+#include "import_utils.h"
+#include "../uml.h"
+#include "../umldoc.h"
+#include "../package.h"
+#include "../folder.h"
+#include "../classifier.h"
+#include "../enum.h"
+#include "../operation.h"
+#include "../attribute.h"
+#include "../association.h"
+
+AdaImport::AdaImport() : NativeImportBase("--") {
+ initVars();
+}
+
+AdaImport::~AdaImport() {
+}
+
+void AdaImport::initVars() {
+ m_inGenericFormalPart = false;
+ m_classesDefinedInThisScope.clear();
+ m_renaming.clear();
+}
+
+/// Split the line so that a string is returned as a single element of the list,
+/// when not in a string then split at white space.
+QStringList AdaImport::split(const QString& lin) {
+ QStringList list;
+ QString listElement;
+ bool inString = false;
+ bool seenSpace = false;
+ QString line = lin.stripWhiteSpace();
+ uint len = line.length();
+ for (uint i = 0; i < len; i++) {
+ const QChar& c = line[i];
+ if (inString) {
+ listElement += c;
+ if (c == '"') {
+ if (i < len - 1 && line[i + 1] == '"') {
+ i++; // escaped quotation mark
+ continue;
+ }
+ list.append(listElement);
+ listElement = QString();
+ inString = false;
+ }
+ } else if (c == '"') {
+ inString = true;
+ if (!listElement.isEmpty())
+ list.append(listElement);
+ listElement = QString(c);
+ seenSpace = false;
+ } else if (c == '\'') {
+ if (i < len - 2 && line[i + 2] == '\'') {
+ // character constant
+ if (!listElement.isEmpty())
+ list.append(listElement);
+ listElement = line.mid(i, 3);
+ i += 2;
+ list.append(listElement);
+ listElement = QString();
+ continue;
+ }
+ listElement += c;
+ seenSpace = false;
+ } else if (c.isSpace()) {
+ if (seenSpace)
+ continue;
+ seenSpace = true;
+ if (!listElement.isEmpty()) {
+ list.append(listElement);
+ listElement = QString();
+ }
+ } else {
+ listElement += c;
+ seenSpace = false;
+ }
+ }
+ if (!listElement.isEmpty())
+ list.append(listElement);
+ return list;
+}
+
+void AdaImport::fillSource(const QString& word) {
+ QString lexeme;
+ const uint len = word.length();
+ for (uint i = 0; i < len; i++) {
+ QChar c = word[i];
+ if (c.isLetterOrNumber() || c == '_' || c == '.' || c == '#') {
+ lexeme += c;
+ } else {
+ if (!lexeme.isEmpty()) {
+ m_source.append(lexeme);
+ lexeme = QString();
+ }
+ if (c == ':' && word[i + 1] == '=') {
+ m_source.append(":=");
+ i++;
+ } else {
+ m_source.append(QString(c));
+ }
+ }
+ }
+ if (!lexeme.isEmpty())
+ m_source.append(lexeme);
+}
+
+QString AdaImport::expand(const QString& name) {
+ QRegExp pfxRegExp("^(\\w+)\\.");
+ pfxRegExp.setCaseSensitive(false);
+ int pos = pfxRegExp.search(name);
+ if (pos == -1)
+ return name;
+ QString result = name;
+ QString pfx = pfxRegExp.cap(1);
+ if (m_renaming.contains(pfx)) {
+ result.remove(pfxRegExp);
+ result.prepend(m_renaming[pfx] + '.');
+ }
+ return result;
+}
+
+void AdaImport::parseStems(const QStringList& stems) {
+ if (stems.isEmpty())
+ return;
+ QString base = stems.first();
+ uint i = 0;
+ while (1) {
+ QString filename = base + ".ads";
+ if (! m_parsedFiles.contains(filename)) {
+ // Save current m_source and m_srcIndex.
+ QStringList source(m_source);
+ uint srcIndex = m_srcIndex;
+ m_source.clear();
+ parseFile(filename);
+ // Restore m_source and m_srcIndex.
+ m_source = source;
+ m_srcIndex = srcIndex;
+ // Also reset m_currentAccess.
+ // CHECK: need to reset more stuff?
+ m_currentAccess = Uml::Visibility::Public;
+ }
+ if (++i >= stems.count())
+ break;
+ base += '-' + stems[i];
+ }
+}
+
+bool AdaImport::parseStmt() {
+ const uint srcLength = m_source.count();
+ QString keyword = m_source[m_srcIndex];
+ UMLDoc *umldoc = UMLApp::app()->getDocument();
+ //kDebug() << '"' << keyword << '"' << endl;
+ if (keyword == "with") {
+ if (m_inGenericFormalPart) {
+ // mapping of generic formal subprograms or packages is not yet implemented
+ return false;
+ }
+ while (++m_srcIndex < srcLength && m_source[m_srcIndex] != ";") {
+ QStringList components = QStringList::split(".", m_source[m_srcIndex].lower());
+ const QString& prefix = components.first();
+ if (prefix == "system" || prefix == "ada" || prefix == "gnat" ||
+ prefix == "interfaces" || prefix == "text_io" ||
+ prefix == "unchecked_conversion" ||
+ prefix == "unchecked_deallocation") {
+ if (advance() != ",")
+ break;
+ continue;
+ }
+ parseStems(components);
+ if (advance() != ",")
+ break;
+ }
+ return true;
+ }
+ if (keyword == "generic") {
+ m_inGenericFormalPart = true;
+ return true;
+ }
+ if (keyword == "package") {
+ const QString& name = advance();
+ QStringList parentPkgs = QStringList::split(".", name.lower());
+ parentPkgs.pop_back(); // exclude the current package
+ parseStems(parentPkgs);
+ UMLObject *ns = NULL;
+ if (advance() == "is") {
+ ns = Import_Utils::createUMLObject(Uml::ot_Package, name,
+ m_scope[m_scopeIndex], m_comment);
+ if (m_source[m_srcIndex + 1] == "new") {
+ m_srcIndex++;
+ QString pkgName = advance();
+ UMLObject *gp = Import_Utils::createUMLObject(Uml::ot_Package, pkgName,
+ m_scope[m_scopeIndex]);
+ gp->setStereotype("generic");
+ // Add binding from instantiator to instantiatee
+ UMLAssociation *assoc = new UMLAssociation(Uml::at_Dependency, ns, gp);
+ assoc->setUMLPackage(umldoc->getRootFolder(Uml::mt_Logical));
+ assoc->setStereotype("bind");
+ // Work around missing display of stereotype in AssociationWidget:
+ assoc->setName(assoc->getStereotype(true));
+ umldoc->addAssociation(assoc);
+ skipStmt();
+ } else {
+ m_scope[++m_scopeIndex] = static_cast<UMLPackage*>(ns);
+ }
+ } else if (m_source[m_srcIndex] == "renames") {
+ m_renaming[name] = advance();
+ } else {
+ kError() << "AdaImport::parseStmt: unexpected: " << m_source[m_srcIndex] << endl;
+ skipStmt("is");
+ }
+ if (m_inGenericFormalPart) {
+ ns->setStereotype("generic");
+ m_inGenericFormalPart = false;
+ }
+ return true;
+ }
+ if (m_inGenericFormalPart)
+ return false; // skip generic formal parameter (not yet implemented)
+ if (keyword == "subtype") {
+ QString name = advance();
+ advance(); // "is"
+ QString base = expand(advance());
+ base.remove("Standard.", false);
+ UMLObject *type = umldoc->findUMLObject(base, Uml::ot_UMLObject, m_scope[m_scopeIndex]);
+ if (type == NULL) {
+ type = Import_Utils::createUMLObject(Uml::ot_Datatype, base, m_scope[m_scopeIndex]);
+ }
+ UMLObject *subtype = Import_Utils::createUMLObject(type->getBaseType(), name,
+ m_scope[m_scopeIndex], m_comment);
+ UMLAssociation *assoc = new UMLAssociation(Uml::at_Dependency, subtype, type);
+ assoc->setUMLPackage(umldoc->getRootFolder(Uml::mt_Logical));
+ assoc->setStereotype("subtype");
+ // Work around missing display of stereotype in AssociationWidget:
+ assoc->setName(assoc->getStereotype(true));
+ umldoc->addAssociation(assoc);
+ skipStmt();
+ return true;
+ }
+ if (keyword == "type") {
+ QString name = advance();
+ QString next = advance();
+ if (next == "(") {
+ kDebug() << "AdaImport::parseStmt(" << name << "): "
+ << "discriminant handling is not yet implemented" << endl;
+ // @todo Find out how to map discriminated record to UML.
+ // For now, we just create a pro forma empty record.
+ Import_Utils::createUMLObject(Uml::ot_Class, name, m_scope[m_scopeIndex],
+ m_comment, "record");
+ skipStmt("end");
+ if ((next = advance()) == "case")
+ m_srcIndex += 2; // skip "case" ";"
+ skipStmt();
+ return true;
+ }
+ if (next == ";") {
+ // forward declaration
+ Import_Utils::createUMLObject(Uml::ot_Class, name, m_scope[m_scopeIndex],
+ m_comment);
+ return true;
+ }
+ if (next != "is") {
+ kError() << "AdaImport::parseStmt: expecting \"is\"" << endl;
+ return false;
+ }
+ next = advance();
+ if (next == "(") {
+ // enum type
+ UMLObject *ns = Import_Utils::createUMLObject(Uml::ot_Enum,
+ name, m_scope[m_scopeIndex], m_comment);
+ UMLEnum *enumType = static_cast<UMLEnum*>(ns);
+ while ((next = advance()) != ")") {
+ Import_Utils::addEnumLiteral(enumType, next, m_comment);
+ m_comment = QString();
+ if (advance() != ",")
+ break;
+ }
+ skipStmt();
+ return true;
+ }
+ bool isTaggedType = false;
+ if (next == "abstract") {
+ m_isAbstract = true;
+ next = advance();
+ }
+ if (next == "tagged") {
+ isTaggedType = true;
+ next = advance();
+ }
+ if (next == "limited" ||
+ next == "task" ||
+ next == "protected" ||
+ next == "synchronized") {
+ next = advance(); // we can't (yet?) represent that
+ }
+ if (next == "private" ||
+ next == "interface" ||
+ next == "record" ||
+ (next == "null" &&
+ m_source[m_srcIndex+1] == "record")) {
+ Uml::Object_Type t = (next == "interface" ? Uml::ot_Interface : Uml::ot_Class);
+ UMLObject *ns = Import_Utils::createUMLObject(t, name, m_scope[m_scopeIndex], m_comment);
+ if (t == Uml::ot_Interface) {
+ while ((next = advance()) == "and") {
+ UMLClassifier *klass = static_cast<UMLClassifier*>(ns);
+ QString base = expand(advance());
+ UMLObject *p = Import_Utils::createUMLObject(Uml::ot_Interface, base, m_scope[m_scopeIndex]);
+ UMLClassifier *parent = static_cast<UMLClassifier*>(p);
+ Import_Utils::createGeneralization(klass, parent);
+ }
+ } else {
+ ns->setAbstract(m_isAbstract);
+ }
+ m_isAbstract = false;
+ if (isTaggedType) {
+ if (! m_classesDefinedInThisScope.contains(ns))
+ m_classesDefinedInThisScope.append(ns);
+ } else {
+ ns->setStereotype("record");
+ }
+ if (next == "record")
+ m_klass = static_cast<UMLClassifier*>(ns);
+ else
+ skipStmt();
+ return true;
+ }
+ if (next == "new") {
+ QString base = expand(advance());
+ QStringList baseInterfaces;
+ while ((next = advance()) == "and") {
+ baseInterfaces.append(expand(advance()));
+ }
+ const bool isExtension = (next == "with");
+ Uml::Object_Type t;
+ if (isExtension || m_isAbstract) {
+ t = Uml::ot_Class;
+ } else {
+ base.remove("Standard.", false);
+ UMLObject *known = umldoc->findUMLObject(base, Uml::ot_UMLObject, m_scope[m_scopeIndex]);
+ t = (known ? known->getBaseType() : Uml::ot_Datatype);
+ }
+ UMLObject *ns = Import_Utils::createUMLObject(t, base, NULL);
+ UMLClassifier *parent = static_cast<UMLClassifier*>(ns);
+ ns = Import_Utils::createUMLObject(t, name, m_scope[m_scopeIndex], m_comment);
+ if (isExtension) {
+ next = advance();
+ if (next == "null" || next == "record") {
+ UMLClassifier *klass = static_cast<UMLClassifier*>(ns);
+ Import_Utils::createGeneralization(klass, parent);
+ if (next == "record") {
+ // Set the m_klass for attributes.
+ m_klass = klass;
+ }
+ if (baseInterfaces.count()) {
+ t = Uml::ot_Interface;
+ QStringList::Iterator end(baseInterfaces.end());
+ for (QStringList::Iterator bi(baseInterfaces.begin()); bi != end; ++bi) {
+ ns = Import_Utils::createUMLObject(t, *bi, m_scope[m_scopeIndex]);
+ parent = static_cast<UMLClassifier*>(ns);
+ Import_Utils::createGeneralization(klass, parent);
+ }
+ }
+ }
+ }
+ skipStmt();
+ return true;
+ }
+ // Datatypes: TO BE DONE
+ return false;
+ }
+ if (keyword == "private") {
+ m_currentAccess = Uml::Visibility::Private;
+ return true;
+ }
+ if (keyword == "end") {
+ if (m_klass) {
+ if (advance() != "record") {
+ kError() << "end: expecting \"record\" at "
+ << m_source[m_srcIndex] << endl;
+ }
+ m_klass = NULL;
+ } else if (m_scopeIndex) {
+ if (advance() != ";") {
+ QString scopeName = m_scope[m_scopeIndex]->getFullyQualifiedName();
+ if (scopeName.lower() != m_source[m_srcIndex].lower())
+ kError() << "end: expecting " << scopeName << ", found "
+ << m_source[m_srcIndex] << endl;
+ }
+ m_scopeIndex--;
+ m_currentAccess = Uml::Visibility::Public; // @todo make a stack for this
+ } else {
+ kError() << "importAda: too many \"end\"" << endl;
+ }
+ skipStmt();
+ return true;
+ }
+ // subprogram
+ if (keyword == "not")
+ keyword = advance();
+ if (keyword == "overriding")
+ keyword = advance();
+ if (keyword == "function" || keyword == "procedure") {
+ const QString& name = advance();
+ QString returnType;
+ if (advance() != "(") {
+ // Unlike an Ada package, a UML package does not support
+ // subprograms.
+ // In order to map those, we would need to create a UML
+ // class with stereotype <<utility>> for the Ada package.
+ kDebug() << "ignoring parameterless " << keyword << " " << name << endl;
+ skipStmt();
+ return true;
+ }
+ UMLClassifier *klass = NULL;
+ UMLOperation *op = NULL;
+ const uint MAX_PARNAMES = 16;
+ while (m_srcIndex < srcLength && m_source[m_srcIndex] != ")") {
+ QString parName[MAX_PARNAMES];
+ uint parNameCount = 0;
+ do {
+ if (parNameCount >= MAX_PARNAMES) {
+ kError() << "MAX_PARNAMES is exceeded at " << name << endl;
+ break;
+ }
+ parName[parNameCount++] = advance();
+ } while (advance() == ",");
+ if (m_source[m_srcIndex] != ":") {
+ kError() << "importAda: expecting ':'" << endl;
+ skipStmt();
+ break;
+ }
+ const QString &direction = advance();
+ QString typeName;
+ Uml::Parameter_Direction dir = Uml::pd_In;
+ if (direction == "access") {
+ // Oops, we have to improvise here because there
+ // is no such thing as "access" in UML.
+ // So we use the next best thing, "inout".
+ // Better ideas, anyone?
+ dir = Uml::pd_InOut;
+ typeName = advance();
+ } else if (direction == "in") {
+ if (m_source[m_srcIndex + 1] == "out") {
+ dir = Uml::pd_InOut;
+ m_srcIndex++;
+ }
+ typeName = advance();
+ } else if (direction == "out") {
+ dir = Uml::pd_Out;
+ typeName = advance();
+ } else {
+ typeName = direction; // In Ada, the default direction is "in"
+ }
+ typeName.remove("Standard.", false);
+ typeName = expand(typeName);
+ if (op == NULL) {
+ // In Ada, the first parameter indicates the class.
+ UMLObject *type = Import_Utils::createUMLObject(Uml::ot_Class, typeName, m_scope[m_scopeIndex]);
+ Uml::Object_Type t = type->getBaseType();
+ if ((t != Uml::ot_Interface &&
+ (t != Uml::ot_Class || type->getStereotype() == "record")) ||
+ !m_classesDefinedInThisScope.contains(type)) {
+ // Not an instance bound method - we cannot represent it.
+ skipStmt(")");
+ break;
+ }
+ klass = static_cast<UMLClassifier*>(type);
+ op = Import_Utils::makeOperation(klass, name);
+ // The controlling parameter is suppressed.
+ parNameCount--;
+ if (parNameCount) {
+ for (uint i = 0; i < parNameCount; i++)
+ parName[i] = parName[i + 1];
+ }
+ }
+ for (uint i = 0; i < parNameCount; i++) {
+ UMLAttribute *att = Import_Utils::addMethodParameter(op, typeName, parName[i]);
+ att->setParmKind(dir);
+ }
+ if (advance() != ";")
+ break;
+ }
+ if (keyword == "function") {
+ if (advance() != "return") {
+ if (klass)
+ kError() << "importAda: expecting \"return\" at function "
+ << name << endl;
+ return false;
+ }
+ returnType = expand(advance());
+ returnType.remove("Standard.", false);
+ }
+ bool isAbstract = false;
+ if (advance() == "is" && advance() == "abstract")
+ isAbstract = true;
+ if (klass != NULL && op != NULL)
+ Import_Utils::insertMethod(klass, op, m_currentAccess, returnType,
+ false, isAbstract, false, false, m_comment);
+ skipStmt();
+ return true;
+ }
+ if (keyword == "task" || keyword == "protected") {
+ // Can task and protected objects/types be mapped to UML?
+ bool isType = false;
+ QString name = advance();
+ if (name == "type") {
+ isType = true;
+ name = advance();
+ }
+ QString next = advance();
+ if (next == "(") {
+ skipStmt(")"); // skip discriminant
+ next = advance();
+ }
+ if (next == "is")
+ skipStmt("end");
+ skipStmt();
+ return true;
+ }
+ if (keyword == "for") { // rep spec
+ QString typeName = advance();
+ QString next = advance();
+ if (next == "'") {
+ advance(); // skip qualifier
+ next = advance();
+ }
+ if (next == "use") {
+ if (advance() == "record")
+ skipStmt("end");
+ } else {
+ kError() << "importAda: expecting \"use\" at rep spec of "
+ << typeName << endl;
+ }
+ skipStmt();
+ return true;
+ }
+ // At this point we're only interested in attribute declarations.
+ if (m_klass == NULL || keyword == "null") {
+ skipStmt();
+ return true;
+ }
+ const QString& name = keyword;
+ if (advance() != ":") {
+ kError() << "adaImport: expecting \":\" at " << name << " "
+ << m_source[m_srcIndex] << endl;
+ skipStmt();
+ return true;
+ }
+ QString nextToken = advance();
+ if (nextToken == "aliased")
+ nextToken = advance();
+ QString typeName = expand(nextToken);
+ QString initialValue;
+ if (advance() == ":=") {
+ initialValue = advance();
+ QString token;
+ while ((token = advance()) != ";") {
+ initialValue.append(' ' + token);
+ }
+ }
+ UMLObject *o = Import_Utils::insertAttribute(m_klass, m_currentAccess, name,
+ typeName, m_comment);
+ UMLAttribute *attr = static_cast<UMLAttribute*>(o);
+ attr->setInitialValue(initialValue);
+ skipStmt();
+ return true;
+}
+
+