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