# ScopedSymbolDictionary.java

    /*
    ** Module   : ScopedSymbolDictionary.java
    ** Abstract : Provides multiscoped symbol dictionary functionality.
    **
    ** Copyright (c) 2004-2023, Golden Code Development Corporation.
    **
    ** -#- -I- --Date-- --JPRM-- ----------------------------Description----------------------------
    ** 001 NVS 20041104   @18750 Created an initial implementation of a mul-
    **                           tiscoped symbol dictionary class.
    ** 002 NVS 20041110   @18751 Implemented configurable case sensitivity.
    **                           Case sensitivity is a parameter to the object
    **                           constructors. Once instantiated, dictionary's
    **                           case sensitivity cannot be changed.
    ** 003 NVS 20050209   @19729 Implemented new method locateSymbol which
    **                           returns the nesting level of the first
    **                           appearance of a symbol. This method is needed
    **                           to fix a Preprocessor bug related to visibi-
    **                           lity of arguments beyond the scope where they
    **                           are defined.
    ** 004 NVS 20050210   @19757 Enhanced debugging features by adding dump-
    **                           Scope private method, recoding dump() and
    **                           adding dumpCurrentScope public method. Now
    **                           the output is indented according to the depth
    **                           of the scope dump for easier interpretation.
    ** 005 SIY 20050607   @21711 Added value-based scope lookup and scope-
    **                           based value setting. Minor cleanups.
    ** 006 GES 20050801   @21943 Added getSymbolAtScope() to allow arbitrary
    **                           symbol lookups at any specific scope
    **                           including the global scope.
    ** 007 ECF 20051028   @23221 Abstracted out majority of function to a new
    **                           superclass, ScopedDictionary. This class now
    **                           delegates its methods to the superclass. The
    **                           superclass uses Object for its keys, while
    **                           this class continues to use String. The case
    **                           sensitivity feature is thus still implemented
    **                           within this class.
    ** 008 SIY 20080904   @39717 Inferred generics.
    ** 009 GES 20080917   @39855 Added isCaseSensitive().
    ** 010 GES 20111205          Exposed processKey() as public.
    ** 011 ECF 20140830          Added symbolSet(int).
    ** 012 CA  20160728          Added copyAll API, to allow a dictionary duplication; if the
    **                           keepGlobal flag is true, then the global scope is shared between the
    **                           source and the copy; otherwise, it's duplicated.
    ** 013 CA  20201003          Disallow IdentityHashMap dictionaries (as the key is a String).
    ** 014 CA  20230727          ScopedSymbolDictionary now uses a CaseInsensitiveHashMap for case-insensitive
    **                           dictionaries, so 'processKey' is no longer needed.
    */
    /*
    ** 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 com.goldencode.util.*;

    /**
     * Implements a symbol dictionary with multiple levels of scope.  Symbols can
     * be added to, removed from and queried in the dictionary.  Each symbol
     * consists of the symbol's <code>String</code> representation and a user-
     * defined object that can store arbitrary user data.  As a result of the
     * lookup process, if a symbol is found, the user-defined object is returned.
     * <p>
     * When symbols are added, they are stored in a flat list called a scope. Each
     * scope can store a set of symbols where each symbol name must be unique.
     * The user has control over how many scopes exist.  By default, no scopes
     * are available.  The user of this class is responsible for adding scopes
     * using the <code>addScope</code> method.  When the symbols stored in the
     * current scope should no longer be available, the scope can be removed
     * using the <code>deleteScope</code> method.  This multilevel scope
     * approach can be thought of as a stack of scopes where the current scope
     * is the top of the stack and the bottom of the stack has a special scope
     * that is considered "global".  Symbols can be added to the current scope
     * or to the global scope using the <code>addSymbol</code> method.
     * <p>
     * Since the global scope has a special meaning, a convenience constructor
     * is provided that automatically creates the global scope.
     * <p>
     * Symbols must only be unique within a single scope.  If the same symbol
     * exists in multiple scopes, the instance found closest to the current
     * scope (top of stack) is the one that is found on lookup.  Likewise, when
     * a symbol is deleted using <code>deleteSymbol</code>, the topmost symbol
     * is the one removed.
     * <p>
     * When instantiating objects of this class, one can chose between case-
     * sensitive and case-insensitive operations. Only the symbol names can be
     * case-(in)sensitive. Once instantiated, the mode of operation cannot be
     * changed. Case sensitivity affects <code>addSymbol</code>,
     * <code>deleteSymbol</code> and <code>lookupSymbol</code> methods.
     * <p>
     * This class is designed to provide a symbol lookup facility such as a
     * compiler would need to properly reference scoped variables or functions.
     * Most functionality is delegated to the superclass.
     * <p>
     * @author  NVS
     * @version 1.0.6
     * @param   <V>
     *          Type of value stored in dictionary.
     */
    public class ScopedSymbolDictionary<V>
    extends ScopedDictionary<String, V>
    {
       /** Defines case sensitivity mode for lookups */
       private boolean caseSensitive = false;

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

       /**
        * Factory method for {@link HashMap} when {@link #caseSensitive} is <code>true</code> and
        * {@link CaseInsensitiveHashMap} when {@link #caseSensitive} is <code>false</code>.
        *
        * @param   size
        *          The maximum expected size of the Map
        *
        * @return  a new map depending on the {@link #caseSensitive} flag.
        */
       @Override
       protected Map<String, V> factoryNodeHashMap(int size)
       {
          return caseSensitive ? super.factoryNodeHashMap(size) : new CaseInsensitiveHashMap<>(size);
       }

       /**
        * Factory method for a {@link Set} as returned by {@link #keySet}.  This will be a set from a
        * {@link HashMap} when {@link #caseSensitive} is <code>true</code> and from {@link CaseInsensitiveHashMap}
        * when {@link #caseSensitive} is <code>false</code>.
        *
        *
        * @return  a new set depending on the {@link #caseSensitive} flag.
        */
       @Override
       protected Set<String> factoryHashSetFromMap()
       {
          return caseSensitive ? super.factoryHashSetFromMap()
                               : Collections.newSetFromMap(new CaseInsensitiveHashMap<>());
       }

       /**
        * Set the {@link #identityKeys} flag.
        * <p>
        * This is a no-op, as the key is a {@link String}.
        *
        * @param    identityKeys
        *           Not used.
        */
       @Override
       public final void setIdentityKeys(boolean identityKeys)
       {
          // no-op
       }

       /**
        * Constructor with explicit case-sensitivity requested.
        *
        * @param  caseSensitive
        *         <code>true</code> makes all symbol lookups case-sensitive.
        */
       public ScopedSymbolDictionary(boolean caseSensitive)
       {
          super();
          this.caseSensitive = caseSensitive;
       }

       /**
        * Convenience constructor that automatically creates the global scope.
        *
        * @param  extra
        *         Object to be associated with the global scope.
        */
       public ScopedSymbolDictionary(Object extra)
       {
          super(extra);
       }

       /**
        * Convenience constructor that automatically creates the global scope
        * with explicit case-sensitivity requested.
        *
        * @param  extra
        *         Object to be associated with the global scope.
        * @param  caseSensitive
        *         <code>true</code> makes all symbol lookups case-sensitive.
        */
       public ScopedSymbolDictionary(Object extra, boolean caseSensitive)
       {
          this(extra);
          this.caseSensitive = caseSensitive;
       }

       /**
        * Reports if this dictionary is case-sensitive.
        *
        * @return   <code>true</code> if this dictionary is case-sensitive.
        */
       public boolean isCaseSensitive()
       {
          return caseSensitive;
       }

       /**
        * Adds a symbol to the current or global scope or replaces
        * its user-defined object. The current scope is the one on the top of the
        * stack. The global scope is always the first scope created on the stack.
        * If only one scope is on the stack, both refer to the same scope.
        *
        * @param   global
        *          Specifies the target scope. <code>false</code> specifies the
        *          current scope, and <code>true</code> specifies the global
        *          scope.
        * @param   name
        *          The symbol name.
        * @param   value
        *          The symbol value which may be any object.
        *
        * @return  <code>true</code> if the symbol has been added,
        *          <code>false</code> if its value has been replaced.
        */
       public boolean addSymbol(boolean global, String name, V value)
       {
          return addEntry(global, name, value);
       }

       /**
        * Searches all scopes from the top of the stack down and deletes
        * the first found occurrence of the symbol.
        *
        * @param   name
        *          The symbol name.
        *
        * @return  <code>true</code> if the symbol has been deleted.
        */
       public boolean deleteSymbol(String name)
       {
          return deleteEntry(name);
       }

       /**
        * Searches all scopes from the top of the stack down for the specified
        * key and value pair and returns 0-based index of the depth where
        * that pair is found.  Both the key and the value must be matched to
        * the given parameters for this to succeed.
        *
        * @param   key
        *          The symbol name.
        * @param   value
        *          The value to search.
        *
        * @return  0-based index of the scope depth at which value is found.
        */
       public int locateSymbolValue(String key, V value)
       {
          return locate(key, value);
       }

       /**
        * Sets the given named symbol in the scope specified, to the given
        * value.
        *
        * @param   name
        *          The symbol name.
        * @param   scope
        *          The depth (in number of scopes from the top of the stack) at
        *          which the value must be set.
        * @param   value
        *          The replacement value.
        *
        * @return  <code>true</code> if the value was successfully set, or
        *          <code>false</code> if any error occurred.
        */
       public boolean setSymbolAtScope(String name, int scope, V value)
       {
          return setValueAtScope(name, scope, value);
       }

       /**
        * Gets the value of the given named symbol in the scope specified.
        *
        * @param   name
        *          The symbol name.
        * @param   scope
        *          The depth (in number of scopes from the top of the stack) at
        *          which the value must be set. Use -1 to reference the global
        *          scope.
        *
        * @return  The object found or <code>null</code> if no such name exists.
        */
       public Object getSymbolAtScope(String name, int scope)
       {
          return getValueAtScope(name, scope);
       }

       /**
        * Searches all scopes from the top of the stack down for a given symbol.
        *
        * @param   name
        *          The symbol name.
        *
        * @return  The user-defined object associated with symbol or
        *          <code>null</code> if not found.
        */
       public Object lookupSymbol(String name)
       {
          return lookup(name);
       }

       /**
        * Searches all scopes from the top of the stack down for a given symbol.
        *
        * @param   name
        *          The symbol name.
        * @return  The nesting level (depth) of the scope where the symbol is
        *          defined first or -1 if not found.
        */
       public int locateSymbol(String name)
       {
          return locate(name);
       }

       /**
        * Returns a set containing all defined symbol names as strings.
        *
        * @return   The set view of all symbol names defined in all scopes,
        */
       public Set<String> symbolSet()
       {
          return keySet();
       }

       /**
        * Returns a set containing all defined symbol names as strings, in the scope specified.
        *
        * @param    scope
        *           The depth (in number of scopes from the top of the stack) from which the symbols
        *           are collected. Use -1 to reference the global scope.
        *
        * @return   The set view of all symbols from the specified scope (and only that scope).
        */
       public Set<String> symbolSet(int scope)
       {
          return keySet(scope);
       }

       /**
        * Clear this dictionary and copy all content from the source.  The global scope will be either
        * duplicated or kept as a standalone reference, to be shared among copies.
        *
        * @param    source
        *           The source dictionary.
        * @param    keepGlobal
        *           When <code>true</code>, the global dictionary is exposed directly to this copy;
        *           otherwise is duplicated.
        */
       public void copyAll(ScopedSymbolDictionary<V> source, boolean keepGlobal)
       {
          this.caseSensitive = source.caseSensitive;

          super.copyAll(source, keepGlobal);
       }
    }