00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013 #include "javaimport.h"
00014
00015
00016 #include <qfile.h>
00017 #include <qtextstream.h>
00018 #include <qstringlist.h>
00019 #include <qregexp.h>
00020 #include <kdebug.h>
00021
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
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
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
00114
00115 bool isArray = className.contains('[');
00116
00117
00118 QString baseClassName = className;
00119 baseClassName.remove('[');
00120 baseClassName.remove(']');
00121
00122
00123
00124
00125
00126 QStringList file = QStringList::split( '/', m_currentFileName);
00127
00128
00129
00130 file.pop_back();
00131
00132
00133
00134
00135 QString myDir = file.join( "/" );
00136 QString myFile = '/' + myDir + '/' + baseClassName + ".java";
00137 if ( QFile::exists(myFile) ) {
00138 spawnImport( myFile );
00139 if ( isArray ) {
00140
00141
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
00148
00149
00150 QStringList package = QStringList::split( '.', m_currentPackage);
00151 int dirsInPackageCount = package.size();
00152
00153 for (int count=0; count < dirsInPackageCount; count ++ ) {
00154
00155
00156 file.pop_back();
00157 }
00158
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();
00166 if ( import.endsWith( "*" ) || import.endsWith( baseClassName) ) {
00167
00168
00169
00170 QString aFile = sourceRoot + split.join("/") + '/' + baseClassName + ".java";
00171 if ( QFile::exists(aFile) ) {
00172 spawnImport( aFile );
00173
00174
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 }
00185 if ( isArray ) {
00186
00187
00188 return Import_Utils::createUMLObject(Uml::ot_Class, className, current);
00189 }
00190
00191 return findObject(baseClassName, current);
00192 }
00193 }
00194 }
00195 return NULL;
00196 }
00197
00198
00200 void JavaImport::parseFile(const QString& filename) {
00201 m_currentFileName= filename;
00202 m_imports.clear();
00203
00204
00205 m_defaultCurrentAccess = Uml::Visibility::Implementation;
00206 m_currentAccess = m_defaultCurrentAccess;
00207 s_parseDepth++;
00208
00209
00210 s_filesAlreadyParsed.append(filename);
00211 NativeImportBase::parseFile(filename);
00212 s_parseDepth--;
00213 if ( s_parseDepth <= 0 ) {
00214
00215
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
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
00253
00254
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
00259 if ( m_klass->isInterface() ) {
00260 m_defaultCurrentAccess = Uml::Visibility::Public;
00261 }
00262 if (advance() == ";")
00263 return true;
00264 if (m_source[m_srcIndex] == "<") {
00265
00266
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 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();
00290 }
00291 if (m_source[m_srcIndex] == "extends") {
00292 const QString& baseName = advance();
00293
00294
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
00309
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
00346
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
00361 if (m_isStatic && keyword == "{") {
00362
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
00388 return true;
00389 }
00390 if (keyword == "import") {
00391
00392 QString import = advance();
00393 if ( import.endsWith(".") ) {
00394
00395
00396 import = import + advance();
00397 }
00398 m_imports.append( import );
00399
00400
00401 skipStmt();
00402 return true;
00403 }
00404 if (keyword == "@") {
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
00420
00421
00422
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
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
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
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
00456 typeName = advance();
00457 }
00458 typeName = joinTypename(typeName);
00459 QString parName = advance();
00460
00461 UMLObject *obj = resolveClass(typeName);
00462 if (obj) {
00463
00464 typeName = obj->getFullyQualifiedName(".");
00465 }
00466 Import_Utils::addMethodParameter(op, typeName, parName);
00467 if (advance() != ",")
00468 break;
00469 m_srcIndex++;
00470 }
00471
00472 UMLObject *obj = resolveClass(typeName);
00473 if (obj) {
00474
00475 typeName = obj->getFullyQualifiedName(".");
00476 }
00477 Import_Utils::insertMethod(m_klass, op, m_currentAccess, typeName,
00478 m_isStatic, m_isAbstract, false ,
00479 false , m_comment);
00480 m_isAbstract = m_isStatic = false;
00481
00482 m_currentAccess = m_defaultCurrentAccess;
00483
00484 do {
00485 nextToken = advance();
00486 } while (nextToken != "{" && nextToken != ";");
00487 if (nextToken == ";") {
00488
00489 return true;
00490 } else {
00491 return skipToClosing('{');
00492 }
00493 }
00494
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;
00518 }
00519 nextToken = advance();
00520 }
00521
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
00532 if (nextToken != ",") {
00533
00534 m_isStatic = m_isAbstract = false;
00535 break;
00536 }
00537 name = advance();
00538 nextToken = advance();
00539 }
00540
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