Bug #11191
ChUI: COMBO-BOX raises error 4058 on valid SCREEN-VALUE assignment
100%
History
#2 Updated by Vladimir Tsichevski 7 months ago
Run this example in CHARACTER mode:
DEFINE VARIABLE id AS CHARACTER FORMAT "x(3)". DEFINE FRAME f id VIEW-AS COMBO-BOX LIST-ITEM-PAIRS "123456789qwe", "123456789abc" DROP-DOWN-LIST. ENABLE ALL WITH FRAME f. id:SCREEN-VALUE = "123456789abc". WAIT-FOR WINDOW-CLOSE OF CURRENT-WINDOW.
- In OE: code runs without error.
- In FWD: error 4058 raised ("**Attribute SCREEN-VALUE for the %s has an invalid value of 123.").
This problem was initially reported as #11126-11.
#3 Updated by Razvan-Nicolae Chichirau 6 months ago
- Status changed from New to WIP
SCREEN-VALUE attribute. I'll use the following messages for reference:
- Any message that results from the fact that a value could not be converted to the combo-box format.
**Attribute SCREEN-VALUE for the <widget_type> <widget_name> has an invalid value of <unformatted_value>. (4058)
For GUI combo boxes of type DROP-DOWN or SIMPLE, the value assigned to SCREEN-VALUE is not validated (i.e., whether it can be formatted or whether the formatted value exists among the items) and is simply accepted, so no errors are thrown. The following is true for DROP-DOWN-LIST on GUI and all types (SIMPLE, DROP-DOWN, DROP-DOWN-LIST) on ChUI:
- Value can not be formatted: 1 (ERROR) + 2 (WARNING)
- Value can be formatted, but does not exist in the items: 2 (WARNING)
- Value is empty string
- no items: no error
- has items: no error
- Value is unknown value:
- no items: no error
- has items: no error
#4 Updated by Razvan-Nicolae Chichirau 6 months ago
- Assignee set to Razvan-Nicolae Chichirau
#5 Updated by Razvan-Nicolae Chichirau 5 months ago
Did some extensive testing for the following control set entities: radio-set, combo-box and selection-list. Consider the error messages from #11191-3.
COMBO-BOX¶
The following data-types were tested:character, logical, integer, decimal, date, datetime, datetime-tz, recid
- GUI
- The
SIMPLEandDROP-DOWNcombos does not throw any errors and accept the input as is. - For
DROP-DOWN-LIST:- Widget is not realized
- Can not format the screen-value: 1 + 2
- Can format, but does not exist among the current items: no errors, silent exit
- Widget is realized
- Can not format the screen-value: 1 + 2
- Can format, but does not exist among the current items: 2
- Widget is not realized
- The
- CHUI
- The following is true for all combo-box types:
- Widget is not realized
- Can not format the screen-value: 1 + 2
- Can format, but does not exist among the current items: no errors, silent exit
- Widget is realized
- Can not format the screen-value: 1 + 2
- Can format, but does not exist among the current items: 2
- Widget is not realized
- The following is true for all combo-box types:
RADIO-SET¶
The following data-types were tested:character, logical, integer, decimal, date, datetime, datetime-tz, recid
- GUI + CHUI
- Widget is not realized or it is realized: 2
SELECTION-LIST¶
Onlycharacter data-type was tested as it was the only one permitted for this widget.
- GUI + CHUI:
- Widget is not realized or it is realized: 2
The browse column widget has another behavior compared with the widgets listed above. This can be treated in another task and the functionality of combo-box, radio-set and selection-list can be grouped and treated in this issue.
#6 Updated by Razvan-Nicolae Chichirau 4 months ago
- % Done changed from 0 to 100
- Status changed from WIP to Review
Hynek: Please review 11191a/rev. 16549.
#7 Updated by Hynek Cihlar 3 months ago
- Status changed from Review to WIP
- % Done changed from 100 to 90
Code review 11191a revisions 16548..16549
- [CRITICAL] style
RadioSetWidget: Copyright year in the file header is still2005-2025, but the file is modified in 2026. Update to2005-2026, Golden Code Development Corporation. - [CRITICAL] functional
ControlSetEntity.invalidScreenValueWarning: Method dereferencesunparsedValueviaunparsedValue.isUnknown()with no null check. Concrete trigger: the 3-argGenericWidget.setScreenValue(ScreenBuffer, Object, boolean)forwardsunparsedValue=nullto the 4-arg overload; this is exercised on the standard frame realization / DISPLAY path viaGenericFrame.copyToScreenBuffer(around line 11301) on a RADIO-SET, SELECTION-LIST or already-realized COMBO-BOX whose value failsisValidScreenValue. The chain reachesvalidateScreenValue(value, null)which callsinvalidScreenValueWarning(null, ...); for RADIO-SET / SELECTION-LIST therealizeCheck=falseguard does not bail out, and the method NPEs onunparsedValue.isUnknown(). The old inline implementation referenced only the parsedvalue(never null), so this is a regression. Add a null guard or fall back to the parsedvaluewhenunparsedValueis null. - [CRITICAL] functional
RadioSetWidget.validateScreenValue: CallsinvalidScreenValueWarning(unparsedValue, false)when validation fails. Same trigger as above —unparsedValuecan benullon the 3-argsetScreenValuepath (used during frame realization). WithrealizeCheck=falseand a non-nullframe,invalidScreenValueWarning(null, false)immediately NPEs onunparsedValue.isUnknown(). - [MAJOR] functional
ControlSetEntity.invalidScreenValueWarning: ReplacesErrorManager.recordOrShowWarning(...)withErrorManager.displayWarning(...). The two are not equivalent: (a)recordOrShowWarningshort-circuits onisSuppressWarnings()and on per-codesuppressWarningsSet.contains(num), whiledisplayWarningnever consults the per-code suppression set and adds the condition to_MSGeven under SESSION:SUPPRESS-WARNINGS; (b)recordOrShowWarningdelegates torecordOrShowErrorwhich throwsDeferredLegacyErrorExceptionwhenhasLegacyError()/mustManageLegacyError()is true, allowing an enclosing 4GLCATCH Progress.Lang.SysErrorto capture the condition —displayWarningnever throws. Reachable from straightforward 4GL:ASSIGN cb:SCREEN-VALUE = "<invalid>"inside SESSION:SUPPRESS-WARNINGS, with SUPPRESS-WARNINGS-LIST naming 4056/4058, or inside a CATCH block. Verify against OE behavior; ifdisplayWarningis the intended API, the previously suppressed/catchable cases need explicit handling. - [MINOR] style
RadioSetWidget.setScreenValueInt: Continuation lines of the multi-lineifcondition are indented one column short of the opening condition character, breaking project convention for multi-line condition alignment. - [MINOR] style
GenericFrame.setScreenValue: Newly added Javadoc\@param widgetdescription reads "The widget for which the value is to be retrieved". This is a setter; description must say "set". - [MINOR] style
ControlSetEntity.invalidScreenValueWarning: Both\@paramtags use 3 spaces after the tag keyword. All other Javadoc\@paramtags in this file and in the same diff use 4 spaces — adjust for consistency. - [MINOR] style
ControlSetEntity.shouldParseScreenValue/ComboBoxWidget.shouldParseScreenValue: Both newly added Javadoc blocks use\@returnwith 2 spaces. Existing\@returntags in these files use 3 spaces — adjust for consistency.
Instead of getSubType().getValue().equals("DROP-DOWN-LIST") compare the enum value returned by ComboBoxConfig.getMode.
In setScreenValueInt catching ErrorConditionException is used for the happy path. The catch block here is part of the expected flow, not an exceptional condition. Let's turn this into a predicate (e.g. tryParseScreenValue returning a boolean) and reserve the exception for genuinely unexpected failures (or the error condition in this case).
Is the 'UNKNOWN' in invalidScreenValueWarning also displayed in OpenEdge? OpenEdge AFAIK typically represents unknown values as '?' in the messages.
#8 Updated by Razvan-Nicolae Chichirau 3 months ago
- % Done changed from 90 to 100
- Status changed from WIP to Review
Hynek Cihlar wrote:
Code review 11191a revisions 16548..16549
- [CRITICAL] style
RadioSetWidget: Copyright year in the file header is still2005-2025, but the file is modified in 2026. Update to2005-2026, Golden Code Development Corporation.- [CRITICAL] functional
ControlSetEntity.invalidScreenValueWarning: Method dereferencesunparsedValueviaunparsedValue.isUnknown()with no null check. Concrete trigger: the 3-argGenericWidget.setScreenValue(ScreenBuffer, Object, boolean)forwardsunparsedValue=nullto the 4-arg overload; this is exercised on the standard frame realization / DISPLAY path viaGenericFrame.copyToScreenBuffer(around line 11301) on a RADIO-SET, SELECTION-LIST or already-realized COMBO-BOX whose value failsisValidScreenValue. The chain reachesvalidateScreenValue(value, null)which callsinvalidScreenValueWarning(null, ...); for RADIO-SET / SELECTION-LIST therealizeCheck=falseguard does not bail out, and the method NPEs onunparsedValue.isUnknown(). The old inline implementation referenced only the parsedvalue(never null), so this is a regression. Add a null guard or fall back to the parsedvaluewhenunparsedValueis null.- [CRITICAL] functional
RadioSetWidget.validateScreenValue: CallsinvalidScreenValueWarning(unparsedValue, false)when validation fails. Same trigger as above —unparsedValuecan benullon the 3-argsetScreenValuepath (used during frame realization). WithrealizeCheck=falseand a non-nullframe,invalidScreenValueWarning(null, false)immediately NPEs onunparsedValue.isUnknown().- [MINOR] style
RadioSetWidget.setScreenValueInt: Continuation lines of the multi-lineifcondition are indented one column short of the opening condition character, breaking project convention for multi-line condition alignment.- [MINOR] style
GenericFrame.setScreenValue: Newly added Javadoc\@param widgetdescription reads "The widget for which the value is to be retrieved". This is a setter; description must say "set".- [MINOR] style
ControlSetEntity.invalidScreenValueWarning: Both\@paramtags use 3 spaces after the tag keyword. All other Javadoc\@paramtags in this file and in the same diff use 4 spaces — adjust for consistency.- [MINOR] style
ControlSetEntity.shouldParseScreenValue/ComboBoxWidget.shouldParseScreenValue: Both newly added Javadoc blocks use\@returnwith 2 spaces. Existing\@returntags in these files use 3 spaces — adjust for consistency.Instead of
getSubType().getValue().equals("DROP-DOWN-LIST")compare the enum value returned byComboBoxConfig.getMode.In
setScreenValueIntcatchingErrorConditionExceptionis used for the happy path. The catch block here is part of the expected flow, not an exceptional condition. Let's turn this into a predicate (e.g.tryParseScreenValuereturning a boolean) and reserve the exception for genuinely unexpected failures (or the error condition in this case).
Fixed.
Raising a warning for the invalid screen-value in 4GL will:
- [MAJOR] functional
ControlSetEntity.invalidScreenValueWarning: ReplacesErrorManager.recordOrShowWarning(...)withErrorManager.displayWarning(...). The two are not equivalent: (a)recordOrShowWarningshort-circuits onisSuppressWarnings()and on per-codesuppressWarningsSet.contains(num), whiledisplayWarningnever consults the per-code suppression set and adds the condition to_MSGeven under SESSION:SUPPRESS-WARNINGS; (b)recordOrShowWarningdelegates torecordOrShowErrorwhich throwsDeferredLegacyErrorExceptionwhenhasLegacyError()/mustManageLegacyError()is true, allowing an enclosing 4GLCATCH Progress.Lang.SysErrorto capture the condition —displayWarningnever throws. Reachable from straightforward 4GL:ASSIGN cb:SCREEN-VALUE = "<invalid>"inside SESSION:SUPPRESS-WARNINGS, with SUPPRESS-WARNINGS-LIST naming 4056/4058, or inside a CATCH block. Verify against OE behavior; ifdisplayWarningis the intended API, the previously suppressed/catchable cases need explicit handling.
- Make the warning be unconditionally added to _MSG, whether
SESSION:SUPPRESS-WARNINGSor set or not - Not display the warning in case of
SESSION:SUPPRESS-WARNINGS - Not throw an actual 4GL legacy error which can be caught by a CATCH block.
As such, displayWarning is the correct API to use here.
Is the 'UNKNOWN' in
invalidScreenValueWarningalso displayed in OpenEdge? OpenEdge AFAIK typically represents unknown values as '?' in the messages.
Check this testcase:
def var rs as char view-as radio-set radio-buttons "a", "b". def frame f rs. rs:screen-value = ?.
Please review 11191a/rev. 16550.
#9 Updated by Hynek Cihlar 3 months ago
- Status changed from Review to Internal Test
Code review 11191a revisions 16548..16550
The changes look good. Please go ahead with regression testing. Also run ChUI regression tests.
#10 Updated by Razvan-Nicolae Chichirau about 2 months ago
- Status changed from Internal Test to Review
Small regression detected on the testcases project:
define variable rs as logical initial ? format 'one/two' view-as radio-set radio-buttons "first", true, "second", true. def frame f rs. rs:screen-value = "". display rs with frame f. wait-for window-close of current-window.
Trunk and 4GL do not raise a warning, meanwhile 11191a does, GUI + ChUI.
Hynek: Please review 11191a/rev. 16624.
#11 Updated by Hynek Cihlar about 1 month ago
- Status changed from Review to WIP
- % Done changed from 100 to 90
Code review 11191a revisions 16621..16624
Intsead of this instanceof BrowseColumnWidget override recoverFromParsingError.
Please check the following functional points.
- [MAJOR] functional
RadioSetWidget.setScreenValueInt/isValidScreenValue: The rev-16624savedValuerecovery that suppresses the false-positive 4058 warning is captured only inside the GUI branch (!LogicalTerminal.isChui()), so the ChUI arm of the reproducer regresses. In ChUI,setScreenValueIntleaves""untouched andsavedValuestaysnull;parseScreenValue(logical, "", "one/two")yields an unknownlogical(empty value, format not delimited by/), whichControlSetEntity.setScreenValuere-derives as an unknowncharacter;isValidScreenValuethen has nosavedValueto restore the original"", returnsfalseon!value.isUnknown(), andvalidateScreenValuefires a spurious 4058. GUI works only because it capturessavedValue = ""and swaps it back. Trigger: the redmine section-3 reproducer (rs as logical initial ? format 'one/two' view-as radio-set ...; rs:screen-value = "") run in CHARACTER mode — redmine states the false positive appeared in both GUI and ChUI on 11191a. CapturesavedValue(or otherwise preserve the original value for validation) on the ChUI path as well.
- [MINOR] functional
ControlSetEntity.invalidScreenValueWarning: The switch fromErrorManager.recordOrShowWarningtoErrorManager.displayWarning(id, msg, true)drops honoring ofSESSION:SUPPRESS-WARNINGS-LIST.recordOrShowWarningsuppresses when either the globalSUPPRESS-WARNINGSboolean or the per-number setsuppressWarningsSetmatches (ErrorManager~line 1452-1454);displayWarningchecks only the global boolean (~line 3441) and never consultssuppressWarningsSet. Trigger: a program setsSESSION:SUPPRESS-WARNINGS-LIST = "4058"(or "4056") then assigns an invalid SCREEN-VALUE to a realized control-set widget — previously suppressed entirely, now displayed and added to_MSG. Redmine section 2 mandates only the globalSUPPRESS-WARNINGSfor this case and requires the warning to unconditionally reach_MSG, so this may be acceptable; verify the per-number-list behavior against OE.
- [MINOR] style
ComboBoxWidget(header history entry #057): Typo in the new history description — "Overriden 'shouldParseScreenValue()'." should be "Overridden".
#12 Updated by Razvan-Nicolae Chichirau about 1 month ago
- % Done changed from 90 to 100
- Status changed from WIP to Review
Hynek Cihlar wrote:
Intsead of
this instanceof BrowseColumnWidgetoverriderecoverFromParsingError.
- [MAJOR] functional
RadioSetWidget.setScreenValueInt/isValidScreenValue: The rev-16624savedValuerecovery that suppresses the false-positive 4058 warning is captured only inside the GUI branch (!LogicalTerminal.isChui()), so the ChUI arm of the reproducer regresses. In ChUI,setScreenValueIntleaves""untouched andsavedValuestaysnull;parseScreenValue(logical, "", "one/two")yields an unknownlogical(empty value, format not delimited by/), whichControlSetEntity.setScreenValuere-derives as an unknowncharacter;isValidScreenValuethen has nosavedValueto restore the original"", returnsfalseon!value.isUnknown(), andvalidateScreenValuefires a spurious 4058. GUI works only because it capturessavedValue = ""and swaps it back. Trigger: the redmine section-3 reproducer (rs as logical initial ? format 'one/two' view-as radio-set ...; rs:screen-value = "") run in CHARACTER mode — redmine states the false positive appeared in both GUI and ChUI on 11191a. CapturesavedValue(or otherwise preserve the original value for validation) on the ChUI path as well.
- [MINOR] style
ComboBoxWidget(header history entry #057): Typo in the new history description — "Overriden 'shouldParseScreenValue()'." should be "Overridden".
Solved.
- [MINOR] functional
ControlSetEntity.invalidScreenValueWarning: The switch fromErrorManager.recordOrShowWarningtoErrorManager.displayWarning(id, msg, true)drops honoring ofSESSION:SUPPRESS-WARNINGS-LIST.recordOrShowWarningsuppresses when either the globalSUPPRESS-WARNINGSboolean or the per-number setsuppressWarningsSetmatches (ErrorManager~line 1452-1454);displayWarningchecks only the global boolean (~line 3441) and never consultssuppressWarningsSet. Trigger: a program setsSESSION:SUPPRESS-WARNINGS-LIST = "4058"(or "4056") then assigns an invalid SCREEN-VALUE to a realized control-set widget — previously suppressed entirely, now displayed and added to_MSG. Redmine section 2 mandates only the globalSUPPRESS-WARNINGSfor this case and requires the warning to unconditionally reach_MSG, so this may be acceptable; verify the per-number-list behavior against OE.
4GL suppresses the warning message if either SESSION:SUPPRESS-WARNINGS is used or the message number is in the SESSION:SUPPRESS-WARNINGS-LIST value. Additionally, it adds it to _MSG() only if silent error is disabled and the process is not running in headless mode. As such, the correct method is recordOrShowWarning(), but with a slight modification. If the warning is suppressed and silent mode is disabled + not running in headless mode, append the message number to _MSG().
Note that I've verified all usages of this method, and the behavior is the same in every case. Please check rev. 16625.
#13 Updated by Hynek Cihlar 8 days ago
- Status changed from Review to WIP
- % Done changed from 100 to 90
Code review 11191a revisions 16621..16625
The code changes look good (just a couple of minor styling issues, please see below), but there are some functional issues. The CRITICAL one is confirmed. I didn't check the MAJOR ones, please do that.
- [CRITICAL] functional
GenericFrame.setScreenValue: On the newshouldParseScreenValue() falsebranch,val = valuealiases the caller's livecharacterinstead of the fresh object the old path always returned (parseValueline 3944 doesresult = new character(value)).val,unparsedValueand the application's own variable then become one object:ControlSetEntity.setScreenValueline 3011 doesval = (character) valuewith no copy, andComboBoxWidget.validateScreenValuemutates it in place —value.assign(itemValue)(line 2221),value.assign(TextOps.rightTrim(value))(2231),value.assign("")(2237). No earlier layer copies:GenericWidget.setScreenValue(character)copies nothing,setScreenValueNoErrorHandlingduplicates only on theframe nullpath, and the conversion rule (rules/convert/methods_attributes.rules:5011-5017) emits a baresetScreenValue(<expr>)passing the live BDT (seeWeblist.java:597,Webradio.java:506). Trigger (GUI, mode SIMPLE or DROP-DOWN):def var c as char init " ". def var cb as char view-as combo-box simple list-items "a","b". def frame f cb. display cb with frame f. cb:screen-value = c. message length(c).— FWD now prints 0 because line 2237 rewrote the application'sc; it printed 3 before. Variant:c = "a "is rewritten to "a" by therightTrim/assign(itemValue)calls. Second effect:ScreenBuffer.putWidgetValue(line 474,swr.value = value) andputScreenValue(line 622) store the reference verbatim, so a laterc = "zzz"(an in-placec.assign) silently changes whatcb:SCREEN-VALUEreturns, diverging from what was already painted on the client. Fix:val = (character) value.duplicate();.
- [MAJOR] functional
ControlSetEntity.validateScreenValue: The issue's own CHARACTER-mode reproducer still raises 4058 — only the printed value changes ("123" becomes "123456789abc"). The ChUI unparsed-value clause(LogicalTerminal.isChui() && unparsedValue != null && !isValidScreenValue(unparsedValue))is a pure context line in the diff. ForDEF VAR id AS CHAR FORMAT "x(3)"+LIST-ITEM-PAIRS "123456789qwe","123456789abc" DROP-DOWN-LIST:parseScreenValue(character, "123456789abc", "x(3)")does not throw (StringFormat.CharBuf.parseScreenValueline 1764 appends the first 3 chars;xaccepts any char socheckFormatpasses), givingval = "123"withunparsedValuethe untouched 12-char value. Item values are truncated by the same format —ComboBoxWidget.setItemsline 1695 doesitem.setValue(TextOps.rightTrim(new character(item.getValue().toString(fmt))))withfmt = resolveFormat()= "x(3)" — so the item value is also "123".ControlSetEntity.isValidScreenValueline 3099 uses exactCompareOps._isEqualonly (no "begins of" leniency; theTextOps._beginsmatch ingetItemValueruns only after validation passes and tests the wrong direction), soisValidScreenValue("123456789abc")is false, the ChUI disjunct fires, andinvalidScreenValueWarning(..., realizeCheck=true)emits 4058 on the widget realized byENABLE ALL. The outcome is robust to the truncation question: were the items not truncated, the first disjunct would fire instead. Validating the raw unparsed value against the item list is the residual defect — a value that formats to an existing item must be accepted silently.
- [MAJOR] functional
GenericFrame.setScreenValue: The newrecoverFromParsingErrorhook is wired only tocatch (DisplayFormatCheckException e)(lines 3786-3804), butparseValue(3929-4014) can raise that exception only in thetype.equals(character.class)branch viapres.checkFormat()(StringFormat.java:1123). For the numeric and date datatypes the "cannot format" case raisesErrorConditionExceptioninstead, which bypasses the hook entirely:integer/int64/decimalviaNumberType.parseDecimaltoerrorInvalidChar(NumberType.java:3495) torecordOrThrowError(76, ...), anddate/datetime/datetime-tzvianew date(character)torecordOrStoreError85(date.java:5137) torecordOrThrowError(85, ...).ErrorConditionExceptionis aRuntimeExceptioncaught nowhere betweenparseScreenValueandGenericWidget.setScreenValueInt(lines 6731-6744), which emits 4078;invalidScreenValueWarningis never reached andControlSetEntity.validateScreenValueis never entered. Trigger (ChUI, realized):def var i as int view-as combo-box list-items "1","2","3". def frame f i. enable all with frame f. i:screen-value = "abc".— FWD emits 76 + 4078 where the matrix requires the format error plus 4058; same for a date combo (85 + 4078). So the central fix works forcharacterandlogicalonly (logicalis fine:new logical(character, format)records 87 and returns unknown without throwing, so the flow reachesvalidateScreenValueand 4058 fires). Note theFIXME: we need to find out which error number to use herecomment survives untouched exactly where these types land. Related gap:recidis not handled byparseValueat all (recid extends int64but the branch uses exacttype.equals(int64.class)), so a recid control set skips parsing and format checking entirely — no error of any kind. Bothinteger/decimal/date/datetime/datetime-tzandrecidare listed in the issue as "covered by testing".
- [MAJOR] functional
ControlSetEntity.validateScreenValue: Replacing the oldframe != null && getAttr(ControlSetConfig::getWasRealized, config, true)guard withinvalidScreenValueWarning(..., this instanceof ComboBoxWidget)drops the realization gate for SELECTION-LIST on the UI-statement path, not just the SCREEN-VALUE attribute path.GenericFrame.display()(line 6011) callscopyToScreenBufferbeforeviewWorker(6013), andcopyToScreenBufferline 11311 callswidget.setScreenValue(frameBuf, value, true)withinUIStmt = true;ControlSetEntity.setScreenValueline 3002 (if (inUIStmt || !internalScreenValueUsage)) then runsvalidateScreenValuewhilewasRealizedis still false. Trigger:DEFINE VARIABLE sl AS CHARACTER INITIAL "zzz" VIEW-AS SELECTION-LIST LIST-ITEMS "a","b". DEFINE FRAME f sl. DISPLAY sl WITH FRAME f.now emits 4058 on the first DISPLAY where the old realization gate kept it silent; the same reachesupdateWorker(9846, 9928 — UPDATE/SET),displayWorker(12012),displayAndDownWorker(13594) andstartEditingMode(8066). Nothing blocks the path —SelectionListWidget.hasFormat()returning false affects only format handling, andSelectionListWidget.validateScreenValue(960) delegates unconditionally to super. The matrix rows describe SCREEN-VALUE assignment; a plain DISPLAY would emit a message naming an attribute the program never touched. Decisive in-diff evidence that the two halves are inconsistent: the author addedif (!internalScreenValueUsage)toRadioSetWidget.validateScreenValue(1435-1439) with the comment "The RADIO-SET widget throws invalid screen-value warnings only for external calls" — RADIO-SET and SELECTION-LIST share a matrix row, yet only one got the gate, so identical 4GL now behaves differently. Suggested fix: gate theControlSetEntitywarning on!internalScreenValueUsagetoo. Aggravating factor (mechanism pre-existing):recordOrShowWarningpassesisError=falseandrecordOrShowErrorre-derivesisError = hasLegacyError() || mustManageLegacyError(), so inside a CATCH or BLOCK-LEVEL UNDO,THROW block this becomes aDeferredLegacyErrorException— an ERROR condition raised by a plain DISPLAY.
- [MAJOR] functional
RadioSetWidget.setScreenValueInt: Removing the!LogicalTerminal.isChui()guard makes the empty-string-to-unknown coercion run on ChUI, changing the stored and rendered value, not just validation.savedValueis consulted only insideisValidScreenValue; the item-matching loop invalidateScreenValuecompares the parameter (the coerced unknown), and bothframeBuf.putWidgetValueandrefreshFrameWidget/putScreenValuereceive the unknown.CompareOps._isEqual(unknown, "")is false, and client-sideRadioSet.setValueonly everselectButton@s on a match, so an unknown is a no-op on the client — no button selected, @config.currentleft as-is. Trigger (ChUI, empty-valued item present, widget realized before the assignment sogetInitialValue()does not mask it):def var c as char view-as radio-set radio-buttons "Nothing","","A","a". def frame f c. display c with frame f. c:screen-value = "".— previously""reached the loop, matched the empty-valued item and was stored, selecting that button; now nothing matches. The guard being removed was added deliberately by the same author at rev. 15471 (#8836) together with the "for GUI clients" Javadoc, so ChUI non-coercion was established behavior, and nothing in the issue claims OE ChUI was re-tested.ComboBoxWidget.setScreenValueIntstill keeps both!isChuiand!pairsguards on the same coercion, andRadioSetConfigsetspairs = trueunconditionally — by that analogy the quirk should apply less here, not more. The coercion is also not required by the rev-16624 fix: capturingsavedValueunconditionally while keeping theisChuiguard on the coercion is sufficient, becauseparseScreenValue(logical, "", 'one/two')already yields an unknown on ChUI, soisValidScreenValuestill gets itssavedValueand no 4058 is emitted. Separately, hoistingreturn trueout of the item loop meansrs:screen-value = ""with no empty-valued item is now accepted (storing unknown) where the old fall-throughreturn falsemadeGenericFrameskipputScreenValue/refreshFrameWidgetand leave the selection untouched.
- [MAJOR] functional
GenericFrame.setScreenValue: The same non-parsing branch loses the unknown-value typing, regressing an intentional OE behavior.ComboBoxWidget.resolveUnknownScreenValue(lines 2017-2020) branches on the buffer value's class —bdt.getClass().isAssignableFrom(date.class) ? new character("") : new character()— andGenericFrameline 3662 documents the rule ("date/datetime/datetime-tz all return empty string for unknown", implemented per history entry048 FER 20250312). Before the changeparseValue(3930-3941) short-circuited unknown input toBaseDataTypeFactory.instantiate(type), i.e. an unknowndate, so the test passed and the read-back was""; now the buffer holds an unknowncharacter, the test fails, and the read-back is?. Trigger (GUI):DEFINE VARIABLE d AS DATE VIEW-AS COMBO-BOX SUBTYPE SIMPLE LIST-ITEMS "01/01/2020".thend:SCREEN-VALUE = ?then readd:SCREEN-VALUE. Scope: confined to plainDATE— fordatetime/datetimetzthe pre-existingisAssignableFromtest is written backwards and was already false, and for the numeric types both before and after yield unknown.
- [MAJOR] functional
GenericFrame.convertScreenValue: Letting an unparseable value into the screen buffer (correct per the matrix, "accepted as-is") makes a downstream read silently swallow a raised 4GL error and substitute the type default.ComboBoxWidget.inputValuetoGenericWidget.inputValuetoframe.getter(id, integer.class, true, false)toconvertScreenValue: the(character, String)constructor lookup fails into the blanketcatch (Exception exc)at line 8841, theninteger.getConstructor(character.class)tonewInstancetoint64.setValue(String)toNumberType.parseDouble("abc")toerrorInvalidChartorecordOrThrowError(76, "Invalid character in numeric input a")— and thatErrorConditionException(wrapped inInvocationTargetException) is discarded by the second blanketcatch (Exception exc)at line 8861, leavingvalue nullsodefaults truesubstitutesBaseDataType.generateDefault(integer.class). Trigger (GUI):def var i as int view-as combo-box simple list-items "1","2". def frame f i. i:screen-value = "abc". message i:input-value.prints 0 and error 76 never surfaces — not even inERROR-STATUS. The blanket catch is pre-existing, but this diff is what makes the path reachable: before the change an unparseable value could never enter the buffer for a GUI SIMPLE/DROP-DOWN combo. Caveat:inputValuedeliberately maps an unknown result togenerateDefault(history entries038 SAT 20231025/26), so the returned 0 may be OE-correct; the destroyed error condition is a defect regardless. Worth confirming on OE whether 76 (or 5321) surfaces for INPUT-VALUE of an unparseable integer combo screen value. - [MINOR] style
ControlSetEntity.invalidScreenValueWarning: The new protected instance method (line 3291) is placed after the private instance methodscontrolSetItem/sortItemsand the private static methodbubbleSort, violating the required member ordering (protected instance methods must precede private static and instance methods). Relocate it up among the other protected instance methods, e.g. nearrecoverFromParsingError/isValidScreenValue.
- [MINOR] style
RadioSetWidget: The new field comment forsavedValue(line 145) uses a plain block comment/* ... */instead of the javadoc/** ... */style used for field documentation elsewhere in the package (e.g. every field inGenericWidget). Convert it to/** ... */.
#14 Updated by Razvan-Nicolae Chichirau 5 days ago
- % Done changed from 90 to 100
- Status changed from WIP to Review
Hynek Cihlar wrote:
- [CRITICAL] functional
GenericFrame.setScreenValue: On the newshouldParseScreenValue() falsebranch,val = valuealiases the caller's livecharacterinstead of the fresh object the old path always returned (parseValueline 3944 doesresult = new character(value)).val,unparsedValueand the application's own variable then become one object:ControlSetEntity.setScreenValueline 3011 doesval = (character) valuewith no copy, andComboBoxWidget.validateScreenValuemutates it in place —value.assign(itemValue)(line 2221),value.assign(TextOps.rightTrim(value))(2231),value.assign("")(2237). No earlier layer copies:GenericWidget.setScreenValue(character)copies nothing,setScreenValueNoErrorHandlingduplicates only on theframe nullpath, and the conversion rule (rules/convert/methods_attributes.rules:5011-5017) emits a baresetScreenValue(<expr>)passing the live BDT (seeWeblist.java:597,Webradio.java:506). Trigger (GUI, mode SIMPLE or DROP-DOWN):def var c as char init " ". def var cb as char view-as combo-box simple list-items "a","b". def frame f cb. display cb with frame f. cb:screen-value = c. message length(c).— FWD now prints 0 because line 2237 rewrote the application'sc; it printed 3 before. Variant:c = "a "is rewritten to "a" by therightTrim/assign(itemValue)calls. Second effect:ScreenBuffer.putWidgetValue(line 474,swr.value = value) andputScreenValue(line 622) store the reference verbatim, so a laterc = "zzz"(an in-placec.assign) silently changes whatcb:SCREEN-VALUEreturns, diverging from what was already painted on the client. Fix:val = (character) value.duplicate();.
Solved.
- [MAJOR] functional
ControlSetEntity.validateScreenValue: The issue's own CHARACTER-mode reproducer still raises 4058 — only the printed value changes ("123" becomes "123456789abc"). The ChUI unparsed-value clause(LogicalTerminal.isChui() && unparsedValue != null && !isValidScreenValue(unparsedValue))is a pure context line in the diff. ForDEF VAR id AS CHAR FORMAT "x(3)"+LIST-ITEM-PAIRS "123456789qwe","123456789abc" DROP-DOWN-LIST:parseScreenValue(character, "123456789abc", "x(3)")does not throw (StringFormat.CharBuf.parseScreenValueline 1764 appends the first 3 chars;xaccepts any char socheckFormatpasses), givingval = "123"withunparsedValuethe untouched 12-char value. Item values are truncated by the same format —ComboBoxWidget.setItemsline 1695 doesitem.setValue(TextOps.rightTrim(new character(item.getValue().toString(fmt))))withfmt = resolveFormat()= "x(3)" — so the item value is also "123".ControlSetEntity.isValidScreenValueline 3099 uses exactCompareOps._isEqualonly (no "begins of" leniency; theTextOps._beginsmatch ingetItemValueruns only after validation passes and tests the wrong direction), soisValidScreenValue("123456789abc")is false, the ChUI disjunct fires, andinvalidScreenValueWarning(..., realizeCheck=true)emits 4058 on the widget realized byENABLE ALL. The outcome is robust to the truncation question: were the items not truncated, the first disjunct would fire instead. Validating the raw unparsed value against the item list is the residual defect — a value that formats to an existing item must be accepted silently.
Testcase as described in the bullet point:
DEF VAR id AS CHAR view-as combo-box LIST-ITEM-PAIRS "123456789qwe","123456789abc" DROP-DOWN-LIST FORMAT "x(3)". def frame f id. enable all with frame f. wait-for window-close of current-window.
4GL and 11191a does not report anything in ChUI.
- [MAJOR] functional
GenericFrame.setScreenValue: The newrecoverFromParsingErrorhook is wired only tocatch (DisplayFormatCheckException e)(lines 3786-3804), butparseValue(3929-4014) can raise that exception only in thetype.equals(character.class)branch viapres.checkFormat()(StringFormat.java:1123). For the numeric and date datatypes the "cannot format" case raisesErrorConditionExceptioninstead, which bypasses the hook entirely:integer/int64/decimalviaNumberType.parseDecimaltoerrorInvalidChar(NumberType.java:3495) torecordOrThrowError(76, ...), anddate/datetime/datetime-tzvianew date(character)torecordOrStoreError85(date.java:5137) torecordOrThrowError(85, ...).ErrorConditionExceptionis aRuntimeExceptioncaught nowhere betweenparseScreenValueandGenericWidget.setScreenValueInt(lines 6731-6744), which emits 4078;invalidScreenValueWarningis never reached andControlSetEntity.validateScreenValueis never entered. Trigger (ChUI, realized):def var i as int view-as combo-box list-items "1","2","3". def frame f i. enable all with frame f. i:screen-value = "abc".— FWD emits 76 + 4078 where the matrix requires the format error plus 4058; same for a date combo (85 + 4078). So the central fix works forcharacterandlogicalonly (logicalis fine:new logical(character, format)records 87 and returns unknown without throwing, so the flow reachesvalidateScreenValueand 4058 fires). Note theFIXME: we need to find out which error number to use herecomment survives untouched exactly where these types land. Related gap:recidis not handled byparseValueat all (recid extends int64but the branch uses exacttype.equals(int64.class)), so a recid control set skips parsing and format checking entirely — no error of any kind. Bothinteger/decimal/date/datetime/datetime-tzandrecidare listed in the issue as "covered by testing".
Valid. Extended the catch.
- [MAJOR] functional
ControlSetEntity.validateScreenValue: Replacing the oldframe != null && getAttr(ControlSetConfig::getWasRealized, config, true)guard withinvalidScreenValueWarning(..., this instanceof ComboBoxWidget)drops the realization gate for SELECTION-LIST on the UI-statement path, not just the SCREEN-VALUE attribute path.GenericFrame.display()(line 6011) callscopyToScreenBufferbeforeviewWorker(6013), andcopyToScreenBufferline 11311 callswidget.setScreenValue(frameBuf, value, true)withinUIStmt = true;ControlSetEntity.setScreenValueline 3002 (if (inUIStmt || !internalScreenValueUsage)) then runsvalidateScreenValuewhilewasRealizedis still false. Trigger:DEFINE VARIABLE sl AS CHARACTER INITIAL "zzz" VIEW-AS SELECTION-LIST LIST-ITEMS "a","b". DEFINE FRAME f sl. DISPLAY sl WITH FRAME f.now emits 4058 on the first DISPLAY where the old realization gate kept it silent; the same reachesupdateWorker(9846, 9928 — UPDATE/SET),displayWorker(12012),displayAndDownWorker(13594) andstartEditingMode(8066). Nothing blocks the path —SelectionListWidget.hasFormat()returning false affects only format handling, andSelectionListWidget.validateScreenValue(960) delegates unconditionally to super. The matrix rows describe SCREEN-VALUE assignment; a plain DISPLAY would emit a message naming an attribute the program never touched. Decisive in-diff evidence that the two halves are inconsistent: the author addedif (!internalScreenValueUsage)toRadioSetWidget.validateScreenValue(1435-1439) with the comment "The RADIO-SET widget throws invalid screen-value warnings only for external calls" — RADIO-SET and SELECTION-LIST share a matrix row, yet only one got the gate, so identical 4GL now behaves differently. Suggested fix: gate theControlSetEntitywarning on!internalScreenValueUsagetoo. Aggravating factor (mechanism pre-existing):recordOrShowWarningpassesisError=falseandrecordOrShowErrorre-derivesisError = hasLegacyError() || mustManageLegacyError(), so inside a CATCH or BLOCK-LEVEL UNDO,THROW block this becomes aDeferredLegacyErrorException— an ERROR condition raised by a plain DISPLAY.
Both 4GL and 11191a raises 4058 in this testcase, so if the trunk was silent on the DISPLAY statement, it was a bug which is now solved.
- [MAJOR] functional
RadioSetWidget.setScreenValueInt: Removing the!LogicalTerminal.isChui()guard makes the empty-string-to-unknown coercion run on ChUI, changing the stored and rendered value, not just validation.savedValueis consulted only insideisValidScreenValue; the item-matching loop invalidateScreenValuecompares the parameter (the coerced unknown), and bothframeBuf.putWidgetValueandrefreshFrameWidget/putScreenValuereceive the unknown.CompareOps._isEqual(unknown, "")is false, and client-sideRadioSet.setValueonly everselectButton@s on a match, so an unknown is a no-op on the client — no button selected, @config.currentleft as-is. Trigger (ChUI, empty-valued item present, widget realized before the assignment sogetInitialValue()does not mask it):def var c as char view-as radio-set radio-buttons "Nothing","","A","a". def frame f c. display c with frame f. c:screen-value = "".— previously""reached the loop, matched the empty-valued item and was stored, selecting that button; now nothing matches. The guard being removed was added deliberately by the same author at rev. 15471 (#8836) together with the "for GUI clients" Javadoc, so ChUI non-coercion was established behavior, and nothing in the issue claims OE ChUI was re-tested.ComboBoxWidget.setScreenValueIntstill keeps both!isChuiand!pairsguards on the same coercion, andRadioSetConfigsetspairs = trueunconditionally — by that analogy the quirk should apply less here, not more. The coercion is also not required by the rev-16624 fix: capturingsavedValueunconditionally while keeping theisChuiguard on the coercion is sufficient, becauseparseScreenValue(logical, "", 'one/two')already yields an unknown on ChUI, soisValidScreenValuestill gets itssavedValueand no 4058 is emitted. Separately, hoistingreturn trueout of the item loop meansrs:screen-value = ""with no empty-valued item is now accepted (storing unknown) where the old fall-throughreturn falsemadeGenericFrameskipputScreenValue/refreshFrameWidgetand leave the selection untouched.
Valid. Testcase:
def var c as char view-as radio-set radio-buttons "A","a","Nothing","". def frame f c. display c with frame f. c:screen-value = "a". c:screen-value = "".
4GL and trunk selects the Nothing button, whereas 11191a ends up with A. Reverted the change.
- [MAJOR] functional
GenericFrame.setScreenValue: The same non-parsing branch loses the unknown-value typing, regressing an intentional OE behavior.ComboBoxWidget.resolveUnknownScreenValue(lines 2017-2020) branches on the buffer value's class —bdt.getClass().isAssignableFrom(date.class) ? new character("") : new character()— andGenericFrameline 3662 documents the rule ("date/datetime/datetime-tz all return empty string for unknown", implemented per history entry048 FER 20250312). Before the changeparseValue(3930-3941) short-circuited unknown input toBaseDataTypeFactory.instantiate(type), i.e. an unknowndate, so the test passed and the read-back was""; now the buffer holds an unknowncharacter, the test fails, and the read-back is?. Trigger (GUI):DEFINE VARIABLE d AS DATE VIEW-AS COMBO-BOX SUBTYPE SIMPLE LIST-ITEMS "01/01/2020".thend:SCREEN-VALUE = ?then readd:SCREEN-VALUE. Scope: confined to plainDATE— fordatetime/datetimetzthe pre-existingisAssignableFromtest is written backwards and was already false, and for the numeric types both before and after yield unknown.
You can not have SIMPLE/DROP-DOWN combo-boxes with non-character data types, even for dynamic widgets:
def var d as handle.
create combo-box d assign
data-type = "date"
subtype = "simple"
list-items = "10/10/2010".
def frame f.
d:frame = frame f:handle.
enable all with frame f.
wait-for window-close of current-window.
ComboBoxWidget.shouldParseScreenValue() returns false only for GUI combo-boxes with SIMPLE or DROP-DOWN types, which can not have a DATE data type.
- [MAJOR] functional
GenericFrame.convertScreenValue: Letting an unparseable value into the screen buffer (correct per the matrix, "accepted as-is") makes a downstream read silently swallow a raised 4GL error and substitute the type default.ComboBoxWidget.inputValuetoGenericWidget.inputValuetoframe.getter(id, integer.class, true, false)toconvertScreenValue: the(character, String)constructor lookup fails into the blanketcatch (Exception exc)at line 8841, theninteger.getConstructor(character.class)tonewInstancetoint64.setValue(String)toNumberType.parseDouble("abc")toerrorInvalidChartorecordOrThrowError(76, "Invalid character in numeric input a")— and thatErrorConditionException(wrapped inInvocationTargetException) is discarded by the second blanketcatch (Exception exc)at line 8861, leavingvalue nullsodefaults truesubstitutesBaseDataType.generateDefault(integer.class). Trigger (GUI):def var i as int view-as combo-box simple list-items "1","2". def frame f i. i:screen-value = "abc". message i:input-value.prints 0 and error 76 never surfaces — not even inERROR-STATUS. The blanket catch is pre-existing, but this diff is what makes the path reachable: before the change an unparseable value could never enter the buffer for a GUI SIMPLE/DROP-DOWN combo. Caveat:inputValuedeliberately maps an unknown result togenerateDefault(history entries038 SAT 20231025/26), so the returned 0 may be OE-correct; the destroyed error condition is a defect regardless. Worth confirming on OE whether 76 (or 5321) surfaces for INPUT-VALUE of an unparseable integer combo screen value.
Again, you can not create GUI combo-boxes of subtype SIMPLE / DROP-DOWN with non-character modes.
- [MINOR] style
ControlSetEntity.invalidScreenValueWarning: The new protected instance method (line 3291) is placed after the private instance methodscontrolSetItem/sortItemsand the private static methodbubbleSort, violating the required member ordering (protected instance methods must precede private static and instance methods). Relocate it up among the other protected instance methods, e.g. nearrecoverFromParsingError/isValidScreenValue.
- [MINOR] style
RadioSetWidget: The new field comment forsavedValue(line 145) uses a plain block comment/* ... */instead of the javadoc/** ... */style used for field documentation elsewhere in the package (e.g. every field inGenericWidget). Convert it to/** ... */.
Solved.
Please check rev. 16626.