Warning, file /pim/kitinerary/src/lib/extractorvalidator.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 /*
0002     SPDX-FileCopyrightText: 2019 Volker Krause <vkrause@kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "extractorvalidator.h"
0008 #include "validator-logging.h"
0009 
0010 #include <KItinerary/BoatTrip>
0011 #include <KItinerary/BusTrip>
0012 #include <KItinerary/Event>
0013 #include <KItinerary/Flight>
0014 #include <KItinerary/Place>
0015 #include <KItinerary/ProgramMembership>
0016 #include <KItinerary/RentalCar>
0017 #include <KItinerary/Reservation>
0018 #include <KItinerary/Taxi>
0019 #include <KItinerary/Ticket>
0020 #include <KItinerary/TrainTrip>
0021 #include <KItinerary/Visit>
0022 
0023 #include <QDateTime>
0024 
0025 using namespace KItinerary;
0026 
0027 namespace KItinerary {
0028 class ExtractorValidatorPrivate {
0029 public:
0030     bool isSupportedTopLevelType(const QVariant &elem) const;
0031     bool filterElement(const QVariant &elem) const;
0032 
0033     bool filterLodgingReservation(const LodgingReservation &res) const;
0034     bool filterAirport(const Airport &airport) const;
0035     bool filterFlight(const Flight &flight) const;
0036     bool filterTrainTrip(const TrainTrip &trip) const;
0037     bool filterBusTrip(const BusTrip &trip) const;
0038     bool filterBoatTrip(const BoatTrip &trip) const;
0039     bool filterEvent(const Event &event) const;
0040     bool filterFoodReservation(const FoodEstablishmentReservation &res) const;
0041     bool filterLocalBusiness(const LocalBusiness &business) const;
0042     bool filterReservation(const Reservation &res) const;
0043     bool filterProgramMembership(const ProgramMembership &program) const;
0044     bool filterTicket(const Ticket &ticket) const;
0045 
0046     std::vector<const QMetaObject*> m_acceptedTypes;
0047     bool m_onlyComplete = true;
0048 };
0049 }
0050 
0051 ExtractorValidator::ExtractorValidator() :
0052     d(new ExtractorValidatorPrivate)
0053 {}
0054 
0055 ExtractorValidator::~ExtractorValidator() = default;
0056 
0057 void ExtractorValidator::setAcceptedTypes(std::vector<const QMetaObject*> &&accptedTypes)
0058 {
0059     d->m_acceptedTypes = std::move(accptedTypes);
0060 }
0061 
0062 void ExtractorValidator::setAcceptOnlyCompleteElements(bool completeOnly)
0063 {
0064     d->m_onlyComplete = completeOnly;
0065 }
0066 
0067 
0068 bool ExtractorValidatorPrivate::filterLodgingReservation(const LodgingReservation &res) const
0069 {
0070     return res.checkinTime().isValid() && res.checkoutTime().isValid() && res.checkinTime() <= res.checkoutTime();
0071 }
0072 
0073 bool ExtractorValidatorPrivate::filterAirport(const Airport &airport) const
0074 {
0075     return !airport.iataCode().isEmpty() || !airport.name().isEmpty();
0076 }
0077 
0078 bool ExtractorValidatorPrivate::filterFlight(const Flight &flight) const
0079 {
0080     // this will be valid if either boarding time, departure time or departure day is set
0081     const auto validDate = flight.departureDay().isValid();
0082     return filterAirport(flight.departureAirport())
0083            && filterAirport(flight.arrivalAirport())
0084            && validDate;
0085 }
0086 
0087 template <typename T>
0088 static bool filterPlace(const T &place)
0089 {
0090     return !place.name().isEmpty();
0091 }
0092 
0093 bool ExtractorValidatorPrivate::filterTrainTrip(const TrainTrip &trip) const
0094 {
0095     return filterPlace(trip.departureStation())
0096            && filterPlace(trip.arrivalStation())
0097            && trip.departureDay().isValid();
0098 }
0099 
0100 bool ExtractorValidatorPrivate::filterBusTrip(const BusTrip &trip) const
0101 {
0102     return filterPlace(trip.departureBusStop())
0103            && filterPlace(trip.arrivalBusStop())
0104            && trip.departureTime().isValid();
0105 }
0106 
0107 bool ExtractorValidatorPrivate::filterBoatTrip(const BoatTrip &trip) const
0108 {
0109     return filterPlace(trip.departureBoatTerminal())
0110         && filterPlace(trip.arrivalBoatTerminal())
0111         && trip.departureTime().isValid()
0112         && trip.arrivalTime().isValid();
0113 }
0114 
0115 bool ExtractorValidatorPrivate::filterEvent(const Event &event) const
0116 {
0117     return !event.name().isEmpty() && event.startDate().isValid();
0118 }
0119 
0120 bool ExtractorValidatorPrivate::filterFoodReservation(const FoodEstablishmentReservation &res) const
0121 {
0122     return res.startTime().isValid();
0123 }
0124 
0125 bool ExtractorValidatorPrivate::filterLocalBusiness(const LocalBusiness &business) const
0126 {
0127     return !business.name().isEmpty();
0128 }
0129 
0130 bool ExtractorValidatorPrivate::filterReservation(const Reservation &res) const
0131 {
0132     if (!m_onlyComplete) { // accept minimal cancellation elements
0133         if (res.reservationFor().isNull()
0134             && res.modifiedTime().isValid()
0135             && !res.reservationNumber().isEmpty()
0136             && res.reservationStatus() == Reservation::ReservationCancelled)
0137         {
0138             return true;
0139         }
0140     }
0141 
0142     if (!filterElement(res.reservationFor())) {
0143         qCDebug(ValidatorLog) << "Reservation element discarded due to rejected reservationFor property:" << res.reservationFor().typeName();
0144         return false;
0145     }
0146     return true;
0147 }
0148 
0149 bool ExtractorValidatorPrivate::filterProgramMembership(const ProgramMembership &program) const
0150 {
0151     return (!program.membershipNumber().isEmpty() || !program.token().isEmpty()) && !program.programName().isEmpty();
0152 }
0153 
0154 bool ExtractorValidatorPrivate::filterTicket(const Ticket &ticket) const
0155 {
0156     return !ticket.ticketToken().isEmpty() && !ticket.name().isEmpty();
0157 }
0158 
0159 template <typename T, bool (ExtractorValidatorPrivate::*F)(const T&) const>
0160 static inline bool callFilterWithType(const ExtractorValidatorPrivate *d, const QVariant &v)
0161 {
0162     // JsonLd::canConvert<T>(v) is guaranteed by walking up the meta object tree here
0163     return (d->*F)(JsonLd::convert<T>(v));
0164 }
0165 
0166 #define FILTER(Type, Func) { &Type::staticMetaObject, callFilterWithType<Type, &ExtractorValidatorPrivate::Func> }
0167 struct {
0168     const QMetaObject *metaObject;
0169     bool (*filter)(const ExtractorValidatorPrivate *d, const QVariant &v);
0170 } static const filter_func_map[] {
0171     FILTER(Flight, filterFlight),
0172     FILTER(TrainTrip, filterTrainTrip),
0173     FILTER(BusTrip, filterBusTrip),
0174     FILTER(BoatTrip, filterBoatTrip),
0175     FILTER(KItinerary::Event, filterEvent),
0176     FILTER(LocalBusiness, filterLocalBusiness),
0177     FILTER(FoodEstablishmentReservation, filterFoodReservation),
0178     FILTER(LodgingReservation, filterLodgingReservation),
0179     FILTER(Reservation, filterReservation),
0180     FILTER(ProgramMembership, filterProgramMembership),
0181     FILTER(Ticket, filterTicket),
0182 };
0183 #undef FILTER
0184 
0185 bool ExtractorValidatorPrivate::filterElement(const QVariant &elem) const
0186 {
0187     auto mo = QMetaType(elem.userType()).metaObject();
0188     if (!mo) {
0189         qCDebug(ValidatorLog) << "Element discarded due to non-gadget type:" << elem.typeName();
0190         return false;
0191     }
0192     while (mo) {
0193         for (const auto &f : filter_func_map) {
0194             if (f.metaObject != mo) {
0195                 continue;
0196             }
0197             if (!f.filter(this, elem)) {
0198                 return false;
0199             }
0200             break;
0201         }
0202         mo = mo->superClass();
0203     }
0204     return true;
0205 }
0206 
0207 bool ExtractorValidatorPrivate::isSupportedTopLevelType(const QVariant &elem) const
0208 {
0209     if (m_acceptedTypes.empty()) { // nothing configured, we accept everything
0210         return true;
0211     }
0212 
0213     auto mo = QMetaType(elem.userType()).metaObject();
0214     if (!mo) {
0215         qCDebug(ValidatorLog) << "Element discarded due to non-gadget top-level type:" << elem.typeName();
0216         return false;
0217     }
0218     while (mo) {
0219         const auto it = std::find(m_acceptedTypes.begin(), m_acceptedTypes.end(), mo);
0220         if (it != m_acceptedTypes.end()) {
0221             return true;
0222         }
0223         mo = mo->superClass();
0224     }
0225     return false;
0226 }
0227 
0228 bool ExtractorValidator::isValidElement(const QVariant &elem) const
0229 {
0230     // check this is an allowed top-level type
0231     if (!d->isSupportedTopLevelType(elem)) {
0232         qCDebug(ValidatorLog) << "Element discarded due to unsupported top-level type:" << elem.typeName();
0233         return false;
0234     }
0235 
0236     // apply type-specific filter functions
0237     return d->filterElement(elem);
0238 }