/*
* 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.resolver.spi;
import java.io.IOException;
import java.util.*;
// Third party packages
import org.apache.log4j.*;
import org.jrdf.graph.BlankNode;
import org.jrdf.graph.Node;
// Locally written packages
import org.mulgara.query.Answer;
import org.mulgara.query.TuplesException;
import org.mulgara.query.rdf.BlankNodeImpl;
import org.mulgara.store.nodepool.NodePoolException;
import org.mulgara.store.tuples.AbstractTuples;
import org.mulgara.store.tuples.Tuples;
import org.mulgara.util.IntFile;
import org.mulgara.util.StringToLongMap;
import org.mulgara.util.TempDir;
/**
* Wrapper around a globally valid {@link Answer} instance, converting into
* a local {@link org.mulgara.store.tuples.Tuples}.
*
* @created 2003-10-28
*
* @author <a href="http://staff.pisoftware.com/raboczi">Simon Raboczi</a>
*
* @version $Revision: 1.8 $
*
* @modified $Date: 2005/01/05 04:58:50 $
*
* @maintenanceAuthor $Author: newmana $
*
* @company <A href="mailto:info@PIsoftware.com">Plugged In Software</A>
*
* @copyright © 2003 <A href="http://www.PIsoftware.com/">Plugged In
* Software Pty Ltd</A>
*
* @licence <a href="{@docRoot}/../../LICENCE">Mozilla Public License v1.1</a>
*/
public class LocalizedTuples extends AbstractTuples {
/** Logger. */
@SuppressWarnings("unused")
private static final Logger logger = Logger.getLogger(LocalizedTuples.class.getName());
/** The session to localize into. */
protected final ResolverSession session;
/**
* The wrapped {@link Answer} instance.
* Treat as final except in clone.
*/
protected Answer answer;
/** Mapping between parsed blank node IDs and local node numbers. */
private IntFile blankNodeIdMap;
/** Mapping between blank node rdf:nodeIDs and local node numbers. */
private StringToLongMap blankNodeNameMap;
/** Indicates that the maps have been initialized and are no longer null. */
private boolean mapsInitialized = false;
/**
* Does the localization need to be done in the persistent store.
*/
protected boolean persist;
/**
* Wrap an {@link Answer} instance.
*
* @param globalAnswer the global answer to wrap
* @throws IllegalArgumentException if <var>globalAnswer</var> is
* <code>null</code>
*/
public LocalizedTuples(ResolverSession session, Answer globalAnswer, boolean persist)
{
if (session == null) {
throw new IllegalArgumentException("Null \"session\" parameter");
}
if (globalAnswer == null) {
throw new IllegalArgumentException("Null \"globalAnswer\" parameter");
}
this.session = session;
answer = (Answer) globalAnswer.clone();
setVariables(answer.getVariables());
this.persist = persist;
}
public LocalizedTuples(ResolverSession session, Answer globalAnswer) throws TuplesException
{
this(session, globalAnswer, false);
}
public void beforeFirst() throws TuplesException {
answer.beforeFirst();
}
public void beforeFirst(long[] prefix, int suffixTruncation)
throws TuplesException
{
if (prefix.length == 0 && suffixTruncation == 0) {
answer.beforeFirst();
} else {
throw new TuplesException(
"LocalizedTuples.beforeFirst not implemented for prefix length " +
prefix.length + " and suffix length " + suffixTruncation);
}
}
public Object clone() {
LocalizedTuples copy = (LocalizedTuples)super.clone();
copy.answer = (Answer)answer.clone();
return copy;
}
public void close() throws TuplesException {
answer.close();
if (mapsInitialized) {
blankNodeNameMap.delete();
try {
blankNodeIdMap.delete();
} catch (IOException ioe) {
throw new TuplesException("Unable to manage temporary files", ioe);
}
}
}
public long getColumnValue(int column) throws TuplesException {
try {
Object node = answer.getObject(column);
assert node instanceof Node;
if (node instanceof BlankNode) return localizeBlankNode((BlankNode)node);
return persist
? session.localizePersistent((Node)node)
: session.localize((Node)node);
} catch (NodePoolException e) {
throw new TuplesException("Couldn't create new node to localize column " + column, e);
} catch (Exception e) {
throw new TuplesException("Couldn't localize column " + column, e);
}
}
private long newBlankNode() throws NodePoolException, LocalizeException {
return persist ? session.newBlankNode() : session.localize(new BlankNodeImpl());
}
public long getRowCount() throws TuplesException {
return answer.getRowCount();
}
public long getRowUpperBound() throws TuplesException {
return answer.getRowUpperBound();
}
public long getRowExpectedCount() throws TuplesException {
return answer.getRowExpectedCount();
}
public boolean isEmpty() throws TuplesException {
return answer.isEmpty();
}
/**
* We can't possibly know whether an {@link Answer} column might be
* <code>null</code> without materializing it, so we have to assume it could
* be.
*
* @return <code>true</code>
*/
public boolean isColumnEverUnbound(int column) throws TuplesException {
return true;
}
// We may not be able to trust the Answer received from a distributed query.
public boolean hasNoDuplicates() {
return false;
}
public List<Tuples> getOperands() {
return new ArrayList<Tuples>(0);
}
public boolean next() throws TuplesException {
return answer.next();
}
/**
* Converts a blank node into a local gNode ID.
* @param node The blank node to localize.
* @return A gNode ID that is unique and reproducable for the blank node.
* @throws NodePoolException There was an error allocating a new gNode ID.
* @throws LocalizeException There was an error recalling an earlier conversion, or mixed local and remote nodes.
* @throws TuplesException There was an error creating the maps needed for this tuples.
* @throws IOException There was an error communicating with files used for recalling conversions.
*/
private long localizeBlankNode(BlankNode node) throws NodePoolException, LocalizeException, TuplesException, IOException {
long nodeId;
if (node instanceof BlankNodeImpl) {
nodeId = ((BlankNodeImpl)node).getNodeId();
if (nodeId < 0) {
long foreignId = -nodeId;
if (mapsInitialized) {
nodeId = blankNodeIdMap.getLong(foreignId);
} else {
initMaps();
nodeId = 0;
}
if (nodeId == 0) {
nodeId = newBlankNode();
blankNodeIdMap.putLong(foreignId, nodeId);
}
}
} else {
String foreignIdStr = node.toString();
if (mapsInitialized) {
nodeId = blankNodeNameMap.get(foreignIdStr);
} else {
initMaps();
nodeId = 0;
}
if (nodeId == 0) {
nodeId = newBlankNode();
blankNodeNameMap.put(foreignIdStr, nodeId);
}
}
return nodeId;
}
private void initMaps() throws TuplesException {
try {
blankNodeIdMap = IntFile.open(TempDir.createTempFile("localIdMap", null), true);
blankNodeNameMap = new StringToLongMap();
} catch (IOException ioe) {
throw new TuplesException("Unable to localize tuples", ioe);
}
mapsInitialized = true;
}
}