AirInv Logo  0.1.2
C++ Simulated Airline Inventory Management System library
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
airinv.cpp
Go to the documentation of this file.
1 
5 // STL
6 #include <cassert>
7 #include <iostream>
8 #include <sstream>
9 #include <fstream>
10 #include <string>
11 // Boost (Extended STL)
12 #include <boost/program_options.hpp>
13 #include <boost/tokenizer.hpp>
14 #include <boost/regex.hpp>
15 #include <boost/swap.hpp>
16 #include <boost/algorithm/string/case_conv.hpp>
17 // StdAir
18 #include <stdair/basic/BasLogParams.hpp>
19 #include <stdair/basic/BasDBParams.hpp>
20 #include <stdair/service/Logger.hpp>
21 // AirInv
24 // GNU Readline Wrapper
26 
27 // //////// Constants //////
31 const std::string K_AIRINV_DEFAULT_LOG_FILENAME ("airinv.log");
32 
36 const std::string K_AIRINV_DEFAULT_INVENTORY_FILENAME (STDAIR_SAMPLE_DIR
37  "/invdump01.csv");
41 const std::string K_AIRINV_DEFAULT_SCHEDULE_FILENAME (STDAIR_SAMPLE_DIR
42  "/schedule01.csv");
46 const std::string K_AIRINV_DEFAULT_OND_FILENAME (STDAIR_SAMPLE_DIR
47  "/ond01.csv");
48 
52 const std::string K_AIRINV_DEFAULT_YIELD_FILENAME (STDAIR_SAMPLE_DIR
53  "/yieldstore01.csv");
54 
59 const bool K_AIRINV_DEFAULT_BUILT_IN_INPUT = false;
60 
65 const bool K_AIRINV_DEFAULT_FOR_SCHEDULE = false;
66 
70 const int K_AIRINV_EARLY_RETURN_STATUS = 99;
71 
76 typedef std::vector<std::string> TokenList_T;
77 
81 struct Command_T {
82  typedef enum {
83  NOP = 0,
84  QUIT,
85  HELP,
86  LIST,
87  DISPLAY,
88  SELECT,
89  SELL,
90  LAST_VALUE
91  } Type_T;
92 };
93 
94 // ///////// Parsing of Options & Configuration /////////
95 // A helper function to simplify the main part.
96 template<class T> std::ostream& operator<< (std::ostream& os,
97  const std::vector<T>& v) {
98  std::copy (v.begin(), v.end(), std::ostream_iterator<T> (std::cout, " "));
99  return os;
100 }
101 
105 int readConfiguration (int argc, char* argv[],
106  bool& ioIsBuiltin, bool& ioIsForSchedule,
107  stdair::Filename_T& ioInventoryFilename,
108  stdair::Filename_T& ioScheduleInputFilename,
109  stdair::Filename_T& ioODInputFilename,
110  stdair::Filename_T& ioYieldInputFilename,
111  std::string& ioLogFilename) {
112  // Default for the built-in input
113  ioIsBuiltin = K_AIRINV_DEFAULT_BUILT_IN_INPUT;
114 
115  // Default for the inventory or schedule option
116  ioIsForSchedule = K_AIRINV_DEFAULT_FOR_SCHEDULE;
117 
118  // Declare a group of options that will be allowed only on command line
119  boost::program_options::options_description generic ("Generic options");
120  generic.add_options()
121  ("prefix", "print installation prefix")
122  ("version,v", "print version string")
123  ("help,h", "produce help message");
124 
125  // Declare a group of options that will be allowed both on command
126  // line and in config file
127 
128  boost::program_options::options_description config ("Configuration");
129  config.add_options()
130  ("builtin,b",
131  "The sample BOM tree can be either built-in or parsed from an input file. That latter must then be given with the -i/--inventory or -s/--schedule option")
132  ("for_schedule,f",
133  "The BOM tree should be built from a schedule file (instead of from an inventory dump)")
134  ("inventory,i",
135  boost::program_options::value< std::string >(&ioInventoryFilename)->default_value(K_AIRINV_DEFAULT_INVENTORY_FILENAME),
136  "(CSV) input file for the inventory")
137  ("schedule,s",
138  boost::program_options::value< std::string >(&ioScheduleInputFilename)->default_value(K_AIRINV_DEFAULT_SCHEDULE_FILENAME),
139  "(CSV) input file for the schedule")
140  ("ond,o",
141  boost::program_options::value< std::string >(&ioODInputFilename)->default_value(K_AIRINV_DEFAULT_OND_FILENAME),
142  "(CSV) input file for the O&D")
143  ("yield,y",
144  boost::program_options::value< std::string >(&ioYieldInputFilename)->default_value(K_AIRINV_DEFAULT_YIELD_FILENAME),
145  "(CSV) input file for the yield")
146  ("log,l",
147  boost::program_options::value< std::string >(&ioLogFilename)->default_value(K_AIRINV_DEFAULT_LOG_FILENAME),
148  "Filename for the logs")
149  ;
150 
151  // Hidden options, will be allowed both on command line and
152  // in config file, but will not be shown to the user.
153  boost::program_options::options_description hidden ("Hidden options");
154  hidden.add_options()
155  ("copyright",
156  boost::program_options::value< std::vector<std::string> >(),
157  "Show the copyright (license)");
158 
159  boost::program_options::options_description cmdline_options;
160  cmdline_options.add(generic).add(config).add(hidden);
161 
162  boost::program_options::options_description config_file_options;
163  config_file_options.add(config).add(hidden);
164  boost::program_options::options_description visible ("Allowed options");
165  visible.add(generic).add(config);
166 
167  boost::program_options::positional_options_description p;
168  p.add ("copyright", -1);
169 
170  boost::program_options::variables_map vm;
171  boost::program_options::
172  store (boost::program_options::command_line_parser (argc, argv).
173  options (cmdline_options).positional(p).run(), vm);
174 
175  std::ifstream ifs ("airinv.cfg");
176  boost::program_options::store (parse_config_file (ifs, config_file_options),
177  vm);
178  boost::program_options::notify (vm);
179 
180  if (vm.count ("help")) {
181  std::cout << visible << std::endl;
182  return K_AIRINV_EARLY_RETURN_STATUS;
183  }
184 
185  if (vm.count ("version")) {
186  std::cout << PACKAGE_NAME << ", version " << PACKAGE_VERSION << std::endl;
187  return K_AIRINV_EARLY_RETURN_STATUS;
188  }
189 
190  if (vm.count ("prefix")) {
191  std::cout << "Installation prefix: " << PREFIXDIR << std::endl;
192  return K_AIRINV_EARLY_RETURN_STATUS;
193  }
194 
195  if (vm.count ("builtin")) {
196  ioIsBuiltin = true;
197  }
198  const std::string isBuiltinStr = (ioIsBuiltin == true)?"yes":"no";
199  std::cout << "The BOM should be built-in? " << isBuiltinStr << std::endl;
200 
201  if (vm.count ("for_schedule")) {
202  ioIsForSchedule = true;
203  }
204  const std::string isForScheduleStr = (ioIsForSchedule == true)?"yes":"no";
205  std::cout << "The BOM should be built from schedule? " << isForScheduleStr
206  << std::endl;
207 
208  if (ioIsBuiltin == false) {
209 
210  if (ioIsForSchedule == false) {
211  // The BOM tree should be built from parsing an inventory dump
212  if (vm.count ("inventory")) {
213  ioInventoryFilename = vm["inventory"].as< std::string >();
214  std::cout << "Input inventory filename is: " << ioInventoryFilename
215  << std::endl;
216 
217  } else {
218  // The built-in option is not selected. However, no inventory dump
219  // file is specified
220  std::cerr << "Either one among the -b/--builtin, -i/--inventory or "
221  << " -f/--for_schedule and -s/--schedule options "
222  << "must be specified" << std::endl;
223  }
224 
225  } else {
226  // The BOM tree should be built from parsing a schedule (and O&D) file
227  if (vm.count ("schedule")) {
228  ioScheduleInputFilename = vm["schedule"].as< std::string >();
229  std::cout << "Input schedule filename is: " << ioScheduleInputFilename
230  << std::endl;
231 
232  } else {
233  // The built-in option is not selected. However, no schedule file
234  // is specified
235  std::cerr << "Either one among the -b/--builtin, -i/--inventory or "
236  << " -f/--for_schedule and -s/--schedule options "
237  << "must be specified" << std::endl;
238  }
239 
240  if (vm.count ("ond")) {
241  ioODInputFilename = vm["ond"].as< std::string >();
242  std::cout << "Input O&D filename is: " << ioODInputFilename << std::endl;
243  }
244 
245  if (vm.count ("yield")) {
246  ioYieldInputFilename = vm["yield"].as< std::string >();
247  std::cout << "Input yield filename is: " << ioYieldInputFilename << std::endl;
248  }
249  }
250  }
251 
252  if (vm.count ("log")) {
253  ioLogFilename = vm["log"].as< std::string >();
254  std::cout << "Log filename is: " << ioLogFilename << std::endl;
255  }
256 
257  return 0;
258 }
259 
260 // //////////////////////////////////////////////////////////////////
261 void initReadline (swift::SReadline& ioInputReader) {
262 
263  // Prepare the list of my own completers
264  std::vector<std::string> Completers;
265 
266  // The following is supported:
267  // - "identifiers"
268  // - special identifier %file - means to perform a file name completion
269  Completers.push_back ("help");
270  Completers.push_back ("list %airline_code %flight_number");
271  Completers.push_back ("select %airline_code %flight_number %flight_date");
272  Completers.push_back ("display");
273  Completers.push_back ("sell %booking_class %party_size %origin %destination");
274  Completers.push_back ("quit");
275 
276 
277  // Now register the completers.
278  // Actually it is possible to re-register another set at any time
279  ioInputReader.RegisterCompletions (Completers);
280 }
281 
282 // //////////////////////////////////////////////////////////////////
283 Command_T::Type_T extractCommand (TokenList_T& ioTokenList) {
284  Command_T::Type_T oCommandType = Command_T::LAST_VALUE;
285 
286  // Interpret the user input
287  if (ioTokenList.empty() == false) {
288  TokenList_T::iterator itTok = ioTokenList.begin();
289  std::string lCommand (*itTok);
290  boost::algorithm::to_lower (lCommand);
291 
292  if (lCommand == "help") {
293  oCommandType = Command_T::HELP;
294 
295  } else if (lCommand == "list") {
296  oCommandType = Command_T::LIST;
297 
298  } else if (lCommand == "display") {
299  oCommandType = Command_T::DISPLAY;
300 
301  } else if (lCommand == "select") {
302  oCommandType = Command_T::SELECT;
303 
304  } else if (lCommand == "sell") {
305  oCommandType = Command_T::SELL;
306 
307  } else if (lCommand == "quit") {
308  oCommandType = Command_T::QUIT;
309  }
310 
311  // Remove the first token (the command), as the corresponding information
312  // has been extracted in the form of the returned command type enumeration
313  ioTokenList.erase (itTok);
314 
315  } else {
316  oCommandType = Command_T::NOP;
317  }
318 
319  return oCommandType;
320 }
321 
322 // //////////////////////////////////////////////////////////////////
323 void parseFlightKey (const TokenList_T& iTokenList,
324  stdair::AirlineCode_T& ioAirlineCode,
325  stdair::FlightNumber_T& ioFlightNumber) {
326  // Interpret the user input
327  if (iTokenList.empty() == false) {
328 
329  // Read the airline code
330  TokenList_T::const_iterator itTok = iTokenList.begin();
331  if (itTok->empty() == false) {
332  ioAirlineCode = *itTok;
333  boost::algorithm::to_upper (ioAirlineCode);
334  }
335 
336  // Read the flight-number
337  ++itTok;
338  if (itTok != iTokenList.end()) {
339 
340  if (itTok->empty() == false) {
341  try {
342 
343  ioFlightNumber = boost::lexical_cast<stdair::FlightNumber_T> (*itTok);
344 
345  } catch (boost::bad_lexical_cast& eCast) {
346  std::cerr << "The flight number ('" << *itTok
347  << "') cannot be understood. "
348  << "The default value (all) is kept."
349  << std::endl;
350  return;
351  }
352  }
353 
354  } else {
355  return;
356  }
357  }
358 }
359 
360 // //////////////////////////////////////////////////////////////////
361 void parseFlightDateKey (const TokenList_T& iTokenList,
362  stdair::AirlineCode_T& ioAirlineCode,
363  stdair::FlightNumber_T& ioFlightNumber,
364  stdair::Date_T& ioDepartureDate) {
365  //
366  const std::string kMonthStr[12] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
367  "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
368  //
369  unsigned short ioDepartureDateYear = ioDepartureDate.year();
370  unsigned short ioDepartureDateMonth = ioDepartureDate.month();
371  std::string ioDepartureDateMonthStr = kMonthStr[ioDepartureDateMonth-1];
372  unsigned short ioDepartureDateDay = ioDepartureDate.day();
373 
374  // Interpret the user input
375  if (iTokenList.empty() == false) {
376 
377  // Read the airline code
378  TokenList_T::const_iterator itTok = iTokenList.begin();
379  if (itTok->empty() == false) {
380  ioAirlineCode = *itTok;
381  boost::algorithm::to_upper (ioAirlineCode);
382  }
383 
384  // Read the flight-number
385  ++itTok;
386  if (itTok != iTokenList.end()) {
387 
388  if (itTok->empty() == false) {
389  try {
390 
391  ioFlightNumber = boost::lexical_cast<stdair::FlightNumber_T> (*itTok);
392 
393  } catch (boost::bad_lexical_cast& eCast) {
394  std::cerr << "The flight number ('" << *itTok
395  << "') cannot be understood. "
396  << "The default value (all) is kept."
397  << std::endl;
398  return;
399  }
400  }
401 
402  } else {
403  return;
404  }
405 
406  // Read the year for the departure date
407  ++itTok;
408  if (itTok != iTokenList.end()) {
409 
410  if (itTok->empty() == false) {
411  try {
412 
413  ioDepartureDateYear = boost::lexical_cast<unsigned short> (*itTok);
414  if (ioDepartureDateYear < 100) {
415  ioDepartureDateYear += 2000;
416  }
417 
418  } catch (boost::bad_lexical_cast& eCast) {
419  std::cerr << "The year of the flight departure date ('" << *itTok
420  << "') cannot be understood. The default value ("
421  << ioDepartureDateYear << ") is kept. " << std::endl;
422  return;
423  }
424  }
425 
426  } else {
427  return;
428  }
429 
430  // Read the month for the departure date
431  ++itTok;
432  if (itTok != iTokenList.end()) {
433 
434  if (itTok->empty() == false) {
435  try {
436 
437  const boost::regex lMonthRegex ("^(\\d{1,2})$");
438  const bool isMonthANumber = regex_match (*itTok, lMonthRegex);
439 
440  if (isMonthANumber == true) {
441  const unsigned short lMonth =
442  boost::lexical_cast<unsigned short> (*itTok);
443  if (lMonth > 12) {
444  throw boost::bad_lexical_cast();
445  }
446  ioDepartureDateMonthStr = kMonthStr[lMonth-1];
447 
448  } else {
449  const std::string lMonthStr (*itTok);
450  if (lMonthStr.size() < 3) {
451  throw boost::bad_lexical_cast();
452  }
453  std::string lMonthStr1 (lMonthStr.substr (0, 1));
454  boost::algorithm::to_upper (lMonthStr1);
455  std::string lMonthStr23 (lMonthStr.substr (1, 2));
456  boost::algorithm::to_lower (lMonthStr23);
457  ioDepartureDateMonthStr = lMonthStr1 + lMonthStr23;
458  }
459 
460  } catch (boost::bad_lexical_cast& eCast) {
461  std::cerr << "The month of the flight departure date ('" << *itTok
462  << "') cannot be understood. The default value ("
463  << ioDepartureDateMonthStr << ") is kept. " << std::endl;
464  return;
465  }
466  }
467 
468  } else {
469  return;
470  }
471 
472  // Read the day for the departure date
473  ++itTok;
474  if (itTok != iTokenList.end()) {
475 
476  if (itTok->empty() == false) {
477  try {
478 
479  ioDepartureDateDay = boost::lexical_cast<unsigned short> (*itTok);
480 
481  } catch (boost::bad_lexical_cast& eCast) {
482  std::cerr << "The day of the flight departure date ('" << *itTok
483  << "') cannot be understood. The default value ("
484  << ioDepartureDateDay << ") is kept. " << std::endl;
485  return;
486  }
487  }
488 
489  } else {
490  return;
491  }
492 
493  // Re-compose the departure date
494  std::ostringstream lDepartureDateStr;
495  lDepartureDateStr << ioDepartureDateYear << "-" << ioDepartureDateMonthStr
496  << "-" << ioDepartureDateDay;
497 
498  try {
499 
500  ioDepartureDate =
501  boost::gregorian::from_simple_string (lDepartureDateStr.str());
502 
503  } catch (boost::gregorian::bad_month& eCast) {
504  std::cerr << "The flight departure date ('" << lDepartureDateStr.str()
505  << "') cannot be understood. The default value ("
506  << ioDepartureDate << ") is kept. " << std::endl;
507  return;
508  }
509 
510  }
511 }
512 
513 // //////////////////////////////////////////////////////////////////
514 void parseBookingClassKey (const TokenList_T& iTokenList,
515  stdair::ClassCode_T& ioBookingClass,
516  stdair::PartySize_T& ioPartySize,
517  stdair::AirportCode_T& ioOrigin,
518  stdair::AirportCode_T& ioDestination) {
519  // Interpret the user input
520  if (iTokenList.empty() == false) {
521 
522  // Read the booking class
523  TokenList_T::const_iterator itTok = iTokenList.begin();
524  if (itTok->empty() == false) {
525  ioBookingClass = *itTok;
526  boost::algorithm::to_upper (ioBookingClass);
527  }
528 
529  // Read the party size
530  ++itTok;
531  if (itTok != iTokenList.end()) {
532 
533  if (itTok->empty() == false) {
534  try {
535 
536  ioPartySize = boost::lexical_cast<stdair::PartySize_T> (*itTok);
537 
538  } catch (boost::bad_lexical_cast& eCast) {
539  std::cerr << "The party size ('" << *itTok
540  << "') cannot be understood. The default value ("
541  << ioPartySize << ") is kept." << std::endl;
542  return;
543  }
544  }
545 
546  } else {
547  return;
548  }
549 
550  // Read the origin
551  ++itTok;
552  if (itTok != iTokenList.end()) {
553 
554  if (itTok->empty() == false) {
555  ioOrigin = *itTok;
556  boost::algorithm::to_upper (ioOrigin);
557  }
558 
559  } else {
560  return;
561  }
562 
563  // Read the destination
564  ++itTok;
565  if (itTok != iTokenList.end()) {
566 
567  if (itTok->empty() == false) {
568  ioDestination = *itTok;
569  boost::algorithm::to_upper (ioDestination);
570  }
571 
572  } else {
573  return;
574  }
575  }
576 }
577 
578 // /////////////////////////////////////////////////////////
579 std::string toString (const TokenList_T& iTokenList) {
580  std::ostringstream oStr;
581 
582  // Re-create the string with all the tokens, trimmed by read-line
583  unsigned short idx = 0;
584  for (TokenList_T::const_iterator itTok = iTokenList.begin();
585  itTok != iTokenList.end(); ++itTok, ++idx) {
586  if (idx != 0) {
587  oStr << " ";
588  }
589  oStr << *itTok;
590  }
591 
592  return oStr.str();
593 }
594 
595 // /////////////////////////////////////////////////////////
596 TokenList_T extractTokenList (const TokenList_T& iTokenList,
597  const std::string& iRegularExpression) {
598  TokenList_T oTokenList;
599 
600  // Re-create the string with all the tokens (which had been trimmed
601  // by read-line)
602  const std::string lFullLine = toString (iTokenList);
603 
604  // See the caller for the regular expression
605  boost::regex expression (iRegularExpression);
606 
607  std::string::const_iterator start = lFullLine.begin();
608  std::string::const_iterator end = lFullLine.end();
609 
610  boost::match_results<std::string::const_iterator> what;
611  boost::match_flag_type flags = boost::match_default | boost::format_sed;
612  regex_search (start, end, what, expression, flags);
613 
614  // Put the matched strings in the list of tokens to be returned back
615  // to the caller
616  const unsigned short lMatchSetSize = what.size();
617  for (unsigned short matchIdx = 1; matchIdx != lMatchSetSize; ++matchIdx) {
618  const std::string lMatchedString (std::string (what[matchIdx].first,
619  what[matchIdx].second));
620  //if (lMatchedString.empty() == false) {
621  oTokenList.push_back (lMatchedString);
622  //}
623  }
624 
625  // DEBUG
626  // std::cout << "After (token list): " << oTokenList << std::endl;
627 
628  return oTokenList;
629 }
630 
631 // /////////////////////////////////////////////////////////
632 TokenList_T extractTokenListForFlight (const TokenList_T& iTokenList) {
639  const std::string lRegEx ("^([[:alpha:]]{2,3})?"
640  "[[:space:]]*([[:digit:]]{1,4})?$");
641 
642  //
643  const TokenList_T& oTokenList = extractTokenList (iTokenList, lRegEx);
644  return oTokenList;
645 }
646 
647 // /////////////////////////////////////////////////////////
648 TokenList_T extractTokenListForFlightDate (const TokenList_T& iTokenList) {
659  const std::string lRegEx("^([[:alpha:]]{2,3})?"
660  "[[:space:]]*([[:digit:]]{1,4})?"
661  "[/ ]*"
662  "([[:digit:]]{2,4})?[/-]?[[:space:]]*"
663  "([[:alpha:]]{3}|[[:digit:]]{1,2})?[/-]?[[:space:]]*"
664  "([[:digit:]]{1,2})?$");
665 
666  //
667  const TokenList_T& oTokenList = extractTokenList (iTokenList, lRegEx);
668  return oTokenList;
669 }
670 
671 // /////////////////////////////////////////////////////////
672 TokenList_T extractTokenListForClass (const TokenList_T& iTokenList) {
681  const std::string lRegEx ("^([[:alpha:]])?"
682  "[[:space:]]*([[:digit:]]{1,3})?"
683  "[[:space:]]*([[:alpha:]]{3})?"
684  "[[:space:]]*([[:alpha:]]{3})?$");
685 
686  //
687  const TokenList_T& oTokenList = extractTokenList (iTokenList, lRegEx);
688  return oTokenList;
689 }
690 
691 
692 // ///////// M A I N ////////////
693 int main (int argc, char* argv[]) {
694 
695  // State whether the BOM tree should be built-in or parsed from an
696  // input file
697  bool isBuiltin;
698  bool isForSchedule;
699 
700  // Input file names
701  stdair::Filename_T lInventoryFilename;
702  stdair::Filename_T lScheduleInputFilename;
703  stdair::Filename_T lODInputFilename;
704  stdair::Filename_T lYieldInputFilename;
705 
706  // Readline history
707  const unsigned int lHistorySize (100);
708  const std::string lHistoryFilename ("airinv.hist");
709  const std::string lHistoryBackupFilename ("airinv.hist.bak");
710 
711  // Default parameters for the interactive session
712  stdair::AirlineCode_T lLastInteractiveAirlineCode;
713  stdair::FlightNumber_T lLastInteractiveFlightNumber;
714  stdair::Date_T lLastInteractiveDate;
715  stdair::AirlineCode_T lInteractiveAirlineCode;
716  stdair::FlightNumber_T lInteractiveFlightNumber;
717  stdair::Date_T lInteractiveDate;
718  stdair::AirportCode_T lInteractiveOrigin;
719  stdair::AirportCode_T lInteractiveDestination;
720  stdair::ClassCode_T lInteractiveBookingClass;
721  stdair::PartySize_T lInteractivePartySize;
722 
723  // Parameters for the sale
724  std::string lSegmentDateKey;
725 
726  // Output log File
727  stdair::Filename_T lLogFilename;
728 
729  // Call the command-line option parser
730  const int lOptionParserStatus =
731  readConfiguration (argc, argv, isBuiltin, isForSchedule, lInventoryFilename,
732  lScheduleInputFilename, lODInputFilename,
733  lYieldInputFilename, lLogFilename);
734 
735  if (lOptionParserStatus == K_AIRINV_EARLY_RETURN_STATUS) {
736  return 0;
737  }
738 
739  // Set the log parameters
740  std::ofstream logOutputFile;
741  // Open and clean the log outputfile
742  logOutputFile.open (lLogFilename.c_str());
743  logOutputFile.clear();
744 
745  // Initialise the inventory service
746  const stdair::BasLogParams lLogParams (stdair::LOG::DEBUG, logOutputFile);
747  AIRINV::AIRINV_Master_Service airinvService (lLogParams);
748 
749  // DEBUG
750  STDAIR_LOG_DEBUG ("Welcome to AirInv");
751 
752  // Check wether or not a (CSV) input file should be read
753  if (isBuiltin == true) {
754 
755  // Build the sample BOM tree for RMOL
756  airinvService.buildSampleBom();
757 
758  // Update the default parameters for the following interactive session
759  lInteractiveAirlineCode = "BA";
760  lInteractiveFlightNumber = 9;
761  lInteractiveDate = stdair::Date_T (2011, 06, 10);
762  lInteractiveBookingClass = "Q";
763  lInteractivePartySize = 2;
764  lInteractiveOrigin = "LHR";
765  lInteractiveDestination = "SYD";
766 
767  } else {
768  if (isForSchedule == true) {
769  // Build the BOM tree from parsing a schedule file (and O&D list)
770  AIRRAC::YieldFilePath lYieldFilePath (lYieldInputFilename);
771  airinvService.parseAndLoad (lScheduleInputFilename, lODInputFilename,
772  lYieldFilePath);
773 
774  // Update the default parameters for the following interactive session
775  lInteractiveAirlineCode = "SQ";
776  lInteractiveFlightNumber = 11;
777  lInteractiveDate = stdair::Date_T (2010, 01, 15);
778  lInteractiveBookingClass = "Y";
779  lInteractivePartySize = 2;
780  lInteractiveOrigin = "SIN";
781  lInteractiveDestination = "BKK";
782 
783  } else {
784  // Build the BOM tree from parsing an inventory dump file
785  airinvService.parseAndLoad (lInventoryFilename);
786 
787  // Update the default parameters for the following interactive session
788  lInteractiveAirlineCode = "SV";
789  lInteractiveFlightNumber = 5;
790  lInteractiveDate = stdair::Date_T (2010, 03, 11);
791  lInteractiveBookingClass = "Y";
792  lInteractivePartySize = 2;
793  lInteractiveOrigin = "KBP";
794  lInteractiveDestination = "JFK";
795  }
796  }
797 
798  // Save the last state
799  lLastInteractiveAirlineCode = lInteractiveAirlineCode;
800  lLastInteractiveFlightNumber = lInteractiveFlightNumber;
801  lLastInteractiveDate = lInteractiveDate;
802 
803  // DEBUG
804  STDAIR_LOG_DEBUG ("====================================================");
805  STDAIR_LOG_DEBUG ("= Beginning of the interactive session =");
806  STDAIR_LOG_DEBUG ("====================================================");
807 
808  // Initialise the GNU readline wrapper
809  swift::SReadline lReader (lHistoryFilename, lHistorySize);
810  initReadline (lReader);
811 
812  // Now we can ask user for a line
813  std::string lUserInput;
814  bool EndOfInput (false);
815  Command_T::Type_T lCommandType (Command_T::NOP);
816 
817  while (lCommandType != Command_T::QUIT && EndOfInput == false) {
818  // Prompt
819  std::ostringstream oPromptStr;
820  oPromptStr << "airinv "
821  << lInteractiveAirlineCode << lInteractiveFlightNumber
822  << " / " << lInteractiveDate
823  << "> ";
824  // Call read-line, which will fill the list of tokens
825  TokenList_T lTokenListByReadline;
826  lUserInput = lReader.GetLine (oPromptStr.str(), lTokenListByReadline,
827  EndOfInput);
828 
829  // The history can be saved to an arbitrary file at any time
830  lReader.SaveHistory (lHistoryBackupFilename);
831 
832  // The end-of-input typically corresponds to a CTRL-D typed by the user
833  if (EndOfInput) {
834  std::cout << std::endl;
835  break;
836  }
837 
838  // Interpret the user input
839  lCommandType = extractCommand (lTokenListByReadline);
840 
841  switch (lCommandType) {
842 
843  // ////////////////////////////// Help ////////////////////////
844  case Command_T::HELP: {
845  std::cout << std::endl;
846  std::cout << "Commands: " << std::endl;
847  std::cout << " help" << "\t\t" << "Display this help" << std::endl;
848  std::cout << " quit" << "\t\t" << "Quit the application" << std::endl;
849  std::cout << " list" << "\t\t"
850  << "List airlines, flights and departure dates" << std::endl;
851  std::cout << " select" << "\t\t"
852  << "Select a flight-date to become the current one"
853  << std::endl;
854  std::cout << " display" << "\t"
855  << "Display the current flight-date" << std::endl;
856  std::cout << " sell" << "\t\t"
857  << "Make a booking on the current flight-date" << std::endl;
858  std::cout << std::endl;
859  break;
860  }
861 
862  // ////////////////////////////// Quit ////////////////////////
863  case Command_T::QUIT: {
864  break;
865  }
866 
867  // ////////////////////////////// List /////////////////////////
868  case Command_T::LIST: {
869  //
870  TokenList_T lTokenList = extractTokenListForFlight (lTokenListByReadline);
871 
872  stdair::AirlineCode_T lAirlineCode ("all");
873  stdair::FlightNumber_T lFlightNumber (0);
874  // Parse the parameters given by the user, giving default values
875  // in case the user does not specify some (or all) of them
876  parseFlightKey (lTokenList, lAirlineCode, lFlightNumber);
877 
878  //
879  const std::string lFlightNumberStr = (lFlightNumber ==0)?" (all)":"";
880  std::cout << "List of flights for "
881  << lAirlineCode << " " << lFlightNumber << lFlightNumberStr
882  << std::endl;
883 
884  // DEBUG: Display the flight-date
885  const std::string& lFlightDateListStr =
886  airinvService.list (lAirlineCode, lFlightNumber);
887 
888  if (lFlightDateListStr.empty() == false) {
889  std::cout << lFlightDateListStr << std::endl;
890  STDAIR_LOG_DEBUG (lFlightDateListStr);
891 
892  } else {
893  std::cerr << "There is no result for "
894  << lAirlineCode << " " << lFlightNumber << lFlightNumberStr
895  << ". Just type the list command without any parameter "
896  << "to see the flight-dates for all the airlines and for all "
897  << "the flight numbers."
898  << std::endl;
899  }
900 
901  break;
902  }
903 
904  // ////////////////////////////// Select ////////////////////////
905  case Command_T::SELECT: {
906  //
907  TokenList_T lTokenList =
908  extractTokenListForFlightDate (lTokenListByReadline);
909 
910  // Check whether the user wants to select the last saved flight-date
911  if (lTokenList.empty() == false) {
912  // Read the booking class
913  TokenList_T::const_iterator itTok = lTokenList.begin();
914 
915  if (*itTok == "-") {
916 
917  // Swap the current state with the last state
918  boost::swap (lInteractiveAirlineCode, lLastInteractiveAirlineCode);
919  boost::swap (lInteractiveFlightNumber, lLastInteractiveFlightNumber);
920  boost::swap (lInteractiveDate, lLastInteractiveDate);
921 
922  break;
923  }
924  }
925 
926  // Parse the parameters given by the user, giving default values
927  // in case the user does not specify some (or all) of them
928  parseFlightDateKey (lTokenList, lInteractiveAirlineCode,
929  lInteractiveFlightNumber, lInteractiveDate);
930 
931  // Check whether the selected flight-date is valid
932  const bool isFlightDateValid =
933  airinvService.check (lInteractiveAirlineCode, lInteractiveFlightNumber,
934  lInteractiveDate);
935  if (isFlightDateValid == false) {
936  std::ostringstream oFDKStr;
937  oFDKStr << "The " << lInteractiveAirlineCode
938  << lInteractiveFlightNumber << " / " << lInteractiveDate
939  << " flight-date is not valid. Make sure it exists (e.g.,"
940  << " with the list command). The current flight-date is kept"
941  << " selected.";
942  std::cout << oFDKStr.str() << std::endl;
943  STDAIR_LOG_ERROR (oFDKStr.str());
944 
945  // Restore the last state
946  lInteractiveAirlineCode = lLastInteractiveAirlineCode;
947  lInteractiveFlightNumber = lLastInteractiveFlightNumber;
948  lInteractiveDate = lLastInteractiveDate;
949 
950  break;
951  }
952 
953  // DEBUG: Display the flight-date selection
954  std::ostringstream oFDKStr;
955  oFDKStr << "Selected the " << lInteractiveAirlineCode
956  << lInteractiveFlightNumber << " / " << lInteractiveDate
957  << " flight-date";
958  std::cout << oFDKStr.str() << std::endl;
959  STDAIR_LOG_DEBUG (oFDKStr.str());
960 
961  // Save the last state
962  lLastInteractiveAirlineCode = lInteractiveAirlineCode;
963  lLastInteractiveFlightNumber = lInteractiveFlightNumber;
964  lLastInteractiveDate = lInteractiveDate;
965 
966  break;
967  }
968 
969  // ////////////////////////////// Display ////////////////////////
970  case Command_T::DISPLAY: {
971  // DEBUG: Display the flight-date
972  const std::string& lCSVFlightDateDump =
973  airinvService.csvDisplay (lInteractiveAirlineCode,
974  lInteractiveFlightNumber, lInteractiveDate);
975  std::cout << lCSVFlightDateDump << std::endl;
976  STDAIR_LOG_DEBUG (lCSVFlightDateDump);
977 
978  break;
979  }
980 
981  // ////////////////////////////// Sell ////////////////////////
982  case Command_T::SELL: {
983  //
984  TokenList_T lTokenList = extractTokenListForClass (lTokenListByReadline);
985 
986  // Parse the parameters given by the user, giving default values
987  // in case the user does not specify some (or all) of them
988  parseBookingClassKey (lTokenList, lInteractiveBookingClass,
989  lInteractivePartySize,
990  lInteractiveOrigin, lInteractiveDestination);
991 
992  // DEBUG: Display the flight-date before the sell
993  const std::string& lCSVFlightDateDumpBefore =
994  airinvService.csvDisplay (lInteractiveAirlineCode,
995  lInteractiveFlightNumber, lInteractiveDate);
996  //std::cout << lCSVFlightDateDumpBefore << std::endl;
997  STDAIR_LOG_DEBUG (lCSVFlightDateDumpBefore);
998 
999  // Make a booking
1000  std::ostringstream oSDKStr;
1001  oSDKStr << lInteractiveAirlineCode << ","
1002  << lInteractiveFlightNumber << ","
1003  << lInteractiveDate << ","
1004  << lInteractiveOrigin << "," << lInteractiveDestination;
1005  const std::string lSegmentDateKey (oSDKStr.str());
1006 
1007  // Perform the sell
1008  const bool isSellSuccessful =
1009  airinvService.sell (lSegmentDateKey,
1010  lInteractiveBookingClass, lInteractivePartySize);
1011 
1012  // DEBUG
1013  const std::string isSellSuccessfulStr =
1014  (isSellSuccessful == true)?"Yes":"No";
1015  std::ostringstream oSaleStr;
1016  oSaleStr << "Sale ('" << lSegmentDateKey << "', "
1017  << lInteractiveBookingClass << ": " << lInteractivePartySize
1018  << ") successful? " << isSellSuccessfulStr;
1019  std::cout << oSaleStr.str() << std::endl;
1020 
1021  // DEBUG
1022  STDAIR_LOG_DEBUG (oSaleStr.str());
1023 
1024  // DEBUG: Display the flight-date after the sell
1025  const std::string& lCSVFlightDateDumpAfter =
1026  airinvService.csvDisplay (lInteractiveAirlineCode,
1027  lInteractiveFlightNumber, lInteractiveDate);
1028  //std::cout << lCSVFlightDateDumpAfter << std::endl;
1029  STDAIR_LOG_DEBUG (lCSVFlightDateDumpAfter);
1030 
1031  break;
1032  }
1033 
1034  // /////////////////////////// Default / No value ///////////////////////
1035  case Command_T::NOP: {
1036  break;
1037  }
1038 
1039  case Command_T::LAST_VALUE:
1040  default: {
1041  // DEBUG
1042  std::ostringstream oStr;
1043  oStr << "That command is not yet understood: '" << lUserInput
1044  << "' => " << lTokenListByReadline;
1045  STDAIR_LOG_DEBUG (oStr.str());
1046  std::cout << oStr.str() << std::endl;
1047  }
1048  }
1049  }
1050 
1051  // DEBUG
1052  STDAIR_LOG_DEBUG ("End of the session. Exiting.");
1053  std::cout << "End of the session. Exiting." << std::endl;
1054 
1055  // Close the Log outputFile
1056  logOutputFile.close();
1057 
1058  /*
1059  Note: as that program is not intended to be run on a server in
1060  production, it is better not to catch the exceptions. When it
1061  happens (that an exception is throwned), that way we get the
1062  call stack.
1063  */
1064 
1065  return 0;
1066 }