/*
 * Hibernate, Relational Persistence for Idiomatic Java
 *
 * Copyright (c) 2008-2011, Red Hat Inc. or third-party contributors as
 * indicated by the @author tags or express copyright attribution
 * statements applied by the authors.  All third-party contributions are
 * distributed under license by Red Hat Inc.
 *
 * This copyrighted material is made available to anyone wishing to use, modify,
 * copy, or redistribute it subject to the terms and conditions of the GNU
 * Lesser General Public License, as published by the Free Software Foundation.
 *
 * This program 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 this distribution; if not, write to:
 * Free Software Foundation, Inc.
 * 51 Franklin Street, Fifth Floor
 * Boston, MA  02110-1301  USA
 */
package org.hibernate.collection.internal;
import java.io.Serializable;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.hibernate.HibernateException;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.loader.CollectionAliases;
import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.type.Type;
/**
 * A persistent wrapper for a <tt>java.util.Map</tt>. Underlying collection
 * is a <tt>HashMap</tt>.
 *
 * @see java.util.HashMap
 * @author Gavin King
 */
public class PersistentMap extends AbstractPersistentCollection implements Map {
	protected Map map;
	/**
	 * Empty constructor.
	 * <p/>
	 * Note: this form is not ever ever ever used by Hibernate; it is, however,
	 * needed for SOAP libraries and other such marshalling code.
	 */
	public PersistentMap() {
		// intentionally empty
	}
	/**
	 * Instantiates a lazy map (the underlying map is un-initialized).
	 *
	 * @param session The session to which this map will belong.
	 */
	public PersistentMap(SessionImplementor session) {
		super( session );
	}
	/**
	 * Instantiates a non-lazy map (the underlying map is constructed
	 * from the incoming map reference).
	 *
	 * @param session The session to which this map will belong.
	 * @param map The underlying map data.
	 */
	public PersistentMap(SessionImplementor session, Map map) {
		super( session );
		this.map = map;
		setInitialized();
		setDirectlyAccessible( true );
	}
	@Override
	@SuppressWarnings( {"unchecked"})
	public Serializable getSnapshot(CollectionPersister persister) throws HibernateException {
		final HashMap clonedMap = new HashMap( map.size() );
		for ( Object o : map.entrySet() ) {
			final Entry e = (Entry) o;
			final Object copy = persister.getElementType().deepCopy( e.getValue(), persister.getFactory() );
			clonedMap.put( e.getKey(), copy );
		}
		return clonedMap;
	}
	@Override
	public Collection getOrphans(Serializable snapshot, String entityName) throws HibernateException {
		final Map sn = (Map) snapshot;
		return getOrphans( sn.values(), map.values(), entityName, getSession() );
	}
	@Override
	public boolean equalsSnapshot(CollectionPersister persister) throws HibernateException {
		final Type elementType = persister.getElementType();
		final Map snapshotMap = (Map) getSnapshot();
		if ( snapshotMap.size() != this.map.size() ) {
			return false;
		}
		for ( Object o : map.entrySet() ) {
			final Entry entry = (Entry) o;
			if ( elementType.isDirty( entry.getValue(), snapshotMap.get( entry.getKey() ), getSession() ) ) {
				return false;
			}
		}
		return true;
	}
	@Override
	public boolean isSnapshotEmpty(Serializable snapshot) {
		return ( (Map) snapshot ).isEmpty();
	}
	@Override
	public boolean isWrapper(Object collection) {
		return map==collection;
	}
	@Override
	public void beforeInitialize(CollectionPersister persister, int anticipatedSize) {
		this.map = (Map) persister.getCollectionType().instantiate( anticipatedSize );
	}
	@Override
	public int size() {
		return readSize() ? getCachedSize() : map.size();
	}
	@Override
	public boolean isEmpty() {
		return readSize() ? getCachedSize()==0 : map.isEmpty();
	}
	@Override
	public boolean containsKey(Object key) {
		final Boolean exists = readIndexExistence( key );
		return exists == null ? map.containsKey( key ) : exists;
	}
	@Override
	public boolean containsValue(Object value) {
		final Boolean exists = readElementExistence( value );
		return exists == null
				? map.containsValue( value )
				: exists;
	}
	@Override
	public Object get(Object key) {
		final Object result = readElementByIndex( key );
		return result == UNKNOWN
				? map.get( key )
				: result;
	}
	@Override
	@SuppressWarnings("unchecked")
	public Object put(Object key, Object value) {
		if ( isPutQueueEnabled() ) {
			final Object old = readElementByIndex( key );
			if ( old != UNKNOWN ) {
				queueOperation( new Put( key, value, old ) );
				return old;
			}
		}
		initialize( true );
		final Object old = map.put( key, value );
		// would be better to use the element-type to determine
		// whether the old and the new are equal here; the problem being
		// we do not necessarily have access to the element type in all
		// cases
		if ( value != old ) {
			dirty();
		}
		return old;
	}
	@Override
	@SuppressWarnings("unchecked")
	public Object remove(Object key) {
		if ( isPutQueueEnabled() ) {
			final Object old = readElementByIndex( key );
			if ( old != UNKNOWN ) {
				queueOperation( new Remove( key, old ) );
				return old;
			}
		}
		// TODO : safe to interpret "map.remove(key) == null" as non-dirty?
		initialize( true );
		if ( map.containsKey( key ) ) {
			dirty();
		}


Answer: 		return map.remove( key );