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;
   }
}