/*- * See the file LICENSE for redistribution information. * * Copyright (c) 2000, 2010 Oracle and/or its affiliates. All rights reserved. * */ package com.sleepycat.collections; import java.util.Collection; import java.util.Collections; import java.util.Iterator; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentMap; import com.sleepycat.bind.EntityBinding; import com.sleepycat.bind.EntryBinding; import com.sleepycat.db.Database; import com.sleepycat.db.OperationStatus; import com.sleepycat.util.RuntimeExceptionWrapper; import com.sleepycat.util.keyrange.KeyRangeException; /** * A Map view of a {@link Database}. * *
In addition to the standard Map methods, this class provides the * following methods for stored maps only. Note that the use of these methods * is not compatible with the standard Java collections interface.
*The key parameter may be null if an entity binding is used and the * key will be derived from the value (entity) parameter. If an entity * binding is used and the key parameter is non-null, then the key * parameter must be equal to the key derived from the value parameter.
* * @return the previous value associated with specified key, or null if * there was no mapping for the key or if duplicates are allowed. * * * @throws UnsupportedOperationException if the collection is indexed, or * if the collection is read-only. * * @throws IllegalArgumentException if an entity value binding is used and * the primary key of the value given is different than the existing stored * primary key. * * @throws RuntimeExceptionWrapper if a checked exception is thrown, * including a {@code DatabaseException} on BDB (C edition). */ public V put(K key, V value) { return (V) putKeyValue(key, value); } /** * Appends a given value returning the newly assigned key. If a {@link * PrimaryKeyAssigner} is associated with Store for this map, it will be * used to assigned the returned key. Otherwise the Store must be a QUEUE * or RECNO database and the next available record number is assigned as * the key. This method does not exist in the standard {@link Map} * interface. * *Note that for the JE product, QUEUE and RECNO databases are not * supported, and therefore a PrimaryKeyAssigner must be associated with * the map in order to call this method.
* * @param value the value to be appended. * * @return the assigned key. * * * @throws UnsupportedOperationException if the collection is indexed, or * if the collection is read-only, or if the Store has no {@link * PrimaryKeyAssigner} and is not a QUEUE or RECNO database. * * @throws RuntimeExceptionWrapper if a checked exception is thrown, * including a {@code DatabaseException} on BDB (C edition). */ public K append(V value) { boolean doAutoCommit = beginAutoCommit(); try { Object[] key = new Object[1]; view.append(value, key, null); commitAutoCommit(doAutoCommit); return (K) key[0]; } catch (Exception e) { throw handleException(e, doAutoCommit); } } /** * Removes the mapping for this key from this map if present (optional * operation). If duplicates are allowed, this method removes all * duplicates for the given key. This method conforms to the {@link * Map#remove} interface. * * * @throws UnsupportedOperationException if the collection is read-only. * * @throws RuntimeExceptionWrapper if a checked exception is thrown, * including a {@code DatabaseException} on BDB (C edition). */ public V remove(Object key) { Object[] oldVal = new Object[1]; removeKey(key, oldVal); return (V) oldVal[0]; } /** * If the specified key is not already associated with a value, associate * it with the given value. This method conforms to the {@link * ConcurrentMap#putIfAbsent} interface. * * * @throws RuntimeExceptionWrapper if a checked exception is thrown, * including a {@code DatabaseException} on BDB (C edition). */ public V putIfAbsent(K key, V value) { DataCursor cursor = null; boolean doAutoCommit = beginAutoCommit(); try { cursor = new DataCursor(view, true); V oldValue; while (true) { OperationStatus status = cursor.putNoOverwrite(key, value, false /*useCurrentKey*/); if (status == OperationStatus.SUCCESS) { /* We inserted the key. Return null. */ oldValue = null; break; } else { status = cursor.getSearchKey(key, null /*value*/, false /*lockForWrite*/); if (status == OperationStatus.SUCCESS) { /* The key is present. Return the current value. */ oldValue = (V) cursor.getCurrentValue(); break; } else { /* * If Serializable isolation is not configured, another * thread can delete the record after our attempt to * insert it failed above. Loop back and try again. */ continue; } } } closeCursor(cursor); commitAutoCommit(doAutoCommit); return oldValue; } catch (Exception e) { closeCursor(cursor); throw handleException(e, doAutoCommit); } } /** * Remove entry for key only if currently mapped to given value. This * method conforms to the {@link ConcurrentMap#remove(Object,Object)} * interface. * * * @throws RuntimeExceptionWrapper if a checked exception is thrown, * including a {@code DatabaseException} on BDB (C edition). */ public boolean remove(Object key, Object value) { DataCursor cursor = null; boolean doAutoCommit = beginAutoCommit(); try { cursor = new DataCursor(view, true, key); OperationStatus status = cursor.getFirst(true /*lockForWrite*/); boolean removed; if (status == OperationStatus.SUCCESS && cursor.getCurrentValue().equals(value)) { cursor.delete(); removed = true; } else { removed = false; } closeCursor(cursor); commitAutoCommit(doAutoCommit); return removed; } catch (Exception e) { closeCursor(cursor); throw handleException(e, doAutoCommit); } } /** * Replace entry for key only if currently mapped to some value. This * method conforms to the {@link ConcurrentMap#replace(Object,Object)} * interface. * * * @throws RuntimeExceptionWrapper if a checked exception is thrown, * including a {@code DatabaseException} on BDB (C edition). */ public V replace(K key, V value) { DataCursor cursor = null; boolean doAutoCommit = beginAutoCommit(); try { cursor = new DataCursor(view, true, key); OperationStatus status = cursor.getFirst(true /*lockForWrite*/); V oldValue; if (status == OperationStatus.SUCCESS) { oldValue = (V) cursor.getCurrentValue(); cursor.putCurrent(value); } else { oldValue = null; } closeCursor(cursor); commitAutoCommit(doAutoCommit); return oldValue; } catch (Exception e) { closeCursor(cursor); throw handleException(e, doAutoCommit); } } /** * Replace entry for key only if currently mapped to given value. This * method conforms to the {@link * ConcurrentMap#replace(Object,Object,Object)} interface. * * * @throws RuntimeExceptionWrapper if a checked exception is thrown, * including a {@code DatabaseException} on BDB (C edition). */ public boolean replace(K key, V oldValue, V newValue) { DataCursor cursor = null; boolean doAutoCommit = beginAutoCommit(); try { cursor = new DataCursor(view, true, key); OperationStatus status = cursor.getFirst(true /*lockForWrite*/); boolean replaced; if (status == OperationStatus.SUCCESS && cursor.getCurrentValue().equals(oldValue)) { cursor.putCurrent(newValue); replaced = true; } else { replaced = false; } closeCursor(cursor); commitAutoCommit(doAutoCommit); return replaced; } catch (Exception e) { closeCursor(cursor); throw handleException(e, doAutoCommit); } } /** * Returns true if this map contains the specified key. This method * conforms to the {@link Map#containsKey} interface. * * * @throws RuntimeExceptionWrapper if a checked exception is thrown, * including a {@code DatabaseException} on BDB (C edition). */ public boolean containsKey(Object key) { return super.containsKey(key); } /** * Returns true if this map contains the specified value. When an entity * binding is used, this method returns whether the map contains the * primary key and value mapping of the entity. This method conforms to * the {@link Map#containsValue} interface. * * * @throws RuntimeExceptionWrapper if a checked exception is thrown, * including a {@code DatabaseException} on BDB (C edition). */ public boolean containsValue(Object value) { return super.containsValue(value); } /** * Copies all of the mappings from the specified map to this map (optional * operation). When duplicates are allowed, the mappings in the specified * map are effectively appended to the existing mappings in this map, that * is no previously existing mappings in this map are replaced. This * method conforms to the {@link Map#putAll} interface. * * * @throws UnsupportedOperationException if the collection is read-only, or * if the collection is indexed. * * @throws RuntimeExceptionWrapper if a checked exception is thrown, * including a {@code DatabaseException} on BDB (C edition). */ public void putAll(Map extends K, ? extends V> map) { boolean doAutoCommit = beginAutoCommit(); Iterator i = null; try { Collection coll = map.entrySet(); i = storedOrExternalIterator(coll); while (i.hasNext()) { Map.Entry entry = (Map.Entry) i.next(); putKeyValue(entry.getKey(), entry.getValue()); } StoredIterator.close(i); commitAutoCommit(doAutoCommit); } catch (Exception e) { StoredIterator.close(i); throw handleException(e, doAutoCommit); } } /** * Returns a set view of the keys contained in this map. A {@link * java.util.SortedSet} is returned if the map supports key ranges. The * returned collection will be read-only if the map is read-only. This * method conforms to the {@link Map#keySet()} interface. * *Note that the return value is a StoredCollection and must be treated * as such; for example, its iterators must be explicitly closed.
* * @return a {@link StoredKeySet} or a {@link StoredSortedKeySet} for this * map. * * @throws RuntimeExceptionWrapper if a checked exception is thrown, * including a {@code DatabaseException} on BDB (C edition). * * @see #areKeyRangesAllowed * @see #isWriteAllowed */ public SetNote that the return value is a StoredCollection and must be treated * as such; for example, its iterators must be explicitly closed.
* * @return a {@link StoredEntrySet} or a {@link StoredSortedEntrySet} for * this map. * * @throws RuntimeExceptionWrapper if a checked exception is thrown, * including a {@code DatabaseException} on BDB (C edition). * * @see #areKeyRangesAllowed * @see #isWriteAllowed */ public SetNote that the return value is a StoredCollection and must be treated * as such; for example, its iterators must be explicitly closed.
* * @return a {@link StoredValueSet} or a {@link StoredSortedValueSet} for * this map. * * @throws RuntimeExceptionWrapper if a checked exception is thrown, * including a {@code DatabaseException} on BDB (C edition). * * @see #areKeyRangesAllowed * @see #isWriteAllowed */ public CollectionIf no mapping for the given key is present, an empty collection is * returned. If duplicates are not allowed, at most a single value will be * in the collection returned. If duplicates are allowed, the returned * collection's add() method may be used to add values for the given * key.
* * @param key is the key for which values are to be returned. * * @throws RuntimeExceptionWrapper if a checked exception is thrown, * including a {@code DatabaseException} on BDB (C edition). */ public CollectionIf no mapping for the given key is present, an empty collection is * returned. If duplicates are not allowed, at most a single value will be * in the collection returned. If duplicates are allowed, the returned * collection's add() method may be used to add values for the given * key.
* * @param secondaryKey is the secondary key for which duplicates values * will be represented by the returned map. * * @param primaryKeyBinding is the binding used for keys in the returned * map. * * @throws RuntimeExceptionWrapper if a checked exception is thrown, * including a {@code DatabaseException} on BDB (C edition). */ public