# Block.java

    /*
    ** Module   : Block.java
    ** Abstract : base class for all delegated code block objects
    **
    ** Copyright (c) 2008-2021, Golden Code Development Corporation.
    **
    ** -#- -I- --Date-- --JPRM-- ----------------------------Description-----------------------------
    ** 001 GES 20080509   @38516 Created initial version. This is the base class for all delegated
    **                           code block objects.
    ** 002 GES 20141012          Added fini() empty method to implement 4GL compatable FINALLY block
    **                           support.
    ** 003 GES 20160830          Added pre() empty method to be called before scope opens (equivalent
    **                           to code executed in the default constructor for an anonymous
    **                           instance).
    **         20160901          Reworked this from an abstract class with no logic to a concrete
    **                           class that contains some number of lambdas, one for each of the
    **                           methods that can be executed. All valid combinations of constructor
    **                           have been created so that the converted code does not have to emit
    **                           null placeholder parameters.  The parameters are differentiated by
    **                           marker interfaces that are the equivalent to Runnable, but which are
    **                           functional interfaces in their own right. These allow the calling
    **                           code to cast each lambda to the right type allowing the matching
    **                           of parameters at compile time (no instanceof nonsense is needed).
    **                           The body should always be passed as non-null (except by a subclass
    **                           that may use the default constructor.
    ** 004 GES 20161012          Exposed the default constructor so that this can be used as an empty
    **                           block equivalent (valid in the 4GL). Added non-body constructor
    **                           variants to allow that lambda to be removed when empty (body is now
    **                           fully optional).
    ** 005 CA  20210310          Added hasFini().
    **     OM  20211111          Added transaction support for FINALLY blocks.
    */

    /*
    ** 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;

    /**
     * Base class for all code blocks.
     */
    public class Block
    {
       /** Lambda representing code to be executed as the contents of the {@link #pre} method. */
       protected Pre preMethod = null;

       /** Lambda representing code to be executed as the contents of the {@link #init} method. */
       protected Init initMethod = null;

       /** Lambda representing code to be executed as the contents of the {@link #enter} method. */
       protected Enter enterMethod = null;

       /** Lambda representing code to be executed as the contents of the {@link #body} method. */
       protected Body bodyMethod = null;

       /** Lambda representing code to be executed as the contents of the {@link #fini} method. */
       protected Fini finiMethod = null;

       /**
        * Default constructor, equivalent to an empty block (one with no code).
        */
       public Block()
       {
       }

       /**
        * Constructor.
        *
        * @param    body
        *           The code to be executed as the block's {@link #body} method.
        */
       public Block(Body body)
       {
          this(null, null, null, body, null);
       }

       /**
        * Constructor.
        *
        * @param    pre
        *           The code to be executed as the block's {@link #pre} method.
        * @param    body
        *           The code to be executed as the block's {@link #body} method.
        */
       public Block(Pre pre, Body body)
       {
          this(pre, null, null, body, null);
       }

       /**
        * Constructor.
        *
        * @param    init
        *           The code to be executed as the block's {@link #init} method.
        * @param    body
        *           The code to be executed as the block's {@link #body} method.
        */
       public Block(Init init, Body body)
       {
          this(null, init, null, body, null);
       }

       /**
        * Constructor.
        *
        * @param    enter
        *           The code to be executed as the block's {@link #enter} method.
        * @param    body
        *           The code to be executed as the block's {@link #body} method.
        */
       public Block(Enter enter, Body body)
       {
          this(null, null, enter, body, null);
       }

       /**
        * Constructor.
        *
        * @param    pre
        *           The code to be executed as the block's {@link #pre} method.
        * @param    init
        *           The code to be executed as the block's {@link #init} method.
        * @param    body
        *           The code to be executed as the block's {@link #body} method.
        */
       public Block(Pre pre, Init init, Body body)
       {
          this(pre, init, null, body, null);
       }

       /**
        * Constructor.
        *
        * @param    pre
        *           The code to be executed as the block's {@link #pre} method.
        * @param    enter
        *           The code to be executed as the block's {@link #enter} method.
        * @param    body
        *           The code to be executed as the block's {@link #body} method.
        */
       public Block(Pre pre, Enter enter, Body body)
       {
          this(pre, null, enter, body, null);
       }

       /**
        * Constructor.
        *
        * @param    init
        *           The code to be executed as the block's {@link #init} method.
        * @param    enter
        *           The code to be executed as the block's {@link #enter} method.
        * @param    body
        *           The code to be executed as the block's {@link #body} method.
        */
       public Block(Init init, Enter enter, Body body)
       {
          this(null, init, enter, body, null);
       }

       /**
        * Constructor.
        *
        * @param    pre
        *           The code to be executed as the block's {@link #pre} method.
        * @param    init
        *           The code to be executed as the block's {@link #init} method.
        * @param    enter
        *           The code to be executed as the block's {@link #enter} method.
        * @param    body
        *           The code to be executed as the block's {@link #body} method.
        */
       public Block(Pre pre, Init init, Enter enter, Body body)
       {
          this(pre, init, enter, body, null);
       }

       /**
        * Constructor.
        *
        * @param   body
        *          The code to be executed as the block's {@link #body} method.
        * @param   fini
        *          The code to be executed as the block's {@link #fini} method, in case there is no transaction
        *          in the 'finally' block.
        */
       public Block(Body body, Fini fini)
       {
          this(null, null, null, body, fini);
       }

       /**
        * Constructor.
        *
        * @param   body
        *          The code to be executed as the block's {@link #body} method.
        * @param   txFini
        *          The code to be executed as the block's {@link #fini} method, in case 'finally' block contains a
        *          full transaction.
        */
       public Block(Body body, TxFini txFini)
       {
          this(null, null, null, body, txFini);
       }

       /**
        * Constructor.
        *
        * @param   body
        *          The code to be executed as the block's {@link #body} method.
        * @param   subTxFini
        *          The code to be executed as the block's {@link #fini} method, in case 'finally' block contains a
        *          sub-transaction.
        */
       public Block(Body body, SubTxFini subTxFini)
       {
          this(null, null, null, body, subTxFini);
       }

       /**
        * Constructor.
        *
        * @param    pre
        *           The code to be executed as the block's {@link #pre} method.
        * @param    body
        *           The code to be executed as the block's {@link #body} method.
        * @param    fini
        *           The code to be executed as the block's {@link #fini} method.
        */
       public Block(Pre pre, Body body, Fini fini)
       {
          this(pre, null, null, body, fini);
       }

       /**
        * Constructor.
        *
        * @param    init
        *           The code to be executed as the block's {@link #init} method.
        * @param    body
        *           The code to be executed as the block's {@link #body} method.
        * @param    fini
        *           The code to be executed as the block's {@link #fini} method.
        */
       public Block(Init init, Body body, Fini fini)
       {
          this(null, init, null, body, fini);
       }

       /**
        * Constructor.
        *
        * @param    enter
        *           The code to be executed as the block's {@link #enter} method.
        * @param    body
        *           The code to be executed as the block's {@link #body} method.
        * @param    fini
        *           The code to be executed as the block's {@link #fini} method.
        */
       public Block(Enter enter, Body body, Fini fini)
       {
          this(null, null, enter, body, fini);
       }

       /**
        * Constructor.
        *
        * @param    pre
        *           The code to be executed as the block's {@link #pre} method.
        * @param    init
        *           The code to be executed as the block's {@link #init} method.
        * @param    body
        *           The code to be executed as the block's {@link #body} method.
        * @param    fini
        *           The code to be executed as the block's {@link #fini} method.
        */
       public Block(Pre pre, Init init, Body body, Fini fini)
       {
          this(pre, init, null, body, fini);
       }

       /**
        * Constructor.
        *
        * @param    pre
        *           The code to be executed as the block's {@link #pre} method.
        * @param    enter
        *           The code to be executed as the block's {@link #enter} method.
        * @param    body
        *           The code to be executed as the block's {@link #body} method.
        * @param    fini
        *           The code to be executed as the block's {@link #fini} method.
        */
       public Block(Pre pre, Enter enter, Body body, Fini fini)
       {
          this(pre, null, enter, body, fini);
       }

       /**
        * Constructor.
        *
        * @param    init
        *           The code to be executed as the block's {@link #init} method.
        * @param    enter
        *           The code to be executed as the block's {@link #enter} method.
        * @param    body
        *           The code to be executed as the block's {@link #body} method.
        * @param    fini
        *           The code to be executed as the block's {@link #fini} method.
        */
       public Block(Init init, Enter enter, Body body, Fini fini)
       {
          this(null, init, enter, body, fini);
       }

       /**
        * Constructor.
        *
        * @param    pre
        *           The code to be executed as the block's {@link #pre} method.
        */
       public Block(Pre pre)
       {
          this(pre, null, null, null, null);
       }

       /**
        * Constructor.
        *
        * @param    init
        *           The code to be executed as the block's {@link #init} method.
        */
       public Block(Init init)
       {
          this(null, init, null, null, null);
       }

       /**
        * Constructor.
        *
        * @param    enter
        *           The code to be executed as the block's {@link #enter} method.
        */
       public Block(Enter enter)
       {
          this(null, null, enter, null, null);
       }

       /**
        * Constructor.
        *
        * @param    pre
        *           The code to be executed as the block's {@link #pre} method.
        * @param    init
        *           The code to be executed as the block's {@link #init} method.
        */
       public Block(Pre pre, Init init)
       {
          this(pre, init, null, null, null);
       }

       /**
        * Constructor.
        *
        * @param    pre
        *           The code to be executed as the block's {@link #pre} method.
        * @param    enter
        *           The code to be executed as the block's {@link #enter} method.
        */
       public Block(Pre pre, Enter enter)
       {
          this(pre, null, enter, null, null);
       }

       /**
        * Constructor.
        *
        * @param    init
        *           The code to be executed as the block's {@link #init} method.
        * @param    enter
        *           The code to be executed as the block's {@link #enter} method.
        */
       public Block(Init init, Enter enter)
       {
          this(null, init, enter, null, null);
       }

       /**
        * Constructor.
        *
        * @param    pre
        *           The code to be executed as the block's {@link #pre} method.
        * @param    init
        *           The code to be executed as the block's {@link #init} method.
        * @param    enter
        *           The code to be executed as the block's {@link #enter} method.
        */
       public Block(Pre pre, Init init, Enter enter)
       {
          this(pre, init, enter, null, null);
       }

       /**
        * Constructor.
        *
        * @param    fini
        *           The code to be executed as the block's {@link #fini} method.
        */
       public Block(Fini fini)
       {
          this(null, null, null, null, fini);
       }

       /**
        * Constructor.
        *
        * @param    pre
        *           The code to be executed as the block's {@link #pre} method.
        * @param    fini
        *           The code to be executed as the block's {@link #fini} method.
        */
       public Block(Pre pre, Fini fini)
       {
          this(pre, null, null, null, fini);
       }

       /**
        * Constructor.
        *
        * @param    init
        *           The code to be executed as the block's {@link #init} method.
        * @param    fini
        *           The code to be executed as the block's {@link #fini} method.
        */
       public Block(Init init, Fini fini)
       {
          this(null, init, null, null, fini);
       }

       /**
        * Constructor.
        *
        * @param    enter
        *           The code to be executed as the block's {@link #enter} method.
        * @param    fini
        *           The code to be executed as the block's {@link #fini} method.
        */
       public Block(Enter enter, Fini fini)
       {
          this(null, null, enter, null, fini);
       }

       /**
        * Constructor.
        *
        * @param    pre
        *           The code to be executed as the block's {@link #pre} method.
        * @param    init
        *           The code to be executed as the block's {@link #init} method.
        * @param    fini
        *           The code to be executed as the block's {@link #fini} method.
        */
       public Block(Pre pre, Init init, Fini fini)
       {
          this(pre, init, null, null, fini);
       }

       /**
        * Constructor.
        *
        * @param    pre
        *           The code to be executed as the block's {@link #pre} method.
        * @param    enter
        *           The code to be executed as the block's {@link #enter} method.
        * @param    fini
        *           The code to be executed as the block's {@link #fini} method.
        */
       public Block(Pre pre, Enter enter, Fini fini)
       {
          this(pre, null, enter, null, fini);
       }

       /**
        * Constructor.
        *
        * @param    init
        *           The code to be executed as the block's {@link #init} method.
        * @param    enter
        *           The code to be executed as the block's {@link #enter} method.
        * @param    fini
        *           The code to be executed as the block's {@link #fini} method.
        */
       public Block(Init init, Enter enter, Fini fini)
       {
          this(null, init, enter, null, fini);
       }

       /**
        * Constructor.
        *
        * @param    pre
        *           The code to be executed as the block's {@link #pre} method.
        * @param    init
        *           The code to be executed as the block's {@link #init} method.
        * @param    enter
        *           The code to be executed as the block's {@link #enter} method.
        * @param    body
        *           The code to be executed as the block's {@link #body} method.
        * @param    fini
        *           The code to be executed as the block's {@link #fini} method.
        */
       public Block(Pre pre, Init init, Enter enter, Body body, Fini fini)
       {
          preMethod   = pre;
          initMethod  = init;
          enterMethod = enter;
          bodyMethod  = body;
          finiMethod  = fini;
       }

       /**
        * Provides a callback to initialize state ONCE before the body gets executed.  This is
        * guaranteed to be called BEFORE the transaction manager scope opens. It will only be
        * called once no matter whether the block iterates or whether there is a retry.  By default
        * this implementation does nothing.  Override it in subclasses to get support.
        */
       public void pre()
       {
          if (preMethod != null)
          {
             preMethod.pre();
          }
       }

       /**
        * Provides a callback to initialize state ONCE before the body gets executed.  This is
        * guaranteed to be called AFTER the transaction manager scope opens but BEFORE the block
        * body is ever entered. It will only be called once no matter whether the block iterates
        * or whether there is a retry.  By default this implementation does nothing.  Override
        * it in subclasses to get support.
        */
       public void init()
       {
          if (initMethod != null)
          {
             initMethod.init();
          }
       }

       /**
        * Provides a callback to execute user-defined logic at the top of the block body but before
        * the block body is executed. This is guaranteed to be called AFTER the transaction manager
        * scope opens but BEFORE the block body is entered. It will be called once per iteration of
        * the block or once if the block is not iterating.  It WILL be called on retry. By default
        * this implementation does nothing.  Override it in subclasses to get support.
        */
       public void enter()
       {
          if (enterMethod != null)
          {
             enterMethod.enter();
          }
       }

       /**
        * Provides a callback that is guaranteed to be called AFTER every block executes and
        * BEFORE the body can iterated, retried or exited. This is the equivalent to the 4GL
        * FINALLY block.  By default this implementation does nothing.  Override it in subclasses
        * to get support.
        */
       public void fini()
       {
          if (finiMethod != null)
          {
             finiMethod.fini();
          }
       }

       /**
        * This implements the delegated code to be executed as part of this instance.
        */
       public void body()
       {
          if (bodyMethod != null)
          {
             bodyMethod.body();
          }
       }

       /**
        * Check if the {@link #finiMethod} exists.
        *
        * @return  {@code true} if {@code #finiMethod} is set.
        */
       final boolean hasFini()
       {
          return finiMethod != null;
       }

       /**
        * Check whether the {@link #finiMethod} exists and is a transaction.
        *
        * @return  {@code true} if the {@code #finiMethod} exists and is a transaction.
        */
       final boolean hasTxFini()
       {
          return finiMethod != null && finiMethod instanceof TxFini;
       }

       /**
        * Check whether the {@link #finiMethod} exists and is a sub-transaction.
        *
        * @return  {@code true} if the {@code #finiMethod} exists and is a sub-transaction.
        */
       final boolean hasSubTxFini()
       {
          return finiMethod != null && finiMethod instanceof SubTxFini;
       }
    }