1   package eu.fbk.knowledgestore.internal.rdf;
2   
3   import java.math.BigDecimal;
4   import java.math.BigInteger;
5   import java.util.Date;
6   import java.util.GregorianCalendar;
7   import java.util.UUID;
8   import java.util.concurrent.atomic.AtomicLong;
9   
10  import javax.annotation.Nullable;
11  import javax.xml.datatype.DatatypeFactory;
12  import javax.xml.datatype.XMLGregorianCalendar;
13  
14  import com.google.common.base.Objects;
15  
16  import org.openrdf.model.BNode;
17  import org.openrdf.model.Literal;
18  import org.openrdf.model.Resource;
19  import org.openrdf.model.Statement;
20  import org.openrdf.model.URI;
21  import org.openrdf.model.Value;
22  import org.openrdf.model.ValueFactory;
23  import org.openrdf.model.datatypes.XMLDatatypeUtil;
24  import org.openrdf.model.impl.BNodeImpl;
25  import org.openrdf.model.impl.BooleanLiteralImpl;
26  import org.openrdf.model.impl.CalendarLiteralImpl;
27  import org.openrdf.model.impl.ContextStatementImpl;
28  import org.openrdf.model.impl.LiteralImpl;
29  import org.openrdf.model.impl.StatementImpl;
30  import org.openrdf.model.impl.URIImpl;
31  import org.openrdf.model.vocabulary.XMLSchema;
32  import org.slf4j.Logger;
33  import org.slf4j.LoggerFactory;
34  
35  public final class CompactValueFactory implements ValueFactory {
36  
37      private static final Logger LOGGER = LoggerFactory.getLogger(CompactValueFactory.class);
38  
39      private static final DatatypeFactory DATATYPE_FACTORY;
40  
41      private static final CompactValueFactory VALUE_FACTORY;
42  
43      static {
44          try {
45              DATATYPE_FACTORY = DatatypeFactory.newInstance();
46              VALUE_FACTORY = new CompactValueFactory();
47          } catch (final Throwable ex) {
48              throw new Error("Unexpected exception (!): " + ex.getMessage(), ex);
49          }
50      }
51  
52      private final String bnodePrefix;
53  
54      private final AtomicLong bnodeCounter;
55  
56      private CompactValueFactory() {
57          final UUID uuid = UUID.randomUUID();
58          final StringBuilder builder = new StringBuilder(12);
59          long num = Math.abs(uuid.getLeastSignificantBits());
60          builder.append(charFor(num % 52));
61          num = num / 52;
62          for (int i = 0; i < 5; ++i) {
63              builder.append(charFor(num % 62));
64              num = num / 62;
65          }
66          num = Math.abs(uuid.getMostSignificantBits());
67          for (int i = 0; i < 6; ++i) {
68              builder.append(charFor(num % 62));
69              num = num / 62;
70          }
71          this.bnodePrefix = builder.toString();
72          this.bnodeCounter = new AtomicLong(0L);
73      }
74  
75      private static char charFor(final long num) {
76          if (num < 26) {
77              return (char) (65 + num);
78          } else if (num < 52) {
79              return (char) (71 + num);
80          } else if (num < 62) {
81              return (char) (num - 4);
82          } else {
83              return 'x';
84          }
85      }
86  
87      public static CompactValueFactory getInstance() {
88          return VALUE_FACTORY;
89      }
90  
91      @SuppressWarnings("unchecked")
92      @Nullable
93      public <T> T normalize(@Nullable final T object) {
94          if (object instanceof Statement) {
95              if (!(object instanceof StatementImpl) && !(object instanceof ContextStatementImpl)) {
96                  final Statement s = (Statement) object;
97                  return s.getContext() == null ? (T) createStatement(s.getSubject(),
98                          s.getPredicate(), s.getObject()) : (T) createStatement(s.getSubject(),
99                          s.getPredicate(), s.getObject(), s.getContext());
100             }
101         } else if (object instanceof URI) {
102             if (!(object instanceof URIImpl)) {
103                 return (T) createURI(((URI) object).stringValue());
104             }
105         } else if (object instanceof BNode) {
106             if (!(object instanceof BNodeImpl)) {
107                 return (T) createBNode(((BNode) object).getID());
108             }
109         } else if (object instanceof Literal) {
110             if (!(object instanceof StringLiteral) && !(object instanceof NumberLiteral)
111                     && !(object instanceof BooleanLiteralImpl)
112                     && !(object instanceof CalendarLiteralImpl)) {
113                 final Literal l = (Literal) object;
114                 return l.getLanguage() != null ? (T) createLiteral(l.getLabel(), l.getLanguage())
115                         : (T) createLiteral(l.getLabel(), l.getDatatype());
116             }
117         }
118         return object;
119     }
120 
121     @Override
122     public URI createURI(final String uri) {
123         return new URIImpl(uri);
124     }
125 
126     @Override
127     public URI createURI(final String namespace, final String localName) {
128         return new URIImpl(namespace + localName);
129     }
130 
131     @Override
132     public BNode createBNode() {
133         return new BNodeImpl(this.bnodePrefix
134                 + Long.toString(this.bnodeCounter.getAndIncrement(), 32));
135     }
136 
137     @Override
138     public BNode createBNode(final String nodeID) {
139         return new BNodeImpl(nodeID);
140     }
141 
142     @Override
143     public Literal createLiteral(final String label) {
144         return new StringLiteral(label, (URI) null);
145     }
146 
147     @Override
148     public Literal createLiteral(final String label, final String language) {
149         return new StringLiteral(label, language);
150     }
151 
152     @Override
153     public Literal createLiteral(final String label, final URI datatype) {
154         try {
155             if (datatype == null) {
156                 return new StringLiteral(label, (String) null);
157             } else if (datatype.equals(XMLSchema.STRING)) {
158                 return new StringLiteral(label, XMLSchema.STRING);
159             } else if (datatype.equals(XMLSchema.BOOLEAN)) {
160                 final boolean value = XMLDatatypeUtil.parseBoolean(label);
161                 return value ? BooleanLiteralImpl.TRUE : BooleanLiteralImpl.FALSE;
162             } else if (datatype.equals(XMLSchema.INT)) {
163                 return new LongLiteral(XMLSchema.INT, XMLDatatypeUtil.parseInt(label));
164             } else if (datatype.equals(XMLSchema.LONG)) {
165                 return new LongLiteral(XMLSchema.LONG, XMLDatatypeUtil.parseLong(label));
166             } else if (datatype.equals(XMLSchema.SHORT)) {
167                 return new LongLiteral(XMLSchema.SHORT, XMLDatatypeUtil.parseShort(label));
168             } else if (datatype.equals(XMLSchema.BYTE)) {
169                 return new LongLiteral(XMLSchema.BYTE, XMLDatatypeUtil.parseByte(label));
170             } else if (datatype.equals(XMLSchema.DOUBLE)) {
171                 return new DoubleLiteral(XMLSchema.DOUBLE, XMLDatatypeUtil.parseDouble(label));
172             } else if (datatype.equals(XMLSchema.FLOAT)) {
173                 return new DoubleLiteral(XMLSchema.FLOAT, XMLDatatypeUtil.parseFloat(label));
174             } else if (datatype.equals(XMLSchema.DATETIME) || datatype.equals(XMLSchema.DATE)
175                     || datatype.equals(XMLSchema.TIME) || datatype.equals(XMLSchema.GYEARMONTH)
176                     || datatype.equals(XMLSchema.GMONTHDAY) || datatype.equals(XMLSchema.GYEAR)
177                     || datatype.equals(XMLSchema.GMONTH) || datatype.equals(XMLSchema.GDAY)) {
178                 return createLiteral(XMLDatatypeUtil.parseCalendar(label));
179             } else if (datatype.equals(XMLSchema.DECIMAL)) {
180                 return new BigDecimalLiteral(datatype, XMLDatatypeUtil.parseDecimal(label));
181             } else if (datatype.equals(XMLSchema.INTEGER)
182                     || datatype.equals(XMLSchema.NON_NEGATIVE_INTEGER)
183                     || datatype.equals(XMLSchema.POSITIVE_INTEGER)
184                     || datatype.equals(XMLSchema.NEGATIVE_INTEGER)) {
185                 return new BigIntegerLiteral(datatype, XMLDatatypeUtil.parseInteger(label));
186             } else {
187                 return new StringLiteral(label, datatype);
188             }
189         } catch (final Throwable ex) {
190             LOGGER.warn("Illegal literal: '" + label + "'^^<" + datatype + "> (dropping datatype)");
191             return createLiteral(label);
192         }
193     }
194 
195     @Override
196     public Literal createLiteral(final boolean value) {
197         return value ? BooleanLiteralImpl.TRUE : BooleanLiteralImpl.FALSE;
198     }
199 
200     @Override
201     public Literal createLiteral(final byte value) {
202         return new LongLiteral(XMLSchema.BYTE, value);
203     }
204 
205     @Override
206     public Literal createLiteral(final short value) {
207         return new LongLiteral(XMLSchema.SHORT, value);
208     }
209 
210     @Override
211     public Literal createLiteral(final int value) {
212         return new LongLiteral(XMLSchema.INT, value);
213     }
214 
215     @Override
216     public Literal createLiteral(final long value) {
217         return new LongLiteral(XMLSchema.LONG, value);
218     }
219 
220     @Override
221     public Literal createLiteral(final float value) {
222         return new DoubleLiteral(XMLSchema.FLOAT, value);
223     }
224 
225     @Override
226     public Literal createLiteral(final double value) {
227         return new DoubleLiteral(XMLSchema.DOUBLE, value);
228     }
229 
230     @Override
231     public Literal createLiteral(final XMLGregorianCalendar calendar) {
232         return new CalendarLiteralImpl(calendar);
233     }
234 
235     @Override
236     public Literal createLiteral(final Date date) {
237         final GregorianCalendar calendar = new GregorianCalendar();
238         calendar.setTime(date);
239         final XMLGregorianCalendar xmlCalendar = DATATYPE_FACTORY
240                 .newXMLGregorianCalendar(calendar);
241         return new CalendarLiteralImpl(xmlCalendar);
242     }
243 
244     @Override
245     public Statement createStatement(final Resource subject, final URI predicate,
246             final Value object) {
247         return new StatementImpl(subject, predicate, object);
248     }
249 
250     @Override
251     public Statement createStatement(final Resource subject, final URI predicate,
252             final Value object, final Resource context) {
253         return context == null ? new StatementImpl(subject, predicate, object)
254                 : new ContextStatementImpl(subject, predicate, object, context);
255     }
256 
257     private static final class StringLiteral extends LiteralImpl {
258 
259         private static final long serialVersionUID = 1L;
260 
261         StringLiteral(final String label, final String language) {
262             super(label, language == null ? null : language.intern());
263         }
264 
265         StringLiteral(final String label, final URI datatype) {
266             super(label, datatype);
267         }
268 
269     }
270 
271     private abstract static class NumberLiteral implements Literal {
272 
273         private static final long serialVersionUID = 1L;
274 
275         private final URI datatype;
276 
277         NumberLiteral(final URI datatype) {
278             this.datatype = datatype;
279         }
280 
281         abstract Number getNumber();
282 
283         boolean equalNumber(final Literal literal) {
284             return getNumber().equals(((NumberLiteral) literal).getNumber());
285         }
286 
287         @Override
288         public String getLabel() {
289             return stringValue();
290         }
291 
292         @Override
293         public String getLanguage() {
294             return null;
295         }
296 
297         @Override
298         public URI getDatatype() {
299             return this.datatype;
300         }
301 
302         @Override
303         public String stringValue() {
304             return getNumber().toString();
305         }
306 
307         @Override
308         public byte byteValue() {
309             return getNumber().byteValue();
310         }
311 
312         @Override
313         public short shortValue() {
314             return getNumber().shortValue();
315         }
316 
317         @Override
318         public int intValue() {
319             return getNumber().intValue();
320         }
321 
322         @Override
323         public long longValue() {
324             return getNumber().longValue();
325         }
326 
327         @Override
328         public float floatValue() {
329             return getNumber().floatValue();
330         }
331 
332         @Override
333         public double doubleValue() {
334             return getNumber().doubleValue();
335         }
336 
337         @Override
338         public boolean booleanValue() {
339             return XMLDatatypeUtil.parseBoolean(getLabel());
340         }
341 
342         @Override
343         public BigInteger integerValue() {
344             return XMLDatatypeUtil.parseInteger(getLabel());
345         }
346 
347         @Override
348         public BigDecimal decimalValue() {
349             return XMLDatatypeUtil.parseDecimal(getLabel());
350         }
351 
352         @Override
353         public XMLGregorianCalendar calendarValue() {
354             return XMLDatatypeUtil.parseCalendar(getLabel());
355         }
356 
357         @Override
358         public boolean equals(final Object object) {
359             if (object == this) {
360                 return true;
361             }
362             if (!(object instanceof Literal)) {
363                 return false;
364             }
365             final Literal other = (Literal) object;
366             if (object.getClass() == this.getClass()) {
367                 return this.datatype.equals(other.getDatatype()) && equalNumber(other);
368             }
369             if (object instanceof NumberLiteral || object instanceof BooleanLiteralImpl
370                     || object instanceof CalendarLiteralImpl || object instanceof StringLiteral) {
371                 return false;
372             }
373             return other.getLanguage() == null && this.datatype.equals(other.getDatatype())
374                     && stringValue().equals(other.stringValue());
375         }
376 
377         @Override
378         public int hashCode() {
379             return Objects.hashCode(getNumber(), getDatatype());
380         }
381 
382         @Override
383         public String toString() {
384             final String label = getLabel();
385             final StringBuilder builder = new StringBuilder(label.length() * 2);
386             builder.append('"');
387             builder.append(label);
388             builder.append('"');
389             builder.append('^').append('^').append('<');
390             builder.append(this.datatype.toString());
391             builder.append('>');
392             return builder.toString();
393         }
394 
395     }
396 
397     private static final class LongLiteral extends NumberLiteral {
398 
399         private static final long serialVersionUID = 1L;
400 
401         private final long value;
402 
403         LongLiteral(final URI datatype, final long value) {
404             super(datatype);
405             this.value = value;
406         }
407 
408         @Override
409         Number getNumber() {
410             return this.value;
411         }
412 
413         @Override
414         public String stringValue() {
415             return Long.toString(this.value);
416         }
417 
418         @Override
419         public byte byteValue() {
420             return (byte) this.value;
421         }
422 
423         @Override
424         public short shortValue() {
425             return (short) this.value;
426         }
427 
428         @Override
429         public int intValue() {
430             return (int) this.value;
431         }
432 
433         @Override
434         public long longValue() {
435             return this.value;
436         }
437 
438         @Override
439         public float floatValue() {
440             return this.value;
441         }
442 
443         @Override
444         public double doubleValue() {
445             return this.value;
446         }
447 
448         @Override
449         public BigInteger integerValue() {
450             return BigInteger.valueOf(this.value);
451         }
452 
453         @Override
454         public BigDecimal decimalValue() {
455             return BigDecimal.valueOf(this.value);
456         }
457     }
458 
459     private static final class DoubleLiteral extends NumberLiteral {
460 
461         private static final long serialVersionUID = 1L;
462 
463         private final double value;
464 
465         DoubleLiteral(final URI datatype, final double value) {
466             super(datatype);
467             this.value = value;
468         }
469 
470         @Override
471         Number getNumber() {
472             return this.value;
473         }
474 
475         @Override
476         public String stringValue() {
477             return Double.toString(this.value);
478         }
479 
480         @Override
481         public byte byteValue() {
482             return (byte) this.value;
483         }
484 
485         @Override
486         public short shortValue() {
487             return (short) this.value;
488         }
489 
490         @Override
491         public int intValue() {
492             return (int) this.value;
493         }
494 
495         @Override
496         public long longValue() {
497             return (long) this.value;
498         }
499 
500         @Override
501         public float floatValue() {
502             return (float) this.value;
503         }
504 
505         @Override
506         public double doubleValue() {
507             return this.value;
508         }
509 
510         @Override
511         public BigDecimal decimalValue() {
512             return BigDecimal.valueOf(this.value);
513         }
514 
515     }
516 
517     private static final class BigIntegerLiteral extends NumberLiteral {
518 
519         private static final long serialVersionUID = 1L;
520 
521         private final BigInteger value;
522 
523         BigIntegerLiteral(final URI datatype, final BigInteger value) {
524             super(datatype);
525             this.value = value;
526         }
527 
528         @Override
529         Number getNumber() {
530             return this.value;
531         }
532 
533         @Override
534         public BigInteger integerValue() {
535             return this.value;
536         }
537 
538         @Override
539         public BigDecimal decimalValue() {
540             return new BigDecimal(this.value);
541         }
542 
543     }
544 
545     private static final class BigDecimalLiteral extends NumberLiteral {
546 
547         private static final long serialVersionUID = 1L;
548 
549         private final BigDecimal value;
550 
551         BigDecimalLiteral(final URI datatype, final BigDecimal value) {
552             super(datatype);
553             this.value = value;
554         }
555 
556         @Override
557         Number getNumber() {
558             return this.value;
559         }
560 
561         @Override
562         public BigInteger integerValue() {
563             return this.value.toBigInteger();
564         }
565 
566         @Override
567         public BigDecimal decimalValue() {
568             return this.value;
569         }
570 
571     }
572 
573 }