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