/* * Copyright (c) 2007-2009, James Leigh All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * - Neither the name of the openrdf.org nor the names of its contributors may * be used to endorse or promote products derived from this software without * specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * */ package org.openrdf.repository.object.behaviours; import info.aduna.iteration.CloseableIteration; import info.aduna.iteration.ConvertingIteration; import java.util.AbstractSequentialList; import java.util.ArrayList; import java.util.ListIterator; import java.util.NoSuchElementException; import org.openrdf.annotations.Precedes; import org.openrdf.model.BNode; import org.openrdf.model.Literal; import org.openrdf.model.Resource; import org.openrdf.model.Statement; import org.openrdf.model.URI; import org.openrdf.model.Value; import org.openrdf.model.ValueFactory; import org.openrdf.model.vocabulary.RDF; import org.openrdf.repository.RepositoryConnection; import org.openrdf.repository.RepositoryException; import org.openrdf.repository.RepositoryResult; import org.openrdf.repository.object.ObjectConnection; import org.openrdf.repository.object.RDFObject; import org.openrdf.repository.object.exceptions.ObjectPersistException; import org.openrdf.repository.object.exceptions.ObjectStoreException; import org.openrdf.repository.object.traits.Mergeable; import org.openrdf.repository.object.traits.Refreshable; /** * Java instance for rdf:List as a familiar interface to manipulate this List. * This implemention can only be modified when in autoCommit (autoFlush), or * when read uncommitted is supported. * * @author James Leigh */ @Precedes(RDFObjectImpl.class) public abstract class RDFList extends AbstractSequentialList<Object> implements Refreshable, Mergeable, RDFObject { private int _size = -1; private RDFList parent; public void refresh() { _size = -1; if (parent != null) parent.refresh(); } public void merge(Object source) { if (source instanceof java.util.List) { clear(); addAll((java.util.List) source); } } ValueFactory getValueFactory() { RepositoryConnection conn = getObjectConnection(); return conn.getValueFactory(); } private CloseableIteration<Value, RepositoryException> getValues(Resource subj, URI pred, Value obj) { try { RepositoryResult<Statement> stmts; ObjectConnection conn = getObjectConnection(); stmts = conn.getStatements(subj, pred, obj); return new ConvertingIteration<Statement, Value, RepositoryException>(stmts) { @Override protected Value convert(Statement stmt) throws RepositoryException { return stmt.getObject(); } }; } catch (RepositoryException e) { throw new ObjectStoreException(e); } } void addStatement(Resource subj, URI pred, Value obj) { if (obj == null) return; try { ObjectConnection conn = getObjectConnection(); conn.add(subj, pred, obj); } catch (RepositoryException e) { throw new ObjectPersistException(e); } } void removeStatements(Resource subj, URI pred, Value obj) { try { ObjectConnection conn = getObjectConnection(); conn.remove(subj, pred, obj); } catch (RepositoryException e) { throw new ObjectPersistException(e); } } @Override public int size() { if (_size < 0) { synchronized (this) { if (_size < 0) { Resource list = getResource(); int size; for (size = 0; list != null && !list.equals(RDF.NIL); size++) { Resource nlist = getRest(list); if (nlist == null && getFirst(list) == null) break; list = nlist; } _size = size; } } } return _size; } @Override public ListIterator<Object> listIterator(final int index) { return new ListIterator<Object>() { private ArrayList<Resource> prevLists = new ArrayList<Resource>(); private boolean removed; Resource list; { for (int i = 0; i < index; i++) { next(); } } public void add(Object o) { ObjectConnection conn = getObjectConnection(); try { boolean autoCommit = conn.isAutoCommit(); if (autoCommit) conn.setAutoCommit(false); try { if (getResource().equals(RDF.NIL)) { // size == 0 throw new ObjectPersistException( "cannot add a value to the nil list"); /* * list = _id = getValueFactory().createBNode(); * addStatement(list, RDF.FIRST, * SesameProperty.createValue(List.this, o)); * addStatement(list, RDF.REST, RDF.NIL); */ } Value value = o == null ? null : getObjectConnection() .addObject(o); if (getFirst(getResource()) == null) { // size == 0 list = getResource(); addStatement(list, RDF.FIRST, value); addStatement(list, RDF.REST, RDF.NIL); } else if (list == null) { // index = 0 Value first = getFirst(getResource()); Resource rest = getRest(getResource()); BNode newList = getValueFactory().createBNode(); addStatement(newList, RDF.FIRST, first); addStatement(newList, RDF.REST, rest); removeStatements(getResource(), RDF.FIRST, first); removeStatements(getResource(), RDF.REST, rest); addStatement(getResource(), RDF.FIRST, value); addStatement(getResource(), RDF.REST, newList); } else if (!list.equals(RDF.NIL)) { Resource rest = getRest(list); BNode newList = getValueFactory().createBNode(); removeStatements(list, RDF.REST, rest); addStatement(list, RDF.REST, newList); addStatement(newList, RDF.FIRST, value); addStatement(newList, RDF.REST, rest); } else { // index == size throw new NoSuchElementException(); } if (autoCommit) conn.setAutoCommit(true); refresh(); } finally { if (autoCommit && !conn.isAutoCommit()) { conn.rollback(); conn.setAutoCommit(true); } } } catch (RepositoryException e) { throw new ObjectPersistException(e); } } public void set(Object o) { ObjectConnection conn = getObjectConnection(); try { boolean autoCommit = conn.isAutoCommit(); if (autoCommit) conn.setAutoCommit(false); try { if (getResource().equals(RDF.NIL)) { // size == 0 throw new NoSuchElementException(); } else if (list.equals(RDF.NIL)) { // index = size throw new NoSuchElementException(); } else { Value first = getFirst(list); removeStatements(list, RDF.FIRST, first); if (o != null) { Value obj = getObjectConnection().addObject(o); addStatement(list, RDF.FIRST, obj); } } if (autoCommit) conn.setAutoCommit(true); } finally { if (autoCommit && !conn.isAutoCommit()) { conn.rollback(); conn.setAutoCommit(true); } } refresh(); } catch (RepositoryException e) { throw new ObjectPersistException(e); } } public void remove() { ObjectConnection conn = getObjectConnection(); try { boolean autoCommit = conn.isAutoCommit(); if (autoCommit) conn.setAutoCommit(false); try { if (prevLists.size() < 1) { // remove index == 0 Value first = getFirst(list); removeStatements(list, RDF.FIRST, first); Resource next = getRest(list); first = getFirst(next); Resource rest = getRest(next); removeStatements(list, RDF.REST, next); if (first != null) { removeStatements(next, RDF.FIRST, first); addStatement(list, RDF.FIRST, first); } if (rest != null) { removeStatements(next, RDF.REST, rest); addStatement(list, RDF.REST, rest); } } else { // remove index > 0 Resource removedList = list; list = prevLists.remove(prevLists.size() - 1); Value first = getFirst(removedList); Resource rest = getRest(removedList); removeStatements(removedList, RDF.FIRST, first); removeStatements(removedList, RDF.REST, rest); removeStatements(list, RDF.REST, removedList); addStatement(list, RDF.REST, rest); } if (autoCommit) conn.setAutoCommit(true); removed = true; refresh(); } finally { if (autoCommit && !conn.isAutoCommit()) { conn.rollback(); conn.setAutoCommit(true); } } } catch (RepositoryException e) { throw new ObjectStoreException(e); } } public boolean hasNext() { Resource next; if (list == null) { next = getResource(); } else { next = getRest(list); } return getFirst(next) != null; } public Object next() { if (list == null) { list = getResource(); } else if (!removed) { prevLists.add(list); list = getRest(list); } else { removed = false; } Value first = getFirst(list); if (first == null) throw new NoSuchElementException(); return createInstance(first); } public int nextIndex() { if (list == null) return 0; return prevLists.size() + 1; } public int previousIndex() { return prevLists.size() - 1; } public boolean hasPrevious() { return prevLists.size() > 0; } public Object previous() { list = prevLists.remove(prevLists.size() - 1); removed = false; Value first = getFirst(list); if (first == null) throw new NoSuchElementException(); return createInstance(first); } private Object createInstance(Value first) { try { if (first instanceof Resource) return getObjectConnection() .getObject((Resource) first); return getObjectConnection().getObjectFactory() .createObject(((Literal) first)); } catch (RepositoryException e) { throw new ObjectStoreException(e); } } }; } @Override public String toString() { return super.toString(); } Value getFirst(Resource list) { if (list == null) return null; try { CloseableIteration<Value, RepositoryException> stmts; stmts = getValues(list, RDF.FIRST, null); try { if (stmts.hasNext()) return stmts.next(); return null; } finally { stmts.close(); } } catch (RepositoryException e) { throw new ObjectStoreException(e); } } Resource getRest(Resource list) { if (list == null) return null; try { CloseableIteration<Value, RepositoryException> stmts; stmts = getValues(list, RDF.REST, null); try { if (stmts.hasNext()) return (Resource) stmts.next(); return null; } finally { stmts.close(); } } catch (RepositoryException e) { throw new ObjectStoreException(e); } } }