001: //$Id: PersistentMap.java 11256 2007-03-07 19:32:58Z steve.ebersole@jboss.com $
002: package org.hibernate.collection;
003:
004: import java.io.Serializable;
005: import java.sql.ResultSet;
006: import java.sql.SQLException;
007: import java.util.ArrayList;
008: import java.util.Collection;
009: import java.util.HashMap;
010: import java.util.Iterator;
011: import java.util.List;
012: import java.util.Map;
013: import java.util.Set;
014:
015: import org.hibernate.EntityMode;
016: import org.hibernate.HibernateException;
017: import org.hibernate.engine.SessionImplementor;
018: import org.hibernate.loader.CollectionAliases;
019: import org.hibernate.persister.collection.CollectionPersister;
020: import org.hibernate.type.Type;
021:
022: /**
023: * A persistent wrapper for a <tt>java.util.Map</tt>. Underlying collection
024: * is a <tt>HashMap</tt>.
025: *
026: * @see java.util.HashMap
027: * @author Gavin King
028: */
029: public class PersistentMap extends AbstractPersistentCollection
030: implements Map {
031:
032: protected Map map;
033:
034: /**
035: * Empty constructor.
036: * <p/>
037: * Note: this form is not ever ever ever used by Hibernate; it is, however,
038: * needed for SOAP libraries and other such marshalling code.
039: */
040: public PersistentMap() {
041: // intentionally empty
042: }
043:
044: /**
045: * Instantiates a lazy map (the underlying map is un-initialized).
046: *
047: * @param session The session to which this map will belong.
048: */
049: public PersistentMap(SessionImplementor session) {
050: super (session);
051: }
052:
053: /**
054: * Instantiates a non-lazy map (the underlying map is constructed
055: * from the incoming map reference).
056: *
057: * @param session The session to which this map will belong.
058: * @param map The underlying map data.
059: */
060: public PersistentMap(SessionImplementor session, Map map) {
061: super (session);
062: this .map = map;
063: setInitialized();
064: setDirectlyAccessible(true);
065: }
066:
067: public Serializable getSnapshot(CollectionPersister persister)
068: throws HibernateException {
069: EntityMode entityMode = getSession().getEntityMode();
070: HashMap clonedMap = new HashMap(map.size());
071: Iterator iter = map.entrySet().iterator();
072: while (iter.hasNext()) {
073: Map.Entry e = (Map.Entry) iter.next();
074: final Object copy = persister.getElementType().deepCopy(
075: e.getValue(), entityMode, persister.getFactory());
076: clonedMap.put(e.getKey(), copy);
077: }
078: return clonedMap;
079: }
080:
081: public Collection getOrphans(Serializable snapshot,
082: String entityName) throws HibernateException {
083: Map sn = (Map) snapshot;
084: return getOrphans(sn.values(), map.values(), entityName,
085: getSession());
086: }
087:
088: public boolean equalsSnapshot(CollectionPersister persister)
089: throws HibernateException {
090: Type elementType = persister.getElementType();
091: Map xmap = (Map) getSnapshot();
092: if (xmap.size() != this .map.size())
093: return false;
094: Iterator iter = map.entrySet().iterator();
095: while (iter.hasNext()) {
096: Map.Entry entry = (Map.Entry) iter.next();
097: if (elementType.isDirty(entry.getValue(), xmap.get(entry
098: .getKey()), getSession()))
099: return false;
100: }
101: return true;
102: }
103:
104: public boolean isSnapshotEmpty(Serializable snapshot) {
105: return ((Map) snapshot).isEmpty();
106: }
107:
108: public boolean isWrapper(Object collection) {
109: return map == collection;
110: }
111:
112: public void beforeInitialize(CollectionPersister persister,
113: int anticipatedSize) {
114: this .map = (Map) persister.getCollectionType().instantiate(
115: anticipatedSize);
116: }
117:
118: /**
119: * @see java.util.Map#size()
120: */
121: public int size() {
122: return readSize() ? getCachedSize() : map.size();
123: }
124:
125: /**
126: * @see java.util.Map#isEmpty()
127: */
128: public boolean isEmpty() {
129: return readSize() ? getCachedSize() == 0 : map.isEmpty();
130: }
131:
132: /**
133: * @see java.util.Map#containsKey(Object)
134: */
135: public boolean containsKey(Object key) {
136: Boolean exists = readIndexExistence(key);
137: return exists == null ? map.containsKey(key) : exists
138: .booleanValue();
139: }
140:
141: /**
142: * @see java.util.Map#containsValue(Object)
143: */
144: public boolean containsValue(Object value) {
145: Boolean exists = readElementExistence(value);
146: return exists == null ? map.containsValue(value) : exists
147: .booleanValue();
148: }
149:
150: /**
151: * @see java.util.Map#get(Object)
152: */
153: public Object get(Object key) {
154: Object result = readElementByIndex(key);
155: return result == UNKNOWN ? map.get(key) : result;
156: }
157:
158: /**
159: * @see java.util.Map#put(Object, Object)
160: */
161: public Object put(Object key, Object value) {
162: if (isPutQueueEnabled()) {
163: Object old = readElementByIndex(key);
164: if (old != UNKNOWN) {
165: queueOperation(new Put(key, value, old));
166: return old;
167: }
168: }
169: initialize(true);
170: Object old = map.put(key, value);
171: // would be better to use the element-type to determine
172: // whether the old and the new are equal here; the problem being
173: // we do not necessarily have access to the element type in all
174: // cases
175: if (value != old) {
176: dirty();
177: }
178: return old;
179: }
180:
181: /**
182: * @see java.util.Map#remove(Object)
183: */
184: public Object remove(Object key) {
185: if (isPutQueueEnabled()) {
186: Object old = readElementByIndex(key);
187: queueOperation(new Remove(key, old));
188: return old;
189: } else {
190: // TODO : safe to interpret "map.remove(key) == null" as non-dirty?
191: initialize(true);
192: if (map.containsKey(key)) {
193: dirty();
194: }
195: return map.remove(key);
196: }
197: }
198:
199: /**
200: * @see java.util.Map#putAll(java.util.Map puts)
201: */
202: public void putAll(Map puts) {
203: if (puts.size() > 0) {
204: initialize(true);
205: Iterator itr = puts.entrySet().iterator();
206: while (itr.hasNext()) {
207: Map.Entry entry = (Entry) itr.next();
208: put(entry.getKey(), entry.getValue());
209: }
210: }
211: }
212:
213: /**
214: * @see java.util.Map#clear()
215: */
216: public void clear() {
217: if (isClearQueueEnabled()) {
218: queueOperation(new Clear());
219: } else {
220: initialize(true);
221: if (!map.isEmpty()) {
222: dirty();
223: map.clear();
224: }
225: }
226: }
227:
228: /**
229: * @see java.util.Map#keySet()
230: */
231: public Set keySet() {
232: read();
233: return new SetProxy(map.keySet());
234: }
235:
236: /**
237: * @see java.util.Map#values()
238: */
239: public Collection values() {
240: read();
241: return new SetProxy(map.values());
242: }
243:
244: /**
245: * @see java.util.Map#entrySet()
246: */
247: public Set entrySet() {
248: read();
249: return new EntrySetProxy(map.entrySet());
250: }
251:
252: public boolean empty() {
253: return map.isEmpty();
254: }
255:
256: public String toString() {
257: read();
258: return map.toString();
259: }
260:
261: public Object readFrom(ResultSet rs, CollectionPersister persister,
262: CollectionAliases descriptor, Object owner)
263: throws HibernateException, SQLException {
264: Object element = persister.readElement(rs, owner, descriptor
265: .getSuffixedElementAliases(), getSession());
266: Object index = persister.readIndex(rs, descriptor
267: .getSuffixedIndexAliases(), getSession());
268: if (element != null)
269: map.put(index, element);
270: return element;
271: }
272:
273: public Iterator entries(CollectionPersister persister) {
274: return map.entrySet().iterator();
275: }
276:
277: /** a wrapper for Map.Entry sets */
278: class EntrySetProxy implements Set {
279: private final Set set;
280:
281: EntrySetProxy(Set set) {
282: this .set = set;
283: }
284:
285: public boolean add(Object entry) {
286: //write(); -- doesn't
287: return set.add(entry);
288: }
289:
290: public boolean addAll(Collection entries) {
291: //write(); -- doesn't
292: return set.addAll(entries);
293: }
294:
295: public void clear() {
296: write();
297: set.clear();
298: }
299:
300: public boolean contains(Object entry) {
301: return set.contains(entry);
302: }
303:
304: public boolean containsAll(Collection entries) {
305: return set.containsAll(entries);
306: }
307:
308: public boolean isEmpty() {
309: return set.isEmpty();
310: }
311:
312: public Iterator iterator() {
313: return new EntryIteratorProxy(set.iterator());
314: }
315:
316: public boolean remove(Object entry) {
317: write();
318: return set.remove(entry);
319: }
320:
321: public boolean removeAll(Collection entries) {
322: write();
323: return set.removeAll(entries);
324: }
325:
326: public boolean retainAll(Collection entries) {
327: write();
328: return set.retainAll(entries);
329: }
330:
331: public int size() {
332: return set.size();
333: }
334:
335: // amazingly, these two will work because AbstractCollection
336: // uses iterator() to fill the array
337: public Object[] toArray() {
338: return set.toArray();
339: }
340:
341: public Object[] toArray(Object[] array) {
342: return set.toArray(array);
343: }
344: }
345:
346: final class EntryIteratorProxy implements Iterator {
347: private final Iterator iter;
348:
349: EntryIteratorProxy(Iterator iter) {
350: this .iter = iter;
351: }
352:
353: public boolean hasNext() {
354: return iter.hasNext();
355: }
356:
357: public Object next() {
358: return new MapEntryProxy((Map.Entry) iter.next());
359: }
360:
361: public void remove() {
362: write();
363: iter.remove();
364: }
365: }
366:
367: final class MapEntryProxy implements Map.Entry {
368: private final Map.Entry me;
369:
370: MapEntryProxy(Map.Entry me) {
371: this .me = me;
372: }
373:
374: public Object getKey() {
375: return me.getKey();
376: }
377:
378: public Object getValue() {
379: return me.getValue();
380: }
381:
382: public boolean equals(Object o) {
383: return me.equals(o);
384: }
385:
386: public int hashCode() {
387: return me.hashCode();
388: }
389:
390: // finally, what it's all about...
391: public Object setValue(Object value) {
392: write();
393: return me.setValue(value);
394: }
395: }
396:
397: public void initializeFromCache(CollectionPersister persister,
398: Serializable disassembled, Object owner)
399: throws HibernateException {
400: Serializable[] array = (Serializable[]) disassembled;
401: int size = array.length;
402: beforeInitialize(persister, size);
403: for (int i = 0; i < size; i += 2) {
404: map.put(persister.getIndexType().assemble(array[i],
405: getSession(), owner), persister.getElementType()
406: .assemble(array[i + 1], getSession(), owner));
407: }
408: }
409:
410: public Serializable disassemble(CollectionPersister persister)
411: throws HibernateException {
412:
413: Serializable[] result = new Serializable[map.size() * 2];
414: Iterator iter = map.entrySet().iterator();
415: int i = 0;
416: while (iter.hasNext()) {
417: Map.Entry e = (Map.Entry) iter.next();
418: result[i++] = persister.getIndexType().disassemble(
419: e.getKey(), getSession(), null);
420: result[i++] = persister.getElementType().disassemble(
421: e.getValue(), getSession(), null);
422: }
423: return result;
424:
425: }
426:
427: public Iterator getDeletes(CollectionPersister persister,
428: boolean indexIsFormula) throws HibernateException {
429: List deletes = new ArrayList();
430: Iterator iter = ((Map) getSnapshot()).entrySet().iterator();
431: while (iter.hasNext()) {
432: Map.Entry e = (Map.Entry) iter.next();
433: Object key = e.getKey();
434: if (e.getValue() != null && map.get(key) == null) {
435: deletes.add(indexIsFormula ? e.getValue() : key);
436: }
437: }
438: return deletes.iterator();
439: }
440:
441: public boolean needsInserting(Object entry, int i, Type elemType)
442: throws HibernateException {
443: final Map sn = (Map) getSnapshot();
444: Map.Entry e = (Map.Entry) entry;
445: return e.getValue() != null && sn.get(e.getKey()) == null;
446: }
447:
448: public boolean needsUpdating(Object entry, int i, Type elemType)
449: throws HibernateException {
450: final Map sn = (Map) getSnapshot();
451: Map.Entry e = (Map.Entry) entry;
452: Object snValue = sn.get(e.getKey());
453: return e.getValue() != null
454: && snValue != null
455: && elemType
456: .isDirty(snValue, e.getValue(), getSession());
457: }
458:
459: public Object getIndex(Object entry, int i,
460: CollectionPersister persister) {
461: return ((Map.Entry) entry).getKey();
462: }
463:
464: public Object getElement(Object entry) {
465: return ((Map.Entry) entry).getValue();
466: }
467:
468: public Object getSnapshotElement(Object entry, int i) {
469: final Map sn = (Map) getSnapshot();
470: return sn.get(((Map.Entry) entry).getKey());
471: }
472:
473: public boolean equals(Object other) {
474: read();
475: return map.equals(other);
476: }
477:
478: public int hashCode() {
479: read();
480: return map.hashCode();
481: }
482:
483: public boolean entryExists(Object entry, int i) {
484: return ((Map.Entry) entry).getValue() != null;
485: }
486:
487: final class Clear implements DelayedOperation {
488: public void operate() {
489: map.clear();
490: }
491:
492: public Object getAddedInstance() {
493: return null;
494: }
495:
496: public Object getOrphan() {
497: throw new UnsupportedOperationException(
498: "queued clear cannot be used with orphan delete");
499: }
500: }
501:
502: final class Put implements DelayedOperation {
503: private Object index;
504: private Object value;
505: private Object old;
506:
507: public Put(Object index, Object value, Object old) {
508: this .index = index;
509: this .value = value;
510: this .old = old;
511: }
512:
513: public void operate() {
514: map.put(index, value);
515: }
516:
517: public Object getAddedInstance() {
518: return value;
519: }
520:
521: public Object getOrphan() {
522: return old;
523: }
524: }
525:
526: final class Remove implements DelayedOperation {
527: private Object index;
528: private Object old;
529:
530: public Remove(Object index, Object old) {
531: this .index = index;
532: this .old = old;
533: }
534:
535: public void operate() {
536: map.remove(index);
537: }
538:
539: public Object getAddedInstance() {
540: return null;
541: }
542:
543: public Object getOrphan() {
544: return old;
545: }
546: }
547: }
|