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 }