# Bazaar merge directive format 2 (Bazaar 0.90) # revision_id: marian.edu@acorn.ro-20210323114256-d1nz4a2ngyns28uf # target_branch: sftp://medu@xfer.goldencode.com/opt/fwd/3821c # testament_sha1: df476bf755bd4d9f152821e1f2ece055edb9c89b # timestamp: 2021-03-23 13:45:48 +0200 # source_branch: 3821c.4384i.patch # base_revision_id: ca@goldencode.com-20210323101252-amfkr18fp4yq4ttw # # Begin patch === modified file 'src/com/goldencode/p2j/oo/core/ByteBucket.java' --- src/com/goldencode/p2j/oo/core/ByteBucket.java 2021-02-21 16:25:50 +0000 +++ src/com/goldencode/p2j/oo/core/ByteBucket.java 2021-03-10 10:53:37 +0000 @@ -15,6 +15,7 @@ ** 006 CA 20210113 Renamed clear() to clear_(), to follow NameConverter's rules. ** 007 CA 20210221 Fixed 'qualified', 'extent' and 'returns' annotations at the legacy ** signature. +** ME 20210310 Use object variables defined out of block for methods that returns objects. */ /* @@ -460,28 +461,33 @@ { int64 pos = TypeFactory.initInput(_pos); int64 size = TypeFactory.initInput(_size); + object ret = TypeFactory.object(Memptr.class); return function(this, "GetBytes", object.class, new Block((Body) () -> { if (getSize().longValue() == 0 || size.longValue() == 0) { - returnNormal(Memptr.getEmpty()); - } - - memptr ptr = TypeFactory.memptr(); - - try - { - ptr.setLength(size); - - readBytes(pos, ptr); - - returnNormal(ObjectOps.newInstance(Memptr.class, "I", ptr)); - } - finally - { - if (ptr.lengthOf() > 0L) - ptr.setLength(0L); - } + ret.assign(Memptr.getEmpty()); + } + else + { + memptr ptr = TypeFactory.memptr(); + + try + { + ptr.setLength(size); + + readBytes(pos, ptr); + + ret.assign(ObjectOps.newInstance(Memptr.class, "I", ptr)); + } + finally + { + if (ptr.lengthOf() > 0L) + ptr.setLength(0L); + } + } + + returnNormal(ret); })); } @@ -672,9 +678,10 @@ @LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL) public static object instance() { - ObjectOps.load(ByteBucket.class); + object bb = TypeFactory.object(ByteBucket.class); + return function(ByteBucket.class, "Instance", object.class, new Block((Body) () -> { - object bb = ObjectOps.newInstance(ByteBucket.class); + bb.assign(ObjectOps.newInstance(ByteBucket.class)); bb.ref().initialize(); returnNormal(bb); })); @@ -693,8 +700,10 @@ public static object instance(final int64 _p1) { int64 p1 = TypeFactory.initInput(_p1); + object bb = TypeFactory.object(ByteBucket.class); + return function(ByteBucket.class, "Instance", object.class, new Block((Body) () -> { - object bb = ObjectOps.newInstance(ByteBucket.class, "I", p1); + bb.assign(ObjectOps.newInstance(ByteBucket.class, "I", p1)); bb.ref().initialize(); returnNormal(bb); })); @@ -716,8 +725,10 @@ { integer p1 = TypeFactory.initInput(_p1); int64 p2 = TypeFactory.initInput(_p2); + object bb = TypeFactory.object(ByteBucket.class); + return function(ByteBucket.class, "Instance", object.class, new Block((Body) () -> { - object bb = ObjectOps.newInstance(ByteBucket.class, "II", p1, p2); + bb.assign(ObjectOps.newInstance(ByteBucket.class, "II", p1, p2)); bb.ref().initialize(); returnNormal(bb); })); === modified file 'src/com/goldencode/p2j/oo/core/util/ConfigBuilder.java' --- src/com/goldencode/p2j/oo/core/util/ConfigBuilder.java 2021-02-21 16:25:50 +0000 +++ src/com/goldencode/p2j/oo/core/util/ConfigBuilder.java 2021-03-10 11:03:54 +0000 @@ -12,6 +12,8 @@ ** 20201207 Use character as map key since unknown keys are allowed in 4GL. ** Add new methods as of OE12.2. ** 005 CA 20210221 Added 'extent' for getOptionStringArrayValue method. +** 006 ME 20210310 Increment object reference on option set, decrement on remove. +** ME 20210310 Return ObjectVar on getOptionObjectValue so the reference is incremented. */ /* @@ -319,10 +321,13 @@ public object getOptionObjectValue(final character _p1) { character p1 = TypeFactory.initInput(_p1); + object ret = TypeFactory.object(_BaseObject_.class); return function(this, "GetOptionObjectValue", object.class, new Block((Body) () -> { - returnNormal(getOption(p1, object.class)); + ret.assign(getOption(p1, object.class)); + + returnNormal(ret); })); } @@ -440,7 +445,15 @@ return function(this, "RemoveOption", logical.class, new Block((Body) () -> { - returnNormal(new logical(options.remove(key) != null)); + OptionValueHolder val = options.remove(key); + + if (val != null && val.value() != null && val.type() == "object") { + object obj = (object) val.value(); + if (obj._isValid()) + ObjectOps.decrement(obj.ref()); + } + + returnNormal(new logical( val != null)); })); } @@ -601,6 +614,11 @@ return function(this, "SetOption", logical.class, new Block((Body) () -> { + if (p2._isValid()) + { + ObjectOps.increment(p2.ref()); + } + returnNormal(setOption(p1, p2, object.class)); })); } === modified file 'src/com/goldencode/p2j/oo/json/JsonBackend.java' --- src/com/goldencode/p2j/oo/json/JsonBackend.java 2021-02-15 09:22:31 +0000 +++ src/com/goldencode/p2j/oo/json/JsonBackend.java 2021-03-19 11:12:54 +0000 @@ -9,6 +9,7 @@ ** 002 HC 20190714 Added more serializable number types. ** 003 ME 20210128 Escape forward slash as in 4GL, json data type names. ** 20210215 Extend pretty printer to match the 4GL formated output. + ** 20210319 Flush output stream on write, replace BigDecimal use for integer/int64. */ /* @@ -159,6 +160,9 @@ target.write("\n".getBytes(Charset.forName(enc.getJavaName()))); } + // make sure we write everything, do not close the stream though + target.flush(); + return enc.getJavaName(); } @@ -217,9 +221,17 @@ Object targetValue = null; // convert the value to json-compatible data type - if (value instanceof NumberType) - { - targetValue = ((NumberType) value).toBigDecimal(); + if (value instanceof integer) + { + targetValue = ((integer) value).toJavaIntegerType(); + } + else if (value instanceof int64) + { + targetValue = ((int64) value).toJavaLongType(); + } + else if (value instanceof NumberType) + { + targetValue = ((NumberType) value).toBigDecimal().stripTrailingZeros(); } else if (value instanceof Text) { @@ -400,7 +412,16 @@ } else if (value instanceof BigDecimal) { - jsonGen.writeNumber((BigDecimal) value); + BigDecimal bd = (BigDecimal) value; + + if (bd.scale() == 0) + { + jsonGen.writeNumber(bd.doubleValue()); + } + else + { + jsonGen.writeNumber(bd); + } } else if (value instanceof String) { === modified file 'src/com/goldencode/p2j/oo/json/objectmodel/JsonArray.java' --- src/com/goldencode/p2j/oo/json/objectmodel/JsonArray.java 2021-03-04 19:07:41 +0000 +++ src/com/goldencode/p2j/oo/json/objectmodel/JsonArray.java 2021-03-23 11:42:56 +0000 @@ -17,6 +17,7 @@ ** 009 CA 20210221 Fixed 'qualified', 'extent' and 'returns' annotations at the legacy ** signature. ** CA 20210304 Fixed date, datetime and datetimetz literal parsing. +** ME 20210319 Fix remove, serialization,error on stream not found. */ /* @@ -1718,7 +1719,7 @@ { addSetElementImpl(pos, val.isUnknown() ? (Object) null : Double.parseDouble(val.getValue()), true); } - catch (Exception e) + catch (NumberFormatException e) { undoThrow(JsonError.newInstance(ErrorManager.getInvalidParameterError("value", this, "AddNumber", "Not a valid JSON number."), 16053)); @@ -2573,10 +2574,10 @@ return function(this, "Remove", logical.class, new Block((Body) () -> { - int pos = idx == null || idx.isUnknown() ? -1 : idx.intValue(); + int pos = idx == null || idx.isUnknown() ? -1 : idx.intValue() - 1; int cnt = count == null || count.isUnknown() ? -1 : count.intValue(); - if (pos < 1 || pos > elements.size()) + if (pos < 0 || pos >= elements.size()) { undoThrow(JsonError.newInstance(ErrorManager.getInvalidParameterError("index", this, "Remove", @@ -2594,7 +2595,7 @@ for (int i = 0; i < cnt; i++) { - Object o = elements.remove(pos - 1); + Object o = elements.remove(pos); if (o instanceof JsonConstruct) { ObjectOps.decrement((_BaseObject_) o); @@ -3254,6 +3255,10 @@ * * @param idx * The 1-based index of the json array element to retrieve. + * @param method + * The actual 'get' method name, used for error message. + * @param type + * The expected JSON type. * @param converter * The function that will convert the stored Java element instance to a legacy instance. * @param nullSupplier @@ -3286,7 +3291,16 @@ method, JsonBackend.instance().getJsonDataTypeName(type), JsonBackend.instance().getJsonDataTypeName(vtype)), 16060)); } - BaseDataType res = converter.apply(val); + BaseDataType res = null; + try + { + res = converter.apply(val); + } + catch(Exception ex) + { + log.log(Level.WARNING, "", ex); + returnError(ObjectOps.newInstance(JsonError.class)); + } returnNormal(res); } @@ -3372,8 +3386,6 @@ logical omit = TypeFactory.initInput(_omit); return function(this, "Read", logical.class, new Block((Body) () -> { - logical ret = TypeFactory.logical(); - if (!htt._isValid() || !htt.isType("TEMP-TABLE")) { undoThrow(JsonError.newInstance(ErrorManager.getInvalidParameterError("temp-table handle", this, @@ -3390,17 +3402,20 @@ JsonExport export = new JsonExport(); try { - export.serializeTempTable(jb, tmpBuffer, !omit.isUnknown() && omit.booleanValue()); - ret.assign(true); + export.serializeTempTable(jb, tmpBuffer, true, !omit.isUnknown() && omit.booleanValue()); + returnNormal(true); } - catch (Exception e) + catch (IOException e) { log.log(Level.WARNING, "", e); returnError(ObjectOps.newInstance(JsonError.class)); } - - returnNormal(ret); })); } + + @Override + protected void throwStreamNotFound (String streamName) { + undoThrow(JsonError.newInstance(String.format("Invalid stream parameter to Progress.Json.ObjectModel.JsonArray:WriteStream( ). Could not find open stream \"%s\".", streamName), 16062)); + } } === modified file 'src/com/goldencode/p2j/oo/json/objectmodel/JsonConstruct.java' --- src/com/goldencode/p2j/oo/json/objectmodel/JsonConstruct.java 2021-02-23 07:39:32 +0000 +++ src/com/goldencode/p2j/oo/json/objectmodel/JsonConstruct.java 2021-03-19 11:24:54 +0000 @@ -25,6 +25,7 @@ ** 20210215 Remove clean/read methods, refactor write. ** 012 CA 20210221 Fixed 'qualified', 'extent' and 'returns' annotations at the legacy ** signature. +** ME 20210319 Fix write stream, add method to throw stream not found exception. */ /* @@ -204,22 +205,31 @@ { handle strHndl = TypeFactory.initInput(_strHndl); logical fmtted = TypeFactory.initInput(_fmtted); - character enc = TypeFactory.initInput(_enc); + character enc = TypeFactory.initInput(_enc); - return function(this, "Write", logical.class, new Block((Body) () -> - { - Stream stream = (Stream) strHndl.getResource(); - OutputStream o = new OutputStreamWrapper(stream); + return function(this, "Write", logical.class, new Block((Body) () -> { + + if (!strHndl._isValid() || !(strHndl.get() instanceof Stream)) + { + ErrorManager.recordOrThrowError(new int[] { 14263, 15351 }, + new String[] { + "Invalid handle or handle type for STREAM-HANDLE", + "WRITE-JSON( ) could not find the specified stream-handle." }, + false, false, false); + } + + OutputStream o = new OutputStreamWrapper(strHndl.unwrapStream()); logical ret = TypeFactory.logical(); - - try { + + try + { writeImpl(o, fmtted, enc); ret.assign(true); } catch (Exception e) { returnError(JsonError.newInstance(e.getMessage(), 0)); - } + } returnNormal(ret); })); } @@ -637,10 +647,11 @@ return function(this, "WriteStream", logical.class, new Block((Body) () -> { - logical ret = TypeFactory.logical(); + Stream s = StreamFactory.findOutputStream(sname.isUnknown() ? null : sname.toStringMessage()); if (s != null) { + logical ret = TypeFactory.logical(); boolean pretty = fmtted != null && !fmtted.isUnknown() && fmtted.booleanValue(); OutputStream o = new OutputStreamWrapper(s); @@ -653,14 +664,29 @@ } catch (Exception e) { - log.log(Level.WARNING, "", e); - returnError(ObjectOps.newInstance(JsonError.class)); + returnError(JsonError.newInstance(e.getMessage(), 0)); } - } - - returnNormal(ret); + + returnNormal(ret); + } + else + { + throwStreamNotFound(sname.toStringMessage()); + } + })); } + + /** + * Throw stream not found exception. + * + * The error number and message is different for JsonArray/JsonObject. + * + * @param streamName + */ + protected void throwStreamNotFound (String streamName) { + undoThrow(SysError.newInstance(String.format("WRITE-JSON( ) could not find open stream \"%s\"", streamName), 15343, false, true)); + } /** * Adds a json element (object property or array element) to the json object. === modified file 'src/com/goldencode/p2j/oo/json/objectmodel/JsonObject.java' --- src/com/goldencode/p2j/oo/json/objectmodel/JsonObject.java 2021-03-04 19:07:41 +0000 +++ src/com/goldencode/p2j/oo/json/objectmodel/JsonObject.java 2021-03-23 11:42:56 +0000 @@ -20,6 +20,8 @@ ** 011 CA 20210221 Fixed 'qualified', 'extent' and 'returns' annotations at the legacy ** signature. ** CA 20210304 Fixed date, datetime and datetimetz literal parsing. +** 012 ME 20210319 Fix error handling, implement missing methods (OE12.2). +** ME 20210323 Format error message when buffer isn't from a temp-table. */ /* @@ -82,14 +84,15 @@ import com.goldencode.p2j.persist.*; import com.goldencode.p2j.persist.serial.*; import com.goldencode.p2j.util.*; +import com.goldencode.p2j.util.ErrorManager; import com.goldencode.p2j.util.BlockManager.Action; import com.goldencode.p2j.util.BlockManager.Condition; import java.io.*; -import java.time.format.*; import java.util.*; import java.util.function.*; import java.util.logging.*; +import java.util.stream.Collectors; import static com.goldencode.p2j.util.BlockManager.*; import static com.goldencode.p2j.report.ReportConstants.*; @@ -444,7 +447,7 @@ return function(this, "AddNull", logical.class, new Block((Body) () -> { - addPropertyImpl(prop, null); + addPropertyImpl(prop, null, "AddNull"); })); } @@ -461,13 +464,16 @@ return function(this, "AddNumber", logical.class, new Block((Body) () -> { + checkPropNotExists(prop, "AddNumber"); + try { - addPropertyImpl(prop, new decimal(val)); + addPropertyImpl(prop.toStringMessage(), val.isUnknown() ? (Object) null : Double.parseDouble(val.getValue())); } catch (NumberFormatException e) { - returnError(ObjectOps.newInstance(JsonError.class)); + undoThrow(JsonError.newInstance(ErrorManager.getInvalidParameterError("value", this, + "AddNumber", "Not a valid JSON number."), 16053)); } })); } @@ -505,7 +511,7 @@ return function(this, "GetCharacter", character.class, new Block((Body) () -> { - getPropertyImpl(prop, o -> new character((String) o), character::new); + getPropertyImpl(prop, "GetCharacter", 1, o -> new character((String) o), unknown::new); })); } @@ -520,7 +526,7 @@ return function(this, "GetCOMHandle", comhandle.class, new Block((Body) () -> { - getPropertyImpl(prop, o -> comhandle.fromResourceId(((Number) o).longValue()), comhandle::new); + getPropertyImpl(prop, "GetCOMHandle", 2, o -> comhandle.fromResourceId(((Number) o).longValue()), comhandle::new); })); } @@ -535,7 +541,7 @@ return function(this, "GetDate", date.class, new Block((Body) () -> { - getPropertyImpl(prop, o -> date.parseLiteral((String) o), date::new); + getPropertyImpl(prop, "GetDate", 1, o -> date.parseLiteral((String) o), date::new); })); } @@ -550,7 +556,7 @@ return function(this, "GetDatetime", datetime.class, new Block((Body) () -> { - getPropertyImpl(prop, o-> datetime.parseLiteral((String) o), datetime::new); + getPropertyImpl(prop, "GetDatetime", 1, o-> datetime.parseLiteral((String) o), datetime::new); })); } @@ -565,7 +571,7 @@ return function(this, "GetDatetimeTZ", datetimetz.class, new Block((Body) () -> { - getPropertyImpl(prop, o -> datetimetz.parseLiteral((String) o), datetimetz::new); + getPropertyImpl(prop, "GetDatetimeTZ", 1, o -> datetimetz.parseLiteral((String) o), datetimetz::new); })); } @@ -580,7 +586,7 @@ return function(this, "GetDecimal", decimal.class, new Block((Body) () -> { - getPropertyImpl(prop, o -> new decimal((Number) o), decimal::new); + getPropertyImpl(prop, "GetDecimal", 2, o -> new decimal((Number) o), decimal::new); })); } @@ -595,7 +601,7 @@ return function(this, "GetHandle", handle.class, new Block((Body) () -> { - getPropertyImpl(prop, o -> handle.fromResourceId(((Number) o).longValue()), handle::new); + getPropertyImpl(prop, "GetHandle", 2, o -> handle.fromResourceId(((Number) o).longValue()), handle::new); })); } @@ -610,7 +616,7 @@ return function(this, "GetInt64", int64.class, new Block((Body) () -> { - getPropertyImpl(prop, o -> new int64((Number) o), int64::new); + getPropertyImpl(prop, "GetInt64", 2, o -> new int64((Number) o), int64::new); })); } @@ -625,7 +631,7 @@ return function(this, "GetInteger", integer.class, new Block((Body) () -> { - getPropertyImpl(prop, o -> new integer((Number) o), integer::new); + getPropertyImpl(prop, "GetInteger", 2, o -> new integer((Number) o), integer::new); })); } @@ -640,7 +646,7 @@ return function(this, "GetJsonArray", object.class, new Block((Body) () -> { - getPropertyImpl(prop, o -> new object((JsonArray) o), object::new); + getPropertyImpl(prop, "GetJsonArray", 5, o -> new object((JsonArray) o), object::new); })); } @@ -655,7 +661,7 @@ return function(this, "GetJsonObject", object.class, new Block((Body) () -> { - getPropertyImpl(prop, o -> new object((JsonObject) o), object::new); + getPropertyImpl(prop, "GetJsonObject", 4, o -> new object((JsonObject) o), object::new); })); } @@ -680,18 +686,17 @@ return function(this, "GetJsonText", longchar.class, new Block((Body) () -> { - getPropertyImpl(prop, o -> - { - try - { - return getJsonText(o); - } - catch (IOException e) - { - throw new RuntimeException(e); - } - }, - longchar::new); + checkPropExists(prop, "GetJsonText"); + + try + { + returnNormal(getJsonText(properties.get(prop.toStringMessage()))); + } + catch (IOException e) + { + log.log(Level.WARNING, "", e); + returnError(ObjectOps.newInstance(JsonError.class)); + } })); } @@ -706,7 +711,7 @@ return function(this, "GetLogical", logical.class, new Block((Body) () -> { - getPropertyImpl(prop, o -> new logical((Boolean) o), logical::new); + getPropertyImpl(prop, "GetLogical", 3, o -> new logical((Boolean) o), logical::new); })); } @@ -721,7 +726,7 @@ return function(this, "GetLongchar", longchar.class, new Block((Body) () -> { - getPropertyImpl(prop, o -> new longchar((String) o), longchar::new); + getPropertyImpl(prop, "GetLongchar", 1, o -> new longchar((String) o), longchar::new); })); } @@ -738,10 +743,13 @@ return function(this, "GetLongchar", longchar.class, new Block((Body) () -> { - getPropertyImpl(prop, o -> + getPropertyImpl(prop, "GetLongchar", 1, o -> { - longchar res = new longchar((String) o); - res.fixCodePage(cp); + longchar res = TypeFactory.longchar(); + if (!TextOps.isEmpty(cp)) + res.fixCodePage(cp); + res.assign(o); + return res; }, longchar::new); @@ -759,7 +767,7 @@ return function(this, "GetMemptr", memptr.class, new Block((Body) () -> { - getPropertyImpl(prop, o -> new memptr(Base64.getDecoder().decode((String) o)), memptr::new); + getPropertyImpl(prop, "GetMemptr", 1, o -> new memptr(Base64.getDecoder().decode((String) o)), unknown::new); })); } @@ -769,9 +777,8 @@ { return extentFunction(this, "GetNames", character.class, new Block((Body) () -> { - Set nameSet = properties.keySet(); - character[] names = nameSet.toArray(new character[0]); - returnExtentNormal(names); + List names = properties.keySet().stream().map(character::new).collect(Collectors.toList()); + returnExtentNormal(names.toArray(new character[names.size()])); })); } @@ -786,7 +793,7 @@ return function(this, "GetRaw", raw.class, new Block((Body) () -> { - getPropertyImpl(prop, + getPropertyImpl(prop, "GetRaw", 1, o -> new raw(Base64.getDecoder().decode((String) o)), raw::instantiateUnknownRaw); })); } @@ -802,7 +809,7 @@ return function(this, "GetRecid", recid.class, new Block((Body) () -> { - getPropertyImpl(prop, o -> new recid(((Number) o).longValue()), recid::new); + getPropertyImpl(prop, "GetRecid", 2, o -> new recid(((Number) o).longValue()), recid::new); })); } @@ -817,7 +824,7 @@ return function(this, "GetRowid", rowid.class, new Block((Body) () -> { - getPropertyImpl(prop, o -> + getPropertyImpl(prop, "GetRowid", 1, o -> { Base64.Decoder dec = Base64.getDecoder(); return new rowid(Utils.bytesToLongLE(dec.decode((String) o))); @@ -837,15 +844,10 @@ return function(this, "GetType", integer.class, new Block((Body) () -> { - if (checkValidPropName(prop)) - { - Object value = properties.get(prop.toStringMessage()); - returnNormal(new integer(JsonBackend.instance().getJsonDataType(value))); - } - else - { - returnError(ObjectOps.newInstance(JsonError.class)); - } + checkPropExists(prop, "GetType"); + + Object value = properties.get(prop.toStringMessage()); + returnNormal(new integer(JsonBackend.instance().getJsonDataType(value))); })); } @@ -860,14 +862,8 @@ return function(this, "Has", logical.class, new Block((Body) () -> { - if (checkValidPropName(prop)) - { - returnNormal(new logical(properties.containsKey(prop.toStringMessage()))); - } - else - { - returnError(ObjectOps.newInstance(JsonError.class)); - } + checkValidPropName(prop, "Has"); + returnNormal(new logical(properties.containsKey(prop.toStringMessage()))); })); } @@ -882,22 +878,10 @@ return function(this, "IsNull", logical.class, new Block((Body) () -> { - if (checkValidPropName(prop)) - { - if (!properties.containsKey(prop.toStringMessage())) - { - returnError(ObjectOps.newInstance(JsonError.class)); - } - else - { - Object val = properties.get(prop.toStringMessage()); - returnNormal(new logical(val == null)); - } - } - else - { - returnError(ObjectOps.newInstance(JsonError.class)); - } + checkPropExists(prop, "IsNull"); + + Object val = properties.get(prop.toStringMessage()); + returnNormal(new logical(val == null)); })); } @@ -935,7 +919,7 @@ logical omit = TypeFactory.initInput(_omit); logical b4img = TypeFactory.initInput(_b4img); - return function(this, "Read", logical.class, new Block((Init) () -> + return function(this, "Read", logical.class, new Block((Body) () -> { if (!bh.isUnknown()) { @@ -943,8 +927,7 @@ if (res instanceof BufferImpl) { BufferImpl buf = (BufferImpl) res; - TemporaryBuffer tb = (TemporaryBuffer) buf.buffer(); - returnNormal(readImpl(tb, omit)); + returnNormal(readImpl(buf, omit)); } else if (res instanceof TempTable) { @@ -957,15 +940,10 @@ DataSet ds = (DataSet) res; returnNormal(readImpl(ds, omit, b4img)); } - else - { - returnError(ObjectOps.newInstance(JsonError.class)); - } - } - else - { - returnError(ObjectOps.newInstance(JsonError.class)); - } + } + + undoThrow(JsonError.newInstance(ErrorManager.getInvalidParameterError("handle", this, + "Read", "Not initialized or not a handle to a temp-table/dataset/buffer."), 16070)); })); } @@ -980,19 +958,14 @@ return function(this, "Remove", logical.class, new Block((Body) () -> { - if (checkPropExists(prop)) - { - Object o = properties.remove(prop.toStringMessage()); - if (o instanceof JsonConstruct) - { - ObjectOps.decrement((_BaseObject_) o); - } - returnNormal(new logical(true)); - } - else - { - returnError(ObjectOps.newInstance(JsonError.class)); - } + checkPropExists(prop, "Remove"); + + Object o = properties.remove(prop.toStringMessage()); + if (o instanceof JsonConstruct) + { + ObjectOps.decrement((_BaseObject_) o); + } + returnNormal(new logical(true)); })); } @@ -1296,7 +1269,7 @@ return function(this, "SetNull", logical.class, new Block((Body) () -> { - setPropertyImpl(prop, null); + setPropertyImpl(prop, null, "SetNull"); })); } @@ -1313,13 +1286,16 @@ return function(this, "SetNumber", logical.class, new Block((Body) () -> { + checkPropExists(prop, "SetNumber"); + try { - setPropertyImpl(prop, new decimal(val)); + addPropertyImpl(prop.toStringMessage(), val.isUnknown() ? (Object) null : Double.parseDouble(val.getValue())); } catch (NumberFormatException e) { - returnError(ObjectOps.newInstance(JsonError.class)); + undoThrow(JsonError.newInstance(ErrorManager.getInvalidParameterError("value", this, + "SetNumber", "Not a valid JSON number."), 16053)); } })); } @@ -1440,20 +1416,30 @@ */ private void addPropertyImpl(character name, BaseDataType value) { - if (checkValidPropName(name)) + addPropertyImpl(name, value, "Add"); + } + + /** + * Implements the {@code add} legacy methods for adding json property to this instance. + * + * @param name + * Property name. + * @param value + * Property value. + * @param method + * Calling method name, used for error message. + */ + private void addPropertyImpl(character name, BaseDataType value, String method) + { + checkPropNotExists(name, method); + + try { - try - { - Object newValue = JsonBackend.instance().convertLegacyValueToJson(value); - addPropertyImpl(name.toStringMessage(), newValue); - returnNormal(new logical(true)); - } - catch (ClassCastException e) - { - returnError(ObjectOps.newInstance(JsonError.class)); - } + Object newValue = value != null ? JsonBackend.instance().convertLegacyValueToJson(value) : null; + addPropertyImpl(name.toStringMessage(), newValue); + returnNormal(new logical(true)); } - else + catch (ClassCastException e) { returnError(ObjectOps.newInstance(JsonError.class)); } @@ -1469,20 +1455,28 @@ */ private void setPropertyImpl(character name, BaseDataType value) { - if (checkPropExists(name)) + setPropertyImpl(name, value, "Set"); + } + + /** + * Implements the {@code set} legacy methods for setting json property in this instance. + * + * @param name + * Property name. + * @param value + * Property value. + */ + private void setPropertyImpl(character name, BaseDataType value, String method) + { + checkPropExists(name, method); + + try { - try - { - Object newValue = JsonBackend.instance().convertLegacyValueToJson(value); - addPropertyImpl(name.toStringMessage(), newValue); - returnNormal(new logical(true)); - } - catch (ClassCastException e) - { - returnError(ObjectOps.newInstance(JsonError.class)); - } + Object newValue = value != null ? JsonBackend.instance().convertLegacyValueToJson(value) : null; + addPropertyImpl(name.toStringMessage(), newValue); + returnNormal(new logical(true)); } - else + catch (ClassCastException e) { returnError(ObjectOps.newInstance(JsonError.class)); } @@ -1493,12 +1487,18 @@ * * @param property * Property name. - * + * @param method + * Calling method name, used for error message. + * * @return {@code true} when the property name is valid, {@code false} otherwise. */ - private boolean checkValidPropName(character property) + private void checkValidPropName(character property, String method) { - return property != null && !property.isUnknown() && !property.toStringMessage().isEmpty(); + if (TextOps.isEmpty(property)) + { + undoThrow(JsonError.newInstance(ErrorManager.getInvalidParameterError("name", this, + method, "Can not be UNKNOWN (?) or empty string."), 16055)); + } } /** @@ -1506,19 +1506,56 @@ * * @param property * Property name. + * @param method + * Calling method name, used for error message. * * @return {@code true} when the property name is valid, {@code false} otherwise. */ - private boolean checkPropExists(character property) + private void checkPropExists(character property, String method) { - return checkValidPropName(property) && properties.containsKey(property.toStringMessage()); + checkValidPropName(property, method); + + String key = property.toStringMessage(); + + if (!properties.containsKey(key)) + { + undoThrow(JsonError.newInstance(String.format("Call to Progress.Json.ObjectModel.JsonObject:%s( ) failed. Property '%s' was not found.", + method, key), 16058)); + } } /** + * Checks whether the supplied value is valid json property name and whether the property not exists. + * + * @param property + * Property name. + * @param method + * Calling method name, used for error message. + * + * @return {@code true} when the property name is valid, {@code false} otherwise. + */ + private void checkPropNotExists(character property, String method) + { + checkValidPropName(property, method); + + String key = property.toStringMessage(); + + if (properties.containsKey(key)) + { + undoThrow(JsonError.newInstance(String.format("Call to Progress.Json.ObjectModel.JsonObject:%s( ) failed. Attempted to add duplicate property '%s'.", + method, key), 16056)); + } + } + + /** * Implements the {@code get} legacy methods. * * @param propName * Property name. + * @param method + * The actual 'get' method name, used for error message. + * @param type + * The expected JSON type. * @param converter * The function that will convert the stored Java value instance to a legacy instance. * @param nullSupplier @@ -1526,46 +1563,44 @@ * corresponding legacy value. */ private void getPropertyImpl(character propName, + String method, + int type, Function converter, Supplier nullSupplier) { - if (checkPropExists(propName)) - { - Object val = properties.get(propName.toStringMessage()); - - BaseDataType res = null; - try - { - if (val == null) - { - res = nullSupplier.get(); - } - else - { - res = converter.apply(val); - } - } - catch(ClassCastException | DateTimeParseException ex) - { - log.log(Level.WARNING, "", ex); - // the value is not the expected type or has incorrect format - returnError(ObjectOps.newInstance(JsonError.class)); - } - catch(RuntimeException ex) - { - log.log(Level.WARNING, "", ex); - returnError(ObjectOps.newInstance(JsonError.class)); - } - - if (res != null) - { - returnNormal(res); - } - } - else - { + checkPropExists(propName, method); + + Object val = properties.get(propName.toStringMessage()); + int vtype = JsonBackend.instance().getJsonDataType(val); + + if (vtype == 6) + returnNormal(nullSupplier.get()); + + if (vtype != type) + { + undoThrow(JsonError.newInstance(String.format("Call to Progress.Json.ObjectModel.JsonObject:%s( ) failed. Expected a JSON %s value, found a JSON %s value.", + method, JsonBackend.instance().getJsonDataTypeName(type), JsonBackend.instance().getJsonDataTypeName(vtype)), 16060)); + } + + BaseDataType res = null; + + try + { + if (val == null) + { + res = nullSupplier.get(); + } + else + { + res = converter.apply(val); + } + } + catch(Exception ex) + { + log.log(Level.WARNING, "", ex); returnError(ObjectOps.newInstance(JsonError.class)); } + returnNormal(res); } protected logical readImpl(TemporaryBuffer tb, final logical omit) @@ -1574,12 +1609,47 @@ ObjectBuilder jb = new ObjectBuilder(this); JsonExport export = new JsonExport(); - try - { - export.exportTempBuffer(tb, omit, new logical(false), jb); - returnNormal(new logical(true)); - } - catch (PersistenceException e) + this.clear(); + + try + { + export.serializeTempTable(jb, tb, false, !omit.isUnknown() && omit.booleanValue()); + returnNormal(new logical(true)); + } + catch (IOException e) + { + log.log(Level.WARNING, "", e); + returnError(ObjectOps.newInstance(JsonError.class)); + } + })); + } + + protected logical readImpl(BufferImpl buffer, final logical omit) + { + return function(this, "Read", logical.class, new Block((Body) () -> { + + if (!buffer._available()) + { + undoThrow(JsonError.newInstance(ErrorManager.getInvalidParameterError("buffer-handle", this, + "Read", "No record in buffer."), 16065)); + } + + if (!buffer.buffer().isTemporary()) + { + ErrorManager.recordOrThrowError(15378, "JSON methods are only allowed on TEMP-TABLE buffers", false, false); + } + + ObjectBuilder jb = new ObjectBuilder(this); + JsonExport export = new JsonExport(); + + this.clear(); + + try + { + export.serializeRecord(jb, (TemporaryBuffer) buffer.buffer(), !omit.isUnknown() && omit.booleanValue()); + returnNormal(new logical(true)); + } + catch (IOException e) { log.log(Level.WARNING, "", e); returnError(ObjectOps.newInstance(JsonError.class)); @@ -1593,16 +1663,18 @@ ObjectBuilder jb = new ObjectBuilder(this); JsonExport export = new JsonExport(); + this.clear(); + try { - export.exportDataSet(pds, false, + export.serializeDataSet(jb, pds, !omit.isUnknown() && omit.booleanValue(), - !b4img.isUnknown() && b4img.booleanValue(), - jb); + false, + !b4img.isUnknown() && b4img.booleanValue()); returnNormal(new logical(true)); } - catch (PersistenceException e) + catch (IOException e) { log.log(Level.WARNING, "", e); returnError(ObjectOps.newInstance(JsonError.class)); === modified file 'src/com/goldencode/p2j/oo/net/http/DefaultRequestBuilder.java' --- src/com/goldencode/p2j/oo/net/http/DefaultRequestBuilder.java 2021-02-21 16:25:50 +0000 +++ src/com/goldencode/p2j/oo/net/http/DefaultRequestBuilder.java 2021-03-11 08:18:20 +0000 @@ -133,10 +133,15 @@ @Override public object getRequest() { - return function(this, "Request", object.class, new Block((Body) () -> - { - object request = TypeFactory.object(IhttpRequest.class); - + object request = TypeFactory.object(IhttpRequest.class); + + return function(this, "Request", object.class, new Block( + (Init) () -> + { + ObjectOps.register(request); + }, + (Body) () -> + { object requestType = getRegistry().ref(). _get(ObjectOps.getLegacyName(IhttpRequest.class)); === modified file 'src/com/goldencode/p2j/oo/net/http/RequestBuilder.java' --- src/com/goldencode/p2j/oo/net/http/RequestBuilder.java 2021-02-23 07:39:32 +0000 +++ src/com/goldencode/p2j/oo/net/http/RequestBuilder.java 2021-03-11 08:18:20 +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. +** ME 20210310 Add block 'referent' for static methods. */ /* @@ -212,7 +213,7 @@ @LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL) private static void initializeRegistry(final object _poRegistry) { - internalProcedure(null, "InitializeRegistry", new Block((Body) () -> { + internalProcedure(RequestBuilder.class, "InitializeRegistry", new Block((Body) () -> { _poRegistry.ref()._put(ObjectOps.getLegacyName(IAuthenticatedRequest.class), ObjectOps.getLegacyClass(AuthenticatedRequest.class)); _poRegistry.ref()._put(ObjectOps.getLegacyName(IhttpRequest.class), @@ -233,7 +234,7 @@ @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) public static object getRegistry() { - return function(null, "Registry", object.class, new Block((Body) () -> + return function(RequestBuilder.class, "Registry", object.class, new Block((Body) () -> { object registry = REGISTRY.get(); === modified file 'src/com/goldencode/p2j/oo/net/http/ResponseBuilder.java' --- src/com/goldencode/p2j/oo/net/http/ResponseBuilder.java 2021-02-23 07:39:32 +0000 +++ src/com/goldencode/p2j/oo/net/http/ResponseBuilder.java 2021-03-11 08:18:20 +0000 @@ -11,6 +11,7 @@ ** 20210208 Fix static registry property initialization. ** 004 CA 20210221 Fixed 'qualified', 'extent' and 'returns' annotations at the legacy ** signature. +** ME 20210310 Add block 'referent' for static methods. */ /* @@ -133,7 +134,7 @@ @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) public static object getRegistry() { - return function(null, "Registry", object.class, new Block((Body) () -> + return function(ResponseBuilder.class, "Registry", object.class, new Block((Body) () -> { object registry = REGISTRY.get(); === modified file 'src/com/goldencode/p2j/oo/net/http/filter/payload/MessageWriter.java' --- src/com/goldencode/p2j/oo/net/http/filter/payload/MessageWriter.java 2021-02-23 07:39:32 +0000 +++ src/com/goldencode/p2j/oo/net/http/filter/payload/MessageWriter.java 2021-03-10 13:13:29 +0000 @@ -13,6 +13,8 @@ ** 20210204 Allocate memory when reading from file input stream. ** 004 CA 20210221 Fixed 'qualified', 'extent' and 'returns' annotations at the legacy ** signature. +** 005 ME 20210310 Use initInput in setter methods to avoid issue with 'invalid' object +** when value is directly returned by a method. */ /* @@ -137,8 +139,10 @@ qualified = "OpenEdge.Logging.ILogWriter", mode = "INPUT") }) @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) - public void setLogger(object poLogger) + public void setLogger(object _poLogger) { + object poLogger = TypeFactory.initInput(_poLogger); + internalProcedure(this, "Logger", new Block((Body) () -> { this.logger.assign(poLogger); @@ -186,8 +190,10 @@ qualified = "Progress.Lang.Object", mode = "INPUT") }) @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) - public void setEntity(object poEntity) + public void setEntity(object _poEntity) { + object poEntity = TypeFactory.initInput(_poEntity); + internalProcedure(this, "Entity", new Block((Body) () -> { Assert.isType(poEntity, entityType); @@ -200,7 +206,11 @@ */ public void __net_http_filter_payload_MessageWriter_execute__() { - externalProcedure(MessageWriter.this, new Block((Body) () -> + externalProcedure(MessageWriter.this, new Block((Init) () -> + { + ObjectOps.register(entity, entityType, logger); + }, + (Body) () -> { onBlockLevel(Condition.ERROR, Action.THROW); { === 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-02-23 07:39:32 +0000 +++ src/com/goldencode/p2j/oo/net/http/filter/writer/AuthenticationRequestWriterBuilder.java 2021-03-11 08:18:20 +0000 @@ -12,6 +12,7 @@ ** 20210208 Fix static registry property initialization. ** 005 CA 20210221 Fixed 'qualified', 'extent' and 'returns' annotations at the legacy ** signature. +** ME 20210310 Add block 'referent' for static methods. */ /* @@ -211,7 +212,7 @@ @LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL) public static object getRegistry() { - return function(null, "Registry", object.class, new Block((Body) () -> { + return function(AuthenticationRequestWriterBuilder.class, "Registry", object.class, new Block((Body) () -> { object registry = REGISTRY.get(); if (!registry._isValid()) === modified file 'src/com/goldencode/p2j/oo/net/http/filter/writer/BodyWriterBuilder.java' --- src/com/goldencode/p2j/oo/net/http/filter/writer/BodyWriterBuilder.java 2021-02-21 16:25:50 +0000 +++ src/com/goldencode/p2j/oo/net/http/filter/writer/BodyWriterBuilder.java 2021-03-11 08:18:20 +0000 @@ -10,6 +10,7 @@ ** 003 MP 20201218 Implement as of OE12.2. ** 004 CA 20210221 Fixed 'qualified', 'extent' and 'returns' annotations at the legacy ** signature. +** ME 20210310 Add block 'referent' for static methods. */ /* @@ -118,7 +119,7 @@ object _poPart) { object poPart = TypeFactory.initInput(_poPart); - return function(null, "Build", object.class, new Block((Body)() -> { + return function(BodyWriterBuilder.class, "Build", object.class, new Block((Body)() -> { if (!poPart.ref().getHeaders().ref().has(new character("Content-Type")).booleanValue()) { @@ -168,7 +169,7 @@ { object poMessage = TypeFactory.initInput(_poMessage); - return function(null, "Build", object.class, new Block((Body)() -> + return function(BodyWriterBuilder.class, "Build", object.class, new Block((Body)() -> { if (!poMessage.ref().hasHeader(new character("Content-Type")).booleanValue()) { @@ -215,7 +216,7 @@ { character pcContentType = TypeFactory.initInput(_pcContentType); - return function(null, "Build", object.class, new Block((Body)() -> + return function(BodyWriterBuilder.class, "Build", object.class, new Block((Body)() -> { returnNormal(MessageWriterBuilder.build(pcContentType, BodyWriterRegistry.getRegistry())); })); === 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-02-23 07:39:32 +0000 +++ src/com/goldencode/p2j/oo/net/http/filter/writer/BodyWriterRegistry.java 2021-03-11 08:18:20 +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. +** ME 20210310 Add block 'referent' for static methods. */ /* @@ -140,7 +141,7 @@ @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) public static void initializeRegistry(final object _poRegistry) { - internalProcedure(null, "InitializeRegistry", new Block((Body) ()-> { + internalProcedure(BodyWriterRegistry.class, "InitializeRegistry", new Block((Body) ()-> { object handlerCls = ObjectOps.getLegacyClass(BinaryBodyWriter.class); // binary _poRegistry.ref()._put("application/gzip", handlerCls); === modified file 'src/com/goldencode/p2j/oo/net/http/filter/writer/EntityWriterBuilder.java' --- src/com/goldencode/p2j/oo/net/http/filter/writer/EntityWriterBuilder.java 2021-02-21 16:25:50 +0000 +++ src/com/goldencode/p2j/oo/net/http/filter/writer/EntityWriterBuilder.java 2021-03-11 08:18:20 +0000 @@ -10,6 +10,7 @@ ** 003 MP 20201218 Implement as of OE12.2. ** 004 CA 20210221 Fixed 'qualified', 'extent' and 'returns' annotations at the legacy ** signature. +** ME 20210310 Add block 'referent' for static methods. */ /* @@ -108,7 +109,7 @@ { object poPart = TypeFactory.initInput(_poPart); - return function(null, "Build", object.class, new Block((Body)() -> { + return function(EntityWriterBuilder.class, "Build", object.class, new Block((Body)() -> { object contentType = TypeFactory.object(HttpHeader.class); @@ -142,7 +143,7 @@ { object poMessage = TypeFactory.initInput(_poMessage); - return function(null, "Build", object.class, new Block((Body)() -> + return function(EntityWriterBuilder.class, "Build", object.class, new Block((Body)() -> { object contentType = TypeFactory.object(HttpHeader.class); @@ -175,7 +176,7 @@ { character pcContentType = TypeFactory.initInput(_pcContentType); - return function(null, "Build", object.class, new Block((Body)() -> + return function(EntityWriterBuilder.class, "Build", object.class, new Block((Body)() -> { returnNormal(MessageWriterBuilder.build(pcContentType, EntityWriterRegistry.getRegistry())); })); === 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-02-23 17:28:47 +0000 +++ src/com/goldencode/p2j/oo/net/http/filter/writer/MessageWriterBuilder.java 2021-03-11 08:18:20 +0000 @@ -12,6 +12,7 @@ ** 20210208 Fix static registry property initialization. ** 004 CA 20210221 Fixed 'qualified', 'extent' and 'returns' annotations at the legacy ** signature. +** ME 20210310 Add block 'referent' for static methods, use initInput for object params. */ /* @@ -183,9 +184,11 @@ @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) public static object getRegistry() { - return function(BodyWriterRegistry.class, "Registry", object.class, new Block((Body) () -> { + object registry = TypeFactory.object(BuilderRegistry.class); + + return function(BodyWriterRegistry.class, "Registry", object.class, new Block((Body) () -> { - object registry = REGISTRY.get(); + registry.assign(REGISTRY.get()); if (!registry._isValid()) { @@ -208,10 +211,10 @@ @LegacyParameter(name = "poRegistry", type = "OBJECT", qualified = "openedge.net.http.builderregistry", mode = "INPUT") }) @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) - public static void initializeRegistry(final object _poRegistry) + private static void initializeRegistry(final object _poRegistry) { - internalProcedure(null, "InitializeRegistry", new Block((Body) ()-> { + internalProcedure(MessageWriterBuilder.class, "InitializeRegistry", new Block((Body) ()-> { _poRegistry.ref().put(new character(ObjectOps.getLegacyName(MessageWriterBuilder.class)), ObjectOps.getLegacyClass(DefaultMessageWriterBuilder.class)); @@ -227,7 +230,7 @@ @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) protected object getWriterRegistry() { - return function(BodyWriterRegistry.class, "WriterRegistry", object.class, + return function(MessageWriterBuilder.class, "WriterRegistry", object.class, new Block((Body) () -> { returnNormal(this.writerRegistry); @@ -252,7 +255,7 @@ @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) protected character getContentType() { - return function(BodyWriterRegistry.class, "ContentType", character.class, + return function(MessageWriterBuilder.class, "ContentType", character.class, new Block((Body) () -> { returnNormal(this.contentType); @@ -274,10 +277,13 @@ }) @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) public static object build( - object poMessage, - object poRegistry) + object _poMessage, + object _poRegistry) { - return function(null, "Build", object.class, new Block((Body)() -> { + object poMessage = TypeFactory.initInput(_poMessage); + object poRegistry = TypeFactory.initInput(_poRegistry); + + return function(MessageWriterBuilder.class, "Build", object.class, new Block((Body)() -> { object contentType = TypeFactory.object(HttpHeader.class); contentType.assign(poMessage.ref().getHeader(new character("Content-Type"))); @@ -306,10 +312,12 @@ }) @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) public static object build(character _pcContentType, - object poRegistry) + object _poRegistry) { character pcContentType = TypeFactory.initInput(_pcContentType); - return function(null, "Build", object.class, new Block((Body)() -> { + object poRegistry = TypeFactory.initInput(_poRegistry); + + return function(MessageWriterBuilder.class, "Build", object.class, new Block((Body)() -> { Assert.notNull(pcContentType, new character("Content type")); Assert.notNull(poRegistry, new character("Writer registry")); === 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-02-23 17:28:47 +0000 +++ src/com/goldencode/p2j/oo/net/http/filter/writer/RequestWriterBuilder.java 2021-03-10 09:44:27 +0000 @@ -12,6 +12,8 @@ ** - TypeFactory.initInput will take care of this. ** 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. +** 20210310 Added missing implementation (OE12.2). */ /* @@ -75,12 +77,13 @@ import com.goldencode.p2j.security.*; import static com.goldencode.p2j.report.ReportConstants.CVT_LVL_FULL; -import static com.goldencode.p2j.report.ReportConstants.RT_LVL_STUB; import static com.goldencode.p2j.report.ReportConstants.RT_LVL_FULL; import static com.goldencode.p2j.util.BlockManager.*; import static com.goldencode.p2j.util.InternalEntry.Type; import com.goldencode.p2j.oo.core.Assert; +import com.goldencode.p2j.oo.core.ByteBucket; +import com.goldencode.p2j.oo.core.ISupportInitialize; import com.goldencode.p2j.oo.lang.LegacyClass; import com.goldencode.p2j.oo.net.http.BuilderRegistry; import com.goldencode.p2j.oo.net.http.IhttpRequest; @@ -93,17 +96,17 @@ * in OpenEdge/Net/HTTP/Filter/Writer/RequestWriterBuilder.cls). */ @LegacyResource(resource = "OpenEdge.Net.HTTP.Filter.Writer.RequestWriterBuilder") -@LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB) +@LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) public class RequestWriterBuilder extends com.goldencode.p2j.oo.net.http.ConfigBuilder { @LegacySignature(type = Type.PROPERTY, name = "Request") - @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB) + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) private object request = TypeFactory.object(IhttpRequest.class); @LegacySignature(type = Type.PROPERTY, name = "Writer") - @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB) + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) private object writer = TypeFactory.object(MessageWriter.class); @@ -129,6 +132,10 @@ public void __net_http_filter_writer_RequestWriterBuilder_execute__() { externalProcedure(RequestWriterBuilder.class, RequestWriterBuilder.this, new Block( + (Init) () -> + { + ObjectOps.register(request, writer, REGISTRY); + }, (Body) () -> { onBlockLevel(Condition.ERROR, Action.THROW); @@ -143,7 +150,7 @@ * @return request. */ @LegacySignature(returns = "OBJECT", qualified = "OpenEdge.Net.HTTP.IHttpRequest", type = Type.GETTER, name = "Request") - @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB) + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) public object getRequest() { return function(RequestWriterBuilder.class, this, "Request", object.class, new Block((Body) () -> @@ -176,9 +183,11 @@ @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) public static object getRegistry() { - return function(null, "Registry", object.class, new Block((Body) () -> + object registry = TypeFactory.object(BuilderRegistry.class); + + return function(RequestWriterBuilder.class, "Registry", object.class, new Block((Body) () -> { - object registry = REGISTRY.get(); + registry.assign(REGISTRY.get()); if (!registry._isValid()) { @@ -203,7 +212,7 @@ @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) private static void initializeRegistry(final object _poRegistry) { - internalProcedure(null, "InitializeRegistry", new Block((Body) () -> + internalProcedure(RequestWriterBuilder.class, "InitializeRegistry", new Block((Body) () -> { _poRegistry.ref().put(new character("HTTP/1.1"), ObjectOps.getLegacyClass(DefaultRequestFilter.class)); @@ -216,7 +225,7 @@ { @LegacyParameter(name = "pRequest", type = "OBJECT", qualified = "openedge.net.http.ihttprequest", mode = "INPUT") }) - @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB) + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) public static object build( final object _pRequest) { @@ -224,22 +233,22 @@ return function(RequestWriterBuilder.class, "Build", object.class, new Block((Body) () -> { - UnimplementedFeature.missing("RequestWriterBuilder METHOD"); + returnNormal(ObjectOps.newInstance(RequestWriterBuilder.class, "I", pRequest)); })); } @LegacySignature(returns = "OBJECT", type = Type.METHOD, name = "Build", qualified = "openedge.net.http.filter.writer.requestwriterbuilder") - @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB) + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) public static object build() { return function(RequestWriterBuilder.class, "Build", object.class, new Block((Body) () -> { - UnimplementedFeature.missing("RequestWriterBuilder:Build METHOD"); + returnNormal(ObjectOps.newInstance(RequestWriterBuilder.class)); })); } @LegacySignature(type = Type.CONSTRUCTOR) - @LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_STUB) + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL) public static void __net_http_filter_writer_RequestWriterBuilder__static__() { externalProcedure(RequestWriterBuilder.class, new Block((Body) () -> @@ -252,7 +261,7 @@ * Constructor */ @LegacySignature(type = Type.CONSTRUCTOR) - @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB) + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) public void __net_http_filter_writer_RequestWriterBuilder_constructor__() { internalProcedure(RequestWriterBuilder.class, this, "__net_http_filter_writer_RequestWriterBuilder_constructor__", new Block((Body) () -> @@ -270,7 +279,7 @@ { @LegacyParameter(name = "pRequest", type = "OBJECT", qualified = "openedge.net.http.ihttprequest", mode = "INPUT") }) - @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB) + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) public void __net_http_filter_writer_RequestWriterBuilder_constructor__( final object _pRequest) { @@ -287,7 +296,7 @@ { @LegacyParameter(name = "poMessage", type = "OBJECT", qualified = "openedge.net.http.ihttprequest", mode = "INPUT") }) - @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB) + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) public object request( final object _poMessage) { @@ -295,7 +304,9 @@ return function(RequestWriterBuilder.class, this, "Request", object.class, new Block((Body) () -> { - UnimplementedFeature.missing("RequestWriterBuilder:Request METHOD"); + Assert.notNull(poMessage, new character("HTTP Message")); + setOption(new character(ObjectOps.getLegacyName(IhttpRequest.class)), poMessage); + returnNormal(ObjectOps.thisObject()); })); } @@ -303,7 +314,7 @@ { @LegacyParameter(name = "poMessageBody", type = "OBJECT", qualified = "openedge.core.bytebucket", mode = "INPUT") }) - @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB) + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) public object writeTo( final object _poMessageBody) { @@ -311,7 +322,9 @@ return function(RequestWriterBuilder.class, this, "WriteTo", object.class, new Block((Body) () -> { - UnimplementedFeature.missing("RequestWriterBuilder:WriteTo METHOD"); + Assert.notNull(poMessageBody, new character("Message Body")); + setOption(new character(ObjectOps.getLegacyName(ByteBucket.class)), poMessageBody); + returnNormal(ObjectOps.thisObject()); })); } @@ -321,12 +334,38 @@ * @return the message writer object */ @LegacySignature(returns = "OBJECT", type = Type.METHOD, name = "NewRequestWriter", qualified = "openedge.net.http.filter.payload.messagewriter") - @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB) + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) protected object newRequestWriter() { 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()) + { + Assert.isType(wCls, ObjectOps.getLegacyClass(MessageWriter.class)); + + wObj.assign(ObjectOps.newDynamicInstance(wCls.ref().getTypeName())); + + String bbClsName = ObjectOps.getLegacyName(ByteBucket.class); + + if (_hasOption(bbClsName)) + { + wObj.ref().setEntity(ObjectOps.cast(getOptionObjectValue(new character(bbClsName)), ByteBucket.class)); + } + else + { + wObj.ref().setEntity(ByteBucket.instance()); + } + + if (ObjectOps.typeOf(wObj, ISupportInitialize.class).booleanValue()) + { + ObjectOps.cast(wObj, ISupportInitialize.class).ref().initialize(); + } + } + + returnNormal(wObj); })); } @@ -340,7 +379,7 @@ @LegacyParameter(name = "pRequest", type = "OBJECT", qualified = "openedge.net.http.ihttprequest", mode = "INPUT"), @LegacyParameter(name = "pRegistry", type = "OBJECT", qualified = "openedge.net.http.builderregistry", mode = "INPUT") }) - @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB) + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) protected object getWriter( final object _pRequest, final object _pRegistry) @@ -354,26 +393,20 @@ object oCls = TypeFactory.object(LegacyClass.class); - if (!pRequest._isValid()) + if (pRequest._isValid()) + { + oCls.assign(pRegistry.ref().get(TextOps.trim(TextOps.substitute("&1+&2", pRequest.ref().getVersion(), pRequest.ref().getMethod())))); + + if (!oCls._isValid()) + { + oCls.assign(pRegistry.ref().get(pRequest.ref().getVersion())); + } + } + + if (!oCls._isValid()) { oCls.assign(pRegistry.ref().get(new character("HTTP/1.1"))); } - - if (!oCls._isValid()) - { - String cKey = pRequest.ref().getVersion() + "+" + pRequest.ref().getMethod(); - oCls.assign(REGISTRY.get().ref().get(new character(cKey.trim()))); - } - - if (!oCls._isValid()) - { - oCls.assign(REGISTRY.get().ref().get(pRequest.ref().getVersion())); - } - - if (!oCls._isValid()) - { - oCls.assign(REGISTRY.get().ref().get(new character("HTTP/1.1"))); - } returnNormal(oCls); })); === 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-02-21 16:25:50 +0000 +++ src/com/goldencode/p2j/oo/net/http/filter/writer/StatusCodeWriterBuilder.java 2021-03-11 08:18:20 +0000 @@ -9,6 +9,7 @@ ** 002 CA 20191024 Added method support levels and updated the class support level. ** 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. */ /* @@ -102,13 +103,7 @@ @Override protected object initialValue() { - object registry = - TypeFactory.object(BuilderRegistry.class); - - registry.assign(ObjectOps.newInstance(BuilderRegistry.class, "I", - ObjectOps.getLegacyClass(IHttpMessageWriter.class))); - initializeRegistry(registry); - return registry; + return TypeFactory.object(BuilderRegistry.class); } }; @@ -121,9 +116,19 @@ @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) public static object getRegistry() { - return function(null, "Registry", object.class, new Block((Body) () -> + return function(StatusCodeWriterBuilder.class, "Registry", object.class, new Block((Body) () -> { - returnNormal(REGISTRY.get()); + object registry = REGISTRY.get(); + + if (!registry._isValid()) + { + registry.assign(ObjectOps.newInstance(BuilderRegistry.class, "I", + ObjectOps.getLegacyClass(IHttpMessageWriter.class))); + initializeRegistry(registry); + } + + returnNormal(registry); + })); } @@ -140,7 +145,7 @@ @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) public static void initializeRegistry(final object _poRegistry) { - internalProcedure(null, "InitializeRegistry", new Block((Body) ()-> { + internalProcedure(StatusCodeWriterBuilder.class, "InitializeRegistry", new Block((Body) ()-> { _poRegistry.ref().put(new character("301"), ObjectOps.getLegacyClass(RedirectStatusFilter.class)); _poRegistry.ref().put(new character("302"), @@ -170,14 +175,14 @@ object poRequest, object poResponse) { - return function(null, "Build", object.class, new Block((Body)() -> { + 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")); - writerType.assign(REGISTRY.get().ref().get( + writerType.assign(getRegistry().ref().get( character.valueOf(poResponse.ref().getStatusCode()))); if (!writerType.isValid().booleanValue()) { === modified file 'src/com/goldencode/p2j/oo/net/http/lib/ClientLibraryBuilder.java' --- src/com/goldencode/p2j/oo/net/http/lib/ClientLibraryBuilder.java 2021-02-23 07:39:32 +0000 +++ src/com/goldencode/p2j/oo/net/http/lib/ClientLibraryBuilder.java 2021-03-11 08:18:20 +0000 @@ -15,6 +15,7 @@ ** 20210210 Lazy instantiate registry, the object seems to be deleted otherwise. ** 007 CA 20210221 Fixed 'qualified', 'extent' and 'returns' annotations at the legacy ** signature. +** ME 20210301 Added class 'referent' for static methods. */ /* @@ -170,7 +171,7 @@ private static void initializeRegistry(final object _poRegistry) { - internalProcedure(null, "InitializeRegistry", new Block((Body) () -> + internalProcedure(ClientLibraryBuilder.class, "InitializeRegistry", new Block((Body) () -> { _poRegistry.ref()._put(ObjectOps.getLegacyName(IHttpClientLibrary.class), ObjectOps.getLegacyClass(LegacySocketLibrary.class)); @@ -188,7 +189,7 @@ @LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL) public static object getRegistry() { - return function(null, "Registry", object.class, new Block((Body) () -> + return function(ClientLibraryBuilder.class, "Registry", object.class, new Block((Body) () -> { object registry = REGISTRY.get(); === modified file 'src/com/goldencode/p2j/oo/net/serverconnection/ClientSocketConnectionParameters.java' --- src/com/goldencode/p2j/oo/net/serverconnection/ClientSocketConnectionParameters.java 2021-03-23 10:12:52 +0000 +++ src/com/goldencode/p2j/oo/net/serverconnection/ClientSocketConnectionParameters.java 2021-03-23 11:42:56 +0000 @@ -14,7 +14,7 @@ ** - TypeFactory.initInput will take care of this. ** 004 CA 20210221 Fixed 'qualified', 'extent' and 'returns' annotations at the legacy ** signature. -** CA 20210322 Fixed setter for SslCiphers and SslProtocols. +** 005 ME 20210322 Implement missing functionality (OE12.2). */ /* @@ -83,8 +83,12 @@ import static com.goldencode.p2j.util.BlockManager.*; import static com.goldencode.p2j.util.InternalEntry.Type; -import static com.goldencode.p2j.report.ReportConstants.CVT_LVL_FULL; -import static com.goldencode.p2j.report.ReportConstants.RT_LVL_STUB; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +import static com.goldencode.p2j.report.ReportConstants.*; import static com.goldencode.p2j.util.ArrayAssigner.*; /** @@ -92,44 +96,44 @@ * in OpenEdge/Net/ServerConnection/ClientSocketConnectionParameters.cls). */ @LegacyResource(resource = "OpenEdge.Net.ServerConnection.ClientSocketConnectionParameters") -@LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB) +@LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) public class ClientSocketConnectionParameters extends BaseObject implements IconnectionParameters { @LegacySignature(type = Type.PROPERTY, name = "FormatMask") - @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB) + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) private object formatMask = TypeFactory.object(FormatMaskEnum.class); @LegacySignature(type = Type.PROPERTY, name = "SslCiphers") - @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB) - private character[] sslCiphers = UndoableFactory.characterExtent(); + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) + private character[] sslCiphers = TypeFactory.characterExtent(); @LegacySignature(type = Type.PROPERTY, name = "SslProtocols") - @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB) - private character[] sslProtocols = UndoableFactory.characterExtent(); + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) + private character[] sslProtocols = TypeFactory.characterExtent(); @LegacySignature(type = Type.PROPERTY, name = "URI") - @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB) - private object uri = UndoableFactory.object(Uri.class); + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) + private object uri = TypeFactory.object(Uri.class); @LegacySignature(type = Type.PROPERTY, name = "VerifyHost") - @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB) - private logical verifyHost = UndoableFactory.logical(); + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) + private logical verifyHost = TypeFactory.logical(); @LegacySignature(type = Type.PROPERTY, name = "ReuseSession") - @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB) - private logical reuseSession = UndoableFactory.logical(); + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) + private logical reuseSession = TypeFactory.logical(); @LegacySignature(type = Type.PROPERTY, name = "ServerNameIndicator") - @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB) + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) private character serverNameIndicator = TypeFactory.character(); public void __net_serverconnection_ClientSocketConnectionParameters_execute__() { externalProcedure(ClientSocketConnectionParameters.class, ClientSocketConnectionParameters.this, new Block((Init) () -> { - ObjectOps.register(uri, formatMask); + ObjectOps.register(uri); }, (Body) () -> { @@ -140,7 +144,7 @@ } @LegacySignature(returns = "OBJECT", qualified = "OpenEdge.Core.ServerConnection.FormatMaskEnum", type = Type.GETTER, name = "FormatMask") - @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB) + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) public object getFormatMask() { return function(ClientSocketConnectionParameters.class, this, "FormatMask", object.class, new Block((Body) () -> @@ -156,7 +160,7 @@ { @LegacyParameter(name = "idx", type = "INT64", mode = "INPUT") }) - @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB) + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) public character getSslCiphers(final int64 _idx) { int64 idx = TypeFactory.initInput(_idx); @@ -168,7 +172,7 @@ } @LegacySignature(returns = "CHARACTER", type = Type.GETTER, name = "SslCiphers", extent = -1) - @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB) + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) public character[] getSslCiphers() { @@ -183,7 +187,7 @@ @LegacyParameter(name = "var", type = "CHARACTER", mode = "INPUT"), @LegacyParameter(name = "idx", type = "INT64", mode = "INPUT") }) - @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB) + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) public void setSslCiphers(final character _var, final int64 _idx) { character var = TypeFactory.initInput(_var); @@ -199,7 +203,7 @@ { @LegacyParameter(name = "var", type = "CHARACTER", extent = -1, mode = "INPUT") }) - @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB) + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) public void setSslCiphers(final character[] _var) { character[] var = TypeFactory.initInput(_var); @@ -211,7 +215,7 @@ } @LegacySignature(returns = "INTEGER", type = Type.LENGTH, name = "SslCiphers") - @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB) + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) public integer lengthOfSslCiphers() { return function(ClientSocketConnectionParameters.class, this, "length-of-SslCiphers", integer.class, new Block((Body) () -> @@ -224,7 +228,7 @@ { @LegacyParameter(name = "size", type = "INT64", mode = "INPUT") }) - @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB) + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) public void resizeSslCiphers(final int64 _size) { int64 size = TypeFactory.initInput(_size); @@ -239,7 +243,7 @@ { @LegacyParameter(name = "idx", type = "INT64", mode = "INPUT") }) - @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB) + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) public character getSslProtocols(final int64 _idx) { int64 idx = TypeFactory.initInput(_idx); @@ -251,7 +255,7 @@ } @LegacySignature(returns = "CHARACTER", type = Type.GETTER, name = "SslProtocols", extent = -1) - @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB) + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) public character[] getSslProtocols() { @@ -266,7 +270,7 @@ @LegacyParameter(name = "var", type = "CHARACTER", mode = "INPUT"), @LegacyParameter(name = "idx", type = "INT64", mode = "INPUT") }) - @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB) + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) public void setSslProtocols(final character _var, final int64 _idx) { character var = TypeFactory.initInput(_var); @@ -282,7 +286,7 @@ { @LegacyParameter(name = "var", type = "CHARACTER", extent = -1, mode = "INPUT") }) - @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB) + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) public void setSslProtocols(final character[] _var) { character[] var = TypeFactory.initInput(_var); @@ -294,7 +298,7 @@ } @LegacySignature(returns = "INTEGER", type = Type.LENGTH, name = "SslProtocols") - @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB) + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) public integer lengthOfSslProtocols() { return function(ClientSocketConnectionParameters.class, this, "length-of-SslProtocols", integer.class, new Block((Body) () -> @@ -307,7 +311,7 @@ { @LegacyParameter(name = "size", type = "INT64", mode = "INPUT") }) - @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB) + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) public void resizeSslProtocols(final int64 _size) { int64 size = TypeFactory.initInput(_size); @@ -319,7 +323,7 @@ } @LegacySignature(returns = "OBJECT", qualified = "OpenEdge.Net.URI", type = Type.GETTER, name = "URI") - @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB) + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) public object getUri() { return function(ClientSocketConnectionParameters.class, this, "URI", object.class, new Block((Body) () -> @@ -332,7 +336,7 @@ { @LegacyParameter(qualified = "OpenEdge.Net.URI", name = "var", type = "OBJECT", mode = "INPUT") }) - @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB) + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) public void setUri(final object _var) { object var = TypeFactory.initInput(_var); @@ -345,7 +349,7 @@ } @LegacySignature(returns = "LOGICAL", type = Type.GETTER, name = "VerifyHost") - @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB) + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) public logical getVerifyHost() { return function(ClientSocketConnectionParameters.class, this, "VerifyHost", logical.class, new Block((Body) () -> @@ -358,7 +362,7 @@ { @LegacyParameter(name = "var", type = "LOGICAL", mode = "INPUT") }) - @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB) + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) public void setVerifyHost(final logical _var) { logical var = TypeFactory.initInput(_var); @@ -370,7 +374,7 @@ } @LegacySignature(returns = "LOGICAL", type = Type.GETTER, name = "ReuseSession") - @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB) + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) public logical getReuseSession() { return function(ClientSocketConnectionParameters.class, this, "ReuseSession", logical.class, new Block((Body) () -> @@ -383,7 +387,7 @@ { @LegacyParameter(name = "var", type = "LOGICAL", mode = "INPUT") }) - @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB) + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) public void setReuseSession(final logical _var) { logical var = TypeFactory.initInput(_var); @@ -395,7 +399,7 @@ } @LegacySignature(returns = "CHARACTER", type = Type.GETTER, name = "ServerNameIndicator") - @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB) + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) public character getServerNameIndicator() { return function(ClientSocketConnectionParameters.class, this, "ServerNameIndicator", character.class, new Block((Body) () -> @@ -408,7 +412,7 @@ { @LegacyParameter(name = "var", type = "CHARACTER", mode = "INPUT") }) - @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB) + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) public void setServerNameIndicator(final character _var) { character var = TypeFactory.initInput(_var); @@ -420,7 +424,7 @@ } @LegacySignature(type = Type.CONSTRUCTOR) - @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB) + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) public void __net_serverconnection_ClientSocketConnectionParameters_constructor__() { internalProcedure(ClientSocketConnectionParameters.class, this, "__net_serverconnection_ClientSocketConnectionParameters_constructor__", new Block((Body) () -> @@ -437,7 +441,7 @@ @LegacyParameter(name = "p2", type = "CHARACTER", mode = "INPUT"), @LegacyParameter(name = "p3", type = "INTEGER", mode = "INPUT") }) - @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB) + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) public void __net_serverconnection_ClientSocketConnectionParameters_constructor__(final character _p1, final character _p2, final integer _p3) { character p1 = TypeFactory.initInput(_p1); @@ -446,7 +450,12 @@ internalProcedure(ClientSocketConnectionParameters.class, this, "__net_serverconnection_ClientSocketConnectionParameters_constructor__", new Block((Body) () -> { - __lang_BaseObject_constructor__(); + __net_serverconnection_ClientSocketConnectionParameters_constructor__(); + Assert.notNullOrEmpty(p1, new character("URI scheme")); + Assert.notNullOrEmpty(p2, new character("Host name")); + Assert.isPositive(p3, new character("Port")); + + this.uri.assign(ObjectOps.newInstance(Uri.class, "III", p1, p2, p3)); })); } @@ -454,45 +463,94 @@ { @LegacyParameter(name = "p1", type = "OBJECT", qualified = "openedge.net.uri", mode = "INPUT") }) - @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB) + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) public void __net_serverconnection_ClientSocketConnectionParameters_constructor__(final object _p1) { object p1 = TypeFactory.initInput(_p1); internalProcedure(ClientSocketConnectionParameters.class, this, "__net_serverconnection_ClientSocketConnectionParameters_constructor__", new Block((Body) () -> { - __lang_BaseObject_constructor__(); + __net_serverconnection_ClientSocketConnectionParameters_constructor__(); + Assert.notNull(p1, new character("URI")); + this.uri.assign(p1); })); } @LegacySignature(returns = "CHARACTER", type = Type.METHOD, name = "GetConnectionString") - @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB) + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) public character getConnectionString() { - return function(ClientSocketConnectionParameters.class, this, "GetConnectionString", character.class, new Block()); + return function(ClientSocketConnectionParameters.class, this, "GetConnectionString", character.class, new Block((Body) () -> { + Assert.notNull(uri, new character("URI")); + + Assert.notNullOrEmpty(uri.ref().getHost(), new character("Host name")); + + integer port = uri.ref().getPort(); + String scheme = uri.ref().getScheme().getValue(); + + if (port.isUnknown() && !uri.ref().getScheme().isUnknown()) + { + if ("http".equalsIgnoreCase(scheme)) + port.assign(80); + else if ("https".equalsIgnoreCase(scheme)) + port.assign(443); + } + + Assert.notNullOrZero(port, new character("Port")); + + if (port.intValue() > 65535) + { + undoThrow(AppError.newInstance(String.format("Port is too large: %d", port.intValue()), 0)); + } + + StringBuilder sb = new StringBuilder(String.format("-H %s -S %d ", uri.ref().getHost().getValue(), port.intValue())); + + if ("https".equalsIgnoreCase(scheme)) + sb.append("-ssl"); + + if (verifyHost.isUnknown() || !verifyHost.booleanValue()) + sb.append(" -nohostverify"); + + if (reuseSession.isUnknown() || !reuseSession.booleanValue()) + sb.append(" -nosessionreuse"); + + // QUIRK: in 4GL any unknown entry will result in '?' to be appended + List protocols = Arrays.stream(sslProtocols).filter(p -> !p.isUnknown()).map(p -> p.getValue()).collect(Collectors.toList()); + + if (protocols.size() > 0) + sb.append(" -sslprotocols ").append(String.join(",", protocols)); + + // QUIRK: in 4GL any unknown entry will result in '?' to be appended + List ciphers = Arrays.stream(sslCiphers).filter(p -> !p.isUnknown()).map(p -> p.getValue()).collect(Collectors.toList()); + + if (ciphers.size() > 0) + sb.append(" -sslciphers ").append(String.join(",", ciphers)); + + if (!TextOps.isEmpty(serverNameIndicator)) + sb.append(" -servername ").append(serverNameIndicator.getValue()); + + returnNormal(new character(sb.toString().trim())); + + })); } @LegacySignature(returns = "CHARACTER", type = Type.METHOD, name = "GetConnectionString", parameters = { @LegacyParameter(name = "p1", type = "CHARACTER", mode = "INPUT") }) - @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB) + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) public character getConnectionString(final character _p1) { - character p1 = TypeFactory.initInput(_p1); - - return function(ClientSocketConnectionParameters.class, this, "GetConnectionString", character.class, new Block()); + return getConnectionString(); } @LegacySignature(returns = "CHARACTER", type = Type.METHOD, name = "GetConnectionString", parameters = { @LegacyParameter(name = "p1", type = "OBJECT", qualified = "openedge.core.serverconnection.formatmaskenum", mode = "INPUT") }) - @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB) + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) public character getConnectionString(final object _p1) { - object p1 = TypeFactory.initInput(_p1); - - return function(ClientSocketConnectionParameters.class, this, "GetConnectionString", character.class, new Block()); + return getConnectionString(); } } === modified file 'src/com/goldencode/p2j/oo/net/serverconnection/SocketReadEventArgs.java' --- src/com/goldencode/p2j/oo/net/serverconnection/SocketReadEventArgs.java 2021-02-21 16:25:50 +0000 +++ src/com/goldencode/p2j/oo/net/serverconnection/SocketReadEventArgs.java 2021-03-18 12:29:51 +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 20210318 Drop StringUtils, use addInterval instead, update support levels. */ /* @@ -77,44 +78,41 @@ import com.goldencode.p2j.oo.core.Assert; import com.goldencode.p2j.oo.core.Memptr; -import static com.goldencode.p2j.report.ReportConstants.CVT_LVL_FULL; -import static com.goldencode.p2j.report.ReportConstants.RT_LVL_STUB; +import static com.goldencode.p2j.report.ReportConstants.*; import static com.goldencode.p2j.util.BlockManager.*; import static com.goldencode.p2j.util.InternalEntry.Type; -import java.util.Date; - /** * Business logic (converted to Java from the 4GL source code * in OpenEdge/Net/ServerConnection/SocketReadEventArgs.cls). */ @LegacyResource(resource = "OpenEdge.Net.ServerConnection.SocketReadEventArgs") -@LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB) +@LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) public class SocketReadEventArgs extends BaseObject { @LegacySignature(type = Type.PROPERTY, name = "ReadComplete") - @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB) + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) private logical readComplete = TypeFactory.logical(); @LegacySignature(type = Type.PROPERTY, name = "ReadTimeout") - @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB) + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) private logical readTimeout = TypeFactory.logical(); @LegacySignature(type = Type.PROPERTY, name = "ReadTerminated") - @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB) + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) private logical readTerminated = TypeFactory.logical(); @LegacySignature(type = Type.PROPERTY, name = "Data") - @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB) + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) private object data = TypeFactory.object(Memptr.class); @LegacySignature(type = Type.PROPERTY, name = "TimeoutEnd") - @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB) + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) private datetimetz timeoutEnd = TypeFactory.datetimetz(); @LegacySignature(type = Type.PROPERTY, name = "BytesRead") - @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB) + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) private int64 bytesRead = TypeFactory.int64(); public void __net_serverconnection_SocketReadEventArgs_execute__() @@ -132,7 +130,7 @@ } @LegacySignature(returns = "LOGICAL", type = Type.GETTER, name = "ReadComplete") - @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB) + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) public logical getReadComplete() { return function(SocketReadEventArgs.class, this, "ReadComplete", logical.class, new Block((Body) () -> @@ -145,7 +143,7 @@ { @LegacyParameter(name = "var", type = "LOGICAL", mode = "INPUT") }) - @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB) + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) public void setReadComplete(final logical _var) { logical var = TypeFactory.initInput(_var); @@ -157,7 +155,7 @@ } @LegacySignature(returns = "LOGICAL", type = Type.GETTER, name = "ReadTimeout") - @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB) + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) public logical getReadTimeout() { return function(SocketReadEventArgs.class, this, "ReadTimeout", logical.class, new Block((Body) () -> @@ -170,7 +168,7 @@ { @LegacyParameter(name = "var", type = "LOGICAL", mode = "INPUT") }) - @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB) + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) public void setReadTimeout(final logical _var) { logical var = TypeFactory.initInput(_var); @@ -182,7 +180,7 @@ } @LegacySignature(returns = "LOGICAL", type = Type.GETTER, name = "ReadTerminated") - @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB) + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) public logical getReadTerminated() { return function(SocketReadEventArgs.class, this, "ReadTerminated", logical.class, new Block((Body) () -> @@ -195,7 +193,7 @@ { @LegacyParameter(name = "var", type = "LOGICAL", mode = "INPUT") }) - @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB) + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) public void setReadTerminated(final logical _var) { logical var = TypeFactory.initInput(_var); @@ -207,7 +205,7 @@ } @LegacySignature(returns = "OBJECT", qualified = "OpenEdge.Core.Memptr", type = Type.GETTER, name = "Data") - @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB) + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) public object getData() { return function(SocketReadEventArgs.class, this, "Data", object.class, new Block((Body) () -> @@ -220,7 +218,7 @@ { @LegacyParameter(qualified = "OpenEdge.Core.Memptr", name = "var", type = "OBJECT", mode = "INPUT") }) - @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB) + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) public void setData(final object _var) { object var = TypeFactory.initInput(_var); @@ -232,7 +230,7 @@ } @LegacySignature(returns = "DATETIMETZ", type = Type.GETTER, name = "TimeoutEnd") - @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB) + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) public datetimetz getTimeoutEnd() { return function(SocketReadEventArgs.class, this, "TimeoutEnd", datetimetz.class, new Block((Body) () -> @@ -242,7 +240,7 @@ } @LegacySignature(returns = "INT64", type = Type.GETTER, name = "BytesRead") - @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB) + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) public int64 getBytesRead() { return function(SocketReadEventArgs.class, this, "BytesRead", int64.class, new Block((Body) () -> @@ -255,7 +253,7 @@ { @LegacyParameter(name = "var", type = "INT64", mode = "INPUT") }) - @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB) + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) public void setBytesRead(final int64 _var) { int64 var = TypeFactory.initInput(_var); @@ -270,7 +268,7 @@ { @LegacyParameter(name = "pdTimeoutOffset", type = "DECIMAL", mode = "INPUT") }) - @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB) + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) public void __net_serverconnection_SocketReadEventArgs_constructor__(final decimal _pdTimeoutOffset) { decimal pdTimeoutOffset = TypeFactory.initInput(_pdTimeoutOffset); @@ -285,7 +283,7 @@ { @LegacyParameter(name = "piTimeoutOffset", type = "INTEGER", mode = "INPUT") }) - @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB) + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) public void __net_serverconnection_SocketReadEventArgs_constructor__(final integer _piTimeoutOffset) { integer piTimeoutOffset = TypeFactory.initInput(_piTimeoutOffset); @@ -296,8 +294,7 @@ Assert.isZeroOrPositive(piTimeoutOffset, new character("Timeout")); if(piTimeoutOffset.intValue() > 0) { - Date intervatToBeAdded = org.apache.commons.lang3.time.DateUtils.addSeconds(new Date(), piTimeoutOffset.intValue()); - this.timeoutEnd.assign(intervatToBeAdded.toString()); + this.timeoutEnd.assign(DateOps.addInterval(datetime.now(), piTimeoutOffset, new character("seconds"))); } })); } @@ -306,7 +303,7 @@ { @LegacyParameter(name = "poData", type = "OBJECT", qualified = "openedge.core.memptr", mode = "INPUT") }) - @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB) + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) public void __net_serverconnection_SocketReadEventArgs_constructor__(final object _poData) { object poData = TypeFactory.initInput(_poData); @@ -321,7 +318,7 @@ } @LegacySignature(type = Type.CONSTRUCTOR) - @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB) + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) public void __net_serverconnection_SocketReadEventArgs_constructor__() { internalProcedure(SocketReadEventArgs.class, this, "__net_serverconnection_SocketReadEventArgs_constructor__", new Block((Body) () -> === modified file 'src/com/goldencode/p2j/persist/serial/JsonExport.java' --- src/com/goldencode/p2j/persist/serial/JsonExport.java 2021-02-04 03:15:49 +0000 +++ src/com/goldencode/p2j/persist/serial/JsonExport.java 2021-03-19 12:11:57 +0000 @@ -18,6 +18,7 @@ ** 007 CA 20200927 Use IdentityHashMap instead of plain map when the key is a Class. ** 008 OM 20201120 Improved compatibility with P4GL. ** ME 20210130 Added serializeTempTable method into JsonArray. +** 20210319 Refactor to reuse code for JsonArray/JsonObject Read methods. */ /* @@ -77,7 +78,6 @@ import java.io.*; import java.lang.reflect.*; -import java.math.BigDecimal; import java.util.*; import java.util.logging.*; import com.fasterxml.jackson.core.*; @@ -207,89 +207,8 @@ { json.startObject(); - if (!noOuter) - { - json.fieldName(dataSet.name().toStringMessage()); - json.startObject(); - } - - int ttCount = dataSet.numBuffers().intValue(); - boolean hasChanges = false; - for (int k = 0; k < ttCount; k++) - { - BufferImpl after = (BufferImpl) dataSet.bufferHandle(k + 1).getResource(); - if (!after.isAfterBuffer()) - { - continue; // SIMPLE buffers don't have changes - } - BufferImpl before = (BufferImpl) after.beforeBuffer().getResource(); - TemporaryBuffer buffer = (TemporaryBuffer) before.buffer(); - if (buffer.count() > 0) - { - hasChanges = true; - break; - } - } - - if (beforeImage /*&& hasChanges */) - { - json.fieldName("prods:hasChanges"); - json.booleanValue(true); - } - - nextBuffer: for (int k = 0; k < ttCount; k++) - { - BufferImpl after = (BufferImpl) dataSet.bufferHandle(k + 1).getResource(); - List rels = dataSet.getRelations(after, false, true, true); - for (DataRelation rel : rels) - { - if (rel.isNested().getValue()) - { - // if [after] is a child buffer in a active nested relation, its data was (or - // will be serialized with the parent) - continue nextBuffer; - } - } - - // if there are no active relation with [after] as child buffer, serialize it now - TemporaryBuffer buffer = (TemporaryBuffer) after.buffer(); - if (buffer.count() > 0) - { - serializeTempTable( - json, null, buffer, null, noInit, true, false, true, beforeImage); - } - } - - if (beforeImage) - { - json.fieldName("prods:before"); - json.startObject(); - - for (int k = 0; k < ttCount; k++) - { - BufferImpl after = (BufferImpl) dataSet.bufferHandle(k + 1).getResource(); - if (!after.isAfterBuffer()) - { - continue; - } - BufferImpl before = (BufferImpl) after.beforeBuffer().getResource(); - TemporaryBuffer buffer = (TemporaryBuffer) before.buffer(); - if (buffer.count() <= 0) - { - continue; - } - String nameOverride = after.name().toStringMessage(); - serializeTempTable( - json, null, buffer, nameOverride, noInit, true, false, true, beforeImage); - } - - json.endObject(); - } - - if (!noOuter) - { - json.endObject(); - } + serializeDataSet(json, dataSet, noInit, noOuter, beforeImage); + json.endObject(); } catch (IOException exc) @@ -585,40 +504,184 @@ * The target of the deserialized json data. * @param buffer * A buffer of the temp-table to be serialized. + * @param omitOuter + * {@code True} to omit data for the outermost object from the output. * @param noInit * Skip the initial values. - + * @throws IOException * If exceptions occur during the serialization. */ - public boolean serializeTempTable(JsonStructureCallback json, - TemporaryBuffer buffer, - boolean noInit) - throws IOException + public boolean serializeTempTable(JsonStructureCallback json, TemporaryBuffer buffer, + boolean omitOuter, boolean noInit) throws IOException { TempTableSchema schema = new TempTableSchema(buffer); - TempTable tempTable = (TempTable) buffer.tableHandle().get(); + TempTable tempTable = buffer.getParentTable(); String serializeName = tempTable.getSerializeName().toJavaType(); - String tableName = serializeName != null && !serializeName.isEmpty() - ? serializeName - : schema.getTableName(); + String tableName = serializeName != null && !serializeName.isEmpty() ? serializeName + : schema.getTableName(); + + if (!omitOuter) + { + json.fieldName(tableName); + json.startArray(); + } + + buffer.readAllRows((dmo) -> writeRecord(json, buffer, dmo, schema, tableName, false, false, + false, false, noInit)); - buffer.readAllRows( - (dmo) -> writeRecord(json, - buffer, - dmo, - schema, - tableName, - false, - false, - false, - false, - noInit)); + if (!omitOuter) + { + json.endArray(); + } return true; } /** + * Actual implementation of a temp-table buffer serialization into a JsonArray (only current row). + * + * @param json + * The target of the deserialized json data. + * @param buffer + * A buffer record to be serialized. + * @param noInit + * Skip the initial values. + + * @throws IOException + * If exceptions occur during the serialization. + */ + public boolean serializeRecord(JsonStructureCallback json, TemporaryBuffer buffer, + boolean noInit) throws IOException + { + if (json != null && buffer != null) + { + try + { + writeRecord(json, buffer.getCurrentRecord(), new XmlTempTableSchema(buffer), false, + noInit); + + return true; + } + catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException exc) + { + throw new RuntimeException("Error exporting JSON data", exc); + } + } + + return false; + } + + /** + * Constructor specific to DATASET serialization in a JSON Object. + * + * @param json + * This is the target for the deserialized json data. + * @param dataSet + * The target object to be serialized. + * @param noInit + * {@code True} to omit data for fields whose values match their initial values. + * @param noOuter + * {@code True} to omit data for the outermost object from the output. + * @param beforeImage + * {@code True} to write before-image data and error information, else {@code false}. + * + * @throws PersistenceException + * On persistence issues. + */ + public void serializeDataSet(JsonStructureCallback json, + DataSet dataSet, + boolean noInit, + boolean noOuter, + boolean beforeImage) + throws IOException + { + if (!noOuter) + { + json.fieldName(dataSet.getSerializeName().toStringMessage()); + json.startObject(); + } + + int ttCount = dataSet.numBuffers().intValue(); + boolean hasChanges = false; + for (int k = 0; k < ttCount; k++) + { + BufferImpl after = (BufferImpl) dataSet.bufferHandle(k + 1).getResource(); + if (!after.isAfterBuffer()) + { + continue; // SIMPLE buffers don't have changes + } + BufferImpl before = (BufferImpl) after.beforeBuffer().getResource(); + TemporaryBuffer buffer = (TemporaryBuffer) before.buffer(); + if (buffer.count() > 0) + { + hasChanges = true; + break; + } + } + + if (beforeImage /*&& hasChanges */) + { + json.fieldName("prods:hasChanges"); + json.booleanValue(true); + } + + nextBuffer: for (int k = 0; k < ttCount; k++) + { + BufferImpl after = (BufferImpl) dataSet.bufferHandle(k + 1).getResource(); + List rels = dataSet.getRelations(after, false, true, true); + for (DataRelation rel : rels) + { + if (rel.isNested().getValue()) + { + // if [after] is a child buffer in a active nested relation, its data was (or + // will be serialized with the parent) + continue nextBuffer; + } + } + + // if there are no active relation with [after] as child buffer, serialize it now + TemporaryBuffer buffer = (TemporaryBuffer) after.buffer(); + if (buffer.count() > 0) + { + serializeTempTable( + json, null, buffer, null, noInit, true, false, true, beforeImage); + } + } + + if (beforeImage) + { + json.fieldName("prods:before"); + json.startObject(); + + for (int k = 0; k < ttCount; k++) + { + BufferImpl after = (BufferImpl) dataSet.bufferHandle(k + 1).getResource(); + if (!after.isAfterBuffer()) + { + continue; + } + BufferImpl before = (BufferImpl) after.beforeBuffer().getResource(); + TemporaryBuffer buffer = (TemporaryBuffer) before.buffer(); + if (buffer.count() <= 0) + { + continue; + } + String nameOverride = after.name().toStringMessage(); + serializeTempTable( + json, null, buffer, nameOverride, noInit, true, false, true, beforeImage); + } + + json.endObject(); + } + + if (!noOuter) + { + json.endObject(); + } + } + + /** * Serialize a single record to the JSON output. * * @param json @@ -627,6 +690,8 @@ * The buffer to which the DMO belongs. * @param dmo * DMO representing the record. + * @param schema + * The temp-table schema. * @param hiddenFields * Write the hidden fields ({@code __ERROR_FLAG__}, {@code __ORIGIN_ROWID__}, * {@code __ERROR_STRING__}, {@code __AFTER_ROWID__}, {@code __ROW_STATE__}). @@ -691,58 +756,7 @@ json.stringValue(Util.getRowStateAsString(rowState)); } - for (TempTableSchema.Column column : schema.columns()) - { - String name = column.getName(); - if (!hiddenFields && name.startsWith("__")) - { - // fields which start with "__" are hidden fields (specific to BEFORE tables) - continue; - } - - Integer extent = column.getExtent(); - if (extent != null && extent > 0) - { - if (noInit) - { - boolean untouched = true; - for (int i = 0; i < extent; i++) - { - if (column.isChanged((BaseDataType) column.getGetter().invoke(dmo, i))) - { - untouched = false; - break; // drop all other tests from this extent - } - } - - if (untouched) - { - continue; // skip to next column - } - } - - json.fieldName(name); - json.startArray(); - - for (int i = 0; i < extent; i++) - { - writeDatum(json, column, dmo, i); - } - - json.endArray(); - } - else - { - if (noInit) - { - if (!column.isChanged((BaseDataType) column.getGetter().invoke(dmo))) - { - continue; // skip to next column - } - } - writeDatum(json, column, dmo, null); - } - } + writeRecord(json, dmo, schema, hiddenFields, noInit); if (dsFlags) { @@ -780,6 +794,89 @@ } } + + /** + * Serialize a single buffer record to the JSON output. + * + * @param json + * Target of the deserialized json data. + * @param dmo + * DMO representing the record. + * @param schema + * The temp-table schema. + * @param hiddenFields + * Write the hidden fields ({@code __ERROR_FLAG__}, {@code __ORIGIN_ROWID__}, + * {@code __ERROR_STRING__}, {@code __AFTER_ROWID__}, {@code __ROW_STATE__}). + * @param noInit + * Flag indicating the initial values are to be omitted. + */ + private boolean writeRecord(JsonStructureCallback json, + TempRecord dmo, + TempTableSchema schema, + boolean hiddenFields, + boolean noInit) throws IOException, IllegalAccessException, IllegalArgumentException, InvocationTargetException + { + if (json != null && dmo != null) + { + for (TempTableSchema.Column column : schema.columns()) + { + String name = column.getName(); + if (!hiddenFields && name.startsWith("__")) + { + // fields which start with "__" are hidden fields (specific to BEFORE tables) + continue; + } + + Integer extent = column.getExtent(); + if (extent != null && extent > 0) + { + if (noInit) + { + boolean untouched = true; + for (int i = 0; i < extent; i++) + { + if (column.isChanged((BaseDataType) column.getGetter().invoke(dmo, i))) + { + untouched = false; + break; // drop all other tests from this extent + } + } + + if (untouched) + { + continue; // skip to next column + } + } + + json.fieldName(name); + json.startArray(); + + for (int i = 0; i < extent; i++) + { + writeDatum(json, column, dmo, i); + } + + json.endArray(); + } + else + { + if (noInit) + { + if (!column.isChanged((BaseDataType) column.getGetter().invoke(dmo))) + { + continue; // skip to next column + } + } + writeDatum(json, column, dmo, null); + } + } + + return true; + } + + return false; + } + /** * Checks supported data types. The evaluation is delegated to {@code Util.checkSupportedTypes()}. * The method returns {@code true} and prints an error messages is unsupported types are detected. @@ -808,6 +905,8 @@ * Target of the deserialized json data. * @param column * Temp-table column schema information. + * @param serializeName + * Column serialize-name value. * @param dmo * DMO containing datum associated with the specified column. * @param index @@ -879,25 +978,7 @@ // use rawDecimalValue because writeNumber will use default BigDecimal format, and // writeString will enclose in double quotes // TODO: output precision in output string not exactly matching that of 4GL - fn = (d) -> - { - // we can't use 'toStringExport' only, as this will inherit the decimal separator! - // we need to use DOT as decimal separator ALWAYS! - decimal dec = (decimal) d; - char gsep = decimal.getGroupSeparator(); - char dsep = decimal.getDecimalSeparator(); - - try - { - decimal.setNumericFormat('.', ','); - BigDecimal bd = new BigDecimal(dec.toStringExport()); - json.rawDecimalValue(bd.toString()); - } - finally - { - decimal.setNumericFormat(dsep, gsep); - } - }; + fn = (d) -> json.numberValue(((decimal) d).toBigDecimal().stripTrailingZeros()); } else if (date.class.isAssignableFrom(type)) { === modified file 'src/com/goldencode/p2j/util/BaseDataType.java' --- src/com/goldencode/p2j/util/BaseDataType.java 2021-01-31 10:14:52 +0000 +++ src/com/goldencode/p2j/util/BaseDataType.java 2021-03-22 08:31:54 +0000 @@ -54,6 +54,7 @@ ** element calls. ** 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. */ /* @@ -679,7 +680,7 @@ } else { - UnimplementedFeature.missing("BDT.assign(" + value.getClass() + ")"); + UnimplementedFeature.missing(String.format("%s.assign(%s)", getClass(), value.getClass())); } } === modified file 'src/com/goldencode/p2j/util/ErrorManager.java' --- src/com/goldencode/p2j/util/ErrorManager.java 2021-03-22 19:29:57 +0000 +++ src/com/goldencode/p2j/util/ErrorManager.java 2021-03-23 11:42:56 +0000 @@ -201,6 +201,7 @@ ** OO error up the stack for silent execution. ** CA 20210216 Allow the nestedSilent caller to process the errors logged during the invocation. ** VVT 20210322 Fixed after the review. See #4880-107. +** ME 20210322 Add an overload for recordOrThrowError to avoid adding the dot between message and number. */ /* @@ -1390,6 +1391,37 @@ boolean asMsg) throws ErrorConditionException { + recordOrThrowError(nums, texts, prefix, asMsg, true); + } + + /** + * Raise an error condition and optionally record the given error (in silent mode). + *

+ * In silent mode, the {@link #silent} worker will catch the exception and it will not be + * seen by the converted code. + * + * @param nums + * The error numbers. + * @param texts + * The text of the error messages. + * @param prefix + * true to prepend a double asterisk prefix to the + * formatted error message, false to omit this text. + * @param asMsg + * true to use the given text as the message text else + * false to use buildErrorText. + * @param addDot + * true if a dot is added to the end of the message. + * + * @throws ErrorConditionException + */ + public static void recordOrThrowError(int[] nums, + String[] texts, + boolean prefix, + boolean asMsg, + boolean addDot) + throws ErrorConditionException + { if (isIgnore()) return; @@ -1403,7 +1435,7 @@ String text = texts[i]; boolean last = i == nums.length - 1; - String errmsg = asMsg ? text : buildErrorText(num, text, prefix); + String errmsg = asMsg ? text : buildErrorText(num, text, prefix, addDot); log(errmsg, silent); @@ -3522,6 +3554,15 @@ } } + public static String getInvalidParameterError(String type, _BaseObject_ instance, String method, String errMsg) + { + return String.format("Invalid %s parameter to %s:%s( ). %s", + type, + instance.getLegacyClass().ref().getTypeName().getValue(), + method, + errMsg); + } + /** * Helper to expose error state in a way that avoids context local lookups. */ === modified file 'src/com/goldencode/p2j/util/FileStream.java' --- src/com/goldencode/p2j/util/FileStream.java 2020-12-20 17:05:05 +0000 +++ src/com/goldencode/p2j/util/FileStream.java 2021-03-22 09:05:19 +0000 @@ -39,6 +39,7 @@ ** 021 CA 20190905 Allow byte buffer instead of memory buffers for read-only files. ** 022 CA 20200116 Javadoc fixes. ** 023 IAS 20201219 Added codepage change at runtime. Javadoc fixes. +** 024 ME 20210322 Flush data/buffered when flush method is called. */ /* @@ -1276,4 +1277,19 @@ memsz = 0; mem = null; } + + @Override + public void flush() + { + try + { + flushData(); + flushBuffered(); + } + catch (IOException e) + { + } + + super.flush(); + } } === modified file 'src/com/goldencode/p2j/util/OutputStreamWrapper.java' --- src/com/goldencode/p2j/util/OutputStreamWrapper.java 2017-10-30 05:09:33 +0000 +++ src/com/goldencode/p2j/util/OutputStreamWrapper.java 2021-03-22 09:00:52 +0000 @@ -9,6 +9,7 @@ ** OutputStream wrapper for a P2J Stream. ** 002 ECF 20171029 Made write(byte[], int, int) more efficient; implemented close to ** close underlying stream. +** 003 ME 20210322 Proxy flush to the underlying stream. */ /* ** This program is free software: you can redistribute it and/or modify @@ -168,4 +169,13 @@ out = null; } } + + @Override + public void flush() throws IOException + { + if (out != null) + { + out.flush(); + } + } } === modified file 'src/com/goldencode/p2j/util/TypeFactory.java' --- src/com/goldencode/p2j/util/TypeFactory.java 2021-02-19 10:01:39 +0000 +++ src/com/goldencode/p2j/util/TypeFactory.java 2021-03-23 07:02:57 +0000 @@ -22,6 +22,7 @@ ** 008 ME 20201125 Register newly created object array with indeterminate extent as dynamic. ** CA 20210215 An INPUT parameter at the property SETTER, method, procedure r function must be registered ** as pending, so its reference can be tracked. +** ME 20210322 Instantiated object arrays are not registered as pending, also register handle arrays. */ /* @@ -673,7 +674,6 @@ if (extent == 0) { - // register indeterminate extent array as dynamic else a latter assign will fail ArrayAssigner.registerDynamicArray(res); ArrayAssigner.setObjectType(res, cls); @@ -706,9 +706,7 @@ */ public static handle[] handleExtent() { - handle[] res = new handle[0]; - - return res; + return handleExtent(0); } /** @@ -723,6 +721,11 @@ { handle[] res = new handle[extent]; + if (extent == 0) + { + ArrayAssigner.registerDynamicArray(res); + } + BaseDataType.initializeDefaultExtent(res); return res; # Begin bundle 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