Mercurial > hg > classical
view common/Objects.h @ 31:07efb25d24d6
* Merge revs 7200-7222 from SVN (update use of loader API, switch to using
mapper for merge operation)
author | Chris Cannam |
---|---|
date | Thu, 18 Mar 2010 16:59:24 +0000 |
parents | 8bed05455706 |
children | 84d6acb6b3ba |
line wrap: on
line source
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ #ifndef _CLASSICAL_DATA_OBJECTS_H_ #define _CLASSICAL_DATA_OBJECTS_H_ #include <dataquay/Uri.h> #include <dataquay/Node.h> #include <QObject> #include <QMetaType> #include <QString> #include <QStringList> #include <QSharedPointer> #include <QSet> #include <QMutex> #include <QMutexLocker> #include <QMap> #include <iostream> namespace ClassicalData { //!!! need to review ownership more carefully -- and use deletion notify signals? class Year { public: Year() : m_year(0) { } Year(int y) : m_year(y) { } int toInt() const { return m_year; } operator int() { return m_year; } struct Encoder : public Dataquay::Node::VariantEncoder { QString fromVariant(const QVariant &v) { QString s = QString("%1").arg(v.value<Year>().toInt()); return s; } QVariant toVariant(const QString &s) { QVariant v = QVariant::fromValue<Year>(s.toInt()); return v; } }; private: int m_year; }; class HistoricalEvent : public QObject { Q_OBJECT Q_PROPERTY(ClassicalData::Year year READ year WRITE setYear NOTIFY yearChanged STORED true) Q_PROPERTY(QString place READ place WRITE setPlace NOTIFY placeChanged STORED true) Q_PROPERTY(bool approximate READ approximate WRITE setApproximate NOTIFY approximateChanged STORED true) public: HistoricalEvent() : m_year(0), m_place(), m_approximate(false) { } HistoricalEvent(Year y) : m_year(y), m_approximate(false) { } HistoricalEvent(Year y, QString p) : m_year(y), m_place(p), m_approximate(false) { } HistoricalEvent(const HistoricalEvent &h) : QObject(), m_year(h.m_year), m_place(h.m_place), m_approximate(h.m_approximate) { } Year year() const { return m_year; } void setYear(Year y) { m_year = y; emit yearChanged(y); } QString place() const { return m_place; } void setPlace(QString p) { m_place = p; emit placeChanged(p); } bool approximate() const { return m_approximate; } void setApproximate(bool a) { m_approximate = a; emit approximateChanged(a); } signals: void yearChanged(Year); void placeChanged(QString); void approximateChanged(bool); private: Year m_year; QString m_place; bool m_approximate; }; class Birth : public HistoricalEvent { Q_OBJECT public: Birth() : HistoricalEvent() { } Birth(Year y) : HistoricalEvent(y) { } Birth(Year y, QString p) : HistoricalEvent(y, p) { } }; class Death : public HistoricalEvent { Q_OBJECT public: Death() : HistoricalEvent() { } Death(Year y) : HistoricalEvent(y) { } Death(Year y, QString p) : HistoricalEvent(y, p) { } }; class Composer; class Work; class Composition : public HistoricalEvent { Q_OBJECT Q_PROPERTY(ClassicalData::Composer *composer READ composer WRITE setComposer NOTIFY composerChanged STORED true) Q_PROPERTY(QSet<ClassicalData::Work *> works READ works WRITE setWorks NOTIFY worksChanged STORED true) Q_PROPERTY(QString composerName READ composerName WRITE setComposerName STORED false) public: Composition() : HistoricalEvent(), m_composer(0) { } Composition(Year y) : HistoricalEvent(y), m_composer(0) { } Composition(Year y, QString p) : HistoricalEvent(y, p), m_composer(0) { } Composer *composer() { return m_composer; } void setComposer(Composer *c) { m_composer = c; emit composerChanged(c); } QSet<Work *> works() { return m_works; } void setWorks(QSet<Work *> c) { m_works = c; emit worksChanged(c); } void addWork(Work *w) { m_works.insert(w); emit worksChanged(m_works); } // Not a storable property, set temporarily while composer record is found QString composerName() const { return m_cname; } void setComposerName(QString n) { m_cname = n; } signals: void composerChanged(Composer *); void worksChanged(QSet<Work *>); private: Composer *m_composer; QSet<Work *> m_works; QString m_cname; }; class Document : public QObject { Q_OBJECT Q_PROPERTY(Dataquay::Uri uri READ uri WRITE setUri NOTIFY uriChanged STORED true) Q_PROPERTY(QString siteName READ siteName WRITE setSiteName NOTIFY siteNameChanged STORED true) Q_PROPERTY(QObject *topic READ topic WRITE setTopic NOTIFY topicChanged STORED true) public: Document(QObject *parent = 0) : QObject(parent), m_topic(0) { } Dataquay::Uri uri() const { return m_uri; } void setUri(Dataquay::Uri uri) { m_uri = uri; emit uriChanged(uri); } QString siteName() const { return m_siteName; } void setSiteName(QString n) { m_siteName = n; emit siteNameChanged(n); } QObject *topic() const { return m_topic; } // I don't own this void setTopic(QObject *t) { m_topic = t; emit topicChanged(t); } signals: void uriChanged(Dataquay::Uri); void siteNameChanged(QString); void topicChanged(QObject *); private: Dataquay::Uri m_uri; QString m_siteName; QObject *m_topic; }; class NamedEntity : public QObject { Q_OBJECT Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged STORED true) Q_PROPERTY(QSet<QString> aliases READ aliases WRITE setAliases NOTIFY aliasesChanged STORED true) Q_PROPERTY(QString remarks READ remarks WRITE setRemarks NOTIFY remarksChanged STORED true) Q_PROPERTY(QSet<ClassicalData::Document*> pages READ pages WRITE setPages NOTIFY pagesChanged STORED true) Q_PROPERTY(QSet<Dataquay::Uri> otherURIs READ otherURIs WRITE setOtherURIs NOTIFY otherURIsChanged STORED true) public: NamedEntity(QObject *parent = 0) : QObject(parent) { } ~NamedEntity() { /* for (QSet<Document *>::iterator i = m_pages.begin(); i != m_pages.end(); ++i) { delete *i; } */ } QString name() const { return m_name; } virtual void setName(QString n) { m_name = n; emit nameChanged(n); } QString remarks() const { return m_remarks; } void setRemarks(QString n) { m_remarks = n; emit remarksChanged(n); } QSet<QString> aliases() const { return m_aliases; } virtual void setAliases(QSet<QString> l) { m_aliases = l; emit aliasesChanged(l); } virtual void addAlias(QString a) { m_aliases.insert(a); emit aliasesChanged(m_aliases); } QSet<Document *> pages() const { return m_pages; } void addPage(Document *p) { m_pages.insert(p); emit pagesChanged(m_pages); } void setPages(QSet<Document *> p) { /* for (QSet<Document *>::iterator i = m_pages.begin(); i != m_pages.end(); ++i) { delete *i; } */ m_pages = p; emit pagesChanged(p); } QSet<Dataquay::Uri> otherURIs() const { return m_otherURIs; } void addOtherURI(Dataquay::Uri u) { m_otherURIs.insert(u); emit otherURIsChanged(m_otherURIs); } void setOtherURIs(QSet<Dataquay::Uri> u) { m_otherURIs = u; emit otherURIsChanged(u); } signals: void nameChanged(QString); void remarksChanged(QString); void aliasesChanged(QSet<QString>); void pagesChanged(QSet<Document *>); void otherURIsChanged(QSet<Dataquay::Uri>); protected: QString m_name; QString m_remarks; QSet<QString> m_aliases; QSet<Document *> m_pages; // we own these QSet<Dataquay::Uri> m_otherURIs; }; class Movement; class Form; class Work : public NamedEntity { Q_OBJECT Q_PROPERTY(QString key READ key WRITE setKey NOTIFY keyChanged STORED true) Q_PROPERTY(QString opus READ opus WRITE setOpus NOTIFY opusChanged STORED true) Q_PROPERTY(QString catalogue READ catalogue WRITE setCatalogue NOTIFY catalogueChanged STORED true) Q_PROPERTY(QString number READ number WRITE setNumber NOTIFY numberChanged STORED true) Q_PROPERTY(QSet<ClassicalData::Form*> forms READ forms WRITE setForms NOTIFY formsChanged STORED true) Q_PROPERTY(ClassicalData::Work* partOf READ partOf WRITE setPartOf NOTIFY partOfChanged STORED true) Q_PROPERTY(QSet<ClassicalData::Work*> parts READ parts WRITE setParts NOTIFY partsChanged STORED true) Q_PROPERTY(QSet<ClassicalData::Movement*> movements READ movements WRITE setMovements NOTIFY movementsChanged STORED true) Q_PROPERTY(ClassicalData::Composition *composition READ composition WRITE setComposition NOTIFY compositionChanged STORED true) public: Work(QObject *parent = 0) : NamedEntity(parent), m_partOf(0), m_composition(0) { } QString key() const { return m_key; } void setKey(QString n) { m_key = n; emit keyChanged(n); } QString opus() const { return m_opus; } void setOpus(QString n) { m_opus = n; emit opusChanged(n); } QString catalogue() const { return m_catalogue; } void setCatalogue(QString n) { m_catalogue = n; emit catalogueChanged(n); } QString number() const { return m_number; } void setNumber(QString n) { m_number = n; emit numberChanged(n); } QSet<Form *> forms() const { return m_forms; } void setForms(QSet<Form *> f) { m_forms = f; emit formsChanged(f); } void addForm(Form *f) { m_forms.insert(f); emit formsChanged(m_forms); } Work *partOf() const { return m_partOf; } void setPartOf(Work *w) { m_partOf = w; emit partOfChanged(w); } QSet<Work *> parts() const { return m_parts; } void setParts(QSet<Work *> l) { m_parts = l; emit partsChanged(l); } void addPart(Work *w) { m_parts.insert(w); emit partsChanged(m_parts); } QSet<Movement *> movements() const { return m_movements; } void setMovements(QSet<Movement *> l) { m_movements = l; emit movementsChanged(l); } void addMovement(Movement *w) { m_movements.insert(w); emit movementsChanged(m_movements); } Composition *composition() { return m_composition; } const Composition *composition() const { return m_composition; } void setComposition(Composition *c) { m_composition = c; emit compositionChanged(c); } struct Ordering { bool operator()(Work *, Work *); }; /** * Compare the ordering of two strings that are known to contain * catalogue number texts, such as "Op. 1 no 4" and "Op. 3 no 2" * (which should compare in that order). Return value is as for * strcmp. */ //!!! todo: unit tests static int compareCatalogueNumberTexts(QString a, QString b); signals: void keyChanged(QString); void opusChanged(QString); void catalogueChanged(QString); void numberChanged(QString); void formsChanged(QSet<Form *>); void partOfChanged(Work *); void partsChanged(QSet<Work *>); void movementsChanged(QSet<Movement *>); void compositionChanged(Composition *); private: QString m_key; QString m_opus; QString m_catalogue; QString m_number; QSet<Form *> m_forms; Work *m_partOf; QSet<Work *> m_parts; QSet<Movement *> m_movements; Composition *m_composition; }; class Movement : public NamedEntity { Q_OBJECT Q_PROPERTY(QString key READ key WRITE setKey NOTIFY keyChanged STORED true) Q_PROPERTY(QString number READ number WRITE setNumber NOTIFY numberChanged STORED true) Q_PROPERTY(ClassicalData::Work* partOf READ partOf WRITE setPartOf NOTIFY partOfChanged STORED true) Q_PROPERTY(QSet<ClassicalData::Movement*> parts READ parts WRITE setParts NOTIFY partsChanged STORED true) // movements can be nested Q_PROPERTY(ClassicalData::Composition *composition READ composition WRITE setComposition NOTIFY compositionChanged STORED true) public: Movement(QObject *parent = 0) : NamedEntity(parent), m_partOf(0), m_composition(0) { } QString key() const { return m_key; } void setKey(QString n) { m_key = n; emit keyChanged(n); } QString number() const { return m_number; } void setNumber(QString n) { m_number = n; emit numberChanged(n); } Work *partOf() const { return m_partOf; } void setPartOf(Work *w) { m_partOf = w; emit partOfChanged(w); } QSet<Movement *> parts() const { return m_parts; } void setParts(QSet<Movement *> l) { m_parts = l; emit partsChanged(l); } void addPart(Movement *w) { m_parts.insert(w); emit partsChanged(m_parts); } Composition *composition() { return m_composition; } const Composition *composition() const { return m_composition; } void setComposition(Composition *c) { m_composition = c; emit compositionChanged(c); } signals: void keyChanged(QString); void numberChanged(QString); void partOfChanged(Work *); void partsChanged(QSet<Movement *>); void compositionChanged(Composition *); private: QString m_key; QString m_number; Work *m_partOf; QSet<Movement *> m_parts; Composition *m_composition; }; class Composer : public NamedEntity { Q_OBJECT Q_PROPERTY(QString gender READ gender WRITE setGender NOTIFY genderChanged STORED true) Q_PROPERTY(QSet<QString> nationality READ nationality WRITE setNationality NOTIFY nationalityChanged STORED true) Q_PROPERTY(QSet<Dataquay::Uri> geonameURIs READ geonameURIs WRITE setGeonameURIs NOTIFY geonameURIsChanged STORED true) Q_PROPERTY(QString period READ period WRITE setPeriod NOTIFY periodChanged STORED true) Q_PROPERTY(ClassicalData::Birth *birth READ birth WRITE setBirth NOTIFY birthChanged STORED true) Q_PROPERTY(ClassicalData::Death *death READ death WRITE setDeath NOTIFY deathChanged STORED true) Q_PROPERTY(QString surname READ getSurname STORED false) Q_PROPERTY(QString forenames READ getForenames STORED false) public: Composer(QObject *parent = 0) : NamedEntity(parent), m_birth(0), m_death(0), m_namesCached(false) { } QString gender() const { return m_gender; } void setGender(QString n) { m_gender = n; emit genderChanged(n); } QSet<QString> nationality() const { return m_nationality; } void setNationality(QSet<QString> n) { m_nationality = n; emit nationalityChanged(n); } void addNationality(QString n) { m_nationality.insert(n); emit nationalityChanged(m_nationality); } QSet<Dataquay::Uri> geonameURIs() const { return m_geonameURIs; } void setGeonameURIs(QSet<Dataquay::Uri> n) { m_geonameURIs = n; emit geonameURIsChanged(n); } void addGeonameURI(Dataquay::Uri n) { m_geonameURIs.insert(n); emit geonameURIsChanged(m_geonameURIs); } QString period() const { return m_period; } void setPeriod(QString n) { m_period = n; emit periodChanged(n); } Birth *birth() { return m_birth; } const Birth *birth() const { return m_birth; } void setBirth(Birth *b) { m_birth = b; emit birthChanged(b); } Death *death() { return m_death; } const Death *death() const { return m_death; } void setDeath(Death *d) { m_death = d; emit deathChanged(d); } virtual void setName(QString n) { NamedEntity::setName(n); m_namesCached = false; } virtual void setAliases(QSet<QString> l) { NamedEntity::setAliases(l); m_namesCached = false; } virtual void addAlias(QString a) { NamedEntity::addAlias(a); m_namesCached = false; } QString getSurname() const; QString getForenames() const; QString getSortName(bool caps) const; QString getDisplayDates() const; /** * Given another composer, return true if the other composer's * dates match outs. This is mostly intended (like * matchCatalogueName) for use in merging distinct catalogues. * Matching is somewhat fuzzy; more slack is cut when the dates * are very long ago or are marked as approximate. */ bool matchDates(const Composer *other) const; // "well enough" /** * Given another name which is intended to be a well-formatted * catalogue name for a composer (but which may differ in * ordering, number of forenames, and perhaps in spelling), test * whether the name is a plausible match for our own. This is * mostly intended (like matchDates) for use in merging distinct * catalogues. Return true if the given name is highly likely to * match our own. */ bool matchCatalogueName(QString otherName) const; /** * Given another name which is believed to be a user-entered * composer name with unpredictable formatting and spelling (and * probably incomplete), return an estimate for the likelihood * that the intended composer was this one. Higher return values * indicate greater confidence; a value of 1.0 or more indicates * that all of the input is at least close to perfectly matched. */ float matchFuzzyName(QString name) const; /** * Given another name which is believed to be a user-entered * composer name with unpredictable formatting and spelling (and * probably incomplete), return an estimate for the likelihood * that the intended composer was this one. Higher return values * indicate greater confidence; a value of 1.0 or more indicates * that all of the input is at least close to perfectly matched. * The supplied name should have been lower-cased and split on * non-alphabetical characters. */ float matchFuzzyName(QStringList name) const; /** * Given a string that is in the process of being typed by the * user, return an estimate of the likelihood that the text is * intended to become this composer's name. If quick is true, do * a quick(er) scan only and avoid very expensive tests. */ float matchTyping(QString text) const; /** * Given a string that is in the process of being typed by the * user, return an estimate of the likelihood that the text is * intended to become this composer's name. In contrast to * matchTyping, do a quick(er) scan only, avoiding more expensive * tests. */ float matchTypingQuick(QString text) const; /** * Merge data from the given composer into this composer record. * That is, add the composer's name and aliases as aliases of this * composer, copy its dates where we lack them, etc. In all * cases, values that exist in this composer already are preferred * over values from the "other" composer. */ void mergeFrom(Composer *c); /** * Return the supplied name reduced into a "simplified" form, * eliminating many of the differences often found particularly in * European language names that have been anglicised. Used in * catalogue and fuzzy name matching. */ static QString reduceName(QString name); signals: void genderChanged(QString); void nationalityChanged(QSet<QString>); void geonameURIsChanged(QSet<Dataquay::Uri>); void periodChanged(QString); void birthChanged(Birth *); void deathChanged(Death *); private: QString m_gender; QSet<QString> m_nationality; QSet<Dataquay::Uri> m_geonameURIs; QString m_period; Birth *m_birth; Death *m_death; void cacheNames() const; float doMatchTyping(QString, bool) const; mutable bool m_namesCached; mutable QString m_surname; mutable QString m_forenames; mutable QStringList m_surnameElements; mutable QStringList m_connectiveElements; mutable QStringList m_forenameElements; mutable QStringList m_otherElements; mutable QStringList m_reducedSurnameElements; mutable QStringList m_reducedForenameElements; }; class Form : public NamedEntity { Q_OBJECT Q_PROPERTY(Dataquay::Uri uri READ uri) public: Form(QObject *parent = 0) : NamedEntity(parent) { } static Form *getFormByName(QString name) { QMutexLocker locker(&m_mutex); if (!m_map.contains(name)) { Form *f = new Form(); f->setName(name); m_map[name] = f; } return m_map[name]; } Dataquay::Uri uri() const { return Dataquay::Uri (QString(":form_%1").arg(name()).toLower().replace(' ', '_')); } private: static QMap<QString, Form *> m_map; static QMutex m_mutex; }; } Q_DECLARE_METATYPE(ClassicalData::Year); Q_DECLARE_METATYPE(ClassicalData::HistoricalEvent*); Q_DECLARE_METATYPE(ClassicalData::Birth*); Q_DECLARE_METATYPE(ClassicalData::Death*); Q_DECLARE_METATYPE(ClassicalData::Composition*); Q_DECLARE_METATYPE(ClassicalData::Work*); Q_DECLARE_METATYPE(ClassicalData::Movement*); Q_DECLARE_METATYPE(ClassicalData::Document*); Q_DECLARE_METATYPE(QSet<QString>); Q_DECLARE_METATYPE(QSet<Dataquay::Uri>); Q_DECLARE_METATYPE(QSet<ClassicalData::Work*>); Q_DECLARE_METATYPE(QSet<ClassicalData::Movement*>); Q_DECLARE_METATYPE(QSet<ClassicalData::Document*>); Q_DECLARE_METATYPE(ClassicalData::Composer*); Q_DECLARE_METATYPE(ClassicalData::Form*); Q_DECLARE_METATYPE(QSet<ClassicalData::Form*>); Q_DECLARE_METATYPE(ClassicalData::NamedEntity*); #endif