# Bazaar merge directive format 2 (Bazaar 0.90) # revision_id: marian.edu@acorn.ro-20210622061537-wicxxnj4il73np5z # target_branch: sftp://medu@xfer.goldencode.com/opt/fwd/3821c # testament_sha1: c5fbee6b1af776855b4aa8387147563d7a62538b # timestamp: 2021-06-22 09:17:11 +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 06:15:37 +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,59 @@ } } - // 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 +2375,6 @@ } } - - isExtent = isValExtent; } } @@ -2328,6 +2389,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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