All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.openjpa.util.AbstractLRSProxyMap Maven / Gradle / Ivy

There is a newer version: 4.0.1
Show newest version
/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.    
 */
package org.apache.openjpa.util;

import java.io.ObjectStreamException;
import java.util.AbstractCollection;
import java.util.AbstractSet;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;

import org.apache.commons.collections.Predicate;
import org.apache.commons.collections.iterators.FilterIterator;
import org.apache.commons.collections.iterators.IteratorChain;
import org.apache.openjpa.kernel.OpenJPAStateManager;
import org.apache.openjpa.lib.util.Closeable;
import org.apache.openjpa.lib.util.Localizer;

/**
 * A map proxy designed for maps backed by extremely large result sets in
 * which each call to {@link #get} or {@link #containsKey} may perform a
 * database query. Changes to the map are tracked through a
 * {@link ChangeTracker}. This map has the following limitations:
 * 
    *
  • The size method may return {@link Integer#MAX_VALUE}.
  • *
  • Null keys and values are not supported.
  • *
* * @author Abe White */ public abstract class AbstractLRSProxyMap implements Map, LRSProxy, MapChangeTracker, Predicate { private static final int MODE_KEY = 0; private static final int MODE_VALUE = 1; private static final int MODE_ENTRY = 2; private static final Localizer _loc = Localizer.forPackage (AbstractLRSProxyMap.class); private Class _keyType = null; private Class _valueType = null; private MapChangeTrackerImpl _ct = null; private OpenJPAStateManager _sm = null; private int _field = -1; private OpenJPAStateManager _origOwner = null; private int _origField = -1; private Map _map = null; private int _count = -1; private boolean _iterated = false; public AbstractLRSProxyMap(Class keyType, Class valueType) { _keyType = keyType; _valueType = valueType; _ct = new MapChangeTrackerImpl(this,false); _ct.setAutoOff(false); } public void setOwner(OpenJPAStateManager sm, int field) { // can't transfer ownership of an lrs proxy if (sm != null && _origOwner != null && (_origOwner != sm || _origField != field)) { throw new InvalidStateException(_loc.get("transfer-lrs", _origOwner.getMetaData().getField(_origField))); } _sm = sm; _field = field; // keep track of original owner so we can detect transfer attempts if (sm != null) { _origOwner = sm; _origField = field; } } public OpenJPAStateManager getOwner() { return _sm; } public int getOwnerField() { return _field; } public ChangeTracker getChangeTracker() { return this; } public Object copy(Object orig) { // used to store fields for rollbac; we don't store lrs fields return null; } /** * used in testing; we need to be able to make sure that OpenJPA does not * iterate lrs fields during standard crud operations */ boolean isIterated() { return _iterated; } /** * used in testing; we need to be able to make sure that OpenJPA does not * iterate lrs fields during standard crud operations */ void setIterated(boolean it) { _iterated = it; } public int size() { if (_count == -1) _count = count(); if (_count == Integer.MAX_VALUE) return _count; return _count + _ct.getAdded().size() - _ct.getRemoved().size(); } public boolean isEmpty() { return size() == 0; } public boolean containsKey(Object key) { if (_keyType != null && !_keyType.isInstance(key)) return false; if (_map != null && _map.containsKey(key)) return true; if (_ct.getTrackKeys()) { if (_ct.getRemoved().contains(key)) return false; return hasKey(key); } // value tracking: // if we've removed values, we need to see if this key represents // a removed instance. otherwise we can rely on the 1-1 between // keys and values when using value tracking if (_ct.getRemoved().isEmpty()) return hasKey(key); return get(key) != null; } public boolean containsValue(Object val) { if (_valueType != null && !_valueType.isInstance(val)) return false; if (_map != null && _map.containsValue(val)) return true; if (!_ct.getTrackKeys()) { if (_ct.getRemoved().contains(val)) return false; return hasValue(val); } // key tracking Collection keys = keys(val); if (keys == null || keys.isEmpty()) return false; keys.removeAll(_ct.getRemoved()); keys.removeAll(_ct.getChanged()); return keys.size() > 0; } public V get(Object key) { if (_keyType != null && !_keyType.isInstance(key)) return null; V ret = (_map == null) ? null : _map.get(key); if (ret != null) return ret; if (_ct.getTrackKeys() && _ct.getRemoved().contains(key)) return null; V val = value(key); if (!_ct.getTrackKeys() && _ct.getRemoved().contains(val)) return null; return val; } public V put(K key, V value) { Proxies.assertAllowedType(key, _keyType); Proxies.assertAllowedType(value, _valueType); Proxies.dirty(this, false); if (_map == null) _map = new HashMap(); V old = _map.put(key, value); if (old == null && (!_ct.getTrackKeys() || !_ct.getRemoved().contains(key))) old = value(key); if (old != null) { _ct.changed(key, old, value); Proxies.removed(this, old, false); } else _ct.added(key, value); return old; } public void putAll(Map m) { for (Map.Entry entry : m.entrySet()) { put(entry.getKey(), entry.getValue()); } } public V remove(Object key) { Proxies.dirty(this, false); V old = (_map == null) ? null : _map.remove(key); if (old == null && (!_ct.getTrackKeys() || !_ct.getRemoved().contains(key))) old = value(key); if (old != null) { _ct.removed(key, old); Proxies.removed(this, key, true); Proxies.removed(this, old, false); } return old; } public void clear() { Proxies.dirty(this, false); Itr itr = iterator(MODE_ENTRY); try { Map.Entry entry; while (itr.hasNext()) { entry = (Map.Entry) itr.next(); Proxies.removed(this, entry.getKey(), true); Proxies.removed(this, entry.getValue(), false); _ct.removed(entry.getKey(), entry.getValue()); } } finally { itr.close(); } } public Set keySet() { return new AbstractSet() { public int size() { return AbstractLRSProxyMap.this.size(); } public boolean remove(Object o) { return AbstractLRSProxyMap.this.remove(o) != null; } public Iterator iterator() { return AbstractLRSProxyMap.this.iterator(MODE_KEY); } }; } public Collection values() { return new AbstractCollection() { public int size() { return AbstractLRSProxyMap.this.size(); } public Iterator iterator() { return AbstractLRSProxyMap.this.iterator(MODE_VALUE); } }; } public Set> entrySet() { return new AbstractSet>() { public int size() { return AbstractLRSProxyMap.this.size(); } public Iterator> iterator() { return AbstractLRSProxyMap.this.iterator(MODE_ENTRY); } }; } protected Object writeReplace() throws ObjectStreamException { Itr itr = iterator(MODE_ENTRY); try { Map map = new HashMap(); Map.Entry entry; while (itr.hasNext()) { entry = (Map.Entry) itr.next(); map.put(entry.getKey(), entry.getValue()); } return map; } finally { itr.close(); } } /** * Return whether the given key exists in the map. */ protected abstract boolean hasKey(Object key); /** * Return whether the given value exists in the map. */ protected abstract boolean hasValue(Object value); /** * Return all keys for the given value. */ protected abstract Collection keys(Object value); /** * Return the value of the given key. */ protected abstract V value(Object key); /** * Implement this method to return an iterator over the entries * in the map. Each returned object must implement the * Map.Entry interface. This method may be invoked multiple * times. The iterator does not have to support the * {@link Iterator#remove} method, and may implement * {@link org.apache.openjpa.lib.util.Closeable}. */ protected abstract Iterator itr(); /** * Return the number of entries in the map, or {@link Integer#MAX_VALUE}. */ protected abstract int count(); private Itr iterator(int mode) { _iterated = true; // have to copy the entry set of _map to prevent concurrent mod errors IteratorChain chain = new IteratorChain(); if (_map != null) chain.addIterator(new ArrayList(_map.entrySet()).iterator()); chain.addIterator(new FilterIterator(itr(), this)); return new Itr(mode, chain); } //////////////////////////// // Predicate Implementation //////////////////////////// public boolean evaluate(Object obj) { Map.Entry entry = (Map.Entry) obj; return (_ct.getTrackKeys() && !_ct.getRemoved().contains(entry.getKey()) || (!_ct.getTrackKeys() && !_ct.getRemoved().contains(entry.getValue()))) && (_map == null || !_map.containsKey(entry.getKey())); } /////////////////////////////////// // MapChangeTracker Implementation /////////////////////////////////// public boolean isTracking() { return _ct.isTracking(); } public void startTracking() { _ct.startTracking(); reset(); } public void stopTracking() { _ct.stopTracking(); reset(); } private void reset() { if (_map != null) _map.clear(); if (_count != Integer.MAX_VALUE) _count = -1; } public boolean getTrackKeys() { return _ct.getTrackKeys(); } public void setTrackKeys(boolean keys) { _ct.setTrackKeys(keys); } public Collection getAdded() { return _ct.getAdded(); } public Collection getRemoved() { return _ct.getRemoved(); } public Collection getChanged() { return _ct.getChanged(); } public void added(Object key, Object val) { _ct.added(key, val); } public void removed(Object key, Object val) { _ct.removed(key, val); } public void changed(Object key, Object orig, Object val) { _ct.changed(key, orig, val); } public int getNextSequence() { return _ct.getNextSequence(); } public void setNextSequence(int seq) { _ct.setNextSequence(seq); } /** * Wrapper around our filtering iterator chain. */ private class Itr implements Iterator, Closeable { private static final int OPEN = 0; private static final int LAST_ELEM = 1; private static final int CLOSED = 2; private final int _mode; private final IteratorChain _itr; private Map.Entry _last = null; private int _state = OPEN; public Itr(int mode, IteratorChain itr) { _mode = mode; _itr = itr; } public boolean hasNext() { if (_state != OPEN) return false; // close automatically if no more elements if (!_itr.hasNext()) { free(); _state = LAST_ELEM; return false; } return true; } public Object next() { if (_state != OPEN) throw new NoSuchElementException(); _last = (Map.Entry) _itr.next(); switch (_mode) { case MODE_KEY: return _last.getKey(); case MODE_VALUE: return _last.getValue(); default: return _last; } } public void remove() { if (_state == CLOSED || _last == null) throw new NoSuchElementException(); Proxies.dirty(AbstractLRSProxyMap.this, false); Proxies.removed(AbstractLRSProxyMap.this, _last.getKey(), true); Proxies.removed(AbstractLRSProxyMap.this, _last.getValue(), false); // need to get a reference to the key before we remove it // from the map, since in JDK 1.3-, the act of removing an entry // from the map will also null the entry's value, which would // result in incorrectly passing a null to the change tracker Object key = _last.getKey(); Object value = _last.getValue(); if (_map != null) _map.remove(key); _ct.removed(key, value); _last = null; } public void close() { free(); _state = CLOSED; } private void free() { if (_state != OPEN) return; List itrs = _itr.getIterators(); Iterator itr; for (int i = 0; i < itrs.size(); i++) { itr = (Iterator) itrs.get(i); if (itr instanceof FilterIterator) itr = ((FilterIterator) itr).getIterator(); ImplHelper.close(itr); } } protected void finalize() { close (); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy