InputParameter.java
/*
** Module : InputParameter.java
** Abstract : Defines APIs which are emitted in special cases, to validate the argument before invoking the
** top-level block.
**
** Copyright (c) 2021-2024, Golden Code Development Corporation.
**
** -#- -I- --Date-- ---------------------------------Description---------------------------------
** 001 CA 20210216 Created initial version.
** TJD 20220504 Java 11 compatibility minor changes
** 002 GBB 20230512 Logging methods replaced by CentralLogger/ConversionStatus.
** 003 RAA 20230525 Creating a new BaseDataType instance is now done using BaseDataTypeFactory.
** 004 ES 20240719 Casting the value to the type defined of the Resolvable.
*/
/*
** 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 com.goldencode.p2j.util.logging.*;
import java.util.*;
import java.util.function.*;
import java.util.logging.Level;
/**
* Defines APIs emitted to preserve the conversion-time type of an expression, and allow the runtime to check
* if the evaluated value of this expression matches the conversion-time type or not.
* <p>
* For OO method calls, this ensures that the proper overloaded method is resolved, as this requires the
* conversion-time type, for both direct and dynamic calls.
*/
public class InputParameter
{
/** Logger */
private static final CentralLogger LOG = CentralLogger.get(InputParameter.class);
/**
* Cast the runtime value to the specified type. If the assignment fails, then the next top-level block
* will not be executed, and instead argument failure errors are shown/thrown.
* <p>
* This API is emitted when the target top-level block is a direct Java call for a legacy OO method.
*
* @param type
* The conversion-time type of the expression.
* @param value
* The runtime value of the argument.
*
* @return The value converted to the conversion-time type.
*/
public static Resolvable arg(Class<? extends BaseDataType> type, BaseDataType value)
{
return new Resolvable()
{
@Override
public Class<? extends BaseDataType> getType()
{
return type;
}
@Override
public BaseDataType resolve()
{
if (value.isUnknown())
{
return BaseDataType.generateUnknown(type);
}
else
{
try
{
return BaseDataTypeFactory.instantiate(type, value);
}
catch (ReflectiveOperationException e)
{
return value;
}
}
}
};
}
/**
* Cast the runtime value to the specified type. If the assignment fails, then the next top-level block
* will not be executed, and instead argument failure errors are shown/thrown.
* <p>
* This API is emitted when the target top-level block is a function.
*
* @param type
* The conversion-time type of the expression.
* @param value
* The runtime value of the argument.
*
* @return The value converted to the conversion-time type.
*/
public static <T extends BaseDataType> T function(Class<T> type, BaseDataType value)
{
return cast(type,
false,
value,
"User-defined Function could not build input runtime parameters from the stack. (6348)",
6348);
}
/**
* Cast the runtime value to the specified type. If the assignment fails, then the next top-level block
* will not be executed, and instead argument failure errors are shown/thrown.
* <p>
* This API is emitted when the target top-level block is from a RUN statement.
*
* @param type
* The conversion-time type of the expression.
* @param value
* The runtime value of the argument.
*
* @return The value converted to the conversion-time type.
*/
public static <T extends BaseDataType> T procedure(Class<T> type, BaseDataType value)
{
return cast(type,
true,
value,
"Procedure could not build input runtime parameters from the stack. (988)",
988);
}
/**
* Cast the runtime value to the specified type. If the assignment fails, then the next top-level block
* will not be executed, and instead argument failure errors are shown/thrown.
* <p>
* This API is emitted when the target top-level block is a property setter.
*
* @param type
* The conversion-time type of the expression.
* @param value
* The runtime value of the argument.
*
* @return The value converted to the conversion-time type.
*/
public static <T extends BaseDataType> T property(Class<T> type, BaseDataType value)
{
return cast(type, true, value, "** Unable to update Field. (142)", 142);
}
/**
* Cast the runtime value to the specified type. If the assignment fails, then the next top-level block
* will not be executed, and instead argument failure errors are shown/thrown.
*
* @param type
* The conversion-time type of the expression.
* @param value
* The runtime value of the argument.
*
* @return The value converted to the conversion-time type.
*/
public static <T extends BaseDataType> T cast(Class<T> type, BaseDataType value)
{
return cast(type,
true,
value,
"User-defined Function could not build input runtime parameters from the stack. (6348)",
6348);
}
/**
* Cast the runtime value to the specified type. If the assignment fails, then the next top-level block
* will not be executed, and instead argument failure errors are shown/thrown.
*
* @param type
* The conversion-time type of the expression.
* @param error
* Flag indicating if an ERROR condition needs to be raised.
* @param value
* The runtime value of the argument.
* @param errMsg
* The error message to add in case of failure.
* @param errNum
* The error number to add in case of failure.
*
* @return The value converted to the conversion-time type.
*/
private static <T extends BaseDataType> T cast(Class<T> type,
boolean error,
BaseDataType value,
String errMsg,
int errNum)
{
if (type == value.getClass())
{
return (T) value;
}
try
{
T newVal = BaseDataTypeFactory.instantiate(type);
List<String> errors = new ArrayList<>();
List<Integer> nums = new ArrayList<>();
BiConsumer<String, Integer> processor = (msg, num) ->
{
errors.add(msg);
nums.add(num);
};
if (ErrorManager.nestedSilent(() -> newVal.assign(value), processor))
{
errors.add(errMsg);
nums.add(errNum);
// get the stacktrace which initiated this ignore, to make it easier to find errors in case the
// 'abend' code gets called for another top-level block
Exception e = new Exception();
e.fillInStackTrace();
StackTraceElement[] ste = e.getStackTrace();
StackTraceElement last = ste[2];
LOG.log(Level.INFO, "Invalid INPUT parameter - ignoring the call at: " + last.toString());
BlockManager.ignoreNextCall(() ->
{
if (error)
{
ErrorManager.recordOrThrowError(Utils.integerCollectionToPrimitive(nums),
errors.toArray(new String[0]),
false,
true);
}
else
{
ErrorManager.recordOrShowError(Utils.integerCollectionToPrimitive(nums),
errors.toArray(new String[0]),
false,
false,
false,
true,
false);
}
});
}
return newVal;
}
catch (ReflectiveOperationException e)
{
// should never happen, but throw anyway
throw new RuntimeException(e);
}
}
}