# Bazaar merge directive format 2 (Bazaar 0.90) # revision_id: marian.edu@acorn.ro-20210622082106-dxv98qp0gs9k3osp # target_branch: sftp://medu@xfer.goldencode.com/opt/fwd/3821c # testament_sha1: 2e571609f77bc7b8017dcc80e8e69d20bee19048 # timestamp: 2021-06-22 11:21:23 +0300 # source_branch: 3821c.4384i.patch # base_revision_id: hc@goldencode.com-20210617110217-qgi4mmz4mvvh4d5z # # Begin patch === modified file 'src/com/goldencode/p2j/oo/core/LegacyString.java' --- src/com/goldencode/p2j/oo/core/LegacyString.java 2021-02-21 16:25:50 +0000 +++ src/com/goldencode/p2j/oo/core/LegacyString.java 2021-05-21 12:43:12 +0000 @@ -13,6 +13,7 @@ ** CA 20210113 Renamed trim() to trim_(), to follow NameConverter's rules. ** 006 CA 20210221 Fixed 'qualified', 'extent' and 'returns' annotations at the legacy ** signature. +** 007 ME 20210520 Set hashCode to return the actual string hashCode. */ /* @@ -680,9 +681,10 @@ })); } + @Override public int hashCode() { - return this.value.hashCode(); + return this.value.toStringMessage().hashCode(); } } === modified file 'src/com/goldencode/p2j/oo/core/collections/LegacyMap.java' --- src/com/goldencode/p2j/oo/core/collections/LegacyMap.java 2021-03-25 07:05:29 +0000 +++ src/com/goldencode/p2j/oo/core/collections/LegacyMap.java 2021-05-24 05:27:05 +0000 @@ -12,6 +12,7 @@ ** 003 CA 20210221 Fixed 'qualified', 'extent' and 'returns' annotations at the legacy ** signature. ** 004 ME 20210325 Increment/decrement object references (key/value) when added/removed. +** ME 20210521 Use a 'key' object with hashCode/equals overrides. */ /* @@ -79,6 +80,9 @@ import static com.goldencode.p2j.util.BlockManager.*; import static com.goldencode.p2j.util.InternalEntry.Type; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Map; @@ -93,6 +97,7 @@ public class LegacyMap extends BaseObject implements com.goldencode.p2j.oo.core.collections.Imap { private HashMap map = new LinkedHashMap(); + // 4GL BUG, size not reset on clear private int size = 0; @@ -214,8 +219,8 @@ return function(LegacyMap.class, this, "ContainsKey", logical.class, new Block((Body) () -> { - returnNormal(new logical(map.containsKey(poKey))); - })); + returnNormal(new logical(map.containsKey(new keyobject(poKey)))); + })); } @LegacySignature(returns = "LOGICAL", type = Type.METHOD, name = "ContainsAllKeys", parameters = { @@ -314,9 +319,10 @@ return function(LegacyMap.class, this, "Get", object.class, new Block((Body) () -> { - - if (map.containsKey(poKey)) - returnNormal(map.get(poKey)); + keyobject key = new keyobject(poKey); + + if (map.containsKey(key)) + returnNormal(map.get(key)); returnNormal(new object()); })); @@ -340,16 +346,18 @@ if (poKey.ref().equals(this)) undoThrow(AppError.newInstance(new character("A Map cannot have itself as key."))); - ObjectOps.increment(poKey.ref()); if (poValue._isValid()) { ObjectOps.increment(poValue.ref()); } - object obj = map.put(poKey, poValue); + object obj = map.put(new keyobject(poKey), poValue); - if (obj == null) + if (obj == null) + { + ObjectOps.increment(poKey.ref()); size++; + } returnNormal(obj != null ? obj : new object()); })); @@ -468,4 +476,30 @@ { return map; } + + class keyobject extends object { + + public keyobject(object other) + { + super(other); + } + + @Override + public int hashCode() + { + return _isValid() ? ref.hashCode() : 0; + } + + @Override + public boolean equals(Object obj) + { + if (obj instanceof object) + { + object other = (object) obj; + return _isValid() ? ref.legacyEquals(other).booleanValue() : !other._isValid(); + } + + return false; + } + } } === modified file 'src/com/goldencode/p2j/oo/lang/ParameterList.java' --- src/com/goldencode/p2j/oo/lang/ParameterList.java 2021-02-21 16:25:50 +0000 +++ src/com/goldencode/p2j/oo/lang/ParameterList.java 2021-05-10 06:17:30 +0000 @@ -14,6 +14,7 @@ ** CA 20210216 Methods which have an int parameter must use the BlockManager API for top-level block. ** 004 CA 20210221 Fixed 'qualified', 'extent' and 'returns' annotations at the legacy ** signature. +** 005 ME 20210507 Improve parameter validation (call syntax uses '-' as separator instead space). */ /* @@ -72,6 +73,9 @@ package com.goldencode.p2j.oo.lang; import static com.goldencode.p2j.util.BlockManager.*; + +import com.goldencode.p2j.persist.DataSetParameter; + import static com.goldencode.p2j.report.ReportConstants.*; import com.goldencode.p2j.util.*; @@ -241,8 +245,9 @@ ErrorManager.recordOrThrowError(15295, "Unusable datatype for SetParameter method.", false); } - CallMode cmode = CallMode.buildMode(mode); - if (cmode == null) + // space is used as separator instead of '-' for call, trailing spaces are allowed + CallMode cmode = CallMode.buildMode(TextOps.replaceAll(TextOps.rightTrim(mode), " ", "-")); + if (!validCallMode(cmode, type.getValue(), val)) { String msg = "SetParameter IOmode parameter is required in the overload" + " being used, and should be 'INPUT', 'OUTPUT', 'INPUT-OUTPUT'" @@ -252,7 +257,9 @@ if (!cmode.input && !Call.validOutputParameter(pos.intValue(), val)) { - returnNormal(new logical(false)); + ErrorManager.recordOrThrowError(15311, + "SetParameter value for OUTPUT parameters may not be a function or any expression unsuitable for output, nor a database field.", + false); } CallParameter param = Call.createParameter(type.getValue(), cmode, val, false, true); @@ -268,6 +275,47 @@ })); } + private boolean validCallMode(CallMode cmode, String type, Object val) + { + if (cmode != null) + { + if (val instanceof BaseDataTypeVariable) + { + val = ((BaseDataTypeVariable) val).get(); + } + + boolean needDataStructure = false; + + switch (cmode.mode) + { + // by-value is not accepted although it's default + case BY_VALUE: + return false; + case BIND: + if (cmode.inputOutput) + return false; + case BY_REFERENCE: + if (cmode.append) + return false; + needDataStructure = true; + + } + + needDataStructure = needDataStructure || cmode.append; + + if (needDataStructure) + return ("TABLE".equalsIgnoreCase(type) || "TABLE-HANDLE".equalsIgnoreCase(type) + || "DATASET".equalsIgnoreCase(type) + || "DATASET-HANDLE".equalsIgnoreCase(type)) + && (val instanceof TableParameter || val instanceof DataSetParameter + || val instanceof handle); + + return true; + } + + return false; + } + /** * Get the argument modes. * === modified file 'src/com/goldencode/p2j/oo/net/FileTypeRegistry.java' --- src/com/goldencode/p2j/oo/net/FileTypeRegistry.java 2021-02-21 16:25:50 +0000 +++ src/com/goldencode/p2j/oo/net/FileTypeRegistry.java 2021-05-24 06:41:34 +0000 @@ -9,6 +9,7 @@ ** 002 CA 20210113 Renamed clear() to clear_(), to follow NameConverter's rules. ** 003 CA 20210221 Fixed 'qualified', 'extent' and 'returns' annotations at the legacy ** signature. +** 004 ME 20210519 Fix registry lazy loading. */ /* @@ -81,9 +82,7 @@ import java.util.ArrayList; import java.util.HashMap; -import java.util.HashSet; import java.util.List; -import java.util.Map.Entry; import java.util.Set; /** @@ -164,11 +163,10 @@ return function(FileTypeRegistry.class, "Registry", object.class, new Block((Body) () -> { - if(registry.get().isUnknown()) + if(!registry.get()._isValid()) { - object oFTR = ObjectOps.newInstance(FileTypeRegistry.class); - initMap(oFTR); - registry.set(oFTR); + registry.get().assign(ObjectOps.newInstance(FileTypeRegistry.class)); + initMap(registry.get()); } returnNormal(registry.get()); })); === modified file 'src/com/goldencode/p2j/oo/net/http/ClientBuilder.java' --- src/com/goldencode/p2j/oo/net/http/ClientBuilder.java 2021-02-21 16:25:50 +0000 +++ src/com/goldencode/p2j/oo/net/http/ClientBuilder.java 2021-05-24 06:41:34 +0000 @@ -11,6 +11,7 @@ ** 004 ME 20200410 Complete implementation as of OE 11.7.4. ** 005 CA 20210221 Fixed 'qualified', 'extent' and 'returns' annotations at the legacy ** signature. +** 006 ME 20210519 Fix registry lazy loading. */ /* @@ -247,6 +248,8 @@ final object p2) { object _p1 = TypeFactory.initInput(p1); + object client = TypeFactory.object(IhttpClient.class); + return function(ClientBuilder.class, "DecorateClient", object.class, new Block((Body) () -> { object decoratorType = ObjectOps.newInstance(LegacyClass.class); @@ -260,8 +263,7 @@ else { Assert.isType(decoratorType, ObjectOps.getLegacyClass(HttpClientDecorator.class)); - object client = ObjectOps - .newInstance(decoratorType.ref().getType(), "I", p2); + client.assign(ObjectOps.newInstance(decoratorType.ref().getType(), "I", p2)); if (!client.isUnknown() && client.ref() instanceof ISupportInitialize) ((ISupportInitialize) client.ref()).initialize(); @@ -303,14 +305,12 @@ { return function(null, "Registry", object.class, new Block((Body) () -> { - object registry = ClientBuilder.registry.get(); - - if (!registry.isValid().booleanValue()) { - registry.assign(ObjectOps.newInstance(BuilderRegistry.class)); - initializeRegistry(registry); + if (!registry.get().isValid().booleanValue()) { + registry.get().assign(ObjectOps.newInstance(BuilderRegistry.class)); + initializeRegistry(registry.get()); } - returnNormal(registry); + returnNormal(registry.get()); })); } === modified file 'src/com/goldencode/p2j/oo/net/http/CookieJarBuilder.java' --- src/com/goldencode/p2j/oo/net/http/CookieJarBuilder.java 2021-02-21 16:25:50 +0000 +++ src/com/goldencode/p2j/oo/net/http/CookieJarBuilder.java 2021-05-24 06:41:34 +0000 @@ -11,6 +11,7 @@ ** 004 ME 20201028 Implement missing methods as of OE12.2. ** 005 CA 20210221 Fixed 'qualified', 'extent' and 'returns' annotations at the legacy ** signature. +** 006 ME 20210519 Fix registry lazy loading. */ /* @@ -124,18 +125,16 @@ { return function(null, "Registry", object.class, new Block((Body) () -> { - object oRegistry = CookieJarBuilder.registry.get(); - - if(!oRegistry._isValid()) + if(!registry.get()._isValid()) { - oRegistry.assign(ObjectOps.newInstance(BuilderRegistry.class)); + registry.get().assign(ObjectOps.newInstance(BuilderRegistry.class)); - oRegistry.ref().put(new character(ObjectOps.getLegacyName(CookieJarBuilder.class)), + registry.get().ref().put(new character(ObjectOps.getLegacyName(CookieJarBuilder.class)), ObjectOps.getLegacyClass(DefaultCookieJarBuilder.class)); - oRegistry.ref().put(new character(ObjectOps.getLegacyName(ICookieJar.class)), + registry.get().ref().put(new character(ObjectOps.getLegacyName(ICookieJar.class)), ObjectOps.getLegacyClass(CookieJar.class)); } - returnNormal(oRegistry); + returnNormal(registry.get()); })); } @@ -158,6 +157,8 @@ @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) public static object build() { + object oCJB = TypeFactory.object(CookieJarBuilder.class); + return function(CookieJarBuilder.class, "Build", object.class, new Block((Body)() -> { @@ -166,8 +167,7 @@ Assert.isType(oBuilderType, ObjectOps.getLegacyClass(CookieJarBuilder.class)); - object oCJB = (object) - ObjectOps.newInstance(oBuilderType.ref().getType()); + oCJB.assign(ObjectOps.newInstance(oBuilderType.ref().getType())); if (oCJB._isValid() && oCJB.ref() instanceof ISupportInitialize) ((ISupportInitialize) oCJB.ref()).initialize(); @@ -240,6 +240,7 @@ { object poDecorationType = TypeFactory.initInput(_poDecorationType); object poDecoratedClient = TypeFactory.initInput(_poDecoratedClient); + object decoratedClient = TypeFactory.object(ICookieJar.class); return function(CookieJarBuilder.class, "DecorateCookieJar", object.class, new Block((Body)() -> { @@ -251,7 +252,7 @@ Assert.isType(oType, ObjectOps.getLegacyClass(CookieJarDecorator.class)); - object decoratedClient = ObjectOps.newInstance(oType.ref().getType(), "I", poDecoratedClient); + decoratedClient.assign(ObjectOps.newInstance(oType.ref().getType(), "I", poDecoratedClient)); if (decoratedClient._isValid() && decoratedClient.ref() instanceof ISupportInitialize) ((ISupportInitialize) decoratedClient.ref()).initialize(); === modified file 'src/com/goldencode/p2j/oo/net/http/HttpClient.java' --- src/com/goldencode/p2j/oo/net/http/HttpClient.java 2021-02-21 16:25:50 +0000 +++ src/com/goldencode/p2j/oo/net/http/HttpClient.java 2021-05-24 06:39:56 +0000 @@ -15,6 +15,7 @@ ** CA 20210221 Fixed parameters at the LegacySignature annotation. ** 007 CA 20210221 Fixed 'qualified', 'extent' and 'returns' annotations at the legacy ** signature. +** 008 ME 20210524 Added support for retries, destroy library and fix variable scope on execute. */ /* @@ -80,8 +81,6 @@ import static com.goldencode.p2j.util.BlockManager.returnNormal; import com.goldencode.p2j.oo.core.*; import com.goldencode.p2j.oo.logging.*; -import com.goldencode.p2j.oo.net.http.filter.status.AuthorizationStatusFilter; -import com.goldencode.p2j.oo.net.http.filter.status.RedirectStatusFilter; import com.goldencode.p2j.oo.net.http.filter.writer.*; import com.goldencode.p2j.ui.LogicalTerminal; import com.goldencode.p2j.util.*; @@ -127,11 +126,15 @@ * Destroy. */ @LegacySignature(type = Type.METHOD, name = "Destroy") - @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB) + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) @Override public void destroy() { - internalProcedure(HttpClient.class, this, "Destroy", new Block()); + internalProcedure(HttpClient.class, this, "Destroy", new Block((Body) () -> + { + if (ObjectOps.typeOf(library, ISupportInitialize.class).booleanValue()) + ObjectOps.cast(library, ISupportInitialize.class).ref().destroy(); + })); } /** @@ -272,18 +275,19 @@ * * @return HTTP Response. */ - @LegacySignature(returns = "OBJECT", type = Type.METHOD, name = "Execute", qualified = IHTTPRESPONSE, parameters = - { - @LegacyParameter(name = "poRequest", type = "OBJECT", qualified = IHTTPREQUEST, mode = "INPUT") - }) - @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) + @LegacySignature(returns = "OBJECT", type = Type.METHOD, name = "Execute", qualified = IHTTPRESPONSE, parameters = { + @LegacyParameter(name = "poRequest", type = "OBJECT", qualified = IHTTPREQUEST, mode = "INPUT") }) + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL) @Override public object execute(object _poRequest) { object poRequest = TypeFactory.initInput(_poRequest); - return function(this, "execute", object.class, new Block((Body) () -> - { - object poResponse = ResponseBuilder.build().ref().getResponse(); + object poResponse = TypeFactory.object(IhttpResponse.class); + + return function(this, "execute", object.class, new Block((Init) () -> { + ObjectOps.register(poResponse); + }, (Body) () -> { + poResponse.assign(ResponseBuilder.build().ref().getResponse()); execute(poRequest, poResponse); returnNormal(poResponse); })); @@ -322,22 +326,78 @@ { object poRequest = TypeFactory.initInput(_poRequest); object poResponse = TypeFactory.initInput(_poResponse); - internalProcedure(this, "__net_http_FwdHttpClientLibrary_constructor__", new Block((Body) () -> + + internalProcedure(this, "execute", new Block((Body) () -> { - int retry = Math.max(options.ref().getNumRetries().intValue(), 0); - double retryPause = retry > 0 ? Math.max(options.ref().getPauseBetweenRetry().doubleValue(), 0) : 0; + + Assert.notNull(poRequest, new character("Http request")); + Assert.notNull(poResponse, new character("Http response")); + + int retries = Math.max(options.ref().getNumRetries().intValue(), 0); + int retry = 0; + double retryPause = retries > 0 ? Math.max(options.ref().getPauseBetweenRetry().doubleValue(), 0) : 0; + + raw reqHash = TypeFactory.raw(); + + if (retries > 0) + getLogger().ref().debug(TextOps.substitute("Initial request for &1 &2; max retries=&3", + poRequest.ref().getMethod(), + poRequest.ref().getUri().ref().toLegacyString(), + retries)); do { + if (retry > 0) + getLogger().ref().debug(TextOps.substitute("Retry attempt &1 of &2 for &3 &4", + retry, + retries, + poRequest.ref().getMethod(), + poRequest.ref().getUri().ref().toLegacyString())); + + addUserAgent(poRequest); + + _addAuthentication(poRequest); + + reqHash.assign(poRequest.ref().getContentMd5()); + library.ref().execute(poRequest, poResponse); - if (retry > 0 && retryPause > 0) - LogicalTerminal.pause(retryPause, (String) null); + if (processStatusAction(poRequest, poResponse).booleanValue()) + break; + + if (retries == 0 || (retries > 0 && CompareOps._isEqual(reqHash, poRequest.ref().getContentMd5()))) + { + retry++; + + if (retryPause > 0) + LogicalTerminal.pause(retryPause, (String) null); + } } - while(retry >= 0 && !processStatusAction(poRequest, poResponse).booleanValue()); + while(retry <= retries); })); } + private void _addAuthentication(object _poRequest) + { + object authRequest = TypeFactory + .object(IAuthenticatedRequest.class); + + if (ObjectOps.typeOf(_poRequest, IAdaptable.class).booleanValue()) + { + authRequest.assign(ObjectOps.cast(_poRequest, IAdaptable.class).ref() + .getAdapter(ObjectOps.getLegacyClass(IAuthenticatedRequest.class))); + } + + if (!authRequest._isValid() && ObjectOps.typeOf(_poRequest, IAuthenticatedRequest.class).booleanValue()) + { + authRequest.assign(ObjectOps.cast(_poRequest, IAuthenticatedRequest.class)); + } + + if (authRequest._isValid()) + authRequest.ref().addAuthentication(); + + } + /** * Process HttpResponse status code. * @@ -361,10 +421,11 @@ if (statusCodeWriter._isValid()) { - // really no point to retry if authorization is required but not an authenticated request - if (ObjectOps.typeOf(statusCodeWriter, AuthorizationStatusFilter.class).booleanValue() && - ObjectOps.typeOf(poRequest, AuthenticatedRequest.class).booleanValue()) - returnNormal(true); + // maybe there is no point to retry if authorization is required but + // not an authenticated request, still seems not to be the case in 4GL + // if (ObjectOps.typeOf(statusCodeWriter, AuthorizationStatusFilter.class).booleanValue() && + // ObjectOps.typeOf(poRequest, AuthenticatedRequest.class).booleanValue()) + // returnNormal(true); // copy session management info from initial response, if any _copyCookies(poResponse.ref(), poRequest.ref()); === modified file 'src/com/goldencode/p2j/oo/net/http/HttpClientDecorator.java' --- src/com/goldencode/p2j/oo/net/http/HttpClientDecorator.java 2021-02-21 16:25:50 +0000 +++ src/com/goldencode/p2j/oo/net/http/HttpClientDecorator.java 2021-05-24 05:32:17 +0000 @@ -13,6 +13,7 @@ ** - TypeFactory.initInput will take care of this. ** 005 CA 20210221 Fixed 'qualified', 'extent' and 'returns' annotations at the legacy ** signature. +** 006 ME 20210520 Fixed local variable scope for return parameter. */ /* @@ -183,9 +184,11 @@ public object execute( final object _poRequest) { + object poResponse = TypeFactory.object(IhttpResponse.class); + return function(HttpClientDecorator.class, this, "Execute", object.class, new Block((Body) () -> { - object poResponse = ResponseBuilder.build().ref().getResponse(); + poResponse.assign(ResponseBuilder.build().ref().getResponse()); execute(_poRequest, poResponse); === modified file 'src/com/goldencode/p2j/oo/net/http/HttpHeader.java' --- src/com/goldencode/p2j/oo/net/http/HttpHeader.java 2021-02-21 16:25:50 +0000 +++ src/com/goldencode/p2j/oo/net/http/HttpHeader.java 2021-05-24 06:41:34 +0000 @@ -13,6 +13,7 @@ ** ME 20210111 Fix toString (null header). ** 005 CA 20210221 Fixed 'qualified', 'extent' and 'returns' annotations at the legacy ** signature. +** 006 ME 20210519 Fix NULL header lazy loading. */ /* @@ -317,15 +318,12 @@ { return function(null, "NullHeader", object.class, new Block((Body) () -> { - object nullHeader = NULL.get(); - - if (!nullHeader._isValid()) + if (!NULL.get()._isValid()) { - nullHeader = ObjectOps.newInstance(NullHeader.class); - NULL.set(nullHeader); + NULL.get().assign(ObjectOps.newInstance(NullHeader.class)); } - returnNormal(nullHeader); + returnNormal(NULL.get()); })); } === modified file 'src/com/goldencode/p2j/oo/net/http/HttpHeaderBuilder.java' --- src/com/goldencode/p2j/oo/net/http/HttpHeaderBuilder.java 2021-02-23 07:39:32 +0000 +++ src/com/goldencode/p2j/oo/net/http/HttpHeaderBuilder.java 2021-05-24 06:41:34 +0000 @@ -11,6 +11,7 @@ ** 004 CA 20210216 Fixed getRegistry() - the static var must be assigned, and not re-initialized. ** 005 CA 20210221 Fixed 'qualified', 'extent' and 'returns' annotations at the legacy ** signature. +** 006 ME 20210519 Fix registry lazy loading. */ /* @@ -203,15 +204,13 @@ { return function(HttpHeaderBuilder.class, "Registry", object.class, new Block((Body) () -> { - object registry = REGISTRY.get(); - - if (!registry._isValid()) + if (!REGISTRY.get()._isValid()) { - registry.assign(ObjectOps.newInstance(BuilderRegistry.class, "I", + REGISTRY.get().assign(ObjectOps.newInstance(BuilderRegistry.class, "I", ObjectOps.getLegacyClass(HttpHeaderBuilder.class))); - initializeRegistry(registry); + initializeRegistry(REGISTRY.get()); } - returnNormal(registry); + returnNormal(REGISTRY.get()); })); } @@ -253,7 +252,8 @@ public static object build(final character _hName) { character hName = TypeFactory.initInput(_hName); - + object builder = TypeFactory.object(HttpHeaderBuilder.class); + return function(HttpHeaderBuilder.class, "Build", object.class, new Block((Body) () -> { Assert.notNullOrEmpty(hName, new character("Header name")); @@ -263,8 +263,7 @@ builderType.assign(getRegistry().ref().get(new character("*"))); } Assert.isType(builderType, ObjectOps.getLegacyClass(HttpHeaderBuilder.class)); - object builder = (object) ObjectOps - .newInstance(builderType.ref().getType(), "I", hName); + builder.assign(ObjectOps.newInstance(builderType.ref().getType(), "I", hName)); if (ObjectOps.typeOf(builder, ISupportInitialize.class).booleanValue()) { ObjectOps.cast(builder, ISupportInitialize.class).ref().initialize(); === modified file 'src/com/goldencode/p2j/oo/net/http/RequestBuilder.java' --- src/com/goldencode/p2j/oo/net/http/RequestBuilder.java 2021-03-11 08:18:20 +0000 +++ src/com/goldencode/p2j/oo/net/http/RequestBuilder.java 2021-05-24 06:41:34 +0000 @@ -16,6 +16,7 @@ ** 007 CA 20210221 Fixed 'qualified', 'extent' and 'returns' annotations at the legacy ** signature. ** ME 20210310 Add block 'referent' for static methods. +** 008 ME 20210519 Fix registry lazy loading. */ /* @@ -172,6 +173,8 @@ { object poDecorationType = TypeFactory.initInput(_poDecorationType); object poDecoratedRequest = TypeFactory.initInput(_poDecoratedRequest); + object oRequest = TypeFactory.object(IhttpRequest.class); + return function(RequestBuilder.class, "DecorateRequest", object.class, new Block((Body) () -> { @@ -193,7 +196,7 @@ Assert.isType(oDecorator, ObjectOps.getLegacyClass(HttpRequestDecorator.class)); - object oRequest = ObjectOps.newInstance(oDecorator.ref().getType(), "I", poDecoratedRequest); + oRequest.assign(ObjectOps.newInstance(oDecorator.ref().getType(), "I", poDecoratedRequest)); if (oRequest._isValid() && oRequest.ref() instanceof ISupportInitialize) ((ISupportInitialize) oRequest.ref()).initialize(); @@ -236,14 +239,12 @@ { return function(RequestBuilder.class, "Registry", object.class, new Block((Body) () -> { - object registry = REGISTRY.get(); - - if (!registry._isValid()) { - registry.assign(ObjectOps.newInstance(BuilderRegistry.class)); - initializeRegistry(registry); + if (!REGISTRY.get()._isValid()) { + REGISTRY.get().assign(ObjectOps.newInstance(BuilderRegistry.class)); + initializeRegistry(REGISTRY.get()); } - returnNormal(registry); + returnNormal(REGISTRY.get()); })); } @@ -264,7 +265,8 @@ { character pcMethod = TypeFactory.initInput(_pcMethod); object poURI = TypeFactory.initInput(_poURI); - + object builder = TypeFactory.object(RequestBuilder.class); + return function(RequestBuilder.class, "Build", object.class, new Block((Body) () -> { object builderCls = ObjectOps .getLegacyClass(RequestBuilder.class); @@ -273,8 +275,7 @@ Assert.isType(builderType, builderCls); - object builder = ObjectOps - .newInstance(builderType.ref().getType(), "II", pcMethod, poURI); + builder.assign(ObjectOps.newInstance(builderType.ref().getType(), "II", pcMethod, poURI)); if (builder._isValid() && builder.ref() instanceof ISupportInitialize) ((ISupportInitialize) builder.ref()).initialize(); === modified file 'src/com/goldencode/p2j/oo/net/http/ResponseBuilder.java' --- src/com/goldencode/p2j/oo/net/http/ResponseBuilder.java 2021-03-11 08:18:20 +0000 +++ src/com/goldencode/p2j/oo/net/http/ResponseBuilder.java 2021-05-24 06:41:34 +0000 @@ -12,6 +12,7 @@ ** 004 CA 20210221 Fixed 'qualified', 'extent' and 'returns' annotations at the legacy ** signature. ** ME 20210310 Add block 'referent' for static methods. +** 005 ME 20210519 Fix registry lazy loading. */ /* @@ -136,18 +137,16 @@ { return function(ResponseBuilder.class, "Registry", object.class, new Block((Body) () -> { - object registry = REGISTRY.get(); - - if (!registry._isValid()) { - registry.assign(ObjectOps.newInstance(BuilderRegistry.class)); + if (!REGISTRY.get()._isValid()) { + REGISTRY.get().assign(ObjectOps.newInstance(BuilderRegistry.class)); - registry.ref()._put(ObjectOps.getLegacyName(IhttpResponse.class), + REGISTRY.get().ref()._put(ObjectOps.getLegacyName(IhttpResponse.class), ObjectOps.getLegacyClass(HttpResponse.class)); - registry.ref()._put(ObjectOps.getLegacyName(ResponseBuilder.class), + REGISTRY.get().ref()._put(ObjectOps.getLegacyName(ResponseBuilder.class), ObjectOps.getLegacyClass(DefaultResponseBuilder.class)); } - returnNormal(registry); + returnNormal(REGISTRY.get()); })); } === modified file 'src/com/goldencode/p2j/oo/net/http/filter/auth/AuthenticationRequestFilter.java' --- src/com/goldencode/p2j/oo/net/http/filter/auth/AuthenticationRequestFilter.java 2021-06-09 14:35:48 +0000 +++ src/com/goldencode/p2j/oo/net/http/filter/auth/AuthenticationRequestFilter.java 2021-06-22 06:15:37 +0000 @@ -245,7 +245,8 @@ final character _pcRealm) { character pcRealm = TypeFactory.initInput(_pcRealm); - + object oAuthReqArgs = TypeFactory.object(AuthenticationRequestEventArgs.class); + return function(AuthenticationRequestFilter.class, this, "GetCredentials", object.class, new Block((Body) () -> { @@ -254,8 +255,8 @@ if (authenticatedRequest.ref().getCredentials()._isValid()) returnNormal(authenticatedRequest.ref().getCredentials()); - object oAuthReqArgs = ObjectOps.newInstance(AuthenticationRequestEventArgs.class, - "II", message, pcRealm); + oAuthReqArgs.assign(ObjectOps.newInstance(AuthenticationRequestEventArgs.class, + "II", message, pcRealm)); onHttpCredentialRequest(oAuthReqArgs); === modified file 'src/com/goldencode/p2j/oo/net/http/filter/writer/AuthenticationRequestWriterBuilder.java' --- src/com/goldencode/p2j/oo/net/http/filter/writer/AuthenticationRequestWriterBuilder.java 2021-03-11 08:18:20 +0000 +++ src/com/goldencode/p2j/oo/net/http/filter/writer/AuthenticationRequestWriterBuilder.java 2021-05-24 06:41:34 +0000 @@ -13,6 +13,7 @@ ** 005 CA 20210221 Fixed 'qualified', 'extent' and 'returns' annotations at the legacy ** signature. ** ME 20210310 Add block 'referent' for static methods. +** 006 ME 20210519 Fix registry lazy loading. */ /* @@ -213,20 +214,19 @@ public static object getRegistry() { return function(AuthenticationRequestWriterBuilder.class, "Registry", object.class, new Block((Body) () -> { - object registry = REGISTRY.get(); - if (!registry._isValid()) + if (!REGISTRY.get()._isValid()) { - registry.assign(ObjectOps.newInstance(BuilderRegistry.class)); + REGISTRY.get().assign(ObjectOps.newInstance(BuilderRegistry.class)); - registry.ref()._put("Basic", + REGISTRY.get().ref()._put("Basic", ObjectOps.getLegacyClass(BasicAuthenticationFilter.class)); - registry.ref()._put("Digest", + REGISTRY.get().ref()._put("Digest", ObjectOps.getLegacyClass(DigestAuthenticationFilter.class)); - registry.ref()._put("None", ObjectOps.getLegacyClass(NoAuthenticationFilter.class)); + REGISTRY.get().ref()._put("None", ObjectOps.getLegacyClass(NoAuthenticationFilter.class)); } - returnNormal(registry); + returnNormal(REGISTRY.get()); })); } } === modified file 'src/com/goldencode/p2j/oo/net/http/filter/writer/BodyWriterRegistry.java' --- src/com/goldencode/p2j/oo/net/http/filter/writer/BodyWriterRegistry.java 2021-03-11 08:18:20 +0000 +++ src/com/goldencode/p2j/oo/net/http/filter/writer/BodyWriterRegistry.java 2021-05-24 06:41:34 +0000 @@ -12,6 +12,7 @@ ** 004 CA 20210221 Fixed 'qualified', 'extent' and 'returns' annotations at the legacy ** signature. ** ME 20210310 Add block 'referent' for static methods. +** 005 ME 20210519 Fix registry lazy loading. */ /* @@ -70,6 +71,8 @@ package com.goldencode.p2j.oo.net.http.filter.writer; import com.goldencode.p2j.util.*; +import com.goldencode.p2j.util.BlockManager.Action; +import com.goldencode.p2j.util.BlockManager.Condition; import com.goldencode.p2j.oo.lang.*; import com.goldencode.p2j.oo.net.http.*; import com.goldencode.p2j.oo.net.http.filter.payload.*; @@ -114,17 +117,15 @@ { return function(BodyWriterRegistry.class, "Registry", object.class, new Block((Body) () -> { - object registry = REGISTRY.get(); - - if (!registry._isValid()) + if (!REGISTRY.get()._isValid()) { - registry.assign(ObjectOps.newInstance(BuilderRegistry.class, "I", + REGISTRY.get().assign(ObjectOps.newInstance(BuilderRegistry.class, "I", ObjectOps.getLegacyClass(MessageWriter.class))); - initializeRegistry(registry); + initializeRegistry(REGISTRY.get()); } - returnNormal(registry); + returnNormal(REGISTRY.get()); })); } @@ -172,4 +173,13 @@ _poRegistry.ref()._put("text/html", ObjectOps.getLegacyClass(HtmlBodyWriter.class)); })); } + + @LegacySignature(type = Type.CONSTRUCTOR) + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL) + public static void __net_http_filter_writer_BodyWriterRegistry_constructor__static__() + { + externalProcedure(BodyWriterRegistry.class, new Block((Body) () -> { + onBlockLevel(Condition.ERROR, Action.THROW); + })); + } } === modified file 'src/com/goldencode/p2j/oo/net/http/filter/writer/DefaultMessageWriterBuilder.java' --- src/com/goldencode/p2j/oo/net/http/filter/writer/DefaultMessageWriterBuilder.java 2021-02-23 07:39:32 +0000 +++ src/com/goldencode/p2j/oo/net/http/filter/writer/DefaultMessageWriterBuilder.java 2021-05-24 06:41:34 +0000 @@ -13,6 +13,7 @@ ** ME 20210204 Cleanup/fix errors on getWritter method. ** 005 CA 20210221 Fixed 'qualified', 'extent' and 'returns' annotations at the legacy ** signature. +** 006 ME 20210519 Fix variable scope for method return value. */ /* @@ -136,7 +137,7 @@ ); })); } - + /** * Get message writer * @@ -147,9 +148,13 @@ @Override public object getWriter() { - return function(this, "Writer", object.class, new Block((Body)() -> { + object writer = TypeFactory.object(MessageWriter.class); + + return function(this, "Writer", object.class, new Block((Init) () -> + { + ObjectOps.register(writer); + }, (Body)() -> { - object writer = TypeFactory.object(MessageWriter.class); object writerType = TypeFactory.object(LegacyClass.class); object entity = TypeFactory.object(BaseObject.class); === modified file 'src/com/goldencode/p2j/oo/net/http/filter/writer/EntityWriterRegistry.java' --- src/com/goldencode/p2j/oo/net/http/filter/writer/EntityWriterRegistry.java 2021-02-23 07:39:32 +0000 +++ src/com/goldencode/p2j/oo/net/http/filter/writer/EntityWriterRegistry.java 2021-05-24 06:41:34 +0000 @@ -11,6 +11,7 @@ ** ME 20210208 Fix static registry property initialization. ** 004 CA 20210221 Fixed 'qualified', 'extent' and 'returns' annotations at the legacy ** signature. +** 005 ME 20210519 Fix registry lazy loading, added static ctor. */ /* @@ -68,9 +69,11 @@ package com.goldencode.p2j.oo.net.http.filter.writer; +import static com.goldencode.p2j.util.BlockManager.externalProcedure; import static com.goldencode.p2j.util.BlockManager.function; import static com.goldencode.p2j.report.ReportConstants.*; import static com.goldencode.p2j.util.BlockManager.internalProcedure; +import static com.goldencode.p2j.util.BlockManager.onBlockLevel; import static com.goldencode.p2j.util.BlockManager.returnNormal; import com.goldencode.p2j.oo.lang.*; @@ -78,6 +81,8 @@ import com.goldencode.p2j.oo.net.http.filter.payload.*; import com.goldencode.p2j.security.*; import com.goldencode.p2j.util.*; +import com.goldencode.p2j.util.BlockManager.Action; +import com.goldencode.p2j.util.BlockManager.Condition; import com.goldencode.p2j.util.InternalEntry.*; /** @@ -105,6 +110,15 @@ } }; + @LegacySignature(type = Type.CONSTRUCTOR) + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL) + public static void __net_http_filter_writer_EntityWriterRegistry_constructor__static__() + { + externalProcedure(EntityWriterRegistry.class, new Block((Body) () -> { + onBlockLevel(Condition.ERROR, Action.THROW); + })); + } + /** * Get registry. * @@ -116,16 +130,14 @@ { return function(EntityWriterRegistry.class, "Registry", object.class, new Block((Body) () -> { - object registry = EntityWriterRegistry.REGISTRY.get(); - - if (!registry._isValid()) + if (!REGISTRY.get()._isValid()) { - registry.assign(ObjectOps.newInstance(BuilderRegistry.class, "I", + REGISTRY.get().assign(ObjectOps.newInstance(BuilderRegistry.class, "I", ObjectOps.getLegacyClass(MessageWriter.class))); - initializeRegistry(registry); + initializeRegistry(REGISTRY.get()); } - returnNormal(registry); + returnNormal(REGISTRY.get()); })); } === modified file 'src/com/goldencode/p2j/oo/net/http/filter/writer/MessageWriterBuilder.java' --- src/com/goldencode/p2j/oo/net/http/filter/writer/MessageWriterBuilder.java 2021-03-30 06:38:40 +0000 +++ src/com/goldencode/p2j/oo/net/http/filter/writer/MessageWriterBuilder.java 2021-05-24 06:41:34 +0000 @@ -14,6 +14,7 @@ ** signature. ** ME 20210310 Add block 'referent' for static methods, use initInput for object params. ** ME 20210330 Fix registry class type. +** 005 ME 20210519 Fix registry lazy loading. */ /* @@ -185,20 +186,17 @@ @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) public static object getRegistry() { - object registry = TypeFactory.object(BuilderRegistry.class); return function(BodyWriterRegistry.class, "Registry", object.class, new Block((Body) () -> { - registry.assign(REGISTRY.get()); - - if (!registry._isValid()) + if (!REGISTRY.get()._isValid()) { - registry.assign(ObjectOps.newInstance(BuilderRegistry.class, "I", + REGISTRY.get().assign(ObjectOps.newInstance(BuilderRegistry.class, "I", ObjectOps.getLegacyClass(MessageWriterBuilder.class))); - initializeRegistry(registry); + initializeRegistry(REGISTRY.get()); } - returnNormal(registry); + returnNormal(REGISTRY.get()); })); } === modified file 'src/com/goldencode/p2j/oo/net/http/filter/writer/RequestWriterBuilder.java' --- src/com/goldencode/p2j/oo/net/http/filter/writer/RequestWriterBuilder.java 2021-03-10 09:44:27 +0000 +++ src/com/goldencode/p2j/oo/net/http/filter/writer/RequestWriterBuilder.java 2021-05-24 06:41:34 +0000 @@ -14,6 +14,7 @@ ** signature. ** ME 20210301 Added class 'referent' for static methods, always use registry getter. ** 20210310 Added missing implementation (OE12.2). +** 004 ME 20210519 Fix registry lazy loading. */ /* @@ -183,20 +184,16 @@ @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) public static object getRegistry() { - object registry = TypeFactory.object(BuilderRegistry.class); - return function(RequestWriterBuilder.class, "Registry", object.class, new Block((Body) () -> { - registry.assign(REGISTRY.get()); - - if (!registry._isValid()) + if (!REGISTRY.get()._isValid()) { - registry.assign(ObjectOps.newInstance(BuilderRegistry.class, "I", + REGISTRY.get().assign(ObjectOps.newInstance(BuilderRegistry.class, "I", ObjectOps.getLegacyClass(MessageWriter.class))); - initializeRegistry(registry); + initializeRegistry(REGISTRY.get()); } - returnNormal(registry); + returnNormal(REGISTRY.get()); })); } @@ -337,10 +334,11 @@ @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) protected object newRequestWriter() { + object wObj = TypeFactory.object(MessageWriter.class); + return function(RequestWriterBuilder.class, this, "NewRequestWriter", object.class, new Block((Body) () -> { object wCls = getWriter(request, getRegistry()); - object wObj = TypeFactory.object(MessageWriter.class); if (wCls._isValid()) { @@ -386,13 +384,12 @@ { object pRequest = TypeFactory.initInput(_pRequest); object pRegistry = TypeFactory.initInput(_pRegistry); + object oCls = TypeFactory.object(LegacyClass.class); return function(RequestWriterBuilder.class, this, "GetWriter", object.class, new Block((Body) () -> { Assert.notNull(pRegistry, new character("Request writer registry")); - object oCls = TypeFactory.object(LegacyClass.class); - if (pRequest._isValid()) { oCls.assign(pRegistry.ref().get(TextOps.trim(TextOps.substitute("&1+&2", pRequest.ref().getVersion(), pRequest.ref().getMethod())))); === modified file 'src/com/goldencode/p2j/oo/net/http/filter/writer/SetHeaderMessageWriterBuilder.java' --- src/com/goldencode/p2j/oo/net/http/filter/writer/SetHeaderMessageWriterBuilder.java 2021-02-23 07:39:32 +0000 +++ src/com/goldencode/p2j/oo/net/http/filter/writer/SetHeaderMessageWriterBuilder.java 2021-05-24 06:41:34 +0000 @@ -11,6 +11,7 @@ ** 20210208 Fix static registry property initialization, add static ctor. ** 004 CA 20210221 Fixed 'qualified', 'extent' and 'returns' annotations at the legacy ** signature. +** 005 ME 20210519 Fix registry lazy loading. */ /* @@ -131,16 +132,14 @@ { return function(SetHeaderMessageWriterBuilder.class, "Registry", object.class, new Block((Body) () -> { - object registry = REGISTRY.get(); - - if (!registry._isValid()) + if (!REGISTRY.get()._isValid()) { - registry.assign(ObjectOps.newInstance(BuilderRegistry.class, "I", + REGISTRY.get().assign(ObjectOps.newInstance(BuilderRegistry.class, "I", ObjectOps.getLegacyClass(IHttpMessageWriter.class))); - initializeRegistry(registry); + initializeRegistry(REGISTRY.get()); } - returnNormal(registry); + returnNormal(REGISTRY.get()); })); } === modified file 'src/com/goldencode/p2j/oo/net/http/filter/writer/StatusCodeWriterBuilder.java' --- src/com/goldencode/p2j/oo/net/http/filter/writer/StatusCodeWriterBuilder.java 2021-03-11 08:18:20 +0000 +++ src/com/goldencode/p2j/oo/net/http/filter/writer/StatusCodeWriterBuilder.java 2021-05-24 06:41:34 +0000 @@ -10,6 +10,7 @@ ** 003 CA 20210221 Fixed 'qualified', 'extent' and 'returns' annotations at the legacy ** signature. ** ME 20210301 Added class 'referent' for static methods, always use registry getter. +** 004 ME 20210519 Fix registry lazy loading. */ /* @@ -67,12 +68,12 @@ package com.goldencode.p2j.oo.net.http.filter.writer; +import static com.goldencode.p2j.util.BlockManager.externalProcedure; import static com.goldencode.p2j.util.BlockManager.function; import static com.goldencode.p2j.report.ReportConstants.*; import static com.goldencode.p2j.util.BlockManager.internalProcedure; -import static com.goldencode.p2j.report.ReportConstants.*; +import static com.goldencode.p2j.util.BlockManager.onBlockLevel; import static com.goldencode.p2j.util.BlockManager.returnNormal; -import static com.goldencode.p2j.report.ReportConstants.*; import com.goldencode.p2j.oo.core.*; import com.goldencode.p2j.oo.lang.*; @@ -80,6 +81,8 @@ import com.goldencode.p2j.oo.net.http.filter.status.*; import com.goldencode.p2j.security.*; import com.goldencode.p2j.util.*; +import com.goldencode.p2j.util.BlockManager.Action; +import com.goldencode.p2j.util.BlockManager.Condition; import com.goldencode.p2j.util.InternalEntry.*; /** @@ -106,6 +109,15 @@ return TypeFactory.object(BuilderRegistry.class); } }; + + @LegacySignature(type = Type.CONSTRUCTOR) + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL) + public static void __net_http_filter_writer_StatusCodeWriterBuilder_constructor__static__() + { + externalProcedure(StatusCodeWriterBuilder.class, new Block((Body) () -> { + onBlockLevel(Condition.ERROR, Action.THROW); + })); + } /** * Get registry. @@ -118,16 +130,14 @@ { return function(StatusCodeWriterBuilder.class, "Registry", object.class, new Block((Body) () -> { - object registry = REGISTRY.get(); - - if (!registry._isValid()) + if (!REGISTRY.get()._isValid()) { - registry.assign(ObjectOps.newInstance(BuilderRegistry.class, "I", + REGISTRY.get().assign(ObjectOps.newInstance(BuilderRegistry.class, "I", ObjectOps.getLegacyClass(IHttpMessageWriter.class))); - initializeRegistry(registry); + initializeRegistry(REGISTRY.get()); } - returnNormal(registry); + returnNormal(REGISTRY.get()); })); } @@ -175,9 +185,10 @@ object poRequest, object poResponse) { + object writer = TypeFactory.object(IHttpMessageWriter.class); + return function(StatusCodeWriterBuilder.class, "Build", object.class, new Block((Body)() -> { - object writer = - TypeFactory.object(IHttpMessageWriter.class); + object writerType = TypeFactory.object(LegacyClass.class); Assert.notNull(poResponse, new character("Response")); === modified file 'src/com/goldencode/p2j/oo/net/http/lib/ClientLibraryBuilder.java' --- src/com/goldencode/p2j/oo/net/http/lib/ClientLibraryBuilder.java 2021-03-11 08:18:20 +0000 +++ src/com/goldencode/p2j/oo/net/http/lib/ClientLibraryBuilder.java 2021-05-24 06:41:34 +0000 @@ -16,6 +16,7 @@ ** 007 CA 20210221 Fixed 'qualified', 'extent' and 'returns' annotations at the legacy ** signature. ** ME 20210301 Added class 'referent' for static methods. +** 008 ME 20210519 Fix registry lazy loading. */ /* @@ -191,14 +192,12 @@ { return function(ClientLibraryBuilder.class, "Registry", object.class, new Block((Body) () -> { - object registry = REGISTRY.get(); - - if (!registry._isValid()) + if (!REGISTRY.get()._isValid()) { - registry.assign(ObjectOps.newInstance(BuilderRegistry.class)); - initializeRegistry(registry); + REGISTRY.get().assign(ObjectOps.newInstance(BuilderRegistry.class)); + initializeRegistry(REGISTRY.get()); } - returnNormal(registry); + returnNormal(REGISTRY.get()); })); } === modified file 'src/com/goldencode/p2j/oo/net/http/lib/sockets/LegacySocketLibrary.java' (properties changed: -x to +x) --- src/com/goldencode/p2j/oo/net/http/lib/sockets/LegacySocketLibrary.java 2021-02-23 07:39:32 +0000 +++ src/com/goldencode/p2j/oo/net/http/lib/sockets/LegacySocketLibrary.java 2021-05-24 06:22:49 +0000 @@ -18,6 +18,8 @@ ** - TypeFactory.initInput will take care of this. ** 007 CA 20210221 Fixed 'qualified', 'extent' and 'returns' annotations at the legacy ** signature. +** 008 ME 20210426 Removed default ctor, add destructor to clean-up client socket and connect params. +** 009 ME 20210524 Added request timeout, improve error handling (4GL style). */ /* @@ -81,6 +83,7 @@ import static com.goldencode.p2j.util.BlockManager.internalProcedure; import static com.goldencode.p2j.util.BlockManager.onBlockLevel; import static com.goldencode.p2j.util.BlockManager.returnNormal; +import static com.goldencode.p2j.util.BlockManager.undoThrow; import java.io.*; import java.net.*; @@ -131,6 +134,7 @@ import com.goldencode.p2j.util.*; import com.goldencode.p2j.util.BlockManager.Action; import com.goldencode.p2j.util.BlockManager.Condition; +import com.goldencode.p2j.util.ErrorManager; import com.goldencode.p2j.util.InternalEntry.*; import com.goldencode.p2j.xml.*; @@ -393,22 +397,85 @@ object _poResponse) { object poRequest = TypeFactory.initInput(_poRequest); - internalProcedure(this, "__net_http_lib_ablsockets_ABLSocketLibrary_constructor__", new Block((Body) () -> + object poResponse = TypeFactory.initInput(_poResponse); + + internalProcedure(this, "execute", new Block((Body) () -> { - _execute(poRequest, _poResponse); + + Assert.notNull(poRequest, new character("HTTP request")); + Assert.notNull(poResponse, new character("HTTP response")); + + integer port = poRequest.ref().getUri().ref().getPort(); + + // too high port number checked by client socket probably, comes before host check + if (!port.isUnknown() && port.intValue() > 65535) + { + undoThrow(AppError.newInstance(String.format("Port is too large: %d", port.intValue()), 0)); + } + + character host = poRequest.ref().getUri().ref().getHost(); + + // spaces in host name is breaking the connection params (-H xxxx yyyy) + if (TextOps.numEntries(host, " ").intValue() > 1) + { + ErrorManager.recordOrThrowError(new int[] { 301, 5509, 5510 }, + new String[] { + String.format("** Could not recognize argument: %s", + TextOps.entry(2, host, " ").toStringMessage()), + "Unable to process parameters", "Invalid parameter string" }, + false); + } + + // negative port is breaking the connection params (-S -xxxx) + if (port.intValue() < 0) + { + ErrorManager.recordOrThrowError(new int[] { 1403, 5509, 5510 }, + new String[] { "You have not supplied a parameter for argument -S", + "Unable to process parameters", "Invalid parameter string" }, + false); + } + + try + { + _execute(poRequest, poResponse); + } + catch (UnknownHostException e) + { + undoThrow(SysError.newInstance(String.format("Unknown hostname %s", + poRequest.ref().getUri().ref().getHost()), 5482, false, true)); + } + catch (HttpHostConnectException e) + { + undoThrow(SysError.newInstance( + String.format("Connection failure for host %s port %d transport TCP", + e.getHost().getHostName(), e.getHost().getPort()), + 9407, false, true)); + } + catch (IOException e) + { + // request terminated + readTerminatedHandler(null, null); + } + catch (Exception e) + { + // TODO: there might be other cases + undoThrow(AppError.newInstance(String.format("Unknown exception: %s", e.getMessage()), 0)); + } + })); } /** - * Constructor + * Destructor */ - @LegacySignature(type = Type.CONSTRUCTOR) - @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL_RESTR) - public void __net_http_lib_sockets_LegacySocketLibrary_constructor__() + @LegacySignature(type = Type.DESTRUCTOR) + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) + public void __net_http_lib_sockets_LegacySocketLibrary_destructor__() { - internalProcedure(this, "__net_http_lib_ablsockets_ABLSocketLibrary_constructor__", new Block((Body) () -> + internalProcedure(this, "__net_http_lib_ablsockets_ABLSocketLibrary_destructor__", new Block((Body) () -> { - __lang_BaseObject_constructor__(); + moSocketLib.setUnknown(); + moSocketConnectionParam.setUnknown(); })); } @@ -543,7 +610,7 @@ @LegacyParameter(name = "poSender", type = "OBJECT", qualified = "openedge.net.serverconnection.clientsocket", mode = "INPUT"), @LegacyParameter(name = "poEventArgs", type = "OBJECT", qualified = "openedge.net.serverconnection.socketreadeventargs", mode = "INPUT") }) - @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB) + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) public void readTerminatedHandler(final object _poSender, final object _poEventArgs) { object poSender = TypeFactory.initInput(_poSender); @@ -551,7 +618,8 @@ internalProcedure(LegacySocketLibrary.class, this, "ReadTerminatedHandler", new Block((Body) () -> { - UnimplementedFeature.missing("ABLSocketLibrary:ReadTerminatedHandler METHOD"); + undoThrow(AppError.newInstance(TextOps.substitute("Read terminated for &1", + moSocketConnectionParam.ref().getUri().ref().toLegacyString()), new integer(105))); })); } @@ -560,7 +628,7 @@ @LegacyParameter(name = "poSender", type = "OBJECT", qualified = "openedge.net.serverconnection.clientsocket", mode = "INPUT"), @LegacyParameter(name = "poEventArgs", type = "OBJECT", qualified = "openedge.net.serverconnection.socketreadeventargs", mode = "INPUT") }) - @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB) + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) public void readTimeoutHandler(final object _poSender, final object _poEventArgs) { object poSender = TypeFactory.initInput(_poSender); @@ -568,7 +636,10 @@ internalProcedure(LegacySocketLibrary.class, this, "ReadTimeoutHandler", new Block((Body) () -> { - UnimplementedFeature.missing("ABLSocketLibrary:ReadTimeoutHandler METHOD"); + undoThrow(ObjectOps.newInstance(HttpRequestError.class, "III", + new character(StatusCodeEnum.requestTimeOut), + new character("read"), + moSocketConnectionParam.ref().getUri().ref().toLegacyString())); })); } @@ -579,8 +650,10 @@ * request * @param rsp * response + * @throws Exception */ - private void _execute(object req, object rsp) + private void _execute(object req, object rsp) + throws Exception { Uri uri = req.ref().getUri().ref(); String method = req.ref().getMethod().getValue(); @@ -598,53 +671,42 @@ } java.net.URI _uri; - try + URIBuilder builder = new URIBuilder().setScheme(scheme).setHost(host).setPort(port) + .setPath(path); + + String encoding = I18nOps.getJavaCharset(req.ref().getCharacterEncoding()); + + if (encoding != null && Charset.isSupported(encoding)) + builder.setCharset(Charset.forName(encoding)); + + object qryMap = uri.getQueryMap_1(); + + if (qryMap._isValid() && qryMap.ref().getSize().intValue() > 0) { - URIBuilder builder = new URIBuilder(). - setScheme(scheme). - setHost(host). - setPort(port). - setPath(path); - - String encoding = I18nOps.getJavaCharset(req.ref().getCharacterEncoding()); - - if (encoding != null && Charset.isSupported(encoding)) - builder.setCharset(Charset.forName(encoding)); - - object qryMap = uri.getQueryMap_1(); - - if (qryMap._isValid() && qryMap.ref().getSize().intValue() > 0) { - object iterator = qryMap.ref().getKeySet().ref().iterator(); - while (iterator.ref().hasNext().booleanValue()) - { - object key = iterator.ref().next_(); - object value = qryMap.ref().get(key); - - builder.addParameter(key.ref().toLegacyString().getValue(), - value.ref().toLegacyString().getValue()); - } + object iterator = qryMap.ref().getKeySet().ref().iterator(); + while (iterator.ref().hasNext().booleanValue()) + { + object key = iterator.ref().next_(); + object value = qryMap.ref().get(key); + + builder.addParameter(key.ref().toLegacyString().getValue(), + value.ref().toLegacyString().getValue()); } - - String fragment = uri.getFragment().getValue(); - - if (!TextOps.isEmpty(fragment)) - builder.setFragment(fragment); - - _uri = builder.build(); - } - catch (URISyntaxException e) - { - LOG.log(Level.WARNING, "Invalid URI", e); - rsp.ref().setStatusCode(new integer(500)); - rsp.ref().setStatusReason(new character("Invalid URI")); - return; } - org.apache.http.client.methods.RequestBuilder requestBuilder = - org.apache.http.client.methods.RequestBuilder.create(method).setUri(_uri); + + String fragment = uri.getFragment().getValue(); + + if (!TextOps.isEmpty(fragment)) + builder.setFragment(fragment); + + _uri = builder.build(); + + org.apache.http.client.methods.RequestBuilder requestBuilder = org.apache.http.client.methods.RequestBuilder + .create(method).setUri(_uri); if (req.ref().hasHeader(new character("Content-Type")).booleanValue()) { - HttpEntity httpEntity = createEntity(req); + HttpEntity httpEntity = createEntity(req); requestBuilder.setEntity(httpEntity); } setHeaders(req, requestBuilder); @@ -666,20 +728,28 @@ } org.apache.http.client.HttpClient client = createHttpClient(proxyUri); + + // request hard timeout + double reqTimeout = options.ref().getRequestTimeout().doubleValue(); - HttpResponse response; - try - { - response = client.execute(request); - } - catch (IOException e) - { - LOG.log(Level.WARNING, "HTTP request failed", e); - rsp.ref().setStatusCode(new integer(500)); - rsp.ref().setStatusReason(new character(e.getMessage())); - return; + if (reqTimeout > 0) + { + TimerTask task = new TimerTask() + { + @Override + public void run() + { + if (request != null) + { + request.abort(); + } + } + }; + new Timer(true).schedule(task, (long) (reqTimeout * 1000)); } + HttpResponse response = client.execute(request); + StatusLine sline = response.getStatusLine(); rsp.ref().setStatusCode(new integer(sline.getStatusCode())); rsp.ref().setStatusReason(new character(sline.getReasonPhrase())); @@ -744,7 +814,15 @@ HttpHost proxy = new HttpHost(proxyUri.ref().getHost().toStringMessage(), proxyUri.ref().getPort().intValue()); builder.setProxy(proxy); } - + + int reqTimeout = options.ref().getRequestTimeout().intValue() * 1000; + + if (reqTimeout > 0) + { + builder.setDefaultRequestConfig(RequestConfig.custom().setConnectTimeout(reqTimeout) + .setConnectionRequestTimeout(reqTimeout).build()); + } + return builder.build(); } @@ -881,11 +959,7 @@ object contentType = rsp.getHeader(new character(HttpHeaders.CONTENT_TYPE)); - if (ObjectOps.typeOf(contentType, NullHeader.class).booleanValue()) - { - rsp.setContentType(new character("application/octet-stream")); - } - else + if (!ObjectOps.typeOf(contentType, NullHeader.class).booleanValue()) { rsp.setContentType(contentType.ref().getValue()); } === modified file 'src/com/goldencode/p2j/oo/net/serverconnection/ClientSocket.java' (properties changed: -x to +x) --- src/com/goldencode/p2j/oo/net/serverconnection/ClientSocket.java 2021-02-21 16:25:50 +0000 +++ src/com/goldencode/p2j/oo/net/serverconnection/ClientSocket.java 2021-05-10 06:10:20 +0000 @@ -13,6 +13,7 @@ ** - TypeFactory.initInput will take care of this. ** 003 CA 20210221 Fixed 'qualified', 'extent' and 'returns' annotations at the legacy ** signature. +** 004 ME 20210426 Fixed properties initial values (class not implemented, http client used instead). */ /* @@ -80,7 +81,6 @@ import com.goldencode.p2j.oo.logging.IlogWriter; import com.goldencode.p2j.oo.lang._BaseObject_; import com.goldencode.p2j.oo.net.Uri; -import com.goldencode.p2j.oo.net.serverconnection.SocketReadEventArgs; import static com.goldencode.p2j.report.ReportConstants.CVT_LVL_FULL; import static com.goldencode.p2j.report.ReportConstants.RT_LVL_STUB; @@ -100,47 +100,47 @@ { @LegacySignature(type = Type.PROPERTY, name = "Connected") @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB) - private logical connected = UndoableFactory.logical(); + private logical connected = TypeFactory.logical(); @LegacySignature(type = Type.PROPERTY, name = "ConnectionParameters") @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB) - private object connectionParameters = UndoableFactory.object(IconnectionParameters.class); + private object connectionParameters = TypeFactory.object(IconnectionParameters.class); @LegacySignature(type = Type.PROPERTY, name = "DefaultReadTimeout") @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB) - private integer defaultReadTimeout = UndoableFactory.integer(); + private integer defaultReadTimeout = TypeFactory.integer(); @LegacySignature(type = Type.PROPERTY, name = "KeepAlive") @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB) - private logical keepAlive = UndoableFactory.logical(); + private logical keepAlive = TypeFactory.logical(null); @LegacySignature(type = Type.PROPERTY, name = "LingerTime") @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB) - private integer lingerTime = UndoableFactory.integer(); + private integer lingerTime = TypeFactory.integer(-1L); @LegacySignature(type = Type.PROPERTY, name = "Logger") @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB) - private object logger = UndoableFactory.object(IlogWriter.class); + private object logger = TypeFactory.object(IlogWriter.class); @LegacySignature(type = Type.PROPERTY, name = "NoDelay") @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB) - private logical noDelay = UndoableFactory.logical(); + private logical noDelay = TypeFactory.logical(null); @LegacySignature(type = Type.PROPERTY, name = "ReadBufferSize") @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB) - private integer readBufferSize = UndoableFactory.integer(); + private integer readBufferSize = TypeFactory.integer(); @LegacySignature(type = Type.PROPERTY, name = "ReceiveBufferSize") @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB) - private integer receiveBufferSize = UndoableFactory.integer(); + private integer receiveBufferSize = TypeFactory.integer(null); @LegacySignature(type = Type.PROPERTY, name = "ReceiveTimeout") @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB) - private integer receiveTimeout = UndoableFactory.integer(); + private integer receiveTimeout = TypeFactory.integer(null); @LegacySignature(type = Type.PROPERTY, name = "ReuseAddress") @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB) - private logical reuseAddress = UndoableFactory.logical(); + private logical reuseAddress = TypeFactory.logical(null); @LegacySignature(type = Type.PROPERTY, name = "RemoteHost") @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB) @@ -160,11 +160,11 @@ @LegacySignature(type = Type.PROPERTY, name = "SendBufferSize") @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB) - private integer sendBufferSize = UndoableFactory.integer(); + private integer sendBufferSize = TypeFactory.integer(null); @LegacySignature(type = Type.PROPERTY, name = "server") @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB) - private object server = UndoableFactory.object(_BaseObject_.class); + private object server = TypeFactory.object(_BaseObject_.class); @LegacySignature(type = Type.PROPERTY, name = "SslServerName") @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB) === modified file 'src/com/goldencode/p2j/util/BaseDataType.java' --- src/com/goldencode/p2j/util/BaseDataType.java 2021-06-09 14:35:48 +0000 +++ src/com/goldencode/p2j/util/BaseDataType.java 2021-06-22 06:15:37 +0000 @@ -55,6 +55,8 @@ ** IAS 20201007 Added Type enum ** ME 20201009 Added new data type for table/dataset handle, mapped to handle. ** ME 20200322 Add info of which data type does not support a certain value assign. +** ME 20210504 Added method isIncompatibleTypesOnConversion to check if POLY data type conversion +** is to be attempted for given data type. ** 035 CA 20210512 Fixed 'getTypeName' when anon classes are used (for 'directAssign' usage). ** 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. @@ -814,7 +816,7 @@ } /** - * Display error message in case of incompatible types for a POLY conversion. + * Throws error message in case of incompatible types for a POLY conversion. * (e.g. DYNAMIC-FUNCTION()). * * @throws ErrorConditionException @@ -827,6 +829,19 @@ } /** + * Returns true if dynamic data type conversion should not be attempted, + * it can also throws in case of incompatible types for a POLY conversion. + * (e.g. DYNAMIC-FUNCTION()). + * + * @throws ErrorConditionException + * If dynamic conversion of input value is not supported. + */ + protected boolean isIncompatibleTypesOnConversion(BaseDataType value) + { + return false; + } + + /** * Display error message in case of an invalid initializer for the given type. * * @param text === modified file 'src/com/goldencode/p2j/util/Call.java' --- src/com/goldencode/p2j/util/Call.java 2021-06-14 07:25:20 +0000 +++ src/com/goldencode/p2j/util/Call.java 2021-06-22 08:21:06 +0000 @@ -23,6 +23,7 @@ ** setParameter was called although the variable might mutate before invoke. ** CA 20210216 Fixed an issue when setParameter passes an unknown literal as the value argument. ** OM 20210309 Removed double display of error message number. +** ME 20210507 Improve error handling for POLY conversion, OO parameter validation. ** CA 20210525 Fixed a problem with Progress.Lang.Object arguments. ** CA 20210609 Fixed OO property references used as arguments. ** Reworked INPUT/INPUT-OUTPUT parameters to a new approach, where they are explicitly @@ -2082,10 +2083,10 @@ FieldReference fref = (FieldReference) value; if (!(fref.getParentBuffer() instanceof TemporaryBuffer)) { - ErrorManager.recordOrShowError(10062, + ErrorManager.recordOrThrowError(10062, "Buffer for output parameter " + pnum + " not available during SET-PARAMETER", - false, false, false); + false, false); return false; } @@ -2094,10 +2095,10 @@ { // 4GL abends... we just continue - ErrorManager.recordOrShowError(10062, + ErrorManager.recordOrThrowError(10062, "Buffer for output parameter " + pnum + " not available during SET-PARAMETER", - false, false, false); + false, false); return false; } @@ -2112,6 +2113,10 @@ { return true; } + else if (value instanceof TableParameter || value instanceof DataSetParameter) + { + return true; + } ErrorManager.recordOrThrowError(10060, "SET-PARAMETER output parameter " + pnum + @@ -2162,7 +2167,8 @@ isExtent = dataTypeMatch.group(3) != null; } - dataType = resolveDataType(dataType); + if (!"DATASET".equalsIgnoreCase(dataType) && !"TABLE".equalsIgnoreCase(dataType)) + dataType = resolveDataType(dataType); if (library) { @@ -2182,7 +2188,8 @@ if (value instanceof DataSetParameter || value instanceof TableParameter || "DATASET".equalsIgnoreCase(dataType) || "TABLE".equalsIgnoreCase(dataType)) { - if ((value instanceof DataSetParameter && !"DATASET".equalsIgnoreCase(dataType)) + if (isExtent + || (value instanceof DataSetParameter && !"DATASET".equalsIgnoreCase(dataType)) || (value instanceof TableParameter && !"TABLE".equalsIgnoreCase(dataType)) || ("DATASET".equalsIgnoreCase(dataType) && !(value instanceof DataSetParameter)) @@ -2201,11 +2208,20 @@ && DATA_TYPES.contains(dataType.toUpperCase()) ? BaseDataType.fromTypeName(dataType) : ObjectOps.resolveClass(dataType); - isClass = cls != null && _BaseObject_.class.isAssignableFrom(cls); + Runnable invalidType = () -> { ErrorManager.recordOrThrowError(10059, "Unable to convert SET-PARAMETER value to datatype passed", false); }; + + Runnable incompatibleType = () -> { + int[] nums = { 5729, 10059 }; + + String[] texts = { "Incompatible datatypes found during runtime conversion", + "Unable to convert SET-PARAMETER value to datatype passed" }; + + ErrorManager.recordOrThrowError(nums, texts, false, false); + }; if (cls == null) { @@ -2215,6 +2231,7 @@ if (value != null) { Class valueCls = value.getClass(); + BaseDataType val = value instanceof BaseDataType ? (BaseDataType) value : null; if (valueCls == unknown.class) { @@ -2230,6 +2247,7 @@ isValExtent = fieldRef.getExtent() != null && fieldRef.getIndex() == -1; valueCls = fieldRef.getType(); + val = fieldRef.get(); } else if (value instanceof PropertyReference) { @@ -2270,21 +2288,19 @@ invalidType.run(); } - if (!isValExtent) { - // run-time data type conversion seems to be attempted first even if data types does not match unless is extent + if (!isValExtent && (!_BaseObject_.class.isAssignableFrom(cls) || !object.class.isAssignableFrom(valueCls))) { + // run-time data type conversion seems to be attempted first even if data types does not match unless is extent + // oddly when class parameter and value is object the assignment is not done, only compatibility check try { BaseDataType inst = _BaseObject_.class.isAssignableFrom(cls) ? TypeFactory.object((Class) cls) : BaseDataType.generateUnknown(cls); - inst.assign(value); + if (!inst.isIncompatibleTypesOnConversion(val)) + inst.assign(val); } catch (ErrorConditionException ece) { - int[] nums = { 5729, 10059 }; - - String[] texts = { "Incompatible datatypes found during runtime conversion", - "Unable to convert SET-PARAMETER value to datatype passed" }; - - ErrorManager.recordOrThrowError(nums, texts, false, false); + ErrorManager.addError(10059, "Unable to convert SET-PARAMETER value to datatype passed", true, false); + throw ece; } catch (Exception ex) { @@ -2292,12 +2308,57 @@ } } - // date->datetime->datetimetz hierarchy works - // decimal accepts both integer/int64 if (validateType) { - if (!cls.isAssignableFrom(valueCls) - && (!cls.equals(decimal.class) || !int64.class.isAssignableFrom(valueCls))) + boolean isCompatible = false; + + // numeric data types are compatible to some extent + // decimal accepts int/int64 + // int64 accepts int + // recid is not accepted by neither decimal/int/int64 + if (cls.equals(decimal.class)) + { + isCompatible = !valueCls.equals(recid.class) && NumberType.class.isAssignableFrom(valueCls); + } + else if (cls.equals(int64.class)) + { + isCompatible = !valueCls.equals(recid.class) && int64.class.isAssignableFrom(valueCls); + } + else if (NumberType.class.isAssignableFrom(cls)) + { + // break int64->integer->recid hierarchy + isCompatible = valueCls.equals(cls); + } + else if (cls.equals(longchar.class)) + { + // longchar accepts also character but not clob + isCompatible = !valueCls.equals(clob.class) && Text.class.isAssignableFrom(valueCls); + } + else if (_BaseObject_.class.isAssignableFrom(cls)) + { + // value not an object + if (!object.class.isAssignableFrom(valueCls)) + { + isCompatible = false; + } + // value is object array + else if (isValExtent) + { + isCompatible = isAssignableFrom((object[]) value, cls); + } + else + { + object oVal = (object) value; + isCompatible = cls.isAssignableFrom(oVal.isUnknown() ? oVal.type() : oVal.ref().getClass()); + } + } + else + { + // date->datetime->datetimetz hierarchy works + isCompatible = valueCls.isAssignableFrom(cls); + } + + if (!isCompatible) { ErrorManager.recordOrThrowError(15315, "SetParameter found incompatible data types used in dynamic parameter", @@ -2312,8 +2373,6 @@ } } - - isExtent = isValExtent; } } @@ -2328,6 +2387,47 @@ return cparam; } + + /** + * Check if all values in the array/extent are compatible with the given data type. + * + * @param values The values array (extent). + * @param cls The data type to validate values against. + * @return True is all values in the array are compatible with the data type. + */ + private static boolean isAssignableFrom(object[] values, Class cls) + { + boolean isEmpty = true; + boolean isAssignable = true; + + for (object value : values) + { + if (!value.isUnknown()) { + isEmpty = false; + // check the actual instance class, object type might be an invalid superclass like P.L.O + isAssignable = cls.isAssignableFrom(value.ref.getClass()); + + if (!isAssignable) + break; + } + } + + if (!isEmpty) + return isAssignable; + + Class valueCls = unknown.class; + + if (values.length > 0) + { + valueCls = values[0].type(); + } + else if (ArrayAssigner.isDynamicArray(values)) + { + valueCls = ArrayAssigner.getObjectType(values); + } + + return cls.isAssignableFrom(valueCls); + } /** * Worker method for the {@link #GET_ATTR_CALL_TYPE} and {@link #SET_ATTR_CALL_TYPE} modes. === modified file 'src/com/goldencode/p2j/util/Text.java' --- src/com/goldencode/p2j/util/Text.java 2021-06-14 07:25:20 +0000 +++ src/com/goldencode/p2j/util/Text.java 2021-06-22 06:15:37 +0000 @@ -53,6 +53,7 @@ ** 028 IAS 20200908 Rework (de)serialization. ** 029 EVL 20200922 Replace Java split() with our implementation for TextOps.splitInt(). ** VVT 20201001 javaTruncateNull() optimized. +** ME 20210504 Added isIncompatibleTypesOnConversion override for invalid POLY conversion. ** 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. */ @@ -2094,6 +2095,18 @@ { assign((BaseDataType) value); } + + @Override + protected boolean isIncompatibleTypesOnConversion(BaseDataType value) + { + // invalid conversion is being thrown even if unknown + if (value instanceof memptr || value instanceof blob || (value instanceof raw && !value.isUnknown())) + { + incompatibleTypesOnConversion(); + } + + return super.isIncompatibleTypesOnConversion(value); + } /** * Sets the state (data and unknown value) of this instance based on the state of the passed @@ -2121,10 +2134,6 @@ */ public void assign(BaseDataType value) { - if (value instanceof raw || value instanceof memptr || value instanceof blob) - { - incompatibleTypesOnConversion(); - } if (value == null || value.isUnknown()) { setUnknown(); === modified file 'src/com/goldencode/p2j/util/decimal.java' --- src/com/goldencode/p2j/util/decimal.java 2020-10-08 21:34:55 +0000 +++ src/com/goldencode/p2j/util/decimal.java 2021-05-10 06:17:30 +0000 @@ -195,6 +195,8 @@ ** ME 20200528 Add assign for "legacy" object - resource id. ** 073 CA 20200608 Fixed equals - must use BigDecimal.compareTo. ** 074 IAS 20201007 Added Type enum +** 075 ME 20210504 Added isIncompatibleTypesOnConversion override +** for invalid POLY conversion. */ /* @@ -572,6 +574,30 @@ } /** + * Constructs an instance after converting the string representation of a number into a + * {@code BigDecimal}. Due to rounding, a loss of precision may occur in this conversion (at + * the 10th digit to the right of the decimal point). + *

+ * This conversion constructor always uses the current number format for parsing. Ex: in the + * default (American) format "3,141" parses as an integer value of 3141, but on European + * (-E Progress command line parameter) it will be parsed as an approximation of PI (3.141). + * For parsing a decimal literal (in American format) regardless of current number format use + * the {@link #fromLiteral(String)}. + *

+ * If the parameter is {@code unknown value} the resulting decimal is the {@code unknown + * value}. + * + * @param value + * The value to be used for this instance. + */ + public decimal(longchar value) + { + // unknown longchar results in 0 decimal value + setValue(value.isUnknown() ? "0" : value.getValue()); + setPrecision(MAX_SCALE); + } + + /** * Constructs an instance wth the given enum's value. *

* If the parameter is {@code unknown value} the resulting instance is the {@code unknown value}. @@ -1237,6 +1263,19 @@ return (new decimal(this)); } + + @Override + protected boolean isIncompatibleTypesOnConversion(BaseDataType value) + { + // invalid conversion is being thrown even if unknown + if (value instanceof longchar || value instanceof clob || value instanceof blob) + { + incompatibleTypesOnConversion(); + } + + return super.isIncompatibleTypesOnConversion(value); + } + /** * Sets the state (data and unknown value) of this instance based on the * state of the passed instance. === modified file 'src/com/goldencode/p2j/util/handle.java' --- src/com/goldencode/p2j/util/handle.java 2021-06-15 16:00:01 +0000 +++ src/com/goldencode/p2j/util/handle.java 2021-06-22 06:15:37 +0000 @@ -240,6 +240,8 @@ ** CA 20210306 HANDLE function can not retrieve a object resource (ERROR condition is raised). ** Moved to always incremental resource IDs. ** CA 20210310 Small performance improvement in removeResource. +** ME 20210504 Added isIncompatibleTypesOnConversion override for invalid POLY +** conversion. Support int64, decimal and date assign. ** AIL 20210615 Added a flag to detect if the handle should be set after run ... persistent set. */ @@ -3972,6 +3974,18 @@ value = null; } + @Override + protected boolean isIncompatibleTypesOnConversion(BaseDataType value) + { + // invalid conversion is being thrown even if unknown + if (value instanceof longchar || value instanceof clob || value instanceof blob) + { + incompatibleTypesOnConversion(); + } + + return super.isIncompatibleTypesOnConversion(value); + } + /** * Sets the state (data and unknown value) of this instance based on the state of the passed * instance. @@ -4009,11 +4023,11 @@ { assign(fromString((character) value)); } - else if (value instanceof integer) + else if (value instanceof int64) { - assign(fromResourceId(((integer) value).longValue())); + assign(fromResourceId(((int64) value).longValue())); } - else if (value instanceof logical) + else if (value instanceof logical || value instanceof date || value instanceof decimal) { assign(fromResourceId((new integer(value)).longValue())); } @@ -4023,8 +4037,7 @@ } else { - String err = "Incompatible data types in expression or assignment."; - ErrorManager.recordOrThrowError(223, err); + incompatibleTypesOnConversion(); } } === modified file 'src/com/goldencode/p2j/util/int64.java' --- src/com/goldencode/p2j/util/int64.java 2021-04-13 12:26:13 +0000 +++ src/com/goldencode/p2j/util/int64.java 2021-05-10 06:17:30 +0000 @@ -47,6 +47,7 @@ ** 024 IAS 20201007 Added Type enum. ** OM 20201120 Avoid construction of supplementary, unneeded wrapping objects. ** CA 20210413 An integer value can be built from a raw's bytes. +** 025 ME 20210504 Added isIncompatibleTypesOnConversion override for invalid POLY conversion. */ /* @@ -785,6 +786,18 @@ return isUnknown() ? 0L : longValue(); } + @Override + protected boolean isIncompatibleTypesOnConversion(BaseDataType value) + { + // invalid conversion is being thrown even if unknown + if (value instanceof longchar || value instanceof clob || value instanceof blob) + { + incompatibleTypesOnConversion(); + } + + return super.isIncompatibleTypesOnConversion(value); + } + /** * Sets the state (data and unknown value) of this instance based on the state of the passed * instance. === modified file 'src/com/goldencode/p2j/util/logical.java' (properties changed: -x to +x) --- src/com/goldencode/p2j/util/logical.java 2021-03-22 19:29:57 +0000 +++ src/com/goldencode/p2j/util/logical.java 2021-05-10 06:17:30 +0000 @@ -113,6 +113,7 @@ ** 048 IAS 20201007 Added Type enum ** 049 VVT 20210403 splitFormat() fixed: only one slash character is allowed. See #4880-67. ** VVT 20210322 Fixed after the review. See #4880-107. +** 050 ME 20210504 Added isIncompatibleTypesOnConversion override for invalid POLY conversion. */ /* @@ -1665,6 +1666,18 @@ unknown = false; } + @Override + protected boolean isIncompatibleTypesOnConversion(BaseDataType value) + { + // invalid conversion is being thrown even if unknown + if (value instanceof longchar || value instanceof clob || value instanceof blob) + { + incompatibleTypesOnConversion(); + } + + return super.isIncompatibleTypesOnConversion(value); + } + /** * Sets the state (data and unknown value) of this instance based on the * state of the passed instance. === modified file 'src/com/goldencode/p2j/util/longchar.java' --- src/com/goldencode/p2j/util/longchar.java 2021-06-09 14:35:48 +0000 +++ src/com/goldencode/p2j/util/longchar.java 2021-06-22 06:15:37 +0000 @@ -32,6 +32,7 @@ ** CA 20210428 Fixed conversion when the lvalue for an assign-styme stmt is a OO property (chained or ** not). In this case, a setter must wrap the assign-style stmt, to change the property's ** value. +** ME 20210504 Added isIncompatibleTypesOnConversion override for invalid POLY conversion. ** 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. */ @@ -828,6 +829,21 @@ * The source {@code Text} to be assigned. */ @Override + protected boolean isIncompatibleTypesOnConversion(BaseDataType value) + { + // invalid conversion is being thrown even if unknown + if (value instanceof blob || + // if not unknown only longchar/character allowed + (value != null && !value.isUnknown() && !(value instanceof longchar) + && !(value instanceof character))) + { + incompatibleTypesOnConversion(); + } + + return super.isIncompatibleTypesOnConversion(value); + } + + @Override public void assign(Text value) { if (value instanceof longchar) === modified file 'src/com/goldencode/p2j/util/object.java' --- src/com/goldencode/p2j/util/object.java 2021-06-12 09:09:17 +0000 +++ src/com/goldencode/p2j/util/object.java 2021-06-22 06:15:37 +0000 @@ -27,6 +27,7 @@ ** CA 20210213 Reverted ME/20201106 - only ObjectVar.setUnknown must decrement references. ** CA 20210305 The object resource ID as reported by toString() or used at the table field must be a ** numeric value (as indexes are affected). +** ME 20210504 Added isIncompatibleTypesOnConversion override for invalid POLY conversion. ** 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. */ @@ -691,4 +692,21 @@ return super.toString(); } + + @Override + protected boolean isIncompatibleTypesOnConversion(BaseDataType value) + { + // invalid conversion is being thrown even if unknown + if (value instanceof raw || value instanceof blob || (value instanceof raw && !value.isUnknown())) + { + incompatibleTypesOnConversion(); + } + // conversion is not attempted + else if (value instanceof clob || value instanceof handle || date.class.equals(value.getClass()) || value instanceof NumberType || value instanceof logical) + { + return true; + } + + return super.isIncompatibleTypesOnConversion(value); + } } === modified file 'src/com/goldencode/p2j/util/raw.java' --- src/com/goldencode/p2j/util/raw.java 2020-10-08 21:34:55 +0000 +++ src/com/goldencode/p2j/util/raw.java 2021-05-10 06:17:30 +0000 @@ -38,6 +38,7 @@ ** 021 OM 20200507 Handling empty strings in constructor as a unknown value. ** 022 IAS 20200908 Rework (de)serialization. ** 023 IAS 20201007 Added Type enum +** 024 ME 20210504 Handle character assign, check dynamic data type conversion allowed. */ /* @@ -816,4 +817,46 @@ checkUndoable(true); } } + + + @Override + protected boolean isIncompatibleTypesOnConversion(BaseDataType value) + { + // invalid conversion is being thrown even if unknown + if (value instanceof clob || value instanceof blob || value instanceof longchar) + { + incompatibleTypesOnConversion(); + } + + // unknown or empty string assignments are not performed + if (value instanceof character && TextOps.isEmpty((character) value)) + return true; + + return super.isIncompatibleTypesOnConversion(value); + } + + @Override + public void assign(BaseDataType data) + { + if (data instanceof character && !data.isUnknown()) + { + // first two characters should be "02" + // following 4 are length of the initial raw "encoded" as string (hex) + // the rest is base64 encoded value of initial raw + String value = ((character) data).toStringMessage(); + + if (value.startsWith("02") && value.length() >= 6) + { + assign(Base64.decodeBase64(value.substring(6).getBytes())); + } + else + { + ErrorManager.recordOrThrowError(4957, "Invalid encoded representation of RAW data"); + } + } + else + { + super.assign(data); + } + } } === modified file 'src/com/goldencode/p2j/util/rowid.java' --- src/com/goldencode/p2j/util/rowid.java 2021-06-09 14:35:48 +0000 +++ src/com/goldencode/p2j/util/rowid.java 2021-06-22 06:15:37 +0000 @@ -43,6 +43,7 @@ ** 016 IAS 20201007 Added Type enum ** 017 VVT 20210331 parseRowidString(String) fixed: does not throw anymore; ** toString(String fmt) fixed: now always emit all 16 digits. See #5218. +** ME 20210504 Added character/longchar value assign (poly conversion). ** 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. */ @@ -424,6 +425,10 @@ { assign((rowid) value); } + else if (value instanceof Text) + { + assign((Text) value); + } else { // conversion impossible @@ -445,6 +450,19 @@ } /** + * Sets the data of this instance based on the given instance. + * + * @param value + * The instance from which to copy state. + */ + public void assign(Text value) + { + checkUndoable(value); + + this.value = parseRowidString(value.toJavaType()); + } + + /** * Sets the state (data and unknown value) of this instance based on the * state of the passed instance. * @@ -659,4 +677,16 @@ out.writeLong(value); } } + + @Override + protected boolean isIncompatibleTypesOnConversion(BaseDataType value) + { + // invalid conversion is being thrown even if unknown + if (value instanceof clob || value instanceof blob || value instanceof longchar) + { + incompatibleTypesOnConversion(); + } + + return super.isIncompatibleTypesOnConversion(value); + } } # Begin bundle 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