/*
* $Header$
* $Revision: 624 $
* $Date: 2006-06-24 21:02:12 +1000 (Sat, 24 Jun 2006) $
*
* ====================================================================
*
* The Apache Software License, Version 1.1
*
* Copyright (c) 2003 The JRDF Project. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution, if
* any, must include the following acknowlegement:
* "This product includes software developed by the
* the JRDF Project (http://jrdf.sf.net/)."
* Alternately, this acknowlegement may appear in the software itself,
* if and wherever such third-party acknowlegements normally appear.
*
* 4. The names "The JRDF Project" and "JRDF" must not be used to endorse
* or promote products derived from this software without prior written
* permission. For written permission, please contact
* newmana@users.sourceforge.net.
*
* 5. Products derived from this software may not be called "JRDF"
* nor may "JRDF" appear in their names without prior written
* permission of the JRDF Project.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the JRDF Project. For more
* information on JRDF, please see <http://jrdf.sourceforge.net/>.
*/
package org.jrdf.graph.mem;
import org.jrdf.graph.*;
import org.mulgara.util.UIDGenerator;
import java.net.URI;
import java.util.HashMap;
import java.util.Map;
/**
* A SkipListNode Factory is a class which create the various components of a graph.
* It is tied to a specific instance of GraphImpl.
*
* @author <a href="mailto:pgearon@users.sourceforge.net">Paula Gearon</a>
*/
public class GraphElementFactoryImpl implements GraphElementFactory {
/** The pool of all nodes, mapped from their ids. */
private Map<Long,MemNode> nodePool;
/** A reverse mapping of all ids, mapped from their string. */
private Map<String,Long> stringPool;
/** The graph that this factory constructs nodes for. */
@SuppressWarnings("unused")
private Graph graph;
/** The next available node id. */
private long nextNode;
/**
* Package scope constructor.
*
* @param newGraph The GraphImpl that this class is attached to.
*/
GraphElementFactoryImpl(Graph newGraph) throws TripleFactoryException {
graph = newGraph;
nodePool = new HashMap<Long,MemNode>();
stringPool = new HashMap<String,Long>();
nextNode = 1;
}
/**
* Create a blank nodes that is associated with a specific graph.
* @return A new blank node within the graph.
* @return the newly created blank node value.
* @throws GraphElementFactoryException If anonymous resources can't be generated.
*/
public BlankNode createResource() throws GraphElementFactoryException {
Long id = new Long(nextNode);
//get an Unique Identifier
String uid = "";
try {
uid = UIDGenerator.generateUID();
} catch (Exception exception) {
throw new GraphElementFactoryException(
"Could not generate Unique Identifier for BlankNode.", exception);
}
// create the new node
BlankNodeImpl node = new BlankNodeImpl(id, uid);
// put the node in the pool
nodePool.put(id, node);
// go on to the next node id
nextNode++;
return node;
}
/**
* Create a URI reference.
* @param uri The URI of the resource.
* @return the newly created URI reference value.
* @throws GraphElementFactoryException If the resource failed to be created.
*/
public URIReference createResource(URI uri)
throws GraphElementFactoryException {
if (null == uri) {
throw new GraphElementFactoryException("URI may not be null for a URIReference");
}
// check if the node already exists in the string pool
Long nodeid = getNodeIdByString(uri.toString());
if (null != nodeid) {
return (URIReference)getNodeById(nodeid);
}
// create the node identifier and increment the node
nodeid = new Long(nextNode++);
// create the new node
URIReferenceImpl node = new URIReferenceImpl(uri, nodeid);
// put the node in the pool
nodePool.put(nodeid, node);
// put the URI string into the pool
// TODO: This could conflict with a literal
stringPool.put(uri.toString(), nodeid);
return node;
}
/**
* Create a URI reference without checking if the URI given is a valid RDF
* URI, currently if the URI is absolute.
* @param uri The URI of the resource.
* @param validate true if we disbale checking to see if the URI is valid.
* @return The newly created URI reference value.
* @throws GraphElementFactoryException
*/
public URIReference createResource(URI uri, boolean validate) throws GraphElementFactoryException {
if (null == uri) {
throw new GraphElementFactoryException("URI may not be null for a URIReference");
}
// check if the node already exists in the string pool
Long nodeid = getNodeIdByString(uri.toString());
if (null != nodeid) {
return (URIReference)getNodeById(nodeid);
}
// create the node identifier and increment the node
nodeid = new Long(nextNode++);
// create the new node
URIReferenceImpl node = new URIReferenceImpl(uri, validate, nodeid);
// put the node in the pool
nodePool.put(nodeid, node);
// put the URI string into the pool
// TODO: This could conflict with a literal
stringPool.put(uri.toString(), nodeid);
return node;
}
/**
* Creates a new literal with the given lexical value, with no language or datatype.
* @param lexicalValue The lexical value for the literal.
* @return the newly created literal value.
* @throws GraphElementFactoryException If the resource failed to be created.
*/
public Literal createLiteral(String lexicalValue) throws GraphElementFactoryException {
LiteralImpl literal = new LiteralImpl(lexicalValue);
addNodeId(literal);
return literal;
}
/**
* Creates a new literal with the given lexical value, with a given language
* but no datatype.
* @param lexicalValue The lexical value for the literal. Cannot be null.
* @param languageType The language of the literal or null if not required.
* @return the newly created literal value.
* @throws GraphElementFactoryException If the resource failed to be created.
*/
public Literal createLiteral(String lexicalValue, String languageType) throws GraphElementFactoryException {
LiteralImpl newLiteral = new LiteralImpl(lexicalValue, languageType);
addNodeId(newLiteral);
return newLiteral;
}
/**
* Creates a new literal with the given lexical value and given datatype.
* @param lexicalValue The lexical value for the literal. Cannot be null.
* @param datatypeURI The URI of the datatype of the literal or null if not required.
* @return the newly created literal value.
* @throws GraphElementFactoryException If the resource failed to be created.
*/
public Literal createLiteral(String lexicalValue, URI datatypeURI) throws GraphElementFactoryException {
// create the node identifier
LiteralImpl newLiteral = new LiteralImpl(lexicalValue, datatypeURI);
addNodeId(newLiteral);
return newLiteral;
}
/**
* Creates a new node id for the given Literal. Sets the node id of the given newLiteral.
* @param newLiteral A newly created newLiteral.
*/
private void addNodeId(LiteralImpl newLiteral) {
// find the string identifier for this node
String strId = newLiteral.getEscapedForm();
// check if the node already exists in the string pool
Long tmpNodeId = stringPool.get(strId);
if (null != tmpNodeId) {
// return the existing node instead
newLiteral.setId(tmpNodeId);
return;
} else {
// create the node identifier
Long nodeId = new Long(nextNode);
newLiteral.setId(nodeId);
// put the node in the pool
nodePool.put(nodeId, newLiteral);
// put the URI string into the pool
stringPool.put(strId, nodeId);
// increment the node, since we used it
nextNode++;
}
}
/**
* Creates a new triple to be used in the graph. Does not add it to the
* graph. Use @see Graph#add.
* @param subject The subject of the statement.
* @param predicate The predicate of the statement.
* @param object The object of the statement.
* @return the newly created triple object.
* @throws GraphElementFactoryException If the resource failed to be created.
*/
public Triple createTriple(SubjectNode subject, PredicateNode predicate,
ObjectNode object) throws GraphElementFactoryException {
return new TripleImpl(subject, predicate, object);
}
/**
* Package method for adding in a node that was not created by this factory.
* Used by GraphImpl for deserializing.
* @param node The node to add.
* @throws IllegalArgumentException The node conflicts with one already in use.
*/
void registerNode(MemNode node) {
// get the id for this node
Long id = node.getId();
// look the node up to see if it already exists in the graph
MemNode existingNode = nodePool.get(id);
if (null != existingNode) {
// check that the node is equal to the one that is already in the graph
if (existingNode.equals(node)) return;
// node does not match
throw new IllegalArgumentException("SkipListNode conflicts with one already in the graph");
}
// add the node
nodePool.put(id, node);
// check if the node has a string representation
if (!(node instanceof BlankNode)) {
if (node instanceof Literal) {
stringPool.put(((Literal) node).getEscapedForm(), node.getId());
} else {
stringPool.put(node.toString(), node.getId());
}
}
// update the nextNode counter to a unique number
if (id.longValue() >= nextNode) nextNode = id.longValue() + 1;
}
/**
* Package scope method to get all the nodes in the node pool. Used by GraphImpl for serializing.
* @return The node pool.
*/
java.util.Collection<MemNode> getNodePool() {
return nodePool.values();
}
/**
* Package method to find a node in the node pool by its id.
*
* @param id The id of the node to search for.
* @return The node referred to by the id, null if not found.
*/
Node getNodeById(Long id) {
return nodePool.get(id);
}
/**
* Package method to find a node id based on its string representation.
*
* @param str The string representation of a node.
* @return The id of the node with the given string.
*/
Long getNodeIdByString(String str) {
return stringPool.get(str);
}
}