changeset 28:7d8a6167febb classical-rdf

* Begin new text entry / search widget & test utility for it * Improve typing matching methods * Update code to use ObjectLoader/Storer instead of old-style ObjectMapper
author Chris Cannam
date Mon, 01 Mar 2010 16:51:14 +0000
parents e8160a2d8b28
children 9729919e589c
files common/EditDistance.cpp common/EditDistance.h common/Matcher.cpp common/Matcher.h common/Objects.cpp common/Objects.h common/TypeRegistrar.cpp common/TypeRegistrar.h common/common.pro import/Import.cpp testapp/Loader.cpp utilities/composer/composer.cpp utilities/composer/composer.pro utilities/widgettest/widgettest.cpp utilities/widgettest/widgettest.pro widgets/TypingSelectWidget.cpp widgets/TypingSelectWidget.h
diffstat 17 files changed, 401 insertions(+), 141 deletions(-) [+]
line wrap: on
line diff
--- a/common/EditDistance.cpp	Fri Feb 26 18:15:33 2010 +0000
+++ b/common/EditDistance.cpp	Mon Mar 01 16:51:14 2010 +0000
@@ -24,28 +24,27 @@
     int *tmp = 0;
     
     for (int i = 0; i < bl; ++i) {
-	prevRow[i] = i * m_editPenalty;
+	prevRow[i] = i;
     }
 
     for (int j = 1; j < al; ++j) {
-	currRow[0] = j * m_editPenalty;
+	currRow[0] = j;
 	bool stillAcceptable = (threshold == 0);
 	for (int i = 1; i < bl; ++i) {
 	    if (a[j-1] == b[i-1]) {
 		currRow[i] = prevRow[i-1];
 	    } else {
 		int horizontal, vertical, diagonal;
-		horizontal = currRow[i-1] + m_editPenalty;
-		diagonal   = prevRow[i-1] + m_editPenalty;
-		if (i+1 == bl) vertical = prevRow[i] + m_suffixPenalty;
-		else vertical = prevRow[i] + m_editPenalty;
+		horizontal = currRow[i-1] + 1;
+		diagonal   = prevRow[i-1] + 1;
+		if (i+1 == bl) vertical = prevRow[i] + 1;
+		else vertical = prevRow[i] + 1;
 		currRow[i] =
 		    std::min(horizontal, std::min(vertical, diagonal));
 	    }
 	    if (m_transpositionMode == RestrictedTransposition) {
 		if (i > 1 && j > 1 && a[i-1]==b[j-2] && a[i-2]==b[j-1]) {
-		    currRow[i] = std::min(currRow[i],
-					  ppreRow[i-2] + m_editPenalty);
+		    currRow[i] = std::min(currRow[i], ppreRow[i-2] + 1);
 		}
 	    }
 	    if (threshold > 0 && currRow[i] <= threshold) {
@@ -60,10 +59,6 @@
     }
 
     int result = prevRow[bl-1];
-    if (m_normalise && m_editPenalty > 1) {
-	result = result / m_editPenalty;
-	if (result % m_editPenalty) ++result;
-    }
     return result;
 }
 
--- a/common/EditDistance.h	Fri Feb 26 18:15:33 2010 +0000
+++ b/common/EditDistance.h	Mon Mar 01 16:51:14 2010 +0000
@@ -16,14 +16,8 @@
 	RestrictedTransposition
     };
 
-    EditDistance(TranspositionMode tm = RestrictedTransposition,
-		 int editPenalty = 1,   //!!! probably better to lose this
-		 int suffixPenalty = 1, //!!! probably better to lose this
-		 bool normalise = true) :
-	m_transpositionMode(tm),
-	m_editPenalty(editPenalty),
-	m_suffixPenalty(suffixPenalty),
-	m_normalise(normalise) { }
+    EditDistance(TranspositionMode tm = RestrictedTransposition) :
+	m_transpositionMode(tm) { }
 
     int calculate(QString a, QString b, int threshold = 0);
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/common/Matcher.cpp	Mon Mar 01 16:51:14 2010 +0000
@@ -0,0 +1,59 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
+
+#include "Matcher.h"
+#include "Objects.h"
+
+#include <QMultiMap>
+
+using namespace Dataquay;
+
+namespace ClassicalData {
+
+UriList
+ComposerTypingQuickMatcher::match(QString text, int maxResults) const
+{
+    UriList results;
+
+    QMultiMap<float, Composer *> matches;
+    foreach (Composer *c, m_composers) {
+        float value = c->matchTypingQuick(text);
+        matches.insert(value, c);
+    }
+    
+    int n = 0;
+    for (QMultiMap<float, Composer *>::const_iterator i = matches.end();
+         i != matches.begin(); ) {
+        --i;
+        if (i.key() <= 0) continue;
+	results.push_back(i.value()->property("uri").value<Uri>());
+        if (++n > maxResults) break;
+    }
+
+    return results;
+}
+
+UriList
+ComposerTypingThoroughMatcher::match(QString text, int maxResults) const
+{
+    UriList results;
+
+    QMultiMap<float, Composer *> matches;
+    foreach (Composer *c, m_composers) {
+        float value = c->matchTyping(text);
+        matches.insert(value, c);
+    }
+    
+    int n = 0;
+    for (QMultiMap<float, Composer *>::const_iterator i = matches.end();
+         i != matches.begin(); ) {
+        --i;
+        if (i.key() <= 0) continue;
+	results.push_back(i.value()->property("uri").value<Uri>());
+        if (++n > maxResults) break;
+    }
+
+    return results;
+}
+
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/common/Matcher.h	Mon Mar 01 16:51:14 2010 +0000
@@ -0,0 +1,47 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
+
+#ifndef _CLASSICAL_DATA_MATCHER_H_
+#define _CLASSICAL_DATA_MATCHER_H_
+
+#include <dataquay/Uri.h>
+
+namespace ClassicalData {
+
+class Composer;
+
+class Matcher
+{
+public:
+    virtual Dataquay::UriList match(QString text, int maxResults) const = 0;
+};
+
+class ComposerTypingQuickMatcher : public Matcher
+{
+public:
+    ComposerTypingQuickMatcher(QList<Composer *> cl) :
+	m_composers(cl) { }
+    
+    virtual Dataquay::UriList match(QString text, int maxResults) const;
+
+private:
+    QList<Composer *> m_composers;
+};
+
+class ComposerTypingThoroughMatcher : public Matcher
+{
+public:
+    ComposerTypingThoroughMatcher(QList<Composer *> cl) :
+	m_composers(cl) { }
+    
+    virtual Dataquay::UriList match(QString text, int maxResults) const;
+
+private:
+    QList<Composer *> m_composers;
+};
+
+
+
+}
+
+#endif
+
--- a/common/Objects.cpp	Fri Feb 26 18:15:33 2010 +0000
+++ b/common/Objects.cpp	Mon Mar 01 16:51:14 2010 +0000
@@ -300,7 +300,7 @@
     cacheNames();
     int fameBonus = m_pages.size();
     
-    EditDistance ed(EditDistance::RestrictedTransposition, 1, 1, true);
+    EditDistance ed(EditDistance::RestrictedTransposition);
     
     int score = 0;
     bool haveSurname = false;
@@ -472,66 +472,114 @@
 float
 Composer::matchTyping(QString t) const
 {
+    return doMatchTyping(t, false);
+}
+
+float
+Composer::matchTypingQuick(QString t) const
+{
+    return doMatchTyping(t, true);
+}
+
+float
+Composer::doMatchTyping(QString t, bool quick) const
+{
     if (t == "") return 0;
 
     cacheNames();
-    float fameBonus = m_pages.size() / 40.f;
+    float fameBonus = m_pages.size() / 400.f;
     
-    QString n = name().replace(",", "").toLower();
-    t = t.replace(",", "").toLower();
+    QString n = name().toLower();
+    t = t.toLower();
 
     if (n == t) return 1.f + fameBonus;
     if (n.startsWith(t)) return 0.8f + fameBonus;
+
+    QSet<QString> sl;
+    QSet<QString> nl;
+    foreach (QString s, m_surnameElements) {
+        sl.insert(s.toLower());
+        nl.insert(s.toLower());
+    }
+    foreach (QString s, m_forenameElements) {
+        nl.insert(s.toLower());
+    }
+    if (!quick) {
+        foreach (QString s, m_otherElements) {
+            nl.insert(s.toLower());
+        }
+        foreach (QString s, m_connectiveElements) {
+            nl.insert(s.toLower());
+        }
+    }
+
+    static QRegExp sre("[\\., -]+");
+    QStringList tl = t.split(sre, QString::SkipEmptyParts);
     
     float score = 0.f;
 
-    static QRegExp sre("[\\., -]+");
-    QStringList nl = n.split(sre, QString::SkipEmptyParts);
-    QStringList tl = t.split(sre, QString::SkipEmptyParts);
     if (nl.empty() || tl.empty()) return 0.f;
     
     int unmatched = 0;
+
     for (int i = 0; i < tl.size(); ++i) {
-        int ni = 0;
-        for (ni = 0; ni < nl.size(); ++ni) {
-            if (tl[i] == nl[ni]) {
-                if (tl[i].length() > 1) {
-                    score += 0.2;
+
+        QString tel = tl[i];
+        float component = 0.f;
+        float max = 0.f;
+
+        for (QSet<QString>::const_iterator ni = nl.begin();
+             ni != nl.end(); ++ni) {
+
+            QString nel = ni->toLower();
+
+            if (tel == nel) {
+                if (tel.length() > 1) {
+                    component = 0.2;
                 } else {
-                    score += 0.1;
+                    component = 0.1;
                 }
-            } else if (nl[ni].startsWith(tl[i])) {
-                score += 0.1;
-            } else if (nl[ni].startsWith(tl[i][0])) {
-                score += 0.03;
-            } else {
-                continue;
+                if (sl.contains(nel)) component *= 1.5;
+                goto calculated;
             }
-            break;
+
+            if (nel.startsWith(tel)) {
+                component = 0.1;
+                if (sl.contains(nel)) component *= 1.5;
+                goto calculated;
+            }
+
+            if (!quick) {
+                int minlen = std::min(nel.length(), tel.length());
+                if (minlen > 3) {
+                    EditDistance ed(EditDistance::RestrictedTransposition);
+                    int dist = calculateThresholdedDistance
+                        (ed, nel.left(minlen), tel.left(minlen));
+                    if (dist >= 0) {
+                        component = 0.08 - dist * 0.01;
+                        if (sl.contains(nel)) component *= 1.5;
+                    }
+                }
+                if (component > 0.f) goto calculated;
+            }
+
+            if (nel.startsWith(tel[0])) {
+                component += 0.02;
+            }
+
+        calculated:
+            if (component > max) max = component;
         }
-        if (ni == nl.size()+1) {
-            ++unmatched;
-        }
-    }
-    if (nl[0] == tl[0]) {
-        score += 0.2;
-    }
-    if (score > unmatched * 0.1) {
-        score -= unmatched * 0.1;
-    } else if (score > 0.1) {
-        score = 0.1;
+
+        score += max;
     }
 
-    float fuzzyScore = matchFuzzyName(t);
-    if (t.contains(" ") && fuzzyScore >= 1.f) score += 0.4;
-
-    if (score == 0.f) {
-        EditDistance ed(EditDistance::RestrictedTransposition, 1, 1, true);
-        if (nl.length() > tl.length()) {
-            int dist = calculateThresholdedDistance(ed, t, n.left(t.length()));
-            if (dist >= 0 && dist < 3) score += (3 - dist) * 0.05;
-        } else {
-            score += fuzzyScore / 10.f;
+    if (!quick) {
+        if (t.contains(" ")) {
+            float fuzzyScore = matchFuzzyName(t);
+            if (fuzzyScore >= 0.4f) {
+                score += fuzzyScore / 3.f;
+            }
         }
     }
 
--- a/common/Objects.h	Fri Feb 26 18:15:33 2010 +0000
+++ b/common/Objects.h	Mon Mar 01 16:51:14 2010 +0000
@@ -422,11 +422,21 @@
     /**
      * 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.
+     * 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
@@ -452,6 +462,7 @@
     Death *m_death;
 
     void cacheNames() const;
+    float doMatchTyping(QString, bool) const;
     mutable bool m_namesCached;
     mutable QString m_surname;
     mutable QString m_forenames;
--- a/common/TypeRegistrar.cpp	Fri Feb 26 18:15:33 2010 +0000
+++ b/common/TypeRegistrar.cpp	Mon Mar 01 16:51:14 2010 +0000
@@ -4,7 +4,7 @@
 #include "Objects.h"
 
 #include <dataquay/BasicStore.h>
-#include <dataquay/objectmapper/ObjectMapper.h>
+#include <dataquay/objectmapper/TypeMapping.h>
 #include <dataquay/objectmapper/ObjectBuilder.h>
 #include <dataquay/objectmapper/ContainerBuilder.h>
 
@@ -100,7 +100,7 @@
 }
 
 void
-TypeRegistrar::addMappings(BasicStore *store, ObjectMapper *mapper)
+TypeRegistrar::addMappings(BasicStore *store, TypeMapping *mapping)
 {
     if (store) {
 	store->addPrefix("foaf", Uri("http://xmlns.com/foaf/0.1/"));
@@ -115,17 +115,17 @@
 	store->addPrefix("sim", Uri("http://purl.org/ontology/similarity/"));
     }
 
-    if (mapper) {
-	mapper->setObjectTypePrefix(Uri("http://dbtune.org/classical/resource/"));
-	mapper->setPropertyPrefix(Uri("http://dbtune.org/classical/resource/vocab/"));
-	mapper->setRelationshipPrefix(Uri("http://dbtune.org/classical/resource/vocab/relationship/"));
+    if (mapping) {
+	mapping->setObjectTypePrefix(Uri("http://dbtune.org/classical/resource/"));
+	mapping->setPropertyPrefix(Uri("http://dbtune.org/classical/resource/vocab/"));
+	mapping->setRelationshipPrefix(Uri("http://dbtune.org/classical/resource/vocab/relationship/"));
     }
 
     if (store) {
-	store->addPrefix("type", mapper->getObjectTypePrefix());
-	store->addPrefix("classical", Uri(mapper->getObjectTypePrefix().toString() + "type/"));
-	store->addPrefix("property", mapper->getPropertyPrefix());
-	store->addPrefix("rel", mapper->getRelationshipPrefix());
+	store->addPrefix("type", mapping->getObjectTypePrefix());
+	store->addPrefix("classical", Uri(mapping->getObjectTypePrefix().toString() + "type/"));
+	store->addPrefix("property", mapping->getPropertyPrefix());
+	store->addPrefix("rel", mapping->getRelationshipPrefix());
 
 	store->add(Triple("classical:Composer", "a", store->expand("owl:Class")));
 	store->add(Triple("classical:Composer", "rdfs:subClassOf", store->expand("mo:MusicArtist")));
@@ -137,49 +137,49 @@
 	store->add(Triple("property:death", "rdfs:subPropertyOf", store->expand("bio:event")));
     }
 
-    if (mapper) {
-	mapper->addTypeMapping("ClassicalData::Composer", "classical:Composer");
-	mapper->addPropertyMapping("ClassicalData::Composer", "pages", "foaf:page");
-	mapper->addPropertyMapping("ClassicalData::Composer", "name", "foaf:name");
-	mapper->addPropertyMapping("ClassicalData::Composer", "aliases", "dbv:alias");
-	mapper->addPropertyMapping("ClassicalData::Composer", "birth", "property:birth");
-	mapper->addPropertyMapping("ClassicalData::Composer", "death", "property:death");
-	mapper->addPropertyMapping("ClassicalData::Composer", "geonameURIs", "foaf:based_near");
-	mapper->addPropertyMapping("ClassicalData::Composer", "otherURIs", "owl:sameAs");
-	mapper->addPropertyMapping("ClassicalData::Composer", "gender", "foaf:gender");
+    if (mapping) {
+	mapping->addTypeMapping("ClassicalData::Composer", store->expand("classical:Composer"));
+	mapping->addPropertyMapping("ClassicalData::Composer", "pages", store->expand("foaf:page"));
+	mapping->addPropertyMapping("ClassicalData::Composer", "name", store->expand("foaf:name"));
+	mapping->addPropertyMapping("ClassicalData::Composer", "aliases", store->expand("dbv:alias"));
+	mapping->addPropertyMapping("ClassicalData::Composer", "birth", store->expand("property:birth"));
+	mapping->addPropertyMapping("ClassicalData::Composer", "death", store->expand("property:death"));
+	mapping->addPropertyMapping("ClassicalData::Composer", "geonameURIs", store->expand("foaf:based_near"));
+	mapping->addPropertyMapping("ClassicalData::Composer", "otherURIs", store->expand("owl:sameAs"));
+	mapping->addPropertyMapping("ClassicalData::Composer", "gender", store->expand("foaf:gender"));
 
-	mapper->addTypeMapping("ClassicalData::Birth", "bio:Birth");
-	mapper->addTypeMapping("ClassicalData::Death", "bio:Death");
-	mapper->addTypeUriPrefixMapping("ClassicalData::Birth", ":event/");
-	mapper->addTypeUriPrefixMapping("ClassicalData::Death", ":event/");
-	mapper->addPropertyMapping("ClassicalData::Birth", "year", "dc:date");
-	mapper->addPropertyMapping("ClassicalData::Death", "year", "dc:date");
-	mapper->addPropertyMapping("ClassicalData::Composition", "year", "dc:date");
-	mapper->addPropertyMapping("ClassicalData::Birth", "place", "bio:place");
-	mapper->addPropertyMapping("ClassicalData::Death", "place", "bio:place");
-	mapper->addPropertyMapping("ClassicalData::Composition", "place", "bio:place");
+	mapping->addTypeMapping("ClassicalData::Birth", store->expand("bio:Birth"));
+	mapping->addTypeMapping("ClassicalData::Death", store->expand("bio:Death"));
+	mapping->addTypeUriPrefixMapping("ClassicalData::Birth", store->expand(":event/"));
+	mapping->addTypeUriPrefixMapping("ClassicalData::Death", store->expand(":event/"));
+	mapping->addPropertyMapping("ClassicalData::Birth", "year", store->expand("dc:date"));
+	mapping->addPropertyMapping("ClassicalData::Death", "year", store->expand("dc:date"));
+	mapping->addPropertyMapping("ClassicalData::Composition", "year", store->expand("dc:date"));
+	mapping->addPropertyMapping("ClassicalData::Birth", "place", store->expand("bio:place"));
+	mapping->addPropertyMapping("ClassicalData::Death", "place", store->expand("bio:place"));
+	mapping->addPropertyMapping("ClassicalData::Composition", "place", store->expand("bio:place"));
 
-	mapper->addTypeMapping("ClassicalData::Document", "foaf:Document");
-	mapper->addPropertyMapping("ClassicalData::Document", "topic", "foaf:primaryTopic");
+	mapping->addTypeMapping("ClassicalData::Document", store->expand("foaf:Document"));
+	mapping->addPropertyMapping("ClassicalData::Document", "topic", store->expand("foaf:primaryTopic"));
 
-	mapper->addTypeMapping("ClassicalData::Work", "mo:MusicalWork");
+	mapping->addTypeMapping("ClassicalData::Work", store->expand("mo:MusicalWork"));
 
-	mapper->addPropertyMapping("ClassicalData::Work", "composition", "mo:composed_in");
-	mapper->addPropertyMapping("ClassicalData::Work", "opus", "mo:opus");
-	mapper->addPropertyMapping("ClassicalData::Work", "catalogue", "mo:catalogue");
-	mapper->addPropertyMapping("ClassicalData::Work", "number", "mo:number");
-	mapper->addPropertyMapping("ClassicalData::Work", "partOf", "dc:isPartOf");
-	mapper->addPropertyMapping("ClassicalData::Work", "parts", "dc:hasPart");
-	mapper->addPropertyMapping("ClassicalData::Work", "pages", "foaf:page");
-	mapper->addPropertyMapping("ClassicalData::Work", "forms", "property:form");
-	mapper->addPropertyMapping("ClassicalData::Work", "key", "mo:key");
-	mapper->addPropertyMapping("ClassicalData::Work", "aliases", "dbv:alias");
-	mapper->addPropertyMapping("ClassicalData::Work", "name", "dc:title");
+	mapping->addPropertyMapping("ClassicalData::Work", "composition", store->expand("mo:composed_in"));
+	mapping->addPropertyMapping("ClassicalData::Work", "opus", store->expand("mo:opus"));
+	mapping->addPropertyMapping("ClassicalData::Work", "catalogue", store->expand("mo:catalogue"));
+	mapping->addPropertyMapping("ClassicalData::Work", "number", store->expand("mo:number"));
+	mapping->addPropertyMapping("ClassicalData::Work", "partOf", store->expand("dc:isPartOf"));
+	mapping->addPropertyMapping("ClassicalData::Work", "parts", store->expand("dc:hasPart"));
+	mapping->addPropertyMapping("ClassicalData::Work", "pages", store->expand("foaf:page"));
+	mapping->addPropertyMapping("ClassicalData::Work", "forms", store->expand("property:form"));
+	mapping->addPropertyMapping("ClassicalData::Work", "key", store->expand("mo:key"));
+	mapping->addPropertyMapping("ClassicalData::Work", "aliases", store->expand("dbv:alias"));
+	mapping->addPropertyMapping("ClassicalData::Work", "name", store->expand("dc:title"));
 
-	mapper->addTypeMapping("ClassicalData::Composition", "mo:Composition");
-	mapper->addTypeUriPrefixMapping("ClassicalData::Composition", ":event/");
-	mapper->addPropertyMapping("ClassicalData::Composition", "composer", "mo:composer");
-	mapper->addPropertyMapping("ClassicalData::Composition", "works", "mo:produced_work");
+	mapping->addTypeMapping("ClassicalData::Composition", store->expand("mo:Composition"));
+	mapping->addTypeUriPrefixMapping("ClassicalData::Composition", store->expand(":event/"));
+	mapping->addPropertyMapping("ClassicalData::Composition", "composer", store->expand("mo:composer"));
+	mapping->addPropertyMapping("ClassicalData::Composition", "works", store->expand("mo:produced_work"));
     }
 }
 
--- a/common/TypeRegistrar.h	Fri Feb 26 18:15:33 2010 +0000
+++ b/common/TypeRegistrar.h	Mon Mar 01 16:51:14 2010 +0000
@@ -4,7 +4,7 @@
 
 namespace Dataquay {
     class BasicStore;
-    class ObjectMapper;
+    class TypeMapping;
 }
 
 namespace ClassicalData
@@ -14,7 +14,7 @@
 {
 public:
     static void registerTypes();
-    static void addMappings(Dataquay::BasicStore *, Dataquay::ObjectMapper *);
+    static void addMappings(Dataquay::BasicStore *, Dataquay::TypeMapping *);
 };
 
 }
--- a/common/common.pro	Fri Feb 26 18:15:33 2010 +0000
+++ b/common/common.pro	Mon Mar 01 16:51:14 2010 +0000
@@ -5,6 +5,6 @@
 
 CONFIG += staticlib
 
-HEADERS += EditDistance.h Objects.h TypeRegistrar.h
-SOURCES += EditDistance.cpp Objects.cpp TypeRegistrar.cpp
+HEADERS += EditDistance.h Objects.h Matcher.h TypeRegistrar.h
+SOURCES += EditDistance.cpp Objects.cpp Matcher.cpp TypeRegistrar.cpp
 
--- a/import/Import.cpp	Fri Feb 26 18:15:33 2010 +0000
+++ b/import/Import.cpp	Mon Mar 01 16:51:14 2010 +0000
@@ -4,8 +4,10 @@
 
 #include <dataquay/BasicStore.h>
 #include <dataquay/RDFException.h>
-#include <dataquay/objectmapper/ObjectMapper.h>
+#include <dataquay/objectmapper/ObjectStorer.h>
+#include <dataquay/objectmapper/ObjectLoader.h>
 #include <dataquay/objectmapper/ObjectBuilder.h>
+#include <dataquay/objectmapper/TypeMapping.h>
 #include <dataquay/objectmapper/ContainerBuilder.h>
 
 #include "ImportClassicalComposersOrg.h"
@@ -489,19 +491,21 @@
 	<HobokenImporter>("ClassicalData::HobokenImporter*");
 
     BasicStore *store = BasicStore::load(QUrl("file:importers.ttl"));
-    ObjectMapper mapper(store);
-    QObject *parentObject = mapper.loadAllObjects(new QObject());
+    ObjectLoader loader(store);
+    QObject *parentObject = loader.loadAllObjects(new QObject());
     
     BasicStore *outstore = new BasicStore();
     outstore->setBaseUri(Uri("http://dbtune.org/classical/resource/"));
-    ObjectMapper outmapper(outstore);
+    ObjectStorer storer(outstore);
+    TypeMapping tm;
 
     TypeRegistrar::registerTypes();
-    TypeRegistrar::addMappings(outstore, &outmapper);
+    TypeRegistrar::addMappings(outstore, &tm);
 
-    outmapper.setPropertyStorePolicy(ObjectMapper::StoreIfChanged);
-    outmapper.setObjectStorePolicy(ObjectMapper::StoreAllObjects);
-    outmapper.setBlankNodePolicy(ObjectMapper::NoBlankNodes);
+    storer.setTypeMapping(tm);
+    storer.setPropertyStorePolicy(ObjectStorer::StoreIfChanged);
+    storer.setObjectStorePolicy(ObjectStorer::StoreAllObjects);
+    storer.setBlankNodePolicy(ObjectStorer::NoBlankNodes);
 
     QList<Importer *> importers = parentObject->findChildren<Importer *>();
     std::cerr << "have " << importers.size() << " importers" << std::endl;
@@ -614,7 +618,7 @@
     }
 
     try {
-        outmapper.storeAllObjects(toStore);
+        storer.storeAllObjects(toStore);
         
     } catch (RDFException e) {
         std::cerr << "Caught RDF exception: " << e.what() << std::endl;
--- a/testapp/Loader.cpp	Fri Feb 26 18:15:33 2010 +0000
+++ b/testapp/Loader.cpp	Mon Mar 01 16:51:14 2010 +0000
@@ -5,7 +5,8 @@
 #include "TypeRegistrar.h"
 
 #include <dataquay/BasicStore.h>
-#include <dataquay/objectmapper/ObjectMapper.h>
+#include <dataquay/objectmapper/ObjectLoader.h>
+#include <dataquay/objectmapper/TypeMapping.h>
 #include <dataquay/Debug.h>
 
 #include <QTemporaryFile>
@@ -45,10 +46,13 @@
 {
     BasicStore *store = new BasicStore();
     store->setBaseUri(Uri("http://dbtune.org/classical/resource/"));
-    ObjectMapper *mapper = new ObjectMapper(store);
+    ObjectLoader *loader = new ObjectLoader(store);
+    TypeMapping tm;
 
     TypeRegistrar::registerTypes();
-    TypeRegistrar::addMappings(store, mapper);
+    TypeRegistrar::addMappings(store, &tm);
+
+    loader->setTypeMapping(tm);
 
     if (!load(store, ":data.ntriples")) {
 	std::cerr << "Failed to unpack and load resource" << std::endl;
@@ -57,9 +61,9 @@
 
     std::cerr << "imported, mapping..." << std::endl;
 
-    QObject *root = mapper->loadAllObjects(0);
+    QObject *root = loader->loadAllObjects(0);
 
-    delete mapper;
+    delete loader;
     delete store;
 
     QObjectList composers;
--- a/utilities/composer/composer.cpp	Fri Feb 26 18:15:33 2010 +0000
+++ b/utilities/composer/composer.cpp	Mon Mar 01 16:51:14 2010 +0000
@@ -5,7 +5,9 @@
 
 #include <dataquay/BasicStore.h>
 #include <dataquay/RDFException.h>
-#include <dataquay/objectmapper/ObjectMapper.h>
+#include <dataquay/objectmapper/ObjectLoader.h>
+#include <dataquay/objectmapper/ObjectStorer.h>
+#include <dataquay/objectmapper/TypeMapping.h>
 #include <dataquay/Debug.h>
 
 #include <QMultiMap>
@@ -192,9 +194,17 @@
 void
 search(QString typing)
 {
-    cout << "Searching for: " << typing << endl;
+    cout << "Searching (quick) for: " << typing << endl;
     QMultiMap<float, Composer *> matches;
     foreach (Composer *c, allComposers) {
+        float value = c->matchTypingQuick(typing);
+        matches.insert(value, c);
+    }
+    showSearchResults(matches, 5);
+
+    cout << "Searching (slow) for: " << typing << endl;
+    matches.clear();
+    foreach (Composer *c, allComposers) {
         float value = c->matchTyping(typing);
         matches.insert(value, c);
     }
@@ -320,10 +330,14 @@
 
     BasicStore *store = new BasicStore();
     store->setBaseUri(Uri("http://dbtune.org/classical/resource/"));
-    ObjectMapper *mapper = new ObjectMapper(store);
+    ObjectLoader *loader = new ObjectLoader(store);
+
+    TypeMapping tm;
 
     TypeRegistrar::registerTypes();
-    TypeRegistrar::addMappings(store, mapper);
+    TypeRegistrar::addMappings(store, &tm);
+
+    loader->setTypeMapping(tm);
 
     if (!load(store, inFileName)) {
 	cerr << "Failed to load data source" << endl;
@@ -331,10 +345,10 @@
     }
 
     cerr << "Imported RDF data, mapping to objects...";
-    QObject *root = mapper->loadAllObjects(0);
+    QObject *root = loader->loadAllObjects(0);
     cerr << " done" << endl;
 
-    delete mapper;
+    delete loader;
 
     allComposers = root->findChildren<Composer *>();
 
@@ -385,23 +399,23 @@
     }
         
     if (write) {
-        ObjectMapper *outmapper = new ObjectMapper(store);
+        ObjectStorer *storer = new ObjectStorer(store);
 
-        TypeRegistrar::addMappings(0, outmapper);
+        storer->setTypeMapping(tm);
 
-        outmapper->setPropertyStorePolicy(ObjectMapper::StoreIfChanged);
-        outmapper->setObjectStorePolicy(ObjectMapper::StoreAllObjects);
-        outmapper->setBlankNodePolicy(ObjectMapper::NoBlankNodes);
+        storer->setPropertyStorePolicy(ObjectStorer::StoreIfChanged);
+        storer->setObjectStorePolicy(ObjectStorer::StoreAllObjects);
+        storer->setBlankNodePolicy(ObjectStorer::NoBlankNodes);
 
         cerr << "Mapping results back to store...";
-        outmapper->storeAllObjects(root->children());
+        storer->storeAllObjects(root->children());
         cerr << " done" << endl;
 
         cerr << "Saving to file out.ttl...";
         store->save("out.ttl");
         cerr << " done" << endl;
         
-        delete outmapper;
+        delete storer;
     }
 
     delete store;
--- a/utilities/composer/composer.pro	Fri Feb 26 18:15:33 2010 +0000
+++ b/utilities/composer/composer.pro	Mon Mar 01 16:51:14 2010 +0000
@@ -1,5 +1,6 @@
 TEMPLATE = app
 TARGET = composer
+QT -= gui network xml
 
 load(../../platform.prf)
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/utilities/widgettest/widgettest.cpp	Mon Mar 01 16:51:14 2010 +0000
@@ -0,0 +1,20 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
+
+#include "TypingSelectWidget.h"
+
+#include <QApplication>
+
+using namespace ClassicalData;
+
+int
+main(int argc, char **argv)
+{
+    QApplication app(argc, argv);
+
+    TypingSelectWidget *w = new TypingSelectWidget();
+    
+    w->show();
+    
+    app.exec();
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/utilities/widgettest/widgettest.pro	Mon Mar 01 16:51:14 2010 +0000
@@ -0,0 +1,11 @@
+TEMPLATE = app
+TARGET = widgettest
+
+load(../../platform.prf)
+
+PRE_TARGETDEPS += ../../common/libcommon.a ../../widgets/libwidgets.a
+
+LIBS += ../../widgets/libwidgets.a ../../common/libcommon.a ../../../turbot/dataquay/libdataquay.a ../../../turbot/ext/libext.a
+
+HEADERS += widgettest.h
+SOURCES += widgettest.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/widgets/TypingSelectWidget.cpp	Mon Mar 01 16:51:14 2010 +0000
@@ -0,0 +1,23 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
+
+#include "TypingSelectWidget.h"
+
+#include <QGridLayout>
+#include <QLineEdit>
+
+namespace ClassicalData {
+
+TypingSelectWidget::TypingSelectWidget(QWidget *parent) :
+    QWidget(parent)
+{
+    QGridLayout *layout = new QGridLayout;
+    setLayout(layout);
+
+    QLineEdit *editor = new QLineEdit;
+    layout->addWidget(editor);
+
+}
+
+
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/widgets/TypingSelectWidget.h	Mon Mar 01 16:51:14 2010 +0000
@@ -0,0 +1,29 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
+
+#ifndef _TYPING_SELECT_WIDGET_H_
+#define _TYPING_SELECT_WIDGET_H_
+
+#include <QWidget>
+#include <QList>
+
+namespace ClassicalData
+{
+
+class Matcher;
+
+class TypingSelectWidget : public QWidget
+{
+public:
+    TypingSelectWidget(QWidget *parent = 0);
+    
+    void setMatchers(QList<Matcher *> m) { m_matchers = m; }
+
+private:
+    QList<Matcher *> m_matchers;
+};
+
+}
+
+#endif
+
+