Add support for optional RDF properties
[qtcontacts-tracker:hasselmms-qtcontacts-tracker.git] / src / dao / contactdetailfield.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
6 **
7 ** This file is part of the Qt Mobility Components.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** No Commercial Usage
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
14 ** this package.
15 **
16 ** GNU Lesser General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU Lesser
18 ** General Public License version 2.1 as published by the Free Software
19 ** Foundation and appearing in the file LICENSE.LGPL included in the
20 ** packaging of this file.  Please review the following information to
21 ** ensure the GNU Lesser General Public License version 2.1 requirements
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23 **
24 ** In addition, as a special exception, Nokia gives you certain additional
25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27 **
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
30 **
31 **
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "contactdetailfield.h"
43 #include "contactdetail.h"
44
45 using namespace SopranoLive;
46
47 QTM_BEGIN_NAMESPACE;
48
49 ////////////////////////////////////////////////////////////////////////////////////////////////////
50
51 void
52 ResourceInfo::modifyFlags(Flags mask, bool value)
53 {
54     if (value) {
55         mFlags |= mask;
56     } else {
57         mFlags &= ~mask;
58     }
59 }
60
61 ////////////////////////////////////////////////////////////////////////////////////////////////////
62
63 QTrackerContactDetailField::QTrackerContactDetailField(const QString &name)
64     : mName(name), mDataType(QVariant::String),
65     mSubjectDomain(QTrackerContactSubject::Anonymous)
66 {
67 }
68
69 QTrackerContactDetailField::~QTrackerContactDetailField()
70 {
71 }
72
73 QTrackerContactDetailField&
74 QTrackerContactDetailField::setDataType(QVariant::Type dataType)
75 {
76     return mDataType = dataType, *this;
77 }
78
79 QTrackerContactDetailField&
80 QTrackerContactDetailField::setDefaultValue(const QVariant &value)
81 {
82     return mDefaultValue = value, *this;
83 }
84
85 QTrackerContactDetailField&
86 QTrackerContactDetailField::setShared(bool value)
87 {
88     return mProperties.last()->modifyFlags(ResourceInfo::Shared, value), *this;
89 }
90
91 QTrackerContactDetailField&
92 QTrackerContactDetailField::setReadOnly(bool value)
93 {
94     return mProperties.last()->modifyFlags(ResourceInfo::ReadOnly, value), *this;
95 }
96
97 QTrackerContactDetailField&
98 QTrackerContactDetailField::setOptional(bool value)
99 {
100     return mProperties.last()->modifyFlags(ResourceInfo::Optional, value), *this;
101 }
102
103 QTrackerContactDetailField&
104 QTrackerContactDetailField::setSubjectDomain(QTrackerContactSubject::Domain domain)
105 {
106     return mSubjectDomain = domain, *this;
107 }
108
109 QUrl
110 QTrackerContactDetailField::makeSubject(const QVariant &value) const
111 {
112     return QTrackerContactSubject::makeIri(subjectDomain(), value);
113 }
114
115 bool
116 QTrackerContactDetailField::hasSubjectDomain() const
117 {
118     return QTrackerContactSubject::Anonymous != subjectDomain();
119 }
120
121 ////////////////////////////////////////////////////////////////////////////////////////////////////
122
123 void
124 QTrackerContactDetailField::bindProperties(const QTrackerContactDetail &detail,
125                                            const RDFVariable &subject,
126                                            const RDFVariableHash &variables,
127                                            PredicateVariableHash &edges) const
128 {
129     // try to bind the field to its column variable
130     const QString fieldId(detail.name() + QLatin1Char('_') + name());
131     const RDFVariableHashConstIter fieldVar(variables.find(fieldId));
132
133     if (fieldVar != variables.end()) {
134         PredicateList predicates;
135         QList<RDFVariable> axis;
136
137         axis.append(detail.isUnique() ? subject.optional() : subject);
138
139         foreach(const ResourceInfoPtr &i, properties()) {
140             const PredicateVariableHashConstIter edgeVar(edges.find(predicates << i->iri()));
141
142             if (i->flags() & ResourceInfo::Optional) {
143                 axis.append(axis.last().optional());
144             }
145
146             if (edgeVar == edges.end()) {
147                 axis.append(i.staticCast<PropertyInfoBase>()->bindProperty(axis.last()));
148                 edges.insert(predicates, axis.last());
149             } else {
150                 axis.append(edgeVar.value());
151             }
152         }
153
154         axis.last().merge(fieldVar.value());
155
156         // bind variable for content URI column
157         if (hasSubjectDomain()) {
158             Q_ASSERT(properties().count() >= 2);
159             Q_ASSERT(axis.count() >= 3);
160
161             const RDFVariableHashConstIter detailVar(variables.find(detail.name()));
162
163             Q_ASSERT(detailVar != variables.end());
164             (axis.end() - 2)->merge(detailVar.value());
165         }
166     }
167
168     // bind subtype variables
169     foreach(const ResourceInfoPtr &subType, subTypes()) {
170         QString subTypeId(fieldId + QLatin1Char('_') + subType->text());
171         const RDFVariableHashConstIter subTypeVar(variables.find(subTypeId));
172         Q_ASSERT(subTypeVar != variables.end());
173
174         if (const PropertyInfoBase *const pi = subType.dynamicCast<PropertyInfoBase>().data()) {
175             pi->bindProperty(subject.optional(), subTypeVar.value());
176         } else if (hasProperties()) {
177             PredicateList predicates;
178
179             foreach(const ResourceInfoPtr &i, properties()) {
180                 predicates.append(i->iri());
181             }
182
183             const PredicateVariableHashConstIter entity(edges.find(predicates));
184             Q_ASSERT(entity != edges.end());
185
186             RDFVariable optional(entity->optional());
187             optional.property<rdf::type>(optional.variable(subTypeVar.value()));
188             optional.variable(subTypeVar.value()).equal(subType->iri());
189         }
190     }
191 }
192
193 void
194 QTrackerContactDetailField::bindColumns(const QTrackerContactDetail &detail,
195                                         RDFSelect &query, RDFVariableHash &variables) const
196 {
197     const QString fieldId(detail.name() + QLatin1Char('_') + name());
198
199     if (hasSubTypes()) {
200         foreach(const ResourceInfoPtr &resource, subTypes()) {
201             const QString subTypeId(fieldId + QLatin1Char('_') + resource->text());
202             const QString columnName(subTypeId + QLatin1String("_IsBound"));
203
204             RDFVariable subTypeVariable(subTypeId);
205             variables.insert(subTypeId, subTypeVariable);
206             query.addColumn(columnName, subTypeVariable.isBound());
207         }
208     } else {
209         RDFVariable fieldVariable(fieldId);
210         variables.insert(fieldId, fieldVariable);
211
212         if (hasInstances()) {
213             const QString columnName(fieldId + QLatin1String("_IsBound"));
214             query.addColumn(columnName, fieldVariable.isBound());
215
216             foreach(const ResourceInfoPtr &resource, instances()) {
217                 const QLatin1String valuePrefix(resource->value().type() != QVariant::String ?
218                                                 resource->value().typeName() : "");
219                 const QString columnName(fieldId + QLatin1String("_IsEqual_") +
220                                          valuePrefix + resource->text());
221                 query.addColumn(columnName, fieldVariable.equal(resource->iri()));
222             }
223         } else {
224             query.addColumn(fieldVariable);
225         }
226     }
227 }
228
229 ////////////////////////////////////////////////////////////////////////////////////////////////////
230
231 QContactDetailFieldDefinition
232 QTrackerContactDetailField::describe() const
233 {
234     QContactDetailFieldDefinition schema;
235     schema.setDataType(dataType());
236
237     // figure out allowable values
238     QVariantList allowableValues;
239
240     // a field cannot describe subTypes and instances at the same time
241     Q_ASSERT(not (hasSubTypes() && hasInstances()));
242
243     foreach(const ResourceInfoPtr &i, subTypes()) {
244         allowableValues.append(i->value());
245     }
246
247     foreach(const ResourceInfoPtr &i, instances()) {
248         allowableValues.append(i->value());
249     }
250
251     if (not allowableValues.isEmpty()) {
252         if (hasDefaultValue() && not allowableValues.contains(defaultValue())) {
253             allowableValues.append(defaultValue());
254         }
255
256         schema.setAllowableValues(allowableValues);
257     }
258
259     return schema;
260 }
261
262 ////////////////////////////////////////////////////////////////////////////////////////////////////
263
264 QTM_END_NAMESPACE;