/*
 * Copyright (C) 2011 The Guava Authors
 *
 * 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 com.google.common.collect;

import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import com.google.common.annotations.GwtCompatible;

/**
 * Workaround for
 * <a href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6312706"> EnumMap
 * bug</a>. If you want to pass an {@code EnumMap}, with the intention of using
 * its {@code entrySet()} method, you should wrap the {@code EnumMap} in this
 * class instead.
 *
 * <p>
 * This class is not thread-safe even if the underlying map is.
 *
 * @author Dimitris Andreou
 */
@GwtCompatible
final class WellBehavedMap<K, V> extends ForwardingMap<K, V> {
	private final Map<K, V> delegate;
	private Set<Entry<K, V>> entrySet;

	private WellBehavedMap(Map<K, V> delegate) {
		this.delegate = delegate;
	}

	/**
	 * Wraps the given map into a {@code WellBehavedEntriesMap}, which intercepts
	 * its {@code entrySet()} method by taking the {@code Set<K> keySet()} and
	 * transforming it to {@code Set<Entry<K, V>>}. All other invocations are
	 * delegated as-is.
	 */
	static <K, V> WellBehavedMap<K, V> wrap(Map<K, V> delegate) {
		return new WellBehavedMap<K, V>(delegate);
	}

	@Override
	protected Map<K, V> delegate() {
		return delegate;
	}

	@Override
	public Set<Entry<K, V>> entrySet() {
		Set<Entry<K, V>> es = entrySet;
		if (es != null) {
			return es;
		}
		return entrySet = new EntrySet();
	}

	private final class EntrySet extends Maps.EntrySet<K, V> {
		@Override
		Map<K, V> map() {
			return WellBehavedMap.this;
		}

		@Override
		public Iterator<Entry<K, V>> iterator() {
			return new TransformedIterator<K, Entry<K, V>>(keySet().iterator()) {
				@Override
				Entry<K, V> transform(final K key) {
					return new AbstractMapEntry<K, V>() {
						@Override
						public K getKey() {
							return key;
						}

						@Override
						public V getValue() {
							return get(key);
						}

						@Override
						public V setValue(V value) {
							return put(key, value);
						}
					};
				}
			};
		}
	}
}
