summaryrefslogtreecommitdiffstats
path: root/umbrello/umbrello/codeimport/javaimport.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'umbrello/umbrello/codeimport/javaimport.cpp')
-rw-r--r--umbrello/umbrello/codeimport/javaimport.cpp549
1 files changed, 549 insertions, 0 deletions
diff --git a/umbrello/umbrello/codeimport/javaimport.cpp b/umbrello/umbrello/codeimport/javaimport.cpp
new file mode 100644
index 00000000..8df6e5e7
--- /dev/null
+++ b/umbrello/umbrello/codeimport/javaimport.cpp
@@ -0,0 +1,549 @@
+/***************************************************************************
+ * *
+ * 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) 2006-2007 *
+ * Umbrello UML Modeller Authors <uml-devel@uml.sf.net> *
+ ***************************************************************************/
+
+// own header
+#include "javaimport.h"
+
+// qt/kde includes
+#include <qfile.h>
+#include <qtextstream.h>
+#include <qstringlist.h>
+#include <qregexp.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"
+
+QStringList JavaImport::s_filesAlreadyParsed;
+int JavaImport::s_parseDepth = 0;
+
+JavaImport::JavaImport() : NativeImportBase("//") {
+ setMultiLineComment("/*", "*/");
+ initVars();
+}
+
+JavaImport::~JavaImport() {
+}
+
+void JavaImport::initVars() {
+ m_isStatic = false;
+}
+
+/// Catenate possible template arguments/array dimensions to the end of the type name.
+QString JavaImport::joinTypename(QString typeName) {
+ if (m_source[m_srcIndex + 1] == "<" ||
+ m_source[m_srcIndex + 1] == "[") {
+ uint start = ++m_srcIndex;
+ if (! skipToClosing(m_source[start][0]))
+ return typeName;
+ for (uint i = start; i <= m_srcIndex; i++) {
+ typeName += m_source[i];
+ }
+ }
+ // to handle multidimensional arrays, call recursively
+ if (m_source[m_srcIndex + 1] == "[") {
+ typeName = joinTypename( typeName );
+ }
+ return typeName;
+}
+
+void JavaImport::fillSource(const QString& word) {
+ QString lexeme;
+ const uint len = word.length();
+ for (uint i = 0; i < len; i++) {
+ const QChar& c = word[i];
+ if (c.isLetterOrNumber() || c == '_' || c == '.') {
+ lexeme += c;
+ } else {
+ if (!lexeme.isEmpty()) {
+ m_source.append(lexeme);
+ lexeme = QString();
+ }
+ m_source.append(QString(c));
+ }
+ }
+ if (!lexeme.isEmpty())
+ m_source.append(lexeme);
+}
+
+
+///Spawn off an import of the specified file
+void JavaImport::spawnImport( QString file ) {
+ // if the file is being parsed, don't bother
+ //
+ if (s_filesAlreadyParsed.contains( file ) ) {
+ return;
+ }
+ if (QFile::exists(file)) {
+ JavaImport importer;
+ QStringList fileList;
+ fileList.append( file );
+ s_filesAlreadyParsed.append( file );
+ importer.importFiles( fileList );
+ }
+}
+
+
+///returns the UML Object if found, or null otherwise
+UMLObject* findObject( QString name, UMLPackage *parentPkg ) {
+ UMLDoc *umldoc = UMLApp::app()->getDocument();
+ UMLObject * o = umldoc->findUMLObject(name, Uml::ot_UMLObject , parentPkg);
+ return o;
+}
+
+
+///Resolve the specified className
+UMLObject* JavaImport::resolveClass (QString className) {
+ kDebug() << "importJava trying to resolve " << className << endl;
+ // keep track if we are dealing with an array
+ //
+ bool isArray = className.contains('[');
+ // remove any [] so that the class itself can be resolved
+ //
+ QString baseClassName = className;
+ baseClassName.remove('[');
+ baseClassName.remove(']');
+
+ // java has a few implicit imports. Most relevant for this is the
+ // current package, which is in the same directory as the current file
+ // being parsed
+ //
+ QStringList file = QStringList::split( '/', m_currentFileName);
+ // remove the filename. This leaves the full path to the containing
+ // dir which should also include the package hierarchy
+ //
+ file.pop_back();
+
+ // the file we're looking for might be in the same directory as the
+ // current class
+ //
+ QString myDir = file.join( "/" );
+ QString myFile = '/' + myDir + '/' + baseClassName + ".java";
+ if ( QFile::exists(myFile) ) {
+ spawnImport( myFile );
+ if ( isArray ) {
+ // we have imported the type. For arrays we want to return
+ // the array type
+ return Import_Utils::createUMLObject(Uml::ot_Class, className, m_scope[m_scopeIndex]);
+ }
+ return findObject(baseClassName, m_scope[m_scopeIndex]);
+ }
+
+ // the class we want is not in the same package as the one being imported.
+ // use the imports to find the one we want.
+ //
+ QStringList package = QStringList::split( '.', m_currentPackage);
+ int dirsInPackageCount = package.size();
+
+ for (int count=0; count < dirsInPackageCount; count ++ ) {
+ // pop off one by one the directories, until only the source root remains
+ //
+ file.pop_back();
+ }
+ // this is now the root of any further source imports
+ QString sourceRoot = '/' + file.join("/") + '/';
+
+ for (QStringList::Iterator pathIt = m_imports.begin();
+ pathIt != m_imports.end(); ++pathIt) {
+ QString import = (*pathIt);
+ QStringList split = QStringList::split( '.', import );
+ split.pop_back(); // remove the * or the classname
+ if ( import.endsWith( "*" ) || import.endsWith( baseClassName) ) {
+ // check if the file we want is in this imported package
+ // convert the org.test type package into a filename
+ //
+ QString aFile = sourceRoot + split.join("/") + '/' + baseClassName + ".java";
+ if ( QFile::exists(aFile) ) {
+ spawnImport( aFile );
+ // we need to set the package for the class that will be resolved
+ // start at the root package
+ UMLPackage *parent = m_scope[0];
+ UMLPackage *current = NULL;
+
+ for (QStringList::Iterator it = split.begin(); it != split.end(); ++it) {
+ QString name = (*it);
+ UMLObject *ns = Import_Utils::createUMLObject(Uml::ot_Package,
+ name, parent);
+ current = static_cast<UMLPackage*>(ns);
+ parent = current;
+ } // for
+ if ( isArray ) {
+ // we have imported the type. For arrays we want to return
+ // the array type
+ return Import_Utils::createUMLObject(Uml::ot_Class, className, current);
+ }
+ // now that we have the right package, the class should be findable
+ return findObject(baseClassName, current);
+ } // if file exists
+ } // if import matches
+ } //foreach import
+ return NULL; // no match
+}
+
+
+/// keep track of the current file being parsed and reset the list of imports
+void JavaImport::parseFile(const QString& filename) {
+ m_currentFileName= filename;
+ m_imports.clear();
+ // default visibility is Impl, unless we are an interface, then it is
+ // public for member vars and methods
+ m_defaultCurrentAccess = Uml::Visibility::Implementation;
+ m_currentAccess = m_defaultCurrentAccess;
+ s_parseDepth++;
+ // in the case of self referencing types, we can avoid parsing the
+ // file twice by adding it to the list
+ s_filesAlreadyParsed.append(filename);
+ NativeImportBase::parseFile(filename);
+ s_parseDepth--;
+ if ( s_parseDepth <= 0 ) {
+ // if the user decides to clear things out and reparse, we need
+ // to honor the request, so reset things for next time.
+ s_filesAlreadyParsed.clear();
+ s_parseDepth = 0;
+ }
+}
+
+
+
+
+bool JavaImport::parseStmt() {
+ const uint srcLength = m_source.count();
+ const QString& keyword = m_source[m_srcIndex];
+ //kDebug() << '"' << keyword << '"' << endl;
+ if (keyword == "package") {
+ m_currentPackage = advance();
+ const QString& qualifiedName = m_currentPackage;
+ QStringList names = QStringList::split(".", qualifiedName);
+ for (QStringList::Iterator it = names.begin(); it != names.end(); ++it) {
+ QString name = (*it);
+ UMLObject *ns = Import_Utils::createUMLObject(Uml::ot_Package,
+ name, m_scope[m_scopeIndex], m_comment);
+ m_scope[++m_scopeIndex] = static_cast<UMLPackage*>(ns);
+ }
+ if (advance() != ";") {
+ kError() << "importJava: unexpected: " << m_source[m_srcIndex] << endl;
+ skipStmt();
+ }
+ return true;
+ }
+ if (keyword == "class" || keyword == "interface") {
+ const QString& name = advance();
+ const Uml::Object_Type t = (keyword == "class" ? Uml::ot_Class : Uml::ot_Interface);
+ UMLObject *ns = Import_Utils::createUMLObject(t, name, m_scope[m_scopeIndex], m_comment);
+ m_scope[++m_scopeIndex] = m_klass = static_cast<UMLClassifier*>(ns);
+ m_klass->setAbstract(m_isAbstract);
+ m_klass->setStatic(m_isStatic);
+ m_klass->setVisibility(m_currentAccess);
+ // The UMLObject found by createUMLObject might originally have been created as a
+ // placeholder with a type of class but if is really an interface, then we need to
+ // change it.
+ Uml::Object_Type ot = (keyword == "interface" ? Uml::ot_Interface : Uml::ot_Class);
+ m_klass->setBaseType(ot);
+ m_isAbstract = m_isStatic = false;
+ // if no modifier is specified in an interface, then it means public
+ if ( m_klass->isInterface() ) {
+ m_defaultCurrentAccess = Uml::Visibility::Public;
+ }
+ if (advance() == ";") // forward declaration
+ return true;
+ if (m_source[m_srcIndex] == "<") {
+ // template args - preliminary, rudimentary implementation
+ // @todo implement all template arg syntax
+ uint start = m_srcIndex;
+ if (! skipToClosing('<')) {
+ kError() << "importJava(" << name << "): template syntax error" << endl;
+ return false;
+ }
+ while (1) {
+ const QString arg = m_source[++start];
+ if (! arg.contains( QRegExp("^[A-Za-z_]") )) {
+ kDebug() << "importJava(" << name << "): cannot handle template syntax ("
+ << arg << ")" << endl;
+ break;
+ }
+ /* UMLTemplate *tmpl = */ m_klass->addTemplate(arg);
+ const QString next = m_source[++start];
+ if (next == ">")
+ break;
+ if (next != ",") {
+ kDebug() << "importJava(" << name << "): can't handle template syntax ("
+ << next << ")" << endl;
+ break;
+ }
+ }
+ advance(); // skip over ">"
+ }
+ if (m_source[m_srcIndex] == "extends") {
+ const QString& baseName = advance();
+ // try to resolve the class we are extending, or if impossible
+ // create a placeholder
+ UMLObject *parent = resolveClass( baseName );
+ if ( parent ) {
+ Import_Utils::createGeneralization(m_klass, static_cast<UMLClassifier*>(parent));
+ } else {
+ kDebug() << "importJava parentClass " << baseName
+ << " is not resolveable. Creating placeholder" << endl;
+ Import_Utils::createGeneralization(m_klass, baseName);
+ }
+ advance();
+ }
+ if (m_source[m_srcIndex] == "implements") {
+ while (m_srcIndex < srcLength - 1 && advance() != "{") {
+ const QString& baseName = m_source[m_srcIndex];
+ // try to resolve the interface we are implementing, if this fails
+ // create a placeholder
+ UMLObject *interface = resolveClass( baseName );
+ if ( interface ) {
+ Import_Utils::createGeneralization(m_klass, static_cast<UMLClassifier*>(interface));
+ } else {
+ kDebug() << "importJava implementing interface "<< baseName
+ <<" is not resolvable. Creating placeholder" <<endl;
+ Import_Utils::createGeneralization(m_klass, baseName);
+ }
+ if (advance() != ",")
+ break;
+ }
+ }
+ if (m_source[m_srcIndex] != "{") {
+ kError() << "importJava: ignoring excess chars at " << name
+ << " (" << m_source[m_srcIndex] << ")" << endl;
+ skipStmt("{");
+ }
+ return true;
+ }
+ if (keyword == "enum") {
+ const QString& name = advance();
+ UMLObject *ns = Import_Utils::createUMLObject(Uml::ot_Enum,
+ name, m_scope[m_scopeIndex], m_comment);
+ UMLEnum *enumType = static_cast<UMLEnum*>(ns);
+ skipStmt("{");
+ while (m_srcIndex < srcLength - 1 && advance() != "}") {
+ Import_Utils::addEnumLiteral(enumType, m_source[m_srcIndex]);
+ QString next = advance();
+ if (next == "{" || next == "(") {
+ if (! skipToClosing(next[0]))
+ return false;
+ next = advance();
+ }
+ if (next != ",") {
+ if (next == ";") {
+ // @todo handle methods in enum
+ // For now, we cheat (skip them)
+ m_source[m_srcIndex] = "{";
+ if (! skipToClosing('{'))
+ return false;
+ }
+ break;
+ }
+ }
+ return true;
+ }
+ if (keyword == "static") {
+ m_isStatic = true;
+ return true;
+ }
+ // if we detected static previously and keyword is { then this is a static block
+ if (m_isStatic && keyword == "{") {
+ // reset static flag and jump to end of static block
+ m_isStatic = false;
+ return skipToClosing('{');
+ }
+ if (keyword == "abstract") {
+ m_isAbstract = true;
+ return true;
+ }
+ if (keyword == "public") {
+ m_currentAccess = Uml::Visibility::Public;
+ return true;
+ }
+ if (keyword == "protected") {
+ m_currentAccess = Uml::Visibility::Protected;
+ return true;
+ }
+ if (keyword == "private") {
+ m_currentAccess = Uml::Visibility::Private;
+ return true;
+ }
+ if (keyword == "final" ||
+ keyword == "native" ||
+ keyword == "synchronized" ||
+ keyword == "transient" ||
+ keyword == "volatile") {
+ //@todo anything to do here?
+ return true;
+ }
+ if (keyword == "import") {
+ // keep track of imports so we can resolve classes we are dependent on
+ QString import = advance();
+ if ( import.endsWith(".") ) {
+ //this most likely an import that ends with a *
+ //
+ import = import + advance();
+ }
+ m_imports.append( import );
+
+ // move past ;
+ skipStmt();
+ return true;
+ }
+ if (keyword == "@") { // annotation
+ advance();
+ if (m_source[m_srcIndex + 1] == "(") {
+ advance();
+ skipToClosing('(');
+ }
+ return true;
+ }
+ if (keyword == "}") {
+ if (m_scopeIndex)
+ m_klass = dynamic_cast<UMLClassifier*>(m_scope[--m_scopeIndex]);
+ else
+ kError() << "importJava: too many }" << endl;
+ return true;
+ }
+ // At this point, we expect `keyword' to be a type name
+ // (of a member of class or interface, or return type
+ // of an operation.) Up next is the name of the attribute
+ // or operation.
+ if (! keyword.contains( QRegExp("^\\w") )) {
+ kError() << "importJava: ignoring " << keyword << endl;
+ return false;
+ }
+ QString typeName = m_source[m_srcIndex];
+ typeName = joinTypename(typeName);
+ // At this point we need a class.
+ if (m_klass == NULL) {
+ kError() << "importJava: no class set for " << typeName << endl;
+ return false;
+ }
+ QString name = advance();
+ QString nextToken;
+ if (typeName == m_klass->getName() && name == "(") {
+ // Constructor.
+ nextToken = name;
+ name = typeName;
+ typeName = QString();
+ } else {
+ nextToken = advance();
+ }
+ if (name.contains( QRegExp("\\W") )) {
+ kError() << "importJava: expecting name in " << name << endl;
+ return false;
+ }
+ if (nextToken == "(") {
+ // operation
+ UMLOperation *op = Import_Utils::makeOperation(m_klass, name);
+ m_srcIndex++;
+ while (m_srcIndex < srcLength && m_source[m_srcIndex] != ")") {
+ QString typeName = m_source[m_srcIndex];
+ if ( typeName == "final" || typeName.startsWith( "//") ) {
+ // ignore the "final" keyword and any comments in method args
+ typeName = advance();
+ }
+ typeName = joinTypename(typeName);
+ QString parName = advance();
+ // the Class might not be resolved yet so resolve it if necessary
+ UMLObject *obj = resolveClass(typeName);
+ if (obj) {
+ // by prepending the package, unwanted placeholder types will not get created
+ typeName = obj->getFullyQualifiedName(".");
+ }
+ /* UMLAttribute *att = */ Import_Utils::addMethodParameter(op, typeName, parName);
+ if (advance() != ",")
+ break;
+ m_srcIndex++;
+ }
+ // before adding the method, try resolving the return type
+ UMLObject *obj = resolveClass(typeName);
+ if (obj) {
+ // using the fully qualified name means that a placeholder type will not be created.
+ typeName = obj->getFullyQualifiedName(".");
+ }
+ Import_Utils::insertMethod(m_klass, op, m_currentAccess, typeName,
+ m_isStatic, m_isAbstract, false /*isFriend*/,
+ false /*isConstructor*/, m_comment);
+ m_isAbstract = m_isStatic = false;
+ // reset the default visibility
+ m_currentAccess = m_defaultCurrentAccess;
+ // At this point we do not know whether the method has a body or not.
+ do {
+ nextToken = advance();
+ } while (nextToken != "{" && nextToken != ";");
+ if (nextToken == ";") {
+ // No body (interface or abstract)
+ return true;
+ } else {
+ return skipToClosing('{');
+ }
+ }
+ // At this point we know it's some kind of attribute declaration.
+ while (1) {
+ while (nextToken != "," && nextToken != ";") {
+ if (nextToken == "=") {
+ if ((nextToken = advance()) == "new") {
+ advance();
+ if ((nextToken = advance()) == "(") {
+ skipToClosing('(');
+ if ((nextToken = advance()) == "{") {
+ skipToClosing('{');
+ } else {
+ skipStmt();
+ break;
+ }
+ } else {
+ skipStmt();
+ break;
+ }
+ } else {
+ skipStmt();
+ break;
+ }
+ } else {
+ name += nextToken; // add possible array dimensions to `name'
+ }
+ nextToken = advance();
+ }
+ // try to resolve the class type, or create a placeholder if that fails
+ UMLObject *type = resolveClass( typeName );
+ UMLObject *o;
+ if (type) {
+ o = Import_Utils::insertAttribute(m_klass, m_currentAccess, name,
+ static_cast<UMLClassifier*>(type), m_comment, m_isStatic);
+ } else {
+ o = Import_Utils::insertAttribute(m_klass, m_currentAccess, name,
+ typeName, m_comment, m_isStatic);
+ }
+ // UMLAttribute *attr = static_cast<UMLAttribute*>(o);
+ if (nextToken != ",") {
+ // reset the modifiers
+ m_isStatic = m_isAbstract = false;
+ break;
+ }
+ name = advance();
+ nextToken = advance();
+ }
+ // reset visibility to default
+ m_currentAccess = m_defaultCurrentAccess;
+ if (m_source[m_srcIndex] != ";") {
+ kError() << "importJava: ignoring trailing items at " << name << endl;
+ skipStmt();
+ }
+ return true;
+}
+
+