/* * Copyright 2008 Fedora Commons, Inc. * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.mulgara.content.rlog; // Java 2 standard packages // Third party packages import java.io.IOException; import java.io.InputStreamReader; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; import org.apache.log4j.Logger; // Apache Log4J // Locally written packages import org.mulgara.content.Content; import org.mulgara.content.NotModifiedException; import org.mulgara.content.NotModifiedTuplesException; import org.mulgara.krule.rlog.Interpreter; import org.mulgara.krule.rlog.ParseException; import org.mulgara.query.Cursor; import org.mulgara.query.TuplesException; import org.mulgara.query.Variable; import org.mulgara.resolver.spi.LocalizeException; import org.mulgara.resolver.spi.ResolverSession; import org.mulgara.resolver.spi.Statements; import org.mulgara.resolver.spi.StatementsWrapperResolution; import org.mulgara.store.nodepool.NodePoolException; import org.mulgara.store.tuples.AbstractTuples; import org.mulgara.store.tuples.Tuples; import org.mulgara.krule.rlog.Rlog; import org.mulgara.krule.rlog.ast.output.KruleGenerator; import org.mulgara.krule.rlog.parser.TypeException; import org.mulgara.krule.rlog.parser.URIParseException; /** * Parses an {@link java.io.InputStream} into {@link Statements}. * This parser uses memory and does not stream. * * @created Feb 24, 2009 * @author Paula Gearon * @copyright © 2008 <a href="http://www.fedora-commons.org/">Fedora Commons</a> */ public class RlogStatements extends AbstractTuples implements Statements { /** Logger. */ private static final Logger logger = Logger.getLogger(RlogStatements.class.getName()); /** The session used to globalize the RDF nodes from the stream. */ private ResolverSession resolverSession; private Interpreter rlogParser = null; private List<long[]> triples = null; /** The current row iterator. If the cursor is not on a row, this will be <code>null</code> */ private Iterator<long[]> tripleIterator = null; /** The current row. If the cursor is not on a row, this will be <code>null</code> */ private long[] triple; /** If this exception is not null then it has yet to be thrown to the calling process */ TuplesException outstandingException = null; /** * Construct an RLog parser. * * @param content the RLog content * @param resolverSession session against which to localize RDF nodes * @throws IllegalArgumentException if <var>inputStream</var> or <var>resolverSession</var> are <code>null</code> */ RlogStatements(Content content, ResolverSession resolverSession) { if (content == null) throw new IllegalArgumentException( "Null \"content\" parameter"); if (resolverSession == null) throw new IllegalArgumentException("Null \"resolverSession\" parameter"); this.resolverSession = resolverSession; // Fix the magical column names for RDF statements setVariables(new Variable[] { SUBJECT, PREDICATE, OBJECT } ); try { InputStreamReader input = new InputStreamReader(content.newInputStream()); try { rlogParser = new Rlog(input, content.getURI()); } finally { input.close(); } } catch (IOException e) { outstandingException = new TuplesException("Unable to access RLog data", e); } catch (ParseException e) { outstandingException = new TuplesException("Bad RLog structure", e); } catch (TypeException e) { outstandingException = new TuplesException("Head of rule has the wrong type", e); } catch (URIParseException e) { outstandingException = new TuplesException("Illegal URI in document", e); } catch (NotModifiedException e) { outstandingException = new TuplesException("Unexpected duplicate access to RLog file", e); } } public long getSubject() throws TuplesException { return getColumnValue(0); } public long getPredicate() throws TuplesException { return getColumnValue(1); } public long getObject() throws TuplesException { return getColumnValue(2); } /** * {@inheritDoc} * * Non-zero length <var>prefix</var> values don't need to be supported by * this class because prefix filtration is implemented by the * {@link StatementsWrapperResolution} which the existing external resolvers * always apply to their content before returning it. * * @param prefix {@inheritDoc}; for this particular implementation, non-zero * length prefixes are not supported * @throws NotModifiedTuplesException if the underlying tuples are cached * @throws TuplesException {@inheritDoc}; also if <var>prefix</var> is non-zero length */ public void beforeFirst(long[] prefix, int suffixTruncation) throws TuplesException { if (logger.isDebugEnabled()) logger.debug("Before first"); // If the parser found an error, then throw it now if (outstandingException != null) throw outstandingException; // Validate "prefix" parameter if (prefix == null) throw new IllegalArgumentException("Null \"prefix\" parameter"); if (prefix.length != 0) { throw new TuplesException(getClass() + ".beforeFirst isn't implemented for non-zero length prefix"); } // Validate "suffixTruncation" parameter if (suffixTruncation != 0) throw new IllegalArgumentException("Null \"suffixTruncation\" parameter"); tripleIterator = getTriples().iterator(); } /** * The cursor position isn't cloned by this method. */ public Object clone() { RlogStatements cloned = (RlogStatements)super.clone(); cloned.triple = null; return cloned; } /** * Parsing is not streamed, so the data stream is already closed. */ public void close() throws TuplesException { // no op } /** * Get the localized value for the column. * @param column 0 for the subject, 1 for the predicate, 2 for the object * @return the localized GNode for the column */ public long getColumnValue(int column) throws TuplesException { if (triple == null) throw new TuplesException("There is no current row"); if (column >= 3) throw new IllegalArgumentException("Index out of bounds for triple: " + column); return triple[column]; } /** * Get the operands this Tuples represents. Since this object is atomic, there are not operands. * @see org.mulgara.store.tuples.Tuples#getOperands() */ public List<Tuples> getOperands() { return Collections.emptyList(); } /** * @see org.mulgara.store.tuples.AbstractTuples#getRowCardinality() */ public int getRowCardinality() throws TuplesException { long statementCount = getTriples().size(); // Convert the statement count into a cardinality class return statementCount == 0 ? Cursor.ZERO : statementCount == 1 ? Cursor.ONE : Cursor.MANY; } /** * @see org.mulgara.store.tuples.AbstractTuples#getRowCount() */ public long getRowCount() throws TuplesException { return getTriples().size(); } /** * @see org.mulgara.store.tuples.AbstractTuples#getRowUpperBound() */ public long getRowUpperBound() throws TuplesException { return getTriples().size(); } /** * @see org.mulgara.store.tuples.AbstractTuples#getRowExpectedCount() */ public long getRowExpectedCount() throws TuplesException { return getTriples().size(); } /** * @see org.mulgara.store.tuples.Tuples#hasNoDuplicates() */ public boolean hasNoDuplicates() throws TuplesException { return false; } /** * @see org.mulgara.store.tuples.AbstractTuples#isColumnEverUnbound(int) */ public boolean isColumnEverUnbound(int column) throws TuplesException { if (column > 2 || column < 0) throw new TuplesException("No such column " + column); return false; } /** * @see org.mulgara.store.tuples.AbstractTuples#next() */ public boolean next() throws TuplesException { if (!tripleIterator.hasNext()) { tripleIterator = null; triple = null; return false; } triple = tripleIterator.next(); return true; } /** * Retrieve the triples from the parser. * @return A List of all the triples. * @throws TuplesException if there was a Node pool or string pool persistence or access error, * or if the RLog file contained bad data that wasn't caught in the initial parse. */ private List<long[]> getTriples() throws TuplesException { try { if (triples == null) { KruleGenerator generator; generator = new KruleGenerator(rlogParser, resolverSession); triples = new ArrayList<long[]>(); generator.emit(triples); } } catch (NodePoolException e) { throw new TuplesException("Unable to create new nodes during RLog parsing.", e); } catch (LocalizeException e) { throw new TuplesException("Unable to localize data during RLog parsing.", e); } catch (ParseException e) { throw new TuplesException("Errors in RLog file.", e); } return triples; } }