// -*- c++ -*-

/*
 *
 * Jakelib2 - General purpose C++ library
 * Copyright (C) 2001 Florian Wolff  (florian@donuz.de)
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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 library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * $Id: Map.h,v 1.9 2003/09/20 09:06:52 florian Exp $
 */

#ifndef INCLUDE_JAKELIB2_UTIL_MAP_H
# define INCLUDE_JAKELIB2_UTIL_MAP_H

#include "Set.h"

namespace jakelib {
  namespace util {
    class Set;
    class Collection;

    /**
     * @author Alex Fabijanic <fabijanica@nucorsteel.com>(jakelib2 port)
     */
    class Map : public jakelib::lang::Interface
    {
      /**
       * Remove all entries from this Map (optional operation).
       *
       * @throws UnsupportedOperationException if clear is not supported
       */
    public: virtual void clear() = 0;

      /**
       * Returns true if this contains a mapping for the given key.
       *
       * @param key the key to search for
       * @return true if the map contains the key
       * @throws NullPointerException if key is <code>null</code> but the map
       *         does not permit null keys
       */
    public: virtual jboolean containsKey(jakelib::lang::Object* key) = 0;

      /**
       * Returns true if this contains at least one mapping with the given value.
       * In other words, returns true if a value v exists where
       * <code>(value == null ? v == null : value.equals(v))</code>. This usually
       * requires linear time.
       *
       * @param value the value to search for
       * @return true if the map contains the value
       */
    public: virtual jboolean containsValue(jakelib::lang::Object* value) = 0;

      /**
       * Returns a set view of the mappings in this Map.  Each element in the
       * set is a Map.Entry.  The set is backed by the map, so that changes in
       * one show up in the other.  Modifications made while an iterator is
       * in progress cause undefined behavior.  If the set supports removal,
       * these methods remove the underlying mapping from the map:
       * <code>Iterator.remove</code>, <code>Set.remove</code>,
       * <code>removeAll</code>, <code>retainAll</code>, and <code>clear</code>.
       * Element addition, via <code>add</code> or <code>addAll</code>, is
       * not supported via this set.
       *
       * @return the set view of all mapping entries
       */
    public: virtual jakelib::util::Set* entrySet() = 0;

      /**
       * Compares the specified object with this map for equality. Returns
       * <code>true</code> if the other object is a Map with the same mappings,
       * that is,<br>
       * <code>o instanceof Map && entrySet().equals(((Map) o).entrySet() = 0;</code>
       * This allows comparison of maps, regardless of implementation.
       *
       * @param o the object to be compared
       * @return true if the object equals this map
       */
    public: virtual jboolean equals(jakelib::lang::Object* o) = 0;

      /**
       * Returns the value mapped by the given key. Returns <code>null</code> if
       * there is no mapping.  However, in Maps that accept null values, you
       * must rely on <code>containsKey</code> to determine if a mapping exists.
       *
       * @param key the key to look up
       * @return the value associated with the key, or null if key not in map
       * @throws NullPointerException if this map does not accept null keys
       */
    public: virtual jakelib::lang::Object* get(jakelib::lang::Object* key) = 0;

      /**
       * Associates the given key to the given value (optional operation). If the
       * map already contains the key, its value is replaced. Be aware that in
       * a map that permits <code>null</code> values, a null return does not
       * always imply that the mapping was created.
       *
       * @param key the key to map
       * @param value the value to be mapped
       * @return the previous value of the key, or null if there was no mapping
       * @throws UnsupportedOperationException if the operation is not supported
       * @throws IllegalArgumentException if something about this key or value
       *         prevents it from existing in this map
       * @throws NullPointerException if the map forbids null keys or values
       */
    public: virtual jakelib::lang::Object* put(jakelib::lang::Object* key,
                                               jakelib::lang::Object* value) = 0;

      /**
       * Returns the hash code for this map. This is the sum of all hashcodes
       * for each Map.Entry object in entrySet.  This allows comparison of maps,
       * regardless of implementation, and satisfies the contract of
       * jakelib::lang::Object*.hashCode.
       *
       * @return the hash code
       */
    public: virtual jint hashCode() = 0;

      /**
       * Returns true if the map contains no mappings.
       *
       * @return true if the map is empty
       */
    public: virtual jboolean isEmpty() = 0;

      /**
       * Returns a set view of the keys in this Map.  The set is backed by the
       * map, so that changes in one show up in the other.  Modifications made
       * while an iterator is in progress cause undefined behavior.  If the set
       * supports removal, these methods remove the underlying mapping from
       * the map: <code>Iterator.remove</code>, <code>Set.remove</code>,
       * <code>removeAll</code>, <code>retainAll</code>, and <code>clear</code>.
       * Element addition, via <code>add</code> or <code>addAll</code>, is
       * not supported via this set.
       *
       * @return the set view of all keys
       */
    public: virtual jakelib::util::Set* keySet() = 0;

      /**
       * Copies all entries of the given map to this one (optional operation). If
       * the map already contains a key, its value is replaced.
       *
       * @param m the mapping to load into this map
       * @throws UnsupportedOperationException if the operation is not supported
       * @throws IllegalArgumentException if something about a key or value
       *         prevents it from existing in this map
       * @throws NullPointerException if the map forbids null keys or values, or
       *         if <code>m</code> is null.
       */
    public: virtual void putAll(Map* m) = 0;

      /**
       * Removes the mapping for this key if present (optional operation). If
       * the key is not present, this returns null. Note that maps which permit
       * null values may also return null if the key was removed.
       *
       * @param key the key to remove
       * @return the value the key mapped to, or null if not present
       * @throws UnsupportedOperationException if deletion is unsupported
       */
    public: virtual jakelib::lang::Object* remove(jakelib::lang::Object* key) = 0;

      /**
       * Returns the number of key-value mappings in the map. If there are more
       * than Integer.MAX_VALUE mappings, return Integer.MAX_VALUE.
       *
       * @return the number of mappings
       */
    public: virtual jint size() = 0;

      /**
       * Returns a collection (or bag) view of the values in this Map.  The
       * collection is backed by the map, so that changes in one show up in
       * the other.  Modifications made while an iterator is in progress cause
       * undefined behavior.  If the collection supports removal, these methods
       * remove the underlying mapping from the map: <code>Iterator.remove</code>,
       * <code>Collection.remove</code>, <code>removeAll</code>,
       * <code>retainAll</code>, and <code>clear</code>. Element addition, via
       * <code>add</code> or <code>addAll</code>, is not supported via this
       * collection.
       *
       * @return the collection view of all values
       */

    public: virtual jakelib::util::Collection* getValues() = 0;

      /**
       * A map entry (key-value pair). The Map.entrySet() method returns a set
       * view of these objects; there is no other valid way to come across them.
       * These objects are only valid for the duration of an iteration; in other
       * words, if you mess with one after modifying the map, you are asking
       * for undefined behavior.
       */
      class Entry : public jakelib::lang::Object, jakelib::lang::Interface
      {
      public:

        /**
         * Get the key corresponding to this entry.
         *
         * @return the key
         */
        virtual jakelib::lang::Object* getKey() = 0;

        /**
         * Get the value corresponding to this entry. If you already called
         * Iterator.remove(), this is undefined.
         *
         * @return the value
         */
        virtual jakelib::lang::Object* getValue() = 0;

        /**
         * Replaces the value with the specified object (optional operation).
         * This writes through to the map, and is undefined if you already
         * called Iterator.remove().
         *
         * @param value the new value to store
         * @return the old value
         * @throws UnsupportedOperationException if the operation is not supported
         * @throws IllegalArgumentException if something about the value
         *         prevents it from existing in this map
         * @throws NullPointerException if the map forbids null values
         */
        virtual jakelib::lang::Object* setValue(jakelib::lang::Object* value) = 0;

        /**
         * Returns the hash code of the entry.  This is defined as the
         * exclusive-or of the hashcodes of the key and value (using 0 for
         * <code>null</code>). In other words, this must be:
         * 
         * \code
         * (getKey() == null ? 0 : getKey().hashCode())
         * ^ (getValue() == null ? 0 : getValue().hashCode())
         * \endcode
         *
         * @return the hash code
         */
        virtual jint hashCode() = 0;

        /**
         * Compares the specified object with this entry. Returns true only if
         * the object is a mapping of identical key and value. In other words,
         * this must be:
         * 
         * \code
         * (o instanceof Map.Entry)
         * && (getKey() == null ? ((HashMap) o).getKey() == null
         * : getKey().equals(((HashMap) o).getKey()))
         * && (getValue() == null ? ((HashMap) o).getValue() == null
         * : getValue().equals(((HashMap) o).getValue()))
         * \endcode
         *
         * @param o the object to compare
         * @return <code>true</code> if it is equal
         */
        virtual jboolean equals(jakelib::lang::Object* o) = 0;

        JAKELIB_DECLARE_INTERFACE(Entry);
      };

      JAKELIB_DECLARE_INTERFACE(Map);
    };
  }
}
#endif
