/* 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.blocking;
import java.io.Serializable;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Set;
/**
* This class is used to create sets of various types. It ensures that each distinct set exists only once,
* thus allowing sets to be compared with ==. Instances of this class are used to create various labels in blocking.
*/
@SuppressWarnings({ "unchecked", "rawtypes"})
public class SetFactory<E> implements Serializable {
private static final long serialVersionUID=7071071962187693657L;
protected Entry[] m_unusedEntries;
protected Entry[] m_entries;
protected int m_size;
protected int m_resizeThreshold;
public SetFactory() {
m_unusedEntries=new Entry[32];
m_entries=new Entry[16];
m_size=0;
m_resizeThreshold=(int)(0.75*m_entries.length);
}
public void clearNonpermanent() {
for (int i=m_entries.length-1;i>=0;--i) {
Entry entry=m_entries[i];
while (entry!=null) {
Entry nextEntry=entry.m_nextEntry;
if (!entry.m_permanent) {
removeEntry(entry);
leaveEntry(entry);
}
entry=nextEntry;
}
}
}
public int sizeInMemory() {
int size=m_unusedEntries.length*4+m_entries.length*4;
for (int i=m_unusedEntries.length-1;i>=0;--i) {
Entry entry=m_unusedEntries[i];
while (entry!=null) {
size+=entry.m_table.length*4+6*4;
entry=entry.m_nextEntry;
}
}
for (int i=m_entries.length-1;i>=0;--i) {
Entry entry=m_entries[i];
while (entry!=null) {
size+=entry.m_table.length*4+6*4;
entry=entry.m_nextEntry;
}
}
return size;
}
public void addReference(Set<E> set) {
((Entry)set).m_referenceCount++;
}
public void removeReference(Set<E> set) {
Entry entry=(Entry)set;
entry.m_referenceCount--;
if (entry.m_referenceCount==0 && !entry.m_permanent) {
removeEntry(entry);
leaveEntry(entry);
}
}
public void makePermanent(Set<E> set) {
((Entry)set).m_permanent=true;
}
public Set<E> getSet(List<E> elements) {
int hashCode=0;
for (int index=elements.size()-1;index>=0;--index)
hashCode+=elements.get(index).hashCode();
int index=getIndexFor(hashCode,m_entries.length);
Entry<E> entry=m_entries[index];
while (entry!=null) {
if (hashCode==entry.m_hashCode && entry.equalsTo(elements))
return entry;
entry=entry.m_nextEntry;
}
entry=getEntry(elements.size());
entry.initialize(elements,hashCode);
entry.m_previousEntry=null;
entry.m_nextEntry=m_entries[index];
if (entry.m_nextEntry!=null)
entry.m_nextEntry.m_previousEntry=entry;
m_entries[index]=entry;
m_size++;
if (m_size>m_resizeThreshold)
resize();
return entry;
}
protected void resize() {
Entry[] newEntries=new Entry[m_entries.length*2];
for (int index=0;index<m_entries.length;index++) {
Entry entry=m_entries[index];
while (entry!=null) {
Entry nextEntry=entry.m_nextEntry;
int newIndex=getIndexFor(entry.m_hashCode,newEntries.length);
entry.m_nextEntry=newEntries[newIndex];
entry.m_previousEntry=null;
if (entry.m_nextEntry!=null)
entry.m_nextEntry.m_previousEntry=entry;
newEntries[newIndex]=entry;
entry=nextEntry;
}
}
m_entries=newEntries;
m_resizeThreshold=(int)(0.75*m_entries.length);
}
protected void removeEntry(Entry<E> entry) {
if (entry.m_nextEntry!=null)
entry.m_nextEntry.m_previousEntry=entry.m_previousEntry;
if (entry.m_previousEntry!=null)
entry.m_previousEntry.m_nextEntry=entry.m_nextEntry;
int index=getIndexFor(entry.m_hashCode,m_entries.length);
if (m_entries[index]==entry)
m_entries[index]=entry.m_nextEntry;
entry.m_nextEntry=null;
entry.m_previousEntry=null;
}
protected Entry<E> getEntry(int size) {
if (size>=m_unusedEntries.length) {
int newSize=m_unusedEntries.length;
while (newSize<=size)
newSize=newSize*3/2;
Entry[] newUnusedEntries=new Entry[newSize];
System.arraycopy(m_unusedEntries,0,newUnusedEntries,0,m_unusedEntries.length);
m_unusedEntries=newUnusedEntries;
}
Entry<E> entry=m_unusedEntries[size];
if (entry==null)
return new Entry<E>(size);
else {
m_unusedEntries[size]=entry.m_nextEntry;
entry.m_nextEntry=null;
return entry;
}
}
protected void leaveEntry(Entry<E> entry) {
entry.m_nextEntry=m_unusedEntries[entry.size()];
entry.m_previousEntry=null;
m_unusedEntries[entry.size()]=entry;
}
protected static int getIndexFor(int hashCode,int tableLength) {
return hashCode & (tableLength-1);
}
protected static class Entry<T> implements Serializable,Set<T> {
private static final long serialVersionUID=-3850593656120645350L;
protected T[] m_table;
protected int m_hashCode;
protected Entry<T> m_previousEntry;
protected Entry<T> m_nextEntry;
protected int m_referenceCount;
protected boolean m_permanent;
public Entry(int size) {
m_hashCode=0;
m_table=(T[])new Object[size];
}
public void initialize(List<T> elements,int hashCode) {
elements.toArray(m_table);
m_hashCode=hashCode;
}
public void clear() {
throw new UnsupportedOperationException();
}
public boolean add(T object) {
throw new UnsupportedOperationException();
}
public boolean equalsTo(List<T> elements) {
if (m_table.length!=elements.size())
return false;
for (int index=m_table.length-1;index>=0;--index)
if (!elements.contains(m_table[index]))
return false;
return true;
}
public boolean addAll(Collection<? extends T> c) {
throw new UnsupportedOperationException();
}
public boolean contains(Object o) {
for (int index=m_table.length-1;index>=0;--index)
if (m_table[index].equals(o))
return true;
return false;
}
public boolean containsAll(Collection<?> c) {
for (Object object : c)
if (!contains(object))
return false;
return true;
}
public boolean isEmpty() {
return m_table.length==0;
}
public Iterator<T> iterator() {
return new EntryIterator();
}
public boolean remove(Object o) {
throw new UnsupportedOperationException();
}
public boolean removeAll(Collection<?> c) {
throw new UnsupportedOperationException();
}
public boolean retainAll(Collection<?> c) {
throw new UnsupportedOperationException();
}
public int size() {
return m_table.length;
}
public Object[] toArray() {
return m_table.clone();
}
public <E> E[] toArray(E[] a) {
System.arraycopy(m_table,0,a,0,m_table.length);
return a;
}
public int hashCode() {
return m_hashCode;
}
public boolean equals(Object that) {
return this==that;
}
protected class EntryIterator implements Iterator<T> {
protected int m_currentIndex;
public EntryIterator() {
m_currentIndex=0;
}
public boolean hasNext() {
return m_currentIndex<m_table.length;
}
public T next() {
if (m_currentIndex>=m_table.length)
throw new NoSuchElementException();
return m_table[m_currentIndex++];
}
public void remove() {
throw new UnsupportedOperationException();
}
}
}
}