/******************************************************************************* * Copyright (c) 2004, 2007 IBM Corporation and Cambridge Semantics Incorporated. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * File: $Source: /cvsroot/slrp/glitter/com.ibm.adtech.glitter/src/com/ibm/adtech/glitter/rdf/BlankNodeManager.java,v $ * Created by: Lee Feigenbaum (<a href="mailto:feigenbl@us.ibm.com">feigenbl@us.ibm.com</a>) * Created on: 10/23/06 * Revision: $Id: BlankNodeManager.java 164 2007-07-31 14:11:09Z mroy $ * * Contributors: IBM Corporation - initial API and implementation * Cambridge Semantics Incorporated - Fork to Anzo *******************************************************************************/ package org.openanzo.rdf; import java.util.HashSet; import java.util.Stack; import java.util.UUID; import org.apache.commons.collections15.BidiMap; import org.apache.commons.collections15.bidimap.DualHashBidiMap; import org.openanzo.glitter.exception.InvalidBlankNodeLabelException; /** * The {@link BlankNodeManager} is a factory for creating blank nodes. It allows the creation of a stack of scopes for blank node labels - blank nodes across * different scopes can share labels while maintaining their distinct identity. * * @author lee <lee@cambridgesemantics.com> * */ public class BlankNodeManager { private final Stack<BlankNodeLabelScope> labelScopes; private final HashSet<String> allLabels; private final boolean prohibitLabelReuse; private static final String USCORE = "_"; private static final String DASH = "\\-"; /** * * @param prohibitLabelReuse * If <tt>true</tt>, throws an {@link InvalidBlankNodeLabelException} if a blank node is created with a duplicate label to another blank node * from a different scope. */ public BlankNodeManager(boolean prohibitLabelReuse) { this.labelScopes = new Stack<BlankNodeLabelScope>(); this.allLabels = new HashSet<String>(); this.prohibitLabelReuse = prohibitLabelReuse; enterLabelScope(); } /** * Push a new label scope onto the stack. Should be paired with {@link #exitLabelScope()}. */ public void enterLabelScope() { this.labelScopes.push(new BlankNodeLabelScope()); } /** * Pop a label scope off of the stack. Should pair with a previous call to {@link #enterLabelScope()}. */ public void exitLabelScope() { this.labelScopes.pop(); } private BlankNodeLabelScope currentLabelScope() { return this.labelScopes.peek(); } /** * Returns a {@link BlankNode} for the given label. If there is an existing blank node in this scope with this label, that {@link BlankNode} gets returned. * Otherwise, a new blank node gets minted for this scope with this label. * * @param label * label of blanknode to convert * @return a {@link BlankNode} for the given label * @throws InvalidBlankNodeLabelException * If this {@link BlankNodeManager} prohibits blank node label reuse and this <tt>label</tt> occurs in some other scope already. */ public BlankNode getBlankNode(String label) throws InvalidBlankNodeLabelException { BlankNode bnode = currentLabelScope().label2bnode.get(label); if (bnode == null) { // SPARQL prohibits different label scopes from having // the same label if (this.prohibitLabelReuse && this.allLabels.contains(label)) throw new InvalidBlankNodeLabelException(label); this.allLabels.add(label); if (label.startsWith(Constants.ANZO_BNODE_PREFIX) || label.startsWith(Constants.ANZO_BNODE)) { bnode = MemValueFactory.defaultFactory.createBNode(label); } else { if (prohibitLabelReuse) { bnode = new MemBlankNode((label.startsWith(Constants.BNODE_PREFIX)) ? label.substring(Constants.BNODE_PREFIX.length()) : label); } else { bnode = getBlankNode(); } } currentLabelScope().label2bnode.put(label, bnode); } return bnode; } protected String getLabel(BlankNode blankNode) { return currentLabelScope().label2bnode.getKey(blankNode); } /** * A new {@link BlankNode} with an auto-generated label. * * @return A new {@link BlankNode} with an auto-generated label. */ public BlankNode getBlankNode() { BlankNode bnode = null; String label = generateBnodeString(); bnode = new MemBlankNode(label); return bnode; } private static class BlankNodeLabelScope { private final BidiMap<String, BlankNode> label2bnode; private BlankNodeLabelScope() { this.label2bnode = new DualHashBidiMap<String, BlankNode>(); } } protected static String generateBnodeString() { return (Constants.ANZO_BNODE + UUID.randomUUID().toString()).replaceAll(DASH, USCORE); } }