/*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
* the License for the specific language governing rights and limitations
* under the License.
*
* The Original Code is the Kowari Metadata Store.
*
* The Initial Developer of the Original Code is Plugged In Software Pty
* Ltd (http://www.pisoftware.com, mailto:info@pisoftware.com). Portions
* created by Plugged In Software Pty Ltd are Copyright (C) 2001,2002
* Plugged In Software Pty Ltd. All Rights Reserved.
*
* Contributor(s): N/A.
*
* [NOTE: The text of this Exhibit A may differ slightly from the text
* of the notices in the Source Code files of the Original Code. You
* should use the text of this Exhibit A rather than the text found in the
* Original Code Source Code for Your Modifications.]
*
*/
package org.mulgara.client.jrdf.answer;
// Java 2 standard packages
import java.util.*;
// Log4J
import org.apache.log4j.Logger;
// JRDF
import org.jrdf.graph.*;
// Internal packages
import org.mulgara.client.jrdf.*;
import org.mulgara.client.jrdf.exception.*;
import org.mulgara.query.*;
/**
* A ClosableIterator implementation for client side use, backed by an Answer.
*
* @created 2004-07-29
*
* @author <a href="mailto:robert.turner@tucanatech.com">Robert Turner</a>
*
* @version $Revision: 1.9 $
*
* @modified $Date: 2005/01/13 11:53:36 $
*
* @maintenanceAuthor $Author: newmana $
*
* @company <A href="mailto:info@PIsoftware.com">Plugged In Software</A>
*
* @copyright ©2001 <a href="http://www.pisoftware.com/">Plugged In
* Software Pty Ltd</a>
*
* @licence <a href="{@docRoot}/../../LICENCE">Mozilla Public License v1.1</a>
* Portions by Paula Gearon.
* @copyright ©2006 <a href="http://www.herzumsoftware.com/">Herzum Software LLC</a>
*/
public class ClosableAnswerIteratorProxy implements VirtualClosableIteratorProxy<Triple> {
/**
* Logger. This is named after the class.
*/
@SuppressWarnings("unused")
private final static Logger log = Logger.getLogger(
ClosableAnswerIteratorProxy.class.getName());
/** An error string that JRDF expects. */
private final static String JRDF_STATE_ERROR_STRING = "Next not called or beyond end of data";
/** Data Source used by the iterator */
private Answer answer = null;
/** Triple used to constrain the results from the Answer. */
private Triple filter = null;
/** Graph used to perform remove operations on - can be null */
private RemoteGraphProxy graph = null;
/** Builder used to create Graph Objects */
private GraphElementBuilder builder = null;
/** Next triple to be returned */
private Triple nextTriple = null;
/** Last triple that was returned */
private Triple lastTriple = null;
/** Indicates the Iterator has been closed */
private boolean closed = false;
/** Answer column that refers to the Subject */
private static final int SUBJECT_COLUMN = 0;
/** Answer column that refers to the Predicate */
private static final int PREDICATE_COLUMN = 1;
/** Answer column that refers to the Object */
private static final int OBJECT_COLUMN = 2;
/**
* Constructor. keeps a reference to the graph that can perform remove
* operations.
*
*/
public ClosableAnswerIteratorProxy(RemoteGraphProxy creator, Triple filter,
Answer dataSource) throws JRDFClientException {
super();
//cannot proceed without a valid proxy
if (dataSource == null) {
throw new IllegalArgumentException("Answer cannot be null.");
}
//must have a creator
if (creator == null) {
throw new IllegalArgumentException("'creator' Graph cannot be null.");
}
this.answer = dataSource;
this.graph = creator;
//null filter indicates that the results are not filtered
if (filter == null) {
try {
this.filter = this.builder.createTriple(null, null, null);
}
catch (GraphElementFactoryException factoryException) {
//error occurred in the Builder
throw new JRDFClientException("Could not create empty Triple.",
factoryException);
}
}
else {
this.filter = filter;
}
//instantiate builder for creating Graph Objects
try {
this.builder = new GraphElementBuilder();
}
catch (GraphException graphException) {
//problem instantiating Builder
throw new JRDFClientException("Could not create GraphElementFactory.",
graphException);
}
//cache first triple
try {
this.answer.beforeFirst();
this.updateNextTriple();
}
catch (TuplesException tuplesException) {
//something went wrong
throw new JRDFClientException("Could not initialize Answer.",
tuplesException);
}
}
/**
* Closes the iterator by freeing any resources that it current holds. This
* must be done as soon as possible. Once an iterator is closed none of the
* operations on a iterator will operate i.e. they will throw an exception.
*/
public boolean close() {
// try {
//only close once
if (!this.closed) {
// this.answer.close();
}
//if there was no Exception, record the close.
return (this.closed = true);
// }
// catch (TuplesException tuplesException) {
//
// //something went wrong
// throw new JRDFClientException("Could not close Answer.", tuplesException);
// }
}
/**
* Returns <tt>true</tt> if the iteration has more elements. (In other
* words, returns <tt>true</tt> if <tt>next</tt> would return an element
* rather than throwing an exception.)
*
* @return <tt>true</tt> if the iterator has more elements.
*/
public boolean hasNext() {
//ensure the iterator is not closed
if (this.closed) {
throw new IllegalStateException("ClosableIterator has been closed.");
}
return (this.nextTriple != null);
}
/**
* Returns the next Triple in the iteration.
*
* @return the next element in the iteration.
* @exception NoSuchElementException iteration has no more elements.
*/
public Triple next() {
//ensure the iterator is not closed
if (this.closed) {
throw new IllegalStateException("ClosableIterator has been closed.");
}
//check if end exceeded
if (!this.hasNext()) {
throw new JRDFClientException(
"next() called after end of Answer reached.");
}
//value to be returned
this.lastTriple = this.nextTriple;
//cache next Triple
this.updateNextTriple();
//return "old" value
return this.lastTriple;
}
/**
* Increments the next triple.
*
* @throws JRDFClientException
*/
private void updateNextTriple() throws JRDFClientException {
//update the next Triple
this.nextTriple = this.getNextTriple();
}
/**
* Gets the next triple from the Answer's row.
*
* @throws JRDFClientException
* @return Triple
*/
private Triple getNextTriple() throws JRDFClientException {
//value to be returned
Triple result = null;
//nodes being checked
SubjectNode subject = null;
PredicateNode predicate = null;
ObjectNode object = null;
try {
//find next valid/filtered triple
while (this.answer.next()) {
//get next triple from the answer
subject = this.getNextSubject();
predicate = this.getNextPredicate();
object = this.getNextObject();
//check triple
if (filterTriple(subject, predicate, object)){
//validate triple and return
if ( (subject == null)
&& (predicate == null)
&& (object == null)) {
throw new JRDFClientException("Answer is invalid. Triple contains " +
"null node.");
}
return this.builder.createTriple(subject, predicate, object);
}
}
}
catch (GraphElementFactoryException factoryException) {
//error occurred in the Builder
throw new JRDFClientException("Could not create next Triple.",
factoryException);
}
catch (TuplesException tupleException) {
//error occurred in the Answer
throw new JRDFClientException("Could not get next Triple from Answer.",
tupleException);
}
return result;
}
/**
* Determines if the Triple is valid when applied to the filter.
* Short-circuits as soon as a condition fails.
*
* @return boolean
*/
private boolean filterTriple(SubjectNode subject, PredicateNode predicate,
ObjectNode object) {
//validate arguments
if ( (subject == null)
|| (predicate == null)
|| (object == null)) {
return false;
}
//check subject (if filter field not null)
if ( (this.filter.getSubject() != null)
&& (!this.filter.getSubject().equals(subject))) {
//filter subject not null and subject argument does not match
return false;
}
//check predicate (if filter field not null)
if ( (this.filter.getPredicate() != null)
&& (!this.filter.getPredicate().equals(predicate))) {
//filter predicate not null and predicate argument does not match
return false;
}
//check object (if filter field not null)
if ( (this.filter.getObject() != null)
&& (!this.filter.getObject().equals(object))) {
//filter object not null and object argument does not match
return false;
}
//all filters have passed, return true
return true;
}
/**
* Evaluates and returns a SubjectNode representing the object in subject
* column.
*
* @throws JRDFClientException
* @return SubjectNode
*/
private SubjectNode getNextSubject() throws JRDFClientException {
try {
return (SubjectNode)this.answer.getObject(SUBJECT_COLUMN);
}
catch (TuplesException tuplesException) {
throw new JRDFClientException("Could not get next Subject.",
tuplesException);
}
catch (ClassCastException classException) {
throw new JRDFClientException("Subject column contains invalid object. " +
"Answer is invalid.", classException);
}
}
/**
* Evaluates and returns a PredicateNode representing the object in predicate
* column.
*
* @throws JRDFClientException
* @return PredicateNode
*/
private PredicateNode getNextPredicate() throws JRDFClientException {
try {
//predicate column must contain a PredicateNode
return (PredicateNode)this.answer.getObject(PREDICATE_COLUMN);
}
catch (TuplesException tuplesException) {
throw new JRDFClientException("Could not get next Predicate.",
tuplesException);
}
catch (ClassCastException classException) {
throw new JRDFClientException("Predicate column contains invalid " +
"object. Answer is invalid.",
classException);
}
}
/**
* Evaluates and returns a ObjectNode representing the object in object column.
*
* @throws JRDFClientException
* @return ObjectNode
*/
private ObjectNode getNextObject() throws JRDFClientException {
try {
return (ObjectNode)this.answer.getObject(OBJECT_COLUMN);
}
catch (TuplesException tuplesException) {
throw new JRDFClientException("Could not get next Object.",
tuplesException);
}
catch (ClassCastException classException) {
throw new JRDFClientException("Object column contains invalid object. " +
"Answer is invalid.", classException);
}
}
/**
* If a Graph is supplied, remove is called on the graph, otherwise an
* Exception is thrown.
*
* @exception UnsupportedOperationException If a Graph has not been supplied.
* @exception JRDFClientException if the <tt>remove</tt> operation is not
* supported by the Graph.
*/
public void remove() {
if (this.graph != null) {
try {
if (this.lastTriple == null) {
throw new IllegalStateException(JRDF_STATE_ERROR_STRING);
}
//wrap the last triple to be returned in an Iterator
Set<Triple> lastTriple = new HashSet<Triple>();
lastTriple.add(this.lastTriple);
//remove
this.graph.remove(lastTriple.iterator());
}
catch (GraphException graphException) {
throw new JRDFClientException("Could not remove Triple.", graphException);
}
} else {
throw new UnsupportedOperationException(
"ClosableAnswerIteratorProxy.remove() not supported. Cannot access Graph.");
}
}
}