You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
357 lines
13 KiB
357 lines
13 KiB
/***************************************************************************
|
|
* *
|
|
* 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 *
|
|
* Umbrello UML Modeller Authors <uml-devel@uml.sf.net> *
|
|
***************************************************************************/
|
|
|
|
// own header
|
|
#include "idlimport.h"
|
|
|
|
#include <stdio.h>
|
|
// qt/kde includes
|
|
// #include <tqprocess.h> //should use this instead of popen()
|
|
#include <tqstringlist.h>
|
|
#include <tqregexp.h>
|
|
#include <kdebug.h>
|
|
// app includes
|
|
#include "import_utils.h"
|
|
#include "../uml.h"
|
|
#include "../umldoc.h"
|
|
#include "../umlpackagelist.h"
|
|
#include "../package.h"
|
|
#include "../classifier.h"
|
|
#include "../enum.h"
|
|
#include "../operation.h"
|
|
#include "../attribute.h"
|
|
|
|
IDLImport::IDLImport() : NativeImportBase("//") {
|
|
m_isOneway = m_isReadonly = m_isAttribute = false;
|
|
setMultiLineComment("/*", "*/");
|
|
}
|
|
|
|
IDLImport::~IDLImport() {
|
|
}
|
|
|
|
/// Check for split type names (e.g. unsigned long long)
|
|
TQString IDLImport::joinTypename() {
|
|
TQString typeName = m_source[m_srcIndex];
|
|
if (m_source[m_srcIndex] == "unsigned")
|
|
typeName += ' ' + advance();
|
|
if (m_source[m_srcIndex] == "long" &&
|
|
(m_source[m_srcIndex + 1] == "long" || m_source[m_srcIndex + 1] == "double"))
|
|
typeName += ' ' + advance();
|
|
return typeName;
|
|
}
|
|
|
|
bool IDLImport::preprocess(TQString& line) {
|
|
// Ignore C preprocessor generated lines.
|
|
if (line.startsWith("#"))
|
|
return true; // done
|
|
return NativeImportBase::preprocess(line);
|
|
}
|
|
|
|
void IDLImport::fillSource(const TQString& word) {
|
|
TQString lexeme;
|
|
const uint len = word.length();
|
|
for (uint i = 0; i < len; i++) {
|
|
TQChar c = word[i];
|
|
if (c.isLetterOrNumber() || c == '_') {
|
|
lexeme += c;
|
|
} else if (c == ':' && word[i + 1] == ':') {
|
|
// compress scoped name into lexeme
|
|
lexeme += "::";
|
|
i++;
|
|
} else if (c == '<') {
|
|
// compress sequence or bounded string into lexeme
|
|
do {
|
|
lexeme += word[i];
|
|
} while (word[i] != '>' && ++i < len);
|
|
} else {
|
|
if (!lexeme.isEmpty()) {
|
|
m_source.append(lexeme);
|
|
lexeme = TQString();
|
|
}
|
|
m_source.append(TQString(c));
|
|
}
|
|
}
|
|
if (!lexeme.isEmpty())
|
|
m_source.append(lexeme);
|
|
}
|
|
|
|
void IDLImport::parseFile(const TQString& filename) {
|
|
if (filename.contains('/')) {
|
|
TQString path = filename;
|
|
path.remove( TQRegExp("/[^/]+$") );
|
|
kDebug() << "IDLImport::parseFile: adding path " << path << endl;
|
|
Import_Utils::addIncludePath(path);
|
|
}
|
|
TQStringList includePaths = Import_Utils::includePathList();
|
|
//TQProcess command("cpp", UMLAp::app());
|
|
TQString command("cpp -C"); // -C means "preserve comments"
|
|
for (TQStringList::Iterator pathIt = includePaths.begin();
|
|
pathIt != includePaths.end(); ++pathIt) {
|
|
TQString path = (*pathIt);
|
|
//command.addArgument(" -I" + path);
|
|
command += " -I" + path;
|
|
}
|
|
command += ' ' + filename;
|
|
kDebug() << "importIDL: " << command << endl;
|
|
FILE *fp = popen(command.ascii(), "r");
|
|
if (fp == NULL) {
|
|
kError() << "IDLImport::parseFile: cannot popen(" << command << ")" << endl;
|
|
return;
|
|
}
|
|
// Scan the input file into the TQStringList m_source.
|
|
m_source.clear();
|
|
char buf[256];
|
|
while (fgets(buf, sizeof(buf), fp) != NULL) {
|
|
int len = strlen(buf);
|
|
if (buf[len - 1] == '\n')
|
|
buf[--len] = '\0';
|
|
NativeImportBase::scan( TQString(buf) );
|
|
}
|
|
// Parse the TQStringList m_source.
|
|
m_scopeIndex = 0;
|
|
m_scope[0] = NULL;
|
|
const uint srcLength = m_source.count();
|
|
for (m_srcIndex = 0; m_srcIndex < srcLength; m_srcIndex++) {
|
|
const TQString& keyword = m_source[m_srcIndex];
|
|
//kDebug() << '"' << keyword << '"' << endl;
|
|
if (keyword.startsWith(m_singleLineCommentIntro)) {
|
|
m_comment = keyword.mid(m_singleLineCommentIntro.length());
|
|
continue;
|
|
}
|
|
if (! parseStmt())
|
|
skipStmt();
|
|
m_currentAccess = Uml::Visibility::Public;
|
|
m_comment = TQString();
|
|
}
|
|
pclose(fp);
|
|
}
|
|
|
|
bool IDLImport::parseStmt() {
|
|
const TQString& keyword = m_source[m_srcIndex];
|
|
const uint srcLength = m_source.count();
|
|
if (keyword == "module") {
|
|
const TQString& name = advance();
|
|
UMLObject *ns = Import_Utils::createUMLObject(Uml::ot_Package,
|
|
name, m_scope[m_scopeIndex], m_comment);
|
|
m_scope[++m_scopeIndex] = static_cast<UMLPackage*>(ns);
|
|
m_scope[m_scopeIndex]->setStereotype("CORBAModule");
|
|
if (advance() != "{") {
|
|
kError() << "importIDL: unexpected: " << m_source[m_srcIndex] << endl;
|
|
skipStmt("{");
|
|
}
|
|
return true;
|
|
}
|
|
if (keyword == "interface") {
|
|
const TQString& name = advance();
|
|
UMLObject *ns = Import_Utils::createUMLObject(Uml::ot_Class,
|
|
name, m_scope[m_scopeIndex], m_comment);
|
|
m_scope[++m_scopeIndex] = m_klass = static_cast<UMLClassifier*>(ns);
|
|
m_klass->setStereotype("CORBAInterface");
|
|
m_klass->setAbstract(m_isAbstract);
|
|
m_isAbstract = false;
|
|
m_comment = TQString();
|
|
if (advance() == ";") // forward declaration
|
|
return true;
|
|
if (m_source[m_srcIndex] == ":") {
|
|
while (++m_srcIndex < srcLength && m_source[m_srcIndex] != "{") {
|
|
const TQString& baseName = m_source[m_srcIndex];
|
|
Import_Utils::createGeneralization(m_klass, baseName);
|
|
if (advance() != ",")
|
|
break;
|
|
}
|
|
}
|
|
if (m_source[m_srcIndex] != "{") {
|
|
kError() << "importIDL: ignoring excess chars at "
|
|
<< name << endl;
|
|
skipStmt("{");
|
|
}
|
|
return true;
|
|
}
|
|
if (keyword == "struct" || keyword == "exception") {
|
|
const TQString& name = advance();
|
|
UMLObject *ns = Import_Utils::createUMLObject(Uml::ot_Class,
|
|
name, m_scope[m_scopeIndex], m_comment);
|
|
m_scope[++m_scopeIndex] = m_klass = static_cast<UMLClassifier*>(ns);
|
|
if (keyword == "struct")
|
|
m_klass->setStereotype("CORBAStruct");
|
|
else
|
|
m_klass->setStereotype("CORBAException");
|
|
if (advance() != "{") {
|
|
kError() << "importIDL: expecting '{' at " << name << endl;
|
|
skipStmt("{");
|
|
}
|
|
return true;
|
|
}
|
|
if (keyword == "union") {
|
|
// TBD. <gulp>
|
|
skipStmt("}");
|
|
m_srcIndex++; // advance to ';'
|
|
return true;
|
|
}
|
|
if (keyword == "enum") {
|
|
const TQString& name = advance();
|
|
UMLObject *ns = Import_Utils::createUMLObject(Uml::ot_Enum,
|
|
name, m_scope[m_scopeIndex], m_comment);
|
|
UMLEnum *enumType = static_cast<UMLEnum*>(ns);
|
|
m_srcIndex++; // skip name
|
|
while (++m_srcIndex < srcLength && m_source[m_srcIndex] != "}") {
|
|
Import_Utils::addEnumLiteral(enumType, m_source[m_srcIndex]);
|
|
if (advance() != ",")
|
|
break;
|
|
}
|
|
skipStmt();
|
|
return true;
|
|
}
|
|
if (keyword == "typedef") {
|
|
const TQString& existingType = advance();
|
|
const TQString& newType = advance();
|
|
Import_Utils::createUMLObject(Uml::ot_Class, newType, m_scope[m_scopeIndex],
|
|
m_comment, "CORBATypedef" /* stereotype */);
|
|
// @todo How do we convey the existingType ?
|
|
skipStmt();
|
|
return true;
|
|
}
|
|
if (keyword == "const") {
|
|
skipStmt();
|
|
return true;
|
|
}
|
|
if (keyword == "custom") {
|
|
return true;
|
|
}
|
|
if (keyword == "abstract") {
|
|
m_isAbstract = true;
|
|
return true;
|
|
}
|
|
if (keyword == "valuetype") {
|
|
const TQString& name = advance();
|
|
UMLObject *ns = Import_Utils::createUMLObject(Uml::ot_Class,
|
|
name, m_scope[m_scopeIndex], m_comment);
|
|
m_scope[++m_scopeIndex] = m_klass = static_cast<UMLClassifier*>(ns);
|
|
m_klass->setAbstract(m_isAbstract);
|
|
m_isAbstract = false;
|
|
if (advance() == ";") // forward declaration
|
|
return true;
|
|
if (m_source[m_srcIndex] == ":") {
|
|
if (advance() == "truncatable")
|
|
m_srcIndex++;
|
|
while (m_srcIndex < srcLength && m_source[m_srcIndex] != "{") {
|
|
const TQString& baseName = m_source[m_srcIndex];
|
|
Import_Utils::createGeneralization(m_klass, baseName);
|
|
if (advance() != ",")
|
|
break;
|
|
m_srcIndex++;
|
|
}
|
|
}
|
|
if (m_source[m_srcIndex] != "{") {
|
|
kError() << "importIDL: ignoring excess chars at "
|
|
<< name << endl;
|
|
skipStmt("{");
|
|
}
|
|
return true;
|
|
}
|
|
if (keyword == "public") {
|
|
return true;
|
|
}
|
|
if (keyword == "private") {
|
|
m_currentAccess = Uml::Visibility::Private;
|
|
return true;
|
|
}
|
|
if (keyword == "readonly") {
|
|
m_isReadonly = true;
|
|
return true;
|
|
}
|
|
if (keyword == "attribute") {
|
|
m_isAttribute = true;
|
|
return true;
|
|
}
|
|
if (keyword == "oneway") {
|
|
m_isOneway = true;
|
|
return true;
|
|
}
|
|
if (keyword == "}") {
|
|
if (m_scopeIndex)
|
|
m_klass = dynamic_cast<UMLClassifier*>(m_scope[--m_scopeIndex]);
|
|
else
|
|
kError() << "importIDL: too many }" << endl;
|
|
m_srcIndex++; // skip ';'
|
|
return true;
|
|
}
|
|
if (keyword == ";")
|
|
return true;
|
|
// At this point, we expect `keyword' to be a type name
|
|
// (of a member of struct or valuetype, or return type
|
|
// of an operation.) Up next is the name of the attribute
|
|
// or operation.
|
|
if (! keyword.contains( TQRegExp("^\\w") )) {
|
|
kError() << "importIDL: ignoring " << keyword << endl;
|
|
return false;
|
|
}
|
|
TQString typeName = joinTypename();
|
|
TQString name = advance();
|
|
if (name.contains( TQRegExp("\\W") )) {
|
|
kError() << "importIDL: expecting name in " << name << endl;
|
|
return false;
|
|
}
|
|
// At this point we most definitely need a class.
|
|
if (m_klass == NULL) {
|
|
kError() << "importIDL: no class set for " << name << endl;
|
|
return false;
|
|
}
|
|
TQString nextToken = advance();
|
|
if (nextToken == "(") {
|
|
// operation
|
|
UMLOperation *op = Import_Utils::makeOperation(m_klass, name);
|
|
m_srcIndex++;
|
|
while (m_srcIndex < srcLength && m_source[m_srcIndex] != ")") {
|
|
const TQString &direction = m_source[m_srcIndex++];
|
|
TQString typeName = joinTypename();
|
|
const TQString &parName = advance();
|
|
UMLAttribute *att = Import_Utils::addMethodParameter(op, typeName, parName);
|
|
Uml::Parameter_Direction dir;
|
|
if (Model_Utils::stringToDirection(direction, dir))
|
|
att->setParmKind(dir);
|
|
else
|
|
kError() << "importIDL: expecting parameter direction at "
|
|
<< direction << endl;
|
|
if (advance() != ",")
|
|
break;
|
|
m_srcIndex++;
|
|
}
|
|
Import_Utils::insertMethod(m_klass, op, Uml::Visibility::Public, typeName,
|
|
false, false, false, false, m_comment);
|
|
if (m_isOneway) {
|
|
op->setStereotype("oneway");
|
|
m_isOneway = false;
|
|
}
|
|
skipStmt(); // skip possible "raises" clause
|
|
return true;
|
|
}
|
|
// At this point we know it's some kind of attribute declaration.
|
|
while (1) {
|
|
while (nextToken != "," && nextToken != ";") {
|
|
name += nextToken; // add possible array dimensions to `name'
|
|
nextToken = advance();
|
|
}
|
|
UMLObject *o = Import_Utils::insertAttribute(m_klass, m_currentAccess, name, typeName, m_comment);
|
|
UMLAttribute *attr = static_cast<UMLAttribute*>(o);
|
|
if (m_isReadonly) {
|
|
attr->setStereotype("readonly");
|
|
m_isReadonly = false;
|
|
}
|
|
if (nextToken != ",")
|
|
break;
|
|
name = advance();
|
|
nextToken = advance();
|
|
}
|
|
return true;
|
|
}
|
|
|