/*
	Реализация спецификаций CLDC версии 1.1 (JSR-139), MIDP версии 2.1 (JSR-118)
	и других спецификаций для функционирования компактных приложений на языке
	Java (мидлетов) в среде программного обеспечения Малик Эмулятор.

	Copyright © 2016, 2019 Малик Разработчик

	Это свободная программа: вы можете перераспространять ее и/или изменять
	ее на условиях Меньшей Стандартной общественной лицензии GNU в том виде,
	в каком она была опубликована Фондом свободного программного обеспечения;
	либо версии 3 лицензии, либо (по вашему выбору) любой более поздней версии.

	Эта программа распространяется в надежде, что она будет полезной,
	но БЕЗО ВСЯКИХ ГАРАНТИЙ; даже без неявной гарантии ТОВАРНОГО ВИДА
	или ПРИГОДНОСТИ ДЛЯ ОПРЕДЕЛЕННЫХ ЦЕЛЕЙ. Подробнее см. в Меньшей Стандартной
	общественной лицензии GNU.

	Вы должны были получить копию Меньшей Стандартной общественной лицензии GNU
	вместе с этой программой. Если это не так, см.
	<https://www.gnu.org/licenses/>.
*/


package java.util;

public class Hashtable extends Object
{
	private static class Entry extends Object
	{
		public int hash;
		public Object key;
		public Object value;
		public Entry next;

		Entry(int hash, Object key, Object value, Entry next)
		{
			this.hash = hash;
			this.key = key;
			this.value = value;
			this.next = next;
		}
	}

	private static class Enumerator extends Object
			implements Enumeration
	{
		private boolean keys;
		private int index;
		private Entry[] table;
		private Entry entry;

		Enumerator(boolean keys, Entry[] table)
		{
			this.keys = keys;
			this.index = table.length;
			this.table = table;
			this.entry = null;
		}

		public boolean hasMoreElements()
		{
			int i;
			Entry[] t;
			if(entry != null)
			{
				return true;
			}
			for(t = table, i = index; i-- > 0; )
			{
				if((entry = t[i]) != null)
				{
					index = i;
					return true;
				}
			}
			index = i;
			return false;
		}

		public Object nextElement()
		{
			int i;
			Entry[] t;
			Entry e;
			if(entry == null)
			{
				for(t = table, i = index; i-- > 0 && (entry = t[i]) == null; );
				index = i;
			}
			if((e = entry) == null)
			{
				throw new NoSuchElementException("Hashtable: " +
						"больше не осталось элементов для перечисления.");
			}
			entry = e.next;
			return keys ? e.key : e.value;
		}
	}


	private int factor;
	private int length;
	private Entry[] table;
	private Object lock;

	public Hashtable()
	{
		this(15);
	}

	public Hashtable(int initialCapacity)
	{
		if(initialCapacity < 0)
		{
			throw new IllegalArgumentException("Hashtable: " +
					"параметр initialCapacity не может быть отрицательным.");
		}
		if(initialCapacity == 0)
		{
			initialCapacity = 1;
		}
		this.factor = (initialCapacity * 3) / 4;
		this.table = new Entry[initialCapacity];
		this.lock = new Object();
	}

	public String toString()
	{
		int i;
		Entry[] t;
		Enumerator k;
		Enumerator e;
		StringBuffer result = new StringBuffer('{');
		synchronized(lock)
		{
			t = table;
			k = new Enumerator(true, t);
			e = new Enumerator(false, t);
			for(i = length; i-- > 0; )
			{
				result.append(k.nextElement().toString()).append('=').
						append(e.nextElement().toString());
				if(i > 0)
				{
					result.append(", ");
				}
			}
		}
		return result.append('}').toString();
	}

	public void clear()
	{
		int i;
		Entry[] t;
		synchronized(lock)
		{
			for(i = (t = table).length; i-- > 0; t[i] = null);
			length = 0;
		}
	}

	public boolean isEmpty()
	{
		return length == 0;
	}

	public boolean contains(Object value)
	{
		boolean result;
		int i;
		Entry[] t;
		Entry e;
		if(value == null)
		{
			throw new NullPointerException("Hashtable.contains: " +
					"параметр value равен нулевой ссылке.");
		}
		synchronized(lock)
		{
			label0:
			{
				for(i = (t = table).length; i-- > 0; )
				{
					for(e = t[i]; e != null; e = e.next)
					{
						if(!value.equals(e.value))
						{
							continue;
						}
						result = true;
						break label0;
					}
				}
				result = false;
			}
		}
		return result;
	}

	public boolean containsKey(Object key)
	{
		boolean result;
		int hash;
		Entry[] t;
		Entry e;
		if(key == null)
		{
			throw new NullPointerException("Hashtable.containsKey: " +
					"параметр key равен нулевой ссылке.");
		}
		synchronized(lock)
		{
			label0:
			{
				e = (t = table)[((hash = key.hashCode()) & Integer.MAX_VALUE) % t.length];
				for(; e != null; e = e.next)
				{
					if(hash != e.hash || !key.equals(e.key))
					{
						continue;
					}
					result = true;
					break label0;
				}
				result = false;
			}
		}
		return result;
	}

	public int size()
	{
		return length;
	}

	public Object remove(Object key)
	{
		int i;
		int hash;
		Entry[] t;
		Entry e;
		Entry f;
		Object result;
		if(key == null)
		{
			throw new NullPointerException("Hashtable.remove: " +
					"параметр key равен нулевой ссылке.");
		}
		synchronized(lock)
		{
			label0:
			{
				f = null;
				e = (t = table)[i = ((hash = key.hashCode()) & Integer.MAX_VALUE) % t.length];
				for(; e != null; f = e, e = e.next)
				{
					if(hash != e.hash || !key.equals(e.key))
					{
						continue;
					}
					if(f != null)
					{
						f.next = e.next;
					} else
					{
						t[i] = e.next;
					}
					length--;
					result = e.value;
					break label0;
				}
				result = null;
			}
		}
		return result;
	}

	public Object get(Object key)
	{
		int hash;
		Entry[] t;
		Entry e;
		Object result;
		if(key == null)
		{
			throw new NullPointerException("Hashtable.get: " +
					"параметр key равен нулевой ссылке.");
		}
		synchronized(lock)
		{
			label0:
			{
				e = (t = table)[((hash = key.hashCode()) & Integer.MAX_VALUE) % t.length];
				for(; e != null; e = e.next)
				{
					if(hash != e.hash || !key.equals(e.key))
					{
						continue;
					}
					result = e.value;
					break label0;
				}
				result = null;
			}
		}
		return result;
	}

	public Object put(Object key, Object value)
	{
		int i;
		int l;
		int hash;
		Entry[] t;
		Entry e;
		Object result;
		if(key == null)
		{
			throw new NullPointerException("Hashtable.put: " +
					"параметр key равен нулевой ссылке.");
		}
		if(value == null)
		{
			throw new NullPointerException("Hashtable.put: " +
					"параметр value равен нулевой ссылке.");
		}
		synchronized(lock)
		{
			label0:
			{
				e = (t = table)[i = ((hash = key.hashCode()) & Integer.MAX_VALUE) % t.length];
				for(; e != null; e = e.next)
				{
					if(hash != e.hash || !key.equals(e.key))
					{
						continue;
					}
					result = e.value;
					e.value = value;
					break label0;
				}
				if((l = length) >= factor)
				{
					rehash();
					i = (hash & Integer.MAX_VALUE) % (t = table).length;
				}
				t[i] = new Entry(hash, key, value, t[i]);
				length = l + 1;
				result = null;
			}
		}
		return result;
	}

	public Enumeration keys()
	{
		return new Enumerator(true, table);
	}

	public Enumeration elements()
	{
		return new Enumerator(false, table);
	}

	protected void rehash()
	{
		int i;
		int j;
		int nc;
		int oc;
		Entry[] nt;
		Entry[] ot;
		Entry e;
		Entry f;
		table = nt = new Entry[nc = ((oc = (ot = table).length) << 1) + 1];
		factor = (nc * 3) / 4;
		for(i = oc; i-- > 0; )
		{
			for(f = ot[i]; f != null; )
			{
				j = ((e = f).hash & Integer.MAX_VALUE) % nc;
				f = f.next;
				e.next = nt[j];
				nt[j] = e;
			}
		}
	}
}
