/*- * See the file LICENSE for redistribution information. * * Copyright (c) 2000-2003 * Sleepycat Software. All rights reserved. * * $Id: StoredCollection.java,v 1.2 2004/03/30 01:23:34 jtownsen Exp $ */ package com.sleepycat.bdb.collection; import com.sleepycat.bdb.DataCursor; import com.sleepycat.bdb.DataView; import com.sleepycat.db.Db; import com.sleepycat.db.DbException; import com.sleepycat.bdb.util.RuntimeExceptionWrapper; import java.io.IOException; import java.util.Arrays; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.List; /** * A abstract base class for all stored collections. This class, and its * base class {@link StoredContainer}, provide implementations of most methods * in the {@link Collection} interface. Other methods, such as {@link #add} * and {@link #remove}, are provided by concrete classes that extend this * class. * *

In addition, this class provides the following methods for stored * collections only. Note that the use of these methods is not compatible with * the standard Java collections interface.

* * * @author Mark Hayes */ public abstract class StoredCollection extends StoredContainer implements Collection { StoredCollection(DataView view) { super(view); } final boolean add(Object key, Object value) { boolean doAutoCommit = beginAutoCommit(); try { int err = view.put(key, value, Db.DB_NODUPDATA, null); commitAutoCommit(doAutoCommit); return (err == 0); } catch (Exception e) { throw handleException(e, doAutoCommit); } } /** * Returns an iterator over the elements in this collection. * The iterator will be read-only if the collection is read-only. * This method conforms to the {@link Collection#iterator} interface. * * @return a {@link StoredIterator} for this collection. * * @throws RuntimeExceptionWrapper if a {@link DbException} is thrown. * * @see #isWriteAllowed */ public Iterator iterator() { return iterator(isWriteAllowed()); } /** * Returns a read or read-write iterator over the elements in this * collection. * This method does not exist in the standard {@link Collection} interface. * * @param writeAllowed is true to open a read-write iterator or false to * open a read-only iterator. If the collection is read-only the iterator * will always be read-only. * * @return a {@link StoredIterator} for this collection. * * @throws IllegalStateException if writeAllowed is true but the collection * is read-only. * * @throws RuntimeExceptionWrapper if a {@link DbException} is thrown. * * @see #isWriteAllowed */ public StoredIterator iterator(boolean writeAllowed) { try { return new StoredIterator(this, writeAllowed && isWriteAllowed(), null); } catch (Exception e) { throw StoredContainer.convertException(e); } } /** * Returns an array of all the elements in this collection. * This method conforms to the {@link Collection#toArray()} interface. * * @throws RuntimeExceptionWrapper if a {@link DbException} is thrown. */ public Object[] toArray() { ArrayList list = new ArrayList(); Iterator i = iterator(); try { while (i.hasNext()) { list.add(i.next()); } } finally { StoredIterator.close(i); } return list.toArray(); } /** * Returns an array of all the elements in this collection whose runtime * type is that of the specified array. * This method conforms to the {@link Collection#toArray(Object[])} * interface. * * @throws RuntimeExceptionWrapper if a {@link DbException} is thrown. */ public Object[] toArray(Object[] a) { int j = 0; Iterator i = iterator(); try { while (j < a.length && i.hasNext()) { a[j++] = i.next(); } if (j < a.length) { a[j] = null; } else if (i.hasNext()) { ArrayList list = new ArrayList(Arrays.asList(a)); while (i.hasNext()) { list.add(i.next()); } a = list.toArray(a); } } finally { StoredIterator.close(i); } return a; } /** * Returns true if this collection contains all of the elements in the * specified collection. * This method conforms to the {@link Collection#containsAll} interface. * * @throws RuntimeExceptionWrapper if a {@link DbException} is thrown. */ public boolean containsAll(Collection coll) { Iterator i = coll.iterator(); try { while (i.hasNext()) { if (!contains(i.next())) { return false; } } } finally { StoredIterator.close(i); } return true; } /** * Adds all of the elements in the specified collection to this collection * (optional operation). * This method calls the {@link #add(Object)} method of the concrete * collection class, which may or may not be supported. * This method conforms to the {@link Collection#addAll} interface. * * @throws UnsupportedOperationException if the collection is read-only, or * if the collection is indexed, or if the add method is not supported by * the concrete collection. * * @throws RuntimeExceptionWrapper if a {@link DbException} is thrown. */ public boolean addAll(Collection coll) { Iterator i = null; boolean doAutoCommit = beginAutoCommit(); try { i = coll.iterator(); boolean changed = false; while (i.hasNext()) { if (add(i.next())) { changed = true; } } StoredIterator.close(i); commitAutoCommit(doAutoCommit); return changed; } catch (Exception e) { StoredIterator.close(i); throw handleException(e, doAutoCommit); } } /** * Removes all this collection's elements that are also contained in the * specified collection (optional operation). * This method conforms to the {@link Collection#removeAll} interface. * * @throws UnsupportedOperationException if the collection is read-only. * * @throws RuntimeExceptionWrapper if a {@link DbException} is thrown. */ public boolean removeAll(Collection coll) { return removeAll(coll, true); } /** * Retains only the elements in this collection that are contained in the * specified collection (optional operation). * This method conforms to the {@link Collection#removeAll} interface. * * @throws UnsupportedOperationException if the collection is read-only. * * @throws RuntimeExceptionWrapper if a {@link DbException} is thrown. */ public boolean retainAll(Collection coll) { return removeAll(coll, false); } private boolean removeAll(Collection coll, boolean ifExistsInColl) { Iterator i = null; boolean doAutoCommit = beginAutoCommit(); try { boolean changed = false; i = iterator(); while (i.hasNext()) { if (ifExistsInColl == coll.contains(i.next())) { i.remove(); changed = true; } } StoredIterator.close(i); commitAutoCommit(doAutoCommit); return changed; } catch (Exception e) { StoredIterator.close(i); throw handleException(e, doAutoCommit); } } /** * Compares the specified object with this collection for equality. * A value comparison is performed by this method and the stored values * are compared rather than calling the equals() method of each element. * This method conforms to the {@link Collection#equals} interface. * * @throws RuntimeExceptionWrapper if a {@link DbException} is thrown. */ public boolean equals(Object other) { if (other instanceof Collection) { Collection otherColl = StoredCollection.copyCollection(other); Iterator i = iterator(); try { while (i.hasNext()) { if (!otherColl.remove(i.next())) { return false; } } return otherColl.isEmpty(); } finally { StoredIterator.close(i); } } else { return false; } } /** * Returns a copy of this collection as an ArrayList. This is the same as * {@link #toArray()} but returns a collection instead of an array. * * @return an {@link ArrayList} containing a copy of all elements in this * collection. * * @throws RuntimeExceptionWrapper if a {@link DbException} is thrown. */ public List toList() { ArrayList list = new ArrayList(); Iterator i = iterator(); try { while (i.hasNext()) list.add(i.next()); return list; } finally { StoredIterator.close(i); } } /** * Converts the collection to a string representation for debugging. * WARNING: All elements will be converted to strings and returned and * therefore the returned string may be very large. * * @return the string representation. * * @throws RuntimeExceptionWrapper if a {@link DbException} is thrown. */ public String toString() { StringBuffer buf = new StringBuffer(); buf.append("["); Iterator i = iterator(); try { while (i.hasNext()) { if (buf.length() > 1) buf.append(','); buf.append(i.next().toString()); } buf.append(']'); return buf.toString(); } finally { StoredIterator.close(i); } } /** * Returns an iterator representing an equality join of the indices and * index key values specified. * The indices will be sorted by least number of references, which is * commonly the best optimization. * This method does not exist in the standard {@link Collection} interface. * *

The returned iterator supports only the two methods: hasNext() and * next(). All other methods will throw UnsupportedOperationException.

* * @param indices is an array of indices with elements corresponding to * those in the indexKeys array. * * @param indexKeys is an array of index key values identifying the * elements to be selected. * * @return an iterator over the elements in this collection that match * all specified index key values. * * @throws IllegalArgumentException if this collection is indexed or if a * given index does not have the same store as this collection. * * @throws RuntimeExceptionWrapper if a {@link DbException} is thrown. */ public StoredIterator join(StoredContainer[] indices, Object[] indexKeys) { return join(indices, indexKeys, false); } /** * Returns an iterator representing an equality join of the indices and * index key values specified. * The indices may be presorted to allow custom optimizations. * This method does not exist in the standard {@link Collection} interface. * *

The returned iterator supports only the two methods: hasNext() and * next(). All other methods will throw UnsupportedOperationException.

* * @param indices is an array of indices with elements corresponding to * those in the indexKeys array. * * @param indexKeys is an array of index key values identifying the * elements to be selected. * * @param presorted is true if the index order should not be changed, or * false to use the default sorting by least number of references. * * @return an iterator over the elements in this collection that match * all specified index key values. * * @throws IllegalArgumentException if this collection is indexed or if a * given index does not have the same store as this collection. * * @throws RuntimeExceptionWrapper if a {@link DbException} is thrown. */ public StoredIterator join(StoredContainer[] indices, Object[] indexKeys, boolean presorted) { try { DataView[] indexViews = new DataView[indices.length]; for (int i = 0; i < indices.length; i += 1) { indexViews[i] = indices[i].view; } DataCursor cursor = view.join(indexViews, indexKeys, presorted); return new StoredIterator(this, false, cursor); } catch (Exception e) { throw StoredContainer.convertException(e); } } final Object getFirstOrLast(boolean doGetFirst) { DataCursor cursor = null; try { cursor = new DataCursor(view, false); int err = cursor.get(null, null, doGetFirst ? Db.DB_FIRST : Db.DB_LAST, false); return (err == 0) ? makeIteratorData(null, cursor) : null; } catch (Exception e) { throw StoredContainer.convertException(e); } finally { closeCursor(cursor); } } abstract Object makeIteratorData(StoredIterator iterator, DataCursor cursor) throws DbException, IOException; abstract boolean hasValues(); boolean iterateDuplicates() { return true; } void checkIterAddAllowed() throws UnsupportedOperationException { if (!areDuplicatesAllowed()) { throw new UnsupportedOperationException("duplicates required"); } } int getIndexOffset() { return 0; } private static Collection copyCollection(Object other) { if (other instanceof StoredCollection) { return ((StoredCollection) other).toList(); } else { return new ArrayList((Collection) other); } } }