/******************************************************************************* * Copyright (c) 2007 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 * * Contributors: * Cambridge Semantics Incorporated *******************************************************************************/ package org.openanzo.client; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Set; import org.openanzo.rdf.INamedGraph; import org.openanzo.rdf.IQuadStore; import org.openanzo.rdf.MemQuadStore; import org.openanzo.rdf.NamedGraph; import org.openanzo.rdf.Resource; import org.openanzo.rdf.Statement; import org.openanzo.rdf.URI; import org.openanzo.rdf.Value; import org.openanzo.rdf.utils.Collections; import org.openanzo.rdf.utils.UriGenerator; import org.openanzo.services.IPrecondition; /** * The Transaction contains pointers to parent, child, and sibling nodes, a list of optional preconditions, and two QuadStore instances that make up the delta * aspect of the Transaction. The pointers work as described in the previous section, and we describe peconditions in the next section. Here we describe how we * keep track of additions and deletions in the Transaction as well as how we filter. * * This class is not thread safe. * * @author Joe Betz * @author Ben Szekely ( <a href="mailto:ben@cambridgesemantics.com">ben@cambridgesemantics.com </a>) * */ class Transaction { // Store that holds all the statements added in this transaction. final MemQuadStore additions; // Store that holds all the statements deleted in this transaction. final MemQuadStore deletions; // Array of preconditions associated with this transaction. final Set<IPrecondition> preconditions; // Reference to the parent transaction. Transaction parentTransaction; // Reference to the child transaction. Transaction childTransaction; // Reference to the previous transaction. Transaction previousTransaction; // Reference to the next transaction. Transaction nextTransaction; // Quad store containing the context graphs the entire transaction hierarchy IQuadStore contextQuadStore; INamedGraph contextGraph; final URI transactionUri; // these two sets are used to keep track of post-update processing when new graphs are created // on the server. protected HashMap<URI, URI> namedGraphsToSubscribe = new HashMap<URI, URI>(); final Set<URI> serverUUIDStoFetch = new HashSet<URI>(); /** * Initialize global variables. * * @param preconditions * Preconditions associated with this transaction. This transaction will fail if any of the preconditions fail. */ Transaction(Set<IPrecondition> preconditions, URI uri) { this.additions = new MemQuadStore(); this.deletions = new MemQuadStore(); this.preconditions = preconditions; this.transactionUri = uri; } Transaction(Set<IPrecondition> preconditions) { this(preconditions, UriGenerator.generateNamedGraphUri()); } Transaction() { this(new HashSet<IPrecondition>()); } /** * Add a single statement or multiple statements to this transaction. * * @param statements * Statements added to the currentTransaction. */ void add(Statement... statements) { this.additions.add(statements); this.deletions.remove(statements); } /** * Remove a single statement or multiple statements from this transaction * * @param statements * Statements removed from the currentTransaction. */ void remove(Statement... statements) { this.additions.remove(statements); this.deletions.add(statements); } /** * Filter the given anzo.util.Set of Statement on the additions and deletions in this Transaction. The quad pattern arguments act as a hint to narrow the * filtering. In particular, every Statement in statements is guaranteed to conform to the given quad pattern. * * @param statements * Statements that are to be filtered. * @param subject * Subject of the quad pattern used to filter the given statements. * @param predicate * Predicate of the quad pattern used to filter the given statements. * @param object * Object of the quad pattern used to filter the given statements. * @param namedGraphUris * Named graph uri of the quad pattern used to filter the given statements. */ void filter(Collection<Statement> statements, Resource subject, URI predicate, Value object, URI... namedGraphUris) { Collections.addAllUnique(this.additions.find(subject, predicate, object, namedGraphUris), statements); statements.removeAll(this.deletions.find(subject, predicate, object, namedGraphUris)); } /** * Walk over the transaction tree in pre-order (root-child-sibling), applying the given mapFunction to each transaction. This utility will also be used by * the code that serializes the tree for updateServer. * * @param mapFunction * Function applied to each transaction in the tree. */ void walkTransactionTree(MapFunction mapFunction) { this.performWalkTransactionTree(mapFunction, this); } /** * Walk over the transaction tree in pre-order (root-child-sibling), applying the given mapFunction to each transaction. This utility will also be used by * the code that serializes the tree for updateServer. * * @param mapFunction * Function applied to each transaction in the tree. * @param root * The target transaction. */ void performWalkTransactionTree(MapFunction mapFunction, Transaction root) { mapFunction.call(root); if (root.childTransaction != null && root.childTransaction != root) { this.performWalkTransactionTree(mapFunction, root.childTransaction); } if (root.nextTransaction != null && root.nextTransaction != root) { this.performWalkTransactionTree(mapFunction, root.nextTransaction); } } interface MapFunction { void call(Transaction transaction); } Collection<Statement> getAdditions() { return additions.getStatements(); } Collection<Statement> getDeletions() { return deletions.getStatements(); } Set<IPrecondition> getPreconditions() { return preconditions; } boolean isEmpty() { boolean empty = additions.isEmpty() && deletions.isEmpty() && (contextGraph == null || contextGraph.isEmpty()); if (!empty) { return false; } empty = childTransaction == null || childTransaction.isEmpty(); if (!empty) { return false; } empty = nextTransaction == null || nextTransaction.isEmpty(); return empty; } protected IQuadStore getContextQuadStore() { if (contextQuadStore == null) { if (parentTransaction != null) { contextQuadStore = parentTransaction.getContextQuadStore(); } else if (previousTransaction != null) { contextQuadStore = previousTransaction.getContextQuadStore(); } else { contextQuadStore = new MemQuadStore(); } } return contextQuadStore; } INamedGraph getContext() { if (contextGraph != null) { return contextGraph; } contextGraph = new NamedGraph(transactionUri, getContextQuadStore()); return contextGraph; } @Override public boolean equals(Object obj) { if (!(obj instanceof Transaction)) { return false; } Transaction other = (Transaction) obj; return this.transactionUri.equals(other.transactionUri); } @Override public int hashCode() { return transactionUri.toString().hashCode(); } @Override public String toString() { return transactionUri.toString(); } /** * @return the serverUUIDStoFetch */ Set<URI> getServerUUIDStoFetch() { return serverUUIDStoFetch; } /** * @return the namedGraphsToSubscribe */ HashMap<URI, URI> getNamedGraphsToSubscribe() { return namedGraphsToSubscribe; } }