Improvements to Adapt sample server/client
[smartapi.git] / Common / Java / SmartAPI / src / smartapi / common / Tools.java
1 package smartapi.common;
2
3 import java.io.BufferedReader;
4 import java.io.ByteArrayInputStream;
5 import java.io.DataInputStream;
6 import java.io.File;
7 import java.io.FileInputStream;
8 import java.io.FileOutputStream;
9 import java.io.InputStream;
10 import java.io.InputStreamReader;
11 import java.io.OutputStream;
12 import java.io.StringWriter;
13 import java.io.UnsupportedEncodingException;
14 import java.io.IOException;
15 import java.lang.reflect.Field;
16 import java.lang.reflect.Method;
17 import java.lang.reflect.InvocationTargetException;
18 import java.net.URL;
19 import java.nio.charset.StandardCharsets;
20 import java.security.PrivateKey;
21 import java.security.PublicKey;
22 import java.sql.Time;
23 import java.util.ArrayList;
24 import java.util.Date;
25 import java.util.HashMap;
26 import java.util.List;
27 import java.util.Map;
28 import java.util.Properties;
29 import java.util.TimeZone;
30
31 import javax.servlet.http.HttpServletRequest;
32 import javax.xml.datatype.DatatypeFactory;
33 import javax.xml.datatype.Duration;
34
35 import org.apache.jena.rdf.model.Literal;
36 import org.apache.jena.rdf.model.Model;
37 import org.apache.jena.rdf.model.ModelFactory;
38 import org.apache.jena.rdf.model.Property;
39 import org.apache.jena.rdf.model.RDFNode;
40 import org.apache.jena.rdf.model.Resource;
41 import org.apache.jena.rdf.model.Statement;
42 import org.apache.jena.rdf.model.StmtIterator;
43 import org.apache.jena.util.FileManager;
44 import org.apache.commons.io.FileUtils;
45 import org.apache.commons.io.IOUtils;
46 import org.apache.commons.lang3.tuple.ImmutablePair;
47
48 import com.google.gson.JsonParser;
49
50 import smartapi.agents.OntologyAgent;
51 import smartapi.exceptions.ParseException;
52 import smartapi.factory.Factory;
53 import smartapi.model.Activity;
54 import smartapi.model.Address;
55 import smartapi.model.Availability;
56 import smartapi.model.Condition;
57 import smartapi.model.Coordinates;
58 import smartapi.model.Entity;
59 import smartapi.model.Grading;
60 import smartapi.model.Message;
61 import smartapi.model.Notification;
62 import smartapi.model.Obj;
63 import smartapi.model.Offering;
64 import smartapi.model.PriceSpecification;
65 import smartapi.model.Request;
66 import smartapi.model.Response;
67 import smartapi.model.SomeItems;
68 import smartapi.model.UnitPriceSpecification;
69 import smartapi.model.ValueObject;
70 import smartapi.rdf.Variant;
71 import smartapi.reasoner.UnitConverter;
72
73 public class Tools {
74
75         // HashMap<prefix, uri>
76         public static HashMap<String, String> prefixes;
77
78         // map of objects that are already serialized during the serialization process, cleared after serialization
79         public static HashMap<Obj, Resource> serializedObjs = new HashMap<Obj, Resource>();
80
81         // map of objects that are already parsed during the parsing process, cleared after parsing
82         public static HashMap<String, Obj> parsedObjs = new HashMap<String, Obj>();
83         
84         // map for storing message parts during serialization and parsing
85         public static Map<String, MessagePart> messageParts = new HashMap<String, MessagePart>();
86
87         // urlencoder
88         public static org.apache.catalina.util.URLEncoder encoder = new org.apache.catalina.util.URLEncoder();
89
90         // general use HTTP client
91         public static HttpClient httpClient = new HttpClient();
92         
93         // general use json parser
94         public static JsonParser jsonParser = new JsonParser();
95
96         /**
97          * Do some initialization for Currency-code converting functions
98          */
99         //private static OntModel smartapiOnt;
100
101         private static String mutex = "mutex";
102
103         static
104         {
105                 SmartAPI.ensureInit();
106         }
107         
108         public Tools()
109         {
110         }
111
112         /**
113          * 
114          * @return (privateKey, publicKey)
115          */
116         public static ImmutablePair<String, String> createCryptoKeys()
117         {
118                 try {
119                         ImmutablePair<PrivateKey, PublicKey> keyPair = Crypto.generateKeyPair();
120                         String publicKeyStr = Crypto.convertPublicKeyToPemFormat(keyPair.getRight());
121                         String privateKeyStr = Crypto.convertPrivateKeyToPemFormat(keyPair.getLeft());
122                         return new ImmutablePair<String, String>(privateKeyStr, publicKeyStr);
123                 } catch ( Exception e ) {
124                         System.err.println("Failed to create crypto keys.");
125                         e.printStackTrace();
126                         return null;
127                 }
128         }
129         
130         public static void uploadPublicKey(String publicKey, String identifier, String username, String password, String serverUri)
131         {
132                 try {
133                         Crypto.uploadPublicKey(publicKey, identifier, serverUri, username, password);
134                 } catch ( Exception e ) {
135                         System.err.println("Failed to upload public key.");
136                         e.printStackTrace();
137                 }
138         }
139         
140         public static void revokePublicKey(String identifier, String username, String password, String serverUri)
141         {
142                 try {
143                         Crypto.revokePublicKey(identifier, serverUri, username, password);
144                 } catch ( Exception e ) {
145                         System.err.println("Failed to revoke public key.");
146                         e.printStackTrace();
147                 }
148         }
149         
150         public static Request parseRequest(String content, String contentType)
151         {
152                 return Tools.parseMessage(Request.class, content, contentType);
153         }
154
155         public static Response parseResponse(String content, String contentType)
156         {
157                 return Tools.parseMessage(Response.class, content, contentType);
158         }
159
160         public static Notification parseNotification(String content, String contentType)
161         {
162                 return Tools.parseMessage(Notification.class, content, contentType);
163         }
164
165         public static Request parseMqttRequest(String content)
166         {
167                 return Tools.parseMqttMessage(Request.class, content);
168         }
169
170         public static Response parseMqttResponse(String content)
171         {
172                 return Tools.parseMqttMessage(Response.class, content);
173         }
174
175         public static Notification parseMqttNotification(String content)
176         {
177                 return Tools.parseMqttMessage(Notification.class, content);
178         }
179
180         public static <O extends Message> O parseMqttMessage(Class<O> clazz, String content)
181         {
182                 String contentType = null;
183                 String body = null;
184                 int endOfContentType = content.indexOf("\r\n\r\n");
185                 if ( endOfContentType > 0 ) {
186                         contentType = content.substring(0, endOfContentType);
187                         body = content.substring(endOfContentType + "\r\n\r\n".length());
188                 } else {
189                         try {
190                                 return (O)Tools.toObj(content);
191                         } catch ( Exception e ) {
192                                 System.err.println("Unable to parse MQTT message.");
193                                 e.printStackTrace();
194                                 return null;
195                         }
196                 }
197                 return Tools.parseMessage(clazz, body, contentType);
198         }
199
200         public static <O extends Message> O parseMessage(Class<O> clazz, String content, String contentType)
201         {
202                 HttpMessage httpMessage = null;
203                 try {
204                         httpMessage = HttpMessage.parse(content, contentType);
205                 } catch ( ParseException e ) {
206                         System.err.println("Exception while parsing string to HttpMessage.");
207                         System.err.println(content);
208                         e.printStackTrace();
209                         return null;
210                 }
211                 O message = null;
212                 if ( httpMessage.isSinglepart() ) {
213                         // get real content-type if this is a multipart message with just one part
214                         if ( contentType.startsWith("multipart/") ) {
215                                 contentType = HttpMessage.getMainRdfContentType(contentType);
216                         }
217                         // handle single part message
218                         String body = httpMessage.getBody();
219                         Obj obj = null;
220                         try {
221                                 if ( contentType != null ) {
222                                         obj = Tools.toObj(body, SERIALIZATION.fromContentType(contentType));
223                                 } else {
224                                         obj = Tools.toObj(body);
225                                 }
226                         } catch ( Exception e ) {
227                                 System.err.println("Exception while parsing body of a HttpMessage into " + clazz.getName() + ".");
228                                 System.err.println(content);
229                                 e.printStackTrace();
230                         }
231                         try { message = clazz.getDeclaredConstructor().newInstance(); }
232                         catch (InstantiationException e) {}
233                         catch (IllegalAccessException e) {}
234                         catch (NoSuchMethodException e) {}
235                         catch (InvocationTargetException e) {}
236                         try {
237                                 message = clazz.cast(obj);
238                         } catch ( Exception e ) {
239                                 System.err.println("Unable to cast received Obj to " + clazz.getName() + ".");
240                                 e.printStackTrace();
241                         }       
242                 } else {
243                         // handle multi-part message
244                         contentType = HttpMessage.getMainRdfContentType(contentType);
245                         String responseString = httpMessage.getMainPart();
246                         Obj obj = null;
247                         synchronized (mutex) {
248                                 Tools.messageParts = httpMessage.getParts();
249                                 try {
250                                         if ( SERIALIZATION.fromContentType(contentType).equals(SERIALIZATION.UNKNOWN) ) {
251                                                 obj = Tools.toObj(responseString);
252                                         } else {
253                                                 obj = Tools.toObj(responseString, SERIALIZATION.fromContentType(contentType));
254                                         }
255                                 } catch ( Exception e ) {
256                                         System.err.println("Exception while parsing Main part of a HttpMessage into " + clazz.getName() + ".");
257                                         e.printStackTrace();
258                                 }
259                                 Tools.clearMessageParts();
260                         }
261                         try { message = clazz.getDeclaredConstructor().newInstance(); }
262                         catch (InstantiationException e) {}
263                         catch (IllegalAccessException e) {}
264                         catch (NoSuchMethodException e) {}
265                         catch (InvocationTargetException e) {}
266                         try {
267                                 message = clazz.cast(obj);
268                         } catch ( Exception e ) {
269                                 System.err.println("Unable to cast received Obj to " + clazz.getName() + ".");
270                                 e.printStackTrace();
271                         }
272                 }
273                 return message;
274         }
275
276         public static ImmutablePair<String, String> serializeRequest(Request request)
277         {
278                 return Tools.serializeMessage(request, SERIALIZATION.DEFAULT);
279         }
280
281         public static ImmutablePair<String, String> serializeRequest(Request request, String serialization)
282         {
283                 return Tools.serializeMessage(request, serialization);
284         }
285
286         public static ImmutablePair<String, String> serializeResponse(Response response)
287         {
288                 return Tools.serializeMessage(response, SERIALIZATION.DEFAULT);
289         }
290
291         public static ImmutablePair<String, String> serializeResponse(Response response, String serialization)
292         {
293                 return Tools.serializeMessage(response, serialization);
294         }
295         
296         public static ImmutablePair<String, String> serializeNotification(Notification notification)
297         {
298                 return Tools.serializeMessage(notification, SERIALIZATION.DEFAULT);
299         }
300
301         public static ImmutablePair<String, String> serializeNotification(Notification notification, String serialization)
302         {
303                 return Tools.serializeMessage(notification, serialization);
304         }
305
306         public static <O extends Message> ImmutablePair<String, String> serializeMessage(O message, String serialization)
307         {
308                 HttpMessage httpMessage = Tools.serializeAsMessage(message, serialization);
309                 if (httpMessage != null)
310                         return new ImmutablePair<String, String>(httpMessage.asString(), httpMessage.getContentType());
311                 return new ImmutablePair<String, String>(Tools.toString(message, serialization), SERIALIZATION.toContentType(serialization));
312         }
313         
314         public static <O extends Message> HttpMessage serializeAsMessage(O message, String serialization)
315         {
316                 HttpMessage httpMessage = new HttpMessage();
317                 synchronized (mutex) {
318                         String origDefaultSerialization = SERIALIZATION.DEFAULT;
319                         SERIALIZATION.setDefaultSerialization(serialization);
320                         String messageString = Tools.toString(message, serialization);
321                         SERIALIZATION.setDefaultSerialization(origDefaultSerialization);
322                         if ( Tools.messageParts.isEmpty() ) {
323                                 return null;
324                         } else {
325                                 MessagePart mainPart = new MessagePart(messageString, SERIALIZATION.toContentType(serialization), HttpMessage.DEFAULT_MAIN_CONTENT_ID_HEADER_VALUE);
326                                 httpMessage.add(HttpMessage.DEFAULT_MAIN_CONTENT_ID_HEADER_VALUE, mainPart);
327
328                                 for ( Map.Entry<String, MessagePart> item : Tools.messageParts.entrySet() ) {
329                                         httpMessage.add(item.getKey(), item.getValue());
330                                 }
331                                 Tools.clearMessageParts();
332                         }
333                 }
334
335                 return httpMessage;
336         }
337
338         public static String toString(Model model, String serialization)
339         {
340                 // serialize
341                 String messageBody = "";
342                 try {
343                         StringWriter writer = new StringWriter();
344                         model.write(writer, serialization);
345                         messageBody = writer.toString();
346                 } catch(Exception e) {
347                         System.err.println("Exception while converting model into string");
348                         e.printStackTrace();
349                 }               
350                 return Tools.standardizeLineBreaks(messageBody);
351         }
352
353         public static String toString(Obj obj)
354         {
355                 return Tools.toString(obj, SERIALIZATION.DEFAULT);
356         }
357
358         public static void addPrefixMappings(Model model)
359         {
360                 HashMap<String, String> usedPrefixes = new HashMap<String, String>();
361
362                 StmtIterator i = model.listStatements();
363                 while ( i.hasNext() ) {
364                         Statement stmt = i.next();
365                         Resource subject = stmt.getSubject();
366                         if ( !subject.isAnon() ) {
367                                 String prefix = NS.getKnownPrefix(subject.getURI());
368                                 if ( prefix != null ) {
369                                         usedPrefixes.put(prefix, Tools.prefixes.get(prefix));
370                                 }
371                         }
372                         Resource predicate = stmt.getPredicate().asResource();
373                         if ( !predicate.isAnon() ) {
374                                 String prefix = NS.getKnownPrefix(predicate.getURI());
375                                 if ( prefix != null ) {
376                                         usedPrefixes.put(prefix, Tools.prefixes.get(prefix));
377                                 }
378                         }
379                         RDFNode objectAsNode = stmt.getObject();
380                         if ( objectAsNode.isResource() ) {
381                                 Resource object = objectAsNode.asResource();
382                                 if ( !object.isAnon() ) {
383                                         String prefix = NS.getKnownPrefix(object.getURI());
384                                         if ( prefix != null ) {
385                                                 usedPrefixes.put(prefix, Tools.prefixes.get(prefix));
386                                         }
387                                 }
388                         } else {
389                                 if ( objectAsNode.isLiteral() ) {
390                                         Literal objectAsLiteral = objectAsNode.asLiteral();
391                                         String prefix = NS.getKnownPrefix(objectAsLiteral.getDatatypeURI());
392                                         if ( prefix != null ) {
393                                                 usedPrefixes.put(prefix, Tools.prefixes.get(prefix));
394                                         }
395                                 }
396                         }
397                 }
398                 for ( Map.Entry<String, String> entry : usedPrefixes.entrySet() ) {
399                         model.setNsPrefix(entry.getKey(), entry.getValue());
400                 }
401         }
402
403         public static Model toModel(Obj obj)
404         {
405                 Model model = ModelFactory.createDefaultModel();
406
407                 // serialize object into jena rdf model
408                 obj.serialize(model);
409
410                 // reset to enable reserialization for the same object
411                 serializedObjs = new HashMap<Obj, Resource>();
412
413                 return model;
414         }
415
416         public static Resource toResource(Obj obj)
417         {
418                 Model model = ModelFactory.createDefaultModel();
419
420                 // serialize object into jena rdf model
421                 obj.serialize(model);
422
423                 // reset to enable reserialization for the same object
424                 serializedObjs = new HashMap<Obj, Resource>();
425
426                 try {
427                         return Tools.getRootResource(model);
428                 } catch ( Exception e ) {
429                         System.err.println("Failed to find root resource from model when converting Obj to Resource.");
430                 }
431
432                 return null;
433         }
434
435         public static Resource toResource(Obj obj, String resourceUri)
436         {
437                 Model model = ModelFactory.createDefaultModel();
438
439                 // serialize object into jena rdf model
440                 obj.serialize(model);
441
442                 // reset to enable reserialization for the same object
443                 serializedObjs = new HashMap<Obj, Resource>();
444
445                 return model.getResource(resourceUri);
446         }
447
448         public static String toString(Obj obj, String serialization)
449         {
450                 // create new jena rdf model
451                 //smartapi.rdf.Model model = Factory.createModel();
452                 //Model model = Factory.createModel();
453                 Model model = ModelFactory.createDefaultModel();
454
455                 // serialize object into jena rdf model
456                 obj.serialize(model);
457
458                 Tools.addPrefixMappings(model);
459
460                 // reset to enable reserialization for the same object
461                 serializedObjs = new HashMap<Obj, Resource>();
462
463                 // serialize model into string
464                 String messageBody = "";
465                 try {
466                         StringWriter writer = new StringWriter();
467                         model.write( writer, serialization);
468                         messageBody = writer.toString();
469                 } catch(Exception e) {
470                         System.err.println("Exception while converting model into string");
471                         e.printStackTrace();
472                 }
473                 return Tools.standardizeLineBreaks(messageBody);
474         }
475         
476         public static String toString(Resource resource, String serialization)
477         {
478                 //smartapi.rdf.Model model = Factory.createModel();
479                 //Model model = Factory.createModel();
480                 Model model = ModelFactory.createDefaultModel();
481
482                 addRecursivelyToModel(resource, model);
483
484                 Tools.addPrefixMappings(model);
485
486                 // serialize
487                 String messageBody = "";
488                 try {
489                         StringWriter writer = new StringWriter();
490                         model.write( writer, serialization);
491                         messageBody = writer.toString();
492                 } catch(Exception e) {
493                         System.err.println("Exception while converting model into string");
494                         e.printStackTrace();
495                 }               
496                 return Tools.standardizeLineBreaks(messageBody);
497         }
498
499         public static Model fromString(String string) throws Exception
500         {
501                 String serialization = SERIALIZATION.guessSerialization(string);
502                 if ( serialization == null || serialization.equals(SERIALIZATION.UNKNOWN) ) {
503                         throw new Exception("Exception while RDF parsing string to Model. Unable to determine RDF serialization.");
504                 }
505                 return Tools.fromString(string, serialization);
506         }
507
508         public static Model fromString(String string, String serialization)
509         {
510                 //smartapi.rdf.Model model = Factory.createModel();
511                 Model model = Factory.createModel();
512                 try {
513                         InputStream in = new ByteArrayInputStream(string.getBytes(StandardCharsets.UTF_8));
514                         model.read(in, null, serialization);
515                 } catch (Exception e) {
516                         // throw exception
517                 }
518                 return model;
519         }
520
521         public static Obj fromStringAsObj(String str, String serialization)
522         {
523                 try {
524                         return Tools.toObj(str, serialization);
525                 } catch (Exception e) {
526                         return null;
527                 }
528         }
529
530         public static Model fromFile(String fileName, String serialization)
531         {
532                 //smartapi.rdf.Model model = Factory.createModel();
533                 Model model = Factory.createModel();
534
535                 // use the FileManager to find the input file
536                 InputStream in = FileManager.get().open( fileName );
537                 if (in == null) {
538                         System.err.println("Unable to read file " + fileName);
539                         throw new IllegalArgumentException(
540                                         "File: " + fileName + " not found");
541                 }
542                 model.read(in, null, serialization);
543                 return model;
544         }
545
546         public static Obj fromFileAsObj(String fileName, String serialization)
547         {
548                 InputStream in = FileManager.get().open( fileName );
549                 if (in == null) {
550                         System.err.println("Unable to read file " + fileName);
551                         throw new IllegalArgumentException(
552                                         "File: " + fileName + " not found");
553                 }
554                 StringWriter writer = new StringWriter();
555                 try {
556                         IOUtils.copy(in, writer, "UTF-8");
557                 } catch (IOException ex) {
558                         System.err.println("Cannot read the contents of " + fileName);
559                         return null;
560                 }
561                 return fromStringAsObj(writer.toString(), serialization);
562         }
563
564         public static Model fromStream(InputStream in, String serialization)
565         {
566                 //smartapi.rdf.Model model = Factory.createModel();
567                 Model model = Factory.createModel();
568
569                 if (in == null) {
570                         throw new IllegalArgumentException(
571                                         "Stream: " + in + " erroneous.");
572                 }
573                 model.read(in, null, serialization);
574                 return model;
575         }
576
577         public static Model fromUri(String uri, String serialization)
578         {
579                 //smartapi.rdf.Model model = Factory.createModel();
580                 Model model = Factory.createModel();
581
582                 model.read(uri, serialization);
583                 return model;
584         }
585
586         public static Model fromUri(String uri)
587         {
588                 Model model = Factory.createModel();
589
590                 model.read(uri);
591                 return model;
592         }
593
594         private static void addRecursivelyToModel(Resource resource, Model model)
595         {
596                 // add this level statements
597                 model.add(resource.listProperties());
598
599                 // go through objects and add next level
600                 StmtIterator i = resource.listProperties();
601                 while(i.hasNext()){
602                         Statement s = i.next();
603                         if( s.getObject().isResource() ) {
604                                 Resource r = s.getObject().asResource();
605                                 addRecursivelyToModel(r, model);
606                         }
607                 }
608         }
609
610         public static Resource getResourceByType(String type, Model model) throws Exception
611         {
612                 // get( ?subject rdf:type <type> )
613                 Resource typeResource = model.createResource(type);
614                 Property typeProperty = model.createProperty(PROPERTY.getUri(PROPERTY.RDF_TYPE));
615                 StmtIterator i = model.listStatements((Resource)null,
616                                 typeProperty,
617                                 typeResource);
618                 Resource resource = null;
619                 if (i.hasNext()) {
620                         Statement statement = i.next();
621                         resource = statement.getSubject();
622                 } else {
623                         throw new Exception("Could not find resource by type "+ type + " from the provided model.");
624                 }
625
626                 return resource;
627         }
628
629         public static Obj toObj(String rdf) throws Exception
630         {
631                 String serialization = SERIALIZATION.guessSerialization(rdf);
632                 if ( serialization.equals(SERIALIZATION.UNKNOWN) ) {
633                         throw new Exception("Exception while RDF parsing string to SMARTAPI Obj. Unable to determine RDF serialization.");
634                 }
635                 return Tools.toObj(rdf, serialization);
636         }
637
638         public static Obj toObj(String rdf, String serialization) throws Exception
639         {
640                 Model model = Tools.fromString(rdf, serialization);
641                 Obj ret = Tools.getRootObj(model);
642                 // clear map to enable reparsing
643                 parsedObjs = new HashMap<String, Obj>();
644                 return ret;
645         }
646
647         public static Obj toObjById(String rdf, String identifier) throws Exception
648         {
649                 Model model = Tools.fromString(rdf);
650                 Obj ret = Tools.getObjById(model, identifier);
651                 // clear map to enable reparsing
652                 parsedObjs = new HashMap<String, Obj>();
653                 return ret;
654         }
655
656         public static Obj getRootObj(Model model) throws Exception
657         {
658                 // get( ?subject rdf:type ?type )
659                 Property typeProperty = model.createProperty(PROPERTY.getUri(PROPERTY.RDF_TYPE));
660                 StmtIterator i = model.listStatements((Resource)null,
661                                 typeProperty,
662                                 (Resource)null);
663                 Resource rootResource = null;
664                 Resource resource = null;
665                 while (i.hasNext()) {
666                         Statement statement = i.next();
667                         resource = statement.getSubject();
668                         // is this resource orphan
669                         StmtIterator i2 = model.listStatements((Resource)null,
670                                         (Property)null,
671                                         resource);
672                         if ( i2.hasNext() ) {
673                                 // not orphan, continue to the next resource
674                                 continue;
675                         } else {
676                                 rootResource = resource;
677                                 return Tools.toObj(rootResource);
678                         }
679                 }
680
681                 // No orphan was found. Probably a loop so just pick first node that has type property.
682                 System.out.println("No orphan was found. Probably a loop so just pick first node that has type property.");
683                 i = model.listStatements((Resource)null,
684                                 typeProperty,
685                                 (Resource)null);
686                 while (i.hasNext()) {
687                         Statement statement = i.next();
688                         return Tools.toObj(statement.getSubject());
689                 }
690                 
691                 if ( rootResource == null ) {
692                         throw new Exception("Could not find a root resource from the given model.");
693                 }
694
695                 return Tools.toObj(rootResource);
696         }
697
698         public static Obj getObjById(Model model, String identifier) throws Exception
699         {
700                 Resource res = model.getResource(identifier);
701                 return Tools.toObj(res);
702         }
703         
704         public static Obj toObj(Resource resource) throws Exception
705         {
706                 // get all types for the resource
707                 StmtIterator i = resource.listProperties(resource.getModel().createProperty(PROPERTY.getUri(PROPERTY.RDF_TYPE)));
708                 ArrayList<Obj> typesList = new ArrayList<Obj>();
709                 while ( i.hasNext() ) {
710                         typesList.add( new Obj(i.next().getResource().getURI()) );
711                 }               
712                 Obj typeClass = ClassMapper.getClass(typesList);
713
714                 if ( typeClass != null ) {
715                         Obj obj = null;
716                         Method method;
717                         try {
718                                 method = typeClass.getClass().getMethod("parse", Class.class, Resource.class);
719                                 obj = (Obj)method.invoke(typeClass, typeClass.getClass(), resource);
720                         } catch (Exception e) {
721                                 System.err.println("Exception while trying to convert resource into Obj (" + typeClass.getClass().getName() + ").");
722                                 e.printStackTrace();
723                                 throw new Exception("Exception while trying to convert resource into Obj (" + typeClass.getClass().getName() + ").");
724                         }
725
726                         return obj;
727                 } else {
728                         // no matching class found for any of the types
729                         return null;
730                 }
731         }
732
733         public static Resource getRootResource(Model model) throws Exception
734         {
735                 // get( ?subject rdf:type ?type )
736                 Property typeProperty = model.createProperty(PROPERTY.getUri(PROPERTY.RDF_TYPE));
737                 StmtIterator i = model.listStatements((Resource)null,
738                                 typeProperty,
739                                 (Resource)null);
740                 Resource resource = null;
741                 while (i.hasNext()) {
742                         Statement statement = i.next();
743                         resource = statement.getSubject();
744                         // is this resource orphan
745                         StmtIterator i2 = model.listStatements((Resource)null,
746                                         (Property)null,
747                                         resource);
748                         if ( i2.hasNext() ) {
749                                 // not orphan, continue to the next resource
750                                 continue;
751                         } else {
752                                 return resource;
753                         }
754
755                 }
756                 // No orphan was found. Probably a loop so just pick first node that has type property.
757                 System.out.println("No orphan was found. Probably a loop so just pick first node that has type property.");
758                 i = model.listStatements((Resource)null,
759                                 typeProperty,
760                                 (Resource)null);
761                 while (i.hasNext()) {
762                         Statement statement = i.next();
763                         return statement.getSubject();
764                 }
765
766                 throw new Exception("Could not find a root resource from the given model.");
767         }
768
769         public static boolean isList(Resource resource) throws Exception
770         {
771                 StmtIterator i = resource.listProperties(resource.getModel().createProperty(PROPERTY.getUri(PROPERTY.FIRST)));
772                 if (i.hasNext() ) {
773                         return true;
774                 }
775                 return false;
776         }
777
778         // @deprecated use List.parse instead
779         //      public static ArrayList parseList(Resource resource) throws Exception
780         //      {
781         //              ArrayList l = new ArrayList();
782         //              StmtIterator i = resource.listProperties(resource.getModel().createProperty(PROPERTY.FIRST));
783         //              if (i.hasNext() ) {
784         //                      Statement statement = i.next();
785         //                      Resource resO = statement.getObject().asResource();
786         //
787         //                      Obj o = toObj(resO);
788         //                      if (o == null) {
789         //                              Variant v = Variant.parse(statement.getObject().asResource());
790         //                              l.add(v);
791         //                      } else {
792         //                              l.add(o);
793         //                      }
794         //
795         //                      StmtIterator r = resO.listProperties(resource.getModel().createProperty(PROPERTY.REST));
796         //                      if (r.hasNext() ) {
797         //                              Statement rstmt = r.next();
798         //                              l.addAll(parseList(rstmt.getObject().asResource()));
799         //                      }
800         //              }
801         //              return l;
802         //      }
803
804         public static String getResourceType(Resource resource)
805         {
806
807                 Resource object = resource.getPropertyResourceValue(resource.getModel().createProperty(PROPERTY.getUri(PROPERTY.RDF_TYPE)));
808                 if ( object != null ) {
809                         return object.toString();
810                 }
811                 return null;
812         }
813
814         public static ArrayList<String> getResourceTypes(Resource resource)
815         {
816                 ArrayList<String> types = new ArrayList<String>();
817                 StmtIterator i = resource.listProperties(resource.getModel().createProperty(PROPERTY.getUri(PROPERTY.RDF_TYPE)));
818                 while( i.hasNext() ) {
819                         types.add(i.next().getResource().toString());
820                 }
821                 return types;
822         }
823
824 //      public static ArrayList<Variant> rdfListToVariantList(Obj list)
825 //      {
826 //              ArrayList<Variant> ret = new ArrayList<Variant>();
827 //
828 //              // add current item to arraylist
829 //              ret.add(list.getFirst(NS.RDF + "first"));
830 //
831 //              Obj rest = (Obj)list.getFirst(NS.RDF + "rest").getValue();
832 //              // until the end of the rdf list
833 //              while( !(NS.RDF + "nil").equalsIgnoreCase(rest.getIdentifierUri()) ) {
834 //                      System.out.println("id: " + rest.getIdentifierUri());
835 //                      // add current item to arraylist
836 //                      ret.add(rest.getFirst(NS.RDF + "first"));
837 //                      rest = (Obj)rest.getFirst(NS.RDF + "rest").getValue();
838 //              }
839 //
840 //              return ret;
841 //      }
842
843 //      /**
844 //       * Convert list stored as Obj into arraylist
845 //       * @param list
846 //       * @return
847 //       */
848 //      public static ArrayList<Obj> rdfListToObjList(Obj list)
849 //      {
850 //              ArrayList<Obj> ret = new ArrayList<Obj>();
851 //
852 //              // add current item to arraylist
853 //              ret.add((Obj)list.getFirst(NS.RDF + "first").getValue());
854 //
855 //              Obj rest = (Obj)list.getFirst(NS.RDF + "rest").getValue();
856 //              // until the end of the rdf list
857 //              while( !(NS.RDF + "nil").equalsIgnoreCase(rest.getIdentifierUri()) ) {
858 //                      System.out.println("id: " + rest.getIdentifierUri());
859 //                      // add current item to arraylist
860 //                      ret.add((Obj)rest.getFirst(NS.RDF + "first").getValue());
861 //                      rest = (Obj)rest.getFirst(NS.RDF + "rest").getValue();
862 //              }
863 //
864 //              return ret;
865 //      }
866
867         public static ArrayList<Variant> objListToVariantList(ArrayList<Obj> list)
868         {
869                 ArrayList<Variant> newList = new ArrayList<Variant>();
870                 for ( Obj obj : list ) {
871                         newList.add(new Variant(obj));
872                 }
873                 return newList;
874         }
875
876         public static Duration toDuration(String duration)
877         {
878                 try{
879                         Duration d = DatatypeFactory.newInstance().newDuration(duration);
880                         return d;
881                 } catch ( Exception e ) {
882                         return null;
883                 }
884         }
885
886         public static void printError(smartapi.model.Error error)
887         {
888                 StringBuilder builder = new StringBuilder();
889                 builder.append("*  ------   ERROR   ------  *\n");
890                 for ( Obj typeObj : error.getTypes() ) {
891                         String typeUri = typeObj.getIdentifierUri();
892                         String type = typeUri;
893                         switch (typeUri) {
894                         case RESOURCE.PARSEERROR:
895                                 type = "Parse error";
896                                 break;
897                         case RESOURCE.INVALIDPARAMS:
898                                 type = "Invalid parameters";
899                                 break;
900                         case RESOURCE.INVALIDREQUEST:
901                                 type = "Invalid request";
902                                 break;
903                         case RESOURCE.SERVERERROR:
904                                 type = "Server error";
905                                 break;
906                         default:
907                                 type = NS.localName(typeUri);
908                         }
909                         builder.append("* Type:          " + type + "\n");
910                 }
911                 if ( error.hasErrorCode() ) {
912                         builder.append("* Code:          " + error.getErrorCode() + " (" + STATUSCODE.asString(error.getErrorCode()) + ")\n");                  
913                 }
914                 String message = error.hasErrorMessage() ? error.getErrorMessage() : error.getDescription();
915                 builder.append("* Error message: " + message + "\n");
916                 builder.append("*  ------   ERROR   ------  *");
917                 System.out.println(builder.toString());
918         }
919
920         public static void printErrors(Obj obj) {
921                 for ( smartapi.model.Error error : obj.getErrors() ) {
922                         Tools.printError(error);
923                 }
924         }
925
926         protected static void handlePolicyViolation( String message, Exception e )
927         {
928                 // ignore mode -> continue
929                 if ( SmartAPI.executionMode == ExecutionMode.IGNORE )  {
930                         return;
931                 }
932                 // non-strict mode, print message and continue
933                 if ( SmartAPI.executionMode == ExecutionMode.NONSTRICT )  {
934                         System.err.println(message);
935                         if ( e != null ) {
936                                 e.printStackTrace();
937                         }
938                         System.err.println("Policy violation on non-strict execution mode. Continuing execution.");
939                         return;
940                 }
941                 // strict mode -> stop execution
942                 if ( SmartAPI.executionMode == ExecutionMode.STRICT )  {
943                         System.err.println(message);
944                         if ( e != null ) {
945                                 e.printStackTrace();
946                         }
947                         System.err.println("Policy violation on strict execution mode. Stopping execution.");
948                         System.exit(0);
949                 }
950         }
951
952         public static String fileToString(String filename) throws Exception {
953                 File f = new File(filename);
954                 FileInputStream fis = new FileInputStream(f);
955                 DataInputStream dis = new DataInputStream(fis);
956                 byte[] keyBytes = new byte[(int) f.length()];
957                 dis.readFully(keyBytes);
958                 dis.close();
959
960                 return new String(keyBytes);
961         }
962
963         public static Obj serializeParse(Obj o)
964         {
965                 try {
966                         String s = Tools.toString(o);
967                         return Tools.toObj(s);
968                 } catch ( Exception e ) {
969                         System.err.println("Exception while serializing and parsing back an object.");
970                         e.printStackTrace();
971                         return null;
972                 }
973         }
974
975         public static String serializeParseSerialize(Obj o)
976         {
977                 try {
978                         String serialized = Tools.toString(o);
979                         Obj parsed = Tools.toObj(serialized);
980                         return Tools.toString(parsed);
981                 } catch ( Exception e ) {
982                         System.err.println("Exception while serializing, parsing, and serializing again an object.");
983                         e.printStackTrace();
984                         return null;
985                 }
986         }
987
988         // Here are some wrappers for UnitConverter functions.
989         /**
990          * 
991          * @param currencyUnit
992          * @return the ISO 4217 code representing the currency
993          */
994         public static String currencyFromUnitToCode(String currencyUnit){
995
996                 return UnitConverter.getCurrencyFromUnitToCodeMap().get(currencyUnit);
997         }
998         /**
999          * 
1000          * @param currencyCode
1001          * @return the URL of the corresponding currency
1002          */
1003         public static String currencyFromCodeToUnit(String  currencyCode){
1004
1005                 return UnitConverter.getCurrencyFromUnitToCodeMap().inverse().get(currencyCode);
1006         }
1007
1008         /**
1009          * Convert value of ValueObject into given unit
1010          * @param valueObject
1011          * @param newUnit
1012          * @return
1013          */
1014         public static ValueObject convert(ValueObject valueObject, String newUnit) {
1015                 return UnitConverter.convert(valueObject, newUnit);
1016         }
1017
1018         /**
1019          * Convert value of from one unit into another
1020          * @param quantity
1021          * @param unit
1022          * @param value
1023          * @param newUnit
1024          * @return
1025          */
1026         public static ValueObject convert(String quantity, String unit, Variant value, String newUnit)
1027         {
1028                 return UnitConverter.convert(new ValueObject(quantity, unit, value), newUnit);
1029         }
1030
1031         /**
1032          * Deep copy an Obj by serializing it and parsing it back
1033          * @param obj
1034          * @return deep copied Obj
1035          */
1036         public static Obj deepCopy(Obj obj)
1037         {
1038                 try {
1039                         return Tools.toObj(obj.toTurtle(), SERIALIZATION.TURTLE);
1040                 } catch ( Exception e ) {
1041                         System.err.println("Exception while deep copying an object.");
1042                         e.printStackTrace();
1043                         return new Obj();
1044                 }
1045         }
1046
1047         //public static Obj clone(Obj obj)
1048         public static <O extends Obj> O clone(O obj)
1049         {               
1050 //              return Tools.deepCopy(obj);
1051                 O clone = null;
1052                 try {
1053                         clone = (O)obj.getClass().getDeclaredConstructor().newInstance();
1054                 } catch ( Exception e ) {
1055                         e.printStackTrace();
1056                 }
1057                 // copy identifier
1058                 if ( obj.hasIdentifierUri() ) {
1059                         clone.setIdentifierUri(obj.getIdentifierUri());
1060                 }
1061                 // copy properties
1062                 clone.setProperties(obj.getAllProperties());
1063                 return clone;
1064         }
1065
1066         /**
1067          * Test if given two Objs are semantically same, i.e.,
1068          * have same triples.
1069          * @param first
1070          * @param second
1071          * @return
1072          */
1073         public static boolean equals(Obj first, Obj second)
1074         {
1075                 Model model1 = Tools.toModel(first);
1076                 Model model2 = Tools.toModel(second);
1077                 return model1.isIsomorphicWith(model2);
1078         }
1079
1080         /**
1081          * Copy all properties from one object to another
1082          * @param from properties are copied from this object
1083          * @param to properties are copied to this object
1084          */
1085         public static void copy(Obj from, Obj to)
1086         {
1087 //              Resource r = Tools.toResource(from);
1088 //
1089 //              StmtIterator i = r.listProperties();
1090 //              while (i.hasNext()) {
1091 //                      Statement statement = i.next();
1092 //                      to.parse(statement);
1093 //              }
1094                 for ( Map.Entry<Integer, List> e : from.getAllProperties().entrySet() ) {
1095                         to.add(e.getKey(), e.getValue());
1096                 }
1097         }
1098
1099         /**
1100          * Move all properties from one object to another
1101          * @param from properties are copied and then removed from this object
1102          * @param to properties are placed to this object
1103          */
1104         public static void move(Obj from, Obj to)
1105         {
1106                 Tools.copy(from, to);
1107                 Tools.clear(from);
1108         }
1109
1110         /**
1111          * Remove all properties from the given object excluding original type (rdf:type).
1112          * @param obj
1113          */
1114         public static void clear(Obj obj)
1115         {
1116                 // create empty version of the object
1117                 Obj emptyObj = new Obj();
1118                 try {
1119                         emptyObj = (Obj)obj.getClass().getDeclaredConstructor().newInstance();
1120                 } catch ( Exception e ) {
1121                         e.printStackTrace();
1122                 }
1123                 obj.clearProperties();
1124                 List types = emptyObj.get(PROPERTY.RDF_TYPE);
1125                 if ( types != null && types.size() > 0 ) {
1126                         obj.set(PROPERTY.RDF_TYPE, types);      
1127                 }
1128         }
1129
1130 //      /**
1131 //       * Remove all properties from the given object excluding original type (rdf:type).
1132 //       * @param obj
1133 //       */
1134 //      public static void clear(Obj obj)
1135 //      {
1136 //              Class tmpClass = obj.getClass();
1137 //              // create empty version of the object
1138 //              Obj emptyObj = new Obj();
1139 //              try {
1140 //                      emptyObj = (Obj)tmpClass.newInstance();
1141 //              } catch ( Exception e ) {
1142 //                      e.printStackTrace();
1143 //              }
1144 //              // loop all inherited classes
1145 //              while (tmpClass != null) {
1146 //                      // get fields of the class
1147 //                      Field[] tfs = tmpClass.getDeclaredFields();
1148 //                      // create empty version of the inherited class
1149 //                      Object o = new Obj();
1150 //                      try {
1151 //                              o = tmpClass.getDeclaredConstructor().newInstance();
1152 //                      } catch ( Exception e ) {
1153 //                              e.printStackTrace();
1154 //                      }
1155 //                      // for each field
1156 //                      for ( Field f : tfs ) {
1157 //                              f.setAccessible(true);
1158 //                              try {
1159 //                                      // if field is the type list, replace it with the type list of the
1160 //                                      // empty version of the argument object
1161 //                                      if ( f.getName().equals("types") ) {
1162 //                                              f.set(obj, emptyObj.getTypes());
1163 //                                              continue;
1164 //                                      }
1165 //                                      // for any other field, replace with the initial value of the
1166 //                                      // inherited class
1167 //                                      f.set(obj, f.get(o));
1168 //                              } catch ( Exception e ) {
1169 //                                      e.printStackTrace();
1170 //                              }
1171 //                      }
1172 //                      tmpClass = tmpClass.getSuperclass();
1173 //              }
1174 //      }
1175
1176         public static void clearMessageParts()
1177         {
1178                 Tools.messageParts = new HashMap<String, MessagePart>();
1179         }
1180
1181         public static String fetchMultipart(String id)
1182         {
1183                 if ( Tools.messageParts.containsKey(id) ) {
1184                         MessagePart part = Tools.messageParts.get(id);
1185                         try {
1186                                 return part.getBody();
1187                         } catch ( Exception e ) {
1188                                 System.err.println("Unable to parse multipart with id " + id + " to Obj.");
1189                                 e.printStackTrace();
1190                                 return null;
1191                         }
1192                 } else {
1193                         System.err.println("Requested multipart with id " + id + " was not found.");
1194                         return null;
1195                 }
1196
1197         }
1198
1199         public static Obj fetchMultipartAsObject(String id)
1200         {
1201                 if ( Tools.messageParts.containsKey(id) ) {
1202                         MessagePart part = Tools.messageParts.get(id);
1203                         try {
1204                                 return Tools.toObj(part.getBody(), SERIALIZATION.fromContentType(part.getContentType()));
1205                         } catch ( Exception e ) {
1206                                 System.err.println("Unable to parse multipart with id " + id + " to Obj.");
1207                                 e.printStackTrace();
1208                                 return null;
1209                         }
1210                 } else {
1211                         System.err.println("Requested multipart with id " + id + " was not found.");
1212                         return null;
1213                 }
1214         }
1215
1216         public static Object variantToJson(Variant v)
1217         {
1218                 if ( v.isString() ) {
1219                         return "\"" + v.asString() + "\"";
1220                 }
1221                 if ( v.isDate() ) {
1222                         return "\"" + Tools.dateToString(v.asDate()) + "\"";
1223                 }
1224                 return v.getValue();
1225         }
1226
1227         public static Variant jsonToVariant(String json)
1228         {
1229                 try {
1230                         return new Variant(Integer.parseInt(json));
1231                 } catch ( Exception e) {}
1232                 try {
1233                         return new Variant(Double.parseDouble(json));
1234                 } catch ( Exception e) {}
1235                 try {
1236                         return new Variant(Tools.stringToDate(json));
1237                 } catch ( Exception e) {}
1238                 return new Variant(json);
1239         }
1240
1241         public static boolean isAvailable(Availability testedAvailability, Availability ruleAvailability)
1242         {
1243                 List<List<Availability>> testedCombinations = Tools.generateAvailabilityLists(testedAvailability);
1244                 List<List<Availability>> ruleCombinations = Tools.generateAvailabilityLists(ruleAvailability);
1245                 return Tools.isAvailable(testedCombinations, ruleCombinations);
1246         }
1247
1248         public static boolean isAvailable(List<List<Availability>> testedCombinations, List<List<Availability>> ruleCombinations)
1249         {
1250                 // if all availabilities in any of the ruleAvailabilityLists are fulfilled -> true
1251                 for( List<Availability> ruleAvailabilityList : ruleCombinations ) {
1252                         // if any of the testedAvailabilityLists fulfills all availabilities in ruleAvailabilityList -> true
1253                         for ( List<Availability> testedAvailabilityList : testedCombinations ) {
1254                                 // if this ruleAvailability is fulfilled by any availability in testedAvailabilityList, mark as fulfilled
1255                                 for ( Availability ruleAvailability : ruleAvailabilityList ) {
1256                                         // mark ruleAvailability as fulfilled if fulfilled by testedAvailability
1257                                         for ( Availability testedAvailability : testedAvailabilityList ) {
1258                                                 Tools.tryToFulfill(testedAvailability, ruleAvailability);
1259                                         }
1260                                 }
1261                                 // check if this testedAvailabilityList fulfilled this ruleAvailabilityList (all in ruleAvailabilityList marked as fulfilled)
1262                                 // if not, reset fulfilled tags and continue (reseting is done in the same loop)
1263                                 boolean allFulfilled = true;
1264                                 for ( Availability a : ruleAvailabilityList ) {
1265                                         if ( !a.fulfilled ) {
1266                                                 allFulfilled = false;
1267                                         } else {
1268                                                 a.fulfilled = false;
1269                                         }
1270                                 }
1271                                 if ( allFulfilled ) {
1272                                         return true;
1273                                 }
1274                         }
1275                 }
1276                 return false;
1277         }
1278
1279         /**
1280          * Should be called only with availabilities without conditions
1281          * @param tested
1282          * @param rule
1283          */
1284         public static void tryToFulfill(Availability tested, Availability rule)
1285         {
1286                 if ( rule.hasType(RESOURCE.TEMPORALAVAILABILITY) ) {
1287                         if ( tested.hasType(RESOURCE.TEMPORALAVAILABILITY) ) {
1288                                 if ( rule.hasTemporalContext() ) {
1289                                         if ( tested.hasTemporalContext() ) {
1290                                                 // test during
1291                                                 if ( rule.getTemporalContext().hasDuring() ) {
1292                                                         if ( tested.getTemporalContext().hasDuring() ) {
1293                                                                 if ( !rule.getTemporalContext().getDuring().equals(tested.getTemporalContext().getDuring()) ) {
1294                                                                         // tested has different during value -> not fulfilled
1295                                                                         return;
1296                                                                 }
1297                                                         } else {
1298                                                                 // tested does not define during but lets assume it means that it is available every day
1299                                                         }
1300                                                 }
1301                                                 // test tcx start
1302                                                 if ( rule.getTemporalContext().hasStart() ) {
1303                                                         if ( rule.getTemporalContext().getStart().isDate() ) {
1304                                                                 if ( tested.getTemporalContext().hasStart() ) {
1305                                                                         if ( tested.getTemporalContext().getStart().isDate() ) {
1306                                                                                 if ( rule.getTemporalContext().getStart().asDate().before(tested.getTemporalContext().getStart().asDate()) ) {
1307                                                                                         // rule availability starts before tested -> not fulfilled
1308                                                                                         return;
1309                                                                                 }                                                                               
1310                                                                         } else {
1311                                                                                 // rule tcx start is date and tested tcx start is not
1312                                                                                 return;
1313                                                                         }
1314                                                                 } else {
1315                                                                         // tested does not define start date but lets assume is means it is already available
1316                                                                 }
1317                                                         }
1318                                                         if ( rule.getTemporalContext().getStart().isTime() ) {
1319                                                                 if ( tested.getTemporalContext().hasStart() ) {
1320                                                                         if ( tested.getTemporalContext().getStart().isTime() ) {
1321                                                                                 if ( rule.getTemporalContext().getStart().asTime().before(tested.getTemporalContext().getStart().asTime()) ) {
1322                                                                                         // rule availability starts before tested -> not fulfilled
1323                                                                                         return;
1324                                                                                 }                                                                               
1325                                                                         } else {
1326                                                                                 // rule tcx start is time and tested tcx start is not
1327                                                                                 return;
1328                                                                         }
1329                                                                 } else {
1330                                                                         // tested does not define start time but lets assume is means it is available at all times
1331                                                                 }                                                               
1332                                                         }
1333                                                 }
1334                                                 // test tcx end
1335                                                 if ( rule.getTemporalContext().hasEnd() ) {
1336                                                         if ( rule.getTemporalContext().getEnd().isDate() ) {
1337                                                                 if ( tested.getTemporalContext().hasEnd() ) {
1338                                                                         if ( tested.getTemporalContext().getEnd().isDate() ) {
1339                                                                                 if ( rule.getTemporalContext().getEnd().asDate().after(tested.getTemporalContext().getEnd().asDate()) ) {
1340                                                                                         // rule availability ends after tested -> not fulfilled
1341                                                                                         return;
1342                                                                                 }
1343                                                                         } else {
1344                                                                                 // rule tcx end is date and tested tcx end is not
1345                                                                                 return;
1346                                                                         }
1347                                                                 } else {
1348                                                                         // tested does not define end date but lets assume is means it is available until forever
1349                                                                 }
1350                                                         }
1351                                                         if ( rule.getTemporalContext().getEnd().isTime() ) {
1352                                                                 if ( tested.getTemporalContext().hasEnd() ) {
1353                                                                         if ( tested.getTemporalContext().getEnd().isTime() ) {
1354                                                                                 if ( rule.getTemporalContext().getEnd().asTime().after(tested.getTemporalContext().getEnd().asTime()) ) {
1355                                                                                         // rule availability ends after tested -> not fulfilled
1356                                                                                         return;
1357                                                                                 }                                                                               
1358                                                                         } else {
1359                                                                                 // rule tcx end is time and tested tcx end is not
1360                                                                                 return;
1361                                                                         }
1362                                                                 } else {
1363                                                                         // tested does not define end time but lets assume is means it is available at all times
1364                                                                 }                                                               
1365                                                         }
1366                                                 }
1367                                                 // all temporal context checks passed -> fulfilled
1368                                                 rule.fulfilled = true;
1369                                         } else {
1370                                                 // tested does not have required temporal context -> not fulfilled
1371                                                 return;
1372                                         }
1373                                 }
1374                                 // temporal availability is fulfilled
1375                                 rule.fulfilled = true;
1376                                 return;
1377                         } else {
1378                                 // wrong type of availability -> not fulfilled
1379                                 return;
1380                         }
1381                 }
1382                 if ( rule.hasType(RESOURCE.PROPERTYBASEDAVAILABILITY) ) {
1383                         if ( tested.hasType(RESOURCE.PROPERTYBASEDAVAILABILITY) ) {
1384                                 if ( rule.hasProperty() && rule.getProperty().hasIdentifierUri() ) {
1385                                         String ruleProperty = rule.getProperty().getIdentifierUri();
1386                                         if ( tested.hasProperty() && tested.getProperty().hasIdentifierUri() ) {
1387                                                 String testedProperty = tested.getProperty().getIdentifierUri();
1388                                                 if ( ruleProperty.equals(testedProperty) ) {
1389                                                         // test address
1390                                                         if ( ruleProperty.equals(PROPERTY.HASADDRESS) ) {
1391                                                                 if ( rule.hasValue() ) {
1392                                                                         if ( tested.hasValue() ) {
1393                                                                                 Address ruleAddress;
1394                                                                                 try {
1395                                                                                         ruleAddress = rule.getFirstAs(Address.class, PROPERTY.HASADDRESS);
1396                                                                                 } catch ( Exception e ) {
1397                                                                                         // wrong type of value, lets assume fulfilled
1398                                                                                         rule.fulfilled = true;
1399                                                                                         return;
1400                                                                                 }
1401                                                                                 Address testedAddress;
1402                                                                                 try {
1403                                                                                         testedAddress = tested.getFirstAs(Address.class, PROPERTY.HASADDRESS);
1404                                                                                 } catch ( Exception e ) {
1405                                                                                         // wrong type of value -> not fulfilled
1406                                                                                         return;
1407                                                                                 }
1408                                                                                 if ( ruleAddress.hasCountry() ) {
1409                                                                                         if ( testedAddress.hasCountry() ) {
1410                                                                                                 if ( !testedAddress.getCountry().equals(ruleAddress.getCountry()) ) {
1411                                                                                                         // wrong country -> not fulfilled
1412                                                                                                         return;
1413                                                                                                 }
1414                                                                                         } else {
1415                                                                                                 // tested address does not have required country -> not fulfilled
1416                                                                                                 return;
1417                                                                                         }
1418                                                                                 }
1419                                                                                 if ( ruleAddress.hasCity() ) {
1420                                                                                         if ( testedAddress.hasCity() ) {
1421                                                                                                 if ( !testedAddress.getCity().equals(ruleAddress.getCity()) ) {
1422                                                                                                         // wrong city -> not fulfilled
1423                                                                                                         return;
1424                                                                                                 }
1425                                                                                         } else {
1426                                                                                                 // tested address does not have required city -> not fulfilled
1427                                                                                                 return;
1428                                                                                         }
1429                                                                                 }
1430                                                                                 if ( ruleAddress.hasStreetAddress() ) {
1431                                                                                         if ( testedAddress.hasStreetAddress() ) {
1432                                                                                                 if ( !testedAddress.getStreetAddress().equals(ruleAddress.getStreetAddress()) ) {
1433                                                                                                         // wrong street address -> not fulfilled
1434                                                                                                         return;
1435                                                                                                 }
1436                                                                                         } else {
1437                                                                                                 // tested address does not have required street address -> not fulfilled
1438                                                                                                 return;
1439                                                                                         }
1440                                                                                 }
1441                                                                                 if ( ruleAddress.hasZipCode() ) {
1442                                                                                         if ( testedAddress.hasZipCode() ) {
1443                                                                                                 if ( !testedAddress.getZipCode().equals(ruleAddress.getZipCode()) ) {
1444                                                                                                         // wrong zip code -> not fulfilled
1445                                                                                                         return;
1446                                                                                                 }
1447                                                                                         } else {
1448                                                                                                 // tested address does not have required zip code -> not fulfilled
1449                                                                                                 return;
1450                                                                                         }
1451                                                                                 }
1452                                                                         } else {
1453                                                                                 // tested does not define address value -> not fulfilled
1454                                                                                 return;
1455                                                                         }
1456                                                                 } else {
1457                                                                         // rule does not define address value, assume tested value fulfilling that
1458                                                                 }
1459                                                                 // property based availability address fulfilled
1460                                                                 rule.fulfilled = true;
1461                                                                 return;
1462                                                         }
1463                                                 } else {
1464                                                         // tested and rule define different properties -> not fulfilled
1465                                                         return;
1466                                                 }
1467                                         } else {
1468                                                 // tested does not have property definition and rule has -> not fulfilled
1469                                                 return;
1470                                         }
1471                                 } else {
1472                                         // rule does not have property definition so we have to assume it fulfilled
1473                                 }
1474                                 // property based availability is fulfilled
1475                                 rule.fulfilled = true;
1476                                 return;
1477                         } else {
1478                                 // wrong type of availability -> not fulfilled
1479                                 return;
1480                         }                       
1481                 }
1482                 // if we got here, rule availability is not Temporal- or PropertyBased, assume that it is empty and thus fulfilled by anything
1483                 rule.fulfilled = true;
1484         }
1485
1486         private static List<List<Availability>> generateAvailabilityLists(Availability testedAvailability)
1487         {
1488                 if ( testedAvailability.hasCondition() ) {
1489                         Condition cond = testedAvailability.getCondition();
1490                         if ( cond.hasAnd() ) {
1491                                 List<List<List<Availability>>> andList = new ArrayList<List<List<Availability>>>();
1492                                 for ( Variant variant : cond.getAnds() ) {
1493                                         Obj andObj = variant.asObj();
1494                                         if ( andObj instanceof Availability ) {
1495                                                 andList.add(Tools.generateAvailabilityLists((Availability)andObj));
1496                                                 continue;
1497                                         }
1498                                         if ( andObj instanceof Condition ) {
1499                                                 andList.add(Tools.generateAvailabilityLists((Condition)andObj));
1500                                                 continue;
1501                                         }
1502                                 }
1503                                 return Tools.generateListCombinations(andList);
1504                         }
1505                         if ( cond.hasOr() ) {
1506                                 List<List<Availability>> orList = new ArrayList<List<Availability>>();
1507                                 for ( Variant variant : cond.getOrs() ) {
1508                                         Obj orObj = variant.asObj();
1509                                         if ( orObj instanceof Availability ) {
1510                                                 orList.addAll(Tools.generateAvailabilityLists((Availability)orObj));
1511                                                 continue;
1512                                         }
1513                                         if ( orObj instanceof Condition ) {
1514                                                 orList.addAll(Tools.generateAvailabilityLists((Condition)orObj));
1515                                                 continue;
1516                                         }
1517                                 }
1518                                 return orList;
1519                         }
1520                         return new ArrayList<List<Availability>>();
1521                 } else {
1522                         // does not have condition, must be just one Availability
1523                         List<List<Availability>> ret = new ArrayList<List<Availability>>();
1524                         List<Availability> list = new ArrayList<Availability>();
1525                         list.add(testedAvailability);
1526                         ret.add(list);
1527                         return ret;
1528                 }
1529         }
1530
1531         private static List<List<Availability>> generateAvailabilityLists(Condition condition)
1532         {
1533                 if ( condition.hasAnd() ) {
1534                         List<List<List<Availability>>> andList = new ArrayList<List<List<Availability>>>();
1535                         for ( Variant variant : condition.getAnds() ) {
1536                                 Obj andObj = variant.asObj();
1537                                 if ( andObj instanceof Availability ) {
1538                                         andList.add(Tools.generateAvailabilityLists((Availability)andObj));
1539                                         continue;
1540                                 }
1541                                 if ( andObj instanceof Condition ) {
1542                                         andList.add(Tools.generateAvailabilityLists((Condition)andObj));
1543                                         continue;
1544                                 }
1545                         }
1546                         return Tools.generateListCombinations(andList);
1547                 }
1548                 if ( condition.hasOr() ) {
1549                         List<List<Availability>> orList = new ArrayList<List<Availability>>();
1550                         for ( Variant variant : condition.getOrs() ) {
1551                                 Obj orObj = variant.asObj();
1552                                 if ( orObj instanceof Availability ) {
1553                                         orList.addAll(Tools.generateAvailabilityLists((Availability)orObj));
1554                                         continue;
1555                                 }
1556                                 if ( orObj instanceof Condition ) {
1557                                         orList.addAll(Tools.generateAvailabilityLists((Condition)orObj));
1558                                         continue;
1559                                 }
1560                         }
1561                         return orList;
1562                 }
1563                 return new ArrayList<List<Availability>>();
1564         }
1565
1566         public static <O extends Object> List<List<O>> generateListCombinations(List<List<List<O>>> input)
1567         {
1568                 List<List<O>> ret = input.get(0);
1569                 // for each list of lists
1570                 for ( int i=1; i < input.size(); i++ ) {
1571                         List<List<O>> newList = new ArrayList<List<O>>();
1572                         for ( List<O> list1 : ret ) {
1573                                 for ( List<O> list2 : input.get(i) ) {
1574                                         List<O> combinedList = new ArrayList<O>();
1575                                         combinedList.addAll(list1);
1576                                         combinedList.addAll(list2);
1577                                         newList.add(combinedList);
1578                                 }
1579                         }
1580                         ret = newList;
1581                 }
1582                 return ret;
1583         }
1584
1585         /**
1586          * Checks if testOfferings have cheaper or equal prices for each ruleOffering.
1587          * @param testedOfferings
1588          * @param ruleOfferings
1589          * @return
1590          */
1591         public static boolean isCheaperOrEqual(List<Offering> testedOfferings, List<Offering> ruleOfferings)
1592         {
1593                 // test each ruleOffering
1594                 for ( Offering ruleOffering : ruleOfferings ) {
1595                         // test if any of the testedOfferings is cheaper
1596                         boolean foundCheaper = false;
1597                         for ( Offering testedOffering : testedOfferings ) {
1598                                 if ( Tools.isCheaperOrEqual(testedOffering, ruleOffering) ) {
1599                                         foundCheaper = true;
1600                                         break;
1601                                 }
1602                         }
1603                         // if there was not cheaper offering for this ruleOffering 
1604                         if ( !foundCheaper ) {
1605                                 return false;
1606                         }
1607                 }
1608                 return true;
1609         }
1610
1611         public static boolean isCheaperOrEqual(Offering testedOffering, Offering ruleOffering)
1612         {
1613                 // get unit prices
1614                 Float ruleUnitPrice = null;
1615                 String ruleUnitCurrency = null;
1616                 Float testedUnitPrice = null;
1617                 String testedUnitCurrency = null;
1618                 if ( ruleOffering.hasPriceSpecification() ) {
1619                         List<PriceSpecification> rulePriceSpefications = ruleOffering.getPriceSpecification();
1620                         for ( PriceSpecification rulePriceSpefication : rulePriceSpefications ) {
1621                                 // take first found unit price specification that has price and currency
1622                                 if ( rulePriceSpefication instanceof UnitPriceSpecification ) {
1623                                         if ( rulePriceSpefication.hasCurrencyValue() && rulePriceSpefication.hasCurrency() ) {
1624                                                 ruleUnitPrice = rulePriceSpefication.getCurrencyValue();
1625                                                 ruleUnitCurrency = rulePriceSpefication.getCurrency();
1626                                                 break;
1627                                         }
1628                                 }
1629                         }
1630                         if ( ruleUnitPrice == null || ruleUnitCurrency == null ) {
1631                                 // price or currency missing from rule offering, assume that anything is cheaper than undefined
1632                                 return true;
1633                         }
1634                         if ( testedOffering.hasPriceSpecification() ) {
1635                                 List<PriceSpecification> testedPriceSpefications = testedOffering.getPriceSpecification();
1636                                 for ( PriceSpecification testedPriceSpefication : testedPriceSpefications ) {
1637                                         // take first found unit price specification that has price and currency
1638                                         if ( testedPriceSpefication instanceof UnitPriceSpecification ) {
1639                                                 if ( testedPriceSpefication.hasCurrencyValue() && testedPriceSpefication.hasCurrency() ) {
1640                                                         testedUnitPrice = testedPriceSpefication.getCurrencyValue();
1641                                                         testedUnitCurrency = testedPriceSpefication.getCurrency();
1642                                                         break;
1643                                                 }
1644                                         }
1645                                 }
1646                                 if ( testedUnitPrice == null || testedUnitCurrency == null ) {
1647                                         // price or currency missing from tested offering, assume it more expensive than rule offering
1648                                         return false;
1649                                 }
1650
1651                         } else {
1652                                 // price specification missing from tested offering, assume it more expensive than rule offering
1653                                 return false;
1654                         }
1655                 } else {
1656                         // price specification missing from rule offering, assume that anything is cheaper than undefined
1657                         return true;                    
1658                 }
1659                 // convert into same currency if needed
1660                 if ( !ruleUnitCurrency.equals(testedUnitCurrency) ) {
1661                         String ruleUnitQudtCurrency = Tools.currencyFromCodeToUnit(ruleUnitCurrency);
1662                         String testedUnitQudtCurrency = Tools.currencyFromCodeToUnit(testedUnitCurrency);
1663                         if ( ruleUnitQudtCurrency == null || testedUnitQudtCurrency == null ) {
1664                                 if ( ruleUnitQudtCurrency == null ) {
1665                                         System.err.println("Invalid currency unit in rule Offering unit price specification: " + ruleUnitCurrency);
1666                                         return false;
1667                                 }
1668                         }
1669                         try {
1670                                 testedUnitPrice = Float.parseFloat(Tools.convert(RESOURCE.CURRENCY, testedUnitQudtCurrency, new Variant(testedUnitPrice), ruleUnitQudtCurrency).getValue().asString());                         
1671                         } catch ( Exception e ) {
1672                                 // unable to convert values into same currency, assume invalid tested offering and interpret as more expensive than rule offering
1673                                 return false;
1674                         }
1675                 }
1676                 // now do the actual price comparison taking into account quantity/duration of items/subscription
1677                 if ( ruleOffering.hasIncludes() ) {
1678                         if ( testedOffering.hasIncludes() ) {
1679                                 List<SomeItems> ruleOfferItems = ruleOffering.getIncludes();
1680                                 List<SomeItems> testedOfferItems = testedOffering.getIncludes();
1681                                 // are there cheaper items in testedOffer for each item in ruleOffer
1682                                 for ( SomeItems ruleOfferItem : ruleOfferItems ) {
1683                                         // test if any of the testedOfferItems is cheaper that ruleOfferItem
1684                                         boolean foundCheaper = false;
1685                                         for ( SomeItems testedOfferItem : testedOfferItems ) {
1686                                                 if ( Tools.isCheaperOrEqual(testedOfferItem, ruleOfferItem, testedUnitPrice, ruleUnitPrice) ) {
1687                                                         foundCheaper = true;
1688                                                         break;
1689                                                 }
1690                                         }
1691                                         // if there was not cheaper item for this ruleOfferItem
1692                                         if ( !foundCheaper ) {
1693                                                 return false;
1694                                         }
1695                                 }
1696                                 return true;
1697                         } else {
1698                                 // tested does not have includes value while rule has -> not cheaper
1699                                 return false;
1700                         }
1701                 } else {
1702                         // rule offering does not define any items, assume that any item is ok then
1703                         return true;
1704                 }
1705         }
1706
1707         public static boolean isCheaperOrEqual(SomeItems testedOfferItem, SomeItems ruleOfferItem, float testedUnitPrice, float ruleUnitPrice) 
1708         {
1709                 if ( ruleOfferItem.hasType(RESOURCE.SERVICESUBSCRIPTION) ) {
1710                         if ( testedOfferItem.hasType(RESOURCE.SERVICESUBSCRIPTION) ) {
1711                                 if ( ruleOfferItem.hasDuration() ) {
1712                                         Duration ruleDuration = ruleOfferItem.getDuration();
1713                                         if ( testedOfferItem.hasDuration() ) {
1714                                                 Duration testedDuration = testedOfferItem.getDuration();
1715                                                 if ( (testedUnitPrice / Tools.durationToSeconds(testedDuration)) <= (ruleUnitPrice / Tools.durationToSeconds(ruleDuration)) ) {
1716                                                         // tested price per time is cheaper than rule
1717                                                         return true;
1718                                                 } else {
1719                                                         // tested price per time is more expensive than rule
1720                                                         return false;
1721                                                 }
1722                                         } else {
1723                                                 // tested item does not have duration while rule has -> not cheaper
1724                                                 return false;
1725                                         }
1726                                 } else {
1727                                         // rule does not define duration, so we can assume that tested is cheaper
1728                                         return true;
1729                                 }
1730                         } else {
1731                                 // testedOfferItem describes wrong type of item
1732                                 return false;
1733                         }
1734                 }
1735                 if ( ruleOfferItem.hasType(RESOURCE.REQUEST) ) {
1736                         if ( testedOfferItem.hasType(RESOURCE.REQUEST) ) {
1737                                 if ( testedUnitPrice <= ruleUnitPrice ) {
1738                                         // tested price is cheaper than rule
1739                                         return true;
1740                                 } else {
1741                                         // tested price is more expensive than rule
1742                                         return false;
1743                                 }
1744                         } else {
1745                                 // testedOfferItem describes wrong type of item
1746                                 return false;
1747                         }
1748                 }
1749                 // to make compile!!!
1750                 System.err.println("Offering price comparison not implemented for the given item type, or type not defined in search.");
1751                 return false;
1752         }
1753
1754         public static int durationToSeconds(Duration duration)
1755         {
1756                 int ret = 0;
1757                 ret += duration.getYears() * 31536000; // 60*60*24*365
1758                 ret += duration.getMonths() * 2592000; // 60*60*24*30
1759                 ret += duration.getDays() * 86400; // 60*60*24
1760                 ret += duration.getHours() * 3600; // 60*60
1761                 ret += duration.getMinutes() * 60;
1762                 ret += duration.getSeconds();
1763                 return ret;
1764         }
1765
1766         public static Model getModel(String ontologyPrefixUri)
1767         {
1768                 // replace this with requesting files from ontology server
1769                 return Tools.fromUri(OntologyAgent.getOntologyDownloadUri(ontologyPrefixUri));
1770
1771         }
1772
1773 //      public static void handleConceptValidation(String concept)
1774 //      {
1775 //              // ignore mode -> continue
1776 //              if ( SmartAPI.validationMode == ValidationMode.NO_VALIDATION ) {
1777 //                      return;
1778 //              }
1779 //              if ( SmartAPI.validationMode == ValidationMode.LOCAL_VALIDATION ) {
1780 //                      if ( ConceptValidator.isValidConcept(concept) ) {
1781 //                              return;
1782 //                      } else {
1783 //                              Tools.handlePolicyViolation("Unable to validate concept (" + concept + ") " +
1784 //                                              "using local validation. If the concept is newly added try changing " +
1785 //                                              "to real-time validation with:\n" +
1786 //                                              "  SmartAPI.setValidationMode(ValidationMode.REALTIME_VALIDATION);", null);
1787 //                      }
1788 //              }
1789 //              if ( SmartAPI.validationMode == ValidationMode.REALTIME_VALIDATION ) {
1790 //                      if ( ConceptValidator.isValidConcept(concept) ) {
1791 //                              return;
1792 //                      } else {
1793 //                              Tools.handlePolicyViolation("Unable to validate concept (" + concept + ") " +
1794 //                                              "using real-time validation. If you are not connected to the Internet " +
1795 //                                              "try changing to local validation with:\n" +
1796 //                                              "  SmartAPI.setValidationMode(ValidationMode.LOCAL_VALIDATION);", null);
1797 //                      }
1798 //              }
1799 //      }
1800
1801         /**
1802          * URL Encodes a string (spaces are converted into %20)
1803          * @param str string to be encoded
1804          * @return encoded string
1805          */
1806         public static String urlEncode(String str)
1807         {
1808                 return Tools.encoder.encode(str);
1809         }
1810
1811         /**
1812          * Decodes URL encoded string
1813          * @param str string to be decoded
1814          * @return decoded string
1815          */
1816         public static String urlDecode(String str)
1817         {
1818                 try {
1819                         return java.net.URLDecoder.decode(str, "UTF-8");                        
1820                 }catch ( Exception e ) {
1821                         System.err.println("Failed to decode url using java.net.URLDecoder.decode.");
1822                         e.printStackTrace();
1823                         return str;
1824                 }
1825                 //return org.apache.catalina.util.RequestUtil.URLDecode(str, "UTF-8");
1826         }
1827
1828         public static String standardizeLineBreaks(String str) {
1829                 return str.replaceAll("\\r\\n|\\r|\\n", "\r\n");
1830         }
1831         
1832         public static String createIdentifierUri(String myDomain, String systemIdentifier, String... systemObjectIdentifier)
1833         {
1834                 if ( myDomain == null || myDomain.equals("") ) {
1835                         myDomain = SmartAPI.getMyIdentity().split("://")[1].split("/")[0];
1836                 }               
1837                 
1838                 StringBuilder builder = new StringBuilder();
1839                 if ( myDomain.startsWith("http://") ) {
1840                         builder.append(myDomain);
1841                 } else {
1842                         builder.append("http://");
1843                         builder.append(myDomain);
1844                 }
1845                 if ( systemIdentifier != null ) {
1846                         builder.append("/");
1847                         builder.append(Tools.urlEncode(systemIdentifier));                      
1848                 }
1849                 for ( String soid : systemObjectIdentifier ) {
1850                         if ( soid != null ) {
1851                                 builder.append("/");
1852                                 builder.append(Tools.urlEncode(soid));
1853                         }                       
1854                 }
1855                 return builder.toString();
1856         }
1857         
1858 //      /**
1859 //       * 
1860 //       * @param method RESOURCE.READ, RESOURCE.WRITE..
1861 //       * @param timeSeries does the searched activity consider timeseries
1862 //       * @param object activities are searched from this entity
1863 //       * @param valueObjects search is done for value objects in the entity with quantities that are present in these value objects
1864 //       * @return
1865 //       */
1866 //      public static Activity getMostCommonActivity(String method, boolean timeSeries, Entity object, ValueObject... valueObjects)
1867 //      {
1868 //              if ( valueObjects == null || valueObjects.length == 0 ) {
1869 //                      valueObjects = object.getValueObjects().toArray(new ValueObject[object.getValueObjects().size()]);
1870 //              }
1871 //              // try to find a common read activity uri for all valueobjects
1872 //              Map<Activity, Integer> commonActivities = new HashMap<Activity, Integer>();
1873 //              for ( ValueObject v : valueObjects ) {
1874 //                      ValueObject vo = object.getValueObject(v.getQuantity());
1875 //                      if ( vo == null || !vo.hasCapability() ) {
1876 //                              vo = v;
1877 //                      }
1878 //                      for ( Activity a : vo.getCapabilities() ) {
1879 //                              if ( a.getMethod().equals(method) ) {
1880 //                                      if ( (timeSeries && a.hasEntity() && a.getEntities().get(0).hasTimeSerie()) ||
1881 //                                                      (!timeSeries && a.hasEntity() && !a.getEntities().get(0).hasTimeSerie()) ) {
1882 //                                              if ( commonActivities.containsKey(a) ) {
1883 //                                                      commonActivities.put(a, commonActivities.get(a)+1);
1884 //                                              } else {
1885 //                                                      commonActivities.put(a, 1);
1886 //                                              }
1887 //                                              
1888 //                                      }
1889 //                              }
1890 //                      }
1891 //              }
1892 //              // get one with most matches
1893 //              Object[] entries = commonActivities.entrySet().toArray();
1894 //              Activity selectedActivity = null;
1895 //              int numberOfMatches = 0;
1896 //              for ( Object entryObj : entries ) {
1897 //                      Map.Entry<Activity, Integer> entry = (Map.Entry<Activity, Integer>)entryObj;
1898 //                      if ( entry.getValue() > numberOfMatches ) {
1899 //                              selectedActivity = entry.getKey();
1900 //                              numberOfMatches = entry.getValue();
1901 //                      }
1902 //              }
1903 //              return selectedActivity;
1904 //      }
1905         
1906         /**
1907          * 
1908          * @param method RESOURCE.READ, RESOURCE.WRITE..
1909          * @param timeSeries does the searched activity consider timeseries
1910          * @param object activities are searched from this entity
1911          * @param valueObjects search is done for value objects in the entity with quantities that are present in these value objects
1912          * @return
1913          */
1914         public static Activity getMostCommonActivity(String method, boolean timeSeries, Entity entity, ValueObject... valueObjects)
1915         {
1916                 // if no valueobjects given, choose all valueobjects of the entity
1917                 if ( valueObjects == null || valueObjects.length == 0 ) {
1918                         valueObjects = entity.getValueObjects().toArray(new ValueObject[entity.getValueObjects().size()]);
1919                 }
1920                 // go through all valueobjects
1921                 // <activity, number of occurrences>
1922                 Map<Activity, Integer> commonActivities = new HashMap<Activity, Integer>();
1923                 for ( ValueObject v : valueObjects ) {
1924                         if ( v == null ) {
1925                                 continue;
1926                         }
1927                         // get equivalent vo from the entity
1928                         ValueObject vo = entity.getValueObjectByIdentifier(v.getIdentifierUri());
1929                         if ( vo == null ) {
1930                                 List<ValueObject> vos = entity.getValueObjectByQuantity(v.getQuantity());
1931                                 if ( vos.size() > 0 ) {
1932                                         vo = vos.get(0); // just get first                                      
1933                                 }
1934                         }
1935                         if ( vo == null ) {
1936                                 vo = v;
1937                         }
1938                         // go through capabilities of the entity
1939                         for ( Activity a : entity.getCapabilities() ) {
1940                                 // if has required method
1941                                 if ( a.getMethod().equals(method) ) {
1942                                         // if has or not required possible timeserie/tcx
1943                                         if ( method.equals(RESOURCE.READ) ) {
1944                                                 if ( (timeSeries && a.hasTemporalContext()) ||
1945                                                                 (!timeSeries && !a.hasTemporalContext()) ) {
1946                                                         if ( Tools.supportsThisValueObject(a, vo) ) {
1947                                                                 if ( commonActivities.containsKey(a) ) {
1948                                                                         commonActivities.put(a, commonActivities.get(a)+1);
1949                                                                 } else {
1950                                                                         commonActivities.put(a, 1);
1951                                                                 }                                                               
1952                                                         }
1953                                                 }
1954                                         } else {
1955                                                 if ( method.equals(RESOURCE.WRITE) ) {
1956                                                         if ( (timeSeries && a.hasTimeSerie()) ||
1957                                                                         (!timeSeries && !a.hasTimeSerie()) ) {
1958                                                                 if ( Tools.supportsThisValueObject(a, vo) ) {
1959                                                                         if ( commonActivities.containsKey(a) ) {
1960                                                                                 commonActivities.put(a, commonActivities.get(a)+1);
1961                                                                         } else {
1962                                                                                 commonActivities.put(a, 1);
1963                                                                         }                                                               
1964                                                                 }
1965                                                         }
1966                                                 }
1967                                         }
1968                                 }
1969                         }
1970                 }
1971                 // get one with most matches
1972                 Object[] entries = commonActivities.entrySet().toArray();
1973                 Activity selectedActivity = null;
1974                 int numberOfMatches = 0;
1975                 for ( Object entryObj : entries ) {
1976                         Map.Entry<Activity, Integer> entry = (Map.Entry<Activity, Integer>)entryObj;
1977                         if ( entry.getValue() > numberOfMatches ) {
1978                                 selectedActivity = entry.getKey();
1979                                 numberOfMatches = entry.getValue();
1980                         }
1981                 }
1982                 return selectedActivity;
1983         }
1984         
1985         // true if activity (a) has any entity that has any value objects that has same
1986         // identifier or same quantity than valueobject (vo)
1987         private static boolean supportsThisValueObject(Activity a, ValueObject vo)
1988         {
1989                 for ( Entity e : a.getEntities() ) {
1990                         if ( e.getValueObjectByIdentifier(vo.getIdentifierUri()) != null ) {
1991                                 return true;
1992                         }
1993                         if ( e.getValueObjectByQuantity(vo.getQuantity()) != null ) {
1994                                 return true;
1995                         }
1996                 }
1997                 return false;
1998         }
1999         
2000         public static Properties readProperties(String file) {
2001                 Properties prop = new Properties();
2002                 InputStream inputStream = null;
2003                 try{
2004                         inputStream = new FileInputStream(file);
2005                         prop.load(inputStream);
2006                         return prop;
2007                 }catch(Exception e){
2008                         System.out.println("Failed to read property file: " + file + ". This is normal for the first run of the library and for the offline use.");
2009                         return null;
2010                 } finally {
2011                         if (inputStream != null) {
2012                                 try {
2013                                         inputStream.close();
2014                                 } catch (IOException e) {
2015                                         System.err.println("Failed to close input stream for property file: " + file);
2016                                         e.printStackTrace();
2017                                 }
2018                         }
2019                 }
2020         }
2021         
2022         public static void writeProperties(Properties properties, String file)
2023         {
2024                 OutputStream output = null;
2025                 try {
2026                         output = new FileOutputStream(file);
2027                         properties.store(output, null);
2028                 } catch (IOException io) {
2029                         System.err.println("Failed to write property file: " + file);
2030                         io.printStackTrace();
2031                 } finally {
2032                         if (output != null) {
2033                                 try {
2034                                         output.close();
2035                                 } catch (IOException e) {
2036                                         System.err.println("Failed to close output stream for property file: " + file);
2037                                         e.printStackTrace();
2038                                 }
2039                         }
2040                 }
2041         }
2042         
2043         public static void downloadFile(String downloadUri, String filePath, String fileName)
2044         {
2045                 try {
2046                         File file = new File(filePath + fileName);
2047                         FileUtils.copyURLToFile(new URL(downloadUri), file);
2048                         System.out.println("Downloaded file saved to " + filePath + fileName + " (source: " + downloadUri + ")");
2049                 } catch ( Exception e ) {
2050                         System.out.println("Failed to download file from " + downloadUri + " and save to " + filePath + fileName);
2051                         System.out.println(e.getMessage());
2052                 }
2053         }
2054         
2055         public static void calculateGrade(Grading grading)
2056         {
2057                 Variant sg = grading.getSyntaxGrade();
2058                 Variant vg = grading.getValueGrade();
2059                 Variant cg = grading.getConceptGrade();
2060                 try {
2061                         int factors = 0;
2062                         double value = 0;
2063                         if ( sg != null ) {
2064                                 value = value + (sg.isDouble() ? sg.asDouble(): 0) + (sg.isFloat() ? sg.asFloat(): 0) + (sg.isInteger() ? sg.asInt(): 0);
2065                                 factors++;
2066                         }
2067                         if ( vg != null ) {
2068                                 value = value + (vg.isDouble() ? vg.asDouble(): 0) + (vg.isFloat() ? vg.asFloat(): 0) + (vg.isInteger() ? vg.asInt(): 0);
2069                                 factors++;
2070                         }
2071                         if ( cg != null ) {
2072                                 value = value + (cg.isDouble() ? cg.asDouble(): 0) + (cg.isFloat() ? cg.asFloat(): 0) + (cg.isInteger() ? cg.asInt(): 0);
2073                                 factors++;
2074                         }
2075                         if ( factors > 0 ) {
2076                                 grading.setGrade(new Variant(value/factors));
2077                         }
2078                 } catch ( Exception e ) {
2079                         System.err.println("Exception while calculating overall grade for Grading.");
2080                         e.printStackTrace();
2081                 }
2082         }
2083         
2084         public static ArrayList<String> toStringList(List<Variant> list)
2085         {
2086                 ArrayList<String> ret = new ArrayList<String>();
2087                 list.forEach(i -> ret.add(i.asString()));
2088                 return ret;
2089         }
2090         
2091         /**
2092          * 
2093          * @param request request object
2094          * @param encoding encoding used in reading the body (i.e. "UTF-8")
2095          * @return request body
2096          * @throws UnsupportedEncodingException
2097          * @throws IOException
2098          */
2099         public static String getRequestBody(HttpServletRequest request, String encoding) throws UnsupportedEncodingException, IOException
2100         {
2101                 BufferedReader in = new BufferedReader(new InputStreamReader(request.getInputStream(), encoding));
2102                 char[] buff = new char[1024];
2103                 int read;
2104                 StringBuilder resp = new StringBuilder();
2105                 while((read = in.read(buff)) != -1) {
2106                         resp.append( buff,0,read ) ;  
2107                 }
2108                 in.close();
2109                 return  resp.toString();
2110         }
2111         
2112         public static boolean compliesWithTemplate(Obj obj, Obj template)
2113         {
2114                 // for all properties
2115                 for ( Map.Entry<Integer,List> entry : template.getAllProperties().entrySet() ) {
2116                         // if obj has same property as template
2117                         if ( obj.has(entry.getKey()) ) {
2118                                 // for all template values
2119                                 valuecheck:
2120                                 for ( Object templateValue : entry.getValue() ) {
2121                                         // check if any of the obj values match with template value
2122                                         for  ( Object objValue : obj.get(entry.getKey()) ) {
2123                                                 // if template value is Obj 
2124                                                 if ( templateValue instanceof Obj ) {
2125                                                         // if also obj value is Obj
2126                                                         if ( objValue instanceof Obj ) {
2127                                                                 // check recursively if they comply
2128                                                                 if ( Tools.compliesWithTemplate((Obj)objValue, (Obj)templateValue) ) {
2129                                                                         continue valuecheck;
2130                                                                 }
2131                                                                 // or obj value is variant Obj
2132                                                         } else if ( objValue instanceof Variant ){
2133                                                                 if ( ((Variant)objValue).isObj() ) {
2134                                                                         // check recursively if they comply
2135                                                                         if ( Tools.compliesWithTemplate(((Variant)objValue).asObj(), (Obj)templateValue) ) {
2136                                                                                 continue valuecheck;
2137                                                                         }
2138                                                                 }
2139                                                         }
2140                                                 // if template value is Variant
2141                                                 } else if ( templateValue instanceof Variant ) {
2142                                                         // if also obj value is Variant
2143                                                         if ( objValue instanceof Variant ) {
2144                                                                 // check recursively if they comply
2145                                                                 if ( Tools.compliesWithTemplate((Variant)objValue, (Variant)templateValue) ) {
2146                                                                         continue valuecheck;
2147                                                                 }
2148                                                                 // or obj value is Obj and template value is variant Obj
2149                                                         } else if ( objValue instanceof Obj ){
2150                                                                 if ( ((Variant)templateValue).isObj() ) {
2151                                                                         // check recursively if they comply
2152                                                                         if ( Tools.compliesWithTemplate((Obj)objValue, ((Variant)templateValue).asObj()) ) {
2153                                                                                 continue valuecheck;
2154                                                                         }
2155                                                                 }
2156                                                         }
2157                                                 }
2158                                         }
2159                                         // none of the obj values match with template value
2160                                         return false;
2161                                 }
2162                         } else {
2163                                 // obj does not have the property that template has
2164                                 return false;
2165                         }
2166                 }
2167                 // if template object has uri, obj uri must match that
2168                 if ( template.hasIdentifierUri() ) {
2169                         if ( obj.hasIdentifierUri() && template.getIdentifierUri().equals(obj.getIdentifierUri()) ) {
2170                                 return true;
2171                         } else {
2172                                 return false;
2173                         }
2174                 }
2175                 // all properties (if any) match and identifier (if exists) matches
2176                 return true;
2177         }
2178         
2179         public static boolean compliesWithTemplate(Variant obj, Variant template)
2180         {
2181                 // check if they are both Objs
2182                 if ( template.isObj() ) {
2183                         if ( obj.isObj() ) {
2184                                 return Tools.compliesWithTemplate(obj.asObj(), template.asObj());
2185                         }
2186                         if ( obj.isUrl() ) {
2187                                 return Tools.compliesWithTemplate(new Obj(obj.asURL().toString()), template.asObj());
2188                         }
2189                         return false;
2190                 }
2191                 if ( template.isUrl() ) {
2192                         if ( obj.isUrl() ) {
2193                                 return obj.asURL().toString().equals(template.asURL().toString());
2194                         }
2195                         if ( obj.isObj() ) {
2196                                 return Tools.compliesWithTemplate(obj.asObj(), new Obj(template.asURL().toString()));
2197                         }
2198                         return false;
2199                 }
2200                 // if variant type does not match -> does not comply
2201                 if ( obj.getType() != template.getType() ) {
2202                         return false;
2203                 }
2204                 return obj.hasSameValue(template);
2205         }
2206         
2207         // forwarding calls for backward compatibility
2208         
2209         public static String dateToString(Date dateTime)
2210         {
2211                 return DateUtils.dateToString(dateTime);
2212         }
2213         
2214         public static Date stringToDate(String dateTime)
2215         {
2216                 return DateUtils.stringToDate(dateTime);
2217         }
2218         
2219         public static Date add(Date date, int years, int months, int days, int hours, int minutes, int seconds)
2220         {
2221                 return DateUtils.add(date, years, months, days, hours, minutes, seconds);
2222         }
2223         
2224         public static String currentDayType()
2225         {
2226                 return DateUtils.currentDayType();
2227         }
2228         
2229         public static String currentDay()
2230         {
2231                 return DateUtils.currentDay();
2232         }
2233         
2234         public static Time toUTC(Time time)
2235         {
2236                 return TimeUtils.toUTC(time);
2237         }
2238
2239         public static Time toLocal(Time time)
2240         {
2241                 return TimeUtils.toLocal(time);
2242         }
2243
2244         public static long toLocalTime(long time, TimeZone to) {
2245                 return TimeUtils.toLocalTime(time, to);
2246         }
2247
2248         public static long toUTC(long time, TimeZone from) {
2249                 return TimeUtils.toUTC(time, from);
2250         }
2251         
2252         public static Time currentLocalTime()
2253         {
2254                 return TimeUtils.currentLocalTime();
2255         }
2256
2257         public static Time currentUTCTime()
2258         {
2259                 return TimeUtils.currentUTCTime();
2260         }
2261         
2262         public static Coordinates getCoordinatesAt(double latitude, double longitude, double distance, double bearing)
2263         {
2264                 return getCoordinatesAt(latitude, longitude, distance, bearing);
2265         }
2266         
2267         public static double getDistanceBetweenCoordinatePointsInKm(double lat1, double lon1, double lat2, double lon2)
2268         {
2269                 return GeoUtils.getDistanceBetweenCoordinatePointsInKm(lat1, lon1, lat2, lon2);
2270         }
2271
2272         public static void autoFill(Entity entity, String generatedBy)
2273         {
2274                 GeoUtils.geoCode(entity, generatedBy);
2275         }
2276
2277 }