SimFQT Logo  1.00.0
C++ Simulated Fare Quote System Library
 All Classes Namespaces Files Functions Variables Typedefs Friends Pages
simfqt.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 // StdAir
16 #include <stdair/basic/BasLogParams.hpp>
17 #include <stdair/basic/BasConst_BomDisplay.hpp>
18 #include <stdair/basic/BasDBParams.hpp>
19 #include <stdair/basic/BasConst_DefaultObject.hpp>
20 #include <stdair/basic/BasConst_Inventory.hpp>
21 #include <stdair/basic/BasConst_Request.hpp>
22 #include <stdair/service/Logger.hpp>
23 #include <stdair/stdair_exceptions.hpp>
24 #include <stdair/stdair_basic_types.hpp>
25 #include <stdair/stdair_date_time_types.hpp>
26 #include <stdair/bom/TravelSolutionStruct.hpp>
27 #include <stdair/bom/BookingRequestStruct.hpp>
28 #include <stdair/bom/ParsedKey.hpp>
29 #include <stdair/bom/BomKeyManager.hpp>
30 #include <stdair/command/CmdBomManager.hpp>
31 // Stdair GNU Readline Wrapper
32 #include <stdair/ui/cmdline/SReadline.hpp>
33 // Simfqt
35 #include <simfqt/config/simfqt-paths.hpp>
36 
37 
38 // //////// Constants //////
42 const std::string K_SIMFQT_DEFAULT_LOG_FILENAME ("simfqt.log");
43 
47 const std::string K_SIMFQT_DEFAULT_FARE_INPUT_FILENAME (STDAIR_SAMPLE_DIR
48  "/fare01.csv");
49 
54 const bool K_SIMFQT_DEFAULT_BUILT_IN_INPUT = false;
55 
59 const int K_SIMFQT_EARLY_RETURN_STATUS = 99;
60 
65 typedef std::vector<std::string> TokenList_T;
66 
70 struct Command_T {
71  typedef enum {
72  NOP = 0,
73  QUIT,
74  HELP,
75  LIST,
76  DISPLAY,
77  PRICE,
78  LAST_VALUE
79  } Type_T;
80 };
81 
82 // ///////// Parsing of Options & Configuration /////////
83 // A helper function to simplify the main part.
84 template<class T> std::ostream& operator<< (std::ostream& os,
85  const std::vector<T>& v) {
86  std::copy (v.begin(), v.end(), std::ostream_iterator<T> (std::cout, " "));
87  return os;
88 }
89 
93 int readConfiguration (int argc, char* argv[], bool& ioIsBuiltin,
94  stdair::Filename_T& ioFareInputFilename,
95  std::string& ioLogFilename) {
96 
97  // Default for the built-in input
98  ioIsBuiltin = K_SIMFQT_DEFAULT_BUILT_IN_INPUT;
99 
100  // Declare a group of options that will be allowed only on command line
101  boost::program_options::options_description generic ("Generic options");
102  generic.add_options()
103  ("prefix", "print installation prefix")
104  ("version,v", "print version string")
105  ("help,h", "produce help message");
106 
107  // Declare a group of options that will be allowed both on command
108  // line and in config file
109  boost::program_options::options_description config ("Configuration");
110  config.add_options()
111  ("builtin,b",
112  "The sample BOM tree can be either built-in or parsed from an input file. That latter must then be given with the -f/--fare option")
113  ("fare,f",
114  boost::program_options::value< std::string >(&ioFareInputFilename)->default_value(K_SIMFQT_DEFAULT_FARE_INPUT_FILENAME),
115  "(CSV) input file for the fare rules")
116  ("log,l",
117  boost::program_options::value< std::string >(&ioLogFilename)->default_value(K_SIMFQT_DEFAULT_LOG_FILENAME),
118  "Filename for the logs")
119  ;
120 
121  // Hidden options, will be allowed both on command line and
122  // in config file, but will not be shown to the user.
123  boost::program_options::options_description hidden ("Hidden options");
124  hidden.add_options()
125  ("copyright",
126  boost::program_options::value< std::vector<std::string> >(),
127  "Show the copyright (license)");
128 
129  boost::program_options::options_description cmdline_options;
130  cmdline_options.add(generic).add(config).add(hidden);
131 
132  boost::program_options::options_description config_file_options;
133  config_file_options.add(config).add(hidden);
134 
135  boost::program_options::options_description visible ("Allowed options");
136  visible.add(generic).add(config);
137 
138  boost::program_options::positional_options_description p;
139  p.add ("copyright", -1);
140 
141  boost::program_options::variables_map vm;
142  boost::program_options::
143  store (boost::program_options::command_line_parser (argc, argv).
144  options (cmdline_options).positional(p).run(), vm);
145 
146  std::ifstream ifs ("simfqt.cfg");
147  boost::program_options::store (parse_config_file (ifs, config_file_options),
148  vm);
149  boost::program_options::notify (vm); if (vm.count ("help")) {
150  std::cout << visible << std::endl;
152  }
153 
154  if (vm.count ("version")) {
155  std::cout << PACKAGE_NAME << ", version " << PACKAGE_VERSION << std::endl;
157  }
158 
159  if (vm.count ("prefix")) {
160  std::cout << "Installation prefix: " << PREFIXDIR << std::endl;
162  }
163 
164  if (vm.count ("builtin")) {
165  ioIsBuiltin = true;
166  }
167  const std::string isBuiltinStr = (ioIsBuiltin == true)?"yes":"no";
168  std::cout << "The BOM should be built-in? " << isBuiltinStr << std::endl;
169 
170  if (ioIsBuiltin == false) {
171 
172  // The BOM tree should be built from parsing a fare (and O&D) file
173  if (vm.count ("fare")) {
174  ioFareInputFilename = vm["fare"].as< std::string >();
175  std::cout << "Input fare filename is: " << ioFareInputFilename
176  << std::endl;
177 
178  } else {
179  // The built-in option is not selected. However, no fare file
180  // is specified
181  std::cerr << "Either one among the -b/--builtin and -f/--fare "
182  << "options must be specified" << std::endl;
183  }
184  }
185 
186  if (vm.count ("log")) {
187  ioLogFilename = vm["log"].as< std::string >();
188  std::cout << "Log filename is: " << ioLogFilename << std::endl;
189  }
190 
191  return 0;
192 
193 }
194 
195 // //////////////////////////////////////////////////////////////////
196 void initReadline (swift::SReadline& ioInputReader) {
197 
198  // Prepare the list of my own completers
199  std::vector<std::string> Completers;
200 
201  // The following is supported:
202  // - "identifiers"
203  // - special identifier %file - means to perform a file name completion
204  Completers.push_back ("help");
205  Completers.push_back ("list");
206  Completers.push_back ("display %airport_code %airport_code %departure_date");
207  Completers.push_back ("price %airline_code %flight_number %departure_date %airport_code %airport_code %departure_time %booking_date %booking_time %POS %channel% %trip_type %stay_duration");
208  Completers.push_back ("quit");
209 
210  // Now register the completers.
211  // Actually it is possible to re-register another set at any time
212  ioInputReader.RegisterCompletions (Completers);
213 }
214 
215 // //////////////////////////////////////////////////////////////////
216 Command_T::Type_T extractCommand (TokenList_T& ioTokenList) {
217  Command_T::Type_T oCommandType = Command_T::LAST_VALUE;
218 
219  // Interpret the user input
220  if (ioTokenList.empty() == false) {
221  TokenList_T::iterator itTok = ioTokenList.begin();
222  std::string& lCommand (*itTok);
223  boost::algorithm::to_lower (lCommand);
224 
225  if (lCommand == "help") {
226  oCommandType = Command_T::HELP;
227 
228  } else if (lCommand == "list") {
229  oCommandType = Command_T::LIST;
230 
231  } else if (lCommand == "display") {
232  oCommandType = Command_T::DISPLAY;
233 
234  } else if (lCommand == "price") {
235  oCommandType = Command_T::PRICE;
236 
237  } else if (lCommand == "quit") {
238  oCommandType = Command_T::QUIT;
239 
240  }
241 
242  // Remove the first token (the command), as the corresponding information
243  // has been extracted in the form of the returned command type enumeration
244  ioTokenList.erase (itTok);
245 
246  } else {
247  oCommandType = Command_T::NOP;
248  }
249 
250  return oCommandType;
251 }
252 
253 // //////////////////////////////////////////////////////////////////
254 // Re-compose a date using three strings: the year, the month and the
255 // day. Return true if a correct date has been computed, false if not.
256 bool retrieveDate (std::string iYearString,
257  std::string iMonthString,
258  std::string iDayString,
259  stdair::Date_T& ioDate) {
260 
261  const std::string kMonthStr[12] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
262  "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
263 
264  // Check the year.
265  unsigned short lDateYear;
266  try {
267 
268  lDateYear = boost::lexical_cast<unsigned short> (iYearString);
269  if (lDateYear < 100) {
270  lDateYear += 2000;
271  }
272 
273  } catch (boost::bad_lexical_cast& eCast) {
274  std::cerr << "The year ('" << iYearString
275  << "') cannot be understood." << std::endl;
276  return false;
277  }
278 
279  // Check the month.
280  std::string lDateMonthStr;
281  try {
282 
283  const boost::regex lMonthRegex ("^(\\d{1,2})$");
284  const bool isMonthANumber = regex_match (iMonthString, lMonthRegex);
285 
286  if (isMonthANumber == true) {
287  const unsigned short lMonth =
288  boost::lexical_cast<unsigned short> (iMonthString);
289  if (lMonth > 12) {
290  throw boost::bad_lexical_cast();
291  }
292  if (lMonth != 0) {
293  lDateMonthStr = kMonthStr[lMonth-1];
294  } else {
295  std::cerr << "The month ('" << iMonthString
296  << "') cannot be understood." << std::endl;
297  return false;
298  }
299 
300  } else {
301  if (iMonthString.size() < 3) {
302  throw boost::bad_lexical_cast();
303  }
304  std::string lMonthStr1 (iMonthString.substr (0, 1));
305  boost::algorithm::to_upper (lMonthStr1);
306  std::string lMonthStr23 (iMonthString.substr (1, 2));
307  boost::algorithm::to_lower (lMonthStr23);
308  lDateMonthStr = lMonthStr1 + lMonthStr23;
309  }
310 
311  } catch (boost::bad_lexical_cast& eCast) {
312  std::cerr << "The month ('" << iMonthString
313  << "') cannot be understood." << std::endl;
314  return false;
315  }
316 
317  // Check the day.
318  unsigned short lDateDay;
319  try {
320 
321  lDateDay = boost::lexical_cast<unsigned short> (iDayString);
322 
323  } catch (boost::bad_lexical_cast& eCast) {
324  std::cerr << "The day ('" << iDayString
325  << "') cannot be understood." << std::endl;
326  return false;
327  }
328 
329  // Re-compose the date.
330  std::ostringstream lDateStr;
331  lDateStr << lDateYear << "-" << lDateMonthStr
332  << "-" << lDateDay;
333  try {
334 
335  ioDate =
336  boost::gregorian::from_simple_string (lDateStr.str());
337 
338  } catch (boost::gregorian::bad_month& eCast) {
339  std::cerr << "The month of the date ('" << lDateStr.str()
340  << "') cannot be understood." << std::endl;
341  return false;
342  } catch (boost::gregorian::bad_day_of_month& eCast) {
343  std::cerr << "The date ('" << lDateStr.str()
344  << "') is not correct: the day of month does not exist."
345  << std::endl;
346  return false;
347  } catch (boost::gregorian::bad_year& eCast) {
348  std::cerr << "The year ('" << lDateStr.str()
349  << "') is not correct."
350  << std::endl;
351  return false;
352  }
353 
354  return true;
355 }
356 
357 // //////////////////////////////////////////////////////////////////
358 // Re-compose a time using two strings: the hour and the minute.
359 // Return true if a correct time has been computed, false if not.
360 bool retrieveTime (std::string iHourString,
361  std::string iMinuteString,
362  stdair::Duration_T& oTime) {
363 
364  // Check the hour
365  unsigned short lTimeHour;
366  try {
367 
368  lTimeHour = boost::lexical_cast<unsigned short> (iHourString);
369 
370  } catch (boost::bad_lexical_cast& eCast) {
371  std::cerr << "The hour of the time ('" << iHourString
372  << "') cannot be understood." << std::endl;
373  return false;
374  }
375 
376  // Check the minutes
377  unsigned short lTimeMinute;
378  try {
379 
380  lTimeMinute = boost::lexical_cast<unsigned short> (iMinuteString);
381 
382  } catch (boost::bad_lexical_cast& eCast) {
383  std::cerr << "The minute of the time ('" << iMinuteString
384  << "') cannot be understood." << std::endl;
385  return false;
386  }
387 
388 
389  // Re-compose the time
390  std::ostringstream lTimeStr;
391  lTimeStr << lTimeHour << ":" << lTimeMinute;
392  oTime =
393  boost::posix_time::duration_from_string (lTimeStr.str());
394 
395  return true;
396 }
397 
398 // //////////////////////////////////////////////////////////////////
399 // Analyze the tokens of the 'price' command in order to construct
400 // a travel solution list and a booking request.
401 const stdair::BookingRequestStruct parseTravelSolutionAndBookingRequestKey
402 (const TokenList_T& iTokenList,
403  stdair::TravelSolutionList_T& ioInteractiveTravelSolutionList,
404  const stdair::BookingRequestStruct& ioBookingRequestStruct) {
405 
406  TokenList_T::const_iterator itTok = iTokenList.begin();
407 
408  if (itTok->empty() == true) {
409 
410  std::cerr << "Wrong list of parameters. "
411  << "The default booking request and travel solution list are kept."
412  << std::endl;
413  return ioBookingRequestStruct;
414 
415 
416  } else {
417  // Parameters corresponding to the tokens.
418  // Each parameter correponds to one token except the dates
419  // (three tokens) and the times (two tokens).
420  stdair::AirlineCode_T lAirlineCode;
421  stdair::FlightNumber_T lflightNumber;
422  stdair::Date_T lDepartureDate;
423  stdair::Duration_T lDepartureTime;
424  stdair::AirportCode_T lOriginAirport;
425  stdair::AirportCode_T lDestinationAirport;
426  stdair::Date_T lRequestDate;
427  stdair::Duration_T lRequestTime;
428  stdair::CityCode_T lPOS;
429  stdair::ChannelLabel_T lChannel;
430  stdair::TripType_T lTripType;
431  unsigned short lStayDuration;
432 
433  // Read the airline code.
434  lAirlineCode = *itTok;
435  boost::algorithm::to_upper (lAirlineCode);
436 
437  // Read the flight-number .
438  ++itTok;
439  if (itTok->empty() == false) {
440  try {
441 
442  lflightNumber = boost::lexical_cast<stdair::FlightNumber_T> (*itTok);
443 
444  } catch (boost::bad_lexical_cast& eCast) {
445  std::cerr << "The flight number ('" << *itTok
446  << "') cannot be understood."
447  << std::endl;
448  return ioBookingRequestStruct;
449  }
450  }
451 
452  // Read the departure date.
453  ++itTok;
454  if (itTok->empty() == true) {
455  return ioBookingRequestStruct;
456  }
457  const std::string lDepartureYearString = *itTok;
458  ++itTok;
459  if (itTok->empty() == true) {
460  return ioBookingRequestStruct;
461  }
462  const std::string lDepartureMonthString = *itTok;
463  ++itTok;
464  if (itTok->empty() == true) {
465  return ioBookingRequestStruct;
466  }
467  const std::string lDepartureDayString = *itTok;
468  const bool IsDepartureDateReadable =
469  retrieveDate (lDepartureYearString, lDepartureMonthString,
470  lDepartureDayString, lDepartureDate);
471 
472  if (IsDepartureDateReadable == false) {
473  std::cerr << "The default booking request and travel solution list are kept."
474  << std::endl;
475  return ioBookingRequestStruct;
476  }
477 
478  // Read the origin.
479  ++itTok;
480  if (itTok->empty() == false) {
481  lOriginAirport = *itTok;
482  boost::algorithm::to_upper (lOriginAirport);
483  }
484 
485  // Read the destination.
486  ++itTok;
487  if (itTok->empty() == false) {
488  lDestinationAirport = *itTok;
489  boost::algorithm::to_upper (lDestinationAirport);
490  }
491 
492  // Read the departure time.
493  ++itTok;
494  if (itTok->empty() == true) {
495  return ioBookingRequestStruct;
496  }
497  const std::string lDepartureHourString = *itTok;
498  ++itTok;
499  if (itTok->empty() == true) {
500  return ioBookingRequestStruct;
501  }
502  const std::string lDepartureMinuteString = *itTok;
503  const bool IsDepartureTimeReadable =
504  retrieveTime (lDepartureHourString, lDepartureMinuteString,
505  lDepartureTime);
506 
507  if (IsDepartureTimeReadable == false) {
508  std::cerr << "The default booking request and travel solution list are kept."
509  << std::endl;
510  return ioBookingRequestStruct;
511  }
512 
513  // Read the request date.
514  ++itTok;
515  if (itTok->empty() == true) {
516  return ioBookingRequestStruct;
517  }
518  const std::string lRequestYearString = *itTok;
519  ++itTok;
520  if (itTok->empty() == true) {
521  return ioBookingRequestStruct;
522  }
523  const std::string lRequestMonthString = *itTok;
524  ++itTok;
525  if (itTok->empty() == true) {
526  return ioBookingRequestStruct;
527  }
528  const std::string lRequestDayString = *itTok;
529  const bool IsRequestDateReadable =
530  retrieveDate (lRequestYearString, lRequestMonthString,
531  lRequestDayString, lRequestDate);
532 
533  if (IsRequestDateReadable == false) {
534  std::cerr << "The default booking request and travel solution list are kept."
535  << std::endl;
536  return ioBookingRequestStruct;
537  }
538 
539  // Read the request time.
540  ++itTok;
541  if (itTok->empty() == true) {
542  return ioBookingRequestStruct;
543  }
544  const std::string lRequestHourString = *itTok;
545  ++itTok;
546  if (itTok->empty() == true) {
547  return ioBookingRequestStruct;
548  }
549  const std::string lRequestMinuteString = *itTok;
550  const bool IsRequestTimeReadable =
551  retrieveTime (lRequestHourString, lRequestMinuteString,
552  lRequestTime);
553 
554  if (IsRequestTimeReadable == false) {
555  std::cerr << "The default booking request and travel solution list are kept."
556  << std::endl;
557  return ioBookingRequestStruct;
558  }
559 
560  // Read the POS.
561  ++itTok;
562  if (itTok->empty() == false) {
563  lPOS = *itTok;
564  boost::algorithm::to_upper (lPOS);
565  }
566 
567  // Read the channel.
568  ++itTok;
569  if (itTok->empty() == false) {
570  lChannel = *itTok;
571  boost::algorithm::to_upper (lChannel);
572  }
573 
574  // Read the trip type.
575  ++itTok;
576  if (itTok->empty() == false) {
577  lTripType = *itTok;
578  boost::algorithm::to_upper (lTripType);
579  }
580 
581  // Read the stay duration.
582  ++itTok;
583  if (itTok->empty() == false) {
584  try {
585 
586  lStayDuration = boost::lexical_cast<unsigned short> (*itTok);
587 
588  } catch (boost::bad_lexical_cast& eCast) {
589  std::cerr << "The stay duration ('" << *itTok
590  << "') cannot be understood." << std::endl;
591  return ioBookingRequestStruct;
592  }
593  }
594 
595  // At this step we know that all the parameters designed to construct
596  // the travel solution and the booking request are correct.
597 
598  // Empty the travel solution list to store a new travel solution.
599  ioInteractiveTravelSolutionList.pop_front();
600  // Construct the new travel solution.
601  stdair::TravelSolutionStruct lTravelSolution;
602  std::ostringstream oStr;
603  oStr << lAirlineCode
604  << stdair::DEFAULT_KEY_FLD_DELIMITER
605  << lflightNumber
606  << stdair::DEFAULT_KEY_SUB_FLD_DELIMITER
607  << lDepartureDate
608  << stdair::DEFAULT_KEY_FLD_DELIMITER
609  << lOriginAirport
610  << stdair::DEFAULT_KEY_SUB_FLD_DELIMITER
611  << lDestinationAirport
612  << stdair::DEFAULT_KEY_FLD_DELIMITER
613  << lDepartureTime;
614  lTravelSolution.addSegment (oStr.str());
615  ioInteractiveTravelSolutionList.push_front(lTravelSolution);
616 
617  // Construct the new booking request.
618  stdair::DateTime_T lRequestDateTime (lRequestDate, lRequestTime);
619  const stdair::BookingRequestStruct &lBookingRequestStruct =
620  stdair::BookingRequestStruct(lOriginAirport,
621  lDestinationAirport,
622  lPOS,
623  lDepartureDate,
624  lRequestDateTime,
625  stdair::CABIN_ECO,
626  stdair::DEFAULT_PARTY_SIZE,
627  lChannel,
628  lTripType,
629  lStayDuration,
630  stdair::FREQUENT_FLYER_MEMBER,
631  lDepartureTime,
632  stdair::DEFAULT_WTP,
633  stdair::DEFAULT_VALUE_OF_TIME,
634  true, 50, true, 50);
635 
636  return lBookingRequestStruct;
637  }
638 }
639 
640 // //////////////////////////////////////////////////////////////////
641 // Analyze the tokens of the 'display' command in order to retrieve
642 // an airport pair and a departure date.
643 void parseFlightDateKey (const TokenList_T& iTokenList,
644  stdair::AirportCode_T& ioOrigin,
645  stdair::AirportCode_T& ioDestination,
646  stdair::Date_T& ioDepartureDate) {
647 
648  TokenList_T::const_iterator itTok = iTokenList.begin();
649 
650  // Interpret the user input.
651  if (itTok->empty() == true) {
652 
653  std::cerr << "Wrong parameters specified. Default paramaters '"
654  << ioOrigin << "-" << ioDestination
655  << "/" << ioDepartureDate
656  << "' are kept."
657  << std::endl;
658 
659  } else {
660 
661  // Read the origin.
662  ioOrigin = *itTok;
663  boost::algorithm::to_upper (ioOrigin);
664 
665  // Read the destination.
666  ++itTok;
667  if (itTok->empty() == false) {
668  ioDestination = *itTok;
669  boost::algorithm::to_upper (ioDestination);
670  }
671 
672  // Read the departure date.
673  ++itTok;
674  if (itTok->empty() == true) {
675  return;
676  }
677  std::string lYearString = *itTok;
678  ++itTok;
679  if (itTok->empty() == true) {
680  return;
681  }
682  std::string lMonthString = *itTok;
683  ++itTok;
684  if (itTok->empty() == true) {
685  return;
686  }
687  std::string lDayString = *itTok;
688  const bool IsDepartureDateReadable =
689  retrieveDate (lYearString, lMonthString, lDayString,
690  ioDepartureDate);
691  if (IsDepartureDateReadable == false) {
692  std::cerr << "Default paramaters '"
693  << ioOrigin << "-" << ioDestination
694  << "/" << ioDepartureDate
695  << "' are kept."
696  << std::endl;
697  return;
698  }
699  }
700 }
701 
702 // /////////////////////////////////////////////////////////
703 std::string toString (const TokenList_T& iTokenList) {
704  std::ostringstream oStr;
705 
706  // Re-create the string with all the tokens, trimmed by read-line
707  unsigned short idx = 0;
708  for (TokenList_T::const_iterator itTok = iTokenList.begin();
709  itTok != iTokenList.end(); ++itTok, ++idx) {
710  if (idx != 0) {
711  oStr << " ";
712  }
713  oStr << *itTok;
714  }
715 
716  return oStr.str();
717 }
718 
719 // /////////////////////////////////////////////////////////
720 TokenList_T extractTokenList (const TokenList_T& iTokenList,
721  const std::string& iRegularExpression) {
722  TokenList_T oTokenList;
723 
724  // Re-create the string with all the tokens (which had been trimmed
725  // by read-line)
726  const std::string lFullLine = toString (iTokenList);
727 
728  // See the caller for the regular expression
729  boost::regex expression (iRegularExpression);
730 
731  std::string::const_iterator start = lFullLine.begin();
732  std::string::const_iterator end = lFullLine.end();
733 
734  boost::match_results<std::string::const_iterator> what;
735  boost::match_flag_type flags = boost::match_default | boost::format_sed;
736  regex_search (start, end, what, expression, flags);
737 
738  // Put the matched strings in the list of tokens to be returned back
739  // to the caller
740  const unsigned short lMatchSetSize = what.size();
741  for (unsigned short matchIdx = 1; matchIdx != lMatchSetSize; ++matchIdx) {
742  const std::string lMatchedString (std::string (what[matchIdx].first,
743  what[matchIdx].second));
744  //if (lMatchedString.empty() == false) {
745  oTokenList.push_back (lMatchedString);
746  //}
747  }
748 
749  // DEBUG
750  // std::cout << "After (token list): " << oTokenList << std::endl;
751 
752  return oTokenList;
753 }
754 
755 // /////////////////////////////////////////////////////////
756 // Parse the token list of the 'price' command.
757 TokenList_T extractTokenListForTSAndBR (const TokenList_T& iTokenList) {
779  const std::string lRegEx("^([[:alpha:]]{2,3})"
780  "[[:space:]]+([[:digit:]]{1,4})"
781  "[/ ]*"
782  "[[:space:]]+([[:digit:]]{2,4})[/-]?"
783  "[[:space:]]*([[:alpha:]]{3}|[[:digit:]]{1,2})[/-]?"
784  "[[:space:]]*([[:digit:]]{1,2})[[:space:]]*"
785  "[[:space:]]+([[:alpha:]]{3})"
786  "[[:space:]]+([[:alpha:]]{3})"
787  "[[:space:]]+([[:digit:]]{1,2})[:]?([[:digit:]]{1,2})"
788  "[[:space:]]+([[:digit:]]{2,4})[/-]?"
789  "[[:space:]]*([[:alpha:]]{3}|[[:digit:]]{1,2})[/-]?"
790  "[[:space:]]*([[:digit:]]{1,2})"
791  "[[:space:]]+([[:digit:]]{1,2})[:]?([[:digit:]]{1,2})"
792  "[[:space:]]+([[:alpha:]]{3})"
793  "[[:space:]]+([[:alpha:]]{2})"
794  "[[:space:]]+([[:alpha:]]{2})"
795  "[[:space:]]+([[:digit:]]{1})$");
796 
797  //
798  const TokenList_T& oTokenList = extractTokenList (iTokenList, lRegEx);
799  return oTokenList;
800 }
801 
802 // /////////////////////////////////////////////////////////
803 // Parse the token list of the 'display' command.
804 TokenList_T extractTokenListForOriDestDate (const TokenList_T& iTokenList) {
814  const std::string lRegEx("^([[:alpha:]]{3})"
815  "[[:space:]]*[/-]?"
816  "[[:space:]]*([[:alpha:]]{3})"
817  "[[:space:]]*[/-]?"
818  "[[:space:]]*([[:digit:]]{2,4})"
819  "[[:space:]]*[/-]?"
820  "[[:space:]]*([[:alpha:]]{3}|[[:digit:]]{1,2})"
821  "[[:space:]]*[/-]?"
822  "[[:space:]]*([[:digit:]]{1,2})$");
823 
824  //
825  const TokenList_T& oTokenList = extractTokenList (iTokenList, lRegEx);
826  return oTokenList;
827 }
828 
829 // ///////// M A I N ////////////
830 int main (int argc, char* argv[]) {
831 
832  // State whether the BOM tree should be built-in or parsed from an
833  // input file
834  bool isBuiltin;
835 
836  // Fare input file name
837  stdair::Filename_T lFareInputFilename;
838 
839  // Readline history
840  const unsigned int lHistorySize (100);
841  const std::string lHistoryFilename ("simfqt.hist");
842  const std::string lHistoryBackupFilename ("simfqt.hist.bak");
843 
844  // Default parameters for the interactive session
845  stdair::AirportCode_T lInteractiveOrigin;
846  stdair::AirportCode_T lInteractiveDestination;
847  stdair::Date_T lInteractiveDepartureDate;
848 
849  // Output log File
850  stdair::Filename_T lLogFilename;
851 
852  // Call the command-line option parser
853  const int lOptionParserStatus =
854  readConfiguration (argc, argv, isBuiltin, lFareInputFilename, lLogFilename);
855 
856  if (lOptionParserStatus == K_SIMFQT_EARLY_RETURN_STATUS) {
857  return 0;
858  }
859 
860  // Set the log parameters
861  std::ofstream logOutputFile;
862  // Open and clean the log outputfile
863  logOutputFile.open (lLogFilename.c_str());
864  logOutputFile.clear();
865 
866  // Initialise the fareQuote service
867  const stdair::BasLogParams lLogParams (stdair::LOG::DEBUG, logOutputFile);
868  SIMFQT::SIMFQT_Service simfqtService (lLogParams);
869 
870  // DEBUG
871  STDAIR_LOG_DEBUG ("Welcome to SimFQT display");
872 
873  // Check wether or not a (CSV) input file should be read
874  if (isBuiltin == true) {
875  // Build the sample BOM tree (filled with fares) for Simfqt
876  simfqtService.buildSampleBom();
877  } else {
878  // Build the BOM tree from parsing a fare file
879  SIMFQT::FareFilePath lFareFilePath (lFareInputFilename);
880  simfqtService.parseAndLoad (lFareFilePath);
881  }
882 
883  // DEBUG: Display the whole BOM tree
884  const std::string& lCSVDump = simfqtService.csvDisplay();
885  STDAIR_LOG_DEBUG (lCSVDump);
886 
887  // DEBUG
888  STDAIR_LOG_DEBUG ("====================================================");
889  STDAIR_LOG_DEBUG ("= Beginning of the interactive session =");
890  STDAIR_LOG_DEBUG ("====================================================");
891 
892  // Initialise the GNU readline wrapper
893  swift::SReadline lReader (lHistoryFilename, lHistorySize);
894  initReadline (lReader);
895 
896  // Now we can ask user for a line
897  std::string lUserInput;
898  bool EndOfInput (false);
899  Command_T::Type_T lCommandType (Command_T::NOP);
900 
901  while (lCommandType != Command_T::QUIT && EndOfInput == false) {
902 
903  stdair::TravelSolutionList_T lInteractiveTravelSolutionList;
904  stdair::TravelSolutionStruct lInteractiveTravelSolution;
905 
906  // Update the default booking request.
907  // If there is an input file, we want the CRS booking request (defined in stdair).
908  // If not, we want the default booking request.
909  const bool isCRSBookingRequest = !isBuiltin;
910  const stdair::BookingRequestStruct& lInteractiveBookingRequest =
911  simfqtService.buildBookingRequest (isCRSBookingRequest);
912 
913  // Update the default parameters for the following interactive session.
914  if (isBuiltin == true) {
915  lInteractiveOrigin = "LHR";
916  lInteractiveDestination = "SYD";
917  lInteractiveDepartureDate = stdair::Date_T(2011,06,10);
918  simfqtService.buildSampleTravelSolutions (lInteractiveTravelSolutionList);
919  } else {
920  lInteractiveOrigin = "SIN";
921  lInteractiveDestination = "BKK";
922  lInteractiveDepartureDate = stdair::Date_T(2010,01,30);
923  //
924  const std::string lBA9_SegmentDateKey ("SQ, 970, 2010-01-30, SIN, BKK, 07:10");
925 
926  // Add the segment date key to the travel solution.
927  lInteractiveTravelSolution.addSegment (lBA9_SegmentDateKey);
928 
929  // Add the travel solution to the list
930  lInteractiveTravelSolutionList.push_back (lInteractiveTravelSolution);
931  }
932 
933  // Prompt.
934  std::ostringstream oPromptStr;
935  oPromptStr << "simfqt "
936  << "> ";
937  // The last parameter could be ommited.
938  TokenList_T lTokenListByReadline;
939  lUserInput = lReader.GetLine (oPromptStr.str(), lTokenListByReadline,
940  EndOfInput);
941 
942  // The history could be saved to an arbitrary file at any time.
943  lReader.SaveHistory (lHistoryBackupFilename);
944 
945  if (EndOfInput) {
946  std::cout << std::endl;
947  break;
948  }
949 
950  // Interpret the user input.
951  lCommandType = extractCommand (lTokenListByReadline);
952 
953  switch (lCommandType) {
954 
955  // ////////////////////////////// Help ////////////////////////
956  case Command_T::HELP: {
957  // Search for information to display default parameters lists.
958  // Get the first travel solution.
959  stdair::TravelSolutionStruct& lTravelSolutionStruct =
960  lInteractiveTravelSolutionList.front();
961  // Get the segment-path of the first travel solution.
962  const stdair::SegmentPath_T& lSegmentPath =
963  lTravelSolutionStruct.getSegmentPath();
964  // Get the first segment of the first travel solution.
965  const std::string& lSegmentDateKey = lSegmentPath.front();
966  // Get the parsed key of the first segment of the first travel solution.
967  const stdair::ParsedKey& lParsedKey =
968  stdair::BomKeyManager::extractKeys (lSegmentDateKey);
969  // Get the request date time
970  const stdair::DateTime_T& lRequestDateTime =
971  lInteractiveBookingRequest.getRequestDateTime();
972  const stdair::Time_T lRequestTime =
973  lRequestDateTime.time_of_day();
974  std::cout << std::endl;
975  // Display help.
976  std::cout << "Commands: " << std::endl;
977  std::cout << " help" << "\t\t" << "Display this help" << std::endl;
978  std::cout << " quit" << "\t\t" << "Quit the application" << std::endl;
979  std::cout << " list" << "\t\t"
980  << "List all the fare rule O&Ds and the corresponding date ranges" << std::endl;
981  std::cout << " display" << "\t"
982  << "Display all fare rules for an O&D and a departure date. \n" << "\t\t"
983  << "If no parameters specified or wrong list of parameters, default values are used: \n"<< "\t\t"
984  << " display " << lInteractiveOrigin << " "
985  << lInteractiveDestination << " "
986  << lInteractiveDepartureDate << std::endl;
987  std::cout << " price" << "\t\t"
988  << "Price the travel solution corresponding to a booking request. \n" << "\t\t"
989  << "If no parameters specified or wrong list of parameters, default value are used: \n" << "\t\t"
990  << " price "
991  << lParsedKey._airlineCode << " "
992  << lParsedKey._flightNumber << " "
993  << lParsedKey._departureDate << " "
994  << lParsedKey._boardingPoint << " "
995  << lParsedKey._offPoint << " "
996  << lParsedKey._boardingTime << " "
997  << lRequestDateTime.date() << " "
998  << lRequestTime.hours() << ":" << lRequestTime.minutes() << " "
999  << lInteractiveBookingRequest.getPOS() << " "
1000  << lInteractiveBookingRequest.getBookingChannel() << " "
1001  << lInteractiveBookingRequest.getTripType() << " "
1002  << lInteractiveBookingRequest.getStayDuration() << std::endl;
1003  std::cout << std::endl;
1004  break;
1005  }
1006 
1007  // ////////////////////////////// Quit ////////////////////////
1008  case Command_T::QUIT: {
1009  break;
1010  }
1011 
1012  // ////////////////////////////// List /////////////////////////
1013  case Command_T::LIST: {
1014 
1015  // Get the list of all airport pairs and date ranges for which
1016  // there are fares available.
1017  const std::string& lAirportPairDateListStr =
1018  simfqtService.list ();
1019 
1020  if (lAirportPairDateListStr.empty() == false) {
1021  std::cout << lAirportPairDateListStr << std::endl;
1022  STDAIR_LOG_DEBUG (lAirportPairDateListStr);
1023 
1024  } else {
1025  std::cerr << "There is no result for airport pairs and date ranges."
1026  << "Make sure your input file is not empty."
1027  << std::endl;
1028  }
1029 
1030  break;
1031  }
1032 
1033  // ////////////////////////////// Display /////////////////////////
1034  case Command_T::DISPLAY: {
1035 
1036  // If no parameters are entered by the user, keep default ones.
1037  if (lTokenListByReadline.empty() == true) {
1038 
1039  std::cout << "No parameters specified. Default paramaters '"
1040  << lInteractiveOrigin << "-" << lInteractiveDestination
1041  << "/" << lInteractiveDepartureDate
1042  << "' are kept."
1043  << std::endl;
1044 
1045  } else {
1046 
1047  // Find the best match corresponding to the given parameters.
1048  TokenList_T lTokenList =
1049  extractTokenListForOriDestDate (lTokenListByReadline);
1050 
1051  // Parse the best match, and give default values in case the
1052  // user does not specify all the parameters or does not
1053  // specify some of them correctly.
1054  parseFlightDateKey (lTokenList, lInteractiveOrigin,
1055  lInteractiveDestination, lInteractiveDepartureDate);
1056 
1057  }
1058 
1059  // Check whether the selected airportpair-date is valid:
1060  // i.e. if there are corresponding fare rules.
1061  const bool isAirportPairDateValid =
1062  simfqtService.check (lInteractiveOrigin, lInteractiveDestination,
1063  lInteractiveDepartureDate);
1064 
1065  if (isAirportPairDateValid == false) {
1066  std::ostringstream oFDKStr;
1067  oFDKStr << "The airport pair/departure date: "
1068  << lInteractiveOrigin << "-" << lInteractiveDestination
1069  << "/" << lInteractiveDepartureDate
1070  << " does not correpond to any fare rule.\n"
1071  << "Make sure it exists with the 'list' command.";
1072  std::cout << oFDKStr.str() << std::endl;
1073  STDAIR_LOG_ERROR (oFDKStr.str());
1074 
1075  break;
1076  }
1077 
1078  // Display the list of corresponding fare rules.
1079  std::cout << "List of fare rules for "
1080  << lInteractiveOrigin << "-"
1081  << lInteractiveDestination << "/"
1082  << lInteractiveDepartureDate
1083  << std::endl;
1084 
1085  const std::string& lFareRuleListStr =
1086  simfqtService.csvDisplay (lInteractiveOrigin,
1087  lInteractiveDestination,
1088  lInteractiveDepartureDate);
1089 
1090  assert (lFareRuleListStr.empty() == false);
1091  std::cout << lFareRuleListStr << std::endl;
1092  STDAIR_LOG_DEBUG (lFareRuleListStr);
1093 
1094  break;
1095  }
1096 
1097  // ////////////////////////////// Price ////////////////////////
1098  case Command_T::PRICE: {
1099 
1100  // If no parameters are entered by the user, keep default ones.
1101  if (lTokenListByReadline.empty() == true) {
1102 
1103  lInteractiveTravelSolution = lInteractiveTravelSolutionList.front();
1104 
1105  std::cout << "No parameters specified. Default booking request and default travel solution list are kept.\n"
1106  << "Booking request: << "
1107  << lInteractiveBookingRequest.display() << " >>"
1108  << "\nTravel Solution: << "
1109  << lInteractiveTravelSolution.display() << " >>"
1110  << "\n********** \n"
1111  << "Fare quote"
1112  << "\n**********"
1113  << std::endl;
1114 
1115  // Try to fareQuote the sample list of travel solutions.
1116  try {
1117  simfqtService.quotePrices (lInteractiveBookingRequest,
1118  lInteractiveTravelSolutionList);
1119  } catch (stdair::ObjectNotFoundException& E) {
1120  std::cerr << "The given travel solution corresponding to the given booking request can not be priced.\n"
1121  << E.what()
1122  << std::endl;
1123  break;
1124  }
1125  } else {
1126 
1127  // Find the best match corresponding to the given parameters.
1128  TokenList_T lTokenList =
1129  extractTokenListForTSAndBR (lTokenListByReadline);
1130 
1131  // Parse the best match, and give default values in case the
1132  // user does not specify all the parameters or does not
1133  // specify some of them correctly.
1134  stdair::BookingRequestStruct lFinalBookingRequest
1135  = parseTravelSolutionAndBookingRequestKey (lTokenList,
1136  lInteractiveTravelSolutionList,
1137  lInteractiveBookingRequest);
1138 
1139 
1140  assert (lInteractiveTravelSolutionList.size() >= 1);
1141  lInteractiveTravelSolution = lInteractiveTravelSolutionList.front();
1142 
1143  // Display the booking request and the first travel solution
1144  // before pricing.
1145  std::cout << "Booking request: << "
1146  << lFinalBookingRequest.display() << " >>"
1147  << "\nTravel Solution: << "
1148  << lInteractiveTravelSolution.display() << " >>"
1149  << "\n********** \n"
1150  << "Fare quote"
1151  << "\n**********"
1152  << std::endl;
1153 
1154  // Try to fareQuote the sample list of travel solutions.
1155  try {
1156  simfqtService.quotePrices (lFinalBookingRequest,
1157  lInteractiveTravelSolutionList);
1158  } catch (stdair::ObjectNotFoundException& E) {
1159  std::cerr << "The given travel solution corresponding to the given booking request can not be priced.\n"
1160  << E.what()
1161  << std::endl;
1162  break;
1163  }
1164  }
1165 
1166  // Display the first travel solution after pricing:
1167  // one or more fare option have been added.
1168  lInteractiveTravelSolution = lInteractiveTravelSolutionList.front();
1169  std::cout << "Travel Solution: << "
1170  << lInteractiveTravelSolution.display() << " >>\n"
1171  << std::endl;
1172 
1173  break;
1174  }
1175 
1176  // /////////////////////////// Default / No value ///////////////////////
1177  case Command_T::NOP: {
1178  break;
1179  }
1180  case Command_T::LAST_VALUE:
1181  default: {
1182  // DEBUG
1183  std::ostringstream oStr;
1184  oStr << "The '" << lUserInput << "' command is not yet understood.\n"
1185  << "Type help to have more information." << std::endl;
1186 
1187  STDAIR_LOG_DEBUG (oStr.str());
1188  std::cout << oStr.str() << std::endl;
1189  }
1190  }
1191  }
1192 
1193  // DEBUG
1194  STDAIR_LOG_DEBUG ("End of the session. Exiting.");
1195  std::cout << "End of the session. Exiting." << std::endl;
1196 
1197  // Close the Log outputFile
1198  logOutputFile.close();
1199 
1200  /*
1201  Note: as that program is not intended to be run on a server in
1202  production, it is better not to catch the exceptions. When it
1203  happens (that an exception is throwned), that way we get the
1204  call stack.
1205  */
1206 
1207  return 0;
1208 }