d45a1854a1bd0c2d344185a85fde286ec42b4ca5
[smartapi.git] / Common / C++ / SmartAPI / smartapi / model / Obj.cpp
1 #include "Obj.h"
2 #include <QtDebug>
3 #include <unistd.h>
4
5 #include "smartapi/agents/TransactionAgent.h"
6 #include "smartapi/common/HttpMessage.h"
7 #include "smartapi/common/NS.h"
8 #include "smartapi/common/URLs.h"
9 #include "smartapi/common/OntologyCache.h"
10 #include "smartapi/common/PROPERTY.h"
11 #include "smartapi/common/SmartAPICrypto.h"
12 #include "smartapi/common/ObjectPrinter.h"
13 #include "smartapi/common/SERIALIZATION.h"
14 #include "smartapi/common/Tools.h"
15 #include "smartapi/exceptions/VerifyException.h"
16 #include "smartapi/model/AbstractEntity.h"
17 #include "smartapi/model/Activity.h"
18 #include "smartapi/model/Device.h"
19 #include "smartapi/model/Entity.h"
20 #include "smartapi/model/Evaluation.h"
21 #include "smartapi/model/Input.h"
22 #include "smartapi/model/Map.h"
23 #include "smartapi/model/Offering.h"
24 #include "smartapi/model/Output.h"
25 #include "smartapi/model/Error.h"
26 #include "smartapi/model/Status.h"
27 #include "smartapi/model/Parameter.h"
28 #include "smartapi/model/PhysicalEntity.h"
29 #include "smartapi/model/Provenance.h"
30 #include "smartapi/model/Restriction.h"
31 #include "smartapi/model/Service.h"
32 #include "smartapi/model/ServiceProvider.h"
33 #include "smartapi/model/TimeSeries.h"
34 #include "smartapi/model/Transaction.h"
35 #include "smartapi/model/WeatherServiceProvider.h"
36         
37         
38 Obj::Obj(QString identifierUri)
39         : GraphItem(),
40         mIdentifierUri(),
41         mName(),
42         mDescription(),
43         mGeneratedAt(),
44         mTransaction(NULL),
45         mSameAs(NULL),
46         mStatus(NULL),
47         mGeneratedBy(),
48         mTypes(QList<QString>()),
49         mSign(false),
50         mSignKey(NULL),
51         mEncrypt(false),
52         mEncryptPKey(NULL),
53         mEncryptSecretKey(),
54         mIsDecrypted(false),
55         mHashCode(),
56         mSignature(),
57         mSessionKey(),
58         mEncryptionKeyType(),
59         mSerializeAsReference(false),
60         mReferenceUriPrefix(REFERENCE_URI_PREFIX)
61 {
62         // Unlike all subclass constructors, Obj constructor doesn't call setType(RESOURCE__OBJ).
63         // The reason for this is that the type is represented as Obj too, so calling setType()
64         // in Obj constructor would lead to a loop. It would be redundant information anyway.
65
66         if (identifierUri.length() > 0) {
67                 mIdentifierUri = QUrl(identifierUri);
68         } else mIdentifierUri = QUrl();
69         mProvenances.setParent(this);
70         mRestrictions.setParent(this);
71         INIT_PROPERTYLIST(mErrors,PROPERTY__ERROR)
72         INIT_PROPERTYLIST(mOfferings,PROPERTY__OFFERS)
73 }
74
75 Obj::Obj(QUrl identifierUri)
76         : Obj()
77 {
78         mIdentifierUri = identifierUri;
79 }
80
81 Obj::Obj(Obj* o)
82         : Obj()
83 {
84         mIdentifierUri = QUrl();
85         if (o != NULL) {
86                 mName = o->getName();
87                 mDescription = o->getDescription();
88                 mGeneratedBy = o->getGeneratedBy();
89                 mGeneratedAt = o->getGeneratedAt();
90                 mSignKey = o->getSignKey();
91                 mEncryptPKey = o->getEncryptKey();
92                 mEncryptSecretKey = o->getSecretKey();
93                 mHashCode = o->getHashCode();
94                 mSignature = o->getSignature();
95                 mSessionKey = o->getSessionKey();
96                 mStringRepresentation = o->getStringRepresentation();
97                 mReferenceUriPrefix = o->getReferenceUriPrefix();
98                 mEncryptionKeyType = o->getEncryptionKeyType();
99                 if (o->hasNotary())                                                     setNotary(o->getNotary());
100
101                 mSign = o->isToBeSigned();
102                 mEncrypt = o->isToBeEncrypted();
103                 mIsDecrypted = o->isDecrypted();
104                 mSerializeAsReference = o->isToBeSerializedAsReference();
105
106                 if (o->hasIdentifierUri())                                      mIdentifierUri = o->getIdentifierUri();
107                 if (o->hasTransaction())                                        mTransaction = new Transaction(o->getTransaction());
108                 if (o->hasSameAs())                                                     mSameAs = new Obj(o->getSameAs());
109                 if (o->hasStatus())                                                     mStatus = new Status(o->getStatus());
110
111                 QListIterator<QString> t(o->getTypes());
112                 while (t.hasNext()) mTypes.append(t.next());
113
114                 mErrors                 = o->getErrors();
115                 mProvenances    = o->getProvenances();
116                 mRestrictions   = o->getRestrictions();
117                 mOfferings              = o->getOfferings();
118
119                 // Copy all items in the property list. Note that because of this operation,
120                 // any child of Obj does not need to copy any properties or property lists
121                 // in its own copy constructor
122                 // Children only copy references from this map into their local variables
123                 QMap<QString, PropertyList<GraphItem*>* > mp = o->getAllProperties();
124                 QList<QString> keys = mp.keys();
125                 for (int j = 0; j < keys.length(); j++) {
126                         PropertyList<GraphItem*>* vars = mp.value(keys.at(j));
127                         for (PropertyListItem<GraphItem*>* i = vars->firstItem(); i != NULL; i = i->next()) {
128 //                              add(keys.at(j), (Variant*)i->getItem());
129                                 GraphItem* val = (Variant*)i->getItem();
130                                 if (val != NULL) {
131                                         add(keys.at(j), Tools::copy(val));
132                                 }
133                         }
134                         // copy list type
135                         PropertyList<GraphItem*>* localVars = getAll(keys.at(j));
136                         if (localVars != NULL && localVars->length() > 0) {
137                                 localVars->setListType(vars->getListType());
138                         }
139                 }
140         }
141         mProvenances.setParent(this);
142         mRestrictions.setParent(this);
143         mOfferings.setParent(this);
144         mErrors.setParent(this);
145 }
146
147 Obj::~Obj()
148 {
149         // Note that because encryption keys are set as pointers, the
150         // destructor should not touch them
151 }
152
153 void Obj::clearReferences(QSet<GraphItem*>* trash)
154 {
155         if (mSameAs != NULL)                            mSameAs->destroyCascade(trash);
156         if (mStatus != NULL)                            mStatus->destroyCascade(trash);
157         if (mTransaction != NULL)                       mTransaction->destroyCascade(trash);
158
159         while (mProvenances.length() > 0)       mProvenances.destroyFirstCascade(trash);
160         while (mRestrictions.length() > 0)      mRestrictions.destroyFirstCascade(trash);
161         while (mErrors.length() > 0)            mErrors.destroyFirstCascade(trash);
162         while (!mVariantMap.isEmpty()) {
163                 QString k = mVariantMap.firstKey();
164                 PropertyList<GraphItem*>* vars = mVariantMap.take(k);
165                 while (vars->length() > 0 && vars->destroyFirstCascade(trash));
166
167                 // If a property list is in stack (not heap), it should not be deleted as items
168                 // in stack are deleted automatically
169                 if (!vars->isInStack()) delete vars;
170         }
171 }
172
173 /*
174 bool Obj::hasSpecialSerialization(Model *model)
175 {
176         // if the object has already been serialized, do not serialize again
177         if (hasIdentifierUri() && model->isCached(getIdentifierUri())) {
178                 //mSpecialSerialization = model->createResource(getIdentifierUri());
179                 return true;
180         }
181
182         // if the object is a reference, then serialize it as one
183         // and add object as a separate part to the HttpMessage
184         if (isToBeSerializedAsReference()) {
185                 mSpecialSerialization = serializeToReference(model);
186                 return true;
187         }
188
189         return false;
190 }
191 */
192
193 void Obj::addType(QString type)
194 {
195         QString typeStr = Tools::toAbsoluteUri(type);
196         if (!mTypes.contains(typeStr))
197                 mTypes.append(typeStr);
198 }
199
200 bool Obj::hasType(QString type)
201 {
202         QString typeStr = Tools::toAbsoluteUri(type);
203         return mTypes.contains(typeStr);
204 }
205
206 bool Obj::removeType(QString type)
207 {
208         QString typeStr = Tools::toAbsoluteUri(type);
209         return mTypes.removeAll(typeStr);
210 }
211
212 bool Obj::hasSpecialSerialization(Model *model)
213 {
214         // is already serialized
215         if (model->isInSerializedObjsCache(this)) {
216                 //mSpecialSerialization = model->createResource(getIdentifierUri());
217                 return true;
218         }
219         // needs to be serialized as reference
220         if (isToBeSerializedAsReference()) {
221                 return true;
222         }
223         return false;
224 }
225
226 Resource* Obj::getSpecialSerialization(Model *model)
227 {
228         // if the object has already been serialized, use that serialized resource
229         if (model->isInSerializedObjsCache(this)) {
230                 return model->getFromSerializedObjsCache(this);
231         }
232
233         // if the object is a reference, then serialize it as one
234         // and add object as a separate part to the HttpMessage
235         if (isToBeSerializedAsReference()) {
236                 return serializeToReference(model);
237         }
238
239         return NULL;
240 }
241
242 QString Obj::generateChecksum()
243 {
244         QString csum = Tools::generateChecksum(this);
245         return csum;
246 }
247
248 QString Obj::generateTemporaryIdentifier()
249 {
250         return "http://smart-api.io/temporary/" + SmartAPICrypto::generate16ByteCryptoRandomString();
251 }
252
253 bool Obj::getCheckResult()
254 {
255         GraphItem* r = get(PROPERTY__CHECKRESULT);
256         if (r != NULL && r->isVariant())
257                 return ((Variant*)r)->asBoolean();
258         return false;
259 }
260
261 QVariantMap Obj::toNude()
262 {
263         return QVariantMap();
264 }
265
266 void Obj::fromNude(QVariantMap n)
267 {
268         Q_UNUSED(n);
269 }
270
271 void Obj::setIdentifierUri(QString uri)
272
273         mIdentifierUri = QUrl(uri);
274 }
275
276 void Obj::setIdentifierUri(QUrl uri)
277
278         mIdentifierUri = uri; 
279 }
280
281 void Obj::setSameAs(QString uri)
282
283         Obj* e = new Obj(uri);
284         setSameAs(e);
285 }
286
287 bool Obj::fetchKeyFromNotary(QString identity, QString notaryServer, EVP_PKEY* privKey)
288 {
289         QByteArray key = Tools::fetchKeyFromNotary(identity, getIdentifierUri(), getHashCode(), getSignature(), notaryServer, privKey);
290
291         if (key.size() > 0) {
292                 mEncryptSecretKey = key;
293                 return true;
294         } else {
295                 return false;
296         }
297 }
298
299 bool Obj::sign(EVP_PKEY* key)
300 {
301         mSign = true;
302         mSignKey = key;
303         mSerializeAsReference = true;
304         return true;
305 }
306
307 bool Obj::verifySignature(EVP_PKEY* key)
308 {
309         bool verified = false;
310         if (!hasHashCode()) {
311                 throw new VerifyException("No hashcode found.");
312         } else if (!hasSignature()) {
313                 throw new VerifyException("No signature found.");
314         } else if (!hasStringRepresentation()) {
315                 throw new VerifyException("No string representation found.");
316         } else if (!verifyHash()) {
317                 throw new VerifyException("Hash code mismatch.");
318         } else {
319                 verified = SmartAPICrypto::verifySignature(getSignature(), key, getStringRepresentation());
320                 // Note that the object from string representation will always be used, even when signature fails
321                 // This way the recipient may choose to process it despite the mismatch.
322                 replaceThisObjectWith(getStringRepresentation());
323         }
324         return verified;
325 }
326
327 bool Obj::verifyHash()
328 {
329         if (!hasHashCode()) {
330                 throw new VerifyException("No hashcode found.");
331         } else if (!hasStringRepresentation()) {
332                 throw new VerifyException("No string representation found.");
333         } else {
334                 return SmartAPICrypto::verifyEncodedMessageDigest(getHashCode(), getStringRepresentation());
335         }
336 }
337
338 bool Obj::encrypt(QByteArray secretKey)
339 {
340         mEncrypt = true;
341         mSerializeAsReference = true;
342         mEncryptSecretKey = secretKey;
343         delete mEncryptPKey;
344         mEncryptPKey = NULL;
345         setEncryptionKeyType(RESOURCE__SESSIONKEY);
346         return true;
347 }
348
349 QByteArray Obj::encrypt(EVP_PKEY *publicKey)
350 {
351         QByteArray secretKey = SmartAPICrypto::generateSymmetricKey();
352         encrypt(publicKey, secretKey);
353         return secretKey;
354 }
355
356 bool Obj::encrypt(EVP_PKEY* publicKey, QByteArray secretKey)
357 {
358         mEncrypt = true;
359         mSerializeAsReference = true;
360         delete mEncryptPKey;
361         mEncryptPKey = publicKey;
362         mEncryptSecretKey = secretKey;
363         setEncryptionKeyType(RESOURCE__PUBLICKEY);
364         //addType(RESOURCE__REFERENCE);
365         addType(RESOURCE__ENCRYPTEDREFERENCE);
366         return true;
367 }
368
369 bool Obj::decrypt(QByteArray secretKey)
370 {
371         if (mStringRepresentation.length() > 0) {
372                 unsigned char* key;
373                 key = (unsigned char*)malloc(AES_KEYLEN/8);
374                 memcpy(key, secretKey.data(), 16);
375                 QString decrypted = SmartAPICrypto::symmetricDecrypt(key, mStringRepresentation);
376                 free(key);
377                 if (replaceThisObjectWith(decrypted)) {
378                         removeType(RESOURCE__ENCRYPTEDREFERENCE);
379                         mIsDecrypted = true;
380                         return true;
381                 } else {
382                         return false;
383                 }
384                 return true;
385         }
386         return false;
387 }
388
389 bool Obj::decrypt(EVP_PKEY* privateKey)
390 {
391         if (hasEncryptionKeyType() && getEncryptionKeyType() == RESOURCE__PUBLICKEY) {
392                 QString decrypted = SmartAPICrypto::asymmetricDecrypt(privateKey, getStringRepresentation(), NULL);
393                 if (replaceThisObjectWith(decrypted)) {
394                         //removeType(RESOURCE__ENCRYPTEDREFERENCE);
395                         mIsDecrypted = true;
396                         return true;
397                 } else {
398                         qDebug() << "WARNING: object decryption failed.";
399                         return false;
400                 }
401         }
402         else if (hasEncryptionKeyType() && getEncryptionKeyType() == RESOURCE__NOTARIZEDSESSIONKEY) {
403                 QByteArray sessionKey = SmartAPICrypto::decryptAndDecodeKey(privateKey, getSessionKey());
404                 return decrypt(sessionKey);
405         }
406         return false;
407 }
408
409 void Obj::encryptAndNotarize(QString notaryAddress, EVP_PKEY* publicKey, EVP_PKEY* privateKey)
410 {
411         mEncrypt = true;
412         delete mEncryptPKey;
413         mEncryptPKey = publicKey;
414         mEncryptSecretKey.clear();
415         addType(RESOURCE__REFERENCE);
416         addType(RESOURCE__ENCRYPTEDREFERENCE);
417         setNotary(notaryAddress);
418         setEncryptionKeyType(RESOURCE__NOTARIZEDSESSIONKEY);
419         if (privateKey) {
420                 mSign = true;
421                 mSignKey = privateKey;
422         }
423         mSerializeAsReference = true;
424 }
425
426 bool Obj::confirmContentValidityAndFetchKey(QString identity, QString notaryAddress, EVP_PKEY* privateKey)
427 {
428         if (verifyHash()) {
429                 return fetchKeyFromNotary(identity, notaryAddress, privateKey);
430         } else {
431                 return false;
432         }
433 }
434
435 // used to replace a Reference with the actual object
436 bool Obj::replaceThisObjectWith(QString str)
437 {
438         // here we could remove some Reference related properties
439
440         // Don't parse a zero length string. The model does not like that.
441         if (str.length() == 0)
442                 return false;
443
444         // parse str to model
445         Model* model = Tools::fromString(str);
446         if (!model) {
447                 return false;
448         }
449
450         // find root resource in model
451         QMap<QString, Resource*> types = Tools::getTopNodes(model);
452         QStringList keys = types.keys();
453         bool ok = true;
454         for (int i = 0; i < keys.length(); i++) {
455                 QString nodeType = keys.at(i);
456                 Resource* res = types.value(nodeType);
457                 if (res) {
458                         QList<Statement*> sl = res->findProperties();
459                         while (sl.length() > 0) {
460                                 parse(sl.at(0));
461                                 delete sl.takeFirst();
462                         }
463                         delete res;
464                 } else {
465                         ok = false;
466                 }
467         }
468         delete model;
469         return ok;
470 }
471
472 Resource* Obj::serialize(Model* model)
473 {
474         // check if this obj is serialized in a special way
475         if ( hasSpecialSerialization(model) ) {
476                 return getSpecialSerialization(model);
477         }
478
479         Resource* resource;
480
481         if (model->isInSerializedObjsCache(this)) {
482                 resource = model->getFromSerializedObjsCache(this);
483         } else {
484                 // create resource
485                 if (hasIdentifierUri()) {
486                         resource = model->createResource(getIdentifierUrl()); // note that it says "url" not "uri" here
487                 } else {
488                         resource = model->createResource();
489                 }
490
491                 // add this object to map of serialized objects
492                 model->addToSerializedObjsCache(this, resource);
493         }
494
495         // transaction
496         if (hasTransaction()) {
497                 resource->addProperty(model->createProperty(PROPERTY__TRANSACTION), getTransaction()->serialize(model));
498         }
499
500         // sameas
501         if (hasSameAs()) {
502                 resource->addProperty(model->createProperty(PROPERTY__SAMEAS), getSameAs()->serialize(model));
503         }
504
505         // types
506         QListIterator<QString> ti(getTypes());
507         while (ti.hasNext()) {
508                 // This needs to be a URI
509                 resource->addProperty(model->createProperty(PROPERTY__RDF_TYPE), QUrl(ti.next()));
510         }
511
512         // generatedby
513         if (hasGeneratedBy()) {
514                 resource->addProperty(model->createProperty(PROPERTY__GENERATEDBY), getGeneratedBy());
515         }
516
517         // generatedat
518         if (hasGeneratedAt()) {
519                 resource->addProperty(model->createProperty(PROPERTY__GENERATEDAT),
520                                 model->createTypedLiteral(Tools::dateToString(getGeneratedAt()), Tools::getXsdString() + "dateTime"));
521         }
522
523         // name
524         if (hasName()) {
525                 resource->addProperty(model->createProperty(PROPERTY__RDFS_LABEL), getName());
526         }
527
528         // comment
529         if (hasDescription()) {
530                 resource->addProperty(model->createProperty(PROPERTY__COMMENT), getDescription());
531         }
532
533         SERIALIZE_PROPERTYLIST(Provenance,mProvenances,PROPERTY__PROVENANCE)    // provenances
534         SERIALIZE_PROPERTYLIST(Restriction,mRestrictions,PROPERTY__RESTRICTION) // restrictions
535         SERIALIZE_PROPERTYLIST(Offering,mOfferings,PROPERTY__OFFERS)                    // offerings
536         SERIALIZE_PROPERTYLIST(Error,mErrors,PROPERTY__ERROR)                                   // errors
537
538         // add additional variant properties
539         QMapIterator<QString, PropertyList<GraphItem*>* > mi(mVariantMap);
540         while (mi.hasNext()) {
541                 mi.next();
542                 PropertyList<GraphItem*>* gi = mi.value();
543                 QString prop = mi.key();
544                 // Properties that properly start with http should be serialized as links ...
545                 if (prop.startsWith("http")) {
546                         if (PropertyList<GraphItem*>* pl = dynamic_cast<PropertyList<GraphItem*>*>(gi)) {
547                                 pl->serialize(resource, prop);
548                         }
549
550                 // ... and properties without a http prefix are parameters
551                 } else {
552                         for (PropertyListItem<GraphItem*>* i = gi->firstItem(); i != NULL; i = i->next()) {
553                                 GraphItem* pi = i->getItem();
554                                 Parameter* p = NULL;
555                                 Variant* v = NULL;
556                                 if (Obj* o = dynamic_cast<Obj*>(pi)) {
557                                         v = new Variant(o);
558                                         p = new Parameter(prop, v);
559                                 } else
560                                         p = new Parameter(prop, ((Variant*)pi));
561                                 resource->addProperty(model->createProperty(PROPERTY__PARAMETER), p->serialize(model));
562                                 model->removeFromSerializedObjsCache(p); // this one is important
563                                 delete p; // no cascade here
564                                 if (v != NULL) delete v; // no cascade here
565                         }
566                 }
567         }
568
569         // hashCode
570         if (hasHashCode()) {
571                 resource->addProperty(model->createProperty(PROPERTY__HASHCODE),
572                                 model->createLiteral(getHashCode()));
573         }
574
575         // signature
576         if (hasSignature()) {
577                 resource->addProperty(model->createProperty(PROPERTY__SIGNATURE),
578                                 model->createLiteral(getSignature()));
579         }
580
581         // sessionKey
582         if (hasSessionKey()) {
583                 resource->addProperty(model->createProperty(PROPERTY__SESSIONKEY),
584                                 model->createLiteral(getSessionKey()));
585         }
586
587         // encryptionKeyType
588         if (hasEncryptionKeyType()) {
589                 resource->addProperty(model->createProperty(PROPERTY__ENCRYPTIONKEYTYPE),
590                                 model->createResource(getEncryptionKeyType()));
591         }
592
593         // notary
594         if (hasNotary()) {
595                 resource->addProperty(model->createProperty(PROPERTY__NOTARY),
596                                 model->createResource(getNotary()));
597         }
598
599         return resource;
600 }
601
602 Obj* Obj::fromStringRepresentation(Model* model)
603 {
604         // Parse an object that contains data from the string representation of the object and return it.
605         Obj* replacement = Tools::fromIdentifiedStringAsObj(getStringRepresentation(), getIdentifierUri(), model, false);
606         if (replacement != NULL) {
607                 replacement->setStringRepresentation(getStringRepresentation());
608                 replacement->setHashCode(getHashCode());
609                 replacement->setSignature(getSignature());
610                 replacement->setIdentifierUri(getIdentifierUri());
611         }
612         return replacement;
613 }
614
615 Resource* Obj::serializeToReference(Model* model)
616 {
617         Resource* resource;
618
619         // No longer need to make reference serialization. Set flag to false to prevent loop.
620         mSerializeAsReference = false;
621
622         // add id to this obj if it does not have one yet
623         if (!hasIdentifierUri()) {
624                 if (mReferenceUriPrefix.length() == 0)
625                         setIdentifierUri("http://" + SmartAPICrypto::generate16ByteCryptoRandomString());
626                 else
627                         setIdentifierUri(mReferenceUriPrefix + SmartAPICrypto::generate16ByteCryptoRandomString());
628         }
629         // Start by creating a standard string representation of this resource.
630         // Note: DO NOT recycle the model here by giving it as an argument to tools.
631         // Redland does not like serializing the same model twice and as toString will
632         // cause one round of  serialization, it must be done with an "independent" model.
633         // What we do want to retain however is the HttpMessage that should be carried out
634         // throughout the serialization process. So feed that in (this will disable the automated
635         // deletion of the message, it will be deleted by the model we are taking the message from)
636         QString partString = Tools::toString(this, SERIALIZATION__DEFAULT, model->getMessage());
637
638         // This is needed to unify the message with encoding put in place during transit as MIME multipart
639         partString.replace("\n", "\r\n");
640         
641         addType(RESOURCE__REFERENCE);
642
643         // encrypt if needed
644         if (mEncrypt && mEncryptPKey != NULL) {
645                 // symmetric with notary
646                 if (getEncryptionKeyType() == RESOURCE__NOTARIZEDSESSIONKEY) {
647                         // encrypt data
648                         QByteArray sessionKey = SmartAPICrypto::generateSymmetricKey();
649                         partString = SmartAPICrypto::symmetricEncrypt((unsigned char*)sessionKey.data(), partString);
650
651                         // encrypt session key with recipient's public key
652                         QString encryptedKey = SmartAPICrypto::encryptAndEncodeKey(mEncryptPKey, sessionKey);
653                         setHashCode(SmartAPICrypto::createEncodedMessageDigest(partString));
654                         if (mSign && mSignKey != NULL) {
655                                 setSignature(SmartAPICrypto::sign(mSignKey, getHashCode()));
656                         }
657
658                         // send encrypted key to notary for storing
659                         TransactionAgent a;
660                         if (!a.sendKeyToNotarySync(getGeneratedBy().toString(), getIdentifierUri(), getHashCode(), encryptedKey, getSignature(), getNotary(), mSignKey)) {
661                                 qCritical() << "Sending notarized session key to notary failed.";
662                         }
663                 }
664
665                 // with public key
666                 else if (getEncryptionKeyType() == RESOURCE__PUBLICKEY) {
667                         // encrypt data
668                         partString = SmartAPICrypto::asymmetricEncrypt(mEncryptPKey, partString, mEncryptSecretKey, NULL);
669                 }
670
671                 // with only session key
672                 else if (getEncryptionKeyType() == RESOURCE__SESSIONKEY) {
673                         // encrypt data
674                         partString = SmartAPICrypto::symmetricEncrypt((unsigned char*)mEncryptSecretKey.data(), partString);
675                         QString key = mEncryptSecretKey.toBase64();
676                         //setSessionKey(key); // do not add sessionkey because key would then be transferred in the message
677                 }
678         }
679
680         // create hash if not yet created
681         if (!hasHashCode()) {
682                 setHashCode(SmartAPICrypto::createEncodedMessageDigest(partString));
683         }
684
685         // sign if needed
686         if (mSign && !hasSignature() && mSignKey != NULL) {
687                 setSignature(SmartAPICrypto::sign(mSignKey, partString));
688         }
689
690         // create and add message part
691         if (model->hasMessage()) {
692                 model->getMessage()->add(getIdentifierUri(), partString.toLocal8Bit());
693                 model->getMessage()->addHeader(getIdentifierUri(), "Content-ID", getIdentifierUri().toLocal8Bit());
694                 model->getMessage()->addHeader(getIdentifierUri(), "Content-Type", "text/turtle");
695         }
696
697         addType(RESOURCE__MULTIPARTREFERENCE);
698
699         // serialize as reference into this model
700         // create resource
701         if (hasIdentifierUri()) {
702                 resource = model->createResource(mIdentifierUri);
703         } else {
704                 resource = model->createResource();
705         }
706         model->addToSerializedObjsCache(this, resource);
707
708         // types
709         QListIterator<QString> typeIter(mTypes);
710         while (typeIter.hasNext()) {
711                 resource->addProperty(model->createProperty(PROPERTY__RDF_TYPE), QUrl(typeIter.next()));
712         }
713
714         // hashCode
715         if (hasHashCode()) {
716                 resource->addProperty(model->createProperty(PROPERTY__HASHCODE), getHashCode());
717         }
718
719         // signature
720         if (hasSignature()) {
721                 resource->addProperty(model->createProperty(PROPERTY__SIGNATURE), getSignature());
722         }
723
724         // session key
725         if (hasSessionKey()) {
726                 resource->addProperty(model->createProperty(PROPERTY__SESSIONKEY), getSessionKey());
727         }
728
729         // encryption key type
730         if (hasEncryptionKeyType()) {
731                 resource->addProperty(model->createProperty(PROPERTY__ENCRYPTIONKEYTYPE), model->createResource(getEncryptionKeyType()));
732         }
733
734         // notary
735         if (hasNotary()) {
736                 resource->addProperty(model->createProperty(PROPERTY__NOTARY), model->createResource(getNotary()));
737         }
738
739         return resource;
740 }
741
742 void Obj::addProperty(Model* model, Resource* resource, QString property, QVariant propObj)
743 {
744         if (property.startsWith("http")) {
745                 if (propObj.canConvert<QString>())
746                         resource->addProperty(model->createProperty(property), propObj.toString());
747                 else if (propObj.canConvert<int>())
748                         resource->addProperty(model->createProperty(property), model->createTypedLiteral(propObj, Tools::getXsdString() + "int"));
749                 else if (propObj.canConvert<double>())
750                         resource->addProperty(model->createProperty(property), model->createTypedLiteral(propObj, Tools::getXsdString() + "double"));
751                 else if (propObj.canConvert<bool>())
752                         resource->addProperty(model->createProperty(property), model->createTypedLiteral(propObj, Tools::getXsdString() + "boolean"));
753                 else {
754                         Variant* propObjVariant = (Variant*)propObj.value<void*>();
755                         if (propObjVariant != NULL)
756                                 resource->addProperty(model->createProperty(property), propObjVariant->serialize(model));
757                 }
758         } else {
759                 Resource* param = model->createResource();
760                 Variant* propVariant = new Variant(property);
761                 Variant* valueVariant = NULL;
762                 param->addProperty(model->createProperty(PROPERTY__RDF_TYPE), model->createResource(PROPERTY__PARAMETER));
763                 param->addProperty(model->createProperty(PROPERTY__KEY), propVariant->serialize(model));
764                 
765                 if (propObj.type() == QMetaType::QVariantList) {
766                         valueVariant = new Variant(propObj);
767                         param->addProperty(model->createProperty(PROPERTY__RDF_VALUE), valueVariant->serialize(model));
768                         delete valueVariant;
769                 } else if (propObj.type() == QMetaType::VoidStar) {
770                         Variant* orig = (Variant*)propObj.value<void*>();
771                         //param->addProperty(model->createProperty(PROPERTY__RDF_VALUE), model->createTypedLiteral(QVariant(1), Tools::getXsdString() + "int"));
772                         
773                         if (orig->isObj()) {
774                                 Obj* origValue = (Obj*)orig->getValue().value<void *>();
775                                 param->addProperty(model->createProperty(PROPERTY__RDF_VALUE), origValue->serialize(model));
776                         } else {
777                                 param->addProperty(model->createProperty(PROPERTY__RDF_VALUE), orig->serialize(model));
778                         }
779                 } else {
780                         valueVariant = new Variant(propObj);
781                         param->addProperty(model->createProperty(PROPERTY__RDF_VALUE), valueVariant->serialize(model));
782                         delete valueVariant;
783                 }
784                 resource->addProperty(model->createProperty(PROPERTY__PARAMETER), param);
785                 delete propVariant;
786         }
787 }
788
789 void Obj::parse(QList<Statement*> statements)
790 {
791         for (int i = 0; i < statements.length(); i++)  parse(statements.at(i));
792 }
793
794 void Obj::parse(Statement* statement)
795 {
796         // get predicate
797         QString predicate = statement->getPredicateUriString();
798
799         // get object
800         raptor_term* objectNode = statement->getObject();
801
802         // transaction
803         if (predicate == PROPERTY__TRANSACTION) {
804                 setTransaction(Obj::parse<Transaction>(statement->getResource()));
805                 return;
806         }
807
808         // type
809         if (predicate == PROPERTY__RDF_TYPE) {
810                 // Set types as strings. Don't change this implementation unless
811                 // you know what you are doing (and test for memleaks)
812                 addType(statement->getResource()->toString());
813                 return;
814         }
815
816         if (predicate == PROPERTY__GENERATEDBY) {
817                 setGeneratedBy(statement->getResource()->toString());
818                 return;
819         }
820
821         // generatedat
822         if (predicate == PROPERTY__GENERATEDAT) {
823                 setGeneratedAt(Tools::stringToDate(statement->getString()));
824                 return;
825         }
826
827         // sameas
828         if (predicate == PROPERTY__SAMEAS) {
829                 setSameAs(Obj::parse<Entity>(statement->getResource()));
830                 return;
831         }
832
833         // label
834         if (predicate == PROPERTY__RDFS_LABEL) {
835                 setName(statement->getString());
836                 // TODO what if it is a Variant? see Java implementation
837                 return;
838         }
839         
840         // comment
841         if (predicate == PROPERTY__COMMENT) {
842                 setDescription(statement->getString());
843                 // TODO what if it is a Variant? see Java implementation
844                 return;
845         }
846         
847         // provenance
848         if (predicate == PROPERTY__PROVENANCE) {
849                 addProvenance(Obj::parse<Provenance>(statement->getResource()));
850                 return;
851         }
852
853         // restriction
854         if (predicate == PROPERTY__RESTRICTION) {
855                 addRestriction(Obj::parse<Restriction>(statement->getResource()));
856                 return;
857         }
858
859         // offerings
860         if (predicate == PROPERTY__OFFERS) {
861                 addOffering(Obj::parse<Offering>(statement->getResource()));
862                 return;
863         }
864
865         // errors
866         if (predicate == PROPERTY__ERROR) {
867                 addError(Obj::parse<Error>(statement->getResource()));
868                 return;
869         }
870
871         // hashCode
872         if (predicate == PROPERTY__HASHCODE) {
873                 setHashCode(statement->getString());
874                 return;
875         }
876
877         // signature
878         if (predicate == PROPERTY__SIGNATURE) {
879                 setSignature(statement->getString());
880                 return;
881         }
882
883         // sessionKey
884         if (predicate == PROPERTY__SESSIONKEY) {
885                 setSessionKey(statement->getString());
886                 return;
887         }
888
889         // encryptionKeyType
890         if (predicate == PROPERTY__ENCRYPTIONKEYTYPE) {
891                 // TODO is this correct? Java impl uses getURI() instead of toString()
892                 setEncryptionKeyType(statement->getResource()->toString());
893                 return;
894         }
895
896         // notary
897         if (predicate == PROPERTY__NOTARY) {
898                 // TODO is this correct? Java impl uses getURI() instead of toString()
899                 setNotary(statement->getResource()->toString());
900                 return;
901         }
902
903         // parameter
904         if (predicate == PROPERTY__PARAMETER) {
905                 Parameter* p = Obj::parse<Parameter>(statement->getResource());
906                 if (p) {
907                         add(p->getKey(), p->getValues());
908                         delete p;
909                 }
910                 return;
911         }
912         
913         // if literal object
914         if (objectNode->type == RAPTOR_TERM_TYPE_LITERAL) {
915                 this->add(predicate, Tools::raptorLiteralToQVariant(objectNode->value.literal));
916                 return;
917         }
918
919         // if resource
920         if (statement->getResource() != NULL) {
921                 Resource* resource = statement->getResource();
922                 Obj* instanceClass = Tools::fromResourceAsObj(resource);
923                 this->add(predicate, instanceClass);
924                 return;
925         }
926 }
927
928 bool Obj::isReadable()
929 {
930         return hasType(RESOURCE__READABLE);
931 }
932
933 bool Obj::isWritable()
934 {
935         return hasType(RESOURCE__WRITABLE);
936 }
937
938 void Obj::setProvenances(QList<Provenance*> provenances)
939 {
940         mProvenances.append(provenances);
941 }
942
943 void Obj::setRestrictions(QList<Restriction*> restrictions)
944 {
945         mRestrictions.append(restrictions);
946 }
947
948 void Obj::setOfferings(QList<Offering *> offerings)
949 {
950         mOfferings.append(offerings);
951 }
952
953 void Obj::addOffering(Offering* offering, float amount, QString unitOfmeasurement, QString name, QString description)
954 {
955         addOffering(offering);
956         offering->addItem(this, amount, unitOfmeasurement, name, description);
957 }
958
959 Offering* Obj::firstOffering()
960 {
961         if (mOfferings.length() > 0) return mOfferings.at(0);
962         return NULL;
963 }
964
965 void Obj::addOptionalPropertyDescription(QStringList dataRanges, QString property)
966 {
967         addRestriction(new Restriction(NULL, NULL, NULL, true, dataRanges, property));
968 }
969
970 void Obj::addOptionalPropertyDescription(QStringList dataRanges, QStringList properties)
971 {
972         addRestriction(new Restriction(NULL, NULL, NULL, true, dataRanges, properties));
973 }
974
975 void Obj::addOptionalPropertyDescription(QString dataRange, QString property)
976 {
977         addRestriction(new Restriction(NULL, NULL, NULL, true, dataRange, property));
978 }
979
980 void Obj::addOptionalPropertyDescription(QString dataRange, QStringList properties)
981 {
982         addRestriction(new Restriction(NULL, NULL, NULL, true, dataRange, properties));
983 }
984
985 void Obj::addOptionalPropertyDescription(int numOfValues, QStringList dataRanges, QString property)
986 {
987         addRestriction(new Restriction(new Variant(numOfValues), NULL, NULL, true, dataRanges, property));
988 }
989
990 void Obj::addOptionalPropertyDescription(int numOfValues, QStringList dataRanges, QStringList properties)
991 {
992         addRestriction(new Restriction(new Variant(numOfValues), NULL, NULL, true, dataRanges, properties));
993 }
994
995 void Obj::addOptionalPropertyDescription(int numOfValues, QString dataRange, QString property)
996 {
997         addRestriction(new Restriction(new Variant(numOfValues), NULL, NULL, true, dataRange, property));
998 }
999
1000 void Obj::addOptionalPropertyDescription(int numOfValues, QString dataRange, QStringList properties)
1001 {
1002         addRestriction(new Restriction(new Variant(numOfValues), NULL, NULL, true, dataRange, properties));
1003 }
1004
1005 void Obj::addOptionalPropertyDescription(Variant* minNumOfValues, Variant* maxNumOfValues, QStringList dataRanges, QString property)
1006 {
1007         addRestriction(new Restriction(NULL, minNumOfValues, maxNumOfValues, true, dataRanges, property));
1008 }
1009
1010 void Obj::addOptionalPropertyDescription(Variant* minNumOfValues, Variant* maxNumOfValues, QStringList dataRanges, QStringList properties)
1011 {
1012         addRestriction(new Restriction(NULL, minNumOfValues, maxNumOfValues, true, dataRanges, properties));
1013 }
1014
1015 void Obj::addOptionalPropertyDescription(Variant* minNumOfValues, Variant* maxNumOfValues, QString dataRange, QString property)
1016 {
1017         addRestriction(new Restriction(NULL, minNumOfValues, maxNumOfValues, true, dataRange, property));
1018 }
1019
1020 void Obj::addOptionalPropertyDescription(Variant* minNumOfValues, Variant* maxNumOfValues, QString dataRange, QStringList properties)
1021 {
1022         addRestriction(new Restriction(NULL, minNumOfValues, maxNumOfValues, true, dataRange, properties));
1023 }
1024
1025 void Obj::addRequiredPropertyDescription(QStringList dataRanges, QString property)
1026 {
1027         addRestriction(new Restriction(NULL, NULL, NULL, false, dataRanges, property));
1028 }
1029
1030 void Obj::addRequiredPropertyDescription(QStringList dataRanges, QStringList properties)
1031 {
1032         addRestriction(new Restriction(NULL, NULL, NULL, false, dataRanges, properties));
1033 }
1034
1035 void Obj::addRequiredPropertyDescription(QString dataRange, QString property)
1036 {
1037         addRestriction(new Restriction(NULL, NULL, NULL, false, dataRange, property));
1038 }
1039
1040 void Obj::addRequiredPropertyDescription(QString dataRange, QStringList properties)
1041 {
1042         addRestriction(new Restriction(NULL, NULL, NULL, false, dataRange, properties));
1043 }
1044
1045 void Obj::addRequiredPropertyDescription(int numOfValues, QStringList dataRanges, QString property)
1046 {
1047         addRestriction(new Restriction(new Variant(numOfValues), NULL, NULL, false, dataRanges, property));
1048 }
1049
1050 void Obj::addRequiredPropertyDescription(int numOfValues, QStringList dataRanges, QStringList properties)
1051 {
1052         addRestriction(new Restriction(new Variant(numOfValues), NULL, NULL, false, dataRanges, properties));
1053 }
1054
1055 void Obj::addRequiredPropertyDescription(int numOfValues, QString dataRange, QString property)
1056 {
1057         addRestriction(new Restriction(new Variant(numOfValues), NULL, NULL, false, dataRange, property));
1058 }
1059
1060 void Obj::addRequiredPropertyDescription(int numOfValues, QString dataRange, QStringList properties)
1061 {
1062         addRestriction(new Restriction(new Variant(numOfValues), NULL, NULL, false, dataRange, properties));
1063 }
1064
1065 void Obj::addRequiredPropertyDescription(Variant* minNumOfValues, Variant* maxNumOfValues, QStringList dataRanges, QString property)
1066 {
1067         addRestriction(new Restriction(NULL, minNumOfValues, maxNumOfValues, false, dataRanges, property));
1068 }
1069
1070 void Obj::addRequiredPropertyDescription(Variant* minNumOfValues, Variant* maxNumOfValues, QStringList dataRanges, QStringList properties)
1071 {
1072         addRestriction(new Restriction(NULL, minNumOfValues, maxNumOfValues, false, dataRanges, properties));
1073 }
1074
1075 void Obj::addRequiredPropertyDescription(Variant* minNumOfValues, Variant* maxNumOfValues, QString dataRange, QString property)
1076 {
1077         addRestriction(new Restriction(NULL, minNumOfValues, maxNumOfValues, false, dataRange, property));
1078 }
1079
1080 void Obj::addRequiredPropertyDescription(Variant* minNumOfValues, Variant* maxNumOfValues, QString dataRange, QStringList properties)
1081 {
1082         addRestriction(new Restriction(NULL, minNumOfValues, maxNumOfValues, false, dataRange, properties));
1083 }
1084
1085 QMap<QString, PropertyList<GraphItem*>* > Obj::getAllProperties()
1086 {
1087         return mVariantMap;
1088 }
1089
1090
1091 PropertyList<GraphItem*>* Obj::getAll(QString property)
1092 {
1093         if (mVariantMap.contains(property)) {
1094                 return mVariantMap.value(property);
1095         }
1096         return NULL;
1097 }
1098
1099 void Obj::setProperties(QMap<QString, PropertyList<GraphItem*>* > properties)
1100 {
1101         mVariantMap = properties;
1102 }
1103
1104 bool Obj::has(QString property)
1105 {
1106         return mVariantMap.contains(property);
1107 }
1108
1109 void Obj::add(QString property, QList<Obj*> obj)
1110 {
1111         Variant* v = new Variant(obj);
1112         add(property, v);
1113 }
1114
1115 void Obj::add(QString property, QList<Map*> obj)
1116 {
1117         Variant* v = new Variant(obj);
1118         add(property, v);
1119 }
1120
1121 void Obj::add(QString property, PropertyList<Variant*> obj)
1122 {
1123         PropertyList<GraphItem*>* l;
1124         if (mVariantMap.contains(property)) {
1125                 l = mVariantMap.value(property);
1126         } else {
1127                 l = new PropertyList<GraphItem*>();
1128                 l->setParent(this);
1129         }
1130
1131         for (PropertyListItem<Variant*>* i = obj.firstItem(); i != NULL; i = i->next()) {
1132                 l->append(i->getItem());
1133         }
1134         mVariantMap.insert(property, l);
1135 }
1136
1137 void Obj::add(QString property, QVariant v)
1138 {
1139         add(property, new Variant(v));
1140 }
1141
1142 void Obj::add(QString property, GraphItem* v)
1143 {
1144         if (v != NULL) {
1145                 if (v->isPropertyList()) {
1146                         mVariantMap.insert(property,(PropertyList<GraphItem*>*)v);
1147                 } else {
1148                         PropertyList<GraphItem*>* l;
1149                         if (mVariantMap.contains(property)) {
1150                                 l = mVariantMap.value(property);
1151                         } else {
1152                                 l = new PropertyList<GraphItem*>();
1153                                 l->setParent(this);
1154                         }
1155                         l->append(v);
1156                         mVariantMap.insert(property, l);
1157                 }
1158         }
1159 }
1160
1161 void Obj::add(QUrl property, GraphItem* obj)
1162 {
1163         add(property.toString(), obj);
1164 }
1165
1166 void Obj::add(QString property, PropertyList<GraphItem*> obj)
1167 {
1168         PropertyList<GraphItem*>* l;
1169         if (mVariantMap.contains(property)) {
1170                 l = mVariantMap.value(property);
1171         } else {
1172                 l = new PropertyList<GraphItem*>();
1173                 l->setParent(this);
1174         }
1175
1176         for (PropertyListItem<GraphItem*>* i = obj.firstItem(); i != NULL; i = i->next())
1177                 l->append(i->getItem());
1178         mVariantMap.insert(property, l);
1179 }
1180
1181 void Obj::set(QString property, GraphItem* v)
1182 {
1183         PropertyList<GraphItem*>* l;
1184         if (mVariantMap.contains(property)) {
1185                 l = mVariantMap.value(property);
1186                 l->destroyCascade();
1187         } else {
1188                 l = new PropertyList<GraphItem*>();
1189                 l->setParent(this);
1190         }
1191         l->append(v);
1192         mVariantMap.insert(property, l);
1193 }
1194
1195 void Obj::remove(QString property, GraphItem* obj)
1196 {
1197         if (mVariantMap.contains(property)) {
1198                 PropertyList<GraphItem*>* l = mVariantMap.value(property);
1199                 cleanList(l, obj);
1200                 mVariantMap.insert(property, l);
1201         }
1202 }
1203
1204 GraphItem* Obj::get(QString property)
1205 {
1206         PropertyList<GraphItem*>* f = getAll(property);
1207         if (f) {
1208                 return f->at(0);
1209         }
1210         return NULL;
1211 }
1212
1213 QVariant Obj::getValue(QString property)
1214 {
1215         GraphItem* g =  get(property);
1216         if (g != NULL && g->isVariant())
1217                 return ((Variant*)g)->getValue();
1218         return QVariant();
1219 }
1220
1221 QString Obj::getAsQString(QString property)
1222 {
1223         GraphItem* g =  get(property);
1224         if (g != NULL && g->isVariant())
1225                 return ((Variant*)g)->getAsString();
1226         return QString();
1227 }
1228
1229 QList<QString> Obj::activePropertyNames()
1230 {
1231         QList<QString> ap;
1232         for (int i = 0; i < mVariantMap.keys().length(); i++) {
1233                 if (mVariantMap.value(mVariantMap.keys().at(i))->length() > 0) {
1234                         ap << mVariantMap.keys().at(i);
1235                 }
1236         }
1237         return ap;
1238 }
1239
1240 QList<QString> Obj::propertyNames()
1241 {
1242         return mVariantMap.keys();
1243 }
1244
1245 void Obj::clearProperties()
1246 {
1247         QSet<GraphItem*> trash;
1248         while (!mVariantMap.isEmpty()) {
1249                 PropertyList<GraphItem*>* vars = mVariantMap.take(mVariantMap.firstKey());
1250                 while (vars->length() > 0) vars->destroyFirstCascade(&trash);
1251                 delete vars;
1252         }
1253         QSet<GraphItem *>::const_iterator i = trash.constBegin();
1254         while (i != trash.constEnd()) { delete (*i); ++i; }
1255 }
1256
1257 /* FIXME
1258 QVariantList Obj::copyList(QList<Variant*> l)
1259 {
1260         QVariantList lc;
1261         for (int i = 0; i < l.length(); i++) {
1262                 QVariant v = l.at(i);
1263                 if (v.type() == QMetaType::VoidStar) {
1264                         Variant* o = new Variant((Variant*)v.value<void*>());
1265                         lc.append(qVariantFromValue((void*)o));
1266                 } else {
1267                         lc.append(v);
1268                 }
1269         }
1270
1271         return lc;
1272 }
1273 */
1274
1275
1276 void Obj::cleanList(PropertyList<GraphItem*>* l)
1277 {
1278         QSet<GraphItem*> trash;
1279         while (l->length() > 0) l->destroyFirstCascade(&trash);
1280         
1281         QSet<GraphItem *>::const_iterator i = trash.constBegin();
1282         while (i != trash.constEnd()) {
1283                 delete (*i);
1284                 ++i;
1285         }
1286 }
1287
1288 void Obj::cleanList(PropertyList<GraphItem*>* l, GraphItem* obj)
1289 {
1290         QSet<GraphItem*> trash;
1291         for (PropertyListItem<GraphItem*>* i = l->firstItem(); i != NULL; i = i->next()) {
1292                 if (i->getItem() == obj) i->getItem()->destroyCascade(&trash);
1293         }
1294         
1295         QSet<GraphItem *>::const_iterator i = trash.constBegin();
1296         while (i != trash.constEnd()) {
1297                 delete (*i);
1298                 ++i;
1299         }
1300 }
1301
1302 QString Obj::getIdentifierUriPart(int index)
1303 {
1304         if (hasIdentifierUri()) {
1305                 QStringList parts = getIdentifierUri().split("://");
1306                 if (parts.length() > 1) {
1307                         QStringList idParts = parts.at(1).split('/');
1308                         if (idParts.length() > index) {
1309                                 return QUrl::fromPercentEncoding(idParts.at(index).toLocal8Bit());
1310                         }
1311                 }
1312         }
1313
1314         return QString();
1315 }
1316
1317 QString Obj::asTurtle()
1318 {
1319         return Tools::toString(this, SERIALIZATION__TURTLE);
1320 }
1321
1322 void Obj::turtlePrint()
1323 {
1324         ObjectPrinter* op = new ObjectPrinter();  // note, the ObjectPrinter deletes itself automatically once printing is done
1325         op->printAsTurtle(this);
1326 }
1327
1328 void Obj::printOut(bool destroyAfterPrinting)
1329 {
1330         ObjectPrinter* op = new ObjectPrinter();  // note, the ObjectPrinter deletes itself automatically once printing is done
1331         op->printAsFlat(this, destroyAfterPrinting);
1332 }
1333
1334 void Obj::explain(bool destroyAfterPrinting)
1335 {
1336         ObjectPrinter* op = new ObjectPrinter();  // note, the ObjectPrinter deletes itself automatically once printing is done
1337         op->printWithExplanations(this, destroyAfterPrinting);
1338 }
1339