001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one
003: * or more contributor license agreements. See the NOTICE file
004: * distributed with this work for additional information
005: * regarding copyright ownership. The ASF licenses this file
006: * to you under the Apache License, Version 2.0 (the
007: * "License"); you may not use this file except in compliance
008: * with the License. You may obtain a copy of the License at
009: *
010: * http://www.apache.org/licenses/LICENSE-2.0
011: *
012: * Unless required by applicable law or agreed to in writing,
013: * software distributed under the License is distributed on an
014: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015: * KIND, either express or implied. See the License for the
016: * specific language governing permissions and limitations
017: * under the License.
018: */
019: package org.apache.openjpa.lib.util;
020:
021: import java.io.IOException;
022: import java.io.ObjectInputStream;
023: import java.io.ObjectOutputStream;
024: import java.lang.ref.Reference;
025:
026: /**
027: * Map in which the key, value, or both may be weak/soft references.
028: *
029: * @author Abe White
030: * @nojavadoc
031: * @since 0.4.0
032: */
033: public class ReferenceHashMap extends
034: org.apache.commons.collections.map.ReferenceMap implements
035: ReferenceMap, SizedMap {
036:
037: private int _maxSize = Integer.MAX_VALUE;
038:
039: public ReferenceHashMap(int keyType, int valueType) {
040: super (toReferenceConstant(keyType),
041: toReferenceConstant(valueType));
042: }
043:
044: public ReferenceHashMap(int keyType, int valueType, int capacity,
045: float loadFactor) {
046: super (toReferenceConstant(keyType),
047: toReferenceConstant(valueType), capacity, loadFactor);
048: }
049:
050: /**
051: * Concver our reference constants to Apache's.
052: */
053: private static int toReferenceConstant(int type) {
054: switch (type) {
055: case ReferenceMap.HARD:
056: return org.apache.commons.collections.map.ReferenceMap.HARD;
057: case ReferenceMap.SOFT:
058: return org.apache.commons.collections.map.ReferenceMap.SOFT;
059: default:
060: return org.apache.commons.collections.map.ReferenceMap.WEAK;
061: }
062: }
063:
064: public int getMaxSize() {
065: return _maxSize;
066: }
067:
068: public void setMaxSize(int maxSize) {
069: _maxSize = (maxSize < 0) ? Integer.MAX_VALUE : maxSize;
070: if (_maxSize != Integer.MAX_VALUE)
071: removeOverflow(_maxSize);
072: }
073:
074: public boolean isFull() {
075: return size() >= _maxSize;
076: }
077:
078: public void overflowRemoved(Object key, Object value) {
079: }
080:
081: public void valueExpired(Object key) {
082: }
083:
084: public void keyExpired(Object value) {
085: }
086:
087: public void removeExpired() {
088: purge();
089: }
090:
091: /**
092: * Remove any entries over max size.
093: */
094: private void removeOverflow(int maxSize) {
095: Object key;
096: while (size() > maxSize) {
097: key = keySet().iterator().next();
098: overflowRemoved(key, remove(key));
099: }
100: }
101:
102: protected void addMapping(int hashIndex, int hashCode, Object key,
103: Object value) {
104: if (_maxSize != Integer.MAX_VALUE)
105: removeOverflow(_maxSize - 1);
106: super .addMapping(hashIndex, hashCode, key, value);
107: }
108:
109: protected HashEntry createEntry(HashEntry next, int hashCode,
110: Object key, Object value) {
111: return new AccessibleEntry(this , next, hashCode, key, value);
112: }
113:
114: protected void purge(Reference ref) {
115: // the logic for this method is taken from the original purge method
116: // we're overriding, with added logic to track the expired key/value
117: int index = hashIndex(ref.hashCode(), data.length);
118: AccessibleEntry entry = (AccessibleEntry) data[index];
119: AccessibleEntry prev = null;
120: Object key = null, value = null;
121: while (entry != null) {
122: if (purge(entry, ref)) {
123: if (isHard(keyType))
124: key = entry.key();
125: else if (isHard(valueType))
126: value = entry.value();
127:
128: if (prev == null)
129: data[index] = entry.nextEntry();
130: else
131: prev.setNextEntry(entry.nextEntry());
132: size--;
133: break;
134: }
135: prev = entry;
136: entry = entry.nextEntry();
137: }
138:
139: if (key != null)
140: valueExpired(key);
141: else if (value != null)
142: keyExpired(value);
143: }
144:
145: /**
146: * See the code for <code>ReferenceMap.ReferenceEntry.purge</code>.
147: */
148: private boolean purge(AccessibleEntry entry, Reference ref) {
149: boolean match = (!isHard(keyType) && entry.key() == ref)
150: || (!isHard(valueType) && entry.value() == ref);
151: if (match) {
152: if (!isHard(keyType))
153: ((Reference) entry.key()).clear();
154: if (!isHard(valueType))
155: ((Reference) entry.value()).clear();
156: else if (purgeValues)
157: entry.nullValue();
158: }
159: return match;
160: }
161:
162: private static boolean isHard(int type) {
163: return type == org.apache.commons.collections.map.ReferenceMap.HARD;
164: }
165:
166: protected void doWriteObject(ObjectOutputStream out)
167: throws IOException {
168: out.writeInt(_maxSize);
169: super .doWriteObject(out);
170: }
171:
172: protected void doReadObject(ObjectInputStream in)
173: throws ClassNotFoundException, IOException {
174: _maxSize = in.readInt();
175: super .doReadObject(in);
176: }
177:
178: /**
179: * Extension of the base entry type that allows our outer class to access
180: * protected state.
181: */
182: private static class AccessibleEntry extends ReferenceEntry {
183:
184: public AccessibleEntry(
185: org.apache.commons.collections.map.AbstractReferenceMap map,
186: HashEntry next, int hashCode, Object key, Object value) {
187: super (map, next, hashCode, key, value);
188: }
189:
190: public Object key() {
191: return key;
192: }
193:
194: public Object value() {
195: return value;
196: }
197:
198: public void nullValue() {
199: value = null;
200: }
201:
202: public AccessibleEntry nextEntry() {
203: return (AccessibleEntry) next;
204: }
205:
206: public void setNextEntry(AccessibleEntry next) {
207: this.next = next;
208: }
209: }
210: }
|