LegacyClass.java
/*
** Module : LegacyClass.java
** Abstract : Implementation of the Progress.Lang.Class builtin class.
**
** Copyright (c) 2018-2025, Golden Code Development Corporation.
**
** -#- -I- --Date-- --------------------------------------Description---------------------------------------
** 001 CA 20181213 First version.
** 002 CA 20190219 Implemented the class methods and properties.
** 003 CA 20190509 Always use <? extends _BaseObject_>.
** 004 CA 20190812 First pass at invoke and new via 4GL reflection.
** 005 CA 20191023 Fixed the LegacySignature.name for isA method.
** 006 CA 20191024 Added method support levels and updated the class support level.
** 007 CA 20191124 Added stubs for GetPropertyValue. Changed class conversion level to PARTIAL.
** CA 20191211 Instances of this class must exist as legacy 4GL objects (with ctor and execute methods).
** 008 CA 20200503 Added stubs for GetEnumNames, GetEnumValues, GetEnumValue, GetEnumName, IsEnum
** and IsFlagsEnum.
** GES 20200519 Added protection against instantiation of legacy enum classes. Added full implementation
** for all enum methods.
** ME 20200423 Add missing methods, mainly reflection related.
** GES 20200528 Fixed enum error handling.
** GES 20200605 Fixed isValidEnum() processing.
** 009 CA 20210221 Fixed 'qualified', 'extent' and 'returns' annotations at the legacy signature.
** OM 20210225 Dropped duplicated error code.
** CA 20220304 An ERROR condition being raised from calling a legacy method mustbe morphed into a legacy
** SysError exception, and not raise an ERROR condition.
** ME 20211028 Use 'POLY' data type for return/parameter on invoke/getPropertyValue/setPropertyValue.
** CA 20220513 Switched invoke() to use BaseDataType as return type, because FWD does not yet support
** 'extent' POLY returned values, and this conflicts with BlockManager.returnNormal()
** parameter.
** CA 20220526 Added default legacy constructor method.
** CA 20220828 Implemented 'getInterfaces()' method.
** 010 CA 20230601 'getInterfaces' in OE does not guarantee the returned interface order, but experimenting
** shows that it does a BFS walk of the hierarchy. Also, fixed a bug in
** 'Utils.collectInterfaces', which returned only the directly implemented interfaces instead
** from the full hierarchy.
** 011 CA 20230801 'invoke' must re-throw a LegacyErrorException thrown by the target.
** 012 ME 20230503 Reconvert skeleton and update method signatures, invoke returns Object (extent) (#6410).
** ME 20230905 Implement getMethod/getMethods, some clean-up on invokeImpl (unused arguments).
** ME 20230918 Use function block for `getMethod` method (error handling).
** 013 CA 20230802 Reset the OutputParameterAssigner after invoke finishes, as the parameters are copied
** explicitly, without the OutputParameterAssigner support.
** 014 CA 20231208 POLY OO method invocations must use the var's declared type and not the runtime type.
** 015 SB 20240116 Implemented functionality for GetPropertyValue.
** 016 CA 20240708 Allow RETURN ERROR to propagate to the caller.
** 017 ICP 20250120 Used logical constants to leverage cached instances.
** 018 ES 20250515 Added runtime support for hasWidgetPool method.
** ES 20250521 Added runtime support for getVariables, getVariable, getProperties and getProperty
** methods.
** ES 20250527 Cache class level properties and variables.
*/
/*
** 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.oo.lang;
import java.io.*;
import java.lang.reflect.*;
import java.util.*;
import java.util.logging.Level;
import com.goldencode.p2j.oo.reflect.*;
import com.goldencode.p2j.util.*;
import com.goldencode.p2j.util.InternalEntry.Type;
import com.goldencode.p2j.util.logging.CentralLogger;
import com.goldencode.util.CaseInsensitiveHashMap;
import static com.goldencode.p2j.report.ReportConstants.*;
import static com.goldencode.p2j.util.BlockManager.*;
/**
* Implementation of the Progress.Lang.Class builtin class.
*/
@LegacyResource(resource = "Progress.Lang.Class")
@LegacyResourceSupport(supportLvl = CVT_LVL_PARTIAL|RT_LVL_PARTIAL)
public class LegacyClass
extends BaseObject
{
/** The legacy class name. */
private final String typeName;
/** The converted Java class. */
private final Class<? extends _BaseObject_> cls;
/**
* Flag (when resolved) indicating if this or any super-class has static members or methods.
*/
private Boolean hasStatics = null;
/** Flag indicating that a class is annotated with LegacyWidgetPool annotation */
private boolean hasWidgetPool = false;
/** Map that caches the variables of the defined class */
private Map<String, Variable> classVariables = new CaseInsensitiveHashMap<>();
/** Map that caches the properties of the defined class */
private Map<String, Property> classProperties = new CaseInsensitiveHashMap<>();
/** Map that caches the methods of the defined class */
private Map<Method, Legacy4GLMethod> classMethods = new CaseInsensitiveHashMap<>();
/** Logger */
private static final CentralLogger LOG = CentralLogger.get(LegacyClass.class.getName());
/**
* Create a new legacy class object.
*
* @param typeName
* The qualified legacy class name.
* @param cls
* The converted type.
*/
public LegacyClass(String typeName, Class<? extends _BaseObject_> cls)
{
this.typeName = typeName;
this.cls = cls;
this.hasWidgetPool = cls.isAnnotationPresent(LegacyWidgetPool.class);
}
/**
* Resolve the static property with the given name.
*
* @param propName
* The property name.
*
* @return See above.
*/
@LegacySignature(type = Type.METHOD, name = "GetPropertyValue", returns = "BDT", parameters =
{
@LegacyParameter(name = "propName", type = "CHARACTER", mode = "INPUT")
})
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB)
public BaseDataType getPropertyValue(character propName)
{
BaseDataType ret = null;
ret = getPropertyValueImpl(null, propName, null);
return ret;
}
/**
* Resolve the static array property value, with the given index and name.
* TODO: the index can be any given data type.
*
* @param propName
* The property name.
* @param idx
* The index.
*
* @return See above.
*/
@LegacySignature(type = Type.METHOD, name = "GetPropertyValue", returns = "BDT", parameters =
{
@LegacyParameter(name = "propName", type = "CHARACTER", mode = "INPUT"),
@LegacyParameter(name = "idx", type = "BDT", mode = "INPUT")
})
@LegacyResourceSupport(supportLvl = CVT_LVL_PARTIAL|RT_LVL_STUB)
public BaseDataType getPropertyValue(character propName, Object idx)
{
BaseDataType ret = null;
ret = getPropertyValueImpl(null, propName, idx);
return ret;
}
/**
* Resolve the instance property value, with the given name.
*
* @param instance
* The instance.
* @param propName
* The property name.
*
* @return See above.
*/
@LegacySignature(type = Type.METHOD, name = "GetPropertyValue", returns = "BDT", parameters =
{
@LegacyParameter(name = "instance", type = "OBJECT", qualified = "Progress.Lang.Object", mode = "INPUT"),
@LegacyParameter(name = "propName", type = "CHARACTER", mode = "INPUT"),
})
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB)
public BaseDataType getPropertyValue(object<? extends _BaseObject_> instance, character propName)
{
BaseDataType ret = null;
ret = getPropertyValueImpl(instance, propName, null);
return ret;
}
/**
* Resolve the instance array property value, with the given index and name.
* TODO: the index can be any given data type.
*
* @param instance
* The instance.
* @param propName
* The property name.
* @param idx
* The index.
*
* @return See above.
*/
@LegacySignature(type = Type.METHOD, name = "GetPropertyValue", returns = "BDT", parameters =
{
@LegacyParameter(name = "instance", type = "OBJECT", qualified = "Progress.Lang.Object", mode = "INPUT"),
@LegacyParameter(name = "propName", type = "CHARACTER", mode = "INPUT"),
@LegacyParameter(name = "idx", type = "BDT", mode = "INPUT")
})
@LegacyResourceSupport(supportLvl = CVT_LVL_PARTIAL|RT_LVL_STUB)
public BaseDataType getPropertyValue(object<? extends _BaseObject_> instance,
character propName,
Object idx)
{
BaseDataType ret = null;
ret = getPropertyValueImpl(instance, propName, idx);
return ret;
}
/**
* Resolve the legacy class from the given name.
*
* @param typeName
* The qualified legacy class name.
*
* @return See above.
*/
@LegacySignature(returns = "OBJECT", qualified = "Progress.Lang.Class", type = Type.METHOD, name = "getClass", parameters =
{
@LegacyParameter(name = "type", type = "CHARACTER", mode = "INPUT")
})
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL)
public static object<LegacyClass> getLegacyClass(character typeName)
{
return getLegacyClass(typeName.isUnknown() ? "" : typeName.toStringMessage());
}
public static object<? extends LegacyClass> getLegacyClass(object<? extends com.goldencode.p2j.oo.lang._BaseObject_> obj)
{
if (obj == null || !obj._isValid())
return null;
return LegacyClass.class.equals(obj.ref().getClass()) ? (object<LegacyClass>) obj : obj.ref().getLegacyClass();
}
/**
* Resolve the legacy class from the given name.
*
* @param typeName
* The qualified legacy class name.
*
* @return See above.
*/
@LegacySignature(returns = "OBJECT", qualified = "Progress.Lang.Class", type = Type.METHOD, name = "getClass", parameters =
{
@LegacyParameter(name = "type", type = "CHARACTER", mode = "INPUT")
})
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL)
public static object<LegacyClass> getLegacyClass(String typeName)
{
return new object(ObjectOps.getLegacyClass(typeName).ref());
}
/**
* Obtains the package name of the object.
*
* @return The package name.
*/
@LegacySignature(returns = "CHARACTER", type = Type.GETTER, name = "Package")
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL)
public character getPackage()
{
int dotIdx = typeName.indexOf('.');
return character.of(dotIdx < 0 ? "" : typeName.substring(0, dotIdx));
}
/**
* Obtains the parent class of the object.
*
* @return The parent class.
*/
@LegacySignature(returns = "OBJECT", qualified = "Progress.Lang.Class", type = Type.GETTER, name = "SuperClass")
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL)
public object<LegacyClass> getSuperClass()
{
if (cls == BaseObject.class)
{
return new object<>();
}
return getLegacyClass(ObjectOps.getLegacyName((Class) cls.getSuperclass()));
}
/**
* Obtains the fully qualified name (package and class) of the object.
*
* @return The fully qualified type name.
*/
@LegacySignature(returns = "CHARACTER", type = Type.GETTER, name = "TypeName")
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL)
public character getTypeName()
{
return character.of(typeName);
}
/**
* Returns a comma-separated text list of the names of all predefined enums in this legacy class, in
* the order of their values. For regular enums, this is a simple ascending sort. For flag enums, it
* is ascending order starting from zero through the positive values and then followed by any negative
* values (also in ascending order).
*
* @return The comma-separated text list of the predefined enum names.
*
* @throws ErrorConditionException
* When called for a class that is not a subclass of {@code LegacyEnum} or {@code FlagsEnum}.
*/
@LegacySignature(returns = "CHARACTER", type = Type.METHOD, name = "GetEnumNames")
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL)
public character getEnumNames()
{
if (!isValidEnum())
{
return new character();
}
return LegacyEnum.getEnumNames((Class<? extends LegacyEnum>) cls);
}
/**
* Returns a comma-separated text list of the values of all predefined enums in this legacy class, in
* the order of their values. For regular enums, this is a simple ascending sort. For flag enums, it
* is ascending order starting from zero through the positive values and then followed by any negative
* values (also in ascending order).
*
* @return The comma-separated text list of the predefined enum values.
*
* @throws ErrorConditionException
* When called for a class that is not a subclass of {@code LegacyEnum} or {@code FlagsEnum}.
*/
@LegacySignature(returns = "CHARACTER", type = Type.METHOD, name = "GetEnumValues")
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL)
public character getEnumValues()
{
if (!isValidEnum())
{
return new character();
}
return LegacyEnum.getEnumValues((Class<? extends LegacyEnum>) cls);
}
/**
* Obtain the value of the named enum. For regular enums, the given name must be a single name that
* matches exactly to a predefined enum name. For flags enums, it can one name or a comma-separated
* list of valid names, whose corresponding values are bitwise OR'd together. Matching is
* case-insensitive and leading/trailing whitespace is ignored. If any name is invalid, then unknown
* value is returned. For regular enums, a comma-separated list is an invalid name.
*
* @param name
* The enum name to lookup.
*
* @return The value found or unknown value if there is an invalid name.
*
* @throws ErrorConditionException
* If the given name is unknown value.
*/
@LegacySignature(returns = "INT64", type = Type.METHOD, name = "GetEnumValue", parameters =
{
@LegacyParameter(name = "name", type = "CHARACTER", mode = "INPUT")
})
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL)
public int64 getEnumValue(character name)
{
if (!isValidEnum())
{
return new int64();
}
return LegacyEnum.getEnumValue((Class<? extends LegacyEnum>) cls, name);
}
/**
* Obtain the value of the named enum. For regular enums, the given name must be a single name that
* matches exactly to a predefined enum name. For flags enums, it can one name or a comma-separated
* list of valid names, whose corresponding values are bitwise OR'd together. Matching is
* case-insensitive and leading/trailing whitespace is ignored. If any name is invalid, then unknown
* value is returned. For regular enums, a comma-separated list is an invalid name.
*
* @param name
* The enum name to lookup.
*
* @return The value found or unknown value if there is an invalid name.
*
* @throws ErrorConditionException
* If the given name is unknown value.
*/
@LegacySignature(returns = "INT64", type = Type.METHOD, name = "GetEnumValue", parameters =
{
@LegacyParameter(name = "name", type = "CHARACTER", mode = "INPUT")
})
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL)
public int64 getEnumValue(String name)
{
return getEnumValue(new character(name));
}
/**
* Obtain the name of the enum(s) specified by this value. For regular enums, the given value must be
* an exact match to a single predefined enum. For flags enums, it can be a single enum's value or it
* can be a bitset that matches more than one enum. In the multiple flag enum case, it will only match
* enum values which are full subsets of the given value (see {@code isFlagSet}). If there is no exact
* match or for flag enums there is no single enum that is a subset, then unknown value is returned.
* <p>
* If multiple enums match (flag enums case), then each of the names will be returned in a comma-separated
* list.
*
* @param value
* The enum value to lookup.
*
* @return The name found or unknown value if there is an invalid value.
*
* @throws ErrorConditionException
* If the given input value is the unknown value.
*/
@LegacySignature(returns = "CHARACTER", type = Type.METHOD, name = "GetEnumName", parameters =
{
@LegacyParameter(name = "value", type = "INT64", mode = "INPUT")
})
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL)
public character getEnumName(int64 value)
{
if (!isValidEnum())
{
return new character();
}
return LegacyEnum.getEnumName((Class<? extends LegacyEnum>) cls, value);
}
/**
* Obtain the name of the enum(s) specified by this value. For regular enums, the given value must be
* an exact match to a single predefined enum. For flags enums, it can be a single enum's value or it
* can be a bitset that matches more than one enum. In the multiple flag enum case, it will only match
* enum values which are full subsets of the given value (see {@code isFlagSet}). If there is no exact
* match or for flag enums there is no single enum that is a subset, then unknown value is returned.
* <p>
* If multiple enums match (flag enums case), then each of the names will be returned in a comma-separated
* list.
*
* @param value
* The enum value to lookup.
*
* @return The name found or unknown value if there is an invalid value.
*
* @throws ErrorConditionException
* If the given input value is the unknown value.
*/
@LegacySignature(returns = "CHARACTER", type = Type.METHOD, name = "GetEnumName", parameters =
{
@LegacyParameter(name = "value", type = "INT64", mode = "INPUT")
})
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL)
public character getEnumName(long value)
{
return getEnumName(new int64(value));
}
/**
* Report if this class is a subclass of {@code LegacyEnum}.
*
* @return {@code true} if this is a subclass of {@code LegacyEnum}.
*/
@LegacySignature(returns = "LOGICAL", type = Type.METHOD, name = "IsEnum")
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB)
public logical isEnum()
{
return logical.of(LegacyEnum.class.isAssignableFrom(cls));
}
/**
* Report if this class is a subclass of {@code FlagsEnum}.
*
* @return {@code true} if this is a subclass of {@code FlagsEnum}.
*/
@LegacySignature(returns = "LOGICAL", type = Type.METHOD, name = "IsFlagsEnum")
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB)
public logical isFlagsEnum()
{
return logical.of(FlagsEnum.class.isAssignableFrom(cls));
}
/**
* Get the converted {@link #cls type}.
*
* @return See above.
*/
public Class<? extends _BaseObject_> getType()
{
return cls;
}
/**
* Check if this is an interface.
*
* @return See above.
*/
@LegacySignature(returns = "LOGICAL", type = Type.METHOD, name = "isInterface")
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL)
public logical isInterface()
{
return logical.of(cls.isInterface());
}
/**
* Check if this is a final class.
*
* @return See above.
*/
@LegacySignature(returns = "LOGICAL", type = Type.METHOD, name = "isFinal")
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL)
public logical isFinal()
{
// SysError can be extended in application although is the super class of other 4GL errors
return logical.of(SysError.class.equals(cls) || Modifier.isFinal(cls.getModifiers()));
}
@LegacySignature(returns = "LOGICAL", type = Type.METHOD, name = "IsGeneric")
public logical isGeneric()
{
// only for .net objects
return logical.of(false);
}
@LegacySignature(returns = "LOGICAL", type = Type.METHOD, name = "IsIndexed")
public logical isIndexed()
{
// only for .net objects
return logical.of(false);
}
@LegacySignature(returns = "LOGICAL", type = Type.METHOD, name = "IsSerializable")
public logical isSerializable()
{
// TODO: 4GL serialization does not use an interface, some annotation maybe
// a class can be non-serializable even if it's base class is
return logical.of(cls.isAssignableFrom(Serializable.class));
}
/**
* Check if this is an abstract class.
*
* @return See above.
*/
@LegacySignature(returns = "LOGICAL", type = Type.METHOD, name = "isAbstract")
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL)
public logical isAbstract()
{
return logical.of(Modifier.isAbstract(cls.getModifiers()));
}
/**
* Check if this or any of the super-classes has static members.
*
* @return See above.
*/
@LegacySignature(returns = "LOGICAL", type = Type.METHOD, name = "hasStatics")
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL)
public logical hasStatics()
{
return logical.of(hasStaticsInt() || getSuperClass().ref().hasStaticsInt());
}
/**
* Check if this class has a USE-WIDGET-POOL option.
*
* @return See above.
*/
@LegacySignature(returns = "LOGICAL", type = Type.METHOD, name = "hasWidgetPool")
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL)
public logical hasWidgetPool()
{
return logical.of(hasWidgetPool);
}
/**
* Check if this type is the same as or a sub-class of the given type.
*
* @param type
* The type to check.
*
* @return See above.
*/
@LegacySignature(returns = "LOGICAL", type = Type.METHOD, name = "isA", parameters =
{
@LegacyParameter(name = "type", type = "CHARACTER")
})
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL)
public logical isA(character type)
{
return isA(getLegacyClass(type));
}
public boolean isA(String type) {
return _isA(getLegacyClass(type));
}
/**
* Check if this type is the same as or a sub-class of the given type.
*
* @param ref
* The type to check.
*
* @return See above.
*/
@LegacySignature(returns = "LOGICAL", type = Type.METHOD, name = "isA", parameters =
{
@LegacyParameter(name = "ref", type = "OBJECT", mode = "INPUT", qualified = "Progress.Lang.Class")
})
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL)
public logical isA(object<? extends LegacyClass> ref)
{
return logical.of(_isA(ref));
}
public boolean _isA(object<? extends LegacyClass> ref)
{
if (ref.isUnknown())
{
return false;
}
LegacyClass obj = ref.ref();
return obj.cls.isAssignableFrom(this.cls);
}
/**
* Create a new instance from this class, using the implicit constructor.
*
* @return See above.
*/
@LegacySignature(returns = "OBJECT", qualified = "Progress.Lang.Object", type = Type.METHOD, name = "new")
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL)
public object<? extends _BaseObject_> new_()
{
// enums cannot be instantiated
if (LegacyEnum.failEnumInstantiation(cls))
{
return null;
}
return ObjectOps.newInstanceInternal(cls, typeName, null);
}
/**
* Create a new instance from this class, using the specified constructor.
*
* @return See above.
*/
@LegacySignature(returns = "OBJECT", qualified = "Progress.Lang.Object", type = Type.METHOD, name = "new", parameters =
{
@LegacyParameter(name = "parms", type = "OBJECT", mode = "INPUT", qualified = "Progress.Lang.ParameterList")
})
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_BASIC)
public object<? extends _BaseObject_> new_(object<? extends ParameterList> parms)
{
// enums cannot be instantiated
if (LegacyEnum.failEnumInstantiation(cls))
{
return null;
}
ParameterList opl = parms.ref();
// TODO: validate params?
String modes = opl.getModes();
Object[] args = opl.getArgs();
object<?>[] retVal = new object[1];
Runnable task = () -> retVal[0] = ObjectOps.newInstanceInternal(cls, typeName, modes, args);
invokeImpl(opl, task);
return retVal[0];
}
/**
* Invoke the specified static method in this class.
*
* @param method
* The static method name.
*/
// @LegacySignature(type = Type.METHOD, name = "invoke", parameters =
// {
// @LegacyParameter(name = "method", type = "CHARACTER", mode = "INPUT")
// })
// DO NOT ANNOTATE THIS!
public void invokeStandalone(character method)
{
ObjectOps.invokeStandalone(typeName, method);
}
/**
* Invoke the specified static method in this class.
*
* @param method
* The static method name.
* @param parms
* The parameter list.
*/
// @LegacySignature(type = Type.METHOD, name = "invoke", parameters =
// {
// @LegacyParameter(name = "method", type = "CHARACTER", mode = "INPUT"),
// @LegacyParameter(name = "parms", type = "OBJECT", mode = "INPUT", qualified = "Progress.Lang.ParameterList")
// })
// DO NOT ANNOTATE THIS!
public void invokeStandalone(character method, object<? extends ParameterList> parms)
{
ParameterList opl = parms.ref();
// TODO: validate params object?
String modes = opl.getModes();
Object[] args = opl.getArgs();
Runnable task = () -> ObjectOps.invokeStandalone(typeName, method, modes, args);
invokeImpl(opl, task);
}
/**
* Invoke the specified static method in this class.
*
* @param method
* The static method name.
*
* @return Any returned value, or unknown if the method is void.
*/
@LegacySignature(type = Type.METHOD, name = "invoke", returns = "BDT", parameters =
{
@LegacyParameter(name = "method", type = "CHARACTER", mode = "INPUT")
})
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL)
public BaseDataType invoke(character method)
{
return ObjectOps.invoke(typeName, method);
}
/**
* Invoke the specified instance method in the specified object.
*
* @param ref
* The legacy object reference.
* @param method
* The method name.
*/
// @LegacySignature(type = Type.METHOD, name = "invoke", parameters =
// {
// @LegacyParameter(name = "method", type = "CHARACTER", mode = "INPUT"),
// @LegacyParameter(name = "parms", type = "OBJECT", mode = "INPUT", qualified = "Progress.Lang.Object")
// })
// DO NOT ANNOTATE THIS!
public void invokeStandalone(object<? extends _BaseObject_> ref, character method)
{
ObjectOps.invokeStandalone(ref, method);
}
/**
* Invoke the specified instance method in the specified object.
*
* @param ref
* The legacy object reference.
* @param method
* The method name.
* @param parms
* The parameter list.
*/
// @LegacySignature(type = Type.METHOD, name = "invoke", parameters =
// {
// @LegacyParameter(name = "ref", type = "OBJECT", mode = "INPUT", qualified = "Progress.Lang.Object"),
// @LegacyParameter(name = "method", type = "CHARACTER", mode = "INPUT"),
// @LegacyParameter(name = "parms", type = "OBJECT" , mode = "INPUT", qualified = "Progress.Lang.ParameterList")
// })
// DO NOT ANNOTATE THIS!
public void invokeStandalone(object<? extends _BaseObject_> ref,
character method,
object<? extends ParameterList> parms)
{
ParameterList opl = parms.ref();
// TODO: validate params object?
String modes = opl.getModes();
Object[] args = opl.getArgs();
Runnable task = () -> ObjectOps.invokeStandalone(ref, method, modes, args);
invokeImpl(opl, task);
}
/**
* Invoke the specified static method in this class.
*
* @param method
* The static method name.
* @param parms
* The parameter list.
*
* @return Any returned value, or unknown if the method is void.
*/
@LegacySignature(type = Type.METHOD, name = "invoke", returns = "BDT", parameters =
{
@LegacyParameter(name = "method", type = "CHARACTER", mode = "INPUT"),
@LegacyParameter(name = "parms", type = "OBJECT", mode = "INPUT", qualified = "Progress.Lang.ParameterList")
})
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_BASIC)
public BaseDataType invoke(character method, object<? extends ParameterList> parms)
{
ParameterList opl = parms.ref();
// TODO: validate params?
String modes = opl.getModes();
Object[] args = opl.getArgs();
BaseDataType[] retVal = new BaseDataType[1];
Runnable task = () -> retVal[0] = ObjectOps.invoke(typeName, method, modes, args);
invokeImpl(opl, task);
return retVal[0];
}
/**
* Invoke the specified instance method in the specified object.
*
* @param ref
* The legacy object reference.
* @param method
* The method name.
*
* @return Any returned value, or unknown if the method is void.
*/
@LegacySignature(type = Type.METHOD, name = "invoke", returns = "BDT", parameters =
{
@LegacyParameter(name = "ref", type = "OBJECT", mode = "INPUT", qualified = "Progress.Lang.Object"),
@LegacyParameter(name = "method", type = "CHARACTER", mode = "INPUT"),
})
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL)
public BaseDataType invoke(object<? extends _BaseObject_> ref, character method)
{
return ObjectOps.invoke(ref, method);
}
/**
* Invoke the specified instance method in the specified object.
*
* @param ref
* The legacy object reference.
* @param method
* The method name.
* @param parms
* The parameter list.
*
* @return Any returned value, or unknown if the method is void.
*/
@LegacySignature(type = Type.METHOD, name = "invoke", returns = "BDT", parameters =
{
@LegacyParameter(name = "ref", type = "OBJECT", mode = "INPUT", qualified = "Progress.Lang.Object"),
@LegacyParameter(name = "method", type = "CHARACTER", mode = "INPUT"),
@LegacyParameter(name = "parms", type = "OBJECT" , mode = "INPUT", qualified = "Progress.Lang.ParameterList")
})
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_BASIC)
public BaseDataType invoke(object<? extends _BaseObject_> ref,
character method,
object<? extends ParameterList> parms)
{
ParameterList opl = parms.ref();
// TODO: validate params?
String modes = opl.getModes();
Object[] args = opl.getArgs();
BaseDataType[] retVal = new BaseDataType[1];
Runnable task = () -> retVal[0] = ObjectOps.invoke(ref, method, modes, args);
invokeImpl(opl, task);
return retVal[0];
}
/**
* Perform the actual invocation. This can be a void or non-void method call, or an object instantiation
* via {@link #new_}.
*
* @param opl
* The {@link ParameterList} reference.
* @param task
* The task to execute, to perform the actual invoke.
*/
public static void invokeImpl(ParameterList opl, Runnable task)
{
boolean silent = ErrorManager.isSilent();
if (!silent)
{
// assume a silent mode, so that errors can be captured and re-raised
ErrorManager.setSilent(true);
}
boolean ran = false;
try
{
CallParameter[] parameters = opl.getParameters();
if (parameters != null)
{
for (CallParameter cparam : parameters)
{
Object arg = cparam.getArgument();
if (arg instanceof AbstractExtentParameter)
{
AbstractParameter.getCurrentScope()
.addExtentParameter((AbstractExtentParameter<?>) arg);
}
else if (arg instanceof AbstractSimpleParameter)
{
AbstractParameter.getCurrentScope()
.addSimpleParameter((AbstractSimpleParameter) arg);
}
}
}
task.run();
ran = true;
if (!silent && ErrorManager.isPendingError())
{
// raise a condition, this is from a RETURN ERROR - will be re-thrown in catch
throw new ErrorConditionException("return error");
}
// now, we need to copy back the output and input-output arguments
if (parameters != null)
{
for (CallParameter cparam : parameters)
{
if (cparam.mode.output || cparam.mode.inputOutput)
{
if (!cparam.copyOutput())
{
// if a parameter copy failed, stop processing
break;
}
}
}
}
}
catch (LegacyErrorException lex)
{
throw lex;
}
catch (ErrorConditionException ece)
{
ErrorManager.setSilent(silent);
boolean pendingError = ErrorManager.isPendingError();
ErrorManager.clearPending();
if (!silent && ErrorManager.mustThrowLegacyError())
{
// check if it must be morphed into a legacy error instance
object<? extends SysError> err = SysError.newInstance(ece.getMessage(),
ece.getProgressErrorCode());
throw new LegacyErrorException(err);
}
if (ran || ControlFlowOps.hadInvalidArguments())
{
int[] nums =
{
ece.getProgressErrorCode(),
5729,
5678
};
String[] texts =
{
ece.getMessage(),
"Incompatible datatypes found during runtime conversion",
"Unable to do run-time conversion of datatypes"
};
ErrorManager.recordOrThrowError(nums, texts, false, true);
return;
}
int[] nums =
{
ece.getProgressErrorCode(),
5729,
10089
};
String[] texts =
{
ece.getMessage(),
"Incompatible datatypes found during runtime conversion",
"Unable to update output parameter for dynamic INVOKE"
};
if (!pendingError)
{
ErrorManager.recordOrShowError(nums, texts, false, false, false, true);
}
else
{
ErrorManager.recordOrThrowError(nums, texts, false, true);
}
}
finally
{
ErrorManager.setSilent(silent);
OutputParameterAssigner opa = TransactionManager.deregisterOutputParameterAssigner();
if (opa != null)
{
opa.abort();
}
}
}
/**
* Check if this type has any static members or methods.
*
* @return See above.
*/
private boolean hasStaticsInt()
{
if (hasStatics != null)
{
return hasStatics;
}
hasStatics = false;
// check if this has static members
Field[] fields = cls.getDeclaredFields();
for (Field f : fields)
{
if (Modifier.isStatic(f.getModifiers()))
{
hasStatics = true;
return true;
}
}
Method[] methods = cls.getDeclaredMethods();
for (Method m : methods)
{
if (Modifier.isStatic(m.getModifiers()))
{
hasStatics = true;
return true;
}
}
return hasStatics;
}
/**
* Check if the class of this instance is an enum and raise an error 18110 if the class is not an enum.
*
* @return {@code true} if this is a valid enum class.
*
* @throws ErrorConditionException
* When called for a class that is not a subclass of {@code LegacyEnum} or {@code FlagsEnum}.
*/
private boolean isValidEnum()
{
// only the subclasses of LegacyEnum and FlagsEnum are allowed
if (!LegacyEnum.class.isAssignableFrom(cls) || cls == LegacyEnum.class || cls == FlagsEnum.class)
{
String cname = ObjectOps.getLegacyName(cls);
String msg = String.format("Class '%s' is not an enum type", cname);
ErrorManager.recordOrThrowError(18110, msg, false);
return false;
}
return true;
}
@Override
public logical legacyEquals(object<? extends _BaseObject_> other)
{
if (!other.isUnknown() && other.ref() instanceof LegacyClass)
return new logical(typeName.equals(((LegacyClass) other.ref()).typeName));
return super.legacyEquals(other);
}
@LegacySignature(returns = "OBJECT", type = Type.METHOD, name = "GetConstructor", qualified = "progress.reflect.constructor", parameters =
{
@LegacyParameter(name = "parms", type = "OBJECT", qualified = "progress.lang.parameterlist", mode = "INPUT")
})
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB)
public object<? extends LegacyConstructor> getConstructor(final object<? extends com.goldencode.p2j.oo.lang.ParameterList> _parms)
{
UnimplementedFeature.missing("Progress.Lang.Class:getConstructor(ParameterList)");
return new object<>();
}
@LegacySignature(returns = "OBJECT", type = Type.METHOD, name = "GetConstructor", qualified = "progress.reflect.constructor", parameters =
{
@LegacyParameter(name = "flags", type = "OBJECT", qualified = "progress.reflect.flags", mode = "INPUT"),
@LegacyParameter(name = "parms", type = "OBJECT", qualified = "progress.lang.parameterlist", mode = "INPUT")
})
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB)
public object<? extends LegacyConstructor> getConstructor(final object<? extends com.goldencode.p2j.oo.reflect.Flags> _flags, final object<? extends com.goldencode.p2j.oo.lang.ParameterList> _parms)
{
UnimplementedFeature.missing("Progress.Lang.Class:getConstructor(Flags, ParameterList)");
return new object<>();
}
@LegacySignature(returns = "OBJECT", type = Type.METHOD, name = "GetConstructors", extent = -1, qualified = "progress.reflect.constructor")
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB)
public object<? extends LegacyConstructor>[] getConstructors()
{
UnimplementedFeature.missing("Progress.Lang.Class:getConstructor()");
return new object[0];
}
@LegacySignature(returns = "OBJECT", type = Type.METHOD, name = "GetConstructors", extent = -1, qualified = "progress.reflect.constructor", parameters =
{
@LegacyParameter(name = "flags", type = "OBJECT", qualified = "progress.reflect.flags", mode = "INPUT")
})
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB)
public object<? extends LegacyConstructor>[] getConstructors(final object<? extends com.goldencode.p2j.oo.reflect.Flags> _flags)
{
UnimplementedFeature.missing("Progress.Lang.Class:getConstructor(Flags)");
return new object[0];
}
@LegacySignature(returns = "OBJECT", type = Type.METHOD, name = "GetEvent", qualified = "progress.reflect.event", parameters =
{
@LegacyParameter(name = "name", type = "CHARACTER", mode = "INPUT")
})
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB)
public object<? extends com.goldencode.p2j.oo.reflect.Event> getEvent(final character _name)
{
UnimplementedFeature.missing("Progress.Lang.Class:GetEvent(character)");
return new object();
}
@LegacySignature(returns = "OBJECT", type = Type.METHOD, name = "GetEvent", qualified = "progress.reflect.event", parameters =
{
@LegacyParameter(name = "name", type = "CHARACTER", mode = "INPUT"),
@LegacyParameter(name = "flags", type = "OBJECT", qualified = "progress.reflect.flags", mode = "INPUT")
})
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB)
public object<? extends com.goldencode.p2j.oo.reflect.Event> getEvent(final character _name, final object<? extends com.goldencode.p2j.oo.reflect.Flags> _flags)
{
UnimplementedFeature.missing("Progress.Lang.Class:GetEvent(character, Flags)");
return new object();
}
@LegacySignature(returns = "OBJECT", type = Type.METHOD, name = "GetEvents", extent = -1, qualified = "progress.reflect.event")
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB)
public object<? extends com.goldencode.p2j.oo.reflect.Event>[] getEvents()
{
UnimplementedFeature.missing("Progress.Lang.Class:getEvents()");
return new object[0];
}
@LegacySignature(returns = "OBJECT", type = Type.METHOD, name = "GetEvents", extent = -1, qualified = "progress.reflect.event", parameters =
{
@LegacyParameter(name = "flags", type = "OBJECT", qualified = "progress.reflect.flags", mode = "INPUT")
})
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB)
public object<? extends com.goldencode.p2j.oo.reflect.Event>[] getEvents(final object<? extends com.goldencode.p2j.oo.reflect.Flags> _flags)
{
UnimplementedFeature.missing("Progress.Lang.Class:getEvents(Flags)");
return new object[0];
}
/**
* Resolve the interfaces implemented by this class or any super class or super-interface.
* If no interface is implemented, it returns a zero-length array.
* The order of the returned interfaces is undetermined.
*
* @return The array of {@link LegacyClass} instances for each interface of this class.
*/
@LegacySignature(returns = "OBJECT", type = Type.METHOD, name = "GetInterfaces", extent = -1, qualified = "progress.lang.class")
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL)
public object<? extends LegacyClass>[] getInterfaces()
{
Set<Class<?>> all = new LinkedHashSet<>();
Utils.collectInterfaces(cls, all);
// collect all legacy interfaces
Iterator<Class<?>> iter = all.iterator();
List<object<? extends LegacyClass>> ifaces = new ArrayList<>(all.size());
while (iter.hasNext())
{
Class<?> type = iter.next();
if (!_BaseObject_.class.isAssignableFrom(type) || type == _BaseObject_.class)
{
continue;
}
ifaces.add(ObjectOps.getLegacyClass((Class) type));
}
object<? extends LegacyClass>[] res = new object[ifaces.size()];
if (!ifaces.isEmpty())
{
for (int i = 0; i < ifaces.size(); i++)
{
res[i] = ifaces.get(i);
}
}
return res;
}
@LegacySignature(returns = "OBJECT", type = Type.METHOD, name = "GetMethod", qualified = "progress.reflect.method", parameters =
{
@LegacyParameter(name = "method-name", type = "CHARACTER", mode = "INPUT"),
@LegacyParameter(name = "parms", type = "OBJECT", qualified = "progress.lang.parameterlist", mode = "INPUT")
})
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL)
public object<? extends Legacy4GLMethod> getMethod(final character _methodName, final object<? extends com.goldencode.p2j.oo.lang.ParameterList> _parms)
{
return getMethod(_methodName, (object<? extends Flags>) FlagsEnum.or(Flags.instance, Flags.public_), _parms);
}
@LegacySignature(returns = "OBJECT", type = Type.METHOD, name = "GetMethod", qualified = "progress.reflect.method", parameters =
{
@LegacyParameter(name = "method-name", type = "CHARACTER", mode = "INPUT"),
@LegacyParameter(name = "flags", type = "OBJECT", qualified = "progress.reflect.flags", mode = "INPUT"),
@LegacyParameter(name = "params", type = "OBJECT", qualified = "progress.lang.parameterlist", mode = "INPUT")
})
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL)
public object<? extends Legacy4GLMethod> getMethod(final character _methodName, final object<? extends com.goldencode.p2j.oo.reflect.Flags> _flags, final object<? extends com.goldencode.p2j.oo.lang.ParameterList> _params)
{
// if flags is unknown - the invalid handle exception is thrown in 4GL
Flags flags = _flags.ref();
if (_params.isUnknown() || !_params.ref().isComplete())
{
ErrorManager.recordOrThrowError(15303, "An invalid or incomplete parameter list was specified when using reflection", false);
return new object<>();
}
ParameterList params = _params.ref();
String modes = params.getModes();
Object[] args = params.getArgs();
InternalEntry ie = ControlFlowOps.resolveLegacyEntry(false, cls, typeName, _methodName.getValue(),
flags.isFlagSet(Flags.static_).booleanValue(), modes, args);
if (ie != null && Legacy4GLMethod.is4GLMethod(ie.getMethod(), flags))
{
Legacy4GLMethod legacy4GLMethod = classMethods.get(ie.getMethod());
if (legacy4GLMethod == null)
{
legacy4GLMethod = new Legacy4GLMethod(this, ie);
classMethods.put(ie.getMethod(), legacy4GLMethod);
}
return new object<>(legacy4GLMethod);
}
return new object<>();
}
@LegacySignature(returns = "OBJECT", type = Type.METHOD, name = "GetMethods", extent = -1, qualified = "progress.reflect.method")
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL)
public object<? extends Legacy4GLMethod>[] getMethods()
{
return getMethods((object<? extends Flags>) FlagsEnum.or(Flags.instance, Flags.public_));
}
@LegacySignature(returns = "OBJECT", type = Type.METHOD, name = "GetMethods", extent = -1, qualified = "progress.reflect.method", parameters =
{
@LegacyParameter(name = "flags", type = "OBJECT", qualified = "progress.reflect.flags", mode = "INPUT")
})
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL)
public object<? extends Legacy4GLMethod>[] getMethods(final object<? extends com.goldencode.p2j.oo.reflect.Flags> flags)
{
object<? extends Legacy4GLMethod>[] res = null;
object<? extends LegacyClass> legacyClass = LegacyClass.getLegacyClass(typeName);
// if flags is unknown - the invalid handle exception is thrown in 4GL
Set<Method> methods = _getMethods(flags.ref(), legacyClass);
res = new object[methods.size()];
if (!methods.isEmpty())
{
int i = 0;
for (Method method: methods)
{
Legacy4GLMethod legacy4GLMethod = classMethods.get(method);
if (legacy4GLMethod == null)
{
legacy4GLMethod = new Legacy4GLMethod(this, method);
classMethods.put(method, legacy4GLMethod);
}
res[i] = new object<> (legacy4GLMethod);
i ++;
}
}
return res;
}
@LegacySignature(returns = "OBJECT", type = Type.METHOD, name = "GetProperties", extent = -1, qualified = "progress.reflect.property")
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL)
public object<? extends com.goldencode.p2j.oo.reflect.Property>[] getProperties()
{
return getProperties((object<? extends Flags>) FlagsEnum.or(Flags.instance, Flags.public_));
}
@LegacySignature(returns = "OBJECT", type = Type.METHOD, name = "GetProperties", extent = -1, qualified = "progress.reflect.property", parameters =
{
@LegacyParameter(name = "flags", type = "OBJECT", qualified = "progress.reflect.flags", mode = "INPUT")
})
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL)
public object<? extends com.goldencode.p2j.oo.reflect.Property>[] getProperties(final object<? extends com.goldencode.p2j.oo.reflect.Flags> flags)
{
object<? extends com.goldencode.p2j.oo.reflect.Property>[] res = null;
object<? extends LegacyClass> legacyClass = LegacyClass.getLegacyClass(typeName);
// if flags is unknown - the invalid handle exception is thrown in 4GL
Set<Property> properties = _getProperties(flags.ref(), legacyClass);
res = new object[properties.size()];
if (!properties.isEmpty())
{
int i = 0;
for (Property property: properties)
{
res[i] = new object<> (property);
i ++;
}
}
return res;
}
@LegacySignature(returns = "OBJECT", type = Type.METHOD, name = "GetProperty", qualified = "progress.reflect.property", parameters =
{
@LegacyParameter(name = "name", type = "CHARACTER", mode = "INPUT")
})
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL)
public object<? extends com.goldencode.p2j.oo.reflect.Property> getProperty(final character _name)
{
return getProperty(_name, (object<? extends Flags>) FlagsEnum.or(Flags.instance, Flags.public_));
}
@LegacySignature(returns = "OBJECT", type = Type.METHOD, name = "GetProperty", qualified = "progress.reflect.property", parameters =
{
@LegacyParameter(name = "name", type = "CHARACTER", mode = "INPUT"),
@LegacyParameter(name = "flags", type = "OBJECT", qualified = "progress.reflect.flags", mode = "INPUT")
})
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL)
public object<? extends com.goldencode.p2j.oo.reflect.Property> getProperty(final character name, final object<? extends com.goldencode.p2j.oo.reflect.Flags> _flags)
{
// if flags is unknown - the invalid handle exception is thrown in 4GL
Flags flags = _flags.ref();
Property property = classProperties.get(name.getValue());
if (property != null)
{
Method method = getBasePropertyMethod(property.getter(), property.setter());
if (checkMethodFlags(method, flags))
{
return new object<>(property);
}
}
object<? extends LegacyClass> legacyClass = LegacyClass.getLegacyClass(typeName);
property = _getProperty(name, flags, legacyClass);
if (property != null)
{
classProperties.put(name.getValue(), property);
return new object<>(property);
}
return new object<>();
}
@LegacySignature(returns = "OBJECT", type = Type.METHOD, name = "GetVariables", extent = -1, qualified = "progress.reflect.variable")
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL)
public object<? extends com.goldencode.p2j.oo.reflect.Variable>[] getVariables()
{
return getVariables((object<? extends Flags>) FlagsEnum.or(Flags.instance, Flags.public_));
}
@LegacySignature(returns = "OBJECT", type = Type.METHOD, name = "GetVariables", extent = -1, qualified = "progress.reflect.variable", parameters =
{
@LegacyParameter(name = "flags", type = "OBJECT", qualified = "progress.reflect.flags", mode = "INPUT")
})
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL)
public object<? extends com.goldencode.p2j.oo.reflect.Variable>[] getVariables(final object<? extends com.goldencode.p2j.oo.reflect.Flags> _flags)
{
object<? extends Variable>[] res = null;
object<? extends LegacyClass> legacyClass = LegacyClass.getLegacyClass(typeName);
// if flags is unknown - the invalid handle exception is thrown in 4GL
Set<Variable> variables = _getVariables(_flags.ref(), legacyClass);
res = new object[variables.size()];
if (!variables.isEmpty())
{
int i = 0;
for (Variable variable: variables)
{
res[i] = new object<> (variable);
i ++;
}
}
return res;
}
@LegacySignature(returns = "OBJECT", type = Type.METHOD, name = "GetVariable", qualified = "progress.reflect.variable", parameters =
{
@LegacyParameter(name = "name", type = "CHARACTER", mode = "INPUT")
})
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL)
public object<? extends com.goldencode.p2j.oo.reflect.Variable> getVariable(final character _name)
{
return getVariable(_name, (object<? extends Flags>) FlagsEnum.or(Flags.instance, Flags.public_));
}
@LegacySignature(returns = "OBJECT", type = Type.METHOD, name = "GetVariable", qualified = "progress.reflect.variable", parameters =
{
@LegacyParameter(name = "name", type = "CHARACTER", mode = "INPUT"),
@LegacyParameter(name = "flags", type = "OBJECT", qualified = "progress.reflect.flags", mode = "INPUT")
})
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL)
public object<? extends com.goldencode.p2j.oo.reflect.Variable> getVariable(final character name, final object<? extends com.goldencode.p2j.oo.reflect.Flags> _flags)
{
Flags flags = _flags.ref();
Variable variable = classVariables.get(name);
if (variable != null)
{
Field field = variable.getField();
if (checkFieldFlags(field, flags))
{
return new object<>(variable);
}
}
object<? extends LegacyClass> legacyClass = LegacyClass.getLegacyClass(typeName);
variable = _getVariable(name, flags, legacyClass);
if (variable != null)
{
classVariables.put(name.getValue(), variable);
return new object<>(variable);
}
return new object<>();
}
@LegacySignature(type = Type.METHOD, name = "SetPropertyValue", parameters =
{
@LegacyParameter(name = "pname", type = "CHARACTER", mode = "INPUT"),
@LegacyParameter(name = "value", type = "BDT", mode = "INPUT")
})
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL)
public void setPropertyValue(final character _pname, final Object _value)
{
object<? extends com.goldencode.p2j.oo.reflect.Flags> flags = new object<com.goldencode.p2j.oo.reflect.Flags>();
flags.assign(com.goldencode.p2j.oo.reflect.Flags.public_);
flags.assign(flags.ref().setFlag(com.goldencode.p2j.oo.reflect.Flags.static_));
flags.assign(flags.ref().setFlag(com.goldencode.p2j.oo.reflect.Flags.instance));
flags.assign(flags.ref().setFlag(com.goldencode.p2j.oo.reflect.Flags.private_));
flags.assign(flags.ref().setFlag(com.goldencode.p2j.oo.reflect.Flags.protected_));
object<? extends com.goldencode.p2j.oo.reflect.Property> prop = getProperty(_pname, flags);
Property property = prop.ref();
if (property == null)
{
String msg = "Could not find property " + "'" + _pname.getValue() + "'" + " in class '" + typeName + "'";
ErrorManager.recordOrThrowError(16548, msg, false);
return;
}
if (!property.getIsStatic().booleanValue())
{
String msg = "Non-static member " + "'" + _pname.getValue() + "'" + " must be accessed via an object instance, "
+ "not via a class name";
ErrorManager.recordOrThrowError(14633, msg, false);
return;
}
if (property.getAccessMode().ref().equals(AccessMode.private_.ref()))
{
String msg = "Property " + _pname.getValue() + " has a PRIVATE SET accessor so can be set only from "
+ "within the class where it's defined";
ErrorManager.recordOrThrowError(13855, msg, false);
return;
}
property.set(_value);
}
@LegacySignature(type = Type.METHOD, name = "SetPropertyValue", parameters = {
@LegacyParameter(name = "pname", type = "CHARACTER", mode = "INPUT"),
@LegacyParameter(name = "index", type = "INTEGER", mode = "INPUT"),
@LegacyParameter(name = "value", type = "BDT", mode = "INPUT") })
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB)
public void setPropertyValue(final character _pname, final integer _index,
final Object _value)
{
UnimplementedFeature.missing("Progress.Lang.Class:setPropertyValue(character,integer,bdt)");
}
@LegacySignature(type = Type.METHOD, name = "SetPropertyValue", parameters =
{
@LegacyParameter(name = "obj-ref", type = "OBJECT", qualified = "progress.lang.object", mode = "INPUT"),
@LegacyParameter(name = "pname", type = "CHARACTER", mode = "INPUT"),
@LegacyParameter(name = "value", type = "BDT", mode = "INPUT")
})
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL)
public void setPropertyValue(final object<? extends com.goldencode.p2j.oo.lang._BaseObject_> _objRef, final character _pname, final Object _value)
{
object<? extends com.goldencode.p2j.oo.reflect.Flags> flags = new object<com.goldencode.p2j.oo.reflect.Flags>();
flags.assign(com.goldencode.p2j.oo.reflect.Flags.public_);
flags.assign(flags.ref().setFlag(com.goldencode.p2j.oo.reflect.Flags.static_));
flags.assign(flags.ref().setFlag(com.goldencode.p2j.oo.reflect.Flags.instance));
flags.assign(flags.ref().setFlag(com.goldencode.p2j.oo.reflect.Flags.private_));
flags.assign(flags.ref().setFlag(com.goldencode.p2j.oo.reflect.Flags.protected_));
object<? extends com.goldencode.p2j.oo.reflect.Property> prop = getProperty(_pname, flags);
Property property = prop.ref();
if (property == null)
{
String msg = "Could not find property " + "'" + _pname.getValue() + "'" + " in class '" + typeName + "'";
ErrorManager.recordOrThrowError(16548, msg, false);
return;
}
if (property.getIsStatic().booleanValue())
{
String msg = "Static member '" + _pname.getValue() + " must be accessed via a class name, "
+ "not via an object instance";
ErrorManager.recordOrThrowError(14634, msg, false);
return;
}
if (property.getAccessMode().ref().equals(AccessMode.private_.ref()))
{
String msg = "Property " + _pname.getValue() + " has a PRIVATE SET accessor so can be set only from "
+ "within the class where it's defined";
ErrorManager.recordOrThrowError(13855, msg, false);
return;
}
property.set(_objRef, _value);
}
@LegacySignature(type = Type.METHOD, name = "SetPropertyValue", parameters = {
@LegacyParameter(name = "obj-ref", type = "OBJECT", qualified = "progress.lang.object", mode = "INPUT"),
@LegacyParameter(name = "pname", type = "CHARACTER", mode = "INPUT"),
@LegacyParameter(name = "index", type = "INTEGER", mode = "INPUT"),
@LegacyParameter(name = "value", type = "BDT", mode = "INPUT") })
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB)
public void setPropertyValue(
final object<? extends com.goldencode.p2j.oo.lang._BaseObject_> _objRef,
final character _pname, final integer _index,
final Object _value)
{
UnimplementedFeature.missing("Progress.Lang.Class:setPropertyValue(progress.lang.object,character,integer,bdt)");
}
public Class<? extends _BaseObject_> getJavaClass()
{
return cls;
}
/**
* Get the list of 4gl methods(annotated) filtered as per given flags.
*
* @param flags
* The flags enum to filter the returned methods.
* @param clazz
* The Java class to collect methods from.
* @return The list of 4GL methods.
*/
private Set<Method> _getMethods (Flags flags, object <? extends LegacyClass> legacyClass)
{
Set<Method> methods = new LinkedHashSet<>();
if (legacyClass.isUnknown())
{
return methods;
}
Class<? extends _BaseObject_> cls = legacyClass.ref().getType();
for (Method method : cls.getDeclaredMethods())
{
if (Legacy4GLMethod.is4GLMethod(method, flags))
{
methods.add(method);
}
}
if (cls.getSuperclass() != null && !flags.isFlagSet(Flags.declaredOnly).booleanValue())
{
for (Method method : _getMethods(flags, legacyClass.ref().getSuperClass()))
{
if (!methods.stream().anyMatch(m -> m.getName().equals(method.getName()) && Arrays.equals(m.getParameters(), method.getParameters())))
{
methods.add(method);
}
}
}
return methods;
}
/**
* Get the list of 4gl variable(annotated) filtered as per given flags.
*
* @param flags
* The flags enum to filter the returned methods.
*
* @param clazz
* The Java class to collect methods from.
*
* @return The list of 4GL variable.
*/
private Set<Variable> _getVariables (Flags flags, object <? extends LegacyClass> legacyClass)
{
Set<Variable> variables = new LinkedHashSet<>();
if (legacyClass.isUnknown())
{
return variables;
}
Class<? extends _BaseObject_> cls = legacyClass.ref().getType();
for (Field field : cls.getDeclaredFields())
{
if (Variable.isVariable(field) && checkFieldFlags(field, flags))
{
String name = field.getName();
Variable variable = classVariables.get(name);
if (variable == null)
{
variable = new Variable(legacyClass.ref(), field);
classVariables.put(name, variable);
}
variables.add(variable);
}
}
if (cls.getSuperclass() != null && !flags.isFlagSet(Flags.declaredOnly).booleanValue())
{
for (Variable varibale : _getVariables(flags, legacyClass.ref().getSuperClass()))
{
if (!variables.contains(varibale))
{
variables.add(varibale);
}
}
}
return variables;
}
/**
* Get the 4gl variable(annotated) with the given name filtered as per given flags.
*
* @param name
* The name of the variable.
*
* @param flags
* The flags enum to filter the returned methods.
*
* @param clazz
* The Java class to collect methods from.
*
* @return The 4GL variable.
*/
private Variable _getVariable (character name, Flags flags, object <? extends LegacyClass> legacyClass)
{
if (legacyClass.isUnknown())
{
return null;
}
Class<? extends _BaseObject_> cls = legacyClass.ref().getType();
for (Field field : cls.getDeclaredFields())
{
if (Variable.isVariable(field))
{
LegacySignature lsig = field.getDeclaredAnnotation(LegacySignature.class);
if (lsig == null || !lsig.name().equalsIgnoreCase(name.getValue()))
{
continue;
}
if (checkFieldFlags(field, flags))
{
Variable variable = classVariables.get(lsig.name());
if (variable == null)
{
variable = new Variable(legacyClass.ref(), field);
classVariables.put(lsig.name(), variable);
}
return variable;
}
}
}
if (cls.getSuperclass() != null &&
cls.getSuperclass() != Object.class &&
!flags.isFlagSet(Flags.declaredOnly).booleanValue())
{
return _getVariable(name, flags, legacyClass.ref().getSuperClass());
}
return null;
}
/**
* Get the list of 4gl properties(annotated) filtered as per given flags.
*
* @param flags
* The flags enum to filter the returned methods.
*
* @param clazz
* The Java class to collect methods from.
*
* @return The list of 4GL variable.
*/
private Set<Property> _getProperties (Flags flags, object <? extends LegacyClass> legacyClass)
{
Set<Property> properties = new LinkedHashSet<>();
if (legacyClass.isUnknown())
{
return properties;
}
Class<? extends _BaseObject_> cls = legacyClass.ref().getType();
Map<String, Method> getters = getters(cls);
Map<String, Method> setters = setters(cls);
for (Field field : cls.getDeclaredFields())
{
if (Property.isProperty(field))
{
LegacySignature lsig = field.getDeclaredAnnotation(LegacySignature.class);
String propertyName = lsig.name();
Method getter = getters.remove(propertyName);
Method setter = setters.remove(propertyName);
Method inferedMethod = getBasePropertyMethod(getter, setter);
if (checkMethodFlags (inferedMethod, flags))
{
Property property = classProperties.get(propertyName);
if (property == null)
{
property = new Property(legacyClass.ref(), field, getter, setter);
classProperties.put(propertyName, property);
}
properties.add(property);
}
}
}
for (String property: getters.keySet())
{
Method getter = getters.get(property);
Method setter = setters.remove(property);
Method inferedMethod = getBasePropertyMethod(getter, setter);
LegacySignature lsig = inferedMethod.getDeclaredAnnotation(LegacySignature.class);
if (checkMethodFlags (inferedMethod, flags))
{
Property existingProperty = classProperties.get(lsig.name());
if (existingProperty == null)
{
existingProperty = new Property(legacyClass.ref(), null, getter, setter);
classProperties.put(lsig.name(), existingProperty);
}
properties.add(existingProperty);
}
}
for (String property: setters.keySet())
{
Method setter = setters.get(property);
Method getter = getters.remove(property);
Method inferedMethod = getBasePropertyMethod(getter, setter);
LegacySignature lsig = inferedMethod.getDeclaredAnnotation(LegacySignature.class);
if (checkMethodFlags (inferedMethod, flags))
{
Property existingProperty = classProperties.get(lsig.name());
if (existingProperty == null)
{
existingProperty = new Property(legacyClass.ref(), null, getter, setter);
classProperties.put(lsig.name(), existingProperty);
}
properties.add(new Property(this, null, getter, setter));
}
}
if (cls.getSuperclass() != null && !flags.isFlagSet(Flags.declaredOnly).booleanValue())
{
for (Property property : _getProperties(flags, legacyClass.ref().getSuperClass()))
{
if (!properties.contains(property))
{
properties.add(property);
}
}
}
return properties;
}
/**
* Get the 4gl variable(annotated) with the given name filtered as per given flags.
*
* @param name
* The name of the variable.
*
* @param flags
* The flags enum to filter the returned methods.
*
* @param clazz
* The Java class to collect methods from.
*
* @return The 4GL variable.
*/
Property _getProperty (character name, Flags flags, object <? extends LegacyClass> legacyClass)
{
if (legacyClass.isUnknown())
{
return null;
}
Class<? extends _BaseObject_> cls = legacyClass.ref().getType();
Map<String, Method> getters = getters(cls);
Map<String, Method> setters = setters(cls);
for (Field field : cls.getDeclaredFields())
{
if (Property.isProperty(field))
{
LegacySignature lsig = field.getDeclaredAnnotation(LegacySignature.class);
if (lsig == null || !lsig.name().equalsIgnoreCase(name.getValue()))
{
continue;
}
String propertyName = lsig.name();
Method getter = getters.remove(propertyName);
Method setter = setters.remove(propertyName);
Method inferedMethod = getBasePropertyMethod(getter, setter);
if (checkMethodFlags (inferedMethod, flags))
{
Property property = classProperties.get(lsig.name());
if (property == null)
{
property = new Property(legacyClass.ref(), field, getter, setter);
classProperties.put(lsig.name(), property);
}
return property;
}
}
}
for (String property: getters.keySet())
{
Method getter = getters.get(property);
Method setter = setters.remove(property);
LegacySignature lsig = getter.getDeclaredAnnotation(LegacySignature.class);
if (lsig == null || !lsig.name().equals(name.getValue()))
{
continue;
}
Method inferedMethod = getBasePropertyMethod(getter, setter);
if (checkMethodFlags (inferedMethod, flags))
{
Property existingProperty = classProperties.get(lsig.name());
if (existingProperty == null)
{
existingProperty = new Property(legacyClass.ref(), null, getter, setter);
classProperties.put(lsig.name(), existingProperty);
}
return existingProperty;
}
}
for (String property: setters.keySet())
{
Method setter = setters.get(property);
Method getter = getters.remove(property);
LegacySignature lsig = setter.getDeclaredAnnotation(LegacySignature.class);
if (lsig == null || !lsig.name().equals(name.getValue()))
{
continue;
}
Method inferedMethod = getBasePropertyMethod(getter, setter);
if (checkMethodFlags (inferedMethod, flags))
{
Property existingProperty = classProperties.get(lsig.name());
if (existingProperty == null)
{
existingProperty = new Property(legacyClass.ref(), null, getter, setter);
classProperties.put(lsig.name(), existingProperty);
}
return existingProperty;
}
}
if (cls.getSuperclass() != null &&
cls.getSuperclass() != Object.class &&
!flags.isFlagSet(Flags.declaredOnly).booleanValue())
{
return _getProperty(name, flags, legacyClass.ref().getSuperClass());
}
return null;
}
/**
* Get the map of 4gl getters (annotated) with name of the property as key.
*
* @param flags
* The flags enum to filter the returned methods.
*
* @param clazz
* The Java class to collect methods from.
*
* @return The list of 4GL variable.
*/
private Map<String, Method> getters(Class<? extends _BaseObject_> clazz)
{
Map<String, Method> getters = new CaseInsensitiveHashMap<>();
Map<String, List<InternalEntry>> clsGetters = SourceNameMapper.getGetters(clazz);
for (String name: clsGetters.keySet())
{
List<InternalEntry> internalEntries = clsGetters.get(name);
InternalEntry ie = null;
int internalEntriesSize = internalEntries.size();
switch (internalEntriesSize)
{
case (1):
ie = internalEntries.get(0);
break;
case (2):
ie = internalEntries.get(0);
break;
default:
LOG.log(Level.SEVERE, "More than 2 GETTERS received, expected max 2.");
ie = internalEntries.get(0);
break;
}
Method getter = ie.getMethod();
getters.put(name, getter);
}
return getters;
}
/**
* Get the map of 4gl setters (annotated) with name of the property as key.
*
* @param flags
* The flags enum to filter the returned methods.
*
* @param clazz
* The Java class to collect methods from.
*
* @return The list of 4GL variable.
*/
private Map<String, Method> setters(Class<? extends _BaseObject_> clazz)
{
Map<String, Method> setters = new CaseInsensitiveHashMap<>();
Map<String, List<InternalEntry>> clsSetters = SourceNameMapper.getSetters(clazz);
for (String name: clsSetters.keySet())
{
List<InternalEntry> internalEntries = clsSetters.get(name);
InternalEntry ie = null;
int internalEntriesSize = internalEntries.size();
switch (internalEntriesSize)
{
case (1):
ie = internalEntries.get(0);
break;
case (2):
ie = internalEntries.get(0);
break;
default:
LOG.log(Level.SEVERE, "More than 2 GETTERS received, expected max 2.");
ie = internalEntries.get(0);
break;
}
Method setter = ie.getMethod();
setters.put(name, setter);
}
return setters;
}
/**
* Get the method with lower access mode between getter / setter.
*
* @param getter
* The getter java method.
*
* @param setter
* The setter java method.
*
* @return The method with lower access mode.
*/
public Method getBasePropertyMethod(Method getter, Method setter)
{
object<? extends Flags> getterModeFlag = getter != null ? Property.getAccesMode(getter) :
new object<>(Flags.private_);
object<? extends Flags> setterModeFlag = setter != null ? Property.getAccesMode(setter) :
new object<>(Flags.private_);
Method inferedMethod = null;
if (getterModeFlag.compareTo(setterModeFlag) <= 0)
{
inferedMethod = getter != null ? getter : setter;
}
else
{
inferedMethod = setter != null ? setter : getter;
}
return inferedMethod;
}
/**
* Check if the java method conforms to the specific flag selection criteria.
*
* @param javaMethod
* the java method.
* @param flags
* the Flags enum to use as selection criteria.
*
* @return <code>true</code> if the method has a valid signature for the input flags
*/
public boolean checkMethodFlags (Method method, Flags flags)
{
if ((Modifier.isStatic(method.getModifiers()) && !flags.isFlagSet(Flags.static_).booleanValue()) ||
(!Modifier.isStatic(method.getModifiers()) && !flags.isFlagSet(Flags.instance).booleanValue()))
{
return false;
}
if (Modifier.isPublic(method.getModifiers()))
{
return flags.isFlagSet(Flags.public_).booleanValue();
}
else if (Modifier.isProtected(method.getModifiers()))
{
return flags.isFlagSet(Flags.protected_).booleanValue();
}
else if (Modifier.isPrivate(method.getModifiers()))
{
return flags.isFlagSet(Flags.private_).booleanValue();
}
// we might need to add a new attribute in LegacySignature if we need to differentiate between the two
return flags.isFlagSet(Flags.packageProtected).booleanValue()
|| flags.isFlagSet(Flags.packagePrivate).booleanValue();
}
/**
* Check if the java field conforms to the specific flag selection criteria.
*
* @param javaField
* the java field.
* @param flags
* the Flags enum to use as selection criteria.
*
* @return <code>true</code> if the field has a valid signature for the input flags
*/
public static boolean checkFieldFlags (Field field, Flags flags)
{
if ((Modifier.isStatic(field.getModifiers()) && !flags.isFlagSet(Flags.static_).booleanValue()) ||
(!Modifier.isStatic(field.getModifiers()) && !flags.isFlagSet(Flags.instance).booleanValue()))
{
return false;
}
if (Modifier.isPublic(field.getModifiers()))
{
return flags.isFlagSet(Flags.public_).booleanValue();
}
else if (Modifier.isProtected(field.getModifiers()))
{
return flags.isFlagSet(Flags.protected_).booleanValue();
}
else if (Modifier.isPrivate(field.getModifiers()))
{
return flags.isFlagSet(Flags.private_).booleanValue();
}
// we might need to add a new attribute in LegacySignature if we need to differentiate between the two
return flags.isFlagSet(Flags.packageProtected).booleanValue()
|| flags.isFlagSet(Flags.packagePrivate).booleanValue();
}
/**
* Helper function that gets the internal entry which contains the GET method for a given
* property name of a class instance. All GET methods are also retrieved from the super-classes of
* the instance, in order to retrieve inherited properties, including Next-Sibling and Prev-Sibling.
* For EXTENT type properties the indexed getter is returned.
*
* @param instance
* The instance.
* @param propName
* The property name.
* @param idx
* The index.
* @return The internal entry which contains the GET method.
*/
private InternalEntry getGetterInternalEntry(object<? extends _BaseObject_> instance, character propName, Object idx)
{
Map<String, List<InternalEntry>> clsGetters = SourceNameMapper.getGetters(cls);
Map<String, List<InternalEntry>> clsSetters = SourceNameMapper.getSetters(cls);
Class<?> superClass = cls.getSuperclass();
while (superClass != Object.class)
{
Class<? extends _BaseObject_> BOSuperClass = (Class<? extends _BaseObject_>) superClass;
Map<String, List<InternalEntry>> clsSuperGetters = SourceNameMapper.getGetters(BOSuperClass);
clsGetters.putAll(clsSuperGetters);
Map<String, List<InternalEntry>> clsSuperSetters = SourceNameMapper.getSetters(BOSuperClass);
clsSetters.putAll(clsSuperSetters);
superClass = superClass.getSuperclass();
}
String propNameLC = propName.toJavaType().toLowerCase();
List<InternalEntry> internalEntries = clsGetters.get(propNameLC);
InternalEntry ie;
if (internalEntries == null || internalEntries.isEmpty())
{
List<InternalEntry> ieSetter = clsSetters.get(propNameLC);
StringBuilder sb = new StringBuilder();
if (ieSetter != null && !ieSetter.isEmpty())
{
sb.append("Property ").append("'").append(propName.toJavaType()).append("'")
.append(" in Class ").append("'").append(typeName).append("'")
.append(" does not allow read access");
ErrorManager.recordOrThrowError(13822, sb.toString());
}
else
{
sb.append("Could not find property ").append("'").append(propName.toJavaType()).append("'")
.append(" in class ").append("'").append(typeName).append("'");
ErrorManager.recordOrThrowError(16548, sb.toString());
}
// this shouldn't be reached as an error is thrown already
return null;
}
int internalEntriesSize = internalEntries.size();
int extentSize = -1;
//assumption is that in case of an EXTENT property, the first getter is the indexed getter
//and the second one is the bulk getter
switch (internalEntriesSize)
{
case (1):
ie = internalEntries.get(0);
break;
case (2):
InternalEntry bulkGetter = internalEntries.get(1);
if (idx == null)
{
ie = bulkGetter;
}
else
{
ie = internalEntries.get(0);
}
extentSize = bulkGetter.getExtent();
break;
default:
LOG.log(Level.SEVERE, "More than 2 GETTERS received, expected max 2.");
ie = internalEntries.get(0);
break;
}
int index = -1;
if (idx != null)
{
//idx is not null for non-extent type, invalid
if (extentSize == -1)
{
StringBuilder sb = new StringBuilder();
sb.append("Invalid runtime use of indeterminate extent. ")
.append("It must be set to a fixed dimension");
ErrorManager.recordOrThrowError(11387, sb.toString());
}
if (!(idx instanceof NumberType))
{
//TODO: Conversion to integer value of given data type
LOG.log(Level.SEVERE, "Index data type for non-numerical type is not implemented.");
}
NumberType indexNT = (NumberType) idx;
index = indexNT.intValue();
if (index < 1)
{
StringBuilder sb = new StringBuilder();
sb.append("Array subscript ").append(index).append("is out of range");
ErrorManager.recordOrThrowError(26, sb.toString());
}
if (index > extentSize)
{
StringBuilder sb = new StringBuilder();
sb.append("Array subscript ").append(index)
.append(" is out of range. The indeterminate extent is fixed to a ")
.append("dimension of ").append(extentSize);
ErrorManager.recordOrThrowError(11388, sb.toString());
}
}
Method getMethod = ie.getMethod();
int getModifier = getMethod.getModifiers();
if (Modifier.isPrivate(getModifier))
{
/* TODO: deny access from "outside" for private entries
StringBuilder sb = new StringBuilder();
sb.append("Property ")
.append(propName.toJavaType())
.append(" has a PRIVATE GET accessor so can be read ")
.append("only from within the class where it's defined");
ErrorManager.recordOrShowError(13833, sb.toString(), true);
*/
if (LOG.isLoggable(Level.WARNING))
{
StringBuilder sb = new StringBuilder();
sb.append("The GET method for ").append("'").append(propName.toJavaType()).append("' is PRIVATE")
.append("but the property was still retrieved ").append("without checking call place. TODO:")
.append(" implement call place checking.");
LOG.log(Level.WARNING, sb.toString());
}
getMethod.setAccessible(true);
}
if (Modifier.isStatic(getModifier) && instance != null)
{
StringBuilder sb = new StringBuilder();
sb.append("Static member ").append("'").append(propName.toJavaType()).append("'")
.append(" must be accessed via a class name, not via")
.append(" an object instance");
ErrorManager.recordOrThrowError(14634, sb.toString(), true);
}
else if (!Modifier.isStatic(getModifier) && instance == null)
{
StringBuilder sb = new StringBuilder();
sb.append("Non-static member ").append("'").append(propName.toJavaType()).append("'")
.append(" must be accessed via an object instance, not via ")
.append("a class name");
ErrorManager.recordOrShowError(14633, sb.toString(), true);
}
return ie;
}
/**
* Helper function used to handle GetPropertyValue, which returns a class property
* based on the property's name. The function gets the InternalEntry which
* holds the GET method of the converted 4GL class. The GET method is then invoked in order to obtain
* the property.
*
* @param instance
* The instance. It is passed as null when called by the static GetPropertyValue overloads.
* @param propName
* The name of the property.
* @param idx
* The index parameter. It is always null when called with the non indexed overload of
* GetPropertyValue.
* @return The value of the property.
*/
private BaseDataType getPropertyValueImpl(object<? extends _BaseObject_> instance,
character propName,
Object idx)
{
BaseDataType ret = null;
try
{
propName = (character) propName.val();
if (instance != null)
{
if (!instance._isValid())
{
String errorMsg = "Invalid handle. Not initialized or points to a deleted object";
ErrorManager.recordOrThrowError(3135, errorMsg, true);
}
if (instance.isUnknown())
{
String errorMsg = "Invalid object reference passed to PLC:Get/SetPropertyValue";
ErrorManager.recordOrThrowError(16580, errorMsg, true);
}
}
if (propName.isUnknown())
{
String errorMsg = "Invalid property name passed to PLC:Get/SetPropertyValue";
ErrorManager.recordOrThrowError(16581, errorMsg, true);
}
InternalEntry ie = getGetterInternalEntry(instance, propName, idx);
if (ie == null)
{
throw new IllegalStateException("Internal entry was not found for : " + propName);
}
Method getMethod = ie.getMethod();
Object invokeRet;
NumberType indexNT = (NumberType) idx;
if (instance != null)
{
//GET is for a non-EXTENT type. assumption is only the indexed getter requires a parameter
if (getMethod.getParameterCount() == 0)
{
invokeRet = getMethod.invoke(instance.ref());
}
else
{
invokeRet = getMethod.invoke(instance.ref(), new integer(indexNT.intValue()));
}
}
else
{
if (getMethod.getParameterCount() == 0)
{
invokeRet = getMethod.invoke(null);
}
else
{
invokeRet = getMethod.invoke(null, new integer(indexNT.intValue()));
}
}
if (invokeRet instanceof BaseDataType)
{
ret = (BaseDataType) invokeRet;
}
else
//invoked the bulk getter
if (invokeRet instanceof BaseDataType[])
{
ret = new unknown();
ErrorManager.recordOrThrowError(-567, "Return is of type EXTENT for GetPropertyValue which is not implemented.");
}
else
{
LOG.log(Level.SEVERE, "Unrecognized property data type for GetPropertyValue.");
}
}
catch (InvocationTargetException exc)
{
if (LOG.isLoggable(Level.SEVERE))
{
StringBuilder sb = new StringBuilder();
sb.append("Exception while attempting to get the property value: ").append(propName);
LOG.log(Level.SEVERE, sb.toString(), exc);
}
}
catch (IllegalAccessException exc)
{
if (LOG.isLoggable(Level.SEVERE))
{
StringBuilder sb = new StringBuilder();
sb.append("Unexpected IllegalAccessException while trying to invoke GET method for property ")
.append(propName).append(": ").append(exc.toString());
LOG.log(Level.SEVERE, sb.toString(), exc);
}
}
return ret;
}
}