1 /*=============================================================================
2
3 Library: XNAT/Core
4
5 Copyright (c) University College London,
6 Centre for Medical Image Computing
7
8 Licensed under the Apache License, Version 2.0 (the "License");
9 you may not use this file except in compliance with the License.
10 You may obtain a copy of the License at
11
12 http://www.apache.org/licenses/LICENSE-2.0
13
14 Unless required by applicable law or agreed to in writing, software
15 distributed under the License is distributed on an "AS IS" BASIS,
16 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 See the License for the specific language governing permissions and
18 limitations under the License.
19
20 =============================================================================*/
21
22 #include "ctkXnatObject.h"
23 #include "ctkXnatObjectPrivate.h"
24
25 #include "ctkXnatDataModel.h"
26 #include "ctkXnatDefaultSchemaTypes.h"
27 #include "ctkXnatException.h"
28 #include "ctkXnatResource.h"
29 #include "ctkXnatResourceFolder.h"
30 #include "ctkXnatSession.h"
31
32 #include <QDateTime>
33 #include <QDebug>
34 #include <QStringList>
35 #include <QVariant>
36
37
38 const QString ctkXnatObject::ID = "ID";
39 const QString ctkXnatObject::NAME = "name";
40 const QString ctkXnatObject::LABEL = "label";
41 const QString ctkXnatObject::URI = "URI";
42 const QString ctkXnatObject::XSI_SCHEMA_TYPE = "xsiType";
43
44 //----------------------------------------------------------------------------
ctkXnatObject(const ctkXnatObject &)45 ctkXnatObject::ctkXnatObject(const ctkXnatObject&)
46 {
47 throw ctkRuntimeException("Copy constructor not implemented");
48 }
49
50 //----------------------------------------------------------------------------
ctkXnatObject(ctkXnatObject * parent,const QString & schemaType)51 ctkXnatObject::ctkXnatObject(ctkXnatObject* parent, const QString& schemaType)
52 : d_ptr(new ctkXnatObjectPrivate())
53 {
54 this->setParent(parent);
55 this->setSchemaType(schemaType);
56 }
57
58 //----------------------------------------------------------------------------
ctkXnatObject(ctkXnatObjectPrivate & dd,ctkXnatObject * parent,const QString & schemaType)59 ctkXnatObject::ctkXnatObject(ctkXnatObjectPrivate& dd, ctkXnatObject* parent, const QString& schemaType)
60 : d_ptr(&dd)
61 {
62 this->setParent(parent);
63 this->setSchemaType(schemaType);
64 }
65
66 //----------------------------------------------------------------------------
~ctkXnatObject()67 ctkXnatObject::~ctkXnatObject()
68 {
69 Q_D(ctkXnatObject);
70 foreach (ctkXnatObject* child, d->children)
71 {
72 delete child;
73 }
74 }
75
76 //----------------------------------------------------------------------------
id() const77 QString ctkXnatObject::id() const
78 {
79 return this->property(ID);
80 }
81
82 //----------------------------------------------------------------------------
setId(const QString & id)83 void ctkXnatObject::setId(const QString& id)
84 {
85 this->setProperty(ID, id);
86 }
87
88 //----------------------------------------------------------------------------
name() const89 QString ctkXnatObject::name() const
90 {
91 return this->property(NAME);
92 }
93
94 //----------------------------------------------------------------------------
setName(const QString & name)95 void ctkXnatObject::setName(const QString& name)
96 {
97 this->setProperty(NAME, name);
98 }
99
100 //----------------------------------------------------------------------------
description() const101 QString ctkXnatObject::description() const
102 {
103 Q_D(const ctkXnatObject);
104 return d->description;
105 }
106
107 //----------------------------------------------------------------------------
setDescription(const QString & description)108 void ctkXnatObject::setDescription(const QString& description)
109 {
110 Q_D(ctkXnatObject);
111 d->description = description;
112 }
113
114 //----------------------------------------------------------------------------
childDataType() const115 QString ctkXnatObject::childDataType() const
116 {
117 return "Resources";
118 }
119
lastModifiedTimeOnServer()120 QDateTime ctkXnatObject::lastModifiedTimeOnServer()
121 {
122 Q_D(ctkXnatObject);
123 QUuid queryId = this->session()->httpHead(this->resourceUri());
124 QMap<QByteArray, QByteArray> header = this->session()->httpHeadSync(queryId);
125 QVariant lastModifiedHeader = header.value("Last-Modified");
126 QDateTime lastModifiedTime;
127
128 if (lastModifiedHeader.isValid())
129 {
130 QStringList dateformates;
131 // In case http date formate RFC 822 ( "Sun, 06 Nov 1994 08:49:37 GMT" )
132 dateformates<<"ddd, dd MMM yyyy HH:mm:ss";
133 // In case http date formate ANSI ( "Sun Nov 6 08:49:37 1994" )
134 dateformates<<"ddd MMM d HH:mm:ss yyyy";
135 // In case http date formate RFC 850 ( "Sunday, 06-Nov-94 08:49:37 GMT" )
136 dateformates<<"dddd, dd-MMM-yy HH:mm:ss";
137
138 QString dateText = lastModifiedHeader.toString();
139 // Remove "GMT" addition at the end of the http timestamp
140 if (dateText.indexOf("GMT") != -1)
141 {
142 dateText = dateText.left(dateText.length()-4);
143 }
144
145 foreach (QString format, dateformates)
146 {
147 lastModifiedTime = QDateTime::fromString(dateText, format);
148 if (lastModifiedTime.isValid())
149 break;
150 }
151 }
152 return lastModifiedTime;
153 }
154
setLastModifiedTime(const QDateTime & lastModifiedTime)155 void ctkXnatObject::setLastModifiedTime(const QDateTime &lastModifiedTime)
156 {
157 Q_D(ctkXnatObject);
158 if (d->lastModifiedTime < lastModifiedTime)
159 {
160 d->lastModifiedTime = lastModifiedTime;
161 }
162 }
163
164 //----------------------------------------------------------------------------
property(const QString & name) const165 QString ctkXnatObject::property(const QString& name) const
166 {
167 Q_D(const ctkXnatObject);
168 ctkXnatObjectPrivate::PropertyMapConstInterator iter = d->properties.find(name);
169 if (iter != d->properties.end())
170 {
171 return iter.value();
172 }
173 return QString::null;
174 }
175
176 //----------------------------------------------------------------------------
setProperty(const QString & name,const QVariant & value)177 void ctkXnatObject::setProperty(const QString& name, const QVariant& value)
178 {
179 Q_D(ctkXnatObject);
180 if (d->properties[name] != value)
181 {
182 d->properties.insert(name, value.toString());
183 }
184 }
185
186 //----------------------------------------------------------------------------
properties() const187 const QMap<QString, QString>& ctkXnatObject::properties() const
188 {
189 Q_D(const ctkXnatObject);
190 return d->properties;
191 }
192
193 //----------------------------------------------------------------------------
parent() const194 ctkXnatObject* ctkXnatObject::parent() const
195 {
196 Q_D(const ctkXnatObject);
197 return d->parent;
198 }
199
200 //----------------------------------------------------------------------------
setParent(ctkXnatObject * parent)201 void ctkXnatObject::setParent(ctkXnatObject* parent)
202 {
203 Q_D(ctkXnatObject);
204 if (d->parent != parent)
205 {
206 if (d->parent)
207 {
208 d->parent->remove(this);
209 }
210 if (parent)
211 {
212 parent->add(this);
213 }
214 }
215 }
216
217 //----------------------------------------------------------------------------
children() const218 QList<ctkXnatObject*> ctkXnatObject::children() const
219 {
220 Q_D(const ctkXnatObject);
221 return d->children;
222 }
223
224 //----------------------------------------------------------------------------
add(ctkXnatObject * child)225 void ctkXnatObject::add(ctkXnatObject* child)
226 {
227 Q_D(ctkXnatObject);
228 if (child->parent() != this)
229 {
230 child->d_func()->parent = this;
231 }
232
233 bool childExists (false);
234
235 QList<ctkXnatObject*>::iterator iter;
236 for (iter = d->children.begin(); iter != d->children.end(); ++iter)
237 {
238 if (((*iter)->id().length() != 0 && (*iter)->id() == child->id()) ||
239 ((*iter)->id().length() == 0 && (*iter)->name() == child->name()))
240 {
241 *iter = child;
242 childExists = true;
243 }
244 }
245
246 if (!childExists)
247 {
248 d->children.push_back(child);
249 }
250 }
251
252 //----------------------------------------------------------------------------
remove(ctkXnatObject * child)253 void ctkXnatObject::remove(ctkXnatObject* child)
254 {
255 Q_D(ctkXnatObject);
256 if (!d->children.removeOne(child))
257 {
258 qWarning() << "ctkXnatObject::remove(): Child does not exist";
259 }
260 }
261
262 //----------------------------------------------------------------------------
reset()263 void ctkXnatObject::reset()
264 {
265 Q_D(ctkXnatObject);
266 // d->properties.clear();
267 d->children.clear();
268 d->fetched = false;
269 }
270
271 //----------------------------------------------------------------------------
isFetched() const272 bool ctkXnatObject::isFetched() const
273 {
274 Q_D(const ctkXnatObject);
275 return d->fetched;
276 }
277
278 //----------------------------------------------------------------------------
schemaType() const279 QString ctkXnatObject::schemaType() const
280 {
281 return this->property(XSI_SCHEMA_TYPE);
282 }
283
284 //----------------------------------------------------------------------------
setSchemaType(const QString & schemaType)285 void ctkXnatObject::setSchemaType(const QString& schemaType)
286 {
287 this->setProperty(XSI_SCHEMA_TYPE, schemaType);
288 }
289
290 //----------------------------------------------------------------------------
fetch(bool forceFetch)291 void ctkXnatObject::fetch(bool forceFetch)
292 {
293 Q_D(ctkXnatObject);
294 if (!d->fetched || forceFetch)
295 {
296 this->fetchImpl();
297 d->fetched = true;
298 }
299 }
300
301 //----------------------------------------------------------------------------
session() const302 ctkXnatSession* ctkXnatObject::session() const
303 {
304 const ctkXnatObject* xnatObject = this;
305 while (ctkXnatObject* parent = xnatObject->parent())
306 {
307 xnatObject = parent;
308 }
309 const ctkXnatDataModel* dataModel = dynamic_cast<const ctkXnatDataModel*>(xnatObject);
310 return dataModel ? dataModel->session() : NULL;
311 }
312
313 //----------------------------------------------------------------------------
download(const QString & filename)314 void ctkXnatObject::download(const QString& filename)
315 {
316 this->downloadImpl(filename);
317 }
318
319 //----------------------------------------------------------------------------
save(bool overwrite)320 void ctkXnatObject::save(bool overwrite)
321 {
322 Q_D(ctkXnatObject);
323 this->saveImpl(overwrite);
324 }
325
326 //----------------------------------------------------------------------------
addResourceFolder(QString foldername,QString format,QString content,QString tags)327 ctkXnatResource* ctkXnatObject::addResourceFolder(QString foldername, QString format,
328 QString content, QString tags)
329 {
330 if (foldername.size() == 0)
331 {
332 throw ctkXnatException("Error creating resource! Foldername must not be empty!");
333 }
334
335 ctkXnatResourceFolder* resFolder = 0;
336 QList<ctkXnatObject*> children = this->children();
337 for (int i = 0; i < children.size(); ++i)
338 {
339 resFolder = dynamic_cast<ctkXnatResourceFolder*>(children.at(i));
340 if (resFolder)
341 {
342 break;
343 }
344 }
345
346 if (!resFolder)
347 {
348 resFolder = new ctkXnatResourceFolder();
349 this->add(resFolder);
350 }
351
352 ctkXnatResource* resource = new ctkXnatResource();
353 resource->setName(foldername);
354 if (format.size() != 0)
355 resource->setFormat(format);
356 if (content.size() != 0)
357 resource->setContent(content);
358 if (tags.size() != 0)
359 resource->setTags(tags);
360
361 resFolder->add(resource);
362
363 if (!resource->exists())
364 resource->save();
365 else
366 qDebug()<<"Not adding resource folder. Folder already exists!";
367
368 return resource;
369 }
370
371 //----------------------------------------------------------------------------
exists() const372 bool ctkXnatObject::exists() const
373 {
374 return this->session()->exists(this);
375 }
376
377 //----------------------------------------------------------------------------
saveImpl(bool)378 void ctkXnatObject::saveImpl(bool /*overwrite*/)
379 {
380 Q_D(ctkXnatObject);
381 ctkXnatSession::UrlParameters urlParams;
382 urlParams["xsiType"] = this->schemaType();
383
384 // Just do this if there is already a valid last-modification-time,
385 // otherwise the object is not yet on the server!
386 QDateTime remoteModTime;
387 if (d->lastModifiedTime.isValid())
388 {
389 // TODO Overwrite this for e.g. project and subject which already support modification time!
390 remoteModTime = this->lastModifiedTimeOnServer();
391 // If the object has been modified on the server, perform an update
392 if (d->lastModifiedTime < remoteModTime)
393 {
394 qWarning()<<"Uploaded object maybe overwritten on server!";
395 // TODO update from server, since modification time is not really supported
396 // by xnat right now this is not of high priority
397 // something like this->updateImpl + setLastModifiedTime()
398 }
399 }
400
401 const QMap<QString, QString>& properties = this->properties();
402 QMapIterator<QString, QString> itProperties(properties);
403 while (itProperties.hasNext())
404 {
405 itProperties.next();
406 if (itProperties.key() == "ID" || itProperties.key() == "xsiType")
407 continue;
408
409 urlParams[itProperties.key()] = itProperties.value();
410 }
411
412 // Execute the update
413 QUuid queryID = this->session()->httpPut(this->resourceUri(), urlParams);
414 const QList<QVariantMap> results = this->session()->httpSync(queryID);
415
416 // If this xnat object did not exist before on the server set the ID returned by Xnat
417 if (results.size() == 1 && results[0].size() == 2)
418 {
419 QVariant id = results[0][ID];
420 if (!id.isNull())
421 {
422 this->setId(id.toString());
423 }
424 }
425
426 // Finally update the modification time on the server
427 remoteModTime = this->lastModifiedTimeOnServer();
428 d->lastModifiedTime = remoteModTime;
429 }
430
431 //----------------------------------------------------------------------------
fetchResources(const QString & path)432 void ctkXnatObject::fetchResources(const QString& path)
433 {
434 Q_UNUSED(path);
435 ctkXnatResourceFolder* resFolder = new ctkXnatResourceFolder();
436 this->add(resFolder);
437 }
438
439 //----------------------------------------------------------------------------
erase()440 void ctkXnatObject::erase()
441 {
442 this->session()->remove(this);
443 this->parent()->remove(this);
444 }
445