/*
 * Copyright 2006 Takahiro Nakamura.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
 * either express or implied. See the License for the specific language
 * governing permissions and limitations under the License.
 */
package woolpack.utils;

import java.util.AbstractMap;
import java.util.AbstractSet;
import java.util.Iterator;
import java.util.Set;

/**
 * キーの{@link Iterator}を用いる{@link java.util.Map}のスケルトン実装です。
 * {@link #getKeyIterator()}と{@link #getValue(Object)}を実装することにより
 * {@link java.util.Map}の動作を実装することができます。
 * その他の拡張方法については{@link AbstractMap}を参照してください。
 * @author nakamura
 * 
 */
public abstract class AbstractKeyIteratorMap<K, V> extends AbstractMap<K, V> {

	/**
	 * {@link #entrySet()}から呼び出されます(called)。
	 * 実装ではキーの{@link Iterator}を返す必要があります。
	 * 
	 * @return キーの{@link Iterator}。
	 */
	protected abstract Iterator<K> getKeyIterator();

	/**
	 * {@link #entrySet()}から呼び出されます(called)。
	 * 実装ではキーに対応する値を返す必要があります。
	 * 
	 * @param key キー。
	 * @return 値。
	 */
	protected abstract V getValue(Object key);

	/**
	 * {@link #getKeyIterator()}と{@link #getValue(Object)}
	 * を使用して{@link java.util.Map}の動作を実装しています。
	 */
	@Override
	public Set<Entry<K, V>> entrySet() {
		return new AbstractSet<Entry<K, V>>() {
			@Override
			public int size() {
				int i = 0;
				for (final Iterator it = iterator(); it.hasNext();) {
					it.next();
					i++;
				}
				return i;
			}

			@Override
			public Iterator<Entry<K, V>> iterator() {
				final Iterator<K> it = getKeyIterator();
				return new Iterator<Entry<K, V>>() {
					public void remove() {
						it.remove();
					}

					public boolean hasNext() {
						return it.hasNext();
					}

					public Entry<K, V> next() {
						final K key = it.next();
						return new Entry<K, V>() {
							public K getKey() {
								return key;
							}

							public V getValue() {
								return AbstractKeyIteratorMap
								.this.getValue(key);
							}

							public V setValue(final V value) {
								return AbstractKeyIteratorMap
								.this.put(key, value);
							}

							@Override
							public boolean equals(final Object obj) {
								if (this == obj) {
									return true;
								}
								if (obj == null) {
									return false;
								}
								if (getClass() != obj.getClass()) {
									return false;
								}
								final Entry other = (Entry) obj;
								if (key == null) {
									if (other.getKey() != null) {
										return false;
									}
								} else if (!key.equals(other.getKey())) {
									return false;
								}
								if (getValue() == null) {
									if (other.getValue() != null) {
										return false;
									}
								} else if (!getValue().equals(other.getValue())) {
									return false;
								}
								return true;
							}

							@Override
							public int hashCode() {
								final int PRIME = 31;
								int result = 1;
								result = PRIME * result + ((key == null) ? 0 : key.hashCode());
								result = PRIME * result + ((getValue() == null) ? 0 : getValue().hashCode());
								return result;
							}
						};
					}
				};
			}
		};
	}

	@Override
	public V get(final Object arg0) {
		return getValue(arg0);
	}
}
