swift
aircraftmodellist.cpp
1 // SPDX-FileCopyrightText: Copyright (C) 2013 swift Project Community / Contributors
2 // SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-swift-pilot-client-1
3 
5 
6 #include <tuple>
7 
8 #include <QDir>
9 #include <QFileInfo>
10 #include <QJsonValue>
11 #include <QList>
12 #include <QMultiMap>
13 #include <QStringBuilder>
14 
15 #include "config/buildconfig.h"
16 #include "misc/aviation/callsign.h"
17 #include "misc/directoryutils.h"
18 #include "misc/fileutils.h"
19 #include "misc/iterator.h"
20 #include "misc/math/mathutils.h"
23 #include "misc/range.h"
24 #include "misc/setbuilder.h"
25 #include "misc/statusmessage.h"
26 #include "misc/stringutils.h"
27 #include "misc/swiftdirectories.h"
28 
29 using namespace swift::config;
30 using namespace swift::misc::network;
31 using namespace swift::misc::math;
32 using namespace swift::misc::aviation;
33 using namespace swift::misc::physical_quantities;
34 
35 SWIFT_DEFINE_SEQUENCE_MIXINS(swift::misc::simulation, CAircraftModel, CAircraftModelList)
36 
37 namespace swift::misc::simulation
38 {
39  CAircraftModelList::CAircraftModelList(const CSequence<CAircraftModel> &other) : CSequence<CAircraftModel>(other) {}
40 
41  bool CAircraftModelList::containsModelString(const QString &modelString, Qt::CaseSensitivity sensitivity) const
42  {
43  return std::any_of(cbegin(), cend(), [&](const CAircraftModel &model) {
44  return model.matchesModelString(modelString, sensitivity);
45  });
46  }
47 
49  Qt::CaseSensitivity sensitivity) const
50  {
51  return std::any_of(cbegin(), cend(), [&](const CAircraftModel &m) {
52  return (m.hasValidDbKey() && m.getDbKey() == model.getDbKey()) ||
53  m.matchesModelString(model.getModelString(), sensitivity);
54  });
55  }
56 
57  bool CAircraftModelList::containsCallsign(const CCallsign &callsign) const
58  {
59  return this->contains(&CAircraftModel::getCallsign, callsign);
60  }
61 
62  bool CAircraftModelList::containsCombinedType(const QString &combinedType) const
63  {
64  if (combinedType.isEmpty()) { return false; }
65  const QString ct(combinedType.toUpper().trimmed());
66  return this->containsBy(
67  [&](const CAircraftModel &model) { return model.getAircraftIcaoCode().getCombinedType() == ct; });
68  }
69 
71  {
72  return this->contains(&CAircraftModel::getAircraftIcaoCodeDesignator, aircraftDesignator);
73  }
74 
76  const QString &airlineDesignator) const
77  {
78  return this->contains(&CAircraftModel::getAircraftIcaoCodeDesignator, aircraftDesignator,
80  }
81 
83  {
84  if (!airline.hasValidDesignator()) { return false; }
85  return this->contains(&CAircraftModel::getAirlineIcaoCode, airline);
86  }
87 
89  Qt::CaseSensitivity sensitivity) const
90  {
91  return this->findBy(
92  [&](const CAircraftModel &model) { return model.matchesModelString(modelString, sensitivity); });
93  }
94 
96  {
97  return this->findBy([&](const CAircraftModel &model) { return !model.hasModelString(); });
98  }
99 
101  Qt::CaseSensitivity sensitivity) const
102  {
103  if (modelString.isEmpty()) { return {}; }
104  return this->findFirstByOrDefault(
105  [&](const CAircraftModel &model) { return model.matchesModelString(modelString, sensitivity); });
106  }
107 
109  Qt::CaseSensitivity sensitivity) const
110  {
111  if (modelString.isEmpty()) { return {}; }
112  return this->findFirstByOrDefault(
113  [&](const CAircraftModel &model) { return model.matchesModelStringOrAlias(modelString, sensitivity); });
114  }
115 
117  {
118  if (callsign.isEmpty()) { return {}; }
119  return this->findFirstByOrDefault([&](const CAircraftModel &model) { return model.getCallsign() == callsign; });
120  }
121 
123  const CAirlineIcaoCode &airlineIcaoCode) const
124  {
125  const QString aircraft(aircraftIcaoCode.getDesignator());
126  const QString airline(airlineIcaoCode.getDesignator());
127 
128  if (airline.isEmpty())
129  {
130  return this->findBy(
131  [&](const CAircraftModel &model) { return model.getAircraftIcaoCode().getDesignator() == aircraft; });
132  }
133  if (aircraft.isEmpty())
134  {
135  return this->findBy(
136  [&](const CAircraftModel &model) { return model.getAirlineIcaoCode().getDesignator() == airline; });
137  }
138  return this->findBy([&](const CAircraftModel &model) {
139  return model.getAirlineIcaoCode().getDesignator() == airline &&
140  model.getAircraftIcaoCode().getDesignator() == aircraft;
141  });
142  }
143 
145  const CAirlineIcaoCode &airlineIcaoCode) const
146  {
148  airlineIcaoCode);
149  }
150 
153  const QString &combinedCode) const
154  {
155  if (aircraftDesignator.isEmpty()) { return {}; }
156  return this->findBy([&](const CAircraftModel &model) {
157  if (!model.getAircraftIcaoCode().matchesDesignator(aircraftDesignator)) { return false; }
158  return model.getLivery().matchesCombinedCode(combinedCode);
159  });
160  }
161 
163  const CLivery &livery) const
164  {
165  return this->findBy(&CAircraftModel::getAircraftIcaoCode, aircraftIcaoCode, &CAircraftModel::getLivery, livery);
166  }
167 
169  {
170  const int id = airline.getGroupId();
171  if (id < 0) return {};
172  return this->findBy([&](const CAircraftModel &model) { return model.getAirlineIcaoCode().getGroupId() == id; });
173  }
174 
176  const QString &telephony,
177  bool onlyIfExistInModel) const
178  {
179  return this->findBy([&](const CAircraftModel &model) {
180  if (!model.hasAirlineDesignator() || !model.hasValidDbKey()) { return false; }
181  const CAirlineIcaoCode &icao = model.getAirlineIcaoCode();
182  if (!name.isEmpty() && (icao.hasName() || !onlyIfExistInModel))
183  {
184  if (!icao.getName().contains(name, Qt::CaseInsensitive)) { return false; }
185  }
186 
187  if (!telephony.isEmpty() && (icao.hasTelephonyDesignator() || !onlyIfExistInModel))
188  {
189  if (!icao.getTelephonyDesignator().contains(name, Qt::CaseInsensitive)) { return false; }
190  }
191  return true;
192  });
193  }
194 
195  CAircraftModelList CAircraftModelList::findByAirlineNamesOrTelephonyDesignator(const QString &name) const
196  {
197  return this->findBy([&](const CAircraftModel &model) {
198  if (!model.hasAirlineDesignator() || !model.hasValidDbKey()) { return false; }
199  const CAirlineIcaoCode &icao = model.getAirlineIcaoCode();
200  return icao.matchesNamesOrTelephonyDesignator(name);
201  });
202  }
203 
204  CAircraftModelList CAircraftModelList::findByLiveryCode(const CLivery &livery) const
205  {
206  if (!livery.hasCombinedCode()) { return {}; }
207  const QString code(livery.getCombinedCode());
208  return this->findBy([&](const CAircraftModel &model) {
209  if (!model.getLivery().hasCombinedCode()) return false;
210  return model.getLivery().getCombinedCode() == code;
211  });
212  }
213 
214  CAircraftModelList CAircraftModelList::findWithFileName() const
215  {
216  return this->findBy([](const CAircraftModel &model) { return model.hasFileName(); });
217  }
218 
219  CAircraftModelList CAircraftModelList::findByDistributor(const CDistributor &distributor) const
220  {
221  return this->findBy([&](const CAircraftModel &model) { return model.getDistributor() == distributor; });
222  }
223 
224  CAircraftModelList CAircraftModelList::findWithAircraftDesignator() const
225  {
226  return this->findBy([](const CAircraftModel &model) { return model.hasAircraftDesignator(); });
227  }
228 
229  CAircraftModelList CAircraftModelList::findWithAircraftDesignator(const QSet<QString> &designators) const
230  {
231  if (designators.isEmpty()) { return {}; }
232  return this->findBy(
233  [&](const CAircraftModel &model) { return designators.contains(model.getAircraftIcaoCodeDesignator()); });
234  }
235 
236  CAircraftModelList CAircraftModelList::findWithKnownAircraftDesignator() const
237  {
238  return this->findBy([](const CAircraftModel &model) { return model.hasKnownAircraftDesignator(); });
239  }
240 
241  CAircraftModelList CAircraftModelList::findByManufacturer(const QString &manufacturer) const
242  {
243  if (manufacturer.isEmpty()) { return {}; }
244  const QString m(manufacturer.toUpper().trimmed());
245  return this->findBy(
246  [&](const CAircraftModel &model) { return model.getAircraftIcaoCode().getManufacturer() == m; });
247  }
248 
249  CAircraftModelList CAircraftModelList::findByFamily(const QString &family) const
250  {
251  if (family.isEmpty()) { return {}; }
252  const QString f(family.toUpper().trimmed());
253  return this->findBy([&](const CAircraftModel &model) {
254  const CAircraftIcaoCode icao(model.getAircraftIcaoCode());
255  if (!icao.hasFamily()) { return false; }
256  return icao.getFamily() == f;
257  });
258  }
259 
260  CAircraftModelList CAircraftModelList::findByFamilyWithColorLivery(const QString &family) const
261  {
262  if (family.isEmpty()) { return {}; }
263  const QString f(family.toUpper().trimmed());
264  return this->findBy([&](const CAircraftModel &model) {
265  if (!model.getLivery().isColorLivery()) { return false; }
266  const CAircraftIcaoCode icao(model.getAircraftIcaoCode());
267  if (!icao.hasFamily()) { return false; }
268  return icao.getFamily() == f;
269  });
270  }
271 
272  CAircraftModelList CAircraftModelList::findByDesignatorOrFamilyWithColorLivery(const CAircraftIcaoCode &icao) const
273  {
274  return this->findBy([&](const CAircraftModel &model) {
275  if (!model.getLivery().isColorLivery()) { return false; }
276  const CAircraftIcaoCode modelIcao(model.getAircraftIcaoCode());
277  if (modelIcao.getDesignator() == icao.getDesignator()) { return true; }
278  return icao.hasFamily() && modelIcao.getFamily() == icao.getFamily();
279  });
280  }
281 
283  CAircraftModelList::findByDesignatorsOrFamilyWithColorLivery(const QStringList &designators) const
284  {
285  return this->findBy([&](const CAircraftModel &model) {
286  if (!model.getLivery().isColorLivery()) { return false; }
287  const CAircraftIcaoCode modelIcao(model.getAircraftIcaoCode());
288  if (designators.contains(modelIcao.getDesignator())) { return true; }
289  return modelIcao.hasFamily() && designators.contains(modelIcao.getFamily());
290  });
291  }
292 
293  CAircraftModelList CAircraftModelList::findByCombinedType(const QString &combinedType) const
294  {
295  const QString cc(combinedType.trimmed().toUpper());
296  if (combinedType.length() != 3) { return {}; }
297  return this->findBy([&](const CAircraftModel &model) {
298  const CAircraftIcaoCode icao(model.getAircraftIcaoCode());
299  return icao.matchesCombinedType(cc);
300  });
301  }
302 
303  CAircraftModelList CAircraftModelList::findByCombinedTypeAndWtc(const QString &combinedType,
304  const QString &wtc) const
305  {
306  const CAircraftModelList ml = this->findByCombinedType(combinedType);
307  if (ml.isEmpty()) { return ml; }
308  const CWakeTurbulenceCategory wtcUc =
310  return this->findBy([&](const CAircraftModel &model) {
311  const CAircraftIcaoCode icao(model.getAircraftIcaoCode());
312  return icao.getWtc() == wtcUc;
313  });
314  }
315 
316  CAircraftModelList CAircraftModelList::findByCombinedTypeWithColorLivery(const QString &combinedType) const
317  {
318  return this->findByCombinedType(combinedType).findColorLiveries();
319  }
320 
321  CAircraftModelList CAircraftModelList::findByCombinedTypeAndWtcWithColorLivery(const QString &combinedType,
322  const QString &wtc) const
323  {
324  return this->findByCombinedTypeAndWtc(combinedType, wtc).findColorLiveries();
325  }
326 
327  CAircraftModelList CAircraftModelList::findByCombinedAndManufacturer(const CAircraftIcaoCode &icao) const
328  {
329  return this->findByCombinedAndManufacturer(icao.getCombinedType(), icao.getManufacturer());
330  }
331 
332  CAircraftModelList CAircraftModelList::findByCombinedAndManufacturer(const QString &combinedType,
333  const QString &manufacturer) const
334  {
335  if (manufacturer.isEmpty()) { return this->findByCombinedType(combinedType); }
336  if (combinedType.isEmpty()) { return this->findByManufacturer(manufacturer); }
337  return this->findBy([&](const CAircraftModel &model) {
338  return model.getAircraftIcaoCode().matchesCombinedTypeAndManufacturer(combinedType, manufacturer);
339  });
340  }
341 
342  CAircraftModelList CAircraftModelList::findClosestColorDistance(const CRgbColor &fuselage,
343  const CRgbColor &tail) const
344  {
345  double distance = 2.0;
346  CAircraftModelList models;
347  for (const CAircraftModel &m : (*this))
348  {
349  const CLivery &l = m.getLivery();
350  if (!l.hasColorTail() || !l.hasColorFuselage()) { continue; }
351  const double d = l.getColorDistance(fuselage, tail);
352  if (qFuzzyCompare(d, distance)) { models.push_back(m); }
353  else if (distance > d)
354  {
355  models.clear();
356  models.push_back(m);
357  distance = d;
358  }
359  }
360  return models;
361  }
362 
363  CAircraftModelList CAircraftModelList::findClosestFuselageColorDistance(const CRgbColor &color) const
364  {
365  return this->findClosestColorDistance(color, color);
366  }
367 
368  CAircraftModelList CAircraftModelList::findColorLiveries() const
369  {
370  return this->findBy([=](const CAircraftModel &model) { return model.getLivery().isColorLivery(); });
371  }
372 
373  CAircraftModelList CAircraftModelList::findByMilitaryFlag(bool military) const
374  {
375  return this->findBy([=](const CAircraftModel &model) { return (model.isMilitary() == military); });
376  }
377 
378  CAircraftModelList CAircraftModelList::findByVtolFlag(bool vtol) const
379  {
380  return this->findBy([=](const CAircraftModel &model) { return (model.isVtol() == vtol); });
381  }
382 
383  CAircraftModelList CAircraftModelList::findByModelMode(CAircraftModel::ModelMode mode) const
384  {
385  return this->findBy([=](const CAircraftModel &model) { return (model.getModelMode() == mode); });
386  }
387 
388  CAircraftModelList CAircraftModelList::findByCategoryFirstLevel(int firstLevel) const
389  {
390  if (firstLevel < 0) { return {}; }
391  return this->findBy([=](const CAircraftModel &model) {
392  return (model.hasCategory() && model.getAircraftIcaoCode().getCategory().getFirstLevel() == firstLevel);
393  });
394  }
395 
396  CAircraftModelList CAircraftModelList::findByCategory(const CAircraftCategory &category) const
397  {
398  if (category.isNull()) { return {}; }
399  return this->findBy([=](const CAircraftModel &model) {
400  return (model.hasCategory() && model.getAircraftIcaoCode().getCategory() == category);
401  });
402  }
403 
404  CAircraftModelList CAircraftModelList::findByCategories(const CAircraftCategoryList &categories) const
405  {
406  if (categories.isEmpty()) { return {}; }
407  return this->findBy([=](const CAircraftModel &model) {
408  return (model.hasCategory() && categories.contains(model.getAircraftIcaoCode().getCategory()));
409  });
410  }
411 
412  CAircraftModelList CAircraftModelList::findFsFamilyModels() const
413  {
414  return this->findBy(
415  [](const CAircraftModel &model) { return model.getSimulator().isMicrosoftOrPrepare3DSimulator(); });
416  }
417 
418  CAircraftModelList CAircraftModelList::findNonFsFamilyModels() const
419  {
420  return this->findBy(
421  [](const CAircraftModel &model) { return !model.getSimulator().isMicrosoftOrPrepare3DSimulator(); });
422  }
423 
424  CAircraftModelList CAircraftModelList::findWithValidDbKey() const
425  {
426  return this->findBy([](const CAircraftModel &model) { return model.hasValidDbKey(); });
427  }
428 
429  CAircraftModelList CAircraftModelList::findWithoutValidDbKey() const
430  {
431  return this->findBy([](const CAircraftModel &model) { return !model.hasValidDbKey(); });
432  }
433 
434  CAircraftModelList CAircraftModelList::findWithoutValidDbKey(int maxElements) const
435  {
436  CAircraftModelList ml = this->findWithoutValidDbKey();
437  ml.truncate(maxElements);
438  return ml;
439  }
440 
441  CAircraftModelList CAircraftModelList::findNonDbModelsForAirline(const QString &airline) const
442  {
443  const CAircraftModelList noDb = this->findWithoutValidDbKey();
444  return noDb.findBy(
445  [&](const CAircraftModel &model) { return model.getAirlineIcaoCode().matchesDesignator(airline); });
446  }
447 
448  CAircraftModelList CAircraftModelList::findNonDbModelsForAircraft(const QString &airline) const
449  {
450  const CAircraftModelList noDb = this->findWithoutValidDbKey();
451  return noDb.findBy(
452  [&](const CAircraftModel &model) { return model.getAircraftIcaoCode().matchesDesignator(airline); });
453  }
454 
455  CAircraftModelList CAircraftModelList::findNonDbModelsForModelString(const QString &modelString) const
456  {
457  const CAircraftModelList noDb = this->findWithoutValidDbKey();
458  return noDb.findBy(
459  [&](const CAircraftModel &model) { return model.matchesModelString(modelString, Qt::CaseInsensitive); });
460  }
461 
462  CAircraftModelList CAircraftModelList::findAllIncludedModels() const
463  {
464  return this->findBy(
465  [](const CAircraftModel &model) { return model.getModelMode() == CAircraftModel::Include; });
466  }
467 
468  CAircraftModelList CAircraftModelList::findAllIncludedModels(int maxElements) const
469  {
470  CAircraftModelList ml = this->findAllIncludedModels();
471  ml.truncate(maxElements);
472  return ml;
473  }
474 
475  CAircraftModelList CAircraftModelList::findAllExcludedModels() const
476  {
477  return this->findBy(
478  [](const CAircraftModel &model) { return model.getModelMode() == CAircraftModel::Exclude; });
479  }
480 
481  CAircraftModelList CAircraftModelList::findAllExcludedModels(int maxElements) const
482  {
483  CAircraftModelList ml = this->findAllExcludedModels();
484  ml.truncate(maxElements);
485  return ml;
486  }
487 
488  CAircraftModelList CAircraftModelList::findDuplicateModelStrings() const
489  {
490  const QMap<QString, int> modelStrings = this->countPerModelString();
491  CAircraftModelList duplicates;
492  for (const auto [string, count] : makePairsRange(modelStrings))
493  {
494  if (count > 1) { duplicates.push_back(this->findByModelString(string, Qt::CaseInsensitive)); }
495  }
496  return duplicates;
497  }
498 
499  QMap<QString, int> CAircraftModelList::countPerModelString() const
500  {
501  QMap<QString, int> modelStrings;
502  for (const CAircraftModel &model : *this)
503  {
504  if (modelStrings.contains(model.getModelString())) { modelStrings[model.getModelModeAsString()]++; }
505  else { modelStrings[model.getModelModeAsString()] = 1; }
506  }
507  return modelStrings;
508  }
509 
510  QMap<CDistributor, int> CAircraftModelList::countPerDistributor() const
511  {
512  QMap<CDistributor, int> distributors;
513  for (const CAircraftModel &model : *this)
514  {
515  if (!model.hasDistributor()) { continue; }
516  if (distributors.contains(model.getDistributor())) { distributors[model.getDistributor()]++; }
517  else { distributors[model.getDistributor()] = 1; }
518  }
519  return distributors;
520  }
521 
522  QMap<CAircraftIcaoCode, int> CAircraftModelList::countPerAircraftIcao() const
523  {
525  for (const CAircraftModel &model : *this)
526  {
527  if (!model.hasAircraftDesignator()) { continue; }
528  if (icaos.contains(model.getAircraftIcaoCode())) { icaos[model.getAircraftIcaoCode()]++; }
529  else { icaos[model.getAircraftIcaoCode()] = 1; }
530  }
531  return icaos;
532  }
533 
534  QMap<CAirlineIcaoCode, int> CAircraftModelList::countPerAirlineIcao() const
535  {
537  for (const CAircraftModel &model : *this)
538  {
539  if (!model.hasAirlineDesignator()) { continue; }
540  if (icaos.contains(model.getAirlineIcaoCode())) { icaos[model.getAirlineIcaoCode()]++; }
541  else { icaos[model.getAirlineIcaoCode()] = 1; }
542  }
543  return icaos;
544  }
545 
546  CAirlineIcaoCode CAircraftModelList::getAirlineWithMaxCount() const
547  {
548  const QMap<CAirlineIcaoCode, int> ac = this->countPerAirlineIcao();
549  if (ac.isEmpty()) { return {}; }
550  if (ac.size() == 1) { return ac.firstKey(); }
551  const QList<int> values = ac.values();
552  const int max = *std::max_element(values.begin(), values.end());
553  return ac.key(max);
554  }
555 
556  CAircraftModelList CAircraftModelList::findModelsWithoutExistingFile() const
557  {
558  return this->findBy([](const CAircraftModel &model) { return !model.hasExistingCorrespondingFile(); });
559  }
560 
561  CAircraftModelList CAircraftModelList::findModelsWithExistingFile() const
562  {
563  return this->findBy([](const CAircraftModel &model) { return model.hasExistingCorrespondingFile(); });
564  }
565 
566  QString CAircraftModelList::designatorToFamily(const CAircraftIcaoCode &aircraftIcaoCode) const
567  {
568  if (aircraftIcaoCode.hasFamily()) { return aircraftIcaoCode.getFamily(); }
569  for (const CAircraftModel &model : (*this))
570  {
571  const CAircraftIcaoCode icao(model.getAircraftIcaoCode());
572  if (!icao.hasFamily()) continue;
573  if (icao.matchesDesignator(aircraftIcaoCode.getDesignator())) { return icao.getFamily(); }
574  }
575  return {};
576  }
577 
578  CAircraftModelList CAircraftModelList::matchesSimulator(const CSimulatorInfo &simulator) const
579  {
580  return this->findBy([&](const CAircraftModel &model) { return model.matchesSimulator(simulator); });
581  }
582 
583  bool CAircraftModelList::containsMatchingSimulator(const CSimulatorInfo &simulators) const
584  {
585  return this->containsBy([&](const CAircraftModel &model) { return model.matchesSimulator(simulators); });
586  }
587 
588  bool CAircraftModelList::containsNotMatchingSimulator(const CSimulatorInfo &simulators) const
589  {
590  return this->containsBy([&](const CAircraftModel &model) { return !model.matchesSimulator(simulators); });
591  }
592 
593  bool CAircraftModelList::containsMilitary() const
594  {
595  return this->containsBy([&](const CAircraftModel &model) { return model.isMilitary(); });
596  }
597 
598  bool CAircraftModelList::containsCivilian() const
599  {
600  return this->containsBy([&](const CAircraftModel &model) { return model.isCivilian(); });
601  }
602 
603  bool CAircraftModelList::containsCivilianAndMilitary() const
604  {
605  return this->containsMilitary() && this->containsCivilian();
606  }
607 
608  bool CAircraftModelList::containsVtol() const
609  {
610  return this->containsBy([&](const CAircraftModel &model) { return model.isVtol(); });
611  }
612 
613  bool CAircraftModelList::containsCategory() const
614  {
615  return this->containsBy([&](const CAircraftModel &model) { return model.hasCategory(); });
616  }
617 
618  bool CAircraftModelList::containsCategory(int firstLevel) const
619  {
620  if (firstLevel < 0) { return false; }
621  return this->containsBy([&](const CAircraftModel &model) {
622  return model.hasCategory() && model.getAircraftIcaoCode().getCategory().getFirstLevel() == firstLevel;
623  });
624  }
625 
626  CAircraftModelList CAircraftModelList::findByDistributors(const CDistributorList &distributors) const
627  {
628  if (distributors.isEmpty()) { return {}; }
629  return this->findBy([&](const CAircraftModel &model) { return model.matchesAnyDbDistributor(distributors); });
630  }
631 
632  int CAircraftModelList::setSimulatorInfo(const CSimulatorInfo &info)
633  {
634  int c = 0;
635  const CSimulatorInfo::Simulator s = info.getSimulator();
636  for (CAircraftModel &model : (*this))
637  {
638  if (model.getSimulator().getSimulator() == s) { continue; }
639  model.setSimulator(info);
640  c++;
641  }
642  return c;
643  }
644 
645  CSimulatorInfo CAircraftModelList::simulatorsSupported() const
646  {
647  CSimulatorInfo::Simulator s = CSimulatorInfo::None;
648  for (const CAircraftModel &model : (*this))
649  {
650  s |= model.getSimulator().getSimulator();
651  if (s == CSimulatorInfo::All) { break; }
652  }
653  return { s };
654  }
655 
656  namespace private_ns
657  {
658  bool isLikelyImpl(double count, double total)
659  {
660  if (total < 1) { return false; }
661  const double fsRatio = count / total;
662  return fsRatio > 0.95;
663  }
664  } // namespace private_ns
665 
666  bool CAircraftModelList::isLikelyFsFamilyModelList() const
667  {
668  if (this->isEmpty()) { return false; } // avoid DIV 0
669  return private_ns::isLikelyImpl(this->countPerSimulator().getCountForFsFamilySimulators(), this->size());
670  }
671 
672  bool CAircraftModelList::isLikelyFsxFamilyModelList() const
673  {
674  if (this->isEmpty()) { return false; } // avoid DIV 0
675  return private_ns::isLikelyImpl(this->countPerSimulator().getCountForFsxFamilySimulators(), this->size());
676  }
677 
678  bool CAircraftModelList::isLikelyXPlaneModelList() const
679  {
680  if (this->isEmpty()) { return false; } // avoid DIV 0
681  return private_ns::isLikelyImpl(this->countPerSimulator().getCount(CSimulatorInfo::xplane()), this->size());
682  }
683 
684  int CAircraftModelList::setModelMode(CAircraftModel::ModelMode mode)
685  {
686  int c = 0;
687  for (CAircraftModel &model : (*this))
688  {
689  if (model.getModelMode() == mode) { continue; }
690  model.setModelMode(mode);
691  c++;
692  }
693  return c;
694  }
695 
696  int CAircraftModelList::setModelType(CAircraftModel::ModelType type)
697  {
698  int c = 0;
699  for (CAircraftModel &model : (*this))
700  {
701  if (model.getModelType() == type) { continue; }
702  model.setModelType(type);
703  c++;
704  }
705  return c;
706  }
707 
708  int CAircraftModelList::setCG(const CLength &cg)
709  {
710  int c = 0;
711  for (CAircraftModel &model : (*this))
712  {
713  if (model.getCG() == cg) { continue; }
714  model.setCG(cg);
715  c++;
716  }
717  return c;
718  }
719 
720  int CAircraftModelList::keepModelsWithString(const QStringList &modelStrings, Qt::CaseSensitivity sensitivity)
721  {
722  const int cs = this->size();
723  (*this) = (findByModelStrings(modelStrings, sensitivity));
724  const int d = cs - this->size();
725  return d;
726  }
727 
728  bool CAircraftModelList::removeModelWithString(const QString &modelString, Qt::CaseSensitivity sensitivity)
729  {
730  if (modelString.isEmpty()) { return false; }
731  if (this->isEmpty()) { return false; }
732  const int r = this->removeIf(
733  [&](const CAircraftModel &model) { return model.matchesModelString(modelString, sensitivity); });
734  return r > 0;
735  }
736 
737  int CAircraftModelList::removeModelsWithString(const CAircraftModelList &models, Qt::CaseSensitivity sensitivity)
738  {
739  if (models.isEmpty()) { return 0; }
740  return this->removeModelsWithString(models.getModelStringList(false), sensitivity);
741  }
742 
743  int CAircraftModelList::removeModelsWithString(const QStringList &modelStrings, Qt::CaseSensitivity sensitivity)
744  {
745  if (modelStrings.isEmpty()) { return 0; }
746  const int cs = this->size();
747  (*this) = (this->findByNotInModelStrings(modelStrings, sensitivity));
748  const int d = cs - this->size();
749  return d;
750  }
751 
752  int CAircraftModelList::removeIfNotMatchingSimulator(const CSimulatorInfo &needToMatch)
753  {
754  if (this->isEmpty()) { return 0; }
755  const int oldSize = this->size();
756  CAircraftModelList models;
757  for (const CAircraftModel &model : *this)
758  {
759  if (model.matchesSimulator(needToMatch)) { models.push_back(model); }
760  }
761  const int diff = models.size() - oldSize;
762  if (diff > 0) { *this = models; }
763  return diff;
764  }
765 
766  int CAircraftModelList::removeAllWithoutModelString()
767  {
768  if (this->isEmpty()) { return 0; }
769  const int s = this->size();
770  CAircraftModelList withModelStr;
771  for (const CAircraftModel &model : *this)
772  {
773  if (!model.hasModelString()) { continue; }
774  withModelStr.push_back(model);
775  }
776  const int diff = s - withModelStr.size();
777  if (diff < 1) { return 0; }
778  *this = withModelStr;
779  return diff;
780  }
781 
782  int CAircraftModelList::removeIfExcluded()
783  {
784  if (this->isEmpty()) { return 0; }
785  const int s = this->size();
786  CAircraftModelList onlyIncluded;
787  for (const CAircraftModel &model : *this)
788  {
789  if (model.getModelMode() == CAircraftModel::Exclude) { continue; }
790  onlyIncluded.push_back(model);
791  }
792  const int diff = s - onlyIncluded.size();
793  if (diff < 1) { return 0; }
794  *this = onlyIncluded;
795  return diff;
796  }
797 
798  int CAircraftModelList::removeXPlaneFlyablePlanes() { return this->removeIfExcluded(); }
799 
800  int CAircraftModelList::removeByDistributor(const CDistributor &distributor)
801  {
802  return this->removeIf(&CAircraftModel::getDistributor, distributor);
803  }
804 
805  int CAircraftModelList::removeByAircraftAndLivery(const CAircraftIcaoCode &aircraftIcao, const CLivery &livery)
806  {
807  return this->removeIf(&CAircraftModel::getAircraftIcaoCode, aircraftIcao, &CAircraftModel::getLivery, livery);
808  }
809 
810  int CAircraftModelList::removeByAircraftAndAirline(const CAircraftIcaoCode &aircraftIcao,
811  const CAirlineIcaoCode &airline)
812  {
813  return this->removeIf(&CAircraftModel::getAircraftIcaoCode, aircraftIcao, &CAircraftModel::getAirlineIcaoCode,
814  airline);
815  }
816 
817  int CAircraftModelList::removeIfNotFsFamily()
818  {
819  if (this->isEmpty()) { return 0; }
820  CAircraftModelList fsOnly = this->findFsFamilyModels();
821  if (fsOnly.size() == this->size()) { return 0; }
822  const int delta = this->size() - fsOnly.size();
823  *this = fsOnly;
824  return delta;
825  }
826 
827  CAircraftModelList CAircraftModelList::removeIfFileButNotInSet(const QString &fileName,
828  const QSet<QString> &modelStrings)
829  {
830  CAircraftModelList removed;
831  for (const CAircraftModel &model : *this)
832  {
833  if (!model.matchesFileName(fileName)) { continue; }
834  if (modelStrings.contains(model.getModelString())) { continue; }
835  removed.push_back(model);
836  }
837 
838  this->removeIfIn(removed);
839  return removed;
840  }
841 
842  bool CAircraftModelList::replaceOrAddModelWithString(const CAircraftModel &addOrReplaceModel,
843  Qt::CaseSensitivity sensitivity)
844  {
845  bool r = false;
846  if (!this->isEmpty()) { r = this->removeModelWithString(addOrReplaceModel.getModelString(), sensitivity); }
847  this->push_back(addOrReplaceModel);
848  return r;
849  }
850 
851  int CAircraftModelList::replaceOrAddModelsWithString(const CAircraftModelList &addOrReplaceList,
852  Qt::CaseSensitivity sensitivity)
853  {
854  if (addOrReplaceList.isEmpty()) { return 0; }
855  if (this->isEmpty())
856  {
857  *this = addOrReplaceList;
858  return addOrReplaceList.size();
859  }
860  CAircraftModelList newModels(*this);
861  const QStringList keys(addOrReplaceList.getModelStringList(false));
862  newModels.removeModelsWithString(keys, sensitivity);
863  int removed = newModels.size(); // size after removing
864  newModels.push_back(addOrReplaceList);
865  *this = newModels;
866  return this->size() - removed;
867  }
868 
869  CAircraftModelList CAircraftModelList::findModelsStartingWith(const QString &modelString,
870  Qt::CaseSensitivity sensitivity) const
871  {
872  return this->findBy(
873  [&](const CAircraftModel &model) { return model.getModelString().startsWith(modelString, sensitivity); });
874  }
875 
876  CAircraftModelList CAircraftModelList::findByModelStrings(const QStringList &modelStrings,
877  Qt::CaseSensitivity sensitivity) const
878  {
879  return this->findBy(
880  [&](const CAircraftModel &model) { return modelStrings.contains(model.getModelString(), sensitivity); });
881  }
882 
883  CAircraftModelList CAircraftModelList::findByNotInModelStrings(const QStringList &modelStrings,
884  Qt::CaseSensitivity sensitivity) const
885  {
886  return this->findBy([&](const CAircraftModel &model) {
887  const bool c = modelStrings.contains(model.getModelString(), sensitivity);
888  return !c;
889  });
890  }
891 
892  QStringList CAircraftModelList::getModelStringList(bool sort) const
893  {
894  QStringList ms;
895  for (const CAircraftModel &model : *this)
896  {
897  if (!model.hasModelString()) { continue; }
898  ms.append(model.getModelString());
899  }
900  if (sort) { ms.sort(Qt::CaseInsensitive); }
901  return ms;
902  }
903 
904  QSet<QString> CAircraftModelList::getModelStringSet() const
905  {
907  for (const CAircraftModel &model : *this)
908  {
909  if (!model.hasModelString()) { continue; }
910  ms.insert(model.getModelString());
911  }
912  return ms;
913  }
914 
915  CCountPerSimulator CAircraftModelList::countPerSimulator() const
916  {
917  CCountPerSimulator count;
918  for (const CAircraftModel &model : *this) { count.increaseSimulatorCounts(model.getSimulator()); }
919  return count;
920  }
921 
922  CSimulatorInfo CAircraftModelList::simulatorsWithMaxEntries() const
923  {
924  if (this->isEmpty()) { return {}; } // not known
925  const CCountPerSimulator counts(this->countPerSimulator());
926  const int simulatorsRepresented = counts.simulatorsRepresented();
927  if (simulatorsRepresented < 1) { return {}; }
929  CSimulatorInfo maxSim = cps.last();
930  const int count = cps.lastKey(); // how many elements
931  const QList<CSimulatorInfo> infoWithMaxValues = cps.values(count); // all with the same counts
932  for (const CSimulatorInfo &info : infoWithMaxValues) { maxSim.addSimulator(info); }
933  return maxSim;
934  }
935 
936  int CAircraftModelList::countModelsWithColorLivery() const
937  {
938  int count = 0;
939  for (const CAircraftModel &model : *this)
940  {
941  if (model.getLivery().isColorLivery()) { count++; }
942  }
943  return count;
944  }
945 
946  int CAircraftModelList::countModelsWithAirlineLivery() const
947  {
948  int count = 0;
949  for (const CAircraftModel &model : *this)
950  {
951  if (model.getLivery().isAirlineLivery()) { count++; }
952  }
953  return count;
954  }
955 
956  int CAircraftModelList::countVtolAircraft() const
957  {
958  int count = 0;
959  for (const CAircraftModel &model : *this)
960  {
961  if (model.isVtol()) { count++; }
962  }
963  return count;
964  }
965 
966  int CAircraftModelList::countByMode(CAircraftModel::ModelMode mode) const
967  {
968  int count = 0;
969  for (const CAircraftModel &model : *this)
970  {
971  if (model.matchesMode(mode)) { count++; }
972  }
973  return count;
974  }
975 
976  int CAircraftModelList::countMilitaryAircraft() const
977  {
978  int count = 0;
979  for (const CAircraftModel &model : *this)
980  {
981  if (model.isMilitary()) { count++; }
982  }
983  return count;
984  }
985 
986  int CAircraftModelList::countCivilianAircraft() const
987  {
988  int count = 0;
989  for (const CAircraftModel &model : (*this))
990  {
991  if (model.isCivilian()) { count++; }
992  }
993  return count;
994  }
995 
996  int CAircraftModelList::countDifferentAirlines() const { return this->getAirlineVDesignators().size(); }
997 
998  int CAircraftModelList::countCombinedTypes() const { return this->getCombinedTypes().size(); }
999 
1000  int CAircraftModelList::countAliases() const
1001  {
1002  int count = 0;
1003  for (const CAircraftModel &model : (*this))
1004  {
1005  if (model.hasModelStringAlias()) { count++; }
1006  }
1007  return count;
1008  }
1009 
1010  void CAircraftModelList::sortByFileName()
1011  {
1012  if (CFileUtils::isFileNameCaseSensitive()) { this->sortBy(&CAircraftModel::getFileName); }
1013  else { this->sortBy(&CAircraftModel::getFileNameLowerCase); }
1014  }
1015 
1016  void CAircraftModelList::updateDistributor(const CDistributor &distributor)
1017  {
1018  for (CAircraftModel &model : *this) { model.setDistributor(distributor); }
1019  }
1020 
1021  CDistributorList CAircraftModelList::getDistributors(bool onlyDbDistributors) const
1022  {
1023  if (this->isEmpty()) { return {}; }
1024  CDistributorList distributors;
1025  for (const CAircraftModel &model : *this)
1026  {
1027  const CDistributor d(model.getDistributor());
1028  if (onlyDbDistributors && !d.hasValidDbKey()) { continue; }
1029  if (distributors.contains(d)) { continue; }
1030  distributors.push_back(d);
1031  }
1032  return distributors;
1033  }
1034 
1035  CAircraftIcaoCodeList CAircraftModelList::getAircraftIcaoCodesFromDb() const
1036  {
1037  if (this->isEmpty()) { return {}; }
1038  QSet<int> keys;
1039  CAircraftIcaoCodeList icaos;
1040  for (const CAircraftModel &model : *this)
1041  {
1042  const CAircraftIcaoCode icao = model.getAircraftIcaoCode();
1043  if (!icao.hasValidDbKey()) { continue; }
1044 
1045  const int key = icao.getDbKey();
1046  if (keys.contains(key)) { continue; }
1047  icaos.push_back(icao);
1048  keys.insert(key);
1049  }
1050  return icaos;
1051  }
1052 
1053  QSet<QString> CAircraftModelList::getAircraftDesignators() const
1054  {
1055  CSetBuilder<QString> designators;
1056  for (const CAircraftModel &model : *this)
1057  {
1058  if (!model.hasAircraftDesignator()) { continue; }
1059  designators.insert(model.getAircraftIcaoCodeDesignator());
1060  }
1061  return designators;
1062  }
1063 
1064  QSet<QString> CAircraftModelList::getAircraftDesignatorsForAirline(const CAirlineIcaoCode &airlineCode) const
1065  {
1066  CSetBuilder<QString> designators;
1067  if (!airlineCode.hasValidDesignator()) { return {}; }
1068  for (const CAircraftModel &model : *this)
1069  {
1070  if (model.getAirlineIcaoCode() != airlineCode) { continue; }
1071  designators.insert(model.getAircraftIcaoCodeDesignator());
1072  }
1073  return designators;
1074  }
1075 
1076  CAircraftIcaoCodeList CAircraftModelList::getAicraftIcaoCodesForAirline(const CAirlineIcaoCode &airlineCode) const
1077  {
1078  CAircraftIcaoCodeList icaos;
1079  if (!airlineCode.hasValidDesignator()) { return icaos; }
1080  for (const CAircraftModel &model : *this)
1081  {
1082  if (model.getAirlineIcaoCode() != airlineCode) { continue; }
1083  icaos.push_back(model.getAircraftIcaoCode());
1084  }
1085  return icaos;
1086  }
1087 
1088  CAirlineIcaoCodeList CAircraftModelList::getAirlineIcaoCodesFromDb() const
1089  {
1090  if (this->isEmpty()) { return {}; }
1091  QSet<int> keys;
1092  CAirlineIcaoCodeList icaos;
1093  for (const CAircraftModel &model : *this)
1094  {
1095  const CAirlineIcaoCode icao = model.getAirlineIcaoCode();
1096  if (!icao.hasValidDbKey()) { continue; }
1097 
1098  const int key = icao.getDbKey();
1099  if (keys.contains(key)) { continue; }
1100  icaos.push_back(icao);
1101  keys.insert(key);
1102  }
1103  return icaos;
1104  }
1105 
1106  QSet<QString> CAircraftModelList::getAirlineDesignators() const
1107  {
1108  CSetBuilder<QString> designators;
1109  for (const CAircraftModel &model : *this)
1110  {
1111  if (!model.hasAirlineDesignator()) { continue; }
1112  designators.insert(model.getAirlineIcaoCodeDesignator());
1113  }
1114  return designators;
1115  }
1116 
1117  QSet<QString> CAircraftModelList::getAirlineVDesignators() const
1118  {
1119  CSetBuilder<QString> designators;
1120  for (const CAircraftModel &model : *this)
1121  {
1122  if (!model.hasAirlineDesignator()) { continue; }
1123  designators.insert(model.getAirlineIcaoCodeVDesignator());
1124  }
1125  return designators;
1126  }
1127 
1128  CAirlineIcaoCodeList CAircraftModelList::getAirlineIcaoCodesForGroup(int groupId) const
1129  {
1130  if (groupId < 0) { return {}; }
1131  CAirlineIcaoCodeList icaos;
1132  for (const CAircraftModel &model : *this)
1133  {
1134  if (model.getAirlineIcaoCode().getGroupId() == groupId) { icaos.push_back(model.getAirlineIcaoCode()); }
1135  }
1136  return icaos;
1137  }
1138 
1139  QSet<QString> CAircraftModelList::getAirlineDesignatorsForGroup(int groupId) const
1140  {
1141  return this->getAirlineIcaoCodesForGroup(groupId).allDesignators();
1142  }
1143 
1144  QSet<QString> CAircraftModelList::getAirlineVDesignatorsForGroup(int groupId) const
1145  {
1146  return this->getAirlineIcaoCodesForGroup(groupId).allVDesignators();
1147  }
1148 
1149  QSet<QString> CAircraftModelList::getCombinedTypes() const
1150  {
1151  CSetBuilder<QString> combinedCodes;
1152  for (const CAircraftModel &model : *this)
1153  {
1154  const QString ct = model.getAircraftIcaoCode().getCombinedType();
1155  if (ct.isEmpty()) { continue; }
1156  combinedCodes.insert(ct);
1157  }
1158  return combinedCodes;
1159  }
1160 
1161  QSet<QString> CAircraftModelList::getAllFileNames() const
1162  {
1163  const bool cs = CFileUtils::isFileNameCaseSensitive();
1164  CSetBuilder<QString> files;
1165  for (const CAircraftModel &model : *this)
1166  {
1167  if (!model.hasFileName()) { continue; }
1168  files.insert(cs ? model.getFileName() : model.getFileNameLowerCase());
1169  }
1170  return files;
1171  }
1172 
1173  QSet<QString> CAircraftModelList::getAllUNCFileNames() const
1174  {
1175  const bool cs = CFileUtils::isFileNameCaseSensitive();
1176  CSetBuilder<QString> files;
1177  for (const CAircraftModel &model : *this)
1178  {
1179  if (!model.hasFileName()) { continue; }
1180  const QString fn = (cs ? model.getFileName() : model.getFileNameLowerCase());
1181  if (CFileUtils::isWindowsUncPath(fn)) { files.insert(fn); }
1182  }
1183  return files;
1184  }
1185 
1186  QString CAircraftModelList::getCombinedTypesAsString(const QString &separator) const
1187  {
1188  if (this->isEmpty()) { return {}; }
1189  return joinStringSet(this->getCombinedTypes(), separator);
1190  }
1191 
1192  QSet<QString> CAircraftModelList::getAicraftAndAirlineDesignators(bool withDbId) const
1193  {
1195  for (const CAircraftModel &model : *this)
1196  {
1197  const QString s =
1198  (model.hasAircraftDesignator() ? (withDbId ? model.getAircraftIcaoCode().getDesignatorDbKey() :
1200  "no aircraft") %
1201  u"/" %
1202  (model.hasAircraftDesignator() ? (withDbId ? model.getAirlineIcaoCode().getDesignatorDbKey() :
1204  "no airline");
1205  str.insert(s);
1206  }
1207  return str;
1208  }
1209 
1210  QString CAircraftModelList::getAicraftAndAirlineDesignatorsAsString(bool withDbId, const QString &separator) const
1211  {
1212  if (this->isEmpty()) { return {}; }
1213  return joinStringSet(this->getAicraftAndAirlineDesignators(withDbId), separator);
1214  }
1215 
1216  void CAircraftModelList::updateAircraftIcao(const CAircraftIcaoCode &icao)
1217  {
1218  for (CAircraftModel &model : *this) { model.setAircraftIcaoCode(icao); }
1219  }
1220 
1221  void CAircraftModelList::updateLivery(const CLivery &livery)
1222  {
1223  for (CAircraftModel &model : *this) { model.setLivery(livery); }
1224  }
1225 
1226  int CAircraftModelList::updateDistributorOrder(const CDistributorList &distributors)
1227  {
1228  if (distributors.isEmpty()) { return 0; }
1229  int found = 0;
1230  for (CAircraftModel &model : *this)
1231  {
1232  if (model.setDistributorOrder(distributors)) { found++; }
1233  }
1234  return found;
1235  }
1236 
1237  void CAircraftModelList::normalizeFileNamesForDb()
1238  {
1239  for (CAircraftModel &model : *this) { model.normalizeFileNameForDb(); }
1240  }
1241 
1242  ScoredModels CAircraftModelList::scoreFull(const CAircraftModel &remoteModel, bool preferColorLiveries,
1243  bool ignoreZeroScores, CStatusMessageList *log) const
1244  {
1245  ScoredModels scoreMap;
1246 
1247  // normally prefer colors if there is no airline
1248  CCallsign::addLogDetailsToList(
1249  log, remoteModel.getCallsign(),
1250  QStringLiteral("Prefer color liveries: '%1', airline: '%2', ignore zero scores: '%3'")
1251  .arg(boolToYesNo(preferColorLiveries), remoteModel.getAirlineIcaoCodeDesignator(),
1252  boolToYesNo(ignoreZeroScores)));
1253  CCallsign::addLogDetailsToList(log, remoteModel.getCallsign(),
1254  QStringLiteral("--- Start scoring in list with %1 models").arg(this->size()));
1255  CCallsign::addLogDetailsToList(log, remoteModel.getCallsign(), this->coverageSummaryForModel(remoteModel));
1256 
1257  int c = 1;
1258  for (const CAircraftModel &model : *this)
1259  {
1260  CStatusMessageList subMsgs;
1261  const int score = model.calculateScore(remoteModel, preferColorLiveries, log ? &subMsgs : nullptr);
1262  if (ignoreZeroScores && score < 1) { continue; }
1263 
1264  CCallsign::addLogDetailsToList(
1265  log, remoteModel.getCallsign(),
1266  QStringLiteral("--- Calculating #%1 '%2'---").arg(c).arg(model.getModelStringAndDbKey()));
1267  if (log) { log->push_back(subMsgs); }
1268  CCallsign::addLogDetailsToList(log, remoteModel.getCallsign(),
1269  QStringLiteral("--- End calculating #%1 ---").arg(c));
1270  c++;
1271  scoreMap.insert(score, model);
1272  }
1273  CCallsign::addLogDetailsToList(log, remoteModel.getCallsign(), QStringLiteral("--- End scoring ---"));
1274  return scoreMap;
1275  }
1276 
1277  QStringList CAircraftModelList::toCompleterStrings(bool sorted, const CSimulatorInfo &simulator) const
1278  {
1279  QStringList c;
1280  for (const CAircraftModel &model : *this)
1281  {
1282  if (model.getSimulator().matchesAny(simulator)) { c.append(model.getModelString()); }
1283  }
1284  if (sorted) { c.sort(); }
1285  return c;
1286  }
1287 
1288  CStatusMessageList CAircraftModelList::validateForPublishing() const
1289  {
1290  CAircraftModelList invalidModels;
1291  CAircraftModelList validModels;
1292  return this->validateForPublishing(validModels, invalidModels);
1293  }
1294 
1295  CStatusMessageList CAircraftModelList::validateForPublishing(CAircraftModelList &validModels,
1296  CAircraftModelList &invalidModels) const
1297  {
1298  if (this->isEmpty()) { return {}; }
1299  CStatusMessageList msgs;
1300  for (const CAircraftModel &model : *this)
1301  {
1302  const CStatusMessageList msgsModel(model.validate(false));
1303  const CStatusMessage msgModel(msgsModel.toSingleMessage());
1304 
1305  QStringList subMsgs;
1306  if (!model.getDistributor().hasValidDbKey()) { subMsgs << "No distributor from DB"; }
1307  if (!model.getAircraftIcaoCode().hasValidDbKey()) { subMsgs << "No aircraft ICAO from DB"; }
1308  if (!model.getLivery().hasValidDbKey()) { subMsgs << "No livery from DB"; }
1309  if (model.getLivery().isAirlineLivery())
1310  {
1311  // for color codes we do not need to check
1312  if (!model.getLivery().getAirlineIcaoCode().hasValidDbKey()) { subMsgs << "No airline ICAO from DB"; }
1313  }
1314 
1315  const CStatusMessage msgDb(CStatusMessage::SeverityError, subMsgs.join(", "));
1316  CStatusMessage singleMsg(CStatusMessageList({ msgModel, msgDb }).toSingleMessage());
1317  if (!singleMsg.isWarningOrAbove())
1318  {
1319  CAircraftModelList::addAsValidOrInvalidModel(model, true, validModels, invalidModels);
1320  continue;
1321  }
1322  if (model.hasModelString()) { singleMsg.prependMessage(model.getModelString() % u": "); }
1323  msgs.push_back(singleMsg);
1324  CAircraftModelList::addAsValidOrInvalidModel(model, false, validModels, invalidModels);
1325  }
1326  return msgs;
1327  }
1328 
1329  CStatusMessageList CAircraftModelList::validateDistributors(const CDistributorList &distributors,
1330  CAircraftModelList &validModels,
1331  CAircraftModelList &invalidModels) const
1332  {
1333  CStatusMessageList msgs;
1334  CDistributorList distributorsFromDb(distributors);
1335  distributorsFromDb.removeIfNotLoadedFromDb();
1336 
1337  // Any DB distributors?
1338  if (distributorsFromDb.isEmpty())
1339  {
1340  const CStatusMessage msg = CStatusMessage(this).validationError(u"No DB distributors for validation");
1341  msgs.push_back(msg);
1342  CAircraftModelList::addAsValidOrInvalidModels(*this, false, validModels, invalidModels);
1343  return msgs;
1344  }
1345 
1346  for (const CAircraftModel &model : *this)
1347  {
1348  const bool valid = (model.hasDbDistributor() || model.matchesAnyDbDistributor(distributorsFromDb));
1349  CAircraftModelList::addAsValidOrInvalidModel(model, valid, validModels, invalidModels);
1350  if (!valid)
1351  {
1352  const CStatusMessage msg =
1353  CStatusMessage(this).validationError(u"No valid distributor for '%1', was '%2'")
1354  << model.getModelString() << model.getDistributor().getDbKey();
1355  msgs.push_back(msg);
1356  }
1357  }
1358  return msgs;
1359  }
1360 
1361  CStatusMessageList CAircraftModelList::validateFiles(CAircraftModelList &validModels,
1362  CAircraftModelList &invalidModels, bool ignoreEmptyFileNames,
1363  int stopAtFailedFiles, std::atomic_bool &wasStopped,
1364  const QString &simRootDirectory, bool alreadySortedByFn) const
1365  {
1366  wasStopped = false;
1367 
1368  CStatusMessageList msgs;
1369  QSet<QString> failedFiles;
1370  QSet<QString> workingFiles;
1371  int failedFilesCount = 0;
1372 
1373  // sorting allows to skip multiple files as once when a file fails
1374  CAircraftModelList sorted(*this);
1375  if (!alreadySortedByFn) { sorted.sortByFileName(); }
1376 
1377  // avoid hanging if UNC paths are not available
1378  if (CBuildConfig::isRunningOnWindowsNtPlatform())
1379  {
1380  const CStatusMessageList uncMsgs = this->validateUncFiles(sorted.getAllUNCFileNames());
1381  if (uncMsgs.hasErrorMessages()) { return uncMsgs; }
1382  }
1383 
1384  const bool caseSensitive = CFileUtils::isFileNameCaseSensitive();
1385  const QString simRootDir = CFileUtils::normalizeFilePathToQtStandard(
1386  CFileUtils::stripLeadingSlashOrDriveLetter(caseSensitive ? simRootDirectory : simRootDirectory.toLower()));
1387 
1388  for (const CAircraftModel &model : std::as_const(sorted))
1389  {
1390  if (wasStopped) { break; } // allow breaking from external
1391  bool ok = false;
1392  do {
1393  if (!model.hasModelString())
1394  {
1395  msgs.push_back(CStatusMessage(this).validationError(u"No model string"));
1396  break;
1397  }
1398 
1399  if (!model.hasFileName())
1400  {
1401  if (ignoreEmptyFileNames) { break; }
1402  msgs.push_back(CStatusMessage(this).validationError(u"'%1', no file name")
1403  << model.getModelStringAndDbKey());
1404  break;
1405  }
1406 
1407  const QString fn(caseSensitive ? model.getFileName() : model.getFileNameLowerCase());
1408  if (failedFiles.contains(fn))
1409  {
1410  msgs.push_back(CStatusMessage(this).validationError(u"'%1', known failed file '%2' skipped")
1411  << model.getModelStringAndDbKey() << model.getFileName());
1412  break;
1413  }
1414 
1415  if (workingFiles.contains(fn) || model.hasExistingCorrespondingFile())
1416  {
1417  if (!simRootDirectory.isEmpty() && !fn.contains(simRootDir))
1418  {
1419  // check if in root directory
1420  msgs.push_back(
1421  CStatusMessage(this).validationError(u"'%1', not in root directory '%2', '%3' skipped")
1422  << model.getModelStringAndDbKey() << simRootDir << model.getFileName());
1423  failedFiles.insert(fn);
1424  failedFilesCount++;
1425  break;
1426  }
1427  else
1428  {
1429  ok = true;
1430  workingFiles.insert(fn);
1431  // msgs.push_back(CStatusMessage(this).validationInfo(u"'%1', file '%2' existing") <<
1432  // model.getModelStringAndDbKey() << model.getFileName());
1433  break;
1434  }
1435  }
1436 
1437  failedFiles.insert(fn);
1438  failedFilesCount++;
1439  msgs.push_back(CStatusMessage(this).validationError(u"'%1', file '%2' not existing")
1440  << model.getModelStringAndDbKey() << model.getFileName());
1441  }
1442  while (false);
1443 
1444  CAircraftModelList::addAsValidOrInvalidModel(model, ok, validModels, invalidModels);
1445  if (stopAtFailedFiles > 0 && failedFilesCount >= stopAtFailedFiles)
1446  {
1447  wasStopped = true;
1448  msgs.push_back(CStatusMessage(this).validationWarning(u"Stopping after %1 failed files")
1449  << failedFilesCount);
1450  break;
1451  }
1452  }
1453 
1454  // Summary
1455  if (!validModels.isEmpty())
1456  {
1457  msgs.push_back(CStatusMessage(this).validationInfo(u"File validation, valid models: %1")
1458  << validModels.size());
1459  }
1460  if (!invalidModels.isEmpty())
1461  {
1462  msgs.push_back(CStatusMessage(this).validationWarning(u"File validation, invalid models: %1")
1463  << invalidModels.size());
1464  }
1465 
1466  // done
1467  return msgs;
1468  }
1469 
1470  CStatusMessageList CAircraftModelList::validateUncFiles(const QSet<QString> &uncFiles) const
1471  {
1472  // check if UNC paths can be reached
1473  CStatusMessageList msgs;
1474  if (!CBuildConfig::isRunningOnWindowsNtPlatform()) { return msgs; }
1475  if (uncFiles.isEmpty()) { return msgs; }
1476 
1477  const QSet<QString> uncMachines = CFileUtils::windowsUncMachines(uncFiles);
1478  if (uncMachines.isEmpty())
1479  {
1480  msgs.push_back(CStatusMessage(this).validationInfo(u"Found NO UNC machines for %1 files, odd...?")
1481  << uncFiles.size());
1482  }
1483  else
1484  {
1485  const QString machines = joinStringSet(uncMachines, ", ");
1486  msgs.push_back(CStatusMessage(this).validationInfo(u"Found %1 UNC files on machines: %2")
1487  << uncFiles.size() << machines);
1488  }
1489 
1490  for (const QString &m : uncMachines)
1491  {
1492  const bool ping = canPing(m);
1493  if (!ping)
1494  {
1495  msgs.push_back(CStatusMessage(this).validationError(u"Cannot ping UNC machine(s): %1. UNC files: %2")
1496  << m << uncFiles.size());
1497  }
1498  }
1499  return msgs;
1500  }
1501 
1502  QJsonObject CAircraftModelList::toMemoizedJson() const
1503  {
1505  QJsonArray array;
1506  for (const auto &it : *this) { array << it.toMemoizedJson(helper); }
1507  QJsonObject json;
1508  json.insert("containerbase", array);
1509  json.insert("aircraftIcaos", helper.getTable<CAircraftIcaoCode>().toJson());
1510  json.insert("liveries", helper.getTable<CLivery>().toJson());
1511  json.insert("distributors", helper.getTable<CDistributor>().toJson());
1512  return json;
1513  }
1514 
1515  void CAircraftModelList::convertFromMemoizedJson(const QJsonObject &json, bool fallbackToConvertToJson)
1516  {
1517  clear();
1518  QJsonValue value = json.value("containerbase");
1519  if (value.isUndefined()) { throw CJsonException("Missing 'containerbase'"); }
1520  QJsonArray array = value.toArray();
1521 
1523  const QJsonValue aircraftIcaos = json.value("aircraftIcaos");
1524  const QJsonValue liveries = json.value("liveries");
1525  const QJsonValue distributors = json.value("distributors");
1526 
1527  const bool undefAc = aircraftIcaos.isUndefined();
1528  const bool undefLiv = liveries.isUndefined();
1529  const bool undefDist = distributors.isUndefined();
1530  const bool undefAll = undefAc && undefDist && undefLiv;
1531 
1532  if (fallbackToConvertToJson && undefAll)
1533  {
1534  this->convertFromJson(json);
1535  return;
1536  }
1537  else
1538  {
1539  if (undefAc) { throw CJsonException("Missing 'aircraftIcaos'"); }
1540  if (undefLiv) { throw CJsonException("Missing 'liveries'"); }
1541  if (undefDist) { throw CJsonException("Missing 'distributors'"); }
1542  }
1543 
1544  // convert
1545  {
1546  CJsonScope scope("aircraftIcaos");
1547  Q_UNUSED(scope)
1548  helper.getTable<CAircraftIcaoCode>().convertFromJson(aircraftIcaos.toObject());
1549  }
1550  {
1551  CJsonScope scope("liveries");
1552  Q_UNUSED(scope)
1553  helper.getTable<CLivery>().convertFromJson(liveries.toObject());
1554  }
1555  {
1556  CJsonScope scope("distributors");
1557  Q_UNUSED(scope)
1558  helper.getTable<CDistributor>().convertFromJson(distributors.toObject());
1559  }
1560 
1561  int index = 0;
1562  for (auto &&i : array)
1563  {
1564  CJsonScope scope("containerbase", index++);
1565  Q_UNUSED(scope)
1566  CAircraftModel value;
1567  value.convertFromMemoizedJson(i.toObject(), helper);
1568  this->push_back(value);
1569  }
1570  }
1571 
1572  QJsonArray CAircraftModelList::toDatabaseJson() const
1573  {
1574  QJsonArray array;
1575  for (const CAircraftModel &model : *this)
1576  {
1577  CAircraftModel copy(model);
1578  copy.normalizeFileNameForDb(); // strip full path
1579  QJsonValue v(copy.toDatabaseJson());
1580  array.append(v);
1581  }
1582  return array;
1583  }
1584 
1585  QString CAircraftModelList::toDatabaseJsonString(QJsonDocument::JsonFormat format) const
1586  {
1587  return QJsonDocument(toDatabaseJson()).toJson(format);
1588  }
1589 
1590  QString CAircraftModelList::asHtmlSummary() const
1591  {
1592  if (this->isEmpty()) { return {}; }
1593  QString html;
1594  for (const CAircraftModel &model : *this)
1595  {
1596  html += html.isEmpty() ? model.asHtmlSummary(" ") : u"<br>" % model.asHtmlSummary(" ");
1597  }
1598  return html;
1599  }
1600 
1601  QString CAircraftModelList::coverageSummary(const QString &separator) const
1602  {
1603  if (this->isEmpty()) { return "no models"; } // avoid division by 0
1604 
1605  const int dbEntries = this->countWithValidDbKey();
1606  const double dbRatio = CMathUtils::round(static_cast<double>(100 * dbEntries) / this->size(), 1);
1607  return u"Entries: " % QString::number(this->size()) % u" | valid DB keys: " % QString::number(dbEntries) %
1608  u" (" % QString::number(dbRatio) % u"%)" % separator % u"color liveries: " %
1609  QString::number(this->countModelsWithColorLivery()) % u" | airline liveries: " %
1610  QString::number(this->countModelsWithAirlineLivery()) % separator % u"VTOL: " %
1611  QString::number(this->countVtolAircraft()) % u" | military: " %
1612  QString::number(this->countMilitaryAircraft()) % u" | civilian: " %
1613  QString::number(this->countCivilianAircraft()) % separator % u"Different airlines: " %
1614  QString::number(this->countDifferentAirlines()) % separator % u"Combined types: '" %
1615  this->getCombinedTypesAsString() % u'\'' % separator %
1616  (this->size() <= 25 ?
1617  (u"Aircraft/airlines: " % this->getAicraftAndAirlineDesignatorsAsString(true) % separator) :
1618  QString()) %
1619  u"Simulators: " % this->countPerSimulator().toQString();
1620  }
1621 
1622  QString CAircraftModelList::coverageSummaryForModel(const CAircraftModel &checkModel,
1623  const QString &separator) const
1624  {
1625  const bool combinedCodeForModel =
1626  this->containsCombinedType(checkModel.getAircraftIcaoCode().getCombinedType());
1627  const bool airlineForModel =
1628  checkModel.hasAirlineDesignator() && this->containsAirlineLivery(checkModel.getAirlineIcaoCode());
1629  return coverageSummary(separator) % separator % u"Data for input model, has combined: " %
1630  boolToYesNo(combinedCodeForModel) %
1631  (checkModel.hasAirlineDesignator() ?
1632  u" airline '" % checkModel.getAirlineIcaoCodeDesignator() % u"': " % boolToYesNo(airlineForModel) :
1633  QString());
1634  }
1635 
1636  QString CAircraftModelList::htmlStatistics(bool aircraftStats, bool airlineStats) const
1637  {
1638  static const QString sep("<br>");
1639  const bool notOnlyDb = this->containsAnyObjectWithoutKey();
1640  QString stats = this->coverageSummary(sep);
1641  if (aircraftStats)
1642  {
1643  const CAircraftIcaoCodeList icaos = this->getAircraftIcaoCodesFromDb();
1644  QStringList designators(icaos.allDesignators().values());
1645  designators.sort();
1646  stats += sep % sep % u"Aircraft ICAOs from DB: " % sep % designators.join(", ");
1647  }
1648 
1649  if (airlineStats)
1650  {
1651  const CAirlineIcaoCodeList icaos = this->getAirlineIcaoCodesFromDb();
1652  const QStringList designators = icaos.toIcaoDesignatorCompleterStrings();
1653  stats += sep % sep % u"Airline ICAOs from DB: " % sep % designators.join(", ");
1654  }
1655 
1656  if (notOnlyDb)
1657  {
1658  const CAircraftModelList dbModels = this->findObjectsWithDbKey();
1659  stats += sep % sep % u"DB objects:<br>---------" % sep % dbModels.htmlStatistics(false, false);
1660  }
1661  return stats;
1662  }
1663 
1664  CStatusMessage CAircraftModelList::saveInvalidModels() const
1665  {
1666  if (this->isEmpty()) { return CStatusMessage(this).info(u"No models"); }
1667  const QString json = this->toJsonString();
1668  const bool s = CFileUtils::writeStringToFile(json, invalidModelFileAndPath());
1669  if (!s)
1670  {
1671  return CStatusMessage(this).error(u"Unable to save %1 entries to '%2'")
1672  << this->size() << invalidModelFileAndPath();
1673  }
1674  return CStatusMessage(this).info(u"Saved %1 entries to '%2'") << this->size() << invalidModelFileAndPath();
1675  }
1676 
1677  CStatusMessage CAircraftModelList::loadInvalidModels()
1678  {
1679  const QString json = CFileUtils::readFileToString(invalidModelFileAndPath());
1680  if (json.isEmpty())
1681  {
1682  return CStatusMessage(this).error(u"Unable to read from '%1'") << invalidModelFileAndPath();
1683  }
1684  *this = CAircraftModelList::fromJson(json, true);
1685  return CStatusMessage(this).info(u"Loaded %1 entries from '%2'") << this->size() << invalidModelFileAndPath();
1686  }
1687 
1688  CAircraftModelList CAircraftModelList::fromDatabaseJsonCaching(const QJsonArray &array,
1689  const CAircraftIcaoCodeList &icaos,
1690  const CAircraftCategoryList &categories,
1691  const CLiveryList &liveries,
1692  const CDistributorList &distributors)
1693  {
1694  AircraftIcaoIdMap aircraftIcaosMap = icaos.toDbKeyValueMap();
1695  LiveryIdMap liveriesMap = liveries.toDbKeyValueMap();
1696  DistributorIdMap distributorsMap = distributors.toDbKeyValueMap();
1697  const AircraftCategoryIdMap categoriesMap = categories.toDbKeyValueMap();
1698 
1699  CAircraftModelList models;
1700  for (const QJsonValue &value : array)
1701  {
1702  models.push_back(CAircraftModel::fromDatabaseJsonCaching(value.toObject(), aircraftIcaosMap, categoriesMap,
1703  liveriesMap, distributorsMap));
1704  }
1705  return models;
1706  }
1707 
1708  const QString &CAircraftModelList::invalidModelFileAndPath()
1709  {
1710  static const QString f =
1711  CFileUtils::appendFilePathsAndFixUnc(CSwiftDirectories::logDirectory(), "invalidmodels.json");
1712  return f;
1713  }
1714 
1715  bool CAircraftModelList::hasInvalidModelFile()
1716  {
1717  const QFileInfo fi(invalidModelFileAndPath());
1718  return fi.exists();
1719  }
1720 
1721  void CAircraftModelList::addAsValidOrInvalidModel(const CAircraftModel &model, bool valid,
1722  CAircraftModelList &validModels,
1723  CAircraftModelList &invalidModels)
1724  {
1725  if (valid)
1726  {
1727  validModels.push_back(model);
1729  }
1730  else
1731  {
1732  invalidModels.push_back(model);
1734  }
1735  }
1736 
1737  void CAircraftModelList::addAsValidOrInvalidModels(const CAircraftModelList &models, bool valid,
1738  CAircraftModelList &validModels,
1739  CAircraftModelList &invalidModels)
1740  {
1741  for (const CAircraftModel &model : models)
1742  {
1743  CAircraftModelList::addAsValidOrInvalidModel(model, valid, validModels, invalidModels);
1744  }
1745  }
1746 } // namespace swift::misc::simulation
Thrown when a convertFromJson method encounters an unrecoverable error in JSON data.
Definition: jsonexception.h:24
Pseudo-RAII pattern that tracks the current JSON value being converted.
Definition: jsonexception.h:50
Memoizer for Ts. Other types are passed through.
Definition: memotable.h:49
const CSequence< T > & getTable() const
Return the values in the T table as a flat list.
Definition: memotable.h:61
Unmemoizer for Ts. Other types are passed through.
Definition: memotable.h:69
CSequence< T > & getTable()
Return reference to the flat list T table.
Definition: memotable.h:73
Derived & validationError(const char16_t(&format)[N])
Set the severity to error, providing a format string, and adding the validation category.
Derived & error(const char16_t(&format)[N])
Set the severity to error, providing a format string.
Derived & info(const char16_t(&format)[N])
Set the severity to info, providing a format string.
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 containsBy(Predicate p) const
Return true if there is an element for which a given predicate returns true.
Definition: range.h:101
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
CSequence findBy(Predicate p) const
Return a copy containing only those elements for which a given predicate returns true.
Definition: sequence.h:398
void truncate(size_type maxSize)
Changes the size of the sequence, if it is bigger than the given size.
Definition: sequence.h:291
void push_back(const T &value)
Appends an element at the end of the sequence.
Definition: sequence.h:305
const_iterator cbegin() const
Returns const iterator at the beginning of the sequence.
Definition: sequence.h:169
void clear()
Removes all elements in the sequence.
Definition: sequence.h:288
const_iterator cend() const
Returns const iterator one past the end of the sequence.
Definition: sequence.h:178
bool isEmpty() const
Synonym for empty.
Definition: sequence.h:285
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
Streamable status message, e.g.
bool isWarningOrAbove() const
Warning or above.
void prependMessage(const QString &msg)
Prepend message.
Status messages, e.g. from Core -> GUI.
CStatusMessage toSingleMessage() const
Merge into a single message.
bool hasErrorMessages() const
Error messages.
Value object for aircraft categories.
Value object encapsulating a list of ICAO codes.
Value object for ICAO classification.
CWakeTurbulenceCategory getWtc() const
Get WTC.
const QString & getFamily() const
Family (e.g. A350)
bool matchesDesignator(const QString &designator, int fuzzyMatch=-1, int *result=nullptr) const
Matches designator string?
const QString & getDesignator() const
Get ICAO designator, e.g. "B737".
QString getDesignatorDbKey() const
Designator and DB key.
bool matchesCombinedType(const QString &combinedType) const
Matches given combined code.
const QString & getCombinedType() const
Get type, e.g. "L2J".
const CAircraftCategory & getCategory() const
Get category.
const QString & getManufacturer() const
Get manufacturer, e.g. "Airbus".
Value object encapsulating a list of ICAO codes.
QSet< QString > allDesignators(bool noUnspecified=true) const
All ICAO codes, no duplicates.
Value object for ICAO classification.
QString getDesignatorDbKey() const
Designator and DB key.
const QString & getDesignator() const
Get airline, e.g. "DLH".
bool hasValidDesignator() const
Airline designator available?
bool matchesNamesOrTelephonyDesignator(const QString &candidate) const
Relaxed check by name or telephony designator (aka callsign, not to be confused with CCallsign)
Value object encapsulating a list of ICAO codes.
QStringList toIcaoDesignatorCompleterStrings(bool combinedString=true, bool sort=true) const
String list for completion by ICAO designator.
Value object encapsulating information of a callsign.
Definition: callsign.h:30
bool isEmpty() const
Is empty?
Definition: callsign.h:63
Value object encapsulating information about an airpot.
Definition: livery.h:29
double getColorDistance(const CLivery &otherLivery) const
Color distance 0..1 (0 is best)
Definition: livery.cpp:180
bool matchesCombinedCode(const QString &candidate) const
Matches combined code.
Definition: livery.cpp:99
const CAirlineIcaoCode & getAirlineIcaoCode() const
Corresponding airline, if any.
Definition: livery.h:65
bool hasColorTail() const
Tail color set?
Definition: livery.cpp:95
bool hasColorFuselage() const
Fuselage color set?
Definition: livery.cpp:93
const QString & getCombinedCode() const
Combined code.
Definition: livery.h:71
bool isAirlineLivery() const
Livery representing airline.
Definition: livery.cpp:165
bool hasCombinedCode() const
Livery combined code available?
Definition: livery.cpp:159
bool isColorLivery() const
Color livery?
Definition: livery.cpp:178
Value object for a list of airports.
Definition: liverylist.h:29
QMap< KEYTYPE, OBJ > toDbKeyValueMap() const
As map with DB key/object.
bool hasValidDbKey() const
Has valid DB key.
Definition: datastore.h:102
bool hasValidDbKey() const
Has valid DB key.
Definition: datastore.h:195
const QString & getDbKey() const
Get DB key.
Definition: datastore.h:180
QJsonObject toJson() const
Cast to JSON object.
Definition: mixinjson.h:132
Physical unit length (length)
Definition: length.h:18
Aircraft model (used by another pilot, my models on disk)
Definition: aircraftmodel.h:71
const aviation::CCallsign & getCallsign() const
Corresponding callsign if applicable.
void convertFromMemoizedJson(const QJsonObject &json, const MemoHelper::CUnmemoizer &)
From JSON with memoized members (used by CAircraftModelList)
bool setDistributorOrder(int order)
Set the distributor order.
bool matchesModelString(const QString &modelString, Qt::CaseSensitivity sensitivity) const
Matches model string?
ModelMode getModelMode() const
Model mode.
const aviation::CAirlineIcaoCode & getAirlineIcaoCode() const
Airline ICAO code.
bool hasKnownAircraftDesignator() const
Has known aircraft designator?
bool hasCategory() const
Assigned a category?
bool hasAirlineDesignator() const
Airline designator?
bool hasExistingCorrespondingFile() const
Does the corresponding file exist?
const aviation::CLivery & getLivery() const
Get livery.
ModelMode
Mode, decides if a model is supposed to be used in the model set for model matching or not....
Definition: aircraftmodel.h:92
int calculateScore(const CAircraftModel &compareModel, bool preferColorLiveries, CStatusMessageList *log=nullptr) const
Calculate score.
const QString & getAircraftIcaoCodeDesignator() const
Aircraft ICAO code designator.
CSimulatorInfo getSimulator() const
Simulator info.
void normalizeFileNameForDb()
File path for DB (absolute paths make no sense in DB)
const QString & getModelString() const
Model key, either queried or loaded from simulator model.
bool hasDistributor() const
Distributor, but not necessarily loaded from DB.
bool isCivilian() const
Civilian model?
const QString & getModelModeAsString() const
Model mode as string.
bool hasAircraftDesignator() const
Has aircraft designator?
QJsonObject toDatabaseJson() const
To database JSON.
bool matchesModelStringOrAlias(const QString &modelString, Qt::CaseSensitivity sensitivity) const
Matches model string or alias?
CStatusMessageList validate(bool withNestedObjects) const
Validate.
bool matchesMode(simulation::CAircraftModel::ModelModeFilter mode) const
Matches given mode?
const CDistributor & getDistributor() const
Get distributor.
const aviation::CAircraftIcaoCode & getAircraftIcaoCode() const
Aircraft ICAO code.
bool matchesFileName(const QString &fileName) const
Matching file name?
bool hasDbDistributor() const
Distributor loaded from DB.
QString getModelStringAndDbKey() const
Model string and DB key (if available)
const QString getAirlineIcaoCodeVDesignator() const
Airline ICAO code designator.
QString getFileNameLowerCase() const
File name as lower case.
bool matchesAnyDbDistributor(const CDistributorList &distributors) const
By distributor.
QString asHtmlSummary(const QString &separator="<br>") const
As a brief HTML summary (e.g. used in tooltips)
const QString & getAirlineIcaoCodeDesignator() const
Airline ICAO code designator.
bool isMilitary() const
Military model?
bool hasModelString() const
Non empty model string?
void setLivery(const aviation::CLivery &livery)
Livery.
void setDistributor(const CDistributor &distributor)
Set distributor.
bool setAircraftIcaoCode(const aviation::CAircraftIcaoCode &aircraftIcaoCode)
Set aircraft ICAO code.
const QString & getFileName() const
File name (corresponding data for simulator, only available if representing simulator model.
bool matchesSimulator(const CSimulatorInfo &simulator) const
Matches given simulator?
Value object encapsulating a list of aircraft models.
bool containsModelStringOrDbKey(const simulation::CAircraftModel &model, Qt::CaseSensitivity sensitivity=Qt::CaseInsensitive) const
Contains model with model string or id?
bool containsAirlineLivery(const aviation::CAirlineIcaoCode &airline) const
Contains airline livery for given airline.
QStringList getModelStringList(bool sort=true) const
Model strings.
CAircraftModel findFirstByModelStringAliasOrDefault(const QString &modelString, Qt::CaseSensitivity sensitivity=Qt::CaseInsensitive) const
Find first by model string.
QSet< QString > getAllUNCFileNames() const
All UNC file names.
CAircraftModelList findEmptyModelStrings() const
Find empty model strings.
CAircraftModel findFirstByModelStringOrDefault(const QString &modelString, Qt::CaseSensitivity sensitivity=Qt::CaseInsensitive) const
Find first by model string.
CAircraftModelList findByAircraftAndAirline(const aviation::CAircraftIcaoCode &aircraftIcaoCode, const aviation::CAirlineIcaoCode &airlineIcaoCode) const
Find by ICAO of aircraft and airline.
CAircraftModelList findByAircraftDesignatorAndLiveryCombinedCode(const QString &aircraftDesignator, const QString &combinedCode) const
Find by designator and livery code.
bool containsCallsign(const aviation::CCallsign &callsign) const
Contains model for callsign?
CAircraftModelList findByAirlineGroup(const swift::misc::aviation::CAirlineIcaoCode &airline) const
Find by the corresponding airline group.
CAircraftModelList findColorLiveries() const
Find color liveries.
CAircraftModelList findByIcaoDesignators(const aviation::CAircraftIcaoCode &aircraftIcaoCode, const aviation::CAirlineIcaoCode &airlineIcaoCode) const
Find by ICAO designators.
CAircraftModelList findByAircraftAndLivery(const aviation::CAircraftIcaoCode &aircraftIcaoCode, const aviation::CLivery &livery) const
Find by aircraft and livery.
int removeModelsWithString(const CAircraftModelList &models, Qt::CaseSensitivity sensitivity)
Remove those models with given model strings.
QString htmlStatistics(bool aircraftStats, bool airlineStats) const
A HTML summary of the data in the list.
CAircraftModelList findByModelString(const QString &modelString, Qt::CaseSensitivity sensitivity=Qt::CaseInsensitive) const
Find by model string.
CAircraftModel findFirstByCallsignOrDefault(const aviation::CCallsign &callsign) const
Find first by callsign.
bool removeModelWithString(const QString &modelString, Qt::CaseSensitivity sensitivity)
Remove those models with given model strings.
CAircraftModelList findByAirlineNameAndTelephonyDesignator(const QString &name, const QString &telephony, bool onlyIfExistInModel=true) const
Find by airline name and telephony.
bool containsCombinedType(const QString &combinedType) const
Contains given combined type?
bool containsModelString(const QString &modelString, Qt::CaseSensitivity sensitivity=Qt::CaseInsensitive) const
Contains model string?
bool containsModelsWithAircraftAndAirlineIcaoDesignator(const QString &aircraftDesignator, const QString &airlineDesignator) const
Contains any model with aircraft and airline ICAO designator?
bool containsModelsWithAircraftIcaoDesignator(const QString &aircraftDesignator) const
Contains any model with aircraft ICAO designator?
Count per simulator, small utility class allows to retrieve values as per simulator.
void increaseSimulatorCounts(const CSimulatorInfo &simulator)
Increase all simulators given here.
int simulatorsRepresented() const
Number of simulators with count > 0.
QMultiMap< int, CSimulatorInfo > countPerSimulator() const
Sorted (ascending) per simulator.
Value object encapsulating information of software distributor.
Definition: distributor.h:33
Value object encapsulating a list of distributors.
int removeIfNotLoadedFromDb()
Remove distributors not from DB.
Simple hardcoded info about the corresponding simulator.
Definition: simulatorinfo.h:41
void addSimulator(Simulator s)
Add simulator flags.
bool matchesAny(const CSimulatorInfo &otherInfo) const
Matches any simulator.
Simulator getSimulator() const
Simulator.
auto removeIfIn(I begin1, I end1, J begin2, J end2)
Removes those elements in range 1 that appear also in range 2 leaving only those that do not appear i...
Definition: algorithm.h:25
SWIFT_MISC_EXPORT QString joinStringSet(const QSet< QString > &set, const QString &separator)
Convert string to bool.
auto makePairsRange(const T &container)
Returns a const CRange for iterating over the keys and values of a Qt associative container.
Definition: range.h:374
bool exists(const QString &path)
void append(const QJsonValue &value)
QByteArray toJson(QJsonDocument::JsonFormat format) const const
QJsonObject::iterator insert(QLatin1StringView key, const QJsonValue &value)
QJsonValue value(QLatin1StringView key) const const
bool isUndefined() const const
QJsonArray toArray() const const
QJsonObject toObject() const const
void append(QList< T > &&value)
QList< T >::iterator begin()
QList< T >::iterator end()
bool isEmpty() const const
bool contains(const Key &key) const const
const Key & firstKey() const const
bool isEmpty() const const
Key key(const T &value, const Key &defaultKey) const const
QMap< Key, T >::size_type size() const const
QList< T > values() const const
QMultiMap< Key, T >::iterator insert(QMultiMap< Key, T >::const_iterator pos, const Key &key, const T &value)
T & last()
const Key & lastKey() const const
QList< T > values() const const
bool contains(const QSet< T > &other) const const
QSet< T >::iterator insert(QSet< T >::const_iterator it, const T &value)
bool isEmpty() const const
qsizetype size() const const
QList< T > values() const const
const QChar at(qsizetype position) const const
bool contains(QChar ch, Qt::CaseSensitivity cs) const const
bool isEmpty() const const
qsizetype length() const const
QString number(double n, char format, int precision)
QString toLower() const const
QString toUpper() const const
QString trimmed() const const
bool contains(QLatin1StringView str, Qt::CaseSensitivity cs) const const
QString join(QChar separator) const const
void sort(Qt::CaseSensitivity cs)
CaseSensitivity
#define SWIFT_DEFINE_SEQUENCE_MIXINS(Namespace, T, List)
Explicit template definition of mixins for a CSequence subclass.
Definition: sequence.h:63