00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026 #include <algorithm>
00027 #include <clocale>
00028 #include <ctime>
00029 #include <fstream>
00030 #include <iostream>
00031 #include <list>
00032 #include <mpcl/automaton/defs.hh>
00033 #include <mpcl/system/defs.hh>
00034 #include <mpcl/text/regex/matcher.hh>
00035 #include <mpcl/text/string.hh>
00036 #include <mpcl/util/prefs/config_processor.hh>
00037 #include <set>
00038
00039
00040
00041
00042
00043
00044 using mpcl::automaton::TIntegrityException;
00045 using mpcl::text::regex::TMatcher;
00046 using mpcl::text::Format;
00047 using mpcl::text::TString;
00048 using mpcl::util::collection::TMap;
00049 using mpcl::util::prefs::TConfigProcessor;
00050 using mpcl::system::GetErrorMessage;
00051 using std::pair;
00052 using std::set;
00053
00054 typedef
00055 pair<TString, TString>
00056 TConnectionPair;
00057
00058 typedef
00059 pair<TString, TString>
00060 TIdStatePair;
00061
00062 typedef
00063 TMap<TString, TConnectionPair>
00064 TMessageToConnectionMap;
00065
00066 typedef
00067 set<TString>
00068 TStateSet;
00069
00070 typedef
00071 TMap<TIdStatePair, TString>
00072 TStateToActionNameMap;
00073
00074
00075
00076
00077
00078
00080 static const char* _pkcVersion = "0.0.0";
00081
00083 static TStateSet _tFinalStateSet;
00084
00086 static TStateSet _tInitialStateSet;
00087
00089 static TMessageToConnectionMap _tMessageToConnectionMap;
00090
00092 static TStateToActionNameMap _tStateToActionNameMap;
00093
00094
00095
00096
00097
00098
00105 static void _CheckIntegrity (void);
00106
00112 static bool _ParseDiaString (TMatcher& rtSOURCE_MATCHER, TString& ryTARGET);
00113
00115 static void
00116 _ParseDiaClassAttribute ( TMatcher& rtSOURCE_MATCHER ,
00117 TString& ryTARGET_ATTRIBUTE_NAME ,
00118 TString& ryTARGET_ATTRIBUTE_VALUE ,
00119 const TString& rkySOURCE_OBJECT_ID );
00120
00122 static void
00123 _ParseDiaClassOperation ( TMatcher& rtSOURCE_MATCHER ,
00124 TString& ryTARGET_OPERATION_NAME ,
00125 const TString& rkySOURCE_OBJECT_ID );
00126
00128 static bool _ParseEndDiaAttribute (TMatcher& rtSOURCE_MATCHER);
00129
00138 static bool
00139 _ParseDiaAttribute ( TMatcher& rtSOURCE_MATCHER ,
00140 TString& ryTARGET_NAME ,
00141 TString& ryTARGET_TYPE ,
00142 const TString& rkySOURCE_OBJECT_ID );
00143
00151 static bool
00152 _ParseDiaConnections ( TMatcher& rtSOURCE_MATCHER ,
00153 TString& ryTARGET_FROM_ID ,
00154 TString& ryTARGET_TO_ID );
00155
00161 static void
00162 _ParseUmlMessage (TMatcher& rtSOURCE_MATCHER, const TString& rkySOURCE_OBJECT_ID);
00163
00169 static void
00170 _ParseUmlClass (TMatcher& rtSOURCE_MATCHER, const TString& rkySOURCE_OBJECT_ID);
00171
00173 static void _PrintUsage (void);
00174
00176 static void _ResolveStates (void);
00177
00179 static void _WriteActions (void);
00180
00185 static void _WriteDfaml (const TString& rkySOURCE_DFAML_NAME);
00186
00191 static void _WriteTransitions (const TString& rkySOURCE_STATE);
00192
00193
00194
00195
00196
00197
00198 static void _CheckIntegrity (void)
00199 {
00200
00201 std::list<TString>::const_iterator I;
00202 TMessageToConnectionMap::iterator J;
00203 std::list<TString> tReachableStateList;
00204
00205
00206
00207
00208 switch (_tInitialStateSet.size())
00209 {
00210 case 0:
00211 {
00212 throw TIntegrityException ("no initial states", __FILE__, __LINE__);
00213 }
00214 case 1:
00215 {
00216 break;
00217 }
00218 default:
00219 {
00220 throw TIntegrityException ("too many initial states", __FILE__, __LINE__);
00221 }
00222 }
00223
00224
00225
00226
00227
00228 tReachableStateList.push_back (*_tInitialStateSet.begin());
00229 for (I = tReachableStateList.begin(); ( I != tReachableStateList.end() ) ;++I)
00230 {
00231 for (J = _tMessageToConnectionMap.begin(); ( J != _tMessageToConnectionMap.end() ) ;++J)
00232 {
00233
00234
00235
00236 if ( *I == J->second.first )
00237 {
00238
00239
00240
00241
00242 if ( find ( tReachableStateList.begin() ,
00243 tReachableStateList.end() ,
00244 J->second.second ) == tReachableStateList.end() )
00245 {
00246 tReachableStateList.push_back (J->second.second);
00247 }
00248 }
00249 }
00250 }
00251
00252
00253
00254
00255
00256 if ( tReachableStateList.size() != _tStateToActionNameMap.size() )
00257 {
00258 throw TIntegrityException ("there are states not reachable", __FILE__, __LINE__);
00259 }
00260
00261 }
00262
00263
00264 static bool _ParseDiaString (TMatcher& rtSOURCE_MATCHER, TString& ryTARGET)
00265 {
00266
00267 return rtSOURCE_MATCHER.scan ("<dia:string>{sep}*#%s#{sep}*</dia:string>{sep}*", &ryTARGET, NULL);
00268
00269 }
00270
00271
00272 static void _ParseDiaClassOperation ( TMatcher& rtSOURCE_MATCHER ,
00273 TString& ryTARGET_OPERATION_NAME ,
00274 const TString& rkySOURCE_OBJECT_ID )
00275 {
00276
00277 TString yNull;
00278
00279 while ( _ParseDiaAttribute ( rtSOURCE_MATCHER ,
00280 ryTARGET_OPERATION_NAME ,
00281 yNull ,
00282 rkySOURCE_OBJECT_ID ) );
00283
00284 }
00285
00286
00287 static void _ParseDiaClassAttribute ( TMatcher& rtSOURCE_MATCHER ,
00288 TString& ryTARGET_ATTRIBUTE_NAME ,
00289 TString& ryTARGET_ATTRIBUTE_VALUE ,
00290 const TString& rkySOURCE_OBJECT_ID )
00291 {
00292
00293 while ( _ParseDiaAttribute ( rtSOURCE_MATCHER ,
00294 ryTARGET_ATTRIBUTE_NAME ,
00295 ryTARGET_ATTRIBUTE_VALUE ,
00296 rkySOURCE_OBJECT_ID ) );
00297
00298 }
00299
00300
00301 static bool _ParseEndDiaAttribute (TMatcher& rtSOURCE_MATCHER)
00302 {
00303
00304 return rtSOURCE_MATCHER.scan ("</dia:attribute>{sep}*", NULL);
00305
00306 }
00307
00308
00309 static bool _ParseDiaAttribute ( TMatcher& rtSOURCE_MATCHER ,
00310 TString& ryTARGET_NAME ,
00311 TString& ryTARGET_TYPE ,
00312 const TString& rkySOURCE_OBJECT_ID )
00313 {
00314
00315 int iRet;
00316 TString yActions;
00317 TString yDiaAttributeName;
00318 TString yDiaString;
00319 TString yDiaOperation;
00320 TString yNull;
00321
00322 iRet = rtSOURCE_MATCHER.scan ("<dia:attribute name=%q>{sep}*", &yDiaAttributeName, NULL);
00323 if ( !iRet )
00324 {
00325
00326
00327
00328 iRet = rtSOURCE_MATCHER.scan ("<dia:attribute name=%q/>{sep}*", &yNull, NULL);
00329 }
00330 else
00331 {
00332 if ( ( yDiaAttributeName == "name" ) ||
00333 ( yDiaAttributeName == "text" ) )
00334 {
00335 _ParseDiaString (rtSOURCE_MATCHER, yDiaString);
00336 ryTARGET_NAME = yDiaString;
00337 _ParseEndDiaAttribute (rtSOURCE_MATCHER);
00338 }
00339 else if ( yDiaAttributeName == "value" )
00340 {
00341 _ParseDiaString (rtSOURCE_MATCHER, yDiaString);
00342 ryTARGET_TYPE = yDiaString;
00343 _ParseEndDiaAttribute (rtSOURCE_MATCHER);
00344 }
00345 else if ( yDiaAttributeName == "operations" )
00346 {
00347
00348
00349
00350 yActions.erase();
00351 while ( rtSOURCE_MATCHER.scan ("<dia:composite type=\"umloperation\">{sep}*", NULL) )
00352 {
00353 _ParseDiaClassOperation (rtSOURCE_MATCHER, yDiaOperation, rkySOURCE_OBJECT_ID);
00354 rtSOURCE_MATCHER.scan ("</dia:composite>{sep}*", NULL);
00355 if ( !yActions.empty() )
00356 {
00357 yActions += ", ";
00358 }
00359 yActions += yDiaOperation;
00360 }
00361 _tStateToActionNameMap.bind (make_pair (rkySOURCE_OBJECT_ID, ryTARGET_NAME), yActions);
00362 _ParseEndDiaAttribute (rtSOURCE_MATCHER);
00363 }
00364 else if ( yDiaAttributeName == "attributes" )
00365 {
00366 TString yAttributeName;
00367 TString yAttributeValue;
00368
00369
00370
00371
00372 while ( rtSOURCE_MATCHER.scan ("<dia:composite type=\"umlattribute\">{sep}*", NULL) )
00373 {
00374 _ParseDiaClassAttribute (rtSOURCE_MATCHER, yAttributeName, yAttributeValue, rkySOURCE_OBJECT_ID);
00375 rtSOURCE_MATCHER.scan ("</dia:composite>{sep}*", NULL);
00376 }
00377 if ( yAttributeName == "type" )
00378 {
00379 if ( yAttributeValue == "final" )
00380 {
00381 _tFinalStateSet.insert (ryTARGET_NAME);
00382 }
00383 else if ( yAttributeValue == "initial" )
00384 {
00385 _tInitialStateSet.insert (ryTARGET_NAME);
00386 }
00387 }
00388 _ParseEndDiaAttribute (rtSOURCE_MATCHER);
00389 }
00390 else
00391 {
00392
00393
00394
00395 while ( true )
00396 {
00397 if ( !rtSOURCE_MATCHER.scan ("<dia:%s val=%q/>{sep}*", NULL) )
00398 {
00399 if ( !rtSOURCE_MATCHER.scan ("<dia:string>%t</dia:string>{sep}*", NULL) )
00400 {
00401 if ( !rtSOURCE_MATCHER.scan ("<dia:string/>{sep}*", NULL) )
00402 {
00403 break;
00404 }
00405 }
00406 }
00407 }
00408 }
00409 _ParseEndDiaAttribute (rtSOURCE_MATCHER);
00410 }
00411 return iRet;
00412
00413 }
00414
00415
00416 static bool _ParseDiaConnections ( TMatcher& rtSOURCE_MATCHER ,
00417 TString& ryTARGET_FROM_ID ,
00418 TString& ryTARGET_TO_ID )
00419 {
00420
00421 TString yNull;
00422
00423 return rtSOURCE_MATCHER.scan ( "<dia:connections>{sep}*"
00424 "<dia:connection handle=%q to=%q connection=%q/>{sep}*"
00425 "<dia:connection handle=%q to=%q connection=%q/>{sep}*"
00426 "</dia:connections>{sep}*" ,
00427 &yNull ,
00428 &ryTARGET_FROM_ID ,
00429 &yNull ,
00430 &yNull ,
00431 &ryTARGET_TO_ID ,
00432 &yNull ,
00433 NULL );
00434
00435 }
00436
00437
00438 static void _ParseUmlClass ( TMatcher& rtSOURCE_MATCHER ,
00439 const TString& rkySOURCE_OBJECT_ID )
00440 {
00441
00442 TStateToActionNameMap::const_iterator I;
00443 TString yDiaClassName;
00444 TString yDiaClassType;
00445
00446 while ( _ParseDiaAttribute (rtSOURCE_MATCHER, yDiaClassName, yDiaClassType, rkySOURCE_OBJECT_ID) );
00447 I = _tStateToActionNameMap.find (make_pair (rkySOURCE_OBJECT_ID, yDiaClassName));
00448 if ( I == _tStateToActionNameMap.end() )
00449 {
00450 _tStateToActionNameMap.bind (make_pair (rkySOURCE_OBJECT_ID, yDiaClassName), "");
00451 }
00452 rtSOURCE_MATCHER.scan ("</dia:object>{sep}*", NULL);
00453
00454 }
00455
00456
00457 static void
00458 _ParseUmlMessage (TMatcher& rtSOURCE_MATCHER, const TString& rkySOURCE_OBJECT_ID)
00459 {
00460
00461 TString yDiaMessageName;
00462 TString yFromId;
00463 TString yNull;
00464 TString yToId;
00465
00466 for (;;)
00467 {
00468 if ( !_ParseDiaAttribute (rtSOURCE_MATCHER, yDiaMessageName, yNull, rkySOURCE_OBJECT_ID) )
00469 {
00470 if ( !_ParseDiaConnections (rtSOURCE_MATCHER, yFromId, yToId) )
00471 {
00472 break;
00473 }
00474 else
00475 {
00476 _tMessageToConnectionMap.bind (yDiaMessageName, make_pair (yFromId, yToId));
00477 }
00478 }
00479 }
00480 rtSOURCE_MATCHER.scan ("</dia:object>{sep}*", NULL);
00481
00482 }
00483
00484
00485 static void _ResolveStates (void)
00486 {
00487
00488 TMessageToConnectionMap::iterator I;
00489 TStateToActionNameMap::const_iterator J;
00490 register short int K;
00491
00492 for (I = _tMessageToConnectionMap.begin(); ( I != _tMessageToConnectionMap.end() ) ;++I)
00493 {
00494 J = _tStateToActionNameMap.begin();
00495 K = 0;
00496 for (; ( ( K < 2 ) && ( J != _tStateToActionNameMap.end() ) ) ;++J)
00497 {
00498
00499
00500
00501 if ( I->second.first == J->first.first )
00502 {
00503 I->second.first = J->first.second;
00504 ++K;
00505 }
00506
00507
00508
00509 if ( I->second.second == J->first.first )
00510 {
00511 I->second.second = J->first.second;
00512 ++K;
00513 }
00514 }
00515 }
00516
00517 }
00518
00519
00520 static void _WriteActions (void)
00521 {
00522
00523 TStateToActionNameMap::iterator I;
00524
00525 for (I = _tStateToActionNameMap.begin(); ( I != _tStateToActionNameMap.end() ) ;++I)
00526 {
00527 if ( !I->second.empty() )
00528 {
00529 std::cout << " <action state=\"" << I->first.second
00530 << "\" actions=\"" << I->second
00531 << "\">\n";
00532 }
00533 }
00534
00535 }
00536
00537
00538 static void _WriteDfaml (const TString& rkySOURCE_DFAML_NAME)
00539 {
00540
00541 using std::cout;
00542 using std::time_t;
00543
00544 TStateToActionNameMap::const_iterator I;
00545 char* pcCurrentTime;
00546 time_t tCurrentTime;
00547 TString yDtdVersion;
00548 TString yInitialState = *_tInitialStateSet.begin();
00549
00550
00551
00552
00553 yDtdVersion = ( _tStateToActionNameMap.empty() ) ? "1.0" : "2.0";
00554
00555
00556
00557
00558
00559 std::time (&tCurrentTime);
00560 pcCurrentTime = std::ctime (&tCurrentTime);
00561 *(pcCurrentTime + std::strlen (pcCurrentTime) - 1) = '\0';
00562
00563
00564
00565
00566 cout << Format ( "<!doctype dfaml public \"-//MPCL//DTD DFAML %s//EN\">\n"
00567 "<dfaml name=\"%s\">\n"
00568 " <desc>Created with DIA2DFAML (DFAML %s) on %s.</desc>\n"
00569 " <transtbl initial=\"%s\">\n" ,
00570 yDtdVersion.c_str() ,
00571 rkySOURCE_DFAML_NAME.c_str() ,
00572 yDtdVersion.c_str() ,
00573 pcCurrentTime ,
00574 yInitialState.c_str() );
00575
00576
00577
00578
00579 for (I = _tStateToActionNameMap.begin(); ( I != _tStateToActionNameMap.end() ) ;++I)
00580 {
00581 cout << " <state name=\"" << I->first.second;
00582
00583
00584
00585
00586 if ( _tFinalStateSet.find (I->first.second) == _tFinalStateSet.end() )
00587 {
00588 cout << "\">\n";
00589 }
00590 else
00591 {
00592 cout << "\" type=\"final\">\n";
00593 }
00594 _WriteTransitions (I->first.second);
00595 cout << " </state>\n";
00596 }
00597 cout << " </transtbl>\n";
00598
00599
00600
00601
00602 if ( !_tStateToActionNameMap.empty() )
00603 {
00604 cout << " <actlist>\n";
00605 _WriteActions();
00606 cout << " </actlist>\n";
00607 }
00608 cout << "</dfaml>\n";
00609
00610 }
00611
00612
00613 static void _WriteTransitions (const TString& rkySOURCE_STATE)
00614 {
00615
00616 TMessageToConnectionMap::iterator I;
00617
00618 for (I = _tMessageToConnectionMap.begin(); ( I != _tMessageToConnectionMap.end() ) ;++I)
00619 {
00620 if ( I->second.first == rkySOURCE_STATE )
00621 {
00622 std::cout << " <transit event=\"" << I->first
00623 << "\" next=\"" << I->second.second
00624 << "\">\n";
00625 }
00626 }
00627
00628 }
00629
00630
00632 static void _PrintUsage (void)
00633 {
00634
00635 std::cout << "Usage: diadfaml [OPTION]... [{FILE, -}]\n\n"
00636 << "Overall Options:\n"
00637 << " -n, --name=DFAML_NAME\n\n"
00638 << "Informative output:\n"
00639 << " -h, --help\n"
00640 << " -V, --version\n\n";
00641
00642 }
00643
00644
00646 int main (int argc, const char* argv[])
00647 {
00648
00649 using std::cerr;
00650 using std::cin;
00651 using std::cout;
00652 using std::endl;
00653
00654 TMatcher tMatcher;
00655 TString yNull;
00656 TString yObjectId;
00657 TString yObjectType;
00658 TConfigProcessor tConfig (argc, argv);
00659 int iExitCode = 0;
00660 std::basic_istream<char>* ptInputIstream = NULL;
00661
00662 std::setlocale (LC_ALL, "");
00663 try
00664 {
00665 tConfig.addOption ("name", "n", "generic_dfa");
00666 tConfig.addOption ("help", "h");
00667 tConfig.addOption ("version", "V");
00668 tConfig.processOptions();
00669 if ( tConfig ["help"].isAtCmdLine() )
00670 {
00671 _PrintUsage();
00672 }
00673 else if ( tConfig ["version"].isAtCmdLine() )
00674 {
00675 cout << _pkcVersion << endl;
00676 }
00677 else
00678 {
00679 switch (tConfig.numberOfArguments())
00680 {
00681 case 0:
00682 {
00683
00684
00685
00686 ptInputIstream = &cin;
00687 break;
00688 }
00689 case 1:
00690 {
00691 if ( tConfig.argumentValue (0) == "-" )
00692 {
00693
00694
00695
00696 ptInputIstream = &cin;
00697 }
00698 else
00699 {
00700
00701
00702
00703 ptInputIstream = new std::basic_ifstream<char> (tConfig.argumentValue (0).c_str());
00704 if ( !ptInputIstream->good() )
00705 {
00706 throw mpcl::TNotFoundException (GetErrorMessage(), __FILE__, __LINE__);
00707 }
00708 }
00709 break;
00710 }
00711 default:
00712 {
00713
00714
00715
00716 throw TIntegrityException ("too many files", __FILE__, __LINE__);
00717 }
00718 }
00719 tMatcher.setInput (*ptInputIstream);
00720 tMatcher.define ("{sep}", "[[:space:]]");
00721 tMatcher.scan ( "%t<dia:layer name=\"Background\" visible=\"true\">{sep}*", NULL);
00722 while ( tMatcher.scan ( "<dia:object type=%q version=%q id=%q>{sep}*" ,
00723 &yObjectType ,
00724 &yNull ,
00725 &yObjectId ,
00726 NULL ) )
00727 {
00728 if ( yObjectType == "UML - Class" )
00729 {
00730 _ParseUmlClass (tMatcher, yObjectId);
00731 }
00732 else if ( yObjectType == "UML - Message" )
00733 {
00734 _ParseUmlMessage (tMatcher, yObjectId);
00735 }
00736 else
00737 {
00738 tMatcher.scan("%t</dia:object>{sep}*", NULL);
00739 }
00740 }
00741
00742
00743
00744 _ResolveStates();
00745 _CheckIntegrity();
00746 _WriteDfaml (tConfig ["name"].getValue());
00747 }
00748 }
00749 catch (const mpcl::TException& rktEXCEPTION)
00750 {
00751 iExitCode = 3;
00752 cerr << rktEXCEPTION.what() << endl;
00753 }
00754 catch (const std::exception& rktEXCEPTION)
00755 {
00756 iExitCode = 2;
00757 cerr << rktEXCEPTION.what() << endl;
00758 }
00759 catch (...)
00760 {
00761 iExitCode = 1;
00762 cerr << mpcl::TException ("unhandled exception", NULL, __FILE__, __LINE__).what() << endl;
00763 }
00764
00765
00766
00767
00768 if ( ptInputIstream && ( ptInputIstream != &cin ) )
00769 {
00770 delete ptInputIstream;
00771 }
00772 return iExitCode;
00773
00774 }