1 25 package org.archive.crawler.settings; 26 27 import java.lang.ref.Reference ; 28 import java.lang.ref.ReferenceQueue ; 29 import java.lang.ref.SoftReference ; 30 import java.util.ConcurrentModificationException ; 31 import java.util.Iterator ; 32 import java.util.NoSuchElementException ; 33 34 35 public class SoftSettingsHash { 36 37 42 private static final int MAXIMUM_CAPACITY = 1 << 30; 43 44 47 private static final float LOAD_FACTOR = 0.75f; 48 49 52 private SettingsEntry[] table; 53 54 57 private int size; 58 59 62 private int threshold; 63 64 67 private final ReferenceQueue <? super String > queue 68 = new ReferenceQueue <String >(); 69 70 77 private volatile int modCount; 78 79 87 public SoftSettingsHash(int initialCapacity) { 88 if (initialCapacity < 0) 89 throw new IllegalArgumentException ("Illegal Initial Capacity: "+ 90 initialCapacity); 91 if (initialCapacity > MAXIMUM_CAPACITY) 92 initialCapacity = MAXIMUM_CAPACITY; 93 94 int capacity = 1; 95 while (capacity < initialCapacity) 96 capacity <<= 1; 97 table = new SettingsEntry[capacity]; 98 threshold = (int)(capacity * LOAD_FACTOR); 99 } 100 101 105 static boolean eq(Object x, Object y) { 106 return x == y || x.equals(y); 107 } 108 109 112 static int indexFor(int h, int length) { 113 return h & (length-1); 114 } 115 116 119 private void expungeStaleEntries() { 120 SettingsEntry entry; 121 Reference ref; 122 while ( (ref = queue.poll()) != null) { 123 entry = (SettingsEntry)ref; 124 int h = entry.hash; 125 int i = indexFor(h, table.length); 126 127 SettingsEntry prev = table[i]; 128 SettingsEntry p = prev; 129 while (p != null) { 130 SettingsEntry next = p.next; 131 if (p == entry) { 132 if (prev == entry) 133 table[i] = next; 134 else 135 prev.next = next; 136 entry.next = null; entry.settings = null; size--; 139 break; 140 } 141 prev = p; 142 p = next; 143 } 144 } 145 } 146 147 153 public int size() { 154 if (size == 0) 155 return 0; 156 expungeStaleEntries(); 157 return size; 158 } 159 160 170 public CrawlerSettings get(String key) { 171 if (key == null) { 172 throw new NullPointerException ("Null key"); 173 } 174 int hash = hash(key); 175 expungeStaleEntries(); 176 int index = indexFor(hash, table.length); 177 SettingsEntry e = table[index]; 178 while (e != null) { 179 if (e.hash == hash && eq(key, e.get())) 180 return e.settings; 181 e = e.next; 182 } 183 return null; 184 } 185 186 199 public CrawlerSettings put(String key, CrawlerSettings settings) { 200 if (settings == null) { 201 throw new NullPointerException ("Settings object was null"); 202 } 203 if (key == null) { 204 throw new NullPointerException ("Null key"); 205 } 206 int hash = hash(key); 207 expungeStaleEntries(); 208 int i = indexFor(hash, table.length); 209 210 for (SettingsEntry entry = table[i]; entry != null; entry = entry.next) { 211 if (hash == entry.hash && eq(key, entry.get())) { 212 CrawlerSettings oldValue = entry.settings; 213 if (settings != oldValue) 214 entry.settings = settings; 215 return oldValue; 216 } 217 } 218 219 modCount++; 220 table[i] = new SettingsEntry(key, settings, queue, hash, table[i]); 221 if (++size >= threshold) 222 resize(table.length * 2); 223 return null; 224 } 225 226 public CrawlerSettings put(SettingsEntry entry) { 227 return put(entry.getKey(), entry.getValue()); 228 } 229 230 240 void resize(int newCapacity) { 241 expungeStaleEntries(); 242 SettingsEntry[] oldTable = table; 243 int oldCapacity = oldTable.length; 244 245 if (size < threshold || oldCapacity > newCapacity) 247 return; 248 249 SettingsEntry[] newTable = new SettingsEntry[newCapacity]; 250 251 transfer(oldTable, newTable); 252 table = newTable; 253 254 259 if (size >= threshold / 2) { 260 threshold = (int)(newCapacity * LOAD_FACTOR); 261 } else { 262 expungeStaleEntries(); 263 transfer(newTable, oldTable); 264 table = oldTable; 265 } 266 } 267 268 269 private void transfer(SettingsEntry[] src, SettingsEntry[] dest) { 270 for (int j = 0; j < src.length; ++j) { 271 SettingsEntry entry = src[j]; 272 src[j] = null; 273 while (entry != null) { 274 SettingsEntry next = entry.next; 275 Object key = entry.get(); 276 if (key == null) { 277 entry.next = null; entry.settings = null; size--; 280 } else { 281 int i = indexFor(entry.hash, dest.length); 282 entry.next = dest[i]; 283 dest[i] = entry; 284 } 285 entry = next; 286 } 287 } 288 } 289 290 298 public Object remove(String key) { 299 if (key == null) { 300 throw new NullPointerException ("Null key"); 301 } 302 int hash = hash(key); 303 expungeStaleEntries(); 304 int i = indexFor(hash, table.length); 305 SettingsEntry prev = table[i]; 306 SettingsEntry entry = prev; 307 308 while (entry != null) { 309 SettingsEntry next = entry.next; 310 if (hash == entry.hash && eq(key, entry.get())) { 311 modCount++; 312 size--; 313 if (prev == entry) 314 table[i] = next; 315 else 316 prev.next = next; 317 return entry.settings; 318 } 319 prev = entry; 320 entry = next; 321 } 322 323 return null; 324 } 325 326 329 public void clear() { 330 while (queue.poll() != null) 333 ; 334 335 modCount++; 336 SettingsEntry tab[] = table; 337 for (int i = 0; i < tab.length; ++i) 338 tab[i] = null; 339 size = 0; 340 341 while (queue.poll() != null) 345 ; 346 } 347 348 352 static class SettingsEntry extends SoftReference <String > { 353 private CrawlerSettings settings; 354 private final int hash; 355 private SettingsEntry next; 356 357 360 SettingsEntry(String key, CrawlerSettings settings, 361 ReferenceQueue <? super String > queue, 362 int hash, SettingsEntry next) { 363 super(key, queue); 364 this.settings = settings; 365 this.hash = hash; 366 this.next = next; 367 } 368 369 public String getKey() { 370 return (String ) this.get(); 371 } 372 373 public CrawlerSettings getValue() { 374 return settings; 375 } 376 377 public boolean equals(Object o) { 378 if (!(o instanceof SettingsEntry)) 379 return false; 380 SettingsEntry e = (SettingsEntry)o; 381 String key1 = getKey(); 382 String key2 = e.getKey(); 383 if (key1 == key2 || (key1 != null && key1.equals(key2))) { 384 CrawlerSettings setting1 = getValue(); 385 CrawlerSettings setting2 = e.getValue(); 386 if (setting1 == setting2 || (setting1 != null && setting1.equals(setting2))) 387 return true; 388 } 389 return false; 390 } 391 } 392 393 395 class EntryIterator implements Iterator { 396 int index; 397 SettingsEntry entry = null; 398 SettingsEntry lastReturned = null; 399 int expectedModCount = modCount; 400 401 405 String nextKey = null; 406 407 411 String currentKey = null; 412 413 EntryIterator() { 414 index = (size() != 0 ? table.length : 0); 415 } 416 417 public boolean hasNext() { 418 SettingsEntry[] t = table; 419 420 while (nextKey == null) { 421 SettingsEntry e = entry; 422 int i = index; 423 while (e == null && i > 0) 424 e = t[--i]; 425 entry = e; 426 index = i; 427 if (e == null) { 428 currentKey = null; 429 return false; 430 } 431 nextKey = (String ) e.get(); if (nextKey == null) 433 entry = entry.next; 434 } 435 return true; 436 } 437 438 439 public Object next() { 440 return nextEntry(); 441 } 442 443 public SettingsEntry nextEntry() { 444 if (modCount != expectedModCount) 445 throw new ConcurrentModificationException (); 446 if (nextKey == null && !hasNext()) 447 throw new NoSuchElementException (); 448 449 lastReturned = entry; 450 entry = entry.next; 451 currentKey = nextKey; 452 nextKey = null; 453 return lastReturned; 454 } 455 456 public void remove() { 457 if (lastReturned == null) 458 throw new IllegalStateException (); 459 if (modCount != expectedModCount) 460 throw new ConcurrentModificationException (); 461 462 SoftSettingsHash.this.remove(currentKey); 463 expectedModCount = modCount; 464 lastReturned = null; 465 currentKey = null; 466 } 467 468 } 469 470 475 static int hash(String key) { 476 int hash = key.hashCode(); 477 478 hash += ~(hash << 9); 479 hash ^= (hash >>> 14); 480 hash += (hash << 4); 481 hash ^= (hash >>> 10); 482 return hash; 483 } 484 485 public EntryIterator iterator() { 486 return new EntryIterator(); 487 } 488 489 } 490 | Popular Tags |