/** * This file is part of ObjectFabric (http://objectfabric.org). * * ObjectFabric is licensed under the Apache License, Version 2.0, the terms * of which may be found at http://www.apache.org/licenses/LICENSE-2.0.html. * * Copyright ObjectFabric Inc. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ package org.objectfabric; import java.util.Collection; import java.util.Iterator; import java.util.Map; import java.util.Set; /** * Transactional map. For each thread this class behaves like an HashMap, except: <br> * <br> * - It does not support null keys. <br> * - It does not implement clone. <br> * - Entries do not support setValue(V). <br> * <br> * See comments on {@link TSet} about iterators and concurrency, methods that both read * and write, and exceptions. */ @SuppressWarnings({ "rawtypes", "unchecked" }) public class TMap<K, V> extends TKeyed<K> implements Map<K, V> { public static final TType TYPE; static { TYPE = Platform.newTType(Platform.get().defaultObjectModel(), BuiltInClass.TMAP_CLASS_ID); } private final TType[] _genericParameters; public TMap(Resource resource) { this(resource, null, null); } /** * This constructor is only useful if the object might get replicated to a .NET * process, to specify which type would be instantiated by the remote runtime. */ public TMap(Resource resource, TType genericParamKey, TType genericParamValue) { super(resource, new TKeyedSharedVersion()); if (genericParamKey == null || genericParamValue == null) _genericParameters = null; else { _genericParameters = Platform.newTTypeArray(2); _genericParameters[0] = genericParamKey; _genericParameters[1] = genericParamValue; } } @Override final TType[] genericParameters() { return _genericParameters; } @Override public void clear() { clearTKeyed(); } @Override public boolean containsKey(Object key) { if (key == null) throw new NullPointerException(Strings.ARGUMENT_NULL); int hash = hash(key); Transaction outer = current_(); Transaction inner = startRead_(outer); TKeyedEntry entry; try { entry = getEntry(inner, (K) key, hash); } finally { endRead_(outer, inner); } return entry != null && !entry.isRemoval(); } @Override public boolean containsValue(Object value) { Transaction outer = current_(); Transaction inner = startRead_(outer); KeyedIterator iterator = new KeyedIterator(inner); boolean result = false; try { while (iterator.hasNext()) { Object object = iterator.nextEntry().getValue(); if (value == object) { result = true; break; } if (value != null && value.equals(object)) { result = true; break; } } } finally { endRead_(outer, inner); } return result; } @Override public boolean equals(Object o) { if (o == this) return true; if (!(o instanceof Map)) return false; Map<K, V> map = (Map<K, V>) o; int size = 0; Transaction outer = current_(); Transaction inner = startRead_(outer); KeyedIterator iterator = new KeyedIterator(inner); try { while (iterator.hasNext()) { Entry<K, V> entry = iterator.nextEntry(); K key = entry.getKey(); V value = entry.getValue(); if (value == null) { if (!(map.get(key) == null && map.containsKey(key))) return false; } else { if (!value.equals(map.get(key))) return false; } size++; } } finally { endRead_(outer, inner); } if (map.size() != size) return false; return true; } @Override public V get(Object key) { if (key == null) throw new NullPointerException(Strings.ARGUMENT_NULL); int hash = hash(key); Transaction outer = current_(); Transaction inner = startRead_(outer); TKeyedEntry entry; try { entry = getEntry(inner, (K) key, hash); } finally { endRead_(outer, inner); } return entry != null && !entry.isRemoval() ? (V) entry.getValue() : null; } @Override public int hashCode() { Transaction outer = current_(); Transaction inner = startRead_(outer); KeyedIterator iterator = new KeyedIterator(inner); int h = 0; try { while (iterator.hasNext()) { Entry<K, V> entry = iterator.nextEntry(); h += entry.hashCode(); } } finally { endRead_(outer, inner); } return h; } @Override public boolean isEmpty() { return size() == 0; } @Override public V put(K key, V value) { if (key == null) throw new NullPointerException(Strings.ARGUMENT_NULL); TKeyedEntry entry = new TKeyedEntry(key, hash(key), value); TKeyedEntry<K, V> previous = putEntry(entry, true); return previous != null && !previous.isRemoval() ? previous.getValue() : null; } /** * Does not return a value to avoid a potentially conflicting read. This might improve * performance in the context of a transaction. */ public void putOnly(K key, V value) { if (key == null) throw new NullPointerException(Strings.ARGUMENT_NULL); TKeyedEntry entry = new TKeyedEntry<K, V>(key, hash(key), value); putEntry(entry, false); } @Override public void putAll(Map<? extends K, ? extends V> t) { Transaction outer = current_(); Transaction inner = startWrite_(outer); boolean ok = false; try { for (Map.Entry<? extends K, ? extends V> entry : t.entrySet()) { if (entry.getKey() == null) throw new NullPointerException(Strings.ARGUMENT_NULL); K key = entry.getKey(); putEntry(inner, new TKeyedEntry(key, hash(key), entry.getValue()), false); } ok = true; } finally { endWrite_(outer, inner, ok); } } @Override public V remove(Object key) { if (key == null) throw new NullPointerException(Strings.ARGUMENT_NULL); TKeyedEntry entry = new TKeyedEntry(key, hash(key), TKeyedEntry.REMOVAL); TKeyedEntry<K, V> previous = putEntry(entry, true); return previous != null && !previous.isRemoval() ? previous.getValue() : null; } /** * Does not return a value to avoid a potentially conflicting read. This might improve * performance in the context of a transaction. */ public void removeOnly(Object key) { if (key == null) throw new NullPointerException(Strings.ARGUMENT_NULL); TKeyedEntry entry = new TKeyedEntry(key, hash(key), TKeyedEntry.REMOVAL); putEntry(entry, false); } // @Override public int size() { return sizeTKeyed(); } // Views @Override public Set<K> keySet() { return new KeySet(); } private final class KeySet implements Set<K> { @Override public boolean add(K e) { ExpectedExceptionThrower.throwUnsupportedOperationException(); return false; } @Override public boolean addAll(Collection<? extends K> c) { ExpectedExceptionThrower.throwUnsupportedOperationException(); return false; } @Override public boolean containsAll(Collection<?> c) { Transaction outer = current_(); Transaction inner = TMap.this.startRead_(outer); boolean result = true; try { for (Object element : c) { if (element == null) { result = false; break; } TKeyedEntry entry = getEntry(inner, (K) element, hash(element)); if (entry == null || entry.isRemoval()) { result = false; break; } } } finally { endRead_(outer, inner); } return result; } @Override public void clear() { TMap.this.clear(); } @Override public boolean contains(Object o) { return TMap.this.containsKey(o); } @Override public boolean isEmpty() { return TMap.this.isEmpty(); } @Override public Iterator<K> iterator() { Transaction transaction = current_(); return new KeyIterator(transaction); } @Override public boolean remove(Object o) { if (o == null) throw new NullPointerException(Strings.ARGUMENT_NULL); TKeyedEntry entry = new TKeyedEntry(o, hash(o), TKeyedEntry.REMOVAL); TKeyedEntry<K, V> previous = TMap.this.putEntry(entry, true); return previous != null && !previous.isRemoval(); } @Override public boolean removeAll(Collection<?> c) { Transaction outer = current_(); Transaction inner = TMap.this.startWrite_(outer); boolean modified = false, ok = false; KeyIterator it = new KeyIterator(inner); try { while (it.hasNext()) { if (c.contains(it.next())) { it.remove(); modified = true; } } ok = true; } finally { endWrite_(outer, inner, ok); } return modified; } @Override public boolean retainAll(Collection<?> c) { Transaction outer = current_(); Transaction inner = TMap.this.startWrite_(outer); boolean modified = false, ok = false; KeyIterator it = new KeyIterator(inner); try { while (it.hasNext()) { if (!c.contains(it.next())) { it.remove(); modified = true; } } ok = true; } finally { endWrite_(outer, inner, ok); } return modified; } @Override public int size() { return TMap.this.size(); } @Override public Object[] toArray() { org.objectfabric.List<Object> list = new org.objectfabric.List<Object>(); Transaction outer = current_(); Transaction inner = TMap.this.startRead_(outer); for (K k : this) list.add(k); endRead_(outer, inner); Object[] array = new Object[list.size()]; list.copyToFixed(array); return array; } @Override public <T> T[] toArray(T[] array) { if (array == null) ExpectedExceptionThrower.throwNullPointerException(); org.objectfabric.List<T> list = new org.objectfabric.List<T>(); Transaction outer = current_(); Transaction inner = TMap.this.startRead_(outer); for (K k : this) list.add((T) k); endRead_(outer, inner); return list.copyToWithResizeAndNullEnd(array); } } private final class KeyIterator extends KeyedIterator implements Iterator<K> { public KeyIterator(Transaction transaction) { super(transaction); } @Override public K next() { return (K) nextEntry().getKey(); } @Override public void remove() { TKeyedEntry current = getCurrent(); TKeyedEntry entry = new TKeyedEntry(current.getKey(), current.getHash(), TKeyedEntry.REMOVAL); TMap.this.putEntry(entry, false); } } @Override public Collection<V> values() { return new Values(); } private final class Values implements Collection<V> { @Override public boolean add(V e) { ExpectedExceptionThrower.throwUnsupportedOperationException(); return false; } @Override public boolean addAll(Collection<? extends V> c) { ExpectedExceptionThrower.throwUnsupportedOperationException(); return false; } @Override public void clear() { TMap.this.clear(); } @Override public boolean contains(Object o) { return TMap.this.containsValue(o); } @Override public boolean containsAll(Collection<?> c) { Transaction outer = current_(); Transaction inner = TMap.this.startRead_(outer); boolean result = true; try { for (Object element : c) { if (!TMap.this.containsValue(element)) { result = false; break; } } } finally { endRead_(outer, inner); } return result; } @Override public Iterator<V> iterator() { Transaction transaction = current_(); return new ValueIterator(transaction); } @Override public boolean isEmpty() { return TMap.this.isEmpty(); } @Override public boolean remove(Object o) { Transaction outer = current_(); Transaction inner = TMap.this.startWrite_(outer); boolean ok = false, result = false; try { Iterator it = new ValueIterator(inner); if (o == null) { while (it.hasNext()) { if (it.next() == null) { it.remove(); result = true; break; } } } else { while (it.hasNext()) { if (o.equals(it.next())) { it.remove(); result = true; break; } } } ok = true; } finally { endWrite_(outer, inner, ok); } return result; } @Override public boolean removeAll(Collection<?> c) { Transaction outer = current_(); Transaction inner = TMap.this.startWrite_(outer); boolean modified = false, ok = false; ValueIterator it = new ValueIterator(inner); try { while (it.hasNext()) { if (c.contains(it.next())) { it.remove(); modified = true; } } ok = true; } finally { endWrite_(outer, inner, ok); } return modified; } @Override public boolean retainAll(Collection<?> c) { Transaction outer = current_(); Transaction inner = TMap.this.startWrite_(outer); boolean modified = false, ok = false; ValueIterator it = new ValueIterator(inner); try { while (it.hasNext()) { if (!c.contains(it.next())) { it.remove(); modified = true; } } ok = true; } finally { endWrite_(outer, inner, ok); } return modified; } @Override public int size() { return TMap.this.size(); } @Override public Object[] toArray() { org.objectfabric.List<Object> list = new org.objectfabric.List<Object>(); Transaction outer = current_(); Transaction inner = TMap.this.startRead_(outer); for (V e : this) list.add(e); endRead_(outer, inner); Object[] array = new Object[list.size()]; list.copyToFixed(array); return array; } @Override public <T> T[] toArray(T[] array) { if (array == null) ExpectedExceptionThrower.throwNullPointerException(); org.objectfabric.List<T> list = new org.objectfabric.List<T>(); Transaction outer = current_(); Transaction inner = TMap.this.startRead_(outer); for (V e : this) list.add((T) e); endRead_(outer, inner); return list.copyToWithResizeAndNullEnd(array); } } private final class ValueIterator extends KeyedIterator implements Iterator<V> { public ValueIterator(Transaction transaction) { super(transaction); } @Override public V next() { return (V) nextEntry().getValue(); } @Override public void remove() { TKeyedEntry current = getCurrent(); TKeyedEntry entry = new TKeyedEntry(current.getKey(), current.getHash(), TKeyedEntry.REMOVAL); TMap.this.putEntry(entry, false); } } @Override public Set<Entry<K, V>> entrySet() { return new EntrySet(); } private final class EntrySet implements Set { @Override public boolean add(Object o) { ExpectedExceptionThrower.throwUnsupportedOperationException(); return false; } @Override public boolean addAll(Collection c) { ExpectedExceptionThrower.throwUnsupportedOperationException(); return false; } @Override public void clear() { TMap.this.clear(); } @Override public boolean contains(Object o) { if (!(o instanceof Entry)) return false; Entry<K, V> given = (Entry<K, V>) o; K key = given.getKey(); int hash = hash(key); Transaction outer = current_(); Transaction inner = TMap.this.startRead_(outer); TKeyedEntry stored; try { stored = getEntry(inner, key, hash); } finally { endRead_(outer, inner); } return stored != null && stored.equals(given); } @Override public boolean containsAll(Collection c) { Transaction outer = current_(); Transaction inner = TMap.this.startRead_(outer); boolean result = true; try { for (Object element : c) { if (!contains(element)) { result = false; break; } } } finally { endRead_(outer, inner); } return result; } @Override public boolean isEmpty() { return TMap.this.isEmpty(); } @Override public Iterator iterator() { Transaction transaction = current_(); return new EntryIterator(transaction); } @Override public boolean remove(Object o) { if (o == null) throw new NullPointerException(Strings.ARGUMENT_NULL); if (!(o instanceof Entry)) return false; Entry<K, V> given = (Entry<K, V>) o; K key = given.getKey(); int hash = hash(key); Transaction outer = current_(); Transaction inner = TMap.this.startWrite_(outer); boolean modified = false, ok = false; try { TKeyedEntry stored = getEntry(inner, key, hash); if (stored != null && stored.equals(given)) { TKeyedEntry entry = new TKeyedEntry(key, hash, TKeyedEntry.REMOVAL); putEntry(inner, entry, true); modified = true; } ok = true; } finally { endWrite_(outer, inner, ok); } return modified; } @Override public boolean removeAll(Collection c) { if (c == null) throw new NullPointerException(); Transaction outer = current_(); Transaction inner = TMap.this.startWrite_(outer); boolean modified = false, ok = false; try { EntryIterator it = new EntryIterator(inner); while (it.hasNext()) { if (c.contains(it.next())) { it.remove(); modified = true; } } ok = true; } finally { endWrite_(outer, inner, ok); } return modified; } @Override public boolean retainAll(Collection c) { if (c == null) throw new NullPointerException(); Transaction outer = current_(); Transaction inner = TMap.this.startWrite_(outer); boolean modified = false, ok = false; try { EntryIterator it = new EntryIterator(inner); while (it.hasNext()) { if (!c.contains(it.next())) { it.remove(); modified = true; } } ok = true; } finally { endWrite_(outer, inner, ok); } return modified; } @Override public int size() { return TMap.this.size(); } @Override public Object[] toArray() { org.objectfabric.List<Object> list = new org.objectfabric.List<Object>(); Transaction outer = current_(); Transaction inner = TMap.this.startRead_(outer); for (Object o : this) list.add(o); endRead_(outer, inner); Object[] array = new Object[list.size()]; list.copyToFixed(array); return array; } @Override public Object[] toArray(Object[] array) { if (array == null) ExpectedExceptionThrower.throwNullPointerException(); org.objectfabric.List<Object> list = new org.objectfabric.List<Object>(); Transaction outer = current_(); Transaction inner = TMap.this.startRead_(outer); for (Object o : this) list.add(o); endRead_(outer, inner); return list.copyToWithResizeAndNullEnd(array); } } private final class EntryIterator extends KeyedIterator implements Iterator<Entry<K, V>> { public EntryIterator(Transaction transaction) { super(transaction); } @Override public Entry<K, V> next() { return nextEntry(); } @Override public void remove() { TKeyedEntry current = getCurrent(); TKeyedEntry entry = new TKeyedEntry(current.getKey(), current.getHash(), TKeyedEntry.REMOVAL); TMap.this.putEntry(entry, false); } } // @Override protected final int classId_() { return BuiltInClass.TMAP_CLASS_ID; } }