/* Copyright 2008, 2009, 2010 by the Oxford University Computing Laboratory
This file is part of HermiT.
HermiT is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
HermiT is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with HermiT. If not, see <http://www.gnu.org/licenses/>.
*/
package org.semanticweb.HermiT.model;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
/**
* The manager for the internable objects.
*/
public abstract class InterningManager<E> {
protected static final double LOAD_FACTOR=0.75;
protected final ReferenceQueue<E> m_referenceQueue;
protected Entry<E>[] m_entries;
protected int m_size;
protected int m_resizeThreshold;
public InterningManager() {
m_referenceQueue=new ReferenceQueue<E>();
m_entries=createEntries(16);
m_size=0;
m_resizeThreshold=(int)(m_entries.length*LOAD_FACTOR);
}
public synchronized E intern(E object) {
processQueue();
int hashCode=getHashCode(object);
int objectEntryIndex=getIndexFor(hashCode,m_entries.length);
Entry<E> previousEntry=null;
Entry<E> entry=m_entries[objectEntryIndex];
while (entry!=null) {
if (hashCode==entry.m_hashCode) {
E entryObject=entry.get();
if (entryObject==null) {
if (previousEntry==null)
m_entries[objectEntryIndex]=entry.m_next;
else
previousEntry.m_next=entry.m_next;
m_size--;
}
else if (equal(object,entryObject))
return entryObject;
}
previousEntry=entry;
entry=entry.m_next;
}
if (m_size>=m_resizeThreshold) {
int newEntriesLength=m_entries.length*2;
Entry<E>[] newEntries=createEntries(newEntriesLength);
for (int entryIndex=0;entryIndex<m_entries.length;entryIndex++) {
Entry<E> currentEntry=m_entries[entryIndex];
while (currentEntry!=null) {
Entry<E> nextEntry=currentEntry.m_next;
if (currentEntry.get()==null)
m_size--;
else {
int newIndex=getIndexFor(currentEntry.m_hashCode,newEntriesLength);
currentEntry.m_next=newEntries[newIndex];
newEntries[newIndex]=currentEntry;
}
currentEntry=nextEntry;
}
}
m_entries=newEntries;
m_resizeThreshold=(int)(newEntriesLength*LOAD_FACTOR);
objectEntryIndex=getIndexFor(hashCode,m_entries.length);
}
Entry<E> newEntry=new Entry<E>(object,m_referenceQueue,hashCode,m_entries[objectEntryIndex]);
m_entries[objectEntryIndex]=newEntry;
m_size++;
return object;
}
protected final int getIndexFor(int hashCode,int entriesLength) {
return hashCode & (entriesLength-1);
}
protected void removeEntry(Entry<E> entry) {
int index=getIndexFor(entry.m_hashCode,m_entries.length);
Entry<E> previousEntry=null;
for (Entry<E> current=m_entries[index];current!=null;current=current.m_next) {
if (current==entry) {
m_size--;
if (previousEntry==null)
m_entries[index]=current.m_next;
else
previousEntry.m_next=current.m_next;
return;
}
previousEntry=current;
}
}
@SuppressWarnings("unchecked")
protected void processQueue() {
Entry<E> entry=(Entry<E>)m_referenceQueue.poll();
while (entry!=null) {
removeEntry(entry);
entry=(Entry<E>)m_referenceQueue.poll();
}
}
@SuppressWarnings("unchecked")
protected final Entry<E>[] createEntries(int size) {
return (Entry<E>[])new Entry[size];
}
protected abstract int getHashCode(E object);
protected abstract boolean equal(E object1,E object2);
/**
* The entry for the hash map.
*/
protected static class Entry<E> extends WeakReference<E> {
public final int m_hashCode;
public Entry<E> m_next;
public Entry(E object,ReferenceQueue<E> referenceQueue,int hashCode,Entry<E> next) {
super(object,referenceQueue);
m_hashCode=hashCode;
m_next=next;
}
}
}