diff --git a/src/datatypes/datatypes_p.h b/src/datatypes/datatypes_p.h index 7cbdf8e..3618087 100644 --- a/src/datatypes/datatypes_p.h +++ b/src/datatypes/datatypes_p.h @@ -1,159 +1,164 @@ /* Copyright (C) 2018 Volker Krause This program is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef KITINERARY_DATATYPES_P_H #define KITINERARY_DATATYPES_P_H #include namespace KItinerary { namespace detail { // Helper types for the auto-generated operator== // This is based on the approach described here https://woboq.com/blog/verdigris-implementation-tricks.html // numerical index of properties, done in a way that we can daisy-chain overloads with it template struct num : public num { static constexpr int value = N; static constexpr num prev() { return {}; } }; template <> struct num<0> { static constexpr int value = 0; }; // type tag, to avoid unwanted overload resolution on arguments other than num<> template struct tag {}; // SFINAE helper to determine if we have a polimorphic or a simple value type template struct base_type { template static typename U::super_type test(typename U::super_type*); template static T test(...); typedef decltype(test(nullptr)) type; static constexpr const bool is_valid = !std::is_same::value; }; // customization hook for comparison for certain types template inline bool equals(typename parameter_type::type lhs, typename parameter_type::type rhs) { return lhs == rhs; } // QDateTime::operator== is true for two instances referring to the same point in time // we however want to know if two instances contain exactly the same information template <> inline bool equals(const QDateTime &lhs, const QDateTime &rhs) { return lhs.timeSpec() == rhs.timeSpec() && lhs == rhs; } // QString::operator== ignores null vs empty // we probably don't care either, but until that's decided this makes the existing tests pass template <> inline bool equals(const QString &lhs, const QString &rhs) { if (lhs.isEmpty() && rhs.isEmpty()) { return lhs.isNull() == rhs.isNull(); } return lhs == rhs; } } #define KITINERARY_PRIVATE_BASE_GADGET(Class) \ public: \ virtual ~ Class ## Private() = default; \ virtual Class ## Private * clone() const { \ return new Class ##Private(*this); \ } \ typedef Class ## Private base_type; \ typedef Class ## Private this_type; \ private: \ #define KITINERARY_PRIVATE_GADGET(Class) \ public: \ inline base_type* clone() const override { \ return new Class ## Private(*this); \ } \ typedef this_type super_type; \ typedef Class ## Private this_type; \ private: #define KITINERARY_MAKE_CLASS_IMPL(Class) \ Q_GLOBAL_STATIC_WITH_ARGS(QExplicitlySharedDataPointer, s_ ## Class ## _shared_null, (new Class ## Private)) \ Class::Class(const Class&) = default; \ Class::~Class() = default; \ Class& Class::operator=(const Class &other) { d = other.d; return *this; } \ QString Class::className() const { return QStringLiteral(#Class); } \ Class::operator QVariant() const { return QVariant::fromValue(*this); } \ static_assert(sizeof(Class) == sizeof(void*), "dptr must be the only member!"); \ namespace detail { \ static constexpr int property_counter(num<0>, tag) { return 1; } \ static constexpr bool property_equals(num<0>, tag, const Class ## Private *, const Class ## Private *) { return true; } \ } #define KITINERARY_MAKE_SIMPLE_CLASS(Class) \ KITINERARY_MAKE_CLASS_IMPL(Class) \ Class::Class() : d(*s_ ## Class ## _shared_null()) {} #define KITINERARY_MAKE_BASE_CLASS(Class) \ KITINERARY_MAKE_CLASS_IMPL(Class) \ Class::Class() : d(*s_ ## Class ## _shared_null()) {} \ Class::Class(Class ## Private *dd) : d(dd) {} +#define KITINERARY_MAKE_INTERMEDIATE_CLASS(Class, Base) \ +KITINERARY_MAKE_CLASS_IMPL(Class) \ +Class::Class() : Base(s_ ## Class ## _shared_null()->data()) {} \ +Class::Class(Class ## Private *dd) : Base(dd) {} + #define KITINERARY_MAKE_SUB_CLASS(Class, Base) \ KITINERARY_MAKE_CLASS_IMPL(Class) \ Class::Class() : Base(s_ ## Class ## _shared_null()->data()) {} #define K_D(Class) auto d = static_cast(this->d.data()) #define KITINERARY_MAKE_PROPERTY(Class, Type, Name, SetName) \ Type Class::Name() const { return static_cast(d.data())->Name; } \ void Class::SetName(detail::parameter_type::type value) { \ if (detail::equals(static_cast(d.data())->Name, value)) { return; } \ d.detach(); \ static_cast(d.data())->Name = value; \ } \ namespace detail { \ static constexpr int property_counter(num(), tag())> n, tag) { return decltype(n)::value + 1; } \ static inline bool property_equals(num(), tag())> n, tag, const Class ## Private *lhs, const Class ## Private *rhs) \ { \ if (equals(lhs->Name, rhs->Name)) { return property_equals(n.prev(), tag(), lhs, rhs); } \ return false; \ } \ } #define KITINERARY_MAKE_OPERATOR(Class) \ bool Class::operator==(const Class &other) const \ { \ static_assert(detail::property_counter(detail::num<0>(), detail::tag()) == 1, "silence unused function warnings"); \ typedef Class ## Private this_type; \ const auto lhs = static_cast(d.data()); \ const auto rhs = static_cast(other.d.data()); \ if (lhs == rhs) { \ return true; \ } \ if (!detail::property_equals(detail::num<>(), detail::tag(), lhs, rhs)) { \ return false; \ } \ if (detail::base_type::is_valid) { \ typedef typename detail::base_type::type super_type; \ return detail::property_equals(detail::num<>(), detail::tag(), static_cast(lhs), static_cast(rhs)); \ } \ return true; \ } } #endif diff --git a/src/datatypes/organization.cpp b/src/datatypes/organization.cpp index 2f6aafd..da95a5b 100644 --- a/src/datatypes/organization.cpp +++ b/src/datatypes/organization.cpp @@ -1,87 +1,94 @@ /* Copyright (C) 2018 Luca Beltrame This program is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "organization.h" #include "datatypes_p.h" #include using namespace KItinerary; namespace KItinerary { class OrganizationPrivate: public QSharedData { KITINERARY_PRIVATE_BASE_GADGET(Organization) public: QString name; QString description; QUrl image; QString email; QString telephone; QUrl url; PostalAddress address; GeoCoordinates geo; QVariantList potentialAction; }; KITINERARY_MAKE_BASE_CLASS(Organization) KITINERARY_MAKE_PROPERTY(Organization, QString, name, setName) KITINERARY_MAKE_PROPERTY(Organization, QString, description, setDescription) KITINERARY_MAKE_PROPERTY(Organization, QUrl, image, setImage) KITINERARY_MAKE_PROPERTY(Organization, QString, email, setEmail) KITINERARY_MAKE_PROPERTY(Organization, QString, telephone, setTelephone) KITINERARY_MAKE_PROPERTY(Organization, QUrl, url, setUrl) KITINERARY_MAKE_PROPERTY(Organization, PostalAddress, address, setAddress) KITINERARY_MAKE_PROPERTY(Organization, KItinerary::GeoCoordinates, geo, setGeo) KITINERARY_MAKE_PROPERTY(Organization, QVariantList, potentialAction, setPotentialAction) KITINERARY_MAKE_OPERATOR(Organization) class AirlinePrivate : public OrganizationPrivate { KITINERARY_PRIVATE_GADGET(Airline) public: QString iataCode; }; KITINERARY_MAKE_SUB_CLASS(Airline, Organization) KITINERARY_MAKE_PROPERTY(Airline, QString, iataCode, setIataCode) KITINERARY_MAKE_OPERATOR(Airline) -class FoodEstablishmentPrivate: public OrganizationPrivate +class LocalBusinessPrivate : public OrganizationPrivate +{ + KITINERARY_PRIVATE_GADGET(LocalBusiness) +}; +KITINERARY_MAKE_INTERMEDIATE_CLASS(LocalBusiness, Organization) +KITINERARY_MAKE_OPERATOR(LocalBusiness) + +class FoodEstablishmentPrivate: public LocalBusinessPrivate { KITINERARY_PRIVATE_GADGET(FoodEstablishment) }; -KITINERARY_MAKE_SUB_CLASS(FoodEstablishment, Organization) +KITINERARY_MAKE_SUB_CLASS(FoodEstablishment, LocalBusiness) KITINERARY_MAKE_OPERATOR(FoodEstablishment) -class LodgingBusinessPrivate : public OrganizationPrivate +class LodgingBusinessPrivate : public LocalBusinessPrivate { KITINERARY_PRIVATE_GADGET(LodgingBusiness) }; -KITINERARY_MAKE_SUB_CLASS(LodgingBusiness, Organization) +KITINERARY_MAKE_SUB_CLASS(LodgingBusiness, LocalBusiness) KITINERARY_MAKE_OPERATOR(LodgingBusiness) } template <> KItinerary::OrganizationPrivate *QExplicitlySharedDataPointer::clone() { return d->clone(); } #include "moc_organization.cpp" diff --git a/src/datatypes/organization.h b/src/datatypes/organization.h index f0bd152..175b168 100644 --- a/src/datatypes/organization.h +++ b/src/datatypes/organization.h @@ -1,94 +1,104 @@ /* Copyright (C) 2018 Luca Beltrame This program is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef KITINERARY_ORGANIZATION_H #define KITINERARY_ORGANIZATION_H #include "kitinerary_export.h" #include "datatypes.h" #include "place.h" class QUrl; namespace KItinerary { class OrganizationPrivate; /** An organization. * * This slightly deviates from the schema.org definition and also includes * properties of Place that its sub-classes need. This is a simplification * to avoid having to use multi-inheritance. * * @see https://schema.org/Organization */ class KITINERARY_EXPORT Organization { KITINERARY_BASE_GADGET(Organization) KITINERARY_PROPERTY(QString, name, setName) KITINERARY_PROPERTY(QString, description, setDescription) KITINERARY_PROPERTY(QUrl, image, setImage) KITINERARY_PROPERTY(QString, email, setEmail) KITINERARY_PROPERTY(QString, telephone, setTelephone) KITINERARY_PROPERTY(QUrl, url, setUrl) KITINERARY_PROPERTY(KItinerary::PostalAddress, address, setAddress) KITINERARY_PROPERTY(KItinerary::GeoCoordinates, geo, setGeo) KITINERARY_PROPERTY(QVariantList, potentialAction, setPotentialAction) protected: ///@cond internal QExplicitlySharedDataPointer d; ///@endcond }; class AirlinePrivate; /** An airline. * @see https://schema.org/Airline */ class KITINERARY_EXPORT Airline : public Organization { - KITINERARY_GADGET(Airline) + KITINERARY_BASE_GADGET(Airline) KITINERARY_PROPERTY(QString, iataCode, setIataCode) }; +class LocalBusinessPrivate; + +/** LocalBusiness. + * @see https://schema.org/LocalBusiness + */ +class KITINERARY_EXPORT LocalBusiness : public Organization +{ + KITINERARY_BASE_GADGET(LocalBusiness) +}; + /** Hotel. * @see https://schema.org/LodgingBusiness */ -class KITINERARY_EXPORT LodgingBusiness: public Organization +class KITINERARY_EXPORT LodgingBusiness: public LocalBusiness { KITINERARY_GADGET(LodgingBusiness) }; /** Food-related business (such as a restaurant, or a bakery). * @see https://schema.org/FoodEstablishment */ -class KITINERARY_EXPORT FoodEstablishment: public Organization +class KITINERARY_EXPORT FoodEstablishment: public LocalBusiness { KITINERARY_GADGET(FoodEstablishment) }; } // namespace KItinerary Q_DECLARE_METATYPE(KItinerary::Organization) Q_DECLARE_METATYPE(KItinerary::Airline) Q_DECLARE_METATYPE(KItinerary::FoodEstablishment) Q_DECLARE_METATYPE(KItinerary::LodgingBusiness) #endif // KITINERARY_ORGANIZATION_H