# TailScopedDictionary.java

    /*
    ** Module   : TailScopedDictionary.java
    ** Abstract : A ScopedDictionary that contains TailMap collections.
    **
    ** Copyright (c) 2022-2023, Golden Code Development Corporation.
    **
    ** -#- -I- --Date-- ----------------------------Description----------------------------
    ** 001 TW  20220617 Initial implementation.
    ** 002 TW  20220620 Javadoc fix.
    ** 003 TW  20220727 Turn EmptyTailMap remove implementations into no-op, to fix ScopedDictionary  problem
    **                  identified by CA. Refs #6356-105
    ** 004 CA  20230223 Avoid the overhead of calculating an empty map when the scope has no dictionary -
    **                  internally, Node.getDictionary(false) will return null if there is no dictionary; but,
    **                  getDictionaryAtScope() will still return an empty map, instead of null.
    */

    /*
    ** This program is free software: you can redistribute it and/or modify
    ** it under the terms of the GNU Affero General Public License as
    ** published by the Free Software Foundation, either version 3 of the
    ** License, or (at your option) any later version.
    **
    ** 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 Affero General Public License for more details.
    **
    ** You may find a copy of the GNU Affero GPL version 3 at the following
    ** location: https://www.gnu.org/licenses/agpl-3.0.en.html
    **
    ** Additional terms under GNU Affero GPL version 3 section 7:
    **
    **   Under Section 7 of the GNU Affero GPL version 3, the following additional
    **   terms apply to the works covered under the License.  These additional terms
    **   are non-permissive additional terms allowed under Section 7 of the GNU
    **   Affero GPL version 3 and may not be removed by you.
    **
    **   0. Attribution Requirement.
    **
    **     You must preserve all legal notices or author attributions in the covered
    **     work or Appropriate Legal Notices displayed by works containing the covered
    **     work.  You may not remove from the covered work any author or developer
    **     credit already included within the covered work.
    **
    **   1. No License To Use Trademarks.
    **
    **     This license does not grant any license or rights to use the trademarks
    **     Golden Code, FWD, any Golden Code or FWD logo, or any other trademarks
    **     of Golden Code Development Corporation. You are not authorized to use the
    **     name Golden Code, FWD, or the names of any author or contributor, for
    **     publicity purposes without written authorization.
    **
    **   2. No Misrepresentation of Affiliation.
    **
    **     You may not represent yourself as Golden Code Development Corporation or FWD.
    **
    **     You may not represent yourself for publicity purposes as associated with
    **     Golden Code Development Corporation, FWD, or any author or contributor to
    **     the covered work, without written authorization.
    **
    **   3. No Misrepresentation of Source or Origin.
    **
    **     You may not represent the covered work as solely your work.  All modified
    **     versions of the covered work must be marked in a reasonable way to make it
    **     clear that the modified work is not originating from Golden Code Development
    **     Corporation or FWD.  All modified versions must contain the notices of
    **     attribution required in this license.
    */

    package com.goldencode.p2j.util;

    import java.util.*;
    import java.util.function.*;

    /**
     * A {@link ScopedDictionary} that contains {@link TailMap} collections.
     * Supported scope: context local.
     * @param   <K>
     *          Dictionary key type
     * @param   <V>
     *          Dictionary value type
     */
    public class TailScopedDictionary<K, V>
    extends ScopedDictionary<K, V>
    {
       /**
        * <code>EMPTY_TAILMAP</code> is a singleton used for {@link #getDictionaryAtScope} when there is no
        * dictionary at that level.
        */
       @SuppressWarnings("rawtypes")
       private static final TailMap EMPTY_TAILMAP = new EmptyTailMap<>();

       /**
        * Default constructor.
        */
       public TailScopedDictionary()
       {
          super();
       }

       /**
        * Returns the dictionary from the scope specified, or <code>null</code> if
        * the specified scope does not exist.
        *
        * @param   scope
        *          The depth (in number of scopes from the top of the stack) at
        *          which the value must be set. Use -1 for the global scope.
        * @param   create
        *          <code>true</code> to create a dictionary for this scope if one
        *          does not already exist;  <code>false</code> to have an empty
        *          (immutable) dictionary returned if one does not already exist.
        *
        * @return  The dictionary, if any, associated with the specified scope.
        *          Note that this may be an immutable, empty map if no dictionary
        *          exists at this scope, and <code>create</code> is set to
        *          <code>false</code>.  If the scope itself does not exist, then
        *          <code>null</code> is returned.
        */
       @Override
       public TailMap<K, V> getDictionaryAtScope(int scope, boolean create)
       {
          Node<K, V> node = getNodeAt(scope);
          // This cast is safe, since all instances come from our factory methods.
          TailMap<K, V> dict = null;
          if (node != null)
          {
             dict = (TailMap<K, V>) node.getDictionary(create);
             return dict == null ? EMPTY_TAILMAP : dict;
          }

          return null;
       }

       /**
        * Factory method for an {@link IdentityHashMap}
        *
        * @return  a new {@link TailIdentityHashMap}
        */
       @Override
       protected Map<K, Boolean> factoryIdentityHashMap()
       {
          return new TailIdentityHashMap<>();
       }

       /**
        * Factory method for a {@link Set} as returned by {@link ScopedDictionary#keySet}
        * <p>
        * Currently, the caller can't yet access {@link TailMap#flagIterating}, since a Set
        * is returned. For now, we assume that it's not a realistic scenario in FWD.
        * Redesign when needed (symptom would be a concurrency exception while iterating keySet).
        *
        * @return The Set returned by <code>Collections.newSetFromMap</code> of a new {@link TailHashMap}
        */
       @Override
       protected Set<K> factoryHashSetFromMap()
       {
          return Collections.newSetFromMap(new TailHashMap<>());
       }

       /**
        * Factory method for an {@link IdentityHashMap} at the scope items (the {@link
        * ScopedDictionary.Node} inner class)
        *
        * @return  a new {@link TailIdentityHashMap}
        */
       @Override
       protected Map<K, V> factoryNodeIdentityHashMap(int size)
       {
          return new TailIdentityHashMap<>(size);
       }

       /**
        * Factory method for a {@link HashMap} at the scope items (the {@link ScopedDictionary.Node}
        * inner class)
        *
        * @return  a new {@link TailHashMap}
        */
       @Override
       protected Map<K, V> factoryNodeHashMap(int size)
       {
          return new TailHashMap<>(size);
       }

       /**
        * An immutable empty static subclass of TailHashMap.
        * Attempts to modify this Map result in an <tt>UnsupportedOperationException</tt>.
        *
        * @param   <K>
        *          Dictionary key type
        * @param   <V>
        *          Dictionary value type
        */
       private static class EmptyTailMap<K, V>
       extends TailHashMap<K, V>
       {
          /** Generated version UID */
          private static final long serialVersionUID = -4775165838523528350L;

          // Note: We can't leverage Collections.unmodifiableMap here, since we prefer to return
          //       a TailMap instance to all client invocations.

          /**
           * Attempts to <code>put</code> result in an <tt>UnsupportedOperationException</tt>.
           */
          @Override
          public V put(K key, V value)
          {
             throw new UnsupportedOperationException();
          }

          /**
           * This is a no-op. Attempts to <code>remove</code> will always return <tt>null</tt>.
           */
          @Override
          public V remove(Object key)
          {
             return null;
          }

          /**
           * Attempts to <code>putAll</code> result in an <tt>UnsupportedOperationException</tt>.
           */
          @Override
          public void putAll(Map<? extends K, ? extends V> m)
          {
             throw new UnsupportedOperationException();
          }

          /**
           * Attempts to <code>replaceAll</code> result in an <tt>UnsupportedOperationException</tt>.
           */
          @Override
          public void replaceAll(BiFunction<? super K, ? super V, ? extends V> function)
          {
             throw new UnsupportedOperationException();
          }

          /**
           * Attempts to <code>putIfAbsent</code> result in an <tt>UnsupportedOperationException</tt>.
           */
          @Override
          public V putIfAbsent(K key, V value)
          {
             throw new UnsupportedOperationException();
          }

          /**
           * This is a no-op. Attempts to <code>remove</code> will always return <tt>false</tt>.
           */
          @Override
          public boolean remove(Object key, Object value)
          {
             return false;
          }

          /**
           * Attempts to <code>replace</code> result in an <tt>UnsupportedOperationException</tt>.
           */
          @Override
          public boolean replace(K key, V oldValue, V newValue)
          {
             throw new UnsupportedOperationException();
          }

          /**
           * Attempts to <code>replace</code> result in an <tt>UnsupportedOperationException</tt>.
           */
          @Override
          public V replace(K key, V value)
          {
             throw new UnsupportedOperationException();
          }

          /**
           * Attempts to <code>computeIfAbsent</code> result in an <tt>UnsupportedOperationException</tt>.
           */
          @Override
          public V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction)
          {
             throw new UnsupportedOperationException();
          }

          /**
           * Attempts to <code>computeIfPresent</code> result in an <tt>UnsupportedOperationException</tt>.
           */
          @Override
          public V computeIfPresent(K key,
                   BiFunction<? super K, ? super V, ? extends V> remappingFunction)
          {
             throw new UnsupportedOperationException();
          }

          /**
           * Attempts to <code>compute</code> result in an <tt>UnsupportedOperationException</tt>.
           */
          @Override
          public V compute(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction)
          {
             throw new UnsupportedOperationException();
          }

          /**
           * Attempts to <code>merge</code> result in an <tt>UnsupportedOperationException</tt>.
           */
          @Override
          public V merge(K key, V value,
                   BiFunction<? super V, ? super V, ? extends V> remappingFunction)
          {
             throw new UnsupportedOperationException();
          }

       }
    }