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  : CSequence<CAirlineIcaoCode>(other)
25  {}
26 
27  CAirlineIcaoCodeList::CAirlineIcaoCodeList(std::initializer_list<CAirlineIcaoCode> il)
29  {}
30 
32  {
33  if (!CAirlineIcaoCode::isValidAirlineDesignator(designator)) { return {}; }
34  return this->findBy([&](const CAirlineIcaoCode &code) { return code.matchesDesignator(designator); });
35  }
36 
38  {
39  if (!CAirlineIcaoCode::isValidIataCode(iata)) { return {}; }
40  return this->findBy([&](const CAirlineIcaoCode &code) { return code.matchesIataCode(iata); });
41  }
42 
44  {
45  const CAirlineIcaoCodeList codes = this->findByIataCode(iata);
46  return codes.size() == 1 ? codes.front() : CAirlineIcaoCode();
47  }
48 
50  {
51  if (designatorOrIata.isEmpty()) { return {}; }
52  return this->findBy(
53  [&](const CAirlineIcaoCode &code) { return code.matchesDesignatorOrIataCode(designatorOrIata); });
54  }
55 
57  {
58  if (!CAirlineIcaoCode::isValidAirlineDesignator(designator)) { return {}; }
59  return this->findBy([&](const CAirlineIcaoCode &code) { return code.matchesVDesignator(designator); });
60  }
61 
63  bool preferOperatingAirlines) const
64  {
65  CAirlineIcaoCodeList codes = this->findByVDesignator(designator);
66  if (codes.size() > 1 && preferOperatingAirlines) { codes.removeIf(&CAirlineIcaoCode::isOperating, false); }
67  return codes.size() == 1 ? codes.front() : CAirlineIcaoCode();
68  }
69 
71  {
72  if (designatorOrIata.isEmpty()) { return {}; }
73  return this->findBy(
74  [&](const CAirlineIcaoCode &code) { return code.matchesVDesignatorOrIataCode(designatorOrIata); });
75  }
76 
78  {
79  if (isoCode.length() != 2) { return {}; }
80  const QString iso(isoCode.toUpper());
81  return this->findBy([&](const CAirlineIcaoCode &code) { return code.getCountry().getIsoCode() == iso; });
82  }
83 
85  {
86  if (containedString.isEmpty()) { return {}; }
87  return this->findBy(
88  [&](const CAirlineIcaoCode &code) { return code.isContainedInSimplifiedName(containedString); });
89  }
90 
92  {
93  if (candidate.isEmpty()) { return {}; }
94  return this->findBy([&](const CAirlineIcaoCode &code) { return code.matchesTelephonyDesignator(candidate); });
95  }
96 
98  {
99  if (candidate.isEmpty()) { return {}; }
100  return this->findBy(
101  [&](const CAirlineIcaoCode &code) { return code.matchesNamesOrTelephonyDesignator(candidate); });
102  }
103 
105  {
106  return this->findBy([&](const CAirlineIcaoCode &code) { return code.isMilitary() == military; });
107  }
108 
110  {
111  return this->findBy([](const CAirlineIcaoCode &code) { return code.hasValidDesignator(); });
112  }
113 
115  {
116  return this->findBy([](const CAirlineIcaoCode &code) { return !code.hasValidDesignator(); });
117  }
118 
120  const CCallsign &callsign) const
121  {
122  if (icaoPattern.hasValidDbKey()) { return icaoPattern; }
123  const CAirlineIcaoCode patternUsed = icaoPattern.thisOrCallsignCode(callsign);
124 
125  // search by parts
126  CAirlineIcaoCodeList codesFound;
127  if (patternUsed.hasValidDesignator())
128  {
129  if (patternUsed.isVirtualAirline())
130  {
131  // we can tell for sure we search an VA
132  codesFound = this->findByVDesignator(patternUsed.getVDesignator());
133  }
134  else
135  {
136  // we do not know if we are looking for an VA
137  codesFound = this->findByDesignator(patternUsed.getDesignator());
138  }
139  }
140  else { codesFound = this->findByIataCode(patternUsed.getIataCode()); }
141 
142  if (codesFound.size() == 1) { return codesFound.front(); }
143  if (codesFound.isEmpty())
144  {
145  // nothing found so far
146  codesFound = this->findByNamesOrTelephonyDesignator(patternUsed.getName());
147  codesFound = codesFound.ifPossibleReduceByTelephonyDesignator(patternUsed.getTelephonyDesignator());
148  codesFound = codesFound.ifPossibleReduceByCountry(patternUsed.getCountryIso());
149  }
150  else
151  {
152  // further reduce
153  bool reduced = false;
154  codesFound = codesFound.ifPossibleReduceNameTelephonyCountry(
155  callsign, patternUsed.getName(), patternUsed.getTelephonyDesignator(), patternUsed.getCountryIso(),
156  reduced, QString(), nullptr);
157  }
158 
159  return codesFound.frontOrDefault();
160  }
161 
163  const CCallsign &cs, const QString &airlineName, const QString &telephony, const QString &countryIso,
164  bool &reduced, const QString &loginfo, CStatusMessageList *log) const
165  {
166  reduced = false;
167  if (this->isEmpty())
168  {
169  if (log)
170  {
171  CCallsign::addLogDetailsToList(log, cs, loginfo % u" Empty input list, cannot reduce",
172  getLogCategories());
173  }
174  return *this;
175  }
176 
177  if (telephony.isEmpty() && airlineName.isEmpty() && countryIso.isEmpty())
178  {
179  if (log)
180  {
182  loginfo % u" No name/telephony/country, cannot reduce " %
183  QString::number(this->size()) % u" entries",
184  getLogCategories());
185  }
186  return *this;
187  }
188 
189  CAirlineIcaoCodeList step1Data =
190  airlineName.isEmpty() ? *this : this->findByNamesOrTelephonyDesignator(airlineName);
191  if (step1Data.isEmpty() || step1Data.size() == this->size())
192  {
193  if (log)
194  {
196  log, cs, loginfo % QStringLiteral(" cannot reduce by '%1'").arg(airlineName), getLogCategories());
197  }
198  step1Data = *this;
199  }
200  else
201  {
202  reduced = true;
203  if (log)
204  {
205  CCallsign::addLogDetailsToList(log, cs, loginfo % QStringLiteral(" reduced by '%1'").arg(airlineName),
206  getLogCategories());
207  }
208  }
209  if (step1Data.size() == 1) { return step1Data; }
210 
211  CAirlineIcaoCodeList step2Data =
212  telephony.isEmpty() ? step1Data : step1Data.findByNamesOrTelephonyDesignator(telephony);
213  if (step2Data.isEmpty() || step2Data.size() == this->size())
214  {
215  if (log)
216  {
218  loginfo % QStringLiteral(" cannot reduce by name '%1'").arg(telephony),
219  getLogCategories());
220  }
221  step2Data = step1Data;
222  }
223  else
224  {
225  reduced = true;
226  if (log)
227  {
229  log, cs, loginfo % QStringLiteral(" reduced by telephony '%1'").arg(telephony), getLogCategories());
230  }
231  }
232  if (step2Data.size() == 1) { return step2Data; }
233 
234  CAirlineIcaoCodeList step3Data = countryIso.isEmpty() ? step2Data : step2Data.findByCountryIsoCode(countryIso);
235  if (step3Data.isEmpty() || step3Data.size() == this->size())
236  {
237  if (log)
238  {
240  log, cs, loginfo % QStringLiteral(" cannot reduce by country '%1'").arg(countryIso),
241  getLogCategories());
242  }
243  step3Data = step2Data;
244  }
245  else
246  {
247  reduced = true;
248  if (log)
249  {
250  CCallsign::addLogDetailsToList(log, cs, loginfo % QStringLiteral(" reduced by '%1'").arg(countryIso),
251  getLogCategories());
252  }
253  }
254  return step3Data;
255  }
256 
258  {
259  if (countryIso.isEmpty()) { return *this; }
260  if (this->isEmpty()) { return *this; }
261  const CAirlineIcaoCodeList found = this->findByCountryIsoCode(countryIso);
262  if (found.size() == this->size() || found.isEmpty()) { return *this; }
263  return found;
264  }
265 
268  {
269  if (telephonyDesignator.isEmpty()) { return *this; }
270  if (this->isEmpty()) { return *this; }
271  const CAirlineIcaoCodeList found = this->findByTelephonyDesignator(telephonyDesignator);
272  if (found.size() == this->size() || found.isEmpty()) { return *this; }
273  return found;
274  }
275 
277  {
278  if (this->isEmpty() || callsign.isEmpty()) { return {}; }
279  const QString airline = callsign.getAirlinePrefix().toUpper();
280  if (airline.isEmpty()) { return {}; }
281  const CAirlineIcaoCode airlineCode = (airline.length() == 3) ?
284  return airlineCode;
285  }
286 
288  CAirlineIcaoCodeList *inconsistent)
289  {
290  CAirlineIcaoCodeList codes;
291  for (const QJsonValue &value : array)
292  {
293  const CAirlineIcaoCode icao(CAirlineIcaoCode::fromDatabaseJson(value.toObject()));
294  const bool incomplete = !icao.hasCompleteData();
295  if (incomplete)
296  {
297  if (ignoreIncomplete) { continue; }
298  if (inconsistent)
299  {
300  inconsistent->push_back(icao);
301  continue;
302  }
303  }
304  codes.push_back(icao);
305  }
306  return codes;
307  }
308 
310  {
311  QStringList c;
312  for (const CAirlineIcaoCode &icao : *this)
313  {
314  if (combinedString)
315  {
316  if (!icao.hasValidDbKey()) { continue; }
317  const QString cs(icao.getCombinedStringWithKey());
318  if (cs.isEmpty()) { continue; }
319  c.append(cs);
320  }
321  else
322  {
323  const QString d(icao.getDesignator());
324  if (c.contains(d) || d.isEmpty()) { continue; }
325  c.append(d);
326  }
327  }
328  if (sort) { c.sort(); }
329  return c;
330  }
331 
333  {
334  QStringList c;
335  for (const CAirlineIcaoCode &icao : *this)
336  {
337  if (!icao.hasValidDesignator()) { continue; }
338  const QString cs(icao.getDesignatorNameCountry());
339  if (cs.isEmpty()) { continue; }
340  c.append(cs);
341  }
342  if (sort) { c.sort(); }
343  return c;
344  }
345 
347  {
348  QStringList c;
349  for (const CAirlineIcaoCode &icao : *this)
350  {
351  if (!icao.hasValidDbKey()) { continue; }
352  const QString cs(icao.getNameWithKey());
353  if (cs.isEmpty()) { continue; }
354  c.append(cs);
355  }
356  if (sort) { c.sort(); }
357  return c;
358  }
359 
361  {
362  CSetBuilder<QString> designators;
363  for (const CAirlineIcaoCode &icao : *this)
364  {
365  if (!icao.hasValidDesignator()) { continue; }
366  designators.insert(icao.getDesignator());
367  }
368  return designators;
369  }
370 
372  {
373  CSetBuilder<QString> designators;
374  for (const CAirlineIcaoCode &icao : *this)
375  {
376  if (!icao.hasValidDesignator()) { continue; }
377  designators.insert(icao.getVDesignator());
378  }
379  return designators;
380  }
381 
382  bool CAirlineIcaoCodeList::containsDesignator(const QString &designator) const
383  {
384  if (designator.isEmpty()) { return false; }
385  return this->contains(&CAirlineIcaoCode::getDesignator, designator.toUpper());
386  }
387 
388  bool CAirlineIcaoCodeList::containsVDesignator(const QString &vDesignator) const
389  {
390  if (vDesignator.isEmpty()) { return false; }
391  if (vDesignator.length() < 4) { return this->containsDesignator(vDesignator); }
392  return this->contains(&CAirlineIcaoCode::getVDesignator, vDesignator.toUpper());
393  }
394 
396  {
397  AirlineIcaoIdMap map;
398  for (const CAirlineIcaoCode &code : *this)
399  {
400  if (!code.hasValidDbKey()) { continue; }
401  map.insert(code.getDbKey(), code);
402  }
403  return map;
404  }
405 } // 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()=default
Default constructor.
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
void append(QList< T > &&value)
bool isEmpty() const const
qsizetype length() const const
QString number(double n, char format, int precision)
QString toUpper() const const
bool contains(QLatin1StringView str, Qt::CaseSensitivity cs) const const
void sort(Qt::CaseSensitivity cs)
#define SWIFT_DEFINE_SEQUENCE_MIXINS(Namespace, T, List)
Explicit template definition of mixins for a CSequence subclass.
Definition: sequence.h:63