/* Copyright (C) 2006 Christian Schneider
*
* This file is part of Nomad.
*
* Nomad is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Nomad 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Nomad; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
/*
* Created on Dec 18, 2006
*/
package net.sf.nmedit.nmutils.collections;
import java.io.IOException;
import java.io.Serializable;
import java.util.Arrays;
import java.util.ConcurrentModificationException;
import java.util.Iterator;
import java.util.NoSuchElementException;
public class ArrayMap<E> implements Iterable<E>, Cloneable, Serializable
{
/**
*
*/
private static final long serialVersionUID = -3401325714509820645L;
private E[] elements;
private int size = 0;
private int minkey = 0;
private transient int modcount;
@SuppressWarnings("unchecked")
public ArrayMap()
{
elements = (E[])new Object[10];
}
public void clear()
{
size = 0;
Arrays.fill(elements, null);
}
public int getMinKey()
{
return minkey;
}
public void setMinKey(int min)
{
if (min<0)
throw new IllegalArgumentException("invalid minimum key:"+min);
this.minkey = min;
}
public int generateIndex()
{
// prefer size rather than elements.length
// because elements in range[size..elements.length]
// are probably not set
return generateIndex(size+getMinKey());
}
public int generateIndex(int start)
{
validateIndex(start);
while (start<elements.length && elements[start]!=null)
start++;
return start;
}
public int indexOf(Object o)
{
for (int i=0;i<elements.length;i++)
{
Object e = elements[i];
if (e!= null &&(e==o || e.equals(o)))
return i;
}
return -1;
}
public E get(int index)
{
validateIndex(index);
return index<elements.length ? elements[index] : null;
}
public E put(IndexedElement<E> e)
{
return put(e.getIndex(), e.getElement());
}
protected void validateIndex(int index)
{
if (index<getMinKey())
throw new IllegalArgumentException("invalid index: "+index);
}
@SuppressWarnings("unchecked")
public E put(int index, E element)
{
validateIndex(index);
E old = null;
if (index<elements.length)
{
old = elements[index];
elements[index] = element;
}
else if (element != null)
{
// IMPLIED: index>=elements.length
// add capacity
E[] na = (E[]) new Object[Math.max(index+1, elements.length*2)];
System.arraycopy(elements, 0, na, 0, elements.length);
elements = na;
elements[index] = element;
}
if (old != element)
{
if (old!=null) size--;
if (element!=null) size++;
modcount++;
}
return old;
}
public boolean contains(Object o)
{
return indexOf(o) >= 0;
}
public int size()
{
return size;
}
public E remove(int index)
{
return put(index, null);
}
public Iterator<Integer> keyIterator()
{
return new Iterator<Integer>()
{
private int knownmod = modcount;
private int index = align(-1);
private int align(int index)
{
while (++index<elements.length && elements[index]==null)
{
;
}
return index;
}
public boolean hasNext()
{
return index<elements.length;
}
private void checkMod()
{
if (knownmod != modcount)
throw new ConcurrentModificationException();
}
public Integer next()
{
checkMod();
if (!hasNext())
throw new NoSuchElementException();
Integer result = new Integer(index);
index = align(index);
return result;
}
public void remove()
{
throw new UnsupportedOperationException();
}
};
}
public Iterator<E> iterator()
{
return new Iterator<E>()
{
private int knownmod = modcount;
private int index = align(-1);
private int storeindex = -1;
private int align(int index)
{
while (++index<elements.length && elements[index]==null);
return index;
}
public boolean hasNext()
{
return index<elements.length;
}
private void checkMod()
{
if (knownmod != modcount)
throw new ConcurrentModificationException();
}
public E next()
{
checkMod();
if (!hasNext())
throw new NoSuchElementException();
storeindex = index;
E e = elements[index];
index = align(index);
return e;
}
public void remove()
{
checkMod();
if (storeindex<0)
throw new IllegalStateException();
put(storeindex, null);
storeindex = -1;
knownmod = modcount;
}
};
}
@SuppressWarnings("unchecked")
public ArrayMap<E> clone()
{
ArrayMap<E> clone = null;
try
{
clone = (ArrayMap<E>) super.clone();
clone.size = size;
if (clone.elements == null || clone.elements==elements)
clone.elements = (E[]) elements.clone();
}
catch (CloneNotSupportedException e)
{
throw new InternalError(e.getMessage());
}
return clone;
}
public boolean equals(Object o)
{
if (o == this)
return true;
if (!(o instanceof ArrayMap))
return false;
ArrayMap am = (ArrayMap)o;
if (size()!=am.size())
return false;
Object[] e2 = am.elements;
int s = Math.min(elements.length, e2.length);
for (int i=0;i<s;i++)
if (!elements[i].equals(e2[i]))
return false;
return true;
}
/**
* @serialData the smallest key (int), the size field (int), largest index (int), followed by the pairs index(int),elements
*/
private void writeObject(java.io.ObjectOutputStream out) throws IOException
{
out.defaultWriteObject();
int max = elements.length;
while (--max>=0 && elements[max]==null);
out.writeInt(minkey);
out.writeInt(size);
out.writeInt(max);
for (int i=max;i>=0;i--)
{
Object o = elements[i];
if (o!=null)
{
out.writeInt(i);
out.writeObject(o);
}
}
}
/**
* @serialData the smallest key (int), the size field (int), largest index (int), followed by the pairs index(int),elements
*/
@SuppressWarnings("unchecked")
private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException
{
in.defaultReadObject();
minkey = in.readInt();
size = in.readInt();
int max = in.readInt();
Object[] e = new Object[max+1];
for (int i=0;i<size;i++)
{
int key = in.readInt();
e[key] = in.readObject();
}
elements = (E[]) e;
}
public boolean containsKey( int index )
{
return index>= 0 && index<elements.length && elements[index]!=null;
}
}