001: /* Copyright (C) 2004 - 2007 db4objects Inc. http://www.db4o.com
002:
003: This file is part of the db4o open source object database.
004:
005: db4o is free software; you can redistribute it and/or modify it under
006: the terms of version 2 of the GNU General Public License as published
007: by the Free Software Foundation and as clarified by db4objects' GPL
008: interpretation policy, available at
009: http://www.db4o.com/about/company/legalpolicies/gplinterpretation/
010: Alternatively you can write to db4objects, Inc., 1900 S Norfolk Street,
011: Suite 350, San Mateo, CA 94403, USA.
012:
013: db4o is distributed in the hope that it will be useful, but WITHOUT ANY
014: WARRANTY; without even the implied warranty of MERCHANTABILITY or
015: FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
016: for more details.
017:
018: You should have received a copy of the GNU General Public License along
019: with this program; if not, write to the Free Software Foundation, Inc.,
020: 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
021: package com.db4o;
022:
023: import java.util.*;
024:
025: import com.db4o.foundation.*;
026: import com.db4o.internal.*;
027: import com.db4o.types.*;
028:
029: /**
030: * @exclude
031: * @persistent
032: */
033: public class P2HashMap extends P1Collection implements Db4oMap,
034: TransactionListener {
035:
036: private static final float FILL = 0.6F;
037:
038: private transient int i_changes;
039: private transient boolean i_dontStoreOnDeactivate;
040:
041: public P1HashElement[] i_entries;
042: public int i_mask;
043: public int i_maximumSize;
044: public int i_size;
045:
046: public int i_type; // 0 == default hash, 1 == ID hash
047:
048: transient P1HashElement[] i_table;
049:
050: public int i_tableSize;
051:
052: P2HashMap() {
053: }
054:
055: P2HashMap(int a_size) {
056: a_size = (int) (a_size / FILL);
057: i_tableSize = 1;
058: while (i_tableSize < a_size) {
059: i_tableSize = i_tableSize << 1;
060: }
061: i_mask = i_tableSize - 1;
062: i_maximumSize = (int) (i_tableSize * FILL);
063: i_table = new P1HashElement[i_tableSize];
064: }
065:
066: public int activationDepth() {
067: return 2;
068: }
069:
070: public int adjustReadDepth(int a_depth) {
071: return 2;
072: }
073:
074: public void checkActive() {
075: super .checkActive();
076: if (i_table == null) {
077: i_table = new P1HashElement[i_tableSize];
078: if (i_entries != null) {
079: for (int i = 0; i < i_entries.length; i++) {
080: if (i_entries[i] != null) {
081: i_entries[i].checkActive();
082: i_table[i_entries[i].i_position] = i_entries[i];
083: }
084: }
085: }
086: i_changes = 0;
087:
088: // FIXME: reducing the table in size can be a problem during defragment in
089: // C/S mode on P2HashMaps that were partially stored uncommitted.
090:
091: // if ((i_size + 1) * 10 < i_tableSize) {
092: // i_tableSize = i_size + 5;
093: // increaseSize();
094: // modified();
095: // }
096:
097: }
098: }
099:
100: public void clear() {
101: synchronized (streamLock()) {
102: checkActive();
103: if (i_size != 0) {
104: for (int i = 0; i < i_table.length; i++) {
105: deleteAllElements(i_table[i]);
106: i_table[i] = null;
107: }
108: if (i_entries != null) {
109: for (int i = 0; i < i_entries.length; i++) {
110: i_entries[i] = null;
111: }
112: }
113: i_size = 0;
114: modified();
115: }
116: }
117: }
118:
119: public boolean containsKey(Object key) {
120: return get(key) != null;
121: }
122:
123: public boolean containsValue(Object value) {
124: throw new UnsupportedOperationException();
125: }
126:
127: public Object createDefault(Transaction a_trans) {
128: checkActive();
129: P2HashMap m4 = new P2HashMap(i_size);
130: m4.i_type = i_type;
131: m4.setTrans(a_trans);
132: P2HashMapIterator i = new P2HashMapIterator(this );
133: while (i.hasNext()) {
134: Object key = i.next();
135: if (key != null) {
136: m4.put4(key, get4(key));
137: }
138: }
139: return m4;
140: }
141:
142: private void deleteAllElements(P1HashElement a_entry) {
143: if (a_entry != null) {
144: a_entry.checkActive();
145: deleteAllElements((P1HashElement) a_entry.i_next);
146: a_entry.delete(i_deleteRemoved);
147: }
148: }
149:
150: public Set entrySet() {
151: final HashSet out = new HashSet(size());
152:
153: Iterator itor = keySet().iterator();
154:
155: while (itor.hasNext()) {
156: final Object key = itor.next();
157: final Object value = get(key);
158: final MapEntry entry = new MapEntry(key);
159: entry.setValue(value);
160: out.add(entry);
161: }
162:
163: return out;
164: }
165:
166: private boolean equals(P1HashElement phe, int hashCode, Object key) {
167: return phe.i_hashCode == hashCode
168: && phe.activatedKey(elementActivationDepth()).equals(
169: key);
170: }
171:
172: public Object get(Object key) {
173: synchronized (streamLock()) {
174: checkActive();
175: return get4(key);
176: }
177: }
178:
179: Object get4(Object key) {
180: if (key == null) {
181: return null;
182: }
183: int hash = hashOf(key);
184: P1HashElement phe = i_table[hash & i_mask];
185: while (phe != null) {
186: phe.checkActive();
187: if (equals(phe, hash, key)) {
188: return phe.activatedObject(elementActivationDepth());
189: }
190: phe = (P1HashElement) phe.i_next;
191: }
192: return null;
193: }
194:
195: private int hashOf(Object key) {
196: if (i_type == 1) {
197: int id = (int) getIDOf(key);
198: if (id == 0) {
199: store(key);
200: }
201: id = (int) getIDOf(key);
202: if (id == 0) {
203: Exceptions4.throwRuntimeException(62);
204: }
205: return id;
206: }
207: return key.hashCode();
208: }
209:
210: private void increaseSize() {
211: i_tableSize = i_tableSize << 1;
212: i_maximumSize = (int) (i_tableSize * FILL);
213: i_mask = i_tableSize - 1;
214: P1HashElement[] temp = i_table;
215: i_table = new P1HashElement[i_tableSize];
216: for (int i = 0; i < temp.length; i++) {
217: reposition(temp[i]);
218: }
219: }
220:
221: public boolean isEmpty() {
222: return size() == 0;
223: }
224:
225: public Set keySet() {
226: return new P2HashMapKeySet(this );
227: }
228:
229: void modified() {
230: if (getTrans() != null) {
231: if (i_changes == 0) {
232: getTrans().addTransactionListener(this );
233: }
234: i_changes++;
235: }
236: }
237:
238: public void postRollback() {
239: i_dontStoreOnDeactivate = true;
240: deactivate();
241: i_dontStoreOnDeactivate = false;
242: }
243:
244: public void preCommit() {
245: if (i_changes > 0) {
246: Collection4 col = new Collection4();
247: for (int i = 0; i < i_table.length; i++) {
248: if (i_table[i] != null) {
249: i_table[i].checkActive();
250: if (i_table[i].i_position != i) {
251: i_table[i].i_position = i;
252: i_table[i].update();
253: }
254: col.add(i_table[i]);
255: }
256: }
257: if (i_entries == null || i_entries.length != col.size()) {
258: i_entries = new P1HashElement[col.size()];
259: }
260: int i = 0;
261: Iterator4 it = col.iterator();
262: while (it.moveNext()) {
263: i_entries[i++] = (P1HashElement) it.current();
264: }
265: store(2);
266: }
267: i_changes = 0;
268: }
269:
270: public void preDeactivate() {
271: if (!i_dontStoreOnDeactivate) {
272: preCommit();
273: }
274: i_table = null;
275: }
276:
277: public Object put(Object key, Object value) {
278: synchronized (streamLock()) {
279: checkActive();
280: return put4(key, value);
281: }
282: }
283:
284: private Object put4(Object key, Object value) {
285: int hash = hashOf(key);
286: P1HashElement entry = new P1HashElement(getTrans(), null, key,
287: hash, value);
288: i_size++;
289: if (i_size > i_maximumSize) {
290: increaseSize();
291: }
292: modified();
293: int index = entry.i_hashCode & i_mask;
294: P1HashElement phe = i_table[index];
295: P1HashElement last = null;
296: while (phe != null) {
297: phe.checkActive();
298: if (equals(phe, entry.i_hashCode, key)) {
299: i_size--;
300: Object ret = phe
301: .activatedObject(elementActivationDepth());
302: entry.i_next = phe.i_next;
303: store(entry);
304: if (last != null) {
305: last.i_next = entry;
306: last.update();
307: } else {
308: i_table[index] = entry;
309: }
310: phe.delete(i_deleteRemoved);
311: return ret;
312: }
313: last = phe;
314: phe = (P1HashElement) phe.i_next;
315: }
316: entry.i_next = i_table[index];
317: i_table[index] = entry;
318: store(entry);
319: return null;
320: }
321:
322: public void putAll(Map t) {
323: synchronized (streamLock()) {
324: checkActive();
325: Iterator i = t.keySet().iterator();
326: while (i.hasNext()) {
327: Object key = i.next();
328: if (key != null) {
329: put4(key, t.get(key));
330: }
331: }
332: }
333: }
334:
335: public Object remove(Object key) {
336: synchronized (streamLock()) {
337: checkActive();
338: return remove4(key);
339: }
340: }
341:
342: Object remove4(Object key) {
343: int hash = hashOf(key);
344: P1HashElement phe = i_table[hash & i_mask];
345: P1HashElement last = null;
346: while (phe != null) {
347: phe.checkActive();
348: if (equals(phe, hash, key)) {
349: if (last != null) {
350: last.i_next = phe.i_next;
351: last.update();
352: } else {
353: i_table[hash & i_mask] = (P1HashElement) phe.i_next;
354: }
355: modified();
356: i_size--;
357: Object obj = phe
358: .activatedObject(elementActivationDepth());
359: phe.delete(i_deleteRemoved);
360: return obj;
361: }
362: last = phe;
363: phe = (P1HashElement) phe.i_next;
364: }
365: return null;
366: }
367:
368: public void replicateFrom(Object obj) {
369: checkActive();
370: if (i_entries != null) {
371: for (int i = 0; i < i_entries.length; i++) {
372: if (i_entries[i] != null) {
373: i_entries[i].delete(false);
374: }
375: i_entries[i] = null;
376: }
377: }
378: if (i_table != null) {
379: for (int i = 0; i < i_table.length; i++) {
380: i_table[i] = null;
381: }
382: }
383: i_size = 0;
384:
385: P2HashMap m4 = (P2HashMap) obj;
386: m4.checkActive();
387: P2HashMapIterator i = new P2HashMapIterator(m4);
388: while (i.hasNext()) {
389: Object key = i.next();
390: put4(key, m4.get4(key));
391: }
392:
393: modified();
394: }
395:
396: private void reposition(P1HashElement a_entry) {
397: if (a_entry != null) {
398: reposition((P1HashElement) a_entry.i_next);
399: a_entry.checkActive();
400: Object oldNext = a_entry.i_next;
401: a_entry.i_next = i_table[a_entry.i_hashCode & i_mask];
402: if (a_entry.i_next != oldNext) {
403: a_entry.update();
404: }
405: i_table[a_entry.i_hashCode & i_mask] = a_entry;
406: }
407: }
408:
409: public int size() {
410: synchronized (streamLock()) {
411: checkActive();
412: return i_size;
413: }
414: }
415:
416: public Object storedTo(Transaction a_trans) {
417: if (getTrans() == null) {
418: setTrans(a_trans);
419: modified();
420: } else {
421: if (a_trans != getTrans()) {
422: return replicate(getTrans(), a_trans);
423:
424: // Test fix for replication duplication. Not a good idea.
425:
426: // Object replicated = asReplicated(getTrans(), a_trans);
427: // if (replicated != null){
428: // return replicated;
429: // }
430: }
431: }
432: return this ;
433: }
434:
435: public Collection values() {
436: throw new UnsupportedOperationException();
437: }
438:
439: private class MapEntry implements Map.Entry {
440: private Object key;
441:
442: private Object value;
443:
444: public MapEntry(Object key_) {
445: key = key_;
446: }
447:
448: public Object getKey() {
449: return key;
450: }
451:
452: public Object getValue() {
453: return value;
454: }
455:
456: public Object setValue(Object value_) {
457: Object result = this .value;
458: value = value_;
459: return result;
460: }
461:
462: public boolean equals(Object obj) {
463: if (!(obj instanceof MapEntry))
464: return false;
465:
466: MapEntry other = (MapEntry) obj;
467:
468: return (key.equals(other.key))
469: && (value.equals(other.value));
470: }
471:
472: public int hashCode() {
473: return key.hashCode() ^ value.hashCode();
474: }
475: }
476: }
|