/* * Copyright Aduna (http://www.aduna-software.com/) (c) 1997-2007. * * Licensed under the Aduna BSD-style license. */ package org.openrdf.sail.memory.model; import java.math.BigInteger; import java.util.Collections; import java.util.Set; import javax.xml.datatype.XMLGregorianCalendar; import org.openrdf.model.BNode; import org.openrdf.model.Literal; import org.openrdf.model.Resource; import org.openrdf.model.Statement; import org.openrdf.model.URI; import org.openrdf.model.Value; import org.openrdf.model.datatypes.XMLDatatypeUtil; import org.openrdf.model.impl.BNodeImpl; import org.openrdf.model.impl.ContextStatementImpl; import org.openrdf.model.impl.LiteralImpl; import org.openrdf.model.impl.StatementImpl; import org.openrdf.model.impl.URIImpl; import org.openrdf.model.impl.ValueFactoryBase; import org.openrdf.model.util.URIUtil; import org.openrdf.model.vocabulary.XMLSchema; /** * A factory for MemValue objects that keeps track of created objects to prevent * the creation of duplicate objects, minimizing memory usage as a result. * * @author Arjohn Kampman * @author David Huynh */ public class MemValueFactory extends ValueFactoryBase { /*-----------* * Variables * *-----------*/ /** * Registry containing the set of MemURI objects as used by a MemoryStore. * This registry enables the reuse of objects, minimizing the number of * objects in main memory. */ private final WeakObjectRegistry<MemURI> uriRegistry = new WeakObjectRegistry<MemURI>(); /** * Registry containing the set of MemBNode objects as used by a MemoryStore. * This registry enables the reuse of objects, minimizing the number of * objects in main memory. */ private final WeakObjectRegistry<MemBNode> bnodeRegistry = new WeakObjectRegistry<MemBNode>(); /** * Registry containing the set of MemLiteral objects as used by a * MemoryStore. This registry enables the reuse of objects, minimizing the * number of objects in main memory. */ private final WeakObjectRegistry<MemLiteral> literalRegistry = new WeakObjectRegistry<MemLiteral>(); /** * Registry containing the set of namespce strings as used by MemURI objects * in a MemoryStore. This registry enables the reuse of objects, minimizing * the number of objects in main memory. */ private final WeakObjectRegistry<String> namespaceRegistry = new WeakObjectRegistry<String>(); /*---------* * Methods * *---------*/ /** * Returns a previously created MemValue that is equal to the supplied value, * or <tt>null</tt> if the supplied value is a new value or is equal to * <tt>null</tt>. * * @param value * The MemValue equivalent of the supplied value, or <tt>null</tt>. * @return A previously created MemValue that is equal to <tt>value</tt>, * or <tt>null</tt> if no such value exists or if <tt>value</tt> * is equal to <tt>null</tt>. */ public MemValue getMemValue(Value value) { if (value instanceof Resource) { return getMemResource((Resource)value); } else if (value instanceof Literal) { return getMemLiteral((Literal)value); } else if (value == null) { return null; } else { throw new IllegalArgumentException("value is not a Resource or Literal: " + value); } } /** * See getMemValue() for description. */ public MemResource getMemResource(Resource resource) { if (resource instanceof URI) { return getMemURI((URI)resource); } else if (resource instanceof BNode) { return getMemBNode((BNode)resource); } else if (resource == null) { return null; } else { throw new IllegalArgumentException("resource is not a URI or BNode: " + resource); } } /** * See getMemValue() for description. */ public MemURI getMemURI(URI uri) { if (isOwnMemValue(uri)) { return (MemURI)uri; } else { return uriRegistry.get(uri); } } /** * See getMemValue() for description. */ public MemBNode getMemBNode(BNode bnode) { if (isOwnMemValue(bnode)) { return (MemBNode)bnode; } else { return bnodeRegistry.get(bnode); } } /** * See getMemValue() for description. */ public MemLiteral getMemLiteral(Literal literal) { if (isOwnMemValue(literal)) { return (MemLiteral)literal; } else { return literalRegistry.get(literal); } } /** * Checks whether the supplied value is an instance of <tt>MemValue</tt> * and whether it has been created by this MemValueFactory. */ private boolean isOwnMemValue(Value value) { return value instanceof MemValue && ((MemValue)value).getCreator() == this; } /** * Gets all URIs that are managed by this value factory. * * @return An unmodifiable Set of MemURI objects. */ public Set<MemURI> getMemURIs() { return Collections.unmodifiableSet(uriRegistry); } /** * Gets all bnodes that are managed by this value factory. * * @return An unmodifiable Set of MemBNode objects. */ public Set<MemBNode> getMemBNodes() { return Collections.unmodifiableSet(bnodeRegistry); } /** * Gets all literals that are managed by this value factory. * * @return An unmodifiable Set of MemURI objects. */ public Set<MemLiteral> getMemLiterals() { return Collections.unmodifiableSet(literalRegistry); } /** * Creates a MemValue for the supplied Value. The supplied value should not * already have an associated MemValue. The created MemValue is returned. * * @param value * A Resource or Literal. * @return The created MemValue. */ public MemValue createMemValue(Value value) { if (value instanceof Resource) { return createMemResource((Resource)value); } else if (value instanceof Literal) { return createMemLiteral((Literal)value); } else { throw new IllegalArgumentException("value is not a Resource or Literal: " + value); } } /** * See createMemValue() for description. */ public MemResource createMemResource(Resource resource) { if (resource instanceof URI) { return createMemURI((URI)resource); } else if (resource instanceof BNode) { return createMemBNode((BNode)resource); } else { throw new IllegalArgumentException("resource is not a URI or BNode: " + resource); } } /** * See createMemValue() for description. */ public MemURI createMemURI(URI uri) { // Namespace strings are relatively large objects and are shared // between uris String namespace = uri.getNamespace(); String sharedNamespace = namespaceRegistry.get(namespace); if (sharedNamespace == null) { // New namespace, add it to the registry namespaceRegistry.add(namespace); } else { // Use the shared namespace namespace = sharedNamespace; } // Create a MemURI and add it to the registry MemURI memURI; if (isOwnMemValue(uri) && namespace == uri.getNamespace()) { // Supplied parameter is a MemURI that can be reused memURI = (MemURI)uri; } else { memURI = new MemURI(this, namespace, uri.getLocalName()); } boolean wasNew = uriRegistry.add(memURI); assert wasNew : "Created a duplicate MemURI for URI " + uri; return memURI; } /** * See createMemValue() for description. */ public MemBNode createMemBNode(BNode bnode) { MemBNode memBNode = new MemBNode(this, bnode.getID()); boolean wasNew = bnodeRegistry.add(memBNode); assert wasNew : "Created a duplicate MemBNode for bnode " + bnode; return memBNode; } /** * See createMemValue() for description. */ public MemLiteral createMemLiteral(Literal literal) { MemLiteral memLiteral = null; String label = literal.getLabel(); URI datatype = literal.getDatatype(); if (datatype != null) { try { if (XMLDatatypeUtil.isIntegerDatatype(datatype)) { memLiteral = new IntegerMemLiteral(this, label, literal.integerValue(), datatype); } else if (datatype.equals(XMLSchema.DECIMAL)) { memLiteral = new DecimalMemLiteral(this, label, literal.decimalValue(), datatype); } else if (datatype.equals(XMLSchema.FLOAT)) { memLiteral = new NumericMemLiteral(this, label, literal.floatValue(), datatype); } else if (datatype.equals(XMLSchema.DOUBLE)) { memLiteral = new NumericMemLiteral(this, label, literal.doubleValue(), datatype); } else if (datatype.equals(XMLSchema.BOOLEAN)) { memLiteral = new BooleanMemLiteral(this, label, literal.booleanValue()); } else if (datatype.equals(XMLSchema.DATETIME)) { memLiteral = new CalendarMemLiteral(this, label, literal.calendarValue()); } else { memLiteral = new MemLiteral(this, literal.getLabel(), datatype); } } catch (IllegalArgumentException e) { // Unable to parse literal label to primitive type memLiteral = new MemLiteral(this, literal.getLabel(), datatype); } } else if (literal.getLanguage() != null) { memLiteral = new MemLiteral(this, literal.getLabel(), literal.getLanguage()); } else { memLiteral = new MemLiteral(this, literal.getLabel()); } boolean wasNew = literalRegistry.add(memLiteral); assert wasNew : "Created a duplicate MemLiteral for literal " + literal; return memLiteral; } public URI createURI(String uri) { URI tempURI = new URIImpl(uri); MemURI memURI = getMemURI(tempURI); if (memURI == null) { memURI = createMemURI(tempURI); } return memURI; } public URI createURI(String namespace, String localName) { URI tempURI = null; // Reuse supplied namespace and local name strings if possible if (URIUtil.isCorrectURISplit(namespace, localName)) { if (namespace.indexOf(':') == -1) { throw new IllegalArgumentException("Not a valid (absolute) URI: " + namespace + localName); } tempURI = new MemURI(this, namespace, localName); } else { tempURI = new URIImpl(namespace + localName); } MemURI memURI = uriRegistry.get(tempURI); if (memURI == null) { memURI = createMemURI(tempURI); } return memURI; } public BNode createBNode(String nodeID) { BNode tempBNode = new BNodeImpl(nodeID); MemBNode memBNode = getMemBNode(tempBNode); if (memBNode == null) { memBNode = createMemBNode(tempBNode); } return memBNode; } public Literal createLiteral(String value) { Literal tempLiteral = new LiteralImpl(value); MemLiteral memLiteral = literalRegistry.get(tempLiteral); if (memLiteral == null) { memLiteral = createMemLiteral(tempLiteral); } return memLiteral; } public Literal createLiteral(String value, String language) { Literal tempLiteral = new LiteralImpl(value, language); MemLiteral memLiteral = literalRegistry.get(tempLiteral); if (memLiteral == null) { memLiteral = createMemLiteral(tempLiteral); } return memLiteral; } public Literal createLiteral(String value, URI datatype) { Literal tempLiteral = new LiteralImpl(value, datatype); MemLiteral memLiteral = literalRegistry.get(tempLiteral); if (memLiteral == null) { memLiteral = createMemLiteral(tempLiteral); } return memLiteral; } @Override public Literal createLiteral(boolean value) { MemLiteral newLiteral = new BooleanMemLiteral(this, value); return getSharedLiteral(newLiteral); } @Override protected Literal createIntegerLiteral(Number n, URI datatype) { MemLiteral newLiteral = new IntegerMemLiteral(this, BigInteger.valueOf(n.longValue()), datatype); return getSharedLiteral(newLiteral); } @Override protected Literal createFPLiteral(Number n, URI datatype) { MemLiteral newLiteral = new NumericMemLiteral(this, n, datatype); return getSharedLiteral(newLiteral); } @Override public Literal createLiteral(XMLGregorianCalendar calendar) { MemLiteral newLiteral = new CalendarMemLiteral(this, calendar); return getSharedLiteral(newLiteral); } private Literal getSharedLiteral(MemLiteral newLiteral) { MemLiteral sharedLiteral = literalRegistry.get(newLiteral); if (sharedLiteral == null) { boolean wasNew = literalRegistry.add(newLiteral); assert wasNew : "Created a duplicate MemLiteral for literal " + newLiteral; sharedLiteral = newLiteral; } return sharedLiteral; } public Statement createStatement(Resource subject, URI predicate, Value object) { return new StatementImpl(subject, predicate, object); } public Statement createStatement(Resource subject, URI predicate, Value object, Resource context) { if (context == null) { return new StatementImpl(subject, predicate, object); } else { return new ContextStatementImpl(subject, predicate, object, context); } } }