Chris@20: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ Chris@20: Chris@20: #include "Objects.h" Chris@20: #include "TypeRegistrar.h" Chris@20: Chris@20: #include Chris@31: #include Chris@20: #include Chris@28: #include Chris@28: #include Chris@31: #include Chris@28: #include Chris@20: Chris@20: #include Chris@20: #include Chris@20: Chris@20: #include Chris@20: Chris@20: using namespace Dataquay; Chris@20: using namespace ClassicalData; Chris@20: using namespace std; Chris@20: Chris@53: /* Chris@20: ostream &operator<<(ostream &target, const QString &str) Chris@20: { Chris@20: return target << str.toLocal8Bit().data(); Chris@20: } Chris@20: Chris@20: ostream &operator<<(ostream &target, const QUrl &u) Chris@20: { Chris@20: return target << "<" << u.toString() << ">"; Chris@20: } Chris@53: */ Chris@20: Chris@20: bool Chris@20: load(BasicStore *store, QString fileName) Chris@20: { Chris@20: QUrl url = QUrl::fromLocalFile(fileName); Chris@20: Chris@20: cerr << "Importing from URL " << url << " ..."; Chris@20: try { Chris@24: store->import(url, BasicStore::ImportPermitDuplicates); Chris@20: } catch (RDFException e) { Chris@24: cerr << " retrying with explicit ntriples type..."; Chris@24: try { Chris@24: store->import(url, BasicStore::ImportPermitDuplicates, "ntriples"); Chris@24: } catch (RDFException e) { Chris@24: cerr << "failed" << endl; Chris@24: cerr << "Import failed: " << e.what() << endl; Chris@24: return false; Chris@24: } Chris@20: } Chris@20: Chris@20: cerr << " done" << endl; Chris@20: return true; Chris@20: } Chris@20: Chris@20: void Chris@20: usage(char *name) Chris@20: { Chris@20: int s = 0; Chris@20: for (int i = 0; name[i]; ++i) if (name[i] == '/') s = i + 1; Chris@20: name = name + s; Chris@24: cerr << "Usage:" << endl; Chris@24: cerr << " " << name << " list" << endl; Chris@24: cerr << " " << name << " list-uris" << endl; Chris@24: cerr << " " << name << " show [ ...]" << endl; Chris@24: cerr << " " << name << " search " << endl; Chris@24: cerr << " " << name << " match " << endl; Chris@24: cerr << " " << name << " merge [ ...]" << endl; Chris@42: cerr << " " << name << " write" << endl; Chris@20: exit(-1); Chris@20: } Chris@20: Chris@20: static QList allComposers; Chris@20: static QMap > worksMap; Chris@20: Chris@20: void Chris@20: show(Composer *c) Chris@20: { Chris@20: cout << c->property("uri").value() << endl; Chris@20: cout << c->getSortName(true); Chris@20: QString d = c->getDisplayDates(); Chris@20: if (d != "") cout << " (" << d << ")"; Chris@20: if (!c->nationality().empty() || c->period() != "") { Chris@20: cout << " ["; Chris@20: bool first = true; Chris@20: foreach (QString n, c->nationality()) { Chris@20: if (!first) cout << "/"; Chris@20: cout << n; Chris@20: first = false; Chris@20: } Chris@20: if (c->period() != "") { Chris@20: if (!first) cout << ", "; Chris@20: cout << c->period(); Chris@20: } Chris@20: cout << "]"; Chris@20: } Chris@20: if (c->gender() != "") { Chris@20: cout << " *" << c->gender(); Chris@20: } Chris@20: if (!worksMap[c].empty()) { Chris@20: cout << " [" << worksMap[c].size() << " work(s)]"; Chris@20: } Chris@20: cout << endl; Chris@20: foreach (QString a, c->aliases()) { Chris@20: cout << " - " << a << endl; Chris@20: } Chris@20: if (c->remarks() != "") { Chris@20: cout << " " << c->remarks() << endl; Chris@20: } Chris@20: foreach (Document *d, c->pages()) { Chris@24: cout << d->siteName() << " -> " << d->uri() << endl; Chris@24: } Chris@24: foreach (Uri u, c->otherURIs()) { Chris@24: cout << "Same as " << u << endl; Chris@20: } Chris@20: } Chris@20: Chris@20: void Chris@20: showBrief(Composer *c) Chris@20: { Chris@20: cout << c->property("uri").value() << endl; Chris@20: cout << c->getSortName(false); Chris@20: QString d = c->getDisplayDates(); Chris@20: if (d != "") cout << " (" << d << ")"; Chris@20: if (!c->nationality().empty() || c->period() != "") { Chris@20: cout << " ["; Chris@20: bool first = true; Chris@20: foreach (QString n, c->nationality()) { Chris@20: if (!first) cout << "/"; Chris@20: cout << n; Chris@20: first = false; Chris@20: } Chris@20: if (c->period() != "") { Chris@20: if (!first) cout << " "; Chris@20: cout << c->period(); Chris@20: } Chris@20: cout << "]"; Chris@20: } Chris@20: if (c->gender() != "") { Chris@20: cout << " *" << c->gender(); Chris@20: } Chris@20: if (!worksMap[c].empty()) { Chris@20: cout << " [" << worksMap[c].size() << " work(s)]"; Chris@20: } Chris@20: cout << endl; Chris@20: } Chris@20: Chris@20: void Chris@20: listBrief(QList composers) Chris@20: { Chris@20: QMultiMap sorted; Chris@20: foreach (Composer *c, composers) { Chris@20: sorted.insert(c->getSortName(false), c); Chris@20: } Chris@20: foreach (Composer *c, sorted) { Chris@20: showBrief(c); Chris@20: } Chris@20: } Chris@20: Chris@20: void Chris@20: listUris(QList composers) Chris@20: { Chris@20: QMultiMap sorted; Chris@20: foreach (Composer *c, composers) { Chris@20: sorted.insert(c->property("uri").value(), c); Chris@20: } Chris@20: foreach (Uri uri, sorted.keys()) { Chris@20: cout << uri << endl; Chris@20: } Chris@20: } Chris@20: Chris@20: void Chris@20: showSearchResults(QMultiMap matches, int count) Chris@20: { Chris@20: int n = 0; Chris@20: for (QMultiMap::const_iterator i = matches.end(); Chris@20: i != matches.begin(); ) { Chris@20: --i; Chris@20: if (i.key() <= 0) continue; Chris@20: cout << endl; Chris@20: if (n == 0) { Chris@20: cout << "Best match:" << endl; Chris@20: } else if (n == 1) { Chris@20: cout << "Other candidate(s):" << endl; Chris@20: } Chris@20: cout << "[" << i.key() << "] "; Chris@20: if (n == 0) show(i.value()); Chris@20: else showBrief(i.value()); Chris@20: if (++n > count) break; Chris@20: } Chris@20: if (n == 0) cout << "No matches" << endl; Chris@20: cout << endl; Chris@20: } Chris@20: Chris@20: void Chris@20: search(QString typing) Chris@20: { Chris@28: cout << "Searching (quick) for: " << typing << endl; Chris@20: QMultiMap matches; Chris@20: foreach (Composer *c, allComposers) { Chris@28: float value = c->matchTypingQuick(typing); Chris@28: matches.insert(value, c); Chris@28: } Chris@28: showSearchResults(matches, 5); Chris@28: Chris@28: cout << "Searching (slow) for: " << typing << endl; Chris@28: matches.clear(); Chris@28: foreach (Composer *c, allComposers) { Chris@20: float value = c->matchTyping(typing); Chris@20: matches.insert(value, c); Chris@20: } Chris@20: showSearchResults(matches, 5); Chris@20: } Chris@20: Chris@20: void Chris@20: match(QString text) Chris@20: { Chris@20: cout << "Matching: " << text << endl; Chris@20: QMultiMap matches; Chris@20: QRegExp sre("[\\., -]+"); Chris@20: QStringList elements = text.toLower().split(sre, QString::SkipEmptyParts); Chris@20: foreach (Composer *c, allComposers) { Chris@20: float value = c->matchFuzzyName(elements); Chris@20: matches.insert(value, c); Chris@20: } Chris@20: showSearchResults(matches, 5); Chris@20: } Chris@20: Chris@24: QList Chris@24: matchWildcard(QString text) Chris@24: { Chris@24: if (!text.contains('/') && !text.contains('*')) { Chris@24: text = "*" + text + "*"; Chris@24: } Chris@24: QRegExp re(text, Qt::CaseInsensitive, QRegExp::Wildcard); Chris@24: QList results; Chris@24: foreach (Composer *c, allComposers) { Chris@24: if (re.exactMatch(c->property("uri").value().toString())) { Chris@24: results.push_back(c); Chris@24: } Chris@24: } Chris@24: return results; Chris@24: } Chris@24: Chris@24: Composer * Chris@24: matchSingle(QString text) Chris@24: { Chris@24: QList matches = matchWildcard(text); Chris@24: if (matches.empty()) { Chris@24: cerr << "matchSingle: No matches for " << text << endl; Chris@24: return 0; Chris@24: } else if (matches.size() > 1) { Chris@24: cerr << "matchSingle: Multiple matches for " << text << endl; Chris@24: return 0; Chris@24: } Chris@24: return matches[0]; Chris@24: } Chris@24: Chris@20: void Chris@20: showWildcard(QString text) Chris@20: { Chris@20: cout << "Showing URI or wildcard: " << text << endl; Chris@24: cout << endl; Chris@24: foreach (Composer *c, matchWildcard(text)) { Chris@24: show(c); Chris@24: cout << endl; Chris@20: } Chris@24: } Chris@24: Chris@24: void Chris@26: merge(Composer *target, QList sources, BasicStore *store) Chris@24: { Chris@24: cout << "Merging into this composer record:" << endl << endl; Chris@24: show(target); Chris@24: cout << endl << "... the following composer record(s):" << endl; Chris@24: foreach (Composer *c, sources) { Chris@24: cout << endl; Chris@24: show(c); Chris@24: target->mergeFrom(c); Chris@26: Chris@26: QSet works = worksMap[c]; Chris@26: foreach (Work *w, works) { Chris@26: w->composition()->setComposer(target); Chris@26: } Chris@26: worksMap[target].unite(works); Chris@26: worksMap.remove(c); Chris@26: Chris@26: delete c->birth(); Chris@26: delete c->death(); Chris@26: delete c; Chris@26: Chris@26: //!!! and composition events! Chris@24: } Chris@24: cout << endl << "Result after merging:" << endl << endl;; Chris@24: show(target); Chris@20: cout << endl; Chris@20: } Chris@20: Chris@20: int Chris@20: main(int argc, char **argv) Chris@20: { Chris@20: if (argc < 3) usage(argv[0]); Chris@20: QString inFileName = argv[1]; Chris@20: QString command = argv[2]; Chris@20: QStringList args; Chris@20: for (int i = 3; i < argc; ++i) { Chris@20: args.push_back(argv[i]); Chris@20: } Chris@20: Chris@20: BasicStore *store = new BasicStore(); Chris@20: store->setBaseUri(Uri("http://dbtune.org/classical/resource/")); Chris@28: ObjectLoader *loader = new ObjectLoader(store); Chris@31: // loader->setFollowPolicy(ObjectLoader::FollowObjectProperties); Chris@28: Chris@28: TypeMapping tm; Chris@20: Chris@22: TypeRegistrar::registerTypes(); Chris@28: TypeRegistrar::addMappings(store, &tm); Chris@28: Chris@28: loader->setTypeMapping(tm); Chris@20: Chris@20: if (!load(store, inFileName)) { Chris@20: cerr << "Failed to load data source" << endl; Chris@20: return 1; Chris@20: } Chris@20: Chris@20: cerr << "Imported RDF data, mapping to objects..."; Chris@31: QObjectList objects = loader->loadAll(); Chris@20: cerr << " done" << endl; Chris@20: Chris@28: delete loader; Chris@20: Chris@42: bool write = false, writeFull = false; Chris@31: if (command == "merge") { Chris@31: write = true; Chris@42: } else if (command == "write") { Chris@42: writeFull = true; Chris@31: } Chris@20: Chris@31: TransactionalStore *ts = 0; Chris@31: ObjectMapper *mapper = 0; Chris@31: Chris@31: if (write) { Chris@31: cerr << "Managing objects..."; Chris@31: ts = new TransactionalStore(store); Chris@31: mapper = new ObjectMapper(ts); Chris@31: mapper->setTypeMapping(tm); Chris@31: mapper->manage(objects); Chris@31: cerr << " done" << endl; Chris@31: } Chris@31: Chris@31: foreach (QObject *o, objects) { Chris@31: Composer *c = qobject_cast(o); Chris@31: if (c) allComposers.push_back(c); Chris@31: } Chris@31: Chris@31: QList works; Chris@31: foreach (QObject *o, objects) { Chris@31: Work *w = qobject_cast(o); Chris@31: if (w) works.push_back(w); Chris@31: } Chris@31: Chris@20: foreach (Work *w, works) { Chris@20: Composition *c = w->composition(); Chris@20: if (c) { Chris@20: Composer *cp = c->composer(); Chris@20: if (cp) worksMap[cp].insert(w); Chris@20: } Chris@20: } Chris@20: Chris@42: if (command == "write") { Chris@42: if (!args.empty()) usage(argv[0]); Chris@42: } else if (command == "list") { Chris@22: if (!args.empty()) usage(argv[0]); Chris@20: listBrief(allComposers); Chris@20: } else if (command == "list-uris") { Chris@22: if (!args.empty()) usage(argv[0]); Chris@20: listUris(allComposers); Chris@20: } else { Chris@20: if (args.empty()) usage(argv[0]); Chris@20: if (command == "show") { Chris@20: foreach (QString s, args) { Chris@20: showWildcard(s); Chris@20: } Chris@20: } else if (command == "search") { Chris@20: foreach (QString s, args) { Chris@20: search(s); Chris@20: } Chris@20: } else if (command == "match") { Chris@20: foreach (QString s, args) { Chris@20: match(s); Chris@20: } Chris@24: } else if (command == "merge") { Chris@24: if (args.size() < 2) usage(argv[0]); Chris@24: Composer *target = matchSingle(args[0]); Chris@24: if (!target) return 1; Chris@24: QList sources; Chris@24: for (int i = 1; i < args.size(); ++i) { Chris@24: Composer *c = matchSingle(args[i]); Chris@24: if (!c) return 1; Chris@24: sources.push_back(c); Chris@24: } Chris@26: merge(target, sources, store); Chris@20: } Chris@20: } Chris@20: Chris@24: if (write) { Chris@31: Chris@31: cerr << "Committing changes..."; Chris@31: mapper->commit(); Chris@24: cerr << " done" << endl; Chris@24: Chris@24: cerr << "Saving to file out.ttl..."; Chris@25: store->save("out.ttl"); Chris@24: cerr << " done" << endl; Chris@42: Chris@42: } else if (writeFull) { Chris@42: Chris@42: ObjectStorer *storer = new ObjectStorer(store); Chris@42: Chris@42: storer->setTypeMapping(tm); Chris@42: Chris@42: storer->setPropertyStorePolicy(ObjectStorer::StoreIfChanged); Chris@53: storer->setBlankNodePolicy(Dataquay::NeverUseBlankNodes); Chris@42: Chris@42: cerr << "Mapping results back to store..."; Chris@42: storer->setFollowPolicy(ObjectStorer::FollowObjectProperties); Chris@42: storer->store(objects); Chris@42: cerr << " done" << endl; Chris@42: Chris@42: cerr << "Saving to file out.ttl..."; Chris@42: store->save("out.ttl"); Chris@42: cerr << " done" << endl; Chris@42: Chris@42: delete storer; Chris@24: } Chris@24: Chris@31: delete mapper; Chris@31: delete ts; Chris@31: Chris@25: delete store; Chris@20: } Chris@20: