InputOutputExtentParameter.java
/*
** Module : InputOutputExtentParameter.java
** Abstract : Base class for special wrapper classes used for variables passed as
** input-output extent parameters to functions or procedures.
**
** Copyright (c) 2014-2022, Golden Code Development Corporation.
**
** -#- -I- --Date-- ---------------------------------------Description----------------------------------------
** 001 HC 20140613 Created initial version.
** 002 HC 20141106 Javadocs fixed
** 003 OM 20150706 Changed errors in initialization. Added implicit conversion support.
** 004 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.
** TJD 20220504 Java 11 compatibility minor changes
*/
/*
** 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 java.util.logging.*;
/**
* Special wrapper used for variables passed as input-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 InputOutputExtentParameter<T extends BaseDataType>
extends AbstractExtentParameter<T>
{
/**
* The constructor responsible for instance initialization by calling
* {@link AbstractExtentParameter#AbstractExtentParameter(BaseDataType[])}.
*/
public InputOutputExtentParameter()
{
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.
*/
protected InputOutputExtentParameter(T[] variable)
{
super(variable);
}
/**
* Initializes the indeterminate or determinate extent parameter reference depending
* on the extent (array size) of the value returned by {@link #getVariable()}.
* 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.
*
* @return The initialized indeterminate extent parameter reference.
*/
@Override
public T[] initParameter()
{
T[] var = getVariableSafe();
return initParameter(var.length);
}
/**
* 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();
boolean isMemptr = memptr.class.isAssignableFrom(varComponentClass);
if (var.length < extent)
{
// The passed-in variable is an unfixed indeterminate extent, allocate
// the parameter variable to the expected extent size.
// Don't use ArrayAssigner.resize in this case, as it also copies decimal
// precision. According to Progress behavior the newly allocated array
// will have the default decimal precision.
param = (T[]) Array.newInstance(varComponentClass, extent);
for (int i = 0; i < extent; i++)
{
param[i] = (T) BaseDataType.generateUnknown(varComponentClass);
if (isMemptr && i < var.length)
{
((memptr) param[i]).asParameter((memptr) var[i]);
}
}
setParameter(param);
}
else
{
// The passed-in variable is a fixed extent, allocate the parameter
// variable to its extent size.
param = (T[]) Array.newInstance(varComponentClass, var.length);
for (int i = 0; i < extent; i++)
{
// duplicate the array content
try
{
if (isMemptr)
{
memptr m = new memptr();
m.asParameter((memptr) var[i]);
param[i] = (T) m;
}
else
{
param[i] = (convCtor == null)
? (T) var[i].duplicate()
: (T) convCtor.newInstance(var[i]);
}
}
catch (ReflectiveOperationException e)
{
// constructor-related exception should not happen, the constructor have been
// detected based on actual data types
String msg = "Failed to convert INPUT-OUTPUT value from " + var[i].getClass() +
" to " + param[i].getTypeName() + " using " + convCtor +
" conversion constructor.";
LOG.log(Level.WARNING, msg);
}
if (decimal.class.equals(varComponentClass))
{
// and set to the default decimal precision
((decimal) param[i]).setPrecision(decimal.MAX_SCALE);
}
}
setParameter(param);
if (extent == 0)
{
// the result is registered as 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);
}
}
return param;
}
}