RuleContainer.java

/*
** Module   : RuleContainer.java
** Abstract : Abstract class to manage a list of rules
**
** Copyright (c) 2005-2023, Golden Code Development Corporation.
**
** -#- -I- --Date-- --JPRM-- ---------------------------------Description-------------------------------------
** 001 ECF 20050228   @20141 Created initial version. Manages a list of
**                           rules.
** 002 ECF 20050310   @20265 Moved expression library support from
**                           RuleSet subclass. This permits other
**                           subclasses to take advantage of this support
**                           also.
** 003 ECF 20050310   @20277 Removed language references. This feature set
**                           was obsoleted by a change to the expression
**                           parser which converts language token names
**                           to token types before expression compilation.
** 004 ECF 20050314   @20330 Added user variable support. A pool of
**                           variables is maintained within the scope of
**                           this rule container. The user may read/write
**                           these variables from logical expressions in
**                           rules contained directly or indirectly within
**                           this container.
** 005 ECF 20050315   @20409 Added constants for descent and ascent rule
**                           types.
** 006 GES 20050428   @20917 Added constant for next-child rule type.
** 007 ECF 20050429   @20962 Changes related to namespace support for user
**                           functions and variables. Moved pattern worker
**                           alias registration and management from the
**                           pattern engine. Only namespace aliases are
**                           managed by this class. The pattern workers
**                           themselves are managed by AstSymbolResolver.
**                           References to them are stored here, mapped by
**                           namespace alias.
** 008 ECF 20050526   @21294 Changes to support new expression engine
**                           implementation. Pattern worker and variable
**                           registration was changed significantly. Also
**                           added a static debugLevel construct for debug
**                           reporting.
** 009 GES 20050601   @21357 Converted the expression library from storing
**                           an infix string to storing a NamedExpression
**                           instance.
** 010 GES 20050602   @21382 Changes to implement the RuleListElement
**                           interface, to migrate the list of rules to
**                           a list of rules/rule containers, removal
**                           of the custom iterator processing and
**                           replacement with a recursive RuleListElement
**                           processing that allows containers to be
**                           arbitrarily nested. Some processing was
**                           moved here from PatternEngine to make the
**                           nesting possible.
** 011 GES 20050602   @21388 Converted NamedExpression to NamedFunction.
** 012 ECF 20050604   @21417 Create expressions using AstSymbolResolver's
**                           createExpression method. Use new Rule
**                           constructor.
** 013 ECF 20050607   @21432 Fixed stack overflow in getFunctionContainer
**                           method. Added the 'omit' parameter to prevent
**                           infinite loop caused by circular includes or
**                           by parent and child rule containers including
**                           the same entity. See also H014 (@21433).
** 014 ECF 20050607   @21433 The fix in H013 (@21432) will only prevent
**                           stack overflow due to circular includes with
**                           one degree of separation. This should cover
**                           the majority of (if not all) cases. If we
**                           find includes which are circular with greater
**                           degrees of separation, a more complex
**                           mechanism will need to be implemented.
** 015 GES 20060128   @24132 Exposed debug level and the related constants 
**                           as public.
** 016 GES 20070514   @33495 Moved debug level constants into a separate 
**                           interface.
** 017 SIY 20070612   @34022 Prevent direct/indirect recursion during
**                           calls to getFunctionContainer().
** 018 ECF 20080730   @39267 Reduced memory footprint. Lowered default
**                           sizes of all collections. Implemented cleanup
**                           method to release resources.
** 019 GES 20130203          Moved to using an Initializer class to allow a
**                           simple instance to be assigned to a new variable
**                           in addition to the previous expression approach.
** 020 ECF 20140403          Implemented generics.
** 021 HC  20160907          Implemented a simple tracing support for rules processing.
** 022 ECF 20171228          Improve performance by reducing context-local lookups.
** 023 ECF 20190620          EmptyIterator API change.
** 024 CA  20221010          Avoid using iterators on ArrayList - instead, use a 'for' loop and get the 
**                           element by index, as iterators are more expensive in terms of memory and 
**                           performance.  Refs #6813
**     TJD 20220504          Upgrade do Java 11 minor changes
** 025 CA  20230531          Moved the scope state from the resolver to the actual scope; this avoids using
**                           a WeakHashMap to determine the scope's state associated with a certain resolver,
**                           as long as a single resolver instance uses that scope (common for conversion).
** 026 CA  20230712          Performance improvement - cache the function's 'winner' container.
*/
/*
** 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.pattern;

import java.util.*;
import com.goldencode.expr.*;
import com.goldencode.util.*;

/**
 * Abstract base class for concrete implementations of rule containers.
 * This class provides shared implementation logic for managing and
 * applying a list of rules, for managing a library of named expressions,
 * and for registering pattern workers to their associated namespaces.
 * A rule container defines an expression engine scope for the purpose of
 * resolving pattern worker libraries and user variables at expression
 * parsing and compilation time. An instance may have an enclosing scope
 * as well.
 * <p>
 * This implementation stores all rule types in a single list. A specialized
 * iterator is used to visit only the types of rules required for a particular
 * iteration (see {@link #rules}). This iterator is not accessed directly by
 * client code. Instead, a concrete subclass will call the {@link #apply}
 * method to iterate over the target set of rules and apply them in turn.
 * <p>
 * Named expressions support associates a library of aliased expression
 * strings with a list of rules, such that any individual rule can access a
 * pre-defined expression by its alias.
 * <p>
 * A pool of readable and writable user variables is registered with the
 * expression engine, for use in rules contained directly or indirectly
 * within this container. User variables are accessed directly by variable
 * name in expressions. Duplicate variable names are not permitted.
 * <p>
 * The level of message output generated by the pattern engine as a whole
 * is maintained by this class. It is accessible via static methods.
 *
 * @see  #getDebugLevel
 * @see  #setDebugLevel
 */
abstract class RuleContainer
extends Scope
implements RuleListElement,
           DebugLevels
{
   /** Constant indicating no rule type */
   protected static final int RULE_NONE = 0;
   
   /** Constant indicating init-rule type */
   protected static final int RULE_INIT = 1;
   
   /** Constant indicating walk-rule type */
   protected static final int RULE_WALK = 2;
   
   /** Constant indicating post-rule type */
   protected static final int RULE_POST = 3;
   
   /** Constant indicating descent-rule type */
   protected static final int RULE_DESCENT = 4;
   
   /** Constant indicating next-child-rule type */
   protected static final int RULE_NEXT_CHILD = 5;
   
   /** Constant indicating ascent-rule type */
   protected static final int RULE_ASCENT = 6;
   
   /** Current debug level */
   private static int debugLevel = MSG_NONE;
   
   /** Pattern workers indexed by namespace alias */
   private Map<String, PatternWorker> namespaces = null;
   
   /** Parent container which provides enclosing scope */
   private RuleContainer parent = null;
   
   /** List of all rules of all types */
   private ArrayList<RuleListElement> rules = null;
   
   /** Rule containers included for their expression libraries */
   private Set<RuleContainer> includes = null;
   
   /** Library of named functions associated with this ruleset */
   private Map<String, NamedFunction> functionLibrary = null;
   
   /** Flag to prevent direct or indirect recursion during search. */
   private boolean processingGetFunctionContainer = false;
   
   /** Cache of the resolved container for a function name. */
   private Map<String, RuleContainer> winners = new HashMap<>();
   
   /**
    * Constructor which assigns this container's enclosing scope.
    *
    * @param   parent
    *          Rule container which provides enclosing scope to this rule
    *          container. May be <code>null</code>.
    */
   protected RuleContainer(RuleContainer parent)
   {
      this.parent = parent;
   }
   
   /**
    * Return the current debug level for the process.
    *
    * @return  Debug level.
    */
   public static int getDebugLevel()
   {
      return debugLevel;
   }
   
   /**
    * Set the current debug level for the process.
    *
    * @param   debugLevel
    *          Debug message output level using <code>MSG_*</code>
    *          constants.
    */
   public static void setDebugLevel(int debugLevel)
   {
      RuleContainer.debugLevel = debugLevel;
   }
   
   /**
    * Return the object which defines the enclosing scope to this rule
    * container's scope.
    *
    * @return  This container's parent scope. May be <code>null</code>.
    */
   public Scope getEnclosingScope()
   {
      return parent;
   }
   
   /**
    * Register a new user variable with this container. If the variable's
    * name is already in use within this rule container's scope, it cannot
    * be re-used and this method will fail. If both <code>type</code> and
    * <code>expression</code> are provided, <code>type</code> must match the
    * return type of <code>expression</code>.
    *
    * @param   name
    *          Name by which this variable will be referenced in expressions.
    * @param   type
    *          Data type of the variable. May be <code>null</code> if and
    *          only if <code>expression</code> is not <code>null</code>
    *          <b>and</b> <code>expression</code>'s return type is not
    *          <code>null</code>.
    * @param   expression
    *          Expression which will be used to initialize the variable each
    *          time it is {@link com.goldencode.expr.Variable#reset reset}
    *          by the pattern engine. May be <code>null</code> if and only
    *          if <code>type</code> is not <code>null</code>.
    *
    * @throws  SymbolException
    *          if the variable's name is already in use in this container's
    *          scope.
    * @throws  ExpressionException
    *          if <code>type</code> is a class which is not assignable from
    *          <code>expression</code>'s return type.
    * @throws  IllegalArgumentException
    *          if <code>type</code> is not provided and it cannot be
    *          determined from <code>expression</code>.
    *
    * @see     com.goldencode.expr.SymbolResolver#resetVariables
    */
   public void registerVariable(String name, Class<?> type, String expression)
   {
      ExpressionInitializer ei = null;
      
      if (expression != null)
      {
         Expression expr = AstSymbolResolver.createExpression(expression);
         ei = new ExpressionInitializer(expr);
      }
      
      AstSymbolResolver resolver = AstSymbolResolver.getResolver();
      resolver.registerVariable(this, name, type, ei, false);
   }
   
   /**
    * Register a new user variable with this container. If the variable's
    * name is already in use within this rule container's scope, it cannot
    * be re-used and this method will fail. If both <code>type</code> and
    * <code>value</code> are provided, <code>type</code> must match the
    * return type of <code>expression</code>.
    *
    * @param   name
    *          Name by which this variable will be referenced in expressions.
    * @param   type
    *          Data type of the variable. May be <code>null</code> if and
    *          only if <code>value</code> is not <code>null</code>.
    * @param   value
    *          The specific instance to which the variable will be
    *          will be initialized.  Unless assigned explicitly from a rule 
    *          set, this instance will not be set to <code>null</code>
    *          using {@link com.goldencode.expr.Variable#reset reset} in the
    *          pattern engine on subsequent runs. May be <code>null</code> if
    *          and only if <code>type</code> is not <code>null</code>.
    *
    * @throws  SymbolException
    *          if the variable's name is already in use in this container's
    *          scope.
    * @throws  IllegalArgumentException
    *          if <code>type</code> is not provided and it cannot be
    *          determined from <code>expression</code>.
    *
    * @see     com.goldencode.expr.SymbolResolver#resetVariables
    */
   public void registerVariableValue(String name, Class<?> type, Object value)
   {
      SimpleInitializer si = (value == null) ? null : new SimpleInitializer(value); 
      
      AstSymbolResolver resolver = AstSymbolResolver.getResolver();
      resolver.registerVariable(this, name, type, si, false);
   }
   
   /**
    * Store a pre-defined function under the specified name. The name acts
    * as an alias for the function, which can be embedded in the
    * <code>evalLib</code> or <code>execLib</code> user function to reference
    * the named function in rules.
    * <p>
    * Note that if any named function already exists in the current library
    * for this container with the alias <code>name</code> at the time this
    * method is invoked, it will be replaced by <code>func</code>.
    *
    * @param   name
    *          Alias under which to store the function.
    * @param   func
    *          The associated named function.
    */
   public void putFunction(String name, NamedFunction func)
   {
      if (functionLibrary == null)
      {
         functionLibrary = new HashMap<>(4);
      }
      
      functionLibrary.put(name, func);
   }
   
   /**
    * Register a pattern worker implementation with this rule container,
    * such that its methods will be called by the engine at the appropriate
    * times. Calls the pattern worker's {@link PatternWorker#initialize}
    * method.
    * <p>
    * If an instance of the same pattern worker class is already registered,
    * <code>worker</code> is discarded, and the previously registered
    * instance is associated with <code>name</code> instead.
    *
    * @param   name
    *          Optional namespace which can be used as a qualifier in
    *          expressions to indicate that this pattern worker should be
    *          used to resolve a constant to a literal. Should be
    *          non-<code>null</code> for any pattern worker implementation
    *          which implements {@link PatternWorker#resolveConstant}
    *          directly; may be <code>null</code> otherwise, however, only
    *          one <code>null</code> name is allowed per rule container.
    * @param   worker
    *          The pattern worker object to register.
    *
    * @return  <code>true</code> if the worker was successfully registered;
    *          <code>false</code> if a worker of the same type was already
    *          registered. In either case, <code>namespace</code> is mapped
    *          to the registered worker upon return from this method.
    *
    * @throws  IllegalArgumentException
    *          if <code>name</code> is already in use or if it is an invalid
    *          namespace identifier.
    */
   public boolean registerWorker(String name, PatternWorker worker)
   {
      // Map worker to its namespace, if provided.
      if (name != null)
      {
         name = name.trim();
         
         // Make sure namespace is not all whitespace.
         if (name.length() == 0)
         {
            throw new IllegalArgumentException(
               "'" + name + "' is not a valid pattern worker namespace");
         }
         
         // Make sure this namespace is available.
         if (namespaces != null && namespaces.containsKey(name))
         {
            throw new IllegalArgumentException(
               "Pattern worker namespace '" + name + "' cannot be used more than once");
         }
      }
      
      // If a worker of this type was already registered, that instance
      // will be returned by the following call. Otherwise, our instance
      // will be registered and returned to us.
      AstSymbolResolver resolver = AstSymbolResolver.getResolver();
      PatternWorker registered = resolver.registerWorker(worker);
      
      // Map the registered worker to the given namespace.
      if (name != null)
      {
         if (namespaces == null)
         {
            namespaces = new HashMap<>(4);
         }
         namespaces.put(name, registered);
      }
      
      // Register the worker library (if any) defined for this pattern
      // worker, within the scope of this rule container.
      Object library = registered.getLibrary();
      if (library != null)
      {
         resolver.registerLibrary(this, name, library);
      }
      
      return (registered == worker);
   }
   
   /**
    * Add a new, empty ruleset to the end of the current pipeline. Rulesets
    * are processed in the order in which they are added to the pipeline.
    * The ruleset is returned in order that the caller may configure it.
    *
    * @return  Newly created ruleset.
    */
   public RuleSet addRuleSet()
   {
      if (rules == null)
      {
         rules = new ArrayList<>(1);
      }
      
      RuleSet ruleSet = new RuleSet(this);
      rules.add(ruleSet);
      
      return ruleSet;
   }
   
   /**
    * Add a rule of the specified type to the container for later use.
    *
    * @param   expr
    *          Logical expression which will be compiled into the condition
    *          expression of the rule.
    * @param   type
    *          Type of rule to add: init, walk, post, etc., using the
    *          <code>RULE_*</code> constants.
    * @param   file
    *          The file the rule is defined in, used for tracing.
    * @param   line
    *          The file line the rule is defined at, used for tracing.
    *
    * @throws  IllegalArgumentException
    *          if the rule type is unrecognized.
    */
   public Rule addRule(String expr, int type, String file, int line)
   {
      switch (type)
      {
         case RULE_WALK:
         case RULE_INIT:
         case RULE_POST:
         case RULE_DESCENT:
         case RULE_NEXT_CHILD:
         case RULE_ASCENT:
            break;
         default:
            throw new IllegalArgumentException("Invalid rule type: " + type);
      }
      
      if (rules == null)
      {
         rules = new ArrayList<>(1);
      }
      
      Rule rule = new Rule(expr, type, file, line);
      rules.add(rule);
      
      return rule;
   }
   
   /**
    * Return an iterator to the list of <code>RuleListElements</code>, each
    * element of which can be either a <code>Rule</code> or a
    * <code>RuleContainer</code> instance.
    */
   public final ArrayList<RuleListElement> ruleList()
   {
      return rules;
   }
   
   /**
    * Termination hook to allow resources to be cleaned up when this object is
    * about to go out of service.
    * <p>
    * Cascades to all contained <code>RuleListElements</code>.
    */
   public void cleanup()
   {
      if (rules != null)
      {
         int size = rules.size();
         RuleListElement elem;
         for (int i = 0; i < size; i++)
         {
            elem = rules.get(i);
            elem.cleanup();
         }
      }
      
      if (functionLibrary != null)
      {
         Iterator<NamedFunction> iter2 = functionLibrary.values().iterator();
         while (iter2.hasNext())
         {
            NamedFunction func = iter2.next();
            func.cleanup();
         }
      }
      
      rules = null;
      functionLibrary = null;
      includes = null;
      namespaces = null;
      parent = null;
   }
   
   /**
    * Apply every rule of the specified type, in the order they were added
    * to this container.
    * 
    * @param   resolver
    *          AST symbol resolver.
    * @param   type
    *          Type of rule to apply: init, walk, or post, using the
    *          <code>RULE_*</code> constants.
    */
   public void apply(AstSymbolResolver resolver, int type)
   {
      // save the current scope and reset the scope in the resolver to ourself
      RuleContainer old = resolver.getRuleScope();
      resolver.setRuleScope(this);
      
      if (rules != null)
      {
         int size = rules.size();
         RuleListElement next;
         for (int i = 0; i < size; i++)
         {
            next = rules.get(i);
            next.apply(resolver, type);
         }
      }
      
      // reset the resolvers scope to the original
      resolver.setRuleScope(old);
   }
   
   /**
    * Reports that this instance is a rule container.
    *
    * @return  Always <code>true</code>.
    */
   public boolean isContainer()
   {
      return true;
   }
   
   /**
    * Indicates whether this object's internal list of rules contains any
    * rules of the specified type.
    *
    * @param   type
    *          Type of rule for which to search: init, walk, or post, using
    *          the <code>RULE_*</code> constants.
    *
    * @return  <code>true</code> if the list of rules contains any rule of
    *          the specified type, else <code>false</code>.
    */
   public boolean hasType(int type)
   {
      if (rules == null)
      {
         return false;
      }
      
      int size = rules.size();
      RuleListElement next;
      for (int i = 0; i < size; i++)
      {
         next = rules.get(i);
         
         if (next.hasType(type))
         {
            return true;
         }
      }
      
      return false; 
   }
   
   /**
    * Instantiate and register a pattern worker implementation, given its
    * class name. The target class' default constructor is used. This results
    * in the registered worker's {@link PatternWorker#initialize} method
    * being invoked immediately after construction.
    * <p>
    * If an instance of the specified class is already registered, this
    * request is ignored. There can be only one.
    *
    * @param   name
    *          Optional namespace which can be used as a qualifier in
    *          expressions to indicate that this pattern worker should be
    *          used to resolve a constant to a literal. Should be
    *          non-<code>null</code> for any pattern worker implementation
    *          which implements {@link PatternWorker#resolveConstant}
    *          directly; may be <code>null</code> otherwise.
    * @param   className
    *          Fully qualified name of the pattern worker's class.
    *
    * @throws  ClassNotFoundException
    *          if a class named <code>className</code> is not found in the
    *          JVM's classpath.
    * @throws  InstantiationException
    *          if an error occurs while instantiating the class (e.g., the
    *          default constructor throws an exception).
    * @throws  IllegalAccessException
    *          if the target class or its default constructor is not
    *          accessible.
    * @throws  ClassCastException
    *          if the target class does not implement the {@link
    *          PatternWorker} interface.
    */
   boolean registerWorker(String name, String className)
   throws ReflectiveOperationException
   {
      AstSymbolResolver resolver = AstSymbolResolver.getResolver();
      PatternWorker worker = resolver.getRegisteredWorker(className);
      if (worker == null)
      {
         worker = (PatternWorker) Class.forName(className).getDeclaredConstructor().newInstance();
      }
      
      return registerWorker(name, worker);
   }
   
   /**
    * Retrieve the pattern worker, if any, registered for the given
    * namespace.
    *
    * @param   name
    *          Namespace identifier with which the requested pattern worker
    *          is associated. May not be <code>null</code>.
    *
    * @return  The requested pattern worker instance, or <code>null</code>
    *          if no match was found.
    */
   PatternWorker getWorker(String name)
   {
      if (name == null || namespaces == null)
      {
         return null;
      }
      
      return (PatternWorker) namespaces.get(name);
   }
   
   /**
    * Add a rule container to this container's set of includes.
    *
    * @param   included
    *          Rule container to include.
    */
   void include(RuleContainer included)
   {
      if (includes == null)
      {
         includes = new LinkedHashSet<>(4);
      }
      
      includes.add(included);
   }
   
   /**
    * Get a named function, given the alias which is used to uniquely
    * identify that function.
    *
    * @param   name
    *          Alias which identifies the function.
    *
    * @return  The function aliased by <code>name</code>, or
    *          <code>null</code> if no function is found.
    */
   NamedFunction getFunction(String name)
   {
      if (functionLibrary == null)
      {
         return null;
      }
      
      return (NamedFunction) functionLibrary.get(name);
   }
   
   /**
    * Get the container instance which contains the function named
    * <code>name</code> in its function library. This may be this container
    * or one that this container includes, or <code>null</code>.
    *
    * @param   name
    *          Name of function to look up.
    * @param   omit
    *          RuleContainer instance to omit from the list of includes to
    *          check. This prevents circular references between parents and
    *          included containers, or between included containers themselves.
    *
    * @return  This container or one of its included containers, or
    *          <code>null</code> if neither this container nor any it
    *          includes can resolve the given name to an function.
    */
   RuleContainer getFunctionContainer(String name, RuleContainer omit)
   {
      if (processingGetFunctionContainer)
      {
         return null;
      }
      
      try
      {
         processingGetFunctionContainer = true;
         
         // Is this function library part of this current container?
         if (functionLibrary != null && functionLibrary.containsKey(name))
         {
            return this;
         }
         
         RuleContainer winner = winners.get(name);
         if (winner != null)
         {
            return winner;
         }
         
         // Perhaps it resides in one of our includes?
         if (includes != null)
         {
            Iterator<RuleContainer> iter = includes.iterator();
            while (winner == null && iter.hasNext())
            {
               RuleContainer next = iter.next();
               
               if (next != omit)
               {
                  winner = next.getFunctionContainer(name, this);
               }
            }
         }
         
         // Check our parent, recursively until the top of the tree is
         // reached or the named function is found. Omit ourselves from our
         // parent's check of its includes.
         if (winner == null && parent != null)
         {
            winner = parent.getFunctionContainer(name, this);
         }
         
         winners.put(name, winner);
         
         return winner;
      }
      finally
      {
         processingGetFunctionContainer = false;
      }
   }   
}