1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
2 
3 /*
4     Dataquay
5 
6     A C++/Qt library for simple RDF datastore management.
7     Copyright 2009-2012 Chris Cannam.
8 
9     Permission is hereby granted, free of charge, to any person
10     obtaining a copy of this software and associated documentation
11     files (the "Software"), to deal in the Software without
12     restriction, including without limitation the rights to use, copy,
13     modify, merge, publish, distribute, sublicense, and/or sell copies
14     of the Software, and to permit persons to whom the Software is
15     furnished to do so, subject to the following conditions:
16 
17     The above copyright notice and this permission notice shall be
18     included in all copies or substantial portions of the Software.
19 
20     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21     EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22     MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23     NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
24     ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
25     CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26     WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27 
28     Except as contained in this notice, the name of Chris Cannam
29     shall not be used in advertising or otherwise to promote the sale,
30     use or other dealings in this Software without prior written
31     authorization.
32 */
33 
34 #include "Uri.h"
35 
36 #include <QDataStream>
37 #include <QTextStream>
38 #include <QVariant>
39 #include <QMutex>
40 #include <QHash>
41 #include <QRegExp>
42 
43 #include <iostream>
44 
45 #ifndef NDEBUG
46 #include <QRegExp>
47 #endif
48 
49 #include "Debug.h"
50 #include "RDFException.h"
51 
52 namespace Dataquay
53 {
54 
55 class UriRegistrar {
56 public:
57     static UriRegistrar *instance() {
58         static UriRegistrar *inst = 0;
59         static QMutex mutex;
60         mutex.lock();
61         if (inst == 0) inst = new UriRegistrar();
62         mutex.unlock();
63         return inst;
64     }
65 
66     int getType() const { return type; }
67     QString getName() const { return name; }
68 
69 private:
70     QString name;
71     int type;
72 
73     UriRegistrar() : name("Dataquay::Uri") {
74         DQ_DEBUG << "UriRegistrar: registering Dataquay::Uri" << endl;
75         QByteArray bname = name.toLatin1();
76         type = qRegisterMetaType<Uri>(bname.data());
77         qRegisterMetaTypeStreamOperators<Uri>(bname.data());
78     }
79 };
80 
81 QString
82 Uri::metaTypeName()
83 {
84     return UriRegistrar::instance()->getName();
85 }
86 
87 int
88 Uri::metaTypeId()
89 {
90     int t = UriRegistrar::instance()->getType();
91     if (t <= 0) {
92 	DQ_DEBUG << "Uri::metaTypeId: No meta type available -- did static registration fail?" << endl;
93 	return 0;
94     }
95     return t;
96 }
97 
98 void
99 Uri::checkComplete()
100 {
101     if (!canBeComplete(m_uri)) { // may modify m_uri
102         throw RDFIncompleteURI
103             ("Uri::Uri: Given string is not a complete absolute URI", m_uri);
104     }
105 }
106 
107 bool
108 Uri::isCompleteUri(QString s)
109 {
110     QString s0(s);
111     return canBeComplete(s0);
112 }
113 
114 bool
115 Uri::canBeComplete(QString &s)
116 {
117     // An RDF URI must be absolute, with a few special cases
118 
119     if (s == "a") {
120 
121         s = rdfTypeUri().toString();
122         return true;
123 
124     } else if (s.isEmpty() || s[0] == '#' || s[0] == ':') {
125 
126         return false;
127 
128     } else {
129 
130         // look for scheme (and we know from the above that the first
131         // char is not ':')
132 
133         bool hasScheme = false;
134 
135         for (int i = 0; i < s.length(); ++i) {
136             if (s[i] == QChar(':')) {
137                 if (s[i+1] != QChar('/')) break;
138                 if (s[i+2] != QChar('/')) break;
139                 hasScheme = true;
140                 break;
141             }
142             if (!s[i].isLetter()) return false;
143         }
144 
145         if (hasScheme) return true;
146 
147         // we are generous with file URIs: if we get file:x, convert
148         // it to file://x
149         if (s.startsWith("file:")) {
150             s = "file://" + s.right(s.length() - 5);
151             return true;
152         } else {
153             return false;
154         }
155     }
156 }
157 
158 QString
159 Uri::scheme() const
160 {
161     int index = m_uri.indexOf(':');
162     if (index < 0) return "";
163     return m_uri.left(index);
164 }
165 
166 bool
167 Uri::operator==(const Uri &u) const
168 {
169     const QString &other = u.m_uri;
170     int len = length();
171     if (len != other.length()) return false;
172     for (int i = len - 1; i >= 0; --i) {
173 	if (m_uri.at(i) != other.at(i)) return false;
174     }
175     return true;
176 }
177 
178 bool
179 Uri::hasUriType(const QVariant &v)
180 {
181     return (v.type() == QVariant::UserType && v.userType() == metaTypeId());
182 }
183 
184 Uri
185 Uri::rdfTypeUri()
186 {
187     return Uri("http://www.w3.org/1999/02/22-rdf-syntax-ns#type");
188 }
189 
190 QDataStream &operator<<(QDataStream &out, const Uri &u) {
191     return out << u.toString();
192 }
193 
194 QDataStream &operator>>(QDataStream &in, Uri &u) {
195     QString s;
196     in >> s;
197     u = Uri(s);
198     return in;
199 }
200 
201 std::ostream &operator<<(std::ostream &out, const Uri &u) {
202     return out << u.toString().toLocal8Bit().data();
203 }
204 
205 QTextStream &operator<<(QTextStream &out, const Uri &u) {
206     return out << u.toString();
207 }
208 
209 }
210 
211 unsigned int qHash(const Dataquay::Uri &u)
212 {
213     return qHash(u.toString());
214 }
215 
216 
217 
218