# AccumulatorManager.java

    /*
    ** Module   : AccumulatorManager.java
    ** Abstract : provides block-behavior for the accumulators.
    **
    ** Copyright (c) 2008-2022, Golden Code Development Corporation.
    **
    ** -#- -I- --Date-- -T- --JPRM-- ----------------Description-----------------
    ** 001 CA  20080129   @37024 Created initial version. Provides context
    **                           local scoped accumulators.
    ** 002 CA  20080214   @37130 The set for each block must contain all
    **                           accumulator instances which can be accesed
    **                           from that block. Removed master set. Now,
    **                           when a new scope is started, the set for the
    **                           new scope is added all known accumulators
    **                           from enclosing scope.
    ** 003 GES 20080625   @38956 Moved to new BlockType enum.
    ** 004 ECF 20080630   @39025 Fixed memory leak. Some Accumulators were
    **                           being added to a block scope one level above
    **                           where they were first being used. If that
    **                           scope was never popped, but the lower scope
    **                           was, these instances would be dead, but still
    **                           doing work during scopeStart(), creating a
    **                           CPU drain.
    ** 005 ECF 20080715   @39126 Fixed NPE. Created default WorkArea ctor
    **                           which initializes deque and pushes the first
    **                           entry.
    ** 006 GES 20080917   @39849 Minor class name change.
    ** 007 CA  20131013          Added no-op scopeDeleted() method, required by the changes in
    **                           Scopeable interface.
    ** 008 CA  20160728          Cleaned up, all accums register with the next scope.
    ** 009 CA  20190326          Implemented TransactionHelper instead of direct usage of the static
    **                           API.  This allows the elimination of context local usage in TM.
    ** 010 CA  20200930          Refactored to remove the singleton scopeable instance (which forced the scope
    **                           notifications to use context-local state).
    **     CA  20210310          Small performance improvement.
    **     CA  20220901          Refactored scope notification support: ScopeableFactory was removed, and the
    **                           registration is now specific to each type of scopeable.  For each case, the block
    **                           will be registered for scope support (for that particular scopeable) only when
    **                           the scopeable is 'active' (i.e. unnamed streams or accumulators are used).  This
    **                           allows a lazy registration of scopeables, to avoid the unnecessary overhead of
    **                           processing all the scopeables for each and every block.
    ** 011 CA  20231031          Added 'BlockDefinition' parameter to 'scopeStart'.
    */
    /*
    ** 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.p2j.security.ContextLocal;

    /**
     * Provides context-local scoped accumulators.
     * <p>
     * All references can be added to the top scope (closest enclosing scope).
     * <p>
     * Implements the <code>Scopeable</code> interface to allow this class to be
     * automatically notified of all scope start and end occurances. This removes
     * the need for client code to be instrumented to provide this service. The
     * use of this class is now tied to the {@link TransactionManager} class. In
     * particular, the singleton instance of this class is used in each context-
     * local list of <code>Scopeable</code> objects as a dispatcher. This bit
     * of nastiness is necessary since the Java Interface can't be implemented
     * on a static basis but instead requires an instance method implementation.
     * These instance methods then call the static counterparts which is where
     * the content-local versions of the resource pools are accessed.
     *
     * @author    CA
     */
    public class AccumulatorManager
    implements Scopeable
    {
       /** Pool of accums which is context-local. Initializes upon first use. */
       private static ContextContainer context = new ContextContainer();

       /** The container with the instance state. */
       private final WorkArea wa = new WorkArea();

       /**
        * Get the {@link AccumulatorManager} context-local instance.
        *
        * @return   See above.
        */
       public static AccumulatorManager getInstance()
       {
          return context.get();
       }

       /**
        * Adds an accumulator to the current set of accumulators.
        *
        * @param   accum
        *          The accumulator
        */
       public static void addAccumulator(Accumulator accum)
       {
          AccumulatorManager mgr = context.get();
          WorkArea wa = mgr.wa;
          wa.tm.registerPendingScopeable(mgr);

          if (wa.pendingSet == null)
          {
             wa.initPending();
          }
          wa.pendingSet.add(accum);
       }

       /**
        * Provides a notification that a new scope is about to be entered.
        *
        * @param    block
        *           The explicit block definition which required this notification.
        */
       @Override
       public void scopeStart(BlockDefinition block)
       {
          // ignore editing blocks
          if (block.type == BlockType.EDITING)
             return;

          Set<Accumulator> prevSet = wa.stack.peek();
          Set<Accumulator> currentSet = wa.pendingSet;

          // if no pending set exists, create one
          if (currentSet == null)
          {
             wa.initPending();
             currentSet = wa.pendingSet;
          }

          if (prevSet != null && !prevSet.isEmpty())
          {
             // roll up all accumulators from the previous scope to the new,
             // current scope
             currentSet.addAll(prevSet);
          }

          // make the pending set of accumulators the current scope's active set
          wa.stack.push(currentSet);

          // notify all registered accumulators a new scope is starting
          Iterator<Accumulator> itr = currentSet.iterator();
          while (itr.hasNext())
          {
             Accumulator a = itr.next();
             a.scopeStart(block);
          }

          // clear the pending set for the next scope
          wa.pendingSet = null;
       }

       /**
        * Provides a notification that a scope is about to be exited.
        */
       public void scopeFinished()
       {
          // ignore editing blocks
          if (wa.tm.getBlockType() == BlockType.EDITING)
             return;

          Set<Accumulator> set = wa.stack.pop();
          if (set != null)
          {
             Iterator<Accumulator> itr = set.iterator();
             while (itr.hasNext())
             {
                Accumulator a = itr.next();
                a.scopeFinished();
             }
          }
       }

       /**
        * Provides notification that the external procedure scope has been deleted.
        * <p>
        * This is a no-op for accumulators.
        */
       @Override
       public void scopeDeleted()
       {
          // no-op
       }

       /**
        * Get the {@link ScopeId} for the instance.
        *
        * @return   {@link ScopeId#ACCUMULATOR_MANAGER}.
        */
       @Override
       public ScopeId getScopeId()
       {
          return ScopeId.ACCUMULATOR_MANAGER;
       }

       /**
        * Stores global data relating to the state of the current context.
        */
       private static class WorkArea
       {
          /** Helper to use the TM without any context local lookups. */
          private final TransactionManager.TransactionHelper tm = TransactionManager.getTransactionHelper();

          /** Set of Accumulators to be added when the next block scope opens */
          private Set<Accumulator> pendingSet = null;

          /** Stack of Accumulator sets for each open block scope */
          private Deque<Set<Accumulator>> stack = new ArrayDeque<Set<Accumulator>>();

          /**
           * Default constructor.
           */
          private WorkArea()
          {
             stack.push(new HashSet<Accumulator>(8));
          }

          /**
           * Initialize the set of pending Accumulator instances.
           */
          private void initPending()
          {
             pendingSet = new HashSet<Accumulator>(8);
          }
       }

       /**
        * Simple <code>ContextLocal</code> subclass that properly initializes the
        * context-local reference to a new instance of a case-sensitive
        * <code>ScopedSymbolDictionary</code> that has a global scope.
        */
       private static class ContextContainer
       extends ContextLocal<AccumulatorManager>
       {
          /**
           * Initializes the dictionary, the first time it is requested within a
           * new context.
           *
           * @return   The newly instantiated dictionary.
           */
          protected synchronized AccumulatorManager initialValue()
          {
             AccumulatorManager wa = new AccumulatorManager();

             return wa;
          }
       }
    }