OutputExtentParameter.java
/*
** Module : OutputExtentParameter.java
** Abstract : Special wrapper used for variables passed as output extent parameters to
** functions or procedures.
**
** Copyright (c) 2013-2021, Golden Code Development Corporation.
**
** -#- -I- --Date-- ---------------------------------------Description----------------------------------------
** 001 CA 20130209 Created initial version.
** 002 CA 20131013 Added no-op scopeDeleted() method, required by the changes in Scopeable
** interface.
** 003 GES 20140122 Added safety code to avoid abends when silent errors occur.
** 004 GES 20140206 Added safety code to avoid abends when the persistence layer is not active.
** 005 HC 20140413 Fixed extent parameter initialization.
** 006 HC 20140613 Improved extent parameter support.
** 007 OM 20150706 Adjusted error management in assign().
** 008 CA 20190720 Fixed processing of dynamic extent 'object' parameters.
** 009 CA 20210609 Reworked INPUT/INPUT-OUTPUT parameters to a new approach, where they are explicitly
** initialized at the method's execution, and not at the caller's arguments.
** CA 20210818 Fixed memptr parameter runtime - they share the 'pointer structure' with the passed
** argument.
*/
/*
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU Affero General Public License as
** published by the Free Software Foundation, either version 3 of the
** License, or (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU Affero General Public License for more details.
**
** You may find a copy of the GNU Affero GPL version 3 at the following
** location: https://www.gnu.org/licenses/agpl-3.0.en.html
**
** Additional terms under GNU Affero GPL version 3 section 7:
**
** Under Section 7 of the GNU Affero GPL version 3, the following additional
** terms apply to the works covered under the License. These additional terms
** are non-permissive additional terms allowed under Section 7 of the GNU
** Affero GPL version 3 and may not be removed by you.
**
** 0. Attribution Requirement.
**
** You must preserve all legal notices or author attributions in the covered
** work or Appropriate Legal Notices displayed by works containing the covered
** work. You may not remove from the covered work any author or developer
** credit already included within the covered work.
**
** 1. No License To Use Trademarks.
**
** This license does not grant any license or rights to use the trademarks
** Golden Code, FWD, any Golden Code or FWD logo, or any other trademarks
** of Golden Code Development Corporation. You are not authorized to use the
** name Golden Code, FWD, or the names of any author or contributor, for
** publicity purposes without written authorization.
**
** 2. No Misrepresentation of Affiliation.
**
** You may not represent yourself as Golden Code Development Corporation or FWD.
**
** You may not represent yourself for publicity purposes as associated with
** Golden Code Development Corporation, FWD, or any author or contributor to
** the covered work, without written authorization.
**
** 3. No Misrepresentation of Source or Origin.
**
** You may not represent the covered work as solely your work. All modified
** versions of the covered work must be marked in a reasonable way to make it
** clear that the modified work is not originating from Golden Code Development
** Corporation or FWD. All modified versions must contain the notices of
** attribution required in this license.
*/
package com.goldencode.p2j.util;
import java.lang.reflect.*;
import com.goldencode.p2j.oo.lang.*;
/**
* Special wrapper used for variables passed as output extent parameters to functions
* or procedures. On normal function and procedure exit, if the reference for an output
* or input-output extent parameter has changed, this will update the caller's variable
* reference too.
*/
public abstract class OutputExtentParameter<T extends BaseDataType>
extends AbstractExtentParameter<T>
{
/**
* The constructor responsible for instance initialization by calling
* {@link AbstractExtentParameter#AbstractExtentParameter(BaseDataType[])}.
*/
public OutputExtentParameter()
{
super(null);
}
/**
* This constructor allows to provide additional initialization logic of the variable
* reference in the extending classes. The initialization logic is encapsulated in
* {@link AbstractExtentParameter#AbstractExtentParameter(BaseDataType[])}.
*
* @param variable
* A valid and initialized variable reference.
*/
public OutputExtentParameter(T[] variable)
{
super(variable);
}
/**
* Copy the input array into an output array, using the output type constructor for each non-null
* element.
*
* @param input
* The input array.
*
* @return The copy of the input array, with the elements converted to the output data type.
*/
public static integer[] toInteger(int64[] input)
{
integer[] output = new integer[input.length];
for (int i = 0; i < output.length; i++)
{
if (input[i] != null)
{
output[i] = new integer(input[i]);
}
}
return output;
}
/**
* Copy the input array into an output array, using the output type constructor for each non-null
* element.
*
* @param input
* The input array.
*
* @return The copy of the input array, with the elements converted to the output data type.
*/
public static integer[] toInteger(decimal[] input)
{
integer[] output = new integer[input.length];
for (int i = 0; i < output.length; i++)
{
if (input[i] != null)
{
output[i] = new integer(input[i]);
}
}
return output;
}
/**
* Copy the input array into an output array, using the output type constructor for each non-null
* element.
*
* @param input
* The input array.
*
* @return The copy of the input array, with the elements converted to the output data type.
*/
public static decimal[] toDecimal(integer[] input)
{
decimal[] output = new decimal[input.length];
for (int i = 0; i < output.length; i++)
{
if (input[i] != null)
{
output[i] = new decimal(input[i]);
}
}
return output;
}
/**
* Copy the input array into an output array, using the output type constructor for each non-null
* element.
*
* @param input
* The input array.
*
* @return The copy of the input array, with the elements converted to the output data type.
*/
public static decimal[] toDecimal(int64[] input)
{
decimal[] output = new decimal[input.length];
for (int i = 0; i < output.length; i++)
{
if (input[i] != null)
{
output[i] = new decimal(input[i]);
}
}
return output;
}
/**
* Copy the input array into an output array, using the output type constructor for each non-null
* element.
*
* @param input
* The input array.
*
* @return The copy of the input array, with the elements converted to the output data type.
*/
public static int64[] toInt64(integer[] input)
{
int64[] output = new int64[input.length];
for (int i = 0; i < output.length; i++)
{
if (input[i] != null)
{
output[i] = new int64(input[i]);
}
}
return output;
}
/**
* Copy the input array into an output array, using the output type constructor for each non-null
* element.
*
* @param input
* The input array.
*
* @return The copy of the input array, with the elements converted to the output data type.
*/
public static int64[] toInt64(decimal[] input)
{
int64[] output = new int64[input.length];
for (int i = 0; i < output.length; i++)
{
if (input[i] != null)
{
output[i] = new int64(input[i]);
}
}
return output;
}
/**
* Copy the input array into an output array, using the output type constructor for each non-null
* element.
*
* @param input
* The input array.
*
* @return The copy of the input array, with the elements converted to the output data type.
*/
public static character[] toCharacter(longchar[] input)
{
character[] output = new character[input.length];
for (int i = 0; i < output.length; i++)
{
if (input[i] != null)
{
output[i] = new character(input[i]);
}
}
return output;
}
/**
* Copy the input array into an output array, using the output type constructor for each non-null
* element.
*
* @param input
* The input array.
*
* @return The copy of the input array, with the elements converted to the output data type.
*/
public static longchar[] toLongchar(character[] input)
{
longchar[] output = new longchar[input.length];
for (int i = 0; i < output.length; i++)
{
if (input[i] != null)
{
output[i] = new longchar(input[i]);
}
}
return output;
}
/**
* Copy the input array into an output array, using the output type constructor for each non-null
* element.
*
* @param input
* The input array.
*
* @return The copy of the input array, with the elements converted to the output data type.
*/
public static date[] toDate(datetime[] input)
{
date[] output = new date[input.length];
for (int i = 0; i < output.length; i++)
{
if (input[i] != null)
{
output[i] = new date(input[i]);
}
}
return output;
}
/**
* Copy the input array into an output array, using the output type constructor for each non-null
* element.
*
* @param input
* The input array.
*
* @return The copy of the input array, with the elements converted to the output data type.
*/
public static date[] toDate(datetimetz[] input)
{
date[] output = new date[input.length];
for (int i = 0; i < output.length; i++)
{
if (input[i] != null)
{
output[i] = new date(input[i]);
}
}
return output;
}
/**
* Copy the input array into an output array, using the output type constructor for each non-null
* element.
*
* @param input
* The input array.
*
* @return The copy of the input array, with the elements converted to the output data type.
*/
public static datetime[] toDatetime(datetimetz[] input)
{
datetime[] output = new datetime[input.length];
for (int i = 0; i < output.length; i++)
{
if (input[i] != null)
{
output[i] = new datetime(input[i]);
}
}
return output;
}
/**
* Copy the input array into an output array, using the output type constructor for each non-null
* element.
*
* @param input
* The input array.
*
* @return The copy of the input array, with the elements converted to the output data type.
*/
public static datetime[] toDatetime(date[] input)
{
datetime[] output = new datetime[input.length];
for (int i = 0; i < output.length; i++)
{
if (input[i] != null)
{
output[i] = new datetime(input[i]);
}
}
return output;
}
/**
* Copy the input array into an output array, using the output type constructor for each non-null
* element.
*
* @param input
* The input array.
*
* @return The copy of the input array, with the elements converted to the output data type.
*/
public static datetimetz[] toDatetimeTz(date[] input)
{
datetimetz[] output = new datetimetz[input.length];
for (int i = 0; i < output.length; i++)
{
if (input[i] != null)
{
output[i] = new datetimetz(input[i]);
}
}
return output;
}
/**
* Copy the input array into an output array, using the output type constructor for each non-null
* element.
*
* @param input
* The input array.
*
* @return The copy of the input array, with the elements converted to the output data type.
*/
public static datetimetz[] toDatetimeTz(datetime[] input)
{
datetimetz[] output = new datetimetz[input.length];
for (int i = 0; i < output.length; i++)
{
if (input[i] != null)
{
output[i] = new datetimetz(input[i]);
}
}
return output;
}
/**
* Initializes the indeterminate extent parameter reference.
*
* @return The initialized indeterminate extent parameter reference.
*/
@Override
public T[] initParameter()
{
return initParameter(0);
}
/**
* Initializes the indeterminate or determinate extent parameter reference depending
* on the {@code extent} value. A zero value will initialize an indeterminate
* extent, a positive integer will initialize a determinate extent. The extent
* will be registered with {@link ArrayAssigner} accordingly.
*
* @param extent
* The parameter extent size.
*
* @return The initialized indeterminate extent parameter reference.
*/
@Override
@SuppressWarnings("unchecked")
public T[] initParameter(int extent)
{
// get the parameter reference
T[] param = getParameter();
if (param != null)
{
// the parameter is already initialized, just return it
return param;
}
// initialize the parameter reference according to the variable reference
T[] var = getVariableSafe();
Class<?> varComponentClass = (paramType != null)
? paramType
: var.getClass().getComponentType();
param = (T[]) Array.newInstance(varComponentClass, extent);
boolean isMemptr = memptr.class.isAssignableFrom(param.getClass().getComponentType());
boolean isObject = object.class.isAssignableFrom(param.getClass().getComponentType());
Class<? extends _BaseObject_> objType =
isObject ? ArrayAssigner.getObjectType((object<? extends _BaseObject_>[]) var)
: null;
for (int i = 0; i < extent; i++)
{
if (isObject)
{
param[i] = (T) new ObjectVar(objType);
}
else if (isMemptr)
{
memptr m = new memptr();
if (i < var.length)
{
m.asParameter((memptr) var[i]);
}
param[i] = (T) m;
}
else
{
param[i] = (T) BaseDataType.generateDefault(varComponentClass);
}
}
setParameter(param);
// handle ArrayAssigner registrations for indeterminate extents
if (extent == 0)
{
// indeterminate extent is registered as a dynamic array
ArrayAssigner.registerDynamicArray(param);
// make sure this extent parameter is tracked by ArrayAssigner,
// to handle parameter value updates on resizes for example
ArrayAssigner.registerExtentParameter(this);
if (isObject)
{
ArrayAssigner.setObjectType((object[]) param, objType);
}
}
return param;
}
/**
* Output parameter validation logic. The method checks for runtime compatibility of the extent
* variable reference and parameter reference and raises an error if validation fails.
*
* @param endingRoutine
* The legacy name of the routine that is ending when these back assignments are
* performed.
* @param function
* {@code true} if the called routine is a function.
*
* @return {@code true} if parameter is valid or {@code false} if it failed validation
*/
@Override
protected boolean validateAssignment(String endingRoutine, boolean function)
{
T[] var = getVariableSafe();
T[] param = getParameter();
// is the assignment fixing the target extent variable?
if (var.length > 0 && var.length != param.length)
{
// no, raise an error
String callingProcedure = SessionUtils._isRemote()
? "(Remote client)"
: ProcedureManager.getStackEntry(0);
// not fixing the target and the extent sizes don't match
String msg = "Extent parameter dimension of %d from procedure %s is mismatched with " +
"sub-procedure %s parameter dimension of %d ...(328)";
msg = String.format(msg,
var.length,
callingProcedure,
endingRoutine,
param.length);
// the error message is always printed
if (function || ErrorManager.isSilentError())
{
ErrorManager.displayError(11428, msg, false);
}
else
{
// in case of a procedure - this is a serious error!
ErrorManager.recordOrThrowError(11428, msg, false);
}
// prevent the assignment
return false;
}
return true;
}
}