umbrello API Documentation

javaimport.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) 2006-2007                                                *
00009  *  Umbrello UML Modeller Authors <uml-devel@uml.sf.net>                   *
00010  ***************************************************************************/
00011 
00012 // own header
00013 #include "javaimport.h"
00014 
00015 // qt/kde includes
00016 #include <qfile.h>
00017 #include <qtextstream.h>
00018 #include <qstringlist.h>
00019 #include <qregexp.h>
00020 #include <kdebug.h>
00021 // app includes
00022 #include "import_utils.h"
00023 #include "../uml.h"
00024 #include "../umldoc.h"
00025 #include "../umlpackagelist.h"
00026 #include "../package.h"
00027 #include "../classifier.h"
00028 #include "../enum.h"
00029 #include "../operation.h"
00030 #include "../attribute.h"
00031 
00032 QStringList JavaImport::s_filesAlreadyParsed;
00033 int JavaImport::s_parseDepth = 0;
00034 
00035 JavaImport::JavaImport() : NativeImportBase("//") {
00036     setMultiLineComment("/*", "*/");
00037     initVars();
00038 }
00039 
00040 JavaImport::~JavaImport() {
00041 }
00042 
00043 void JavaImport::initVars() {
00044     m_isStatic = false;
00045 }
00046 
00048 QString JavaImport::joinTypename(QString typeName) {
00049     if (m_source[m_srcIndex + 1] == "<" ||
00050         m_source[m_srcIndex + 1] == "[") {
00051         uint start = ++m_srcIndex;
00052         if (! skipToClosing(m_source[start][0]))
00053             return typeName;
00054         for (uint i = start; i <= m_srcIndex; i++) {
00055             typeName += m_source[i];
00056         }
00057     }
00058     // to handle multidimensional arrays, call recursively
00059     if (m_source[m_srcIndex + 1] == "[") {
00060         typeName = joinTypename( typeName );
00061     }
00062     return typeName;
00063 }
00064 
00065 void JavaImport::fillSource(const QString& word) {
00066     QString lexeme;
00067     const uint len = word.length();
00068     for (uint i = 0; i < len; i++) {
00069         const QChar& c = word[i];
00070         if (c.isLetterOrNumber() || c == '_' || c == '.') {
00071             lexeme += c;
00072         } else {
00073             if (!lexeme.isEmpty()) {
00074                 m_source.append(lexeme);
00075                 lexeme = QString();
00076             }
00077             m_source.append(QString(c));
00078         }
00079     }
00080     if (!lexeme.isEmpty())
00081         m_source.append(lexeme);
00082 }
00083 
00084 
00086 void JavaImport::spawnImport( QString file ) {
00087     // if the file is being parsed, don't bother
00088     //
00089     if (s_filesAlreadyParsed.contains( file ) ) {
00090         return;
00091     }
00092     if (QFile::exists(file)) {
00093           JavaImport importer;
00094           QStringList fileList;
00095           fileList.append( file );
00096           s_filesAlreadyParsed.append( file );
00097           importer.importFiles( fileList );
00098     }
00099 }
00100 
00101 
00103 UMLObject* findObject( QString name,   UMLPackage *parentPkg ) {
00104     UMLDoc *umldoc = UMLApp::app()->getDocument();
00105     UMLObject * o = umldoc->findUMLObject(name, Uml::ot_UMLObject , parentPkg);
00106     return o;
00107 }
00108 
00109 
00111 UMLObject* JavaImport::resolveClass (QString className) {
00112     kDebug() << "importJava trying to resolve " << className << endl;
00113     // keep track if we are dealing with an array
00114     //
00115     bool isArray = className.contains('[');
00116     // remove any [] so that the class itself can be resolved
00117     //
00118     QString baseClassName = className;
00119     baseClassName.remove('[');
00120     baseClassName.remove(']');
00121 
00122     // java has a few implicit imports.  Most relevant for this is the
00123     // current package, which is in the same directory as the current file
00124     // being parsed
00125     //
00126     QStringList file = QStringList::split( '/', m_currentFileName);
00127     // remove the filename.  This leaves the full path to the containing
00128     // dir which should also include the package hierarchy
00129     //
00130     file.pop_back();
00131 
00132     // the file we're looking for might be in the same directory as the
00133     // current class
00134     //
00135     QString myDir = file.join( "/" );
00136     QString myFile = '/' + myDir + '/' + baseClassName + ".java";
00137     if ( QFile::exists(myFile) ) {
00138         spawnImport( myFile );
00139         if ( isArray ) {
00140             // we have imported the type. For arrays we want to return
00141             // the array type
00142             return Import_Utils::createUMLObject(Uml::ot_Class, className, m_scope[m_scopeIndex]);
00143         }
00144         return findObject(baseClassName, m_scope[m_scopeIndex]);
00145     }
00146 
00147     // the class we want is not in the same package as the one being imported.
00148     // use the imports to find the one we want.
00149     //
00150     QStringList package = QStringList::split( '.', m_currentPackage);
00151     int dirsInPackageCount = package.size();
00152 
00153     for (int count=0; count < dirsInPackageCount; count ++ ) {
00154         // pop off one by one the directories, until only the source root remains
00155         //
00156         file.pop_back();
00157     }
00158     // this is now the root of any further source imports
00159     QString sourceRoot = '/' + file.join("/") + '/';
00160 
00161     for (QStringList::Iterator pathIt = m_imports.begin();
00162                                    pathIt != m_imports.end(); ++pathIt) {
00163         QString import = (*pathIt);
00164         QStringList split = QStringList::split( '.', import );
00165         split.pop_back(); // remove the * or the classname
00166         if ( import.endsWith( "*" ) || import.endsWith( baseClassName) ) {
00167             // check if the file we want is in this imported package
00168             // convert the org.test type package into a filename
00169             //
00170             QString aFile = sourceRoot + split.join("/") + '/' + baseClassName + ".java";
00171             if ( QFile::exists(aFile) ) {
00172                 spawnImport( aFile );
00173                 // we need to set the package for the class that will be resolved
00174                 // start at the root package
00175                 UMLPackage *parent = m_scope[0];
00176                 UMLPackage *current = NULL;
00177 
00178                 for (QStringList::Iterator it = split.begin(); it != split.end(); ++it) {
00179                     QString name = (*it);
00180                     UMLObject *ns = Import_Utils::createUMLObject(Uml::ot_Package,
00181                                                                   name, parent);
00182                     current = static_cast<UMLPackage*>(ns);
00183                     parent = current;
00184                 } // for
00185                 if ( isArray ) {
00186                     // we have imported the type. For arrays we want to return
00187                     // the array type
00188                     return Import_Utils::createUMLObject(Uml::ot_Class, className, current);
00189                 }
00190                 // now that we have the right package, the class should be findable
00191                 return findObject(baseClassName, current);
00192             } // if file exists
00193         } // if import matches
00194     } //foreach import
00195     return NULL; // no match
00196 }
00197 
00198 
00200 void JavaImport::parseFile(const QString& filename) {
00201     m_currentFileName= filename;
00202     m_imports.clear();
00203     // default visibility is Impl, unless we are an interface, then it is
00204     // public for member vars and methods
00205     m_defaultCurrentAccess = Uml::Visibility::Implementation;
00206     m_currentAccess = m_defaultCurrentAccess;
00207     s_parseDepth++;
00208     // in the case of self referencing types, we can avoid parsing the
00209     // file twice by adding it to the list
00210     s_filesAlreadyParsed.append(filename);
00211     NativeImportBase::parseFile(filename);
00212     s_parseDepth--;
00213     if ( s_parseDepth <= 0 ) {
00214         // if the user decides to clear things out and reparse, we need
00215         // to honor the request, so reset things for next time.
00216         s_filesAlreadyParsed.clear();
00217         s_parseDepth = 0;
00218     }
00219 }
00220 
00221 
00222 
00223 
00224 bool JavaImport::parseStmt() {
00225     const uint srcLength = m_source.count();
00226     const QString& keyword = m_source[m_srcIndex];
00227     //kDebug() << '"' << keyword << '"' << endl;
00228     if (keyword == "package") {
00229         m_currentPackage = advance();
00230         const QString& qualifiedName = m_currentPackage;
00231         QStringList names = QStringList::split(".", qualifiedName);
00232         for (QStringList::Iterator it = names.begin(); it != names.end(); ++it) {
00233             QString name = (*it);
00234             UMLObject *ns = Import_Utils::createUMLObject(Uml::ot_Package,
00235                             name, m_scope[m_scopeIndex], m_comment);
00236             m_scope[++m_scopeIndex] = static_cast<UMLPackage*>(ns);
00237         }
00238         if (advance() != ";") {
00239             kError() << "importJava: unexpected: " << m_source[m_srcIndex] << endl;
00240             skipStmt();
00241         }
00242         return true;
00243     }
00244     if (keyword == "class" || keyword == "interface") {
00245         const QString& name = advance();
00246         const Uml::Object_Type t = (keyword == "class" ? Uml::ot_Class : Uml::ot_Interface);
00247         UMLObject *ns = Import_Utils::createUMLObject(t, name, m_scope[m_scopeIndex], m_comment);
00248         m_scope[++m_scopeIndex] = m_klass = static_cast<UMLClassifier*>(ns);
00249         m_klass->setAbstract(m_isAbstract);
00250         m_klass->setStatic(m_isStatic);
00251         m_klass->setVisibility(m_currentAccess);
00252         // The UMLObject found by createUMLObject might originally have been created as a
00253         // placeholder with a type of class but if is really an interface, then we need to
00254         // change it.
00255         Uml::Object_Type ot = (keyword == "interface" ? Uml::ot_Interface : Uml::ot_Class);
00256         m_klass->setBaseType(ot);
00257         m_isAbstract = m_isStatic = false;
00258         // if no modifier is specified in an interface, then it means public
00259         if ( m_klass->isInterface() ) {
00260             m_defaultCurrentAccess =  Uml::Visibility::Public;
00261         }
00262         if (advance() == ";")   // forward declaration
00263             return true;
00264         if (m_source[m_srcIndex] == "<") {
00265             // template args - preliminary, rudimentary implementation
00266             // @todo implement all template arg syntax
00267             uint start = m_srcIndex;
00268             if (! skipToClosing('<')) {
00269                 kError() << "importJava(" << name << "): template syntax error" << endl;
00270                 return false;
00271             }
00272             while (1) {
00273                 const QString arg = m_source[++start];
00274                 if (! arg.contains( QRegExp("^[A-Za-z_]") )) {
00275                     kDebug() << "importJava(" << name << "): cannot handle template syntax ("
00276                         << arg << ")" << endl;
00277                     break;
00278                 }
00279                 /* UMLTemplate *tmpl = */ m_klass->addTemplate(arg);
00280                 const QString next = m_source[++start];
00281                 if (next == ">")
00282                     break;
00283                 if (next != ",") {
00284                     kDebug() << "importJava(" << name << "): can't handle template syntax ("
00285                         << next << ")" << endl;
00286                     break;
00287                 }
00288             }
00289             advance();  // skip over ">"
00290         }
00291         if (m_source[m_srcIndex] == "extends") {
00292             const QString& baseName = advance();
00293             // try to resolve the class we are extending, or if impossible
00294             // create a placeholder
00295             UMLObject *parent = resolveClass( baseName );
00296             if ( parent ) {
00297                 Import_Utils::createGeneralization(m_klass, static_cast<UMLClassifier*>(parent));
00298             } else {
00299                 kDebug() << "importJava parentClass " << baseName
00300                     << " is not resolveable. Creating placeholder" << endl;
00301                 Import_Utils::createGeneralization(m_klass, baseName);
00302             }
00303             advance();
00304         }
00305         if (m_source[m_srcIndex] == "implements") {
00306             while (m_srcIndex < srcLength - 1 && advance() != "{") {
00307                 const QString& baseName = m_source[m_srcIndex];
00308                 // try to resolve the interface we are implementing, if this fails
00309                 // create a placeholder
00310                 UMLObject *interface = resolveClass( baseName );
00311                 if ( interface ) {
00312                      Import_Utils::createGeneralization(m_klass, static_cast<UMLClassifier*>(interface));
00313                 } else {
00314                     kDebug() << "importJava implementing interface "<< baseName
00315                         <<" is not resolvable. Creating placeholder" <<endl;
00316                     Import_Utils::createGeneralization(m_klass, baseName);
00317                 }
00318                 if (advance() != ",")
00319                     break;
00320             }
00321         }
00322         if (m_source[m_srcIndex] != "{") {
00323             kError() << "importJava: ignoring excess chars at " << name
00324                 << " (" << m_source[m_srcIndex] << ")" << endl;
00325             skipStmt("{");
00326         }
00327         return true;
00328     }
00329     if (keyword == "enum") {
00330         const QString& name = advance();
00331         UMLObject *ns = Import_Utils::createUMLObject(Uml::ot_Enum,
00332                         name, m_scope[m_scopeIndex], m_comment);
00333         UMLEnum *enumType = static_cast<UMLEnum*>(ns);
00334         skipStmt("{");
00335         while (m_srcIndex < srcLength - 1 && advance() != "}") {
00336             Import_Utils::addEnumLiteral(enumType, m_source[m_srcIndex]);
00337             QString next = advance();
00338             if (next == "{" || next == "(") {
00339                 if (! skipToClosing(next[0]))
00340                     return false;
00341                 next = advance();
00342             }
00343             if (next != ",") {
00344                 if (next == ";") {
00345                     // @todo handle methods in enum
00346                     // For now, we cheat (skip them)
00347                     m_source[m_srcIndex] = "{";
00348                     if (! skipToClosing('{'))
00349                         return false;
00350                 }
00351                 break;
00352             }
00353         }
00354         return true;
00355     }
00356     if (keyword == "static") {
00357         m_isStatic = true;
00358         return true;
00359     }
00360     // if we detected static previously and keyword is { then this is a static block
00361     if (m_isStatic && keyword == "{") {
00362         // reset static flag and jump to end of static block
00363         m_isStatic = false;
00364         return skipToClosing('{');
00365     }
00366     if (keyword == "abstract") {
00367         m_isAbstract = true;
00368         return true;
00369     }
00370     if (keyword == "public") {
00371         m_currentAccess = Uml::Visibility::Public;
00372         return true;
00373     }
00374     if (keyword == "protected") {
00375         m_currentAccess = Uml::Visibility::Protected;
00376         return true;
00377     }
00378     if (keyword == "private") {
00379         m_currentAccess = Uml::Visibility::Private;
00380         return true;
00381     }
00382     if (keyword == "final" ||
00383         keyword == "native" ||
00384         keyword == "synchronized" ||
00385         keyword == "transient" ||
00386         keyword == "volatile") {
00387         //@todo anything to do here?
00388         return true;
00389     }
00390     if (keyword == "import") {
00391         // keep track of imports so we can resolve classes we are dependent on
00392         QString import = advance();
00393         if ( import.endsWith(".") ) {
00394             //this most likely an import that ends with a *
00395             //
00396             import = import + advance();
00397         }
00398         m_imports.append( import );
00399 
00400         // move past ;
00401         skipStmt();
00402         return true;
00403     }
00404     if (keyword == "@") {  // annotation
00405         advance();
00406         if (m_source[m_srcIndex + 1] == "(") {
00407             advance();
00408             skipToClosing('(');
00409         }
00410         return true;
00411     }
00412     if (keyword == "}") {
00413         if (m_scopeIndex)
00414             m_klass = dynamic_cast<UMLClassifier*>(m_scope[--m_scopeIndex]);
00415         else
00416             kError() << "importJava: too many }" << endl;
00417         return true;
00418     }
00419     // At this point, we expect `keyword' to be a type name
00420     // (of a member of class or interface, or return type
00421     // of an operation.) Up next is the name of the attribute
00422     // or operation.
00423     if (! keyword.contains( QRegExp("^\\w") )) {
00424         kError() << "importJava: ignoring " << keyword << endl;
00425         return false;
00426     }
00427     QString typeName = m_source[m_srcIndex];
00428     typeName = joinTypename(typeName);
00429     // At this point we need a class.
00430     if (m_klass == NULL) {
00431         kError() << "importJava: no class set for " << typeName << endl;
00432         return false;
00433     }
00434     QString name = advance();
00435     QString nextToken;
00436     if (typeName == m_klass->getName() && name == "(") {
00437         // Constructor.
00438         nextToken = name;
00439         name = typeName;
00440         typeName = QString();
00441     } else {
00442         nextToken = advance();
00443     }
00444     if (name.contains( QRegExp("\\W") )) {
00445         kError() << "importJava: expecting name in " << name << endl;
00446         return false;
00447     }
00448     if (nextToken == "(") {
00449         // operation
00450         UMLOperation *op = Import_Utils::makeOperation(m_klass, name);
00451         m_srcIndex++;
00452         while (m_srcIndex < srcLength && m_source[m_srcIndex] != ")") {
00453             QString typeName = m_source[m_srcIndex];
00454             if ( typeName == "final" || typeName.startsWith( "//") ) {
00455                 // ignore the "final" keyword and any comments in method args
00456                 typeName = advance();
00457             }
00458             typeName = joinTypename(typeName);
00459             QString parName = advance();
00460             // the Class might not be resolved yet so resolve it if necessary
00461             UMLObject *obj = resolveClass(typeName);
00462             if (obj) {
00463                 // by prepending the package, unwanted placeholder types will not get created
00464                 typeName = obj->getFullyQualifiedName(".");
00465             }
00466             /* UMLAttribute *att = */ Import_Utils::addMethodParameter(op, typeName, parName);
00467             if (advance() != ",")
00468                 break;
00469             m_srcIndex++;
00470         }
00471         // before adding the method, try resolving the return type
00472         UMLObject *obj = resolveClass(typeName);
00473         if (obj) {
00474             // using the fully qualified name means that a placeholder type will not be created.
00475             typeName = obj->getFullyQualifiedName(".");
00476         }
00477         Import_Utils::insertMethod(m_klass, op, m_currentAccess, typeName,
00478                                    m_isStatic, m_isAbstract, false /*isFriend*/,
00479                                    false /*isConstructor*/, m_comment);
00480         m_isAbstract = m_isStatic = false;
00481         // reset the default visibility
00482         m_currentAccess = m_defaultCurrentAccess;
00483         // At this point we do not know whether the method has a body or not.
00484         do {
00485             nextToken = advance();
00486         } while (nextToken != "{" && nextToken != ";");
00487         if (nextToken == ";") {
00488             // No body (interface or abstract)
00489             return true;
00490         } else {
00491             return skipToClosing('{');
00492         }
00493     }
00494     // At this point we know it's some kind of attribute declaration.
00495     while (1) {
00496         while (nextToken != "," && nextToken != ";") {
00497             if (nextToken == "=") {
00498                 if ((nextToken = advance()) == "new") {
00499                     advance();
00500                     if ((nextToken = advance()) == "(") {
00501                         skipToClosing('(');
00502                         if ((nextToken = advance()) == "{") {
00503                             skipToClosing('{');
00504                         } else {
00505                             skipStmt();
00506                             break;
00507                         }
00508                     } else {
00509                         skipStmt();
00510                         break;
00511                     }
00512                 } else {
00513                     skipStmt();
00514                     break;
00515                 }
00516             } else {
00517                 name += nextToken;  // add possible array dimensions to `name'
00518             }
00519             nextToken = advance();
00520         }
00521         // try to resolve the class type, or create a placeholder if that fails
00522         UMLObject *type = resolveClass( typeName );
00523         UMLObject *o;
00524         if (type) {
00525             o = Import_Utils::insertAttribute(m_klass, m_currentAccess, name,
00526                                            static_cast<UMLClassifier*>(type), m_comment, m_isStatic);
00527         } else {
00528             o = Import_Utils::insertAttribute(m_klass, m_currentAccess, name,
00529                                                   typeName, m_comment, m_isStatic);
00530         }
00531         // UMLAttribute *attr = static_cast<UMLAttribute*>(o);
00532         if (nextToken != ",") {
00533             // reset the modifiers
00534             m_isStatic = m_isAbstract = false;
00535             break;
00536         }
00537         name = advance();
00538         nextToken = advance();
00539     }
00540     // reset visibility to default
00541     m_currentAccess = m_defaultCurrentAccess;
00542     if (m_source[m_srcIndex] != ";") {
00543         kError() << "importJava: ignoring trailing items at " << name << endl;
00544         skipStmt();
00545     }
00546     return true;
00547 }
00548 
00549 
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:57 2007 by doxygen 1.4.1 written by Dimitri van Heesch, © 1997-2003