001: /*-
002: * See the file LICENSE for redistribution information.
003: *
004: * Copyright (c) 2000,2008 Oracle. All rights reserved.
005: *
006: * $Id: StoredMap.java,v 1.45.2.3 2008/01/07 15:14:06 cwl Exp $
007: */
008:
009: package com.sleepycat.collections;
010:
011: import java.util.Collection;
012: import java.util.Collections;
013: import java.util.Iterator;
014: import java.util.Map;
015: import java.util.Set;
016:
017: import com.sleepycat.bind.EntityBinding;
018: import com.sleepycat.bind.EntryBinding;
019: import com.sleepycat.je.Database;
020: import com.sleepycat.util.keyrange.KeyRangeException;
021:
022: /**
023: * A Map view of a {@link Database}.
024: *
025: * <p>In addition to the standard Map methods, this class provides the
026: * following methods for stored maps only. Note that the use of these methods
027: * is not compatible with the standard Java collections interface.</p>
028: * <ul>
029: * <li>{@link #duplicates}</li>
030: * <li>{@link #duplicatesMap}</li>
031: * <li>{@link #append}</li>
032: * </ul>
033: *
034: * @author Mark Hayes
035: */
036: public class StoredMap extends StoredContainer implements Map {
037:
038: private StoredKeySet keySet;
039: private StoredEntrySet entrySet;
040: private StoredValueSet valueSet;
041:
042: /**
043: * Creates a map view of a {@link Database}.
044: *
045: * @param database is the Database underlying the new collection.
046: *
047: * @param keyBinding is the binding used to translate between key buffers
048: * and key objects.
049: *
050: * @param valueBinding is the binding used to translate between value
051: * buffers and value objects.
052: *
053: * @param writeAllowed is true to create a read-write collection or false
054: * to create a read-only collection.
055: *
056: * @throws IllegalArgumentException if formats are not consistently
057: * defined or a parameter is invalid.
058: *
059: * @throws RuntimeExceptionWrapper if a {@link
060: * com.sleepycat.je.DatabaseException} is thrown.
061: */
062: public StoredMap(Database database, EntryBinding keyBinding,
063: EntryBinding valueBinding, boolean writeAllowed) {
064:
065: super (new DataView(database, keyBinding, valueBinding, null,
066: writeAllowed, null));
067: initView();
068: }
069:
070: /**
071: * Creates a map view of a {@link Database} with a {@link
072: * PrimaryKeyAssigner}. Writing is allowed for the created map.
073: *
074: * @param database is the Database underlying the new collection.
075: *
076: * @param keyBinding is the binding used to translate between key buffers
077: * and key objects.
078: *
079: * @param valueBinding is the binding used to translate between value
080: * buffers and value objects.
081: *
082: * @param keyAssigner is used by the {@link #append} method to assign
083: * primary keys.
084: *
085: * @throws IllegalArgumentException if formats are not consistently
086: * defined or a parameter is invalid.
087: *
088: * @throws RuntimeExceptionWrapper if a {@link
089: * com.sleepycat.je.DatabaseException} is thrown.
090: */
091: public StoredMap(Database database, EntryBinding keyBinding,
092: EntryBinding valueBinding, PrimaryKeyAssigner keyAssigner) {
093:
094: super (new DataView(database, keyBinding, valueBinding, null,
095: true, keyAssigner));
096: initView();
097: }
098:
099: /**
100: * Creates a map entity view of a {@link Database}.
101: *
102: * @param database is the Database underlying the new collection.
103: *
104: * @param keyBinding is the binding used to translate between key buffers
105: * and key objects.
106: *
107: * @param valueEntityBinding is the binding used to translate between
108: * key/value buffers and entity value objects.
109: *
110: * @param writeAllowed is true to create a read-write collection or false
111: * to create a read-only collection.
112: *
113: * @throws IllegalArgumentException if formats are not consistently
114: * defined or a parameter is invalid.
115: *
116: * @throws RuntimeExceptionWrapper if a {@link
117: * com.sleepycat.je.DatabaseException} is thrown.
118: */
119: public StoredMap(Database database, EntryBinding keyBinding,
120: EntityBinding valueEntityBinding, boolean writeAllowed) {
121:
122: super (new DataView(database, keyBinding, null,
123: valueEntityBinding, writeAllowed, null));
124: initView();
125: }
126:
127: /**
128: * Creates a map entity view of a {@link Database} with a {@link
129: * PrimaryKeyAssigner}. Writing is allowed for the created map.
130: *
131: * @param database is the Database underlying the new collection.
132: *
133: * @param keyBinding is the binding used to translate between key buffers
134: * and key objects.
135: *
136: * @param valueEntityBinding is the binding used to translate between
137: * key/value buffers and entity value objects.
138: *
139: * @param keyAssigner is used by the {@link #append} method to assign
140: * primary keys.
141: *
142: * @throws IllegalArgumentException if formats are not consistently
143: * defined or a parameter is invalid.
144: *
145: * @throws RuntimeExceptionWrapper if a {@link
146: * com.sleepycat.je.DatabaseException} is thrown.
147: */
148: public StoredMap(Database database, EntryBinding keyBinding,
149: EntityBinding valueEntityBinding,
150: PrimaryKeyAssigner keyAssigner) {
151:
152: super (new DataView(database, keyBinding, null,
153: valueEntityBinding, true, keyAssigner));
154: initView();
155: }
156:
157: StoredMap(DataView view) {
158:
159: super (view);
160: initView();
161: }
162:
163: /**
164: * Override this method to initialize view-dependent fields.
165: */
166: void initAfterClone() {
167: initView();
168: }
169:
170: /**
171: * The keySet, entrySet and valueSet are created during Map construction
172: * rather than lazily when requested (as done with the java.util.Map
173: * implementations). This is done to avoid synchronization every time they
174: * are requested. Since they are requested often but a StoredMap is
175: * created infrequently, this gives the best performance. The additional
176: * views are small objects and are cheap to construct.
177: */
178: private void initView() {
179:
180: /* entrySet */
181: if (isOrdered()) {
182: entrySet = new StoredSortedEntrySet(view);
183: } else {
184: entrySet = new StoredEntrySet(view);
185: }
186:
187: /* keySet */
188: DataView newView = view.keySetView();
189: if (isOrdered()) {
190: keySet = new StoredSortedKeySet(newView);
191: } else {
192: keySet = new StoredKeySet(newView);
193: }
194:
195: /* valueSet */
196: newView = view.valueSetView();
197: if (isOrdered() && newView.canDeriveKeyFromValue()) {
198: valueSet = new StoredSortedValueSet(newView);
199: } else {
200: valueSet = new StoredValueSet(newView);
201: }
202: }
203:
204: /**
205: * Returns the value to which this map maps the specified key. If
206: * duplicates are allowed, this method returns the first duplicate, in the
207: * order in which duplicates are configured, that maps to the specified
208: * key.
209: *
210: * This method conforms to the {@link Map#get} interface.
211: *
212: * @throws RuntimeExceptionWrapper if a {@link
213: * com.sleepycat.je.DatabaseException} is thrown.
214: */
215: public Object get(Object key) {
216:
217: return super .get(key);
218: }
219:
220: /**
221: * Associates the specified value with the specified key in this map
222: * (optional operation). If duplicates are allowed and the specified key
223: * is already mapped to a value, this method appends the new duplicate
224: * after the existing duplicates. This method conforms to the {@link
225: * Map#put} interface.
226: *
227: * <p>The key parameter may be null if an entity binding is used and the
228: * key will be derived from the value (entity) parameter. If an entity
229: * binding is used and the key parameter is non-null, then the key
230: * parameter must be equal to the key derived from the value parameter.</p>
231: *
232: * @return the previous value associated with specified key, or null if
233: * there was no mapping for the key or if duplicates are allowed.
234: *
235: * @throws UnsupportedOperationException if the collection is indexed, or
236: * if the collection is read-only.
237: *
238: * @throws IllegalArgumentException if an entity value binding is used and
239: * the primary key of the value given is different than the existing stored
240: * primary key.
241: *
242: * @throws RuntimeExceptionWrapper if a {@link
243: * com.sleepycat.je.DatabaseException} is thrown.
244: */
245: public Object put(Object key, Object value) {
246:
247: return super .put(key, value);
248: }
249:
250: /**
251: * Appends a given value returning the newly assigned key. If a {@link
252: * PrimaryKeyAssigner} is associated with Store for this map, it will be
253: * used to assigned the returned key. Otherwise the Store must be a QUEUE
254: * or RECNO database and the next available record number is assigned as
255: * the key. This method does not exist in the standard {@link Map}
256: * interface.
257: *
258: * <p>Note that for the JE product, QUEUE and RECNO databases are not
259: * supported, and therefore a PrimaryKeyAssigner must be associated with
260: * the map in order to call this method.</p>
261: *
262: * @param value the value to be appended.
263: *
264: * @return the assigned key.
265: *
266: * @throws UnsupportedOperationException if the collection is indexed, or
267: * if the collection is read-only, or if the Store has no {@link
268: * PrimaryKeyAssigner} and is not a QUEUE or RECNO database.
269: *
270: * @throws RuntimeExceptionWrapper if a {@link
271: * com.sleepycat.je.DatabaseException} is thrown.
272: */
273: public Object append(Object value) {
274:
275: boolean doAutoCommit = beginAutoCommit();
276: try {
277: Object[] key = new Object[1];
278: view.append(value, key, null);
279: commitAutoCommit(doAutoCommit);
280: return key[0];
281: } catch (Exception e) {
282: throw handleException(e, doAutoCommit);
283: }
284: }
285:
286: /**
287: * Removes the mapping for this key from this map if present (optional
288: * operation). If duplicates are allowed, this method removes all
289: * duplicates for the given key. This method conforms to the {@link
290: * Map#remove} interface.
291: *
292: * @throws UnsupportedOperationException if the collection is read-only.
293: *
294: * @throws RuntimeExceptionWrapper if a {@link
295: * com.sleepycat.je.DatabaseException} is thrown.
296: */
297: public Object remove(Object key) {
298:
299: Object[] oldVal = new Object[1];
300: removeKey(key, oldVal);
301: return oldVal[0];
302: }
303:
304: /**
305: * Returns true if this map contains the specified key. This method
306: * conforms to the {@link Map#containsKey} interface.
307: *
308: * @throws RuntimeExceptionWrapper if a {@link
309: * com.sleepycat.je.DatabaseException} is thrown.
310: */
311: public boolean containsKey(Object key) {
312:
313: return super .containsKey(key);
314: }
315:
316: /**
317: * Returns true if this map contains the specified value. When an entity
318: * binding is used, this method returns whether the map contains the
319: * primary key and value mapping of the entity. This method conforms to
320: * the {@link Map#containsValue} interface.
321: *
322: * @throws RuntimeExceptionWrapper if a {@link
323: * com.sleepycat.je.DatabaseException} is thrown.
324: */
325: public boolean containsValue(Object value) {
326:
327: return super .containsValue(value);
328: }
329:
330: /**
331: * Copies all of the mappings from the specified map to this map (optional
332: * operation). When duplicates are allowed, the mappings in the specified
333: * map are effectively appended to the existing mappings in this map, that
334: * is no previously existing mappings in this map are replaced. This
335: * method conforms to the {@link Map#putAll} interface.
336: *
337: * @throws UnsupportedOperationException if the collection is read-only, or
338: * if the collection is indexed.
339: *
340: * @throws RuntimeExceptionWrapper if a {@link
341: * com.sleepycat.je.DatabaseException} is thrown.
342: */
343: public void putAll(Map map) {
344:
345: boolean doAutoCommit = beginAutoCommit();
346: Iterator i = null;
347: try {
348: Collection coll = map.entrySet();
349: i = storedOrExternalIterator(coll);
350: while (i.hasNext()) {
351: Map.Entry entry = (Map.Entry) i.next();
352: put(entry.getKey(), entry.getValue());
353: }
354: StoredIterator.close(i);
355: commitAutoCommit(doAutoCommit);
356: } catch (Exception e) {
357: StoredIterator.close(i);
358: throw handleException(e, doAutoCommit);
359: }
360: }
361:
362: /**
363: * Returns a set view of the keys contained in this map. A {@link
364: * java.util.SortedSet} is returned if the map is ordered. The returned
365: * collection will be read-only if the map is read-only. This method
366: * conforms to the {@link Map#keySet()} interface.
367: *
368: * <p>Note that the return value is a StoredCollection and must be treated
369: * as such; for example, its iterators must be explicitly closed.</p>
370: *
371: * @return a {@link StoredKeySet} or a {@link StoredSortedKeySet} for this
372: * map.
373: *
374: * @throws RuntimeExceptionWrapper if a {@link
375: * com.sleepycat.je.DatabaseException} is thrown.
376: *
377: * @see #isOrdered
378: * @see #isWriteAllowed
379: */
380: public Set keySet() {
381:
382: return keySet;
383: }
384:
385: /**
386: * Returns a set view of the mappings contained in this map. A {@link
387: * java.util.SortedSet} is returned if the map is ordered. The returned
388: * collection will be read-only if the map is read-only. This method
389: * conforms to the {@link Map#entrySet()} interface.
390: *
391: * <p>Note that the return value is a StoredCollection and must be treated
392: * as such; for example, its iterators must be explicitly closed.</p>
393: *
394: * @return a {@link StoredEntrySet} or a {@link StoredSortedEntrySet} for
395: * this map.
396: *
397: * @throws RuntimeExceptionWrapper if a {@link
398: * com.sleepycat.je.DatabaseException} is thrown.
399: *
400: * @see #isOrdered
401: * @see #isWriteAllowed
402: */
403: public Set entrySet() {
404:
405: return entrySet;
406: }
407:
408: /**
409: * Returns a collection view of the values contained in this map. A {@link
410: * java.util.SortedSet} is returned if the map is ordered and the
411: * value/entity binding can be used to derive the map's key from its
412: * value/entity object. The returned collection will be read-only if the
413: * map is read-only. This method conforms to the {@link Map#values()}
414: * interface.
415: *
416: * <p>Note that the return value is a StoredCollection and must be treated
417: * as such; for example, its iterators must be explicitly closed.</p>
418: *
419: * @return a {@link StoredValueSet} or a {@link StoredSortedValueSet} for
420: * this map.
421: *
422: * @throws RuntimeExceptionWrapper if a {@link
423: * com.sleepycat.je.DatabaseException} is thrown.
424: *
425: * @see #isOrdered
426: * @see #isWriteAllowed
427: */
428: public Collection values() {
429:
430: return valueSet;
431: }
432:
433: /**
434: * Returns a new collection containing the values mapped to the given key
435: * in this map. This collection's iterator() method is particularly useful
436: * for iterating over the duplicates for a given key, since this is not
437: * supported by the standard Map interface. This method does not exist in
438: * the standard {@link Map} interface.
439: *
440: * <p>If no mapping for the given key is present, an empty collection is
441: * returned. If duplicates are not allowed, at most a single value will be
442: * in the collection returned. If duplicates are allowed, the returned
443: * collection's add() method may be used to add values for the given
444: * key.</p>
445: *
446: * @param key is the key for which values are to be returned.
447: *
448: * @throws RuntimeExceptionWrapper if a {@link
449: * com.sleepycat.je.DatabaseException} is thrown.
450: */
451: public Collection duplicates(Object key) {
452:
453: try {
454: DataView newView = view.valueSetView(key);
455: return new StoredValueSet(newView);
456: } catch (KeyRangeException e) {
457: return Collections.EMPTY_SET;
458: } catch (Exception e) {
459: throw StoredContainer.convertException(e);
460: }
461: }
462:
463: /**
464: * Returns a new map from primary key to value for the subset of records
465: * having a given secondary key (duplicates). This method does not exist
466: * in the standard {@link Map} interface.
467: *
468: * <p>If no mapping for the given key is present, an empty collection is
469: * returned. If duplicates are not allowed, at most a single value will be
470: * in the collection returned. If duplicates are allowed, the returned
471: * collection's add() method may be used to add values for the given
472: * key.</p>
473: *
474: * @param secondaryKey is the secondary key for which duplicates values
475: * will be represented by the returned map.
476: *
477: * @param primaryKeyBinding is the binding used for keys in the returned
478: * map.
479: *
480: * @throws RuntimeExceptionWrapper if a {@link
481: * com.sleepycat.je.DatabaseException} is thrown.
482: */
483: public Map duplicatesMap(Object secondaryKey,
484: EntryBinding primaryKeyBinding) {
485: try {
486: DataView newView = view.duplicatesView(secondaryKey,
487: primaryKeyBinding);
488: if (isOrdered()) {
489: return new StoredSortedMap(newView);
490: } else {
491: return new StoredMap(newView);
492: }
493: } catch (Exception e) {
494: throw StoredContainer.convertException(e);
495: }
496: }
497:
498: /**
499: * Compares the specified object with this map for equality. A value
500: * comparison is performed by this method and the stored values are
501: * compared rather than calling the equals() method of each element. This
502: * method conforms to the {@link Map#equals} interface.
503: *
504: * @throws RuntimeExceptionWrapper if a {@link
505: * com.sleepycat.je.DatabaseException} is thrown.
506: */
507: public boolean equals(Object other) {
508:
509: if (other instanceof Map) {
510: return entrySet().equals(((Map) other).entrySet());
511: } else {
512: return false;
513: }
514: }
515:
516: /*
517: * Add this in to keep FindBugs from whining at us about implementing
518: * equals(), but not hashCode().
519: */
520: public int hashCode() {
521: return super .hashCode();
522: }
523:
524: // Inherit javadoc
525: public int size() {
526: return values().size();
527: }
528:
529: /**
530: * Converts the map to a string representation for debugging. WARNING: All
531: * mappings will be converted to strings and returned and therefore the
532: * returned string may be very large.
533: *
534: * @return the string representation.
535: *
536: * @throws RuntimeExceptionWrapper if a {@link
537: * com.sleepycat.je.DatabaseException} is thrown.
538: */
539: public String toString() {
540:
541: return entrySet().toString();
542: }
543: }
|