AirRAC Logo  0.2.0
C++ Simulated Revenue Accounting (RAC) System Library
YieldParserHelper.cpp
Go to the documentation of this file.
00001 // //////////////////////////////////////////////////////////////////////
00002 // Import section
00003 // //////////////////////////////////////////////////////////////////////
00004 // STL
00005 #include <cassert>
00006 #include <fstream>
00007 #include <vector>
00008 // StdAir
00009 #include <stdair/basic/BasFileMgr.hpp>
00010 #include <stdair/basic/BasConst_Request.hpp>
00011 #include <stdair/bom/BomRoot.hpp>
00012 #include <stdair/service/Logger.hpp>
00013 // Airrac
00014 #include <airrac/command/YieldParserHelper.hpp>
00015 #include <airrac/command/YieldRuleGenerator.hpp>
00016 
00017 namespace AIRRAC {
00018 
00019   namespace YieldParserHelper {
00020 
00021     // //////////////////////////////////////////////////////////////////
00022     //  Semantic actions
00023     // //////////////////////////////////////////////////////////////////
00024 
00025     ParserSemanticAction::
00026     ParserSemanticAction (YieldRuleStruct& ioYieldRule)
00027       : _yieldRule (ioYieldRule) {
00028     }      
00029    
00030     // //////////////////////////////////////////////////////////////////
00031     storeYieldId::
00032     storeYieldId (YieldRuleStruct& ioYieldRule)
00033       : ParserSemanticAction (ioYieldRule) {
00034     }
00035     
00036     // //////////////////////////////////////////////////////////////////
00037     void storeYieldId::operator() (unsigned int iYieldId,
00038                                    boost::spirit::qi::unused_type,
00039                                    boost::spirit::qi::unused_type) const {
00040       _yieldRule.setYieldID (iYieldId);
00041       
00042       // DEBUG
00043       //STDAIR_LOG_DEBUG ( "Yield Id: " << _yieldRule.getYieldID ());
00044 
00045       const stdair::AirlineCode_T lEmptyAirlineCode ("");
00046       _yieldRule.setAirlineCode(lEmptyAirlineCode);
00047       _yieldRule.clearAirlineCodeList();
00048       const stdair::ClassCode_T lEmptyClassCode ("");
00049       _yieldRule.setClassCode(lEmptyClassCode);
00050       _yieldRule.clearClassCodeList();
00051       _yieldRule._itSeconds = 0; 
00052 
00053     }
00054     
00055     // //////////////////////////////////////////////////////////////////
00056     storeOrigin ::
00057     storeOrigin (YieldRuleStruct& ioYieldRule)
00058       : ParserSemanticAction (ioYieldRule) {
00059     }
00060     
00061     // //////////////////////////////////////////////////////////////////
00062     void storeOrigin::operator() (std::vector<char> iChar,
00063                                   boost::spirit::qi::unused_type,
00064                                   boost::spirit::qi::unused_type) const {
00065        const stdair::AirportCode_T lOrigin (iChar.begin(), iChar.end());
00066        _yieldRule.setOrigin (lOrigin);
00067        // DEBUG
00068        //STDAIR_LOG_DEBUG ( "Origin: " << _yieldRule.getOrigin ());
00069     }
00070 
00071     // //////////////////////////////////////////////////////////////////
00072     storeDestination ::
00073     storeDestination (YieldRuleStruct& ioYieldRule)
00074       : ParserSemanticAction (ioYieldRule) {
00075     }
00076     
00077     // //////////////////////////////////////////////////////////////////
00078     void storeDestination::operator() (std::vector<char> iChar,
00079                                        boost::spirit::qi::unused_type,
00080                                        boost::spirit::qi::unused_type) const {
00081        const stdair::AirportCode_T lDestination (iChar.begin(), iChar.end());
00082        _yieldRule.setDestination (lDestination);
00083        // DEBUG
00084        //STDAIR_LOG_DEBUG ( "Destination: " << _yieldRule.getDestination ());
00085     }
00086 
00087     // //////////////////////////////////////////////////////////////////
00088     storeTripType ::
00089     storeTripType (YieldRuleStruct& ioYieldRule)
00090       : ParserSemanticAction (ioYieldRule) {
00091     }
00092     
00093     // //////////////////////////////////////////////////////////////////
00094     void storeTripType::operator() (std::vector<char> iChar,
00095                                     boost::spirit::qi::unused_type,
00096                                     boost::spirit::qi::unused_type) const {
00097       const stdair::TripType_T lTripType (iChar.begin(), iChar.end());
00098       if (lTripType == "OW" || lTripType == "RT") {
00099          _yieldRule.setTripType (lTripType);
00100       } else {
00101         // ERROR
00102         STDAIR_LOG_ERROR ("Invalid trip type  " << lTripType);
00103       }                       
00104       // DEBUG
00105       //STDAIR_LOG_DEBUG ("TripType: " << _yieldRule.getTripType ());
00106     }
00107 
00108     
00109     // //////////////////////////////////////////////////////////////////
00110     storeDateRangeStart::
00111     storeDateRangeStart (YieldRuleStruct& ioYieldRule)
00112       : ParserSemanticAction (ioYieldRule) {
00113     }
00114     
00115     // //////////////////////////////////////////////////////////////////
00116     void storeDateRangeStart::operator() (boost::spirit::qi::unused_type,
00117                                           boost::spirit::qi::unused_type,
00118                                           boost::spirit::qi::unused_type) const {
00119       const stdair::Date_T& lDateStart = _yieldRule.calculateDate ();
00120       _yieldRule.setDateRangeStart (lDateStart);
00121       // DEBUG
00122       //STDAIR_LOG_DEBUG ("Date Range Start: "<< _yieldRule.getDateRangeStart ());
00123     }
00124 
00125     // //////////////////////////////////////////////////////////////////
00126     storeDateRangeEnd::
00127     storeDateRangeEnd(YieldRuleStruct& ioYieldRule)
00128       : ParserSemanticAction (ioYieldRule) {
00129     }
00130     
00131     // //////////////////////////////////////////////////////////////////
00132     void storeDateRangeEnd::operator() (boost::spirit::qi::unused_type,
00133                                         boost::spirit::qi::unused_type,
00134                                         boost::spirit::qi::unused_type) const {
00135       const stdair::Date_T& lDateEnd = _yieldRule.calculateDate ();
00136       // As a Boost date period (DatePeriod_T) defines the last day of
00137       // the period to be end-date - one day, we have to add one day to that
00138       // end date before.
00139       const stdair::DateOffset_T oneDay (1);
00140       const stdair::Date_T lBoostDateEnd = lDateEnd + oneDay;
00141       _yieldRule.setDateRangeEnd (lBoostDateEnd);
00142        // DEBUG
00143        //STDAIR_LOG_DEBUG ("Date Range End: " << _yieldRule.getDateRangeEnd ());
00144     }
00145 
00146     // //////////////////////////////////////////////////////////////////
00147     storeStartRangeTime::
00148     storeStartRangeTime (YieldRuleStruct& ioYieldRule)
00149       : ParserSemanticAction (ioYieldRule) {
00150     }
00151     
00152     // //////////////////////////////////////////////////////////////////
00153     void storeStartRangeTime::operator() (boost::spirit::qi::unused_type,
00154                                           boost::spirit::qi::unused_type,
00155                                           boost::spirit::qi::unused_type) const {
00156       const stdair::Duration_T& lTimeStart = _yieldRule.calculateTime ();
00157       _yieldRule.setTimeRangeStart (lTimeStart);
00158       // DEBUG
00159       //STDAIR_LOG_DEBUG ("Time Range Start: " << _yieldRule.getTimeRangeStart ());
00160       // Reset the number of seconds
00161       _yieldRule._itSeconds = 0;
00162     }
00163 
00164     // //////////////////////////////////////////////////////////////////
00165     storeEndRangeTime::
00166     storeEndRangeTime (YieldRuleStruct& ioYieldRule)
00167       : ParserSemanticAction (ioYieldRule) {
00168     }
00169     
00170     // //////////////////////////////////////////////////////////////////
00171     void storeEndRangeTime::operator() (boost::spirit::qi::unused_type,
00172                                         boost::spirit::qi::unused_type,
00173                                         boost::spirit::qi::unused_type) const {
00174       const stdair::Duration_T& lTimeEnd = _yieldRule.calculateTime ();
00175       _yieldRule.setTimeRangeEnd (lTimeEnd);
00176       // DEBUG
00177       //STDAIR_LOG_DEBUG ("Time Range End: " << _yieldRule.getTimeRangeEnd ());
00178       // Reset the number of seconds
00179       _yieldRule._itSeconds = 0;
00180     }
00181 
00182     // //////////////////////////////////////////////////////////////////
00183     storePOS ::
00184     storePOS (YieldRuleStruct& ioYieldRule)
00185       : ParserSemanticAction (ioYieldRule) {
00186     }
00187     
00188     // //////////////////////////////////////////////////////////////////
00189     void storePOS::operator() (std::vector<char> iChar,
00190                                boost::spirit::qi::unused_type,
00191                                boost::spirit::qi::unused_type) const {
00192       const stdair::CityCode_T lPOS (iChar.begin(), iChar.end());
00193       if (lPOS == _yieldRule.getOrigin() || lPOS == _yieldRule.getDestination() ) {
00194         _yieldRule.setPOS (lPOS);
00195       } else if (lPOS == "ROW") {
00196         const stdair::CityCode_T lPOSROW ("ROW");
00197         _yieldRule.setPOS (lPOSROW);
00198       } else if (lPOS == stdair::DEFAULT_POS) {
00199         _yieldRule.setPOS (stdair::DEFAULT_POS);
00200       } else {
00201         // ERROR
00202         STDAIR_LOG_ERROR ("Invalid point of sale " << lPOS);
00203       }
00204       // DEBUG
00205       //STDAIR_LOG_DEBUG ("POS: " << _yieldRule.getPOS ());
00206     }
00207 
00208     // //////////////////////////////////////////////////////////////////
00209     storeCabinCode ::
00210     storeCabinCode (YieldRuleStruct& ioYieldRule)
00211       : ParserSemanticAction (ioYieldRule) {
00212     }
00213     
00214     // //////////////////////////////////////////////////////////////////
00215     void storeCabinCode::operator() (char iChar,
00216                                      boost::spirit::qi::unused_type,
00217                                      boost::spirit::qi::unused_type) const {
00218       std::ostringstream ostr;
00219       ostr << iChar;
00220       const std::string& cabinCodeStr = ostr.str();
00221       const stdair::CabinCode_T lCabinCode (cabinCodeStr);
00222       _yieldRule.setCabinCode (lCabinCode);
00223      
00224       // DEBUG
00225       //STDAIR_LOG_DEBUG ("Cabin Code: " << _yieldRule.getCabinCode ());                 
00226     
00227     }
00228 
00229     // //////////////////////////////////////////////////////////////////
00230     storeChannel ::
00231     storeChannel (YieldRuleStruct& ioYieldRule)
00232       : ParserSemanticAction (ioYieldRule) {
00233     }
00234     
00235     // //////////////////////////////////////////////////////////////////
00236     void storeChannel::operator() (std::vector<char> iChar,
00237                                    boost::spirit::qi::unused_type,
00238                                    boost::spirit::qi::unused_type) const {
00239       const stdair::ChannelLabel_T lChannel (iChar.begin(), iChar.end());
00240       if (lChannel != "IN" && lChannel != "IF" && lChannel != "DN"
00241           && lChannel != "DF" && lChannel != stdair::DEFAULT_CHANNEL) {
00242         // ERROR
00243         STDAIR_LOG_ERROR ("Invalid channel " << lChannel);
00244       }
00245       _yieldRule.setChannel (lChannel);
00246       // DEBUG
00247       //STDAIR_LOG_DEBUG ("Channel: " << _yieldRule.getChannel ());
00248     }
00249 
00250     // //////////////////////////////////////////////////////////////////
00251     storeYield::
00252     storeYield (YieldRuleStruct& ioYieldRule)
00253       : ParserSemanticAction (ioYieldRule) {
00254     }
00255     
00256     // //////////////////////////////////////////////////////////////////
00257     void storeYield::operator() (double iYield,
00258                                 boost::spirit::qi::unused_type,
00259                                 boost::spirit::qi::unused_type) const {
00260       const stdair::YieldValue_T lYield= iYield;
00261       _yieldRule.setYield (lYield);
00262       // DEBUG
00263       //STDAIR_LOG_DEBUG ("Yield: " << _yieldRule.getYield ());
00264     }
00265 
00266     // //////////////////////////////////////////////////////////////////
00267     storeAirlineCode ::
00268     storeAirlineCode (YieldRuleStruct& ioYieldRule)
00269       : ParserSemanticAction (ioYieldRule) {
00270     }
00271     
00272     // //////////////////////////////////////////////////////////////////
00273     void storeAirlineCode::operator() (std::vector<char> iChar,
00274                                        boost::spirit::qi::unused_type,
00275                                        boost::spirit::qi::unused_type) const {
00276 
00277       const stdair::AirlineCode_T lAirlineCode (iChar.begin(), iChar.end());
00278       // Update the airline code
00279       _yieldRule.setAirlineCode (lAirlineCode);
00280       // Insertion of this airline Code list in the whole AirlineCode name
00281       _yieldRule.addAirlineCode (lAirlineCode); 
00282       // DEBUG
00283       //STDAIR_LOG_DEBUG ( "Airline code: " << lAirlineCode);
00284     }
00285 
00286     // //////////////////////////////////////////////////////////////////
00287     storeClass ::
00288     storeClass (YieldRuleStruct& ioYieldRule)
00289       : ParserSemanticAction (ioYieldRule) {
00290     }
00291     
00292     // //////////////////////////////////////////////////////////////////
00293     void storeClass::operator() (std::vector<char> iChar,
00294                                  boost::spirit::qi::unused_type,
00295                                  boost::spirit::qi::unused_type) const {
00296       std::ostringstream ostr;
00297       for (std::vector<char>::const_iterator lItVector = iChar.begin();
00298          lItVector != iChar.end();
00299          lItVector++) {
00300         ostr << *lItVector;
00301       }
00302       const std::string& classCodeStr = ostr.str();
00303       const stdair::ClassCode_T lClassCode (classCodeStr);
00304       // Insertion of this class Code list in the whole classCode name
00305       _yieldRule.addClassCode (lClassCode);
00306       // DEBUG
00307       //STDAIR_LOG_DEBUG ("Class Code: " << classCodeStr);
00308     }
00309     
00310     // //////////////////////////////////////////////////////////////////
00311     doEndYield::
00312     doEndYield (stdair::BomRoot& ioBomRoot,
00313                YieldRuleStruct& ioYieldRule)
00314       : ParserSemanticAction (ioYieldRule),
00315         _bomRoot (ioBomRoot) {
00316     }
00317     
00318     // //////////////////////////////////////////////////////////////////
00319     void doEndYield::operator() (boost::spirit::qi::unused_type,
00320                                 boost::spirit::qi::unused_type,
00321                                 boost::spirit::qi::unused_type) const {
00322       // DEBUG
00323       // STDAIR_LOG_DEBUG ("Do End");
00324       // Generation of the yield rule object.
00325       YieldRuleGenerator::createAirportPair (_bomRoot, _yieldRule);
00326       STDAIR_LOG_DEBUG (_yieldRule.describe());
00327     }  
00328     
00329     // ///////////////////////////////////////////////////////////////////
00330     //
00331     //  Utility Parsers
00332     //
00333     // ///////////////////////////////////////////////////////////////////
00335     namespace bsq = boost::spirit::qi;
00336     namespace bsa = boost::spirit::ascii;
00337     
00339     stdair::int1_p_t int1_p;
00340     
00342     stdair::uint2_p_t uint2_p;
00343 
00345     stdair::uint4_p_t uint4_p;
00346     
00348     stdair::uint1_4_p_t uint1_4_p;
00349 
00351     stdair::hour_p_t hour_p;
00352     stdair::minute_p_t minute_p;
00353     stdair::second_p_t second_p;
00354 
00356     stdair::year_p_t year_p;
00357     stdair::month_p_t month_p;
00358     stdair::day_p_t day_p;
00359         
00360     // //////////////////////////////////////////////////////////////////
00361     //  (Boost Spirit) Grammar Definition
00362     // //////////////////////////////////////////////////////////////////
00363     
00364     // //////////////////////////////////////////////////////////////////
00365     YieldRuleParser::YieldRuleParser (stdair::BomRoot& ioBomRoot,
00366                                       YieldRuleStruct& ioYieldRule) :
00367       YieldRuleParser::base_type(start),
00368       _bomRoot(ioBomRoot), _yieldRule(ioYieldRule) {
00369 
00370       start = *(comments | yield_rule);
00371 
00372       comments = (bsq::lexeme[bsq::repeat(2)[bsa::char_('/')]
00373                               >> +(bsa::char_ - bsq::eol)
00374                               >> bsq::eol]
00375                   | bsq::lexeme[bsa::char_('/') >>bsa::char_('*') 
00376                                 >> +(bsa::char_ - bsa::char_('*')) 
00377                                 >> bsa::char_('*') >> bsa::char_('/')]);
00378       
00379       yield_rule =  yield_id
00380         >> ';' >> origin >> ';' >> destination
00381         >> ';' >> tripType
00382         >> ';' >> dateRangeStart >> ';' >> dateRangeEnd
00383         >> ';' >> timeRangeStart >> ';' >> timeRangeEnd
00384         >> ';' >> point_of_sale >>  ';' >> cabinCode
00385         >> ';' >> channel >> ';' >> yield
00386         >> +( ';' >> segment )
00387         >> yield_rule_end[doEndYield(_bomRoot, _yieldRule)];
00388         ;
00389 
00390       yield_id = uint1_4_p[storeYieldId(_yieldRule)];
00391 
00392       origin = bsq::repeat(3)[bsa::char_("A-Z")][storeOrigin(_yieldRule)];
00393       
00394       destination =  
00395         bsq::repeat(3)[bsa::char_("A-Z")][storeDestination(_yieldRule)];
00396 
00397       
00398 tripType =
00399         bsq::repeat(2)[bsa::char_("A-Z")][storeTripType(_yieldRule)];
00400       
00401       dateRangeStart = date[storeDateRangeStart(_yieldRule)];
00402 
00403       dateRangeEnd = date[storeDateRangeEnd(_yieldRule)];
00404 
00405       date = bsq::lexeme
00406         [year_p[boost::phoenix::ref(_yieldRule._itYear) = bsq::labels::_1]
00407         >> '-'
00408         >> month_p[boost::phoenix::ref(_yieldRule._itMonth) = bsq::labels::_1]
00409         >> '-'
00410         >> day_p[boost::phoenix::ref(_yieldRule._itDay) = bsq::labels::_1] ];
00411 
00412       timeRangeStart = time[storeStartRangeTime(_yieldRule)];
00413       
00414       timeRangeEnd = time[storeEndRangeTime(_yieldRule)];
00415 
00416       time =  bsq::lexeme
00417         [hour_p[boost::phoenix::ref(_yieldRule._itHours) = bsq::labels::_1]
00418         >> ':'
00419         >> minute_p[boost::phoenix::ref(_yieldRule._itMinutes) = bsq::labels::_1]      
00420         >> - (':' >> second_p[boost::phoenix::ref(_yieldRule._itSeconds) = bsq::labels::_1]) ];
00421 
00422       point_of_sale = bsq::repeat(3)[bsa::char_("A-Z")][storePOS(_yieldRule)];
00423 
00424       cabinCode = bsa::char_("A-Z")[storeCabinCode(_yieldRule)];
00425 
00426       channel = bsq::repeat(2)[bsa::char_("A-Z")][storeChannel(_yieldRule)];
00427 
00428       yield = bsq::double_[storeYield(_yieldRule)];
00429 
00430       segment = bsq::repeat(2)[bsa::char_("A-Z")][storeAirlineCode(_yieldRule)]
00431         >> ';'
00432         >> bsq::repeat(1,bsq::inf)[bsa::char_("A-Z")][storeClass(_yieldRule)];
00433 
00434       yield_rule_end = bsa::char_(';');
00435 
00436       // BOOST_SPIRIT_DEBUG_NODE (YieldParser);
00437       BOOST_SPIRIT_DEBUG_NODE (start);
00438       BOOST_SPIRIT_DEBUG_NODE (comments);
00439       BOOST_SPIRIT_DEBUG_NODE (yield_rule);
00440       BOOST_SPIRIT_DEBUG_NODE (yield_id);
00441       BOOST_SPIRIT_DEBUG_NODE (origin);
00442       BOOST_SPIRIT_DEBUG_NODE (destination);
00443       BOOST_SPIRIT_DEBUG_NODE (tripType);
00444       BOOST_SPIRIT_DEBUG_NODE (dateRangeStart);
00445       BOOST_SPIRIT_DEBUG_NODE (dateRangeEnd);
00446       BOOST_SPIRIT_DEBUG_NODE (date);
00447       BOOST_SPIRIT_DEBUG_NODE (timeRangeStart);
00448       BOOST_SPIRIT_DEBUG_NODE (timeRangeEnd);
00449       BOOST_SPIRIT_DEBUG_NODE (time);
00450       BOOST_SPIRIT_DEBUG_NODE (point_of_sale);
00451       BOOST_SPIRIT_DEBUG_NODE (cabinCode);
00452       BOOST_SPIRIT_DEBUG_NODE (channel);
00453       BOOST_SPIRIT_DEBUG_NODE (yield);
00454       BOOST_SPIRIT_DEBUG_NODE (segment);
00455       BOOST_SPIRIT_DEBUG_NODE (yield_rule_end);
00456      
00457     }
00458     
00459   }
00460 
00461 
00463   //
00464   //  Entry class for the file parser
00465   //
00467 
00468   // //////////////////////////////////////////////////////////////////////
00469   YieldFileParser::YieldFileParser (stdair::BomRoot& ioBomRoot,
00470                                     const std::string& iFilename)
00471     : _filename (iFilename), _bomRoot (ioBomRoot) {
00472     init();
00473   }
00474 
00475   // //////////////////////////////////////////////////////////////////////
00476   void YieldFileParser::init() {
00477 
00478     // Check that the file exists and is readable
00479     const bool doesExistAndIsReadable =
00480       stdair::BasFileMgr::doesExistAndIsReadable (_filename);
00481 
00482     if (doesExistAndIsReadable == false) {
00483       STDAIR_LOG_ERROR ("The yield schedule file " << _filename
00484                         << " does not exist or can not be  read.");
00485       
00486       throw YieldInputFileNotFoundException ("The yield file " + _filename + " does not exist or can not be read");
00487     }
00488   }
00489     
00490   // //////////////////////////////////////////////////////////////////////
00491   void YieldFileParser::generateYieldStore () {
00492     
00493     STDAIR_LOG_DEBUG ("Parsing yield input file: " << _filename);
00494 
00495     // File to be parsed
00496     std::ifstream fileToBeParsed (_filename.c_str(), std::ios_base::in);
00497 
00498     // Check the filename exists and can be open
00499     if (fileToBeParsed == false) {
00500       STDAIR_LOG_ERROR ("The yield store file " << _filename
00501                         << " can not be open."
00502                         << std::endl);
00503 
00504       throw YieldInputFileNotFoundException ("The file " + _filename
00505                                              + " does not exist or can not be read");
00506     }
00507     
00508     // Create an input iterator
00509     stdair::base_iterator_t inputBegin (fileToBeParsed);
00510 
00511     // Convert input iterator to an iterator usable by spirit parser  
00512     stdair::iterator_t 
00513       start (boost::spirit::make_default_multi_pass (inputBegin));
00514     stdair::iterator_t end;
00515 
00516     // Initialise the parser (grammar) with the helper/staging structure.
00517     YieldParserHelper::YieldRuleParser lYParser(_bomRoot, _yieldRule);
00518       
00519     // Launch the parsing of the file and, thanks to the doEndYield
00520     // call-back structure, the building of the whole BomRoot BOM
00521     const bool hasParsingBeenSuccesful = 
00522        boost::spirit::qi::phrase_parse (start, end, lYParser,
00523                                         boost::spirit::ascii::space);
00524       
00525     if (hasParsingBeenSuccesful == false) {
00526       // TODO: decide whether to throw an exceqption
00527       STDAIR_LOG_ERROR ("Parsing of yield input file: " << _filename
00528                         << " failed");
00529       throw YieldFileParsingFailedException ("Parsing of yield input file: "
00530                                             + _filename + " failed");
00531     }
00532     if  (start != end) {
00533       // TODO: decide whether to throw an exception
00534       STDAIR_LOG_ERROR ("Parsing of yield input file: " << _filename
00535                         << " failed");
00536       throw YieldFileParsingFailedException ("Parsing of yield input file: "
00537                                             + _filename + " failed");
00538     }
00539     if (hasParsingBeenSuccesful == true && start == end) {
00540       STDAIR_LOG_DEBUG ("Parsing of yield input file: " << _filename
00541       << " succeeded");
00542     }
00543     
00544   }
00545     
00546 }