swift
airlineicaocodelist.cpp
1 // SPDX-FileCopyrightText: Copyright (C) 2015 swift Project Community / Contributors
2 // SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-swift-pilot-client-1
3 
5 
6 #include <QJsonValue>
7 #include <QStringBuilder>
8 
9 #include "misc/country.h"
10 #include "misc/logcategories.h"
11 #include "misc/setbuilder.h"
12 
13 SWIFT_DEFINE_SEQUENCE_MIXINS(swift::misc::aviation, CAirlineIcaoCode, CAirlineIcaoCodeList)
14 
15 namespace swift::misc::aviation
16 {
18  {
19  static const QStringList cats(CLogCategories::aviation());
20  return cats;
21  }
22 
24 
26  : CSequence<CAirlineIcaoCode>(other)
27  {}
28 
29  CAirlineIcaoCodeList::CAirlineIcaoCodeList(std::initializer_list<CAirlineIcaoCode> il)
31  {}
32 
34  {
36  return this->findBy([&](const CAirlineIcaoCode &code) { return code.matchesDesignator(designator); });
37  }
38 
40  {
42  return this->findBy([&](const CAirlineIcaoCode &code) { return code.matchesIataCode(iata); });
43  }
44 
46  {
47  const CAirlineIcaoCodeList codes = this->findByIataCode(iata);
48  return codes.size() == 1 ? codes.front() : CAirlineIcaoCode();
49  }
50 
52  {
53  if (designatorOrIata.isEmpty()) { return CAirlineIcaoCodeList(); }
54  return this->findBy(
55  [&](const CAirlineIcaoCode &code) { return code.matchesDesignatorOrIataCode(designatorOrIata); });
56  }
57 
59  {
61  return this->findBy([&](const CAirlineIcaoCode &code) { return code.matchesVDesignator(designator); });
62  }
63 
65  bool preferOperatingAirlines) const
66  {
67  CAirlineIcaoCodeList codes = this->findByVDesignator(designator);
68  if (codes.size() > 1 && preferOperatingAirlines) { codes.removeIf(&CAirlineIcaoCode::isOperating, false); }
69  return codes.size() == 1 ? codes.front() : CAirlineIcaoCode();
70  }
71 
73  {
74  if (designatorOrIata.isEmpty()) { return CAirlineIcaoCodeList(); }
75  return this->findBy(
76  [&](const CAirlineIcaoCode &code) { return code.matchesVDesignatorOrIataCode(designatorOrIata); });
77  }
78 
80  {
81  if (isoCode.length() != 2) { return CAirlineIcaoCodeList(); }
82  const QString iso(isoCode.toUpper());
83  return this->findBy([&](const CAirlineIcaoCode &code) { return code.getCountry().getIsoCode() == iso; });
84  }
85 
87  {
88  if (containedString.isEmpty()) { return CAirlineIcaoCodeList(); }
89  return this->findBy(
90  [&](const CAirlineIcaoCode &code) { return code.isContainedInSimplifiedName(containedString); });
91  }
92 
94  {
95  if (candidate.isEmpty()) { return CAirlineIcaoCodeList(); }
96  return this->findBy([&](const CAirlineIcaoCode &code) { return code.matchesTelephonyDesignator(candidate); });
97  }
98 
100  {
101  if (candidate.isEmpty()) { return CAirlineIcaoCodeList(); }
102  return this->findBy(
103  [&](const CAirlineIcaoCode &code) { return code.matchesNamesOrTelephonyDesignator(candidate); });
104  }
105 
107  {
108  return this->findBy([&](const CAirlineIcaoCode &code) { return code.isMilitary() == military; });
109  }
110 
112  {
113  return this->findBy([](const CAirlineIcaoCode &code) { return code.hasValidDesignator(); });
114  }
115 
117  {
118  return this->findBy([](const CAirlineIcaoCode &code) { return !code.hasValidDesignator(); });
119  }
120 
122  const CCallsign &callsign) const
123  {
124  if (icaoPattern.hasValidDbKey()) { return icaoPattern; }
125  const CAirlineIcaoCode patternUsed = icaoPattern.thisOrCallsignCode(callsign);
126 
127  // search by parts
128  CAirlineIcaoCodeList codesFound;
129  if (patternUsed.hasValidDesignator())
130  {
131  if (patternUsed.isVirtualAirline())
132  {
133  // we can tell for sure we search an VA
134  codesFound = this->findByVDesignator(patternUsed.getVDesignator());
135  }
136  else
137  {
138  // we do not know if we are looking for an VA
139  codesFound = this->findByDesignator(patternUsed.getDesignator());
140  }
141  }
142  else { codesFound = this->findByIataCode(patternUsed.getIataCode()); }
143 
144  if (codesFound.size() == 1) { return codesFound.front(); }
145  if (codesFound.isEmpty())
146  {
147  // nothing found so far
148  codesFound = this->findByNamesOrTelephonyDesignator(patternUsed.getName());
149  codesFound = codesFound.ifPossibleReduceByTelephonyDesignator(patternUsed.getTelephonyDesignator());
150  codesFound = codesFound.ifPossibleReduceByCountry(patternUsed.getCountryIso());
151  }
152  else
153  {
154  // further reduce
155  bool reduced = false;
156  codesFound = codesFound.ifPossibleReduceNameTelephonyCountry(
157  callsign, patternUsed.getName(), patternUsed.getTelephonyDesignator(), patternUsed.getCountryIso(),
158  reduced, QString(), nullptr);
159  }
160 
161  return codesFound.frontOrDefault();
162  }
163 
165  const CCallsign &cs, const QString &airlineName, const QString &telephony, const QString &countryIso,
166  bool &reduced, const QString &loginfo, CStatusMessageList *log) const
167  {
168  reduced = false;
169  if (this->isEmpty())
170  {
171  if (log)
172  {
173  CCallsign::addLogDetailsToList(log, cs, loginfo % u" Empty input list, cannot reduce",
174  getLogCategories());
175  }
176  return *this;
177  }
178 
179  if (telephony.isEmpty() && airlineName.isEmpty() && countryIso.isEmpty())
180  {
181  if (log)
182  {
184  loginfo % u" No name/telephony/country, cannot reduce " %
185  QString::number(this->size()) % u" entries",
186  getLogCategories());
187  }
188  return *this;
189  }
190 
191  CAirlineIcaoCodeList step1Data =
192  airlineName.isEmpty() ? *this : this->findByNamesOrTelephonyDesignator(airlineName);
193  if (step1Data.isEmpty() || step1Data.size() == this->size())
194  {
195  if (log)
196  {
198  log, cs, loginfo % QStringLiteral(" cannot reduce by '%1'").arg(airlineName), getLogCategories());
199  }
200  step1Data = *this;
201  }
202  else
203  {
204  reduced = true;
205  if (log)
206  {
207  CCallsign::addLogDetailsToList(log, cs, loginfo % QStringLiteral(" reduced by '%1'").arg(airlineName),
208  getLogCategories());
209  }
210  }
211  if (step1Data.size() == 1) { return step1Data; }
212 
213  CAirlineIcaoCodeList step2Data =
214  telephony.isEmpty() ? step1Data : step1Data.findByNamesOrTelephonyDesignator(telephony);
215  if (step2Data.isEmpty() || step2Data.size() == this->size())
216  {
217  if (log)
218  {
220  loginfo % QStringLiteral(" cannot reduce by name '%1'").arg(telephony),
221  getLogCategories());
222  }
223  step2Data = step1Data;
224  }
225  else
226  {
227  reduced = true;
228  if (log)
229  {
231  log, cs, loginfo % QStringLiteral(" reduced by telephony '%1'").arg(telephony), getLogCategories());
232  }
233  }
234  if (step2Data.size() == 1) { return step2Data; }
235 
236  CAirlineIcaoCodeList step3Data = countryIso.isEmpty() ? step2Data : step2Data.findByCountryIsoCode(countryIso);
237  if (step3Data.isEmpty() || step3Data.size() == this->size())
238  {
239  if (log)
240  {
242  log, cs, loginfo % QStringLiteral(" cannot reduce by country '%1'").arg(countryIso),
243  getLogCategories());
244  }
245  step3Data = step2Data;
246  }
247  else
248  {
249  reduced = true;
250  if (log)
251  {
252  CCallsign::addLogDetailsToList(log, cs, loginfo % QStringLiteral(" reduced by '%1'").arg(countryIso),
253  getLogCategories());
254  }
255  }
256  return step3Data;
257  }
258 
260  {
261  if (countryIso.isEmpty()) { return *this; }
262  if (this->isEmpty()) { return *this; }
263  const CAirlineIcaoCodeList found = this->findByCountryIsoCode(countryIso);
264  if (found.size() == this->size() || found.isEmpty()) { return *this; }
265  return found;
266  }
267 
269  CAirlineIcaoCodeList::ifPossibleReduceByTelephonyDesignator(const QString &telephonyDesignator) const
270  {
271  if (telephonyDesignator.isEmpty()) { return *this; }
272  if (this->isEmpty()) { return *this; }
273  const CAirlineIcaoCodeList found = this->findByTelephonyDesignator(telephonyDesignator);
274  if (found.size() == this->size() || found.isEmpty()) { return *this; }
275  return found;
276  }
277 
279  {
280  if (this->isEmpty() || callsign.isEmpty()) { return CAirlineIcaoCode(); }
281  const QString airline = callsign.getAirlinePrefix().toUpper();
282  if (airline.isEmpty()) { return CAirlineIcaoCode(); }
283  const CAirlineIcaoCode airlineCode = (airline.length() == 3) ?
286  return airlineCode;
287  }
288 
289  CAirlineIcaoCodeList CAirlineIcaoCodeList::fromDatabaseJson(const QJsonArray &array, bool ignoreIncomplete,
290  CAirlineIcaoCodeList *inconsistent)
291  {
292  CAirlineIcaoCodeList codes;
293  for (const QJsonValue &value : array)
294  {
295  const CAirlineIcaoCode icao(CAirlineIcaoCode::fromDatabaseJson(value.toObject()));
296  const bool incomplete = !icao.hasCompleteData();
297  if (incomplete)
298  {
299  if (ignoreIncomplete) { continue; }
300  if (inconsistent)
301  {
302  inconsistent->push_back(icao);
303  continue;
304  }
305  }
306  codes.push_back(icao);
307  }
308  return codes;
309  }
310 
311  QStringList CAirlineIcaoCodeList::toIcaoDesignatorCompleterStrings(bool combinedString, bool sort) const
312  {
313  QStringList c;
314  for (const CAirlineIcaoCode &icao : *this)
315  {
316  if (combinedString)
317  {
318  if (!icao.hasValidDbKey()) { continue; }
319  const QString cs(icao.getCombinedStringWithKey());
320  if (cs.isEmpty()) { continue; }
321  c.append(cs);
322  }
323  else
324  {
325  const QString d(icao.getDesignator());
326  if (c.contains(d) || d.isEmpty()) { continue; }
327  c.append(d);
328  }
329  }
330  if (sort) { c.sort(); }
331  return c;
332  }
333 
335  {
336  QStringList c;
337  for (const CAirlineIcaoCode &icao : *this)
338  {
339  if (!icao.hasValidDesignator()) { continue; }
340  const QString cs(icao.getDesignatorNameCountry());
341  if (cs.isEmpty()) { continue; }
342  c.append(cs);
343  }
344  if (sort) { c.sort(); }
345  return c;
346  }
347 
348  QStringList CAirlineIcaoCodeList::toNameCompleterStrings(bool sort) const
349  {
350  QStringList c;
351  for (const CAirlineIcaoCode &icao : *this)
352  {
353  if (!icao.hasValidDbKey()) { continue; }
354  const QString cs(icao.getNameWithKey());
355  if (cs.isEmpty()) { continue; }
356  c.append(cs);
357  }
358  if (sort) { c.sort(); }
359  return c;
360  }
361 
363  {
364  CSetBuilder<QString> designators;
365  for (const CAirlineIcaoCode &icao : *this)
366  {
367  if (!icao.hasValidDesignator()) { continue; }
368  designators.insert(icao.getDesignator());
369  }
370  return designators;
371  }
372 
374  {
375  CSetBuilder<QString> designators;
376  for (const CAirlineIcaoCode &icao : *this)
377  {
378  if (!icao.hasValidDesignator()) { continue; }
379  designators.insert(icao.getVDesignator());
380  }
381  return designators;
382  }
383 
384  bool CAirlineIcaoCodeList::containsDesignator(const QString &designator) const
385  {
386  if (designator.isEmpty()) { return false; }
387  return this->contains(&CAirlineIcaoCode::getDesignator, designator.toUpper());
388  }
389 
390  bool CAirlineIcaoCodeList::containsVDesignator(const QString &vDesignator) const
391  {
392  if (vDesignator.isEmpty()) { return false; }
393  if (vDesignator.length() < 4) { return this->containsDesignator(vDesignator); }
394  return this->contains(&CAirlineIcaoCode::getVDesignator, vDesignator.toUpper());
395  }
396 
398  {
399  AirlineIcaoIdMap map;
400  for (const CAirlineIcaoCode &code : *this)
401  {
402  if (!code.hasValidDbKey()) { continue; }
403  map.insert(code.getDbKey(), code);
404  }
405  return map;
406  }
407 } // namespace swift::misc::aviation
static const QString & aviation()
Aviation specific.
auto findFirstByOrDefault(Predicate p, const Value &def) const
Return a copy of the first element for which a given predicate returns true, or a default value if th...
Definition: range.h:70
bool contains(const T &object) const
Return true if there is an element equal to given object. Uses the most efficient implementation avai...
Definition: range.h:109
size_type size() const
Returns number of elements in the sequence.
Definition: sequence.h:273
const_reference frontOrDefault() const
Access the first element, or a default-initialized value if the sequence is empty.
Definition: sequence.h:239
CSequence findBy(Predicate p) const
Return a copy containing only those elements for which a given predicate returns true.
Definition: sequence.h:398
int removeIf(Predicate p)
Remove elements for which a given predicate returns true.
Definition: sequence.h:446
void push_back(const T &value)
Appends an element at the end of the sequence.
Definition: sequence.h:305
reference front()
Access the first element.
Definition: sequence.h:225
bool isEmpty() const
Synonym for empty.
Definition: sequence.h:285
void sort(Predicate p)
In-place sort by a given comparator predicate.
Definition: sequence.h:560
Build a QSet more efficiently when calling insert() in a for loop.
Definition: setbuilder.h:25
void insert(const T &value)
Add an element to the set. Runs in amortized constant time.
Definition: setbuilder.h:29
Status messages, e.g. from Core -> GUI.
Value object for ICAO classification.
QString getCombinedStringWithKey() const
Comined string with key.
bool isVirtualAirline() const
Virtual airline.
static bool isValidAirlineDesignator(const QString &airline)
Valid designator?
const QString & getCountryIso() const
Get country, e.g. "FR".
const QString & getIataCode() const
IATA code.
bool isContainedInSimplifiedName(const QString &candidate) const
Does simplified name contain the candidate.
bool matchesDesignator(const QString &designator) const
Matches designator string?
bool isMilitary() const
Military, air force or such?
const QString & getTelephonyDesignator() const
Telephony designator such as "Speedbird".
const QString & getDesignator() const
Get airline, e.g. "DLH".
bool hasCompleteData() const
Complete data.
bool hasValidDesignator() const
Airline designator available?
static CAirlineIcaoCode fromDatabaseJson(const QJsonObject &json, const QString &prefix=QString())
From our DB JSON.
bool matchesTelephonyDesignator(const QString &candidate) const
Matches telephony designator (aka callsign, not to be confused with CCallsign)
bool matchesVDesignator(const QString &designator) const
Matches v-designator string?
static bool isValidIataCode(const QString &iataCode)
Valid IATA code?
bool matchesNamesOrTelephonyDesignator(const QString &candidate) const
Relaxed check by name or telephony designator (aka callsign, not to be confused with CCallsign)
bool matchesVDesignatorOrIataCode(const QString &candidate) const
Matches IATA code or v-designator?
bool matchesDesignatorOrIataCode(const QString &candidate) const
Matches IATA code or designator?
const CCountry & getCountry() const
Get country, e.g. "FRANCE".
bool matchesIataCode(const QString &iata) const
Matches IATA code?
QString getDesignatorNameCountry() const
Combined string designator, name, country.
QString getVDesignator() const
Get airline, e.g. "DLH", but "VMVA" for virtual airlines.
const QString & getName() const
Get name, e.g. "Lufthansa".
CAirlineIcaoCode thisOrCallsignCode(const CCallsign &callsign) const
What is better, the callsign airline code or this code. Return the better one.
QString getNameWithKey() const
Name plus key, e.g. "Lufthansa (3421)".
Value object encapsulating a list of ICAO codes.
CAirlineIcaoCode findByUniqueIataCodeOrDefault(const QString &iata) const
Find by IATA code if this is unique, otherwise return default object.
CAirlineIcaoCodeList ifPossibleReduceNameTelephonyCountry(const swift::misc::aviation::CCallsign &cs, const QString &airlineName, const QString &telephony, const QString &countryIso, bool &reduced, const QString &logInfo, CStatusMessageList *log) const
Reduce by airline name/telephone designator, ISO country.
CAirlineIcaoCodeList findByMilitary(bool military) const
Find by military flag.
QSet< QString > allVDesignators() const
All designators.
QStringList toIcaoDesignatorNameCountryCompleterStrings(bool sort=true) const
String list for completion by ICAO designator plus Name.
CAirlineIcaoCodeList findByNamesOrTelephonyDesignator(const QString &candidate) const
Find by names or telephony designator (aka callsign, not to be confused with CCallsign)
bool containsVDesignator(const QString &vDesignator) const
Contains given designator?
CAirlineIcaoCodeList findByValidDesignator() const
The ones with a valid designator.
CAirlineIcaoCodeList findByTelephonyDesignator(const QString &candidate) const
Find by names or telephony designator (aka callsign, not to be confused with CCallsign)
AirlineIcaoIdMap toIdMap() const
To id map.
CAirlineIcaoCodeList findByVDesignatorOrIataCode(const QString &designatorOrIata) const
Find by v-designator or IATA code.
static const QStringList & getLogCategories()
Categories.
CAirlineIcaoCodeList findByInvalidDesignator() const
The ones with an invalid designator.
static CAirlineIcaoCodeList fromDatabaseJson(const QJsonArray &array, bool ignoreIncomplete=true, CAirlineIcaoCodeList *inconsistent=nullptr)
From our DB JSON.
CAirlineIcaoCodeList ifPossibleReduceByCountry(const QString &countryIso) const
Reduce by ISO country.
CAirlineIcaoCode findBestMatchByCallsign(const CCallsign &callsign) const
Use callsign to conclude airline.
CAirlineIcaoCodeList findByIataCode(const QString &iata) const
Find by IATA code Not unique because of virtual airlines and ceased airlines.
CAirlineIcaoCodeList findByDesignatorOrIataCode(const QString &designatorOrIata) const
Find by designator or IATA code.
CAirlineIcaoCodeList findByCountryIsoCode(const QString &isoCode) const
Find by country code.
QStringList toNameCompleterStrings(bool sort=true) const
String list for completion by name.
CAirlineIcaoCodeList findBySimplifiedNameContaining(const QString &containedString) const
Find if simplified name contains search string.
CAirlineIcaoCodeList ifPossibleReduceByTelephonyDesignator(const QString &telephonyDesignator) const
Reduce by telephony designator.
CAirlineIcaoCode findByUniqueVDesignatorOrDefault(const QString &designator, bool preferOperatingAirlines) const
Find by ICAO code if this is unique, otherwise return default object.
QStringList toIcaoDesignatorCompleterStrings(bool combinedString=true, bool sort=true) const
String list for completion by ICAO designator.
CAirlineIcaoCodeList findByDesignator(const QString &designator) const
Find by designator Not unique because of virtual airlines.
bool containsDesignator(const QString &designator) const
Contains given designator?
QSet< QString > allDesignators() const
All designators.
CAirlineIcaoCode smartAirlineIcaoSelector(const CAirlineIcaoCode &icaoPattern, const CCallsign &callsign) const
Best selection by given pattern.
CAirlineIcaoCodeList findByVDesignator(const QString &designator) const
Find by v-designator, this should be unique.
Value object encapsulating information of a callsign.
Definition: callsign.h:30
static void addLogDetailsToList(CStatusMessageList *log, const CCallsign &callsign, const QString &message, const QStringList &extraCategories={}, CStatusMessage::StatusSeverity s=CStatusMessage::SeverityInfo)
Specialized log for matching / reverse lookup.
Definition: callsign.cpp:110
bool isEmpty() const
Is empty?
Definition: callsign.h:63
QString getAirlinePrefix() const
Airline suffix (e.g. DLH1234 -> DLH) if applicable.
Definition: callsign.cpp:219
bool hasValidDbKey() const
Has valid DB key.
Definition: datastore.h:102
#define SWIFT_DEFINE_SEQUENCE_MIXINS(Namespace, T, List)
Explicit template definition of mixins for a CSequence subclass.
Definition: sequence.h:63