# ErrorManager.java

    /*
    ** Module   : ErrorManager.java
    ** Abstract : Provides ERROR-STATUS, NO-ERROR and error message support.
    **
    ** Copyright (c) 2005-2025, Golden Code Development Corporation.
    **
    ** -#- -I- --Date-- --JPRM-- ----------------------------------Description-----------------------------------
    ** 001 GES 20050823   @21210 Created initial version which provides
    **                           ERROR-STATUS and NO-ERROR support.
    ** 002 GES 20050829   @22362 Minor mods to more closely duplicate how
    **                           Progress handles error text. There are
    **                           cases where error text is added but no
    **                           error condition is raised (even in a
    **                           non-silent mode situation).
    ** 003 ECF 20051104   @23262 Added capability to extract error data from
    **                           Java exceptions. This includes both formatted
    **                           error texts as well as recording ErrorEntry
    **                           objects. Overloaded recordOrThrowError for
    **                           this purpose and added a set of overloaded
    **                           errorTexts() methods.
    ** 004 ECF 20051205   @23698 Added recordOrShowError method variants.
    **                           Displays an error message in an alert box and
    **                           logs the error.
    ** 005 ECF 20051219   @23752 Added warning mode. When in warning mode,
    **                           errors are reported to the UI using an alert
    **                           box, rather than an exception being thrown.
    ** 006 ECF 20060117   @23959 Enabled show error code. Removed temporary
    **                           code which used stderr and replaced with
    **                           calls to LogicalTerminal.message().
    ** 007 GES 20060123   @24021 Moved to context-local storage.
    ** 008 GES 20060208   @24389 Errors are now displayed via message when
    **                           not in silent error mode.
    ** 009 NVS 20060306   @24903 Errors occurring on the client are now passed
    **                           to the server for centralized logging.
    ** 010 NVS 20060313   @24994 Error messages are displayed using Logical
    **                           Terminal methods if on the server, and Client
    **                           Exports directly otherwise.
    ** 011 GES 20060313   @24995 Use new logging infrastructure.
    ** 012 GES 20060626   @27569 Implemented scoped context local data which
    **                           makes it safe to use silent error mode in
    **                           a nested manner.
    ** 013 ECF 20060719   @28122 Changed logging level for silent mode errors
    **                           from WARNING to FINE.
    ** 014 GES 20060807   @28454 Match new signature in ClientExports.
    ** 015 GES 20060927   @29966 Exposed displayError() as public.
    ** 016 GES 20061004   @30134 Added support for nested errors (errors
    **                           generated during processing of another
    **                           error).
    ** 017 NVS 20061017   @30470 Implemented ability to restart the session
    **                           without exiting the process.
    ** 018 ECF 20071217   @36499 Added headless mode. Intended to support
    **                           environments with no UI and limited logging.
    **                           Implementation at this time is limited.
    ** 019 CA  20080530   @38463 Added methods related to displaying an error
    **                           message before the application stops.
    ** 020 GES 20080619   @38886 Added a convenience method to format and
    **                           display an error without throwing any
    **                           exception, w/o being affected by silent
    **                           error mode and without recording anything.
    ** 021 GES 20080624   @38912 Added an ignore flag to NOP all display,
    **                           recording or exception generation. Cleaned
    **                           up the API to meet coding standards.
    ** 022 CA  20080815   @39447 Added displayAbend - it displays an abend
    **                           message, but no actual abend is triggered.
    ** 023 SVL 20081208   @40826 Match client exports API change.
    ** 024 ECF 20081215   @40913 Added throwError() variants. Unconditionally
    **                           displays an error message and throws an error
    **                           condition exception, regardless of silent
    **                           error mode. Headless mode is still honored.
    ** 025 GES 20090422   @41908 Converted to standard string formatting.
    ** 026 SVL 20090612   @42669 showErrorAndAbend() can be executed on the client
    **                           side.
    ** 027 ECF 20090622   @42987 Added new display option for errors. New variants
    **                           of displayError() and buildErrorText() allow the
    **                           standard message prefix to be omitted.
    ** 028 SIY 20090701   @43012 Made ErrorEntry public for use by other classes.
    **                           Inferred generics. Minor cleanups.
    ** 029 SVL 20090803   @43476 showErrorAndAbend(int[],String[]) has been added.
    ** 030 GES 20090831   @43802 Removed stacking from the error-status data
    **                           members. The 4GL doesn't stack the error list or
    **                           the error flag. The previous stacking was an
    **                           over-compensation for saving state across top-
    **                           level block boundaries. The transaction mgr now
    **                           handles saving/restoring the silent error mode
    **                           flag in the necessary cases. This class now can
    **                           be greatly simplified which also fixes some issues
    **                           with the error-status system handle.
    ** 031 SVL 20090908   @43847 recordOrThrowError() saves error code in the
    **                           ErrorConditionException.
    ** 032 GES 20090924   @44010 Completed headless support. Both the global flag
    **                           and a context-specific flag are honored.
    ** 033 SIY 20110112          Minor refactorings and cleanups.
    ** 034 CA  20130112          Added asHandle, getType, isValid and isUnknown
    **                           methods, accessed by the ERROR-STATUS handle.
    **                           Added new recordOrThrowError APIs.
    ** 035 CA  20130216          Added support for ERROR-OBJECT-DETAIL attribute.
    ** 036 CA  20130221          getType getter for TYPE attribute was renamed to getResourceType.
    ** 037 CS  20130221          Added conversion support for _msg() function
    ** 038 CA  20130222          Added missing setter for ERROR attribute.
    ** 039 OM  20130304          Refactored isValid and isUnknown of WrappedResource to valid
    **                           and unknown.
    ** 040 VMN 20130411          Added boolean parameter modal to recordOrShowError() method.
    ** 041 EVK 20130413          Implemented method getErrorNumberAtIndex
    ** 042 CA  20130612          ErrorConditionException's generated in recordOrThrowError will carry
    **                           as cause a NumberedException identifying the error number and the
    **                           associated message.
    ** 043 OM  20130606          Handle for soapFault set to null for the moment. This is a bug that
    **                           manifests with pl/java and should be fixed (with lazy initialization ?)
    ** 044 EVL 20130718          Adding new method to display message and abend. Also it silently
    **                           returns if NO-ERROR option is specified.
    ** 045 VMN 20130731          Adding asMsg parameter to recordOrThrowError() method.
    ** 046 CA  20130909          Added "prefix" parameter to recordOrShowError, to allow error
    **                           messages without double-asterisk prefix.
    **                           Added "isError" parameter to recordOrShowError, to allow messages to
    **                           be recorded without setting the pending error flag.
    ** 047 CA  20130920          Fixed H046 - if ERROR attribute is set to false by recordOrShowError,
    **                           than do not clean the error message list.
    ** 048 EVK 20130922          Added new addError version which will conditionally add the "**" to
    **                           the error message, based on the new prefix parameter.
    ** 049 CA  20130927          Static proxies need the unknown() API. Resource type is determined
    **                           from LegacyResource constants.
    ** 050 CA  20131219          Added setDontClean static implementation. Fixed implementation of
    **                           isErrorFlag.
    ** 051 EVL 20131119          Separating error output from client implementation.
    ** 052 CA  20140107          Made setErrorObjectDetail package private.  The WorkArea.soapFault
    **                           must be initialized to null by default, to allow ErrorManager usage
    **                           from pl/java (for this reason, setErrorObjectDetail will create a new
    **                           handle and assign it to WorkArea.soapFault).
    ** 053 GES 20140121          Added noRecordOrThrowError().
    ** 054 CA  20140513          Added a weight for the context-local var, to ensure predetermined
    **                           order during context reset.
    ** 055 VIG 20140701          Added separate error statuses for the current and pending statuses.
    **                           Added class ErrorStatus, that corresponds to ERROR-STATUS handle.
    **                           Removed clearList and ServerDataAccess.setError methods.
    ** 056 SVL 20140709          Prefix field was added to ErrorEntry.
    ** 057 VIG 20140709          Changed implementation of getElementAtIndex methods (_MSG),
    **                           Added raisedConditions - list of error numbers and corresp. methods.
    ** 058 OM  20141015          Eliminate NPE in addRaisedCondition() when argument is null.
    **                           Fixed postprocessOnAbend(String[] text) message.
    ** 059 SVL 20141209          Added ability to display error messages without finishing dot.
    ** 060 CA  20150308          Added support for client-side error data storage.  This allows
    **                           temporary disabling the error-management without a server-side trip.
    ** 061 CA  20150502          The resource ID needs to be kept at the resource, so it can be
    **                           accessed after the resource is deleted.
    ** 062 OM  20151004          Added setter stub for ERROR-STATUS:NUM-MESSAGES attribute.
    ** 063 EVL 20160224          Javadoc fixes to make compatible with Oracle Java 8 for Solaris 10.
    ** 064 IAS 20160328          Client log support
    ** 065 HC  20160722          Added recordOrShowWarning() stub forwarding calls to
    **                           recordOrShowError().
    ** 066 ECF 20160827          Minor refactoring to avoid expense of getting context-local work
    **                           area multiple times in some API calls.
    ** 067 ECF 20160912          Minor logging change.
    ** 068 HC  20171003          Implemented SESSION:SUPPRESS-WARNINGS runtime support and the
    **                           ability to handle (display or output depending on the runtime state)
    **                           legacy warnings.
    ** 069 SVL 20171024          Added displayError(errmsg, noMessageBox).
    ** 070 CA  20171030          Made addError public.
    ** 071 GES 20171205          Reimplemented silent error mode using a worker that takes a lambda.
    **                           This reads better in calling code. With this change, the silent mode
    **                           now actually throws the error condition and it gets caught in the
    **                           worker, which is more functionally correct because it aborts further
    **                           processing on errors in the calling context rather than using
    **                           isPendingError() to bypass other work. Anything with side effects
    **                           causes issues in the old approach.
    **     ECF 20171212          Added method to clear pending error status. In silent error mode, do
    **                           not generate NumberedException in recordOrThrowError, in order to
    **                           avoid the overhead of stack trace generation.
    **     GES 20171220          Changed throwError() to set a force flag on the raised error
    **                           condition such that the silent mode working will re-throw the error
    **                           instead of supressing it. This is needed for some deferred error
    **                           processing cases found in the persistence layer.
    ** 072 CA  20180503          Added SESSION:SYSTEM-ALERT-BOXES.
    ** 073 ECF 20190326          Minor performance improvement.
    ** 074 OM  20190611          Added id/parameters based APIs.
    ** 075 OM  20190720          Updated id/parameters error list.
    ** 076 OM  20191014          Added ERROR, schema validation and SAVE-ROW-CHANGES error messages.
    ** 077 IAS 20191022          Added DEBUG-ALERT support to the displayError method.
    ** 078 CA  20191119          Added nestedSilent(), a FWD-internal helper, which allows execution
    **                           of code in silent-mode, even if the caller is already in NO-ERROR
    **                           mode (and preserving any previous error state).
    ** 079 CA  20200324          More fixes related to OO-like error processing via AppError.
    **     CA  20200406          mustManageLegacyError must delegate the call to the server side.
    **                           ERROR conditions generated by the FWD runtime must be deferred, so
    **                           that they can be emitted by the client-side, too.
    **     SBI 20200430          Added UnableToSetComHandleProperty and WhileProcessingComponentProperty
    **                           errors.
    **     CA  20200503          Added ERROR:ERROR-STRING stubs.
    ** 080 GES 20200803          Rework to expose ErrorHelper so that server-side callers can avoid context-local
    **                           lookups.  Fixed swapped parameters to ErrorConditionException constructor which
    **                           was treating an error code as a key code.
    **     GES 20200820          Safety code in case ErrorHelper is requested on the client side.
    **     GES 20200821          Added ErrorHelper.isSuppressWarnings().
    **     ME  20201014          Do not append error number if zero, there are system errors thrown with number
    **                           set to zero.
    **     OM  20201020          Renamed warning to errorsAsWarnings and added accessors in ErrorHelper.
    **                           Added some missing error messages.
    **     OM  20201120          Changed method name to match no-bean prefixes buffer convention.
    **         20201125          displayError is no-op in silent mode.
    **     OM  20201203          Fixed handling of READ/ONLY attributes.
    **     ME  20210128          Add helper method for 'invalid parameter' error and push all messages from an
    **                           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.
    **     OM  20210312          Added error messages related to LOBs and LONGCHAR variables.
    **     CA  20210629          Added error messages related to indexed dereferenced fields (lvalue and rvalue).
    **     OM  20210809          Added error messages for serialization of tables to JSON objects.
    **     CA  20210928          Added the text for error 12355.
    **     CA  20211001          Added setter support for ERROR-STATUS:ERROR.
    **     IAS 20211223          Special processing of errors raised by UDFs.
    **     OM  20220112          Added error messages related to SKIP-DELETED-RECORD attribute.
    **     CA  20220120          In NO-ERROR mode, delete any legacy exception once it was consumed.
    **     OM  20220217          Added error messages related to ATTACH-DATA-SOURCE method.
    **     CA  20220304          An ERROR condition being raised from within a block which can catch SysError must
    **                           be morphed into a legacy SysError exception, and not raise an ERROR condition.
    **     OM  20220328          Added new JSON and XML deserialization errors (related to unique idx validation).
    **     ME  20220512          Do not show warning messages if suppressed for the session.
    **     CA  20220524          recordForLegacyThrow must rebuild the SysError reference if it got deleted.
    **     OM  20220525          Added text for error no. 15376.
    **     CA  20220526          Added displayAndRaiseError, which shows error texts and raises an ERROR condition
    **                           regardless of NO-ERROR status (required by DELETE OBJECT extProg, when there are
    **                           buffer validation errors).
    **     OM  20220527          Added text for error no. 12298. Flattened the stack-trace a bit.
    **     CA  20220531          displayError must throw a legacy exception, if the block is configured it.
    **     OM  20220531          Added some missing errors related to sequences.
    **     OM  20220604          Added some missing errors related to dereferenciation support.
    **     CA  20220614          recordOrShowError will raise legacy OO exceptions if the ERROR flag is set and
    **                           the surrounding code allows for it.
    **     VVT 20220911          ErrorManager$ErrorEntry must be serializable. See #6694-16.
    **     VVT 20221003          CommonHandle.getResourceType() method renamed to resourceType() to prevent conflicts
    **                           with namesakes in DMO. See #6694.
    **     CA  20221012          Fixed JSON import of tables with extent fields, when the source is a JsonObject.
    **     CA  20220524          recordForLegacyThrow must rebuild the SysError reference if it got deleted.
    **     OM  20220525          Added text for error no. 15376.
    **     CA  20220526          Added displayAndRaiseError, which shows error texts and raises an ERROR condition
    **                           regardless of NO-ERROR status (required by DELETE OBJECT extProg, when there are
    **                           buffer validation errors).
    **     OM  20220527          Added text for error no. 12298. Flattened the stack-trace a bit.
    **     CA  20220531          displayError must throw a legacy exception, if the block is configured it.
    **     OM  20220531          Added some missing errors related to sequences.
    **     OM  20220604          Added some missing errors related to dereferenciation support.
    **     CA  20220613          Allow OO exceptions to propagate to the remote side, when exiting the root block
    **                           on a appserver call.
    **     OM  20220706          New error message related to PRIMARY attribute validation.
    **     VVT 20220913          CommonHandle.getResourceType() method renamed to resourceType() to prevent conflicts
    **                           with namesakes in DMO. See #6694.
    **     IAS 20221003          Fixed error message for error 3131
    **     IAS 20221006          Reverted error message for error 3131 change.
    **     IAS 20221102          Added message for error 12323.
    **     IAS 20221100          Added message for warning 13095.
    **     IAS 20221112          Added recordOrShowError(int id, boolean isError, boolean isModal, String... params)
    **     CA  20230110          When NO-ERROR is used, it is assumed that an ERROR condition will be added to the
    **                           'errorList' in a significant less cases than the number of calls - thus let this
    **                           list be created in a lazy manner, when an ERROR message is actually registered
    **                           via NO-ERROR.
    **     CA  20230116          Improvement for context-local state resolution.
    ** 081 GBB 20230410          ErrorWriter.logStackTrace() replaced with rpc call to ErrorDataAccess#writeLog().
    **                           Flag logCallStack added.
    ** 082 IAS 20230325          Added recordOrShowWarning(int id, String... params) method. Added
    **                           error messages for 13184 and 15357.
    **     IAS 20230405          Added error message for error 14282.
    ** 083 GBB 20230512          Logging methods replaced by CentralLogger/ConversionStatus.
    ** 084 GBB 20230608          LOG-MANAGER constant for a subsys renamed.
    ** 085 TT  20230614          Added the functionality for session:suppress-warnings-list.
    ** 086 CA  20230907          Added missing 10515 error text.
    ** 087 GBB 20231101          Hardcoded true replaced by boolean isFromLogManager in recordOrThrowError.
    ** 088 OM  20231110          Prevented downstream NPE caused by log accessing non-existing user context.
    ** 089 OM  20240110          Added message no. 12349.
    ** 090 DDF 20240423          Added an overload for showErrorAndAbend which forces an error to be displayed
    **                           even if it's silent and does not mark it as an error (it is not recorded).
    ** 091 DDF 20240523          Added a displayError() method that receives an additional parameter.
    **     DDF 20240524          isError flag can be influenced by the existence of a CATCH block in the
    **                           current block, removed unnecessary displayError() method.
    **     DDF 20240524          Modified condition for setting the isError flag in recordOrShowError().
    **     DDF 20240527          Added recordOrShowDatabaseError().
    ** 092 CA  20240609          An ERROR condition must be triggered in the caller (outside of the top-level
    **                           block) is current file has ROUTINE-/BLOCK-LEVEL statetements and this condition
    **                           can't be morphed in a structured exception in the current stacktrace.
    ** 093 GBB 20240610          noRecordOrThrowErrorWorker() should not log errors. Implementing
    **                           RemoteErrorData.handleSoupFault() to be handled server-side.
    ** 094 GBB 20240621          New appserver connect error msgs added to numbered errors.
    **                           Adding static throwErrorConditionException for network interface impl.
    ** 095 DDF 20240814          Added handleForwardPending(), getErrorStatus() and processLegacyErrorException()
    **                           methods.
    **     DDF 20240822          Moved processLegacyErrorException() method from ErrorHelper to ErrorManager.
    ** 096 AS  20240911          Added 12311 and 12314 error support.
    ** 097 TJD 20230224          isSilent should be checked before calling <code>displayError</code>, not inside it.
    **     TJD 20240422          checking isSilent restored in <code>displayError</code>, temporary disable
    **                           isSilent in <code>throwError</code> instead
    **     TJD 20240423          No need to restore silent mode if it was not set
    ** 098 OM  20241001          Added tenant-related error messages.
    ** 099 OM  20240925          The error message was not parametrised in recordOrShowError().
    ** 100 AL2 20241108          Reduce logging level if the error is going to be caught by a catch block.
    ** 101 CA  20240625          Certain database-level ERROR conditions (like 132/buffer already exists with)
    **                           must be processed as if BLOCK-LEVEL ON ERROR UNDO, THROW is set (in other words,
    **                           it can get morphed into a strcutured exceptions).
    ** 102 OM  20241128          A static method cannot be final. (It cannot be overridden). Formatting fixes.
    ** 103 AS  20241204          Do not log caught errors when using LOG-MANAGER.
    ** 104 ICP 20241202          Added buffer copy and temp table copy error messages.
    ** 105 FER 20241212          Added a displayError() method is facilitates an error box with custom msg.
    ** 106 ICP 20250129          Used logical constants to leverage cached instances.
    ** 107 GBB 20250403          Support for the new multi-session appserver.
    ** 108 ES  20250304          Generate a StopError legacy exception when error code is
    **                           EXIT_CODE_PROCEDURE_NOT_FOUND
    */

    /*
    ** This program is free software: you can redistribute it and/or modify
    ** it under the terms of the GNU Affero General Public License as
    ** published by the Free Software Foundation, either version 3 of the
    ** License, or (at your option) any later version.
    **
    ** This program is distributed in the hope that it will be useful,
    ** but WITHOUT ANY WARRANTY; without even the implied warranty of
    ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    ** GNU Affero General Public License for more details.
    **
    ** You may find a copy of the GNU Affero GPL version 3 at the following
    ** location: https://www.gnu.org/licenses/agpl-3.0.en.html
    **
    ** Additional terms under GNU Affero GPL version 3 section 7:
    **
    **   Under Section 7 of the GNU Affero GPL version 3, the following additional
    **   terms apply to the works covered under the License.  These additional terms
    **   are non-permissive additional terms allowed under Section 7 of the GNU
    **   Affero GPL version 3 and may not be removed by you.
    **
    **   0. Attribution Requirement.
    **
    **     You must preserve all legal notices or author attributions in the covered
    **     work or Appropriate Legal Notices displayed by works containing the covered
    **     work.  You may not remove from the covered work any author or developer
    **     credit already included within the covered work.
    **
    **   1. No License To Use Trademarks.
    **
    **     This license does not grant any license or rights to use the trademarks
    **     Golden Code, FWD, any Golden Code or FWD logo, or any other trademarks
    **     of Golden Code Development Corporation. You are not authorized to use the
    **     name Golden Code, FWD, or the names of any author or contributor, for
    **     publicity purposes without written authorization.
    **
    **   2. No Misrepresentation of Affiliation.
    **
    **     You may not represent yourself as Golden Code Development Corporation or FWD.
    **
    **     You may not represent yourself for publicity purposes as associated with
    **     Golden Code Development Corporation, FWD, or any author or contributor to
    **     the covered work, without written authorization.
    **
    **   3. No Misrepresentation of Source or Origin.
    **
    **     You may not represent the covered work as solely your work.  All modified
    **     versions of the covered work must be marked in a reasonable way to make it
    **     clear that the modified work is not originating from Golden Code Development
    **     Corporation or FWD.  All modified versions must contain the notices of
    **     attribution required in this license.
    */

    package com.goldencode.p2j.util;

    import java.io.*;
    import java.util.*;
    import java.util.function.*;
    import java.util.logging.*;
    import com.goldencode.p2j.*;
    import com.goldencode.p2j.net.*;
    import com.goldencode.p2j.oo.lang.*;
    import com.goldencode.p2j.security.*;
    import com.goldencode.p2j.util.appserver.*;
    import com.goldencode.p2j.util.logging.*;
    import com.goldencode.proxy.*;

    /**
     * A context-local class providing static methods to support the
     * <code>ERROR-STATUS</code> and <code>NO-ERROR</code> Progress features.
     * <p>
     * The following is the mapping of Progress features to Java:
     * <p>
     * <pre>
     * Progress Feature             Type          Java Feature
     * ---------------------------  ------------  ----------------------------
     * error-status:error           attribute     {@link #isError}
     * error-status:num-messages    attribute     {@link #numErrors}
     * error-status:get-message     method        {@link #getErrorText}
     * error-status:get-number      method        {@link #getErrorNumber}
     * </pre>
     * <p>
     * In order to properly use this class, all processing which must be handled
     * on a <code>NO-ERROR</code> basis must be run as delegated code block processed by
     * {@link #silent}.  This code catches any error exception so that the business logic
     * aborts at the point of the error but the exception does not reach the block level.
     * All called code that honors <code>NO-ERROR</code> must be aware of silent error
     * mode.  This can be done via the following methods (in a library):
     * <p>
     * <ol>
     *    <li> If a simple return from a library is easily possible, delegate
     *         processing to this class using {@link #recordOrThrowError}. After
     *         calling this method, return silently from the library.
     *    <li> Do it yourself.
     *    <ul>
     *       <li> Use {@link #isSilentError} to determine if the silent mode is
     *            enabled.
     *       <li> Use {@link #addError} to add an error to the list if silent
     *            mode is enabled.
     *       <li> Throw {@link ErrorConditionException}.
     *       <li> This may be useful if a simple silent return is not easy to do
     *            as one can use the {@link ErrorUnwindException} to unwind the
     *            stack to the point in the library where a silent return is
     *            possible.  This exception must be caught in the library code
     *            and will not be caught in the client application.  Use of
     *            this exception should be completely hidden from the client
     *            application.
     *    </ul>
     * </ol>
     * <p>
     * On the server, the silent error mode flag is saved at every top level
     * scope (see {@link TransactionManager#pushScope}) and it is restored at
     * the end of that scope (see {@link TransactionManager#popScope()}).
     * This is done so that "nested" NO-ERROR conditions (e.g. during a RUN
     * statement on an internal or external procedure or an assignment statement
     * with a call to a user defined function) are maintained properly.  In
     * particular, the called procedure or function does NOT execute as if in
     * silent mode unless the code there is so bracketed.  However, the one
     * exception to this is that the RETURN ERROR behavior implemented by the
     * <code>TransactionManager</code> is aware of whether or not silent error
     * mode was enabled in the caller.
     * <p>
     * It must be noted that Progress seems to provide a single global instance of
     * ERROR-STATUS handle and all its data. Thus this data can be seen across
     * call/return boundaries and is only modified at very specific points. for
     * example, the error flag is only changed at the end of a NO-ERROR statement.
     * Likewise, the previous list of errors can be read until the first error
     * occurs after a NO-ERROR statement starts. This deferred clearing of the
     * error list is implemented using the pending error flag. When that flag is
     * set, the error list is cleared. If no errors occur in the NO-ERROR statement
     * then the error list is cleared at the same time the error flag is cleared
     * at the end of the NO-ERROR statement.
     */
    public class ErrorManager
    {
       /** Capacity of error conditions stack for _MSG function */
       private static final int ERR_CONDITIONS_CAPACITY = 25;

       /** Logger (safe as a JVM-wide value rather than as context-local). */
       private static final CentralLogger LOG = CentralLogger.get(ErrorManager.class);

       /** Stores context-local state variables. */
       private static final ContextContainer work = new ContextContainer();

       /**
        * Server-side it's a local instance.
        * Client-side it's a remote interface accessing the error data on the server.
        */
       private static RemoteErrorData server = new ServerErrorDataAccessor();

       /** The multi-session appserver manager. */
       private static final MultiSessionAppserverManager msaManager = MultiSessionAppserverManager.getInstance();

       /** Handler for ignored errors. */
       private static final RemoteErrorData suppressedHandler = new SuppressedErrorDataAccessor();

       /** Headless mode */
       private static boolean headless = false;

       /** Error writer object instance for client. */
       private static ErrorWriter errorWriter = null;

       /** Does this instance belong to the server side part. */
       private static boolean serverSide = true;

       /**
        * Get a the instance for the ERROR-STATUS system handle.  Is obtained using
        * a call to the {@link StaticProxy#obtain(Class, Class[])}, using the
        * {@link CommonErrorStatus} interface and its methods implemented by these
        * classes: {@link ErrorManager}.
        *
        * @return   See above.
        */
       public static handle asHandle()
       {
          WorkArea wa = work.get();

          Class[] classes = { ErrorManager.class };
          CommonErrorStatus proxy = StaticProxy.obtain(CommonErrorStatus.class, classes);

          if (wa.id == null)
          {
             wa.id = handle.resourceId(proxy);
          }

          return new handle(proxy);
       }

       /**
        * Get this resource's ID.
        *
        * @return   The resource's ID.
        */
       public static Long id()
       {
          WorkArea wa = work.get();

          return wa.id;
       }

       /**
        * Set this resource's ID.
        * <p>
        * This is a no-op for system handles.
        *
        * @param    id
        *           The resource's ID.
        */
       public static void id(long id)
       {
          // no-op
       }

       /**
        * Implementation for the {@link WrappedResource#valid()} API.
        *
        * @return   Always true.
        */
       public static boolean valid()
       {
          return true;
       }

       /**
        * Implementation for the {@link WrappedResource#unknown()} API.
        *
        * @return   Always false.
        */
       public static boolean unknown()
       {
          return false;
       }

       /**
        * API needed to implement read-only attribute assignment (a 4GL "feature").
        *
        * @param    attribute
        *           The attribute's name.
        *
        * @see      handle#readOnlyError(handle, String)
        */
       public static void readOnlyError(String attribute)
       {
          handle.readOnlyError(asHandle(), attribute);
       }

       /**
        * API needed to implement read-only attribute assignment (a 4GL "feature").
        *
        * @param    attribute
        *           The attribute's name.
        * @param    expr
        *           The value which is attempted to be assigned to the read-only attribute.
        *
        * @see      handle#readOnlyError(handle, String, Object)
        */
       public static void readOnlyError(String attribute, Object expr)
       {
          handle.readOnlyError(asHandle(), attribute);
       }

       /**
        * Initialize <code>ErrorManager</code> for headless mode.
        */
       public static void setHeadless()
       {
          headless = true;
       }

       /**
        * Set the state of SESSION:SYSTEM-ALERT-BOXES attribute.
        *
        * @param   on
        *          The new state for SESSION:SYSTEM-ALERT-BOXES attribute.
        */
       public static void setSystemAlertBoxes(boolean on)
       {
          work.obtain().systemAlertBoxes = on;
       }

       /**
        * Get the state of SESSION:SYSTEM-ALERT-BOXES attribute.
        *
        * @return  the state of SESSION:SYSTEM-ALERT-BOXES attribute.
        */
       public static boolean isSystemAlertBoxes()
       {
          return server.isSystemAlertBoxes();
       }

       /**
        * Initializes the proper error data access on the client.
        *
        * @param   errorServer
        *          The remote error interface
        */
       public static void initClient(RemoteErrorData errorServer)
       {
          server = errorServer;
          serverSide = false;
       }

       /**
        * Initializes the proper error writer on the client.
        *
        * @param    errorWriterInitial
        *           Error writer implementation for client side.
        */
       public static void initErrorWriter(ErrorWriter errorWriterInitial)
       {
          errorWriter = errorWriterInitial;
       }

       /**
        * Checks if the client is running in batch mode.
        *
        * @return  <code>true</code> for batch mode, <code>false</code> otherwise.
        *
        */
       public static boolean isInBatchMode()
       {
          return errorWriter != null && errorWriter.isInBatchMode();
       }

       /**
        * Stops the associated client session by generating UnstoppableExitException.
        */
       public static void stopBatchSession()
       {
          if (errorWriter != null)
          {
             errorWriter.stopBatchSession();
          }
       }

       /**
        * Reverts the error data access on the client.
        */
       public static void resetClient()
       {
          server = new ServerErrorDataAccessor();
       }

       /**
        * Reports if silent error mode has been enabled.
        *
        * @return   <code>true</code> if silent mode is enabled, otherwise
        *           <code>false</code>.
        */
       public static boolean isSilentError()
       {
          return server.isSilent();
       }

       /**
        * Queries the state of <code>ignore</code> mode.
        *
        * @return   <code>true</code> if ignore mode is enabled, otherwise <code>false</code>.
        */
       public static boolean isIgnore()
       {
          return isIgnoreWorker(work.obtain());
       }

       /**
        * Execute the given block while supressing normal error processing by temporarily enabling the
        * ignore flag.
        *
        * @param    code
        *           The code to execute.
        */
       public static void ignore(Runnable code)
       {
          ignoreWorker(code, work.obtain());
       }

       /**
        * Execute the given block while suppressing ALL error processing.
        *
        * @param    code
        *           The code to execute.
        */
       public static void nullErrors(Runnable code)
       {
          RemoteErrorData old = server;

          server = suppressedHandler;

          try
          {
             ignore(code);
          }
          finally
          {
             server = old;
          }
       }

       /**
        * Reports if warning mode has been enabled.  When in warning mode, error
        * messages are reported to the UI using an alert box.
        *
        * @return   <code>true</code> if warning mode is enabled, otherwise
        *           <code>false</code>.
        */
       public static boolean isWarningMode()
       {
          return work.obtain().errorsAsWarnings;
       }

       /**
        * Worker method that executes a given block of code with silent error mode turned on for
        * the duration of the code block.  Before the code block is executed, silent error mode
        * is turned on and after the code block completes (normally or abnormally), silent error
        * mode will be disabled.
        * <p>
        * This method can only be executed on the server side.
        *
        * @param    code
        *           The block of code to execute in silent mode.
        *
        * @return   The value of the ERROR-STATUS:ERROR flag when the execution is complete.
        */
       public static boolean silent(Runnable code)
       {
          return silentWorker(code, (ServerErrorDataAccessor) server, work.obtain());
       }

       /**
        * Worker method that executes a given block of code with silent error mode turned on for
        * the duration of the code block.  Before the code block is executed, silent error mode
        * is turned on and after the code block completes (normally or abnormally), silent error
        * mode will be disabled.
        * <p>
        * This method can only be executed on the server side.  It will backup and restore the current
        * {@link WorkArea#errorStatus}, to allow for nested silent-mode usages (internally by FWD).
        *
        * @param    code
        *           The block of code to execute in silent mode.
        *
        * @return   The value of the ERROR-STATUS:ERROR flag when the execution is complete.
        */
       public static boolean nestedSilent(Runnable code)
       {
          return nestedSilent(code, null);
       }

       /**
        * Worker method that executes a given block of code with silent error mode turned on for
        * the duration of the code block.  Before the code block is executed, silent error mode
        * is turned on and after the code block completes (normally or abnormally), silent error
        * mode will be disabled.
        * <p>
        * This method can only be executed on the server side.  It will backup and restore the current
        * {@link WorkArea#errorStatus}, to allow for nested silent-mode usages (internally by FWD).
        *
        * @param    code
        *           The block of code to execute in silent mode.
        * @param    errorProcessor
        *           A consumer of the logged errors in silent mode.
        *
        * @return   The value of the ERROR-STATUS:ERROR flag when the execution is complete.
        */
       public static boolean nestedSilent(Runnable code, BiConsumer<String, Integer> errorProcessor)
       {
          return nestedSilent(null, code, errorProcessor);
       }

       /**
        * Worker method that executes a given block of code with silent error mode turned on for
        * the duration of the code block.  Before the code block is executed, silent error mode
        * is turned on and after the code block completes (normally or abnormally), silent error
        * mode will be disabled.
        * <p>
        * This method can only be executed on the server side.  It will backup and restore the current
        * {@link WorkArea#errorStatus}, to allow for nested silent-mode usages (internally by FWD).
        *
        * @param    wa
        *           The context-local state.
        * @param    code
        *           The block of code to execute in silent mode.
        * @param    errorProcessor
        *           A consumer of the logged errors in silent mode.
        *
        * @return   The value of the ERROR-STATUS:ERROR flag when the execution is complete.
        */
       public static boolean nestedSilent(WorkArea wa, Runnable code, BiConsumer<String, Integer> errorProcessor)
       {
          ServerErrorDataAccessor srv = (ServerErrorDataAccessor) server;
          if (wa == null)
          {
             wa  = work.obtain();
          }

          ErrorStatus saveError = wa.errorStatus;
          ErrorStatus savePending = new ErrorStatus();
          savePending.set(wa.pendingErrorStatus);
          wa.errorStatus = new ErrorStatus();
          wa.pendingErrorStatus.initialize();
          forwardPending(wa);

          boolean saveSilent = srv.isSilent();

          // enable silent error processing
          srv.setSilent(wa, true);
          boolean hadError = false;

          try
          {
             code.run();
          }

          catch (ErrorConditionException err)
          {
             // normally we just eat the exception because it is being used to abort processing
             // exactly where the error occurred, which will restart processing after the given
             // code block; however, there is an exception for some deferred error processing use
             // cases where we must bypass silent error mode (throwError(NumberedException, boolean))
             if (err.isForce())
             {
                // don't honor silent mode, no suppression of errors here
                throw err;
             }
          }

          finally
          {
             // disable further silent error processing
             srv.setSilent(wa, false);
             forwardPending(wa);
             hadError = wa.errorStatus.error;

             if (errorProcessor != null && wa.errorStatus.errorList != null)
             {
                for (Object o : wa.errorStatus.errorList)
                {
                   ErrorEntry entry = (ErrorEntry) o;

                   String msg = (entry.prefix ? "** " : "") + entry.text;

                   errorProcessor.accept(msg, entry.num);
                }
             }

             wa.errorStatus = saveError;
             wa.pendingErrorStatus.set(savePending);

             if (saveSilent)
             {
                // re-enable it if it was enabled
                srv.setSilent(wa, true);
             }
          }

          return hadError;
       }

       /**
        * Enables warning mode, during which errors are non-fatal, but are
        * reported to the UI of the current context via a call to
        * <code>LogicalTerminal.message()</code>.
        * <p>
        * Each call to this method must be matched with a corresponding call
        * to {@link #warningModeDisable}.
        */
       public static void warningModeEnable()
       {
          // enable warning processing
          work.obtain().errorsAsWarnings = true;
       }

       /**
        * Disables warning mode.
        * <p>
        * Each call to this method must be matched with a corresponding, previous
        * call to {@link #warningModeEnable}.
        */
       public static void warningModeDisable()
       {
          // disable further warning processing
          work.obtain().errorsAsWarnings = false;
       }

       /**
        * Manufacture error text based on the given parameters, if silent error mode is enabled,
        * then this error number and the generated error text will be stored in the error list,
        * otherwise this generated error text will be used to display modal or modeless box in
        * the UI.
        * <p>
        * The error message(s) will be prefixed with <code>**</code> and the <code>pending</code>
        * error flag will be set to <code>true</code>.
        *
        * @param    num
        *           The error number.
        * @param    text
        *           The text of the error message.
        * @param    modal
        *           <code>true</code> if message will be shown in modal dialog.
        */
       public static void recordOrShowError(int num, String text, boolean modal)
       {
          recordOrShowError(new int[] { num }, new String[] { text }, modal, true, true);
       }

       /**
        * Manufacture error text based on the given parameters, if silent error mode is enabled,
        * then this error number and the generated error text will be stored in the error list,
        * otherwise this generated error text will be used to display modal or modeless box in
        * the UI.
        * <p>
        * The error message(s) will be prefixed with <code>**</code> and the <code>pending</code>
        * error flag will be set to <code>true</code>.
        *
        * @param    num
        *           The error number.
        * @param    text
        *           The text of the error message.
        * @param    modal
        *           <code>true</code> if message will be shown in modal dialog.
        */
       public static void recordOrShowError(int[] num, String[] text, boolean modal)
       {
          recordOrShowError(num, text, modal, true, true);
       }

       /**
        * Manufacture error text based on the given parameters, if silent error mode is enabled,
        * then this error number and the generated error text will be stored in the error list,
        * otherwise this generated error text will be used to display modal or modeless box in
        * the UI.
        * <p>
        * The error message(s) will be prefixed with <code>**</code> depending on the
        * <code>prefix</code> parameter and the <code>pending</code> error flag will be set to
        * <code>true</code>.
        *
        * @param    num
        *           The error number.
        * @param    text
        *           The text of the error message.
        * @param    modal
        *           <code>true</code> if message will be shown in modal dialog.
        * @param    prefix
        *           <code>true</code> to prepend a double asterisk prefix to the formatted error
        *           message, <code>false</code> to omit this text.
        */
       public static void recordOrShowError(int num, String text, boolean modal, boolean prefix)
       {
          recordOrShowError(new int[] { num }, new String[] { text }, modal, prefix, true);
       }

       /**
        * Manufacture error text based on the given parameters, if silent error mode is enabled,
        * then this error number and the generated error text will be stored in the error list,
        * otherwise this generated error text will be used to display modal or modeless box in
        * the UI.
        * <p>
        * The error message(s) will be prefixed with <code>**</code> depending on the
        * <code>prefix</code> parameter and the <code>pending</code> error flag will be set to
        * <code>true</code>.
        *
        * @param    num
        *           The error number.
        * @param    text
        *           The text of the error message.
        * @param    modal
        *           <code>true</code> if message will be shown in modal dialog.
        * @param    prefix
        *           <code>true</code> to prepend a double asterisk prefix to the formatted error
        *           message, <code>false</code> to omit this text.
        */
       public static void recordOrShowError(int[] num, String[] text, boolean modal, boolean prefix)
       {
          recordOrShowError(num, text, modal, prefix, true);
       }

       /**
        * Manufacture one or more error texts based on the given parameters, if silent error mode is
        * enabled, then these error numbers and the respective generated error text messages will be
        * stored in the error list, otherwise this generated error texts will be used to display
        * modal or modeless box in the UI.
        * <p>
        * The error message(s) will be prefixed with <code>**</code> depending on the
        * <code>prefix</code> parameter and the <code>pending</code> error flag will be set to the
        * value specified by the <code>isError</code> flag.
        *
        * @param    num
        *           The error number.
        * @param    text
        *           The text of the error message.
        * @param    modal
        *           <code>true</code> if message will be shown in modal dialog.
        * @param    prefix
        *           <code>true</code> to prepend a double asterisk prefix to the formatted error
        *           message, <code>false</code> to omit this text.
        * @param    isError
        *           The new value for the <code>pending</code> error flag.
        */
       public static void recordOrShowError(int     num,
                                            String  text,
                                            boolean modal,
                                            boolean prefix,
                                            boolean isError)
       {
          recordOrShowError(num, text, modal, prefix, isError, false);
       }

       /**
        * Manufacture one or more error texts based on the given parameters, if silent error mode is
        * enabled, then these error numbers and the respective generated error text messages will be
        * stored in the error list, otherwise this generated error texts will be used to display
        * modal or modeless box in the UI.
        * <p>
        * The error message(s) will be prefixed with <code>**</code> depending on the
        * <code>prefix</code> parameter and the <code>pending</code> error flag will be set to the
        * value specified by the <code>isError</code> flag.
        *
        * @param    num
        *           The error number.
        * @param    text
        *           The text of the error message.
        * @param    modal
        *           <code>true</code> if message will be shown in modal dialog.
        * @param    prefix
        *           <code>true</code> to prepend a double asterisk prefix to the formatted error
        *           message, <code>false</code> to omit this text.
        * @param    isError
        *           The new value for the <code>pending</code> error flag.
        * @param    asMsg
        *           <code>true</code> to use the given text as the message text else
        *           <code>false</code> to use buildErrorText.
        */
       public static void recordOrShowError(int     num,
                                            String  text,
                                            boolean modal,
                                            boolean prefix,
                                            boolean isError,
                                            boolean asMsg)
       {
          recordOrShowError(new int[] { num }, new String[] { text }, modal, prefix, isError, asMsg);
       }

       /**
        * Manufacture one or more error texts based on the given parameters, if silent error mode is
        * enabled, then these error numbers and the respective generated error text messages will be
        * stored in the error list, otherwise this generated error texts will be used to display
        * modal or modeless box in the UI.
        * <p>
        * The error message(s) will be prefixed with <code>**</code> depending on the
        * <code>prefix</code> parameter and the <code>pending</code> error flag will be set to the
        * value specified by the <code>isError</code> flag.
        *
        * @param    num
        *           The error number.
        * @param    text
        *           The text of the error message.
        * @param    modal
        *           <code>true</code> if message will be shown in modal dialog.
        * @param    prefix
        *           <code>true</code> to prepend a double asterisk prefix to the formatted error
        *           message, <code>false</code> to omit this text.
        * @param    isError
        *           The new value for the <code>pending</code> error flag.
        */
       public static void recordOrShowError(int[]    num,
                                            String[] text,
                                            boolean  modal,
                                            boolean  prefix,
                                            boolean  isError)
       {
          recordOrShowError(num, text, modal, prefix, isError, false);
       }

       /**
        * Manufacture one or more error texts based on the given parameters, if silent error mode is
        * enabled, then these error numbers and the respective generated error text messages will be
        * stored in the error list, otherwise this generated error texts will be used to display
        * modal or modeless box in the UI.
        * <p>
        * The error message(s) will be prefixed with <code>**</code> depending on the
        * <code>prefix</code> parameter and the <code>pending</code> error flag will be set to the
        * value specified by the <code>isError</code> flag.
        *
        * @param    num
        *           The error number.
        * @param    text
        *           The text of the error message.
        * @param    modal
        *           <code>true</code> if message will be shown in modal dialog.
        * @param    prefix
        *           <code>true</code> to prepend a double asterisk prefix to the formatted error
        *           message, <code>false</code> to omit this text.
        * @param    isError
        *           The new value for the <code>pending</code> error flag.
        * @param    asMsg
        *           <code>true</code> to use the given text as the message text else
        *           <code>false</code> to use buildErrorText.
        */
       public static void recordOrShowError(int[]    num,
                                            String[] text,
                                            boolean  modal,
                                            boolean  prefix,
                                            boolean  isError,
                                            boolean  asMsg)
       {
          recordOrShowError(num, text, modal, prefix, isError, asMsg, true);
       }

       /**
        * Manufacture one or more error texts based on the given parameters, if silent error mode is
        * enabled, then these error numbers and the respective generated error text messages will be
        * stored in the error list, otherwise this generated error texts will be used to display
        * modal or modeless box in the UI.
        * <p>
        * The error message(s) will be prefixed with <code>**</code> depending on the
        * <code>prefix</code> parameter and the <code>pending</code> error flag will be set to the
        * value specified by the <code>isError</code> flag.
        *
        * @param    num
        *           The error number.
        * @param    text
        *           The text of the error message.
        * @param    modal
        *           <code>true</code> if message will be shown in modal dialog.
        * @param    prefix
        *           <code>true</code> to prepend a double asterisk prefix to the formatted error
        *           message, <code>false</code> to omit this text.
        * @param    isError
        *           The new value for the <code>pending</code> error flag.
        * @param    asMsg
        *           <code>true</code> to use the given text as the message text else
        *           <code>false</code> to use buildErrorText.
        * @param    addDot
        *           <code>true</code> if dot(s) are added to the end of the message(s).
        */
       public static void recordOrShowError(int[]    num,
                                            String[] text,
                                            boolean  modal,
                                            boolean  prefix,
                                            boolean  isError,
                                            boolean  asMsg,
                                            boolean  addDot)
       {
          recordOrShowError(num, text, modal, prefix, isError, asMsg, addDot, false);
       }
       /**
        * Manufacture one or more error texts based on the given parameters, if silent error mode is
        * enabled, then these error numbers and the respective generated error text messages will be
        * stored in the error list, otherwise this generated error texts will be used to display
        * modal or modeless box in the UI.
        * <p>
        * The error message(s) will be prefixed with <code>**</code> depending on the
        * <code>prefix</code> parameter and the <code>pending</code> error flag will be set to the
        * value specified by the <code>isError</code> flag.
        *
        * @param    num
        *           The error number.
        * @param    text
        *           The text of the error message.
        * @param    modal
        *           <code>true</code> if message will be shown in modal dialog.
        * @param    prefix
        *           <code>true</code> to prepend a double asterisk prefix to the formatted error
        *           message, <code>false</code> to omit this text.
        * @param    isError
        *           The new value for the <code>pending</code> error flag.
        * @param    asMsg
        *           <code>true</code> to use the given text as the message text else
        *           <code>false</code> to use buildErrorText.
        * @param    addDot
        *           <code>true</code> if dot(s) are added to the end of the message(s).
        * @param    force
        *           <code>true</code> if errors which cause a modal dialog should be logged even in
        *           silent (ignore NO-ERROR clause) and headless modes
        */
       public static void recordOrShowError(int[]    num,
                                             String[] text,
                                             boolean  modal,
                                             boolean  prefix,
                                             boolean  isError,
                                             boolean  asMsg,
                                             boolean  addDot,
                                             boolean  force)
       {
          recordOrShowError(num, text, modal, prefix, isError, asMsg, addDot, force, false);
       }

       /**
        * Manufacture one or more error texts based on the given parameters, if silent error mode is
        * enabled, then these error numbers and the respective generated error text messages will be
        * stored in the error list, otherwise this generated error texts will be used to display
        * modal or modeless box in the UI.
        * <p>
        * The error message(s) will be prefixed with <code>**</code> depending on the
        * <code>prefix</code> parameter and the <code>pending</code> error flag will be set to the
        * value specified by the <code>isError</code> flag.
        *
        * @param    num
        *           The error number.
        * @param    text
        *           The text of the error message.
        * @param    modal
        *           <code>true</code> if message will be shown in modal dialog.
        * @param    prefix
        *           <code>true</code> to prepend a double asterisk prefix to the formatted error
        *           message, <code>false</code> to omit this text.
        * @param    isError
        *           The new value for the <code>pending</code> error flag.
        * @param    asMsg
        *           <code>true</code> to use the given text as the message text else
        *           <code>false</code> to use buildErrorText.
        * @param    addDot
        *           <code>true</code> if dot(s) are added to the end of the message(s).
        * @param    force
        *           <code>true</code> if errors which cause a modal dialog should be logged even in
        *           silent (ignore NO-ERROR clause) and headless modes
        * @param    isFromLogManager
        *           <code>true</code> if used for LOG-MANAGER, <code>false</code>
        */
       public static void recordOrShowError(int[]   num,
                                            String[] text,
                                            boolean  modal,
                                            boolean  prefix,
                                            boolean  isError,
                                            boolean  asMsg,
                                            boolean  addDot,
                                            boolean  force,
                                            boolean  isFromLogManager)
       {
          recordOrShowError(num, text, modal, prefix, isError, asMsg, addDot, force, isFromLogManager, false);
       }

       /**
        * Manufacture one or more error texts based on the given parameters, if silent error mode is
        * enabled, then these error numbers and the respective generated error text messages will be
        * stored in the error list, otherwise this generated error texts will be used to display
        * modal or modeless box in the UI.
        * <p>
        * The error message(s) will be prefixed with <code>**</code> depending on the
        * <code>prefix</code> parameter and the <code>pending</code> error flag will be set to the
        * value specified by the <code>isError</code> flag.
        *
        * @param    num
        *           The error number.
        * @param    text
        *           The text of the error message.
        * @param    modal
        *           <code>true</code> if message will be shown in modal dialog.
        * @param    prefix
        *           <code>true</code> to prepend a double asterisk prefix to the formatted error
        *           message, <code>false</code> to omit this text.
        * @param    isError
        *           The new value for the <code>pending</code> error flag.
        * @param    asMsg
        *           <code>true</code> to use the given text as the message text else
        *           <code>false</code> to use buildErrorText.
        * @param    addDot
        *           <code>true</code> if dot(s) are added to the end of the message(s).
        * @param    force
        *           <code>true</code> if errors which cause a modal dialog should be logged even in
        *           silent (ignore NO-ERROR clause) and headless modes
        * @param    isFromLogManager
        *           <code>true</code> if used for LOG-MANAGER, <code>false</code>
        * @param    isDatabaseError
        *           <code>true</code> to allow errors related to database influence the value of the
        *           isError flag, <code>false</code> otherwise. THis flag must only be used for
        *           database related errors.
        */
       private static void recordOrShowError(int[]    num,
                                            String[] text,
                                            boolean  modal,
                                            boolean  prefix,
                                            boolean  isError,
                                            boolean  asMsg,
                                            boolean  addDot,
                                            boolean  force,
                                            boolean  isFromLogManager,
                                            boolean  isDatabaseError)
       {
          if (isIgnore())
             return;

          if (num.length != text.length)
          {
             throw new IllegalArgumentException("Unbalanced error number and text message pairs");
          }

          StringBuilder buf = new StringBuilder();
          String sep = System.getProperty("line.separator");
          boolean silent = isSilentError();
          // isError flag can be influenced if there is a CATCH block in the current block,
          // making it possible to turn it into true even if it is specified as false.
          if (isDatabaseError)
          {
             isError = isError || hasLegacyError();
          }
          boolean manageLegacyError = !silent && isError && server.mustThrowLegacyError();

          for (int i = 0; i < num.length; i++)
          {
             String errmsg = asMsg ? text[i] : buildErrorText(num[i], text[i], prefix, addDot);

             if (i > 0)
             {
                buf.append(sep);
             }
             buf.append(errmsg);

             if (manageLegacyError)
             {
                server.recordForLegacyThrow(errmsg, num[i]);
             }
             else if (silent)
             {
                if (i == 0)
                {
                   // this may reset the error list so it must be done BEFORE we add the new error(s) to the list;
                   // this only needs to be done the first time through this loop
                   server.setPending(isError);
                }

                server.addRecord(new ErrorEntry(num[i], errmsg));
             }
          }

          String masterMsg = buf.toString();

          if (manageLegacyError)
          {
             if (!isFromLogManager && !hasLegacyError())
             {
                writeLog(masterMsg);
             }
             throw new DeferredLegacyErrorException();
          }

          log(masterMsg, silent);

          if ((!silent && !isHeadless()) || force)
          {
             if (errorWriter != null)
             {
                if (modal && server.isSystemAlertBoxes())
                {
                   errorWriter.messageBox(masterMsg, "Error", force);
                }
                else
                {
                   if (!isFromLogManager && !hasLegacyError())
                   {
                      writeLog(masterMsg);
                   }
                   errorWriter.message(masterMsg);
                }
             }
             else
             {
                if (!isFromLogManager && !hasLegacyError())
                {
                   writeLog(masterMsg);
                }
             }

             addRaisedCondition(num);
          }
       }

       /**
        * Manufacture warning text based on the given parameters.
        *
        * @param   id
        *          The id of the message. The associated text is filled with {@code params} tokens.
        * @param   params
        *          The parameters for the message.
        */
       public static void recordOrShowWarning(int id, String... params)
       {
          ErrorManager.recordOrShowError(new int[] { id },
                                         new String[] { replaceTokens(id, params) },
                                         true,   // modal
                                         false,  // no prefix
                                         false,  // error
                                         false,  // not as message
                                         true);  // add final dot
       }
       /**
        * Manufacture one or more warning texts based on the given parameters.
        * <p>
        * The error message(s) will be prefixed with <code>**</code> depending on the
        * <code>prefix</code> parameter and the <code>pending</code> error flag will be set to the
        * value specified by the <code>isError</code> flag.
        *
        * @param    num
        *           The error number.
        * @param    text
        *           The text of the error message.
        * @param    prefix
        *           <code>true</code> to prepend a double asterisk prefix to the formatted error
        *           message, <code>false</code> to omit this text.
        * @param    isError
        *           The new value for the <code>pending</code> error flag.
        * @param    asMsg
        *           <code>true</code> to use the given text as the message text else
        *           <code>false</code> to use buildErrorText.
        * @param    addDot
        *           <code>true</code> if dot(s) are added to the end of the message(s).
        */
       public static void recordOrShowWarning(int      num,
                                              String   text,
                                              boolean  prefix,
                                              boolean  isError,
                                              boolean  asMsg,
                                              boolean  addDot)
       {
          // TODO: Currently this method just forwards the call to recordOrShowError(),
          //       unless warnings are suppressed for the session.
          //       Depending on the expected semantics of the method, its name may change to
          //       showWarning.
          boolean suppressWarningCode = work.get().suppressWarningsSet.contains(num);

          if (!isSuppressWarnings() && !suppressWarningCode)
          {
             recordOrShowError(new int[] {num}, new String[] {text}, true, prefix, isError, asMsg, addDot);
          }
       }

       /**
        * Manufacture one or more warning texts based on the given parameters.
        * <p>
        * The error message(s) will be prefixed with <code>**</code> depending on the
        * <code>prefix</code> parameter and the <code>pending</code> error flag will be set to the
        * value specified by the <code>isError</code> flag.
        * <p>
        * Used by LOG-MANAGER exclusively.
        *
        * @param    num
        *           The error number.
        * @param    text
        *           The text of the error message.
        * @param    prefix
        *           <code>true</code> to prepend a double asterisk prefix to the formatted error
        *           message, <code>false</code> to omit this text.
        */
       static void recordOrShowLogManagerWarning(int num, String text, boolean prefix)
       {
          // TODO: Currently this method just forwards the call to recordOrShowError(),
          //       unless warnings are suppressed for the session.
          //       Depending on the expected semantics of the method, its name may change to
          //       showWarning.
          if (!isSuppressWarnings())
          {
             recordOrShowError(new int[] {num}, new String[] {text}, true, prefix, false, false, false, false,
                               true);
          }
       }

       /**
        * Manufacture error text based on the given parameters, if silent error mode is enabled, then
        * this error number and the generated error text will be stored in the error list, otherwise
        * this generated error text will be used to display modal or modeless box in the UI.
        *
        * @param   id
        *          The id of the message. The associated text is filled with {@code params} tokens.
        * @param   params
        *          The parameters for the message.
        */
       public static void recordOrShowError(int id, String... params)
       {
          ErrorManager.recordOrShowError(new int[] { id },
                                         new String[] { replaceTokens(id, params) },
                                         true,   // modal
                                         false,  // no prefix
                                         true,   // error
                                         false,  // not as message
                                         true);  // add final dot
       }

       /**
        * Manufacture error text based on the given parameters, if silent error mode is enabled, then
        * this error number and the generated error text will be stored in the error list, otherwise
        * this generated error text will be used to display modal or modeless box in the UI.
        *
        * @param   id
        *          The id of the message. The associated text is filled with {@code params} tokens.
        * @param   isError
        *          The new value for the <code>pending</code> error flag.
        * @param   params
        *          The parameters for the message.
        */
       public static void recordOrShowError(int id, boolean isError, String... params)
       {
          ErrorManager.recordOrShowError(new int[] { id },
                                         new String[] { replaceTokens(id, params) },
                                         true,    // modal
                                         false,   // no prefix
                                         isError, // error
                                         false,   // not as message
                                         true);   // add final dot
       }

       /**
        * Manufacture error text based on the given parameters, if silent error mode is enabled, then
        * this error number and the generated error text will be stored in the error list, otherwise
        * this generated error text will be thrown (and execution of the procedure halted).
        *
        * @param   id
        *          The id of the message. The associated text is filled with {@code params} tokens.
        * @param    isModal
        *           <code>true</code> if message will be shown in modal dialog.
        * @param    isError
        *           The new value for the <code>pending</code> error flag.
        * @param   params
        *          The parameters for the message. If there are less than two token-parameters
        *          use empty Strings ({@code ""}) to make the compiler select the proper signature.
        */
       public static void recordOrShowError(int id, boolean isError, boolean isModal, String... params)
       {
          ErrorManager.recordOrShowError(new int[] { id },
                                         new String[] { replaceTokens(id, params) },
                                         isModal,    // not modal
                                         false,      // no prefix
                                         isError,    // not an error
                                         false,      // not as message
                                         true);      // add final dot
       }

       /**
        * Manufacture one or more error texts based on the given parameters, if silent error mode is
        * enabled, then these error numbers and the respective generated error text messages will be
        * stored in the error list, otherwise this generated error texts will be used to display
        * modal or modeless box in the UI.
        * <p>
        * The error message(s) will be prefixed with <code>**</code> depending on the
        * <code>prefix</code> parameter and the <code>pending</code> error flag will be set to the
        * value specified by the <code>isError</code> flag.
        * <p>
        * This is a specific method for errors related to the database. Such errors can influence
        * the value of the isError flag, for example a CATCH block in the current block will
        * force the isError flag to become <code>true</code>.
        * <p>
        * This method will call the recordOrShowError() method with the isError parameter set to
        * <code>false</code> and isDatabaseError set to <code>true</code>.
        *
        * @param   id
        *          The error number.
        * @param   message
        *          The error message.
        * @param   modal
        *          <code>true</code> if message will be shown in modal dialog.
        * @param   prefix
        *          <code>true</code> to prepend a double asterisk prefix to the formatted error
        *          message, <code>false</code> to omit this text.
        */
       public static void recordOrShowDatabaseError(int id, String message, boolean modal, boolean prefix)
       {
          recordOrShowDatabaseError(new int[] { id }, new String[] { message }, modal, prefix);
       }

       /**
        * Manufacture one or more error texts based on the given parameters, if silent error mode is
        * enabled, then these error numbers and the respective generated error text messages will be
        * stored in the error list, otherwise this generated error texts will be used to display
        * modal or modeless box in the UI.
        * <p>
        * The error message(s) will be prefixed with <code>**</code> depending on the
        * <code>prefix</code> parameter and the <code>pending</code> error flag will be set to the
        * value specified by the <code>isError</code> flag.
        * <p>
        * This is a specific method for errors related to the database. Such errors can influence
        * the value of the isError flag, for example a CATCH block in the current block will
        * force the isError flag to become <code>true</code>.
        * <p>
        * This method will call the recordOrShowError() method with the isError parameter set to
        * <code>false</code> and isDatabaseError set to <code>true</code>.
        *
        * @param   ids
        *          The error numbers.
        * @param   messages
        *          The error messages.
        * @param   modal
        *          <code>true</code> if message will be shown in modal dialog.
        * @param   prefix
        *          <code>true</code> to prepend a double asterisk prefix to the formatted error
        *          message, <code>false</code> to omit this text.
        */
       public static void recordOrShowDatabaseError(int[] ids, String[] messages, boolean modal, boolean prefix)
       {
          recordOrShowError(ids, messages, modal, prefix, false, false, true, false, false, true);
       }

       /**
        * Display error text and throw an exception based upon the given exception.
        * Manufactures error text based on the given parameters, if silent error mode is enabled, then
        * this error number and the generated error text will be stored in the error list, otherwise
        * this generated error text will be used to display modal or modeless box in the UI.
        *
        * @param   id
        *          The id of the message. The associated text is filled with {@code params} tokens.
        * @param   params
        *          The parameters for the message.
        */
       public static void throwError(int id, String... params)
       {
          ErrorManager.throwError(new NumberedException(replaceTokens(id, params), id, false), isSilent());
       }

       /**
        * Manufacture error text based on the given parameters; after displaying
        * the error text on the client, a pause is forced before stopping the
        * application.
        *
        * @param    num
        *           The error number.
        * @param    text
        *           The text of the error message.
        *
        * @throws   StopConditionException
        *           always, unless the error manager is in ignore mode.
        */
       public static void showErrorAndAbend(int num, String text)
       throws StopConditionException
       {
          if (isIgnore())
             return;

          String errmsg = buildAbendText(num, text);
          try
          {
             if (!isHeadless())
             {
                // display the error message (because of pause processing, an
                // endkey, error... even stop condition can be generated here)
                displayError(errmsg);

                if (errorWriter != null)
                {
                   errorWriter.pause();
                }
             }
          }
          finally
          {
             postprocessOnAbend(errmsg);
          }
       }

       /**
        * Manufacture error text based on the given parameters; After displaying
        * the error text on the client, a pause is forced ignoring the NO-ERROR
        * clause and headless modes. At the end, a StopErrorCondition is thrown.
        *
        * @param    num
        *           The error number.
        * @param    text
        *           The text of the error message.
        * @param    prefix
        *           <code>true</code> to prepend a double asterisk prefix to the
        *           formatted error message, <code>false</code> to omit this text.
        *
        * @throws   StopConditionException
        *           always, unless the error manager is in ignore mode.
        */
       public static void showErrorAndAbend(int num, String text, boolean prefix)
       throws StopConditionException
       {
          if (isIgnore())
          {
             return;
          }

          String errmsg = buildErrorText(num, text, prefix, true);
          try
          {
             recordOrShowError(new int[] { num },
                               new String[] { text },
                               true,
                               prefix,
                               false,
                               false,
                               true,
                               true,
                               false);
          }
          finally
          {
             postprocessOnAbend(errmsg);
          }
       }

       /**
        * Manufacture error text based on the given parameters; after displaying the error text on
        * the client, the application is stopping.
        *
        * @param    num
        *           The error number.
        * @param    text
        *           The text of the error message.
        *
        * @throws   StopConditionException
        *           always, unless the error manager is in ignore mode.
        */
       public static void conditionalShowErrorAndAbend(int num, String text)
       throws StopConditionException
       {
          // If the NO-ERROR option was specified this method just silently retuns doing nothing
          if (isSilentError())
          {
             return;
          }

          if (isIgnore())
          {
             return;
          }

          String errmsg = buildAbendText(num, text);
          try
          {
             if (!isHeadless())
             {
                // display the error message (because of pause processing, an
                // endkey, error... even stop condition can be generated here)
                displayError(errmsg);
             }
          }
          finally
          {
             postprocessOnAbend(errmsg);
          }
       }

       /**
        * Perform postprocessing for {@link #showErrorAndAbend(int, String)}.
        *
        * @param   errmsg
        *          Message text.
        */
       private static void postprocessOnAbend(String errmsg)
       {
          throw new StopConditionException(errmsg);
       }

       /**
        * Manufacture one or more error texts based on the given parameters, if
        * silent error mode is enabled, then these error numbers and the
        * respective generated error text messages will be stored in the error
        * list, otherwise this generated error texts will be used to display a
        * modal, alert (error) box in the UI.<p>
        * After error messages has been processed, a STOP condition is raised.
        *
        * @param    num
        *           The error numbers.
        * @param    text
        *           The text of the error messages.
        *
        * @throws   StopConditionException
        *           always, unless the error manager is in ignore mode.
        */
       public static void showErrorAndAbend(int[] num, String[] text)
       throws StopConditionException
       {
          if (isIgnore())
          {
             return;
          }

          try
          {
             recordOrShowError(num, text, true);
          }
          finally
          {
             postprocessOnAbend(text);
          }
       }

       /**
        * Raise an error condition and optionally record the given error (in silent mode).
        * <p>
        * In silent mode, the {@link #silent} worker will catch the exception and it will not be
        * seen by the converted code.
        *
        * @param    num
        *           The error number.
        * @param    text
        *           The text of the error message.
        *
        * @throws   ErrorConditionException
        */
       public static void recordOrThrowError(int num, String text)
       throws ErrorConditionException
       {
          recordOrThrowError(num, text, true);
       }

       /**
        * Raise an error condition and optionally record the given error (in silent mode).
        * <p>
        * In silent mode, the {@link #silent} worker will catch the exception and it will not be
        * seen by the converted code.
        *
        * @param    num
        *           The error number.
        * @param    text
        *           The text of the error message.
        * @param    prefix
        *           <code>true</code> to prepend a double asterisk prefix to the
        *           formatted error message, <code>false</code> to omit this text.
        *
        * @throws   ErrorConditionException
        */
       public static void recordOrThrowError(int num, String text, boolean prefix)
       {
          recordOrThrowError(new int[] { num }, new String[] { text }, prefix, false);
       }

       /**
        * Raise an error condition and optionally record the given error (in silent mode).
        * <p>
        * In silent mode, the {@link #silent} worker will catch the exception and it will not be
        * seen by the converted code.
        *
        * @param    num
        *           The error number.
        * @param    text
        *           The text of the error message.
        * @param    prefix
        *           <code>true</code> to prepend a double asterisk prefix to the
        *           formatted error message, <code>false</code> to omit this text.
        *
        * @throws   ErrorConditionException
        */
       static void recordOrThrowLogManagerError(int num, String text, boolean prefix)
       {
          recordOrThrowError(new int[] { num }, new String[] { text }, prefix, false, true, true);
       }

       /**
        * Raise an error condition and optionally record the given error (in silent mode).
        * <p>
        * 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
        *           <code>true</code> to prepend a double asterisk prefix to the
        *           formatted error message, <code>false</code> to omit this text.
        *
        * @throws   ErrorConditionException
        */
       public static void recordOrThrowError(int[] nums, String[] texts, boolean prefix)
       throws ErrorConditionException
       {
          recordOrThrowError(nums, texts, prefix, false);
       }

       /**
        * Raise an error condition and optionally record the given error (in silent mode).
        * <p>
        * 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
        *           <code>true</code> to prepend a double asterisk prefix to the
        *           formatted error message, <code>false</code> to omit this text.
        * @param    asMsg
        *           <code>true</code> to use the given text as the message text else
        *           <code>false</code> to use buildErrorText.
        *
        * @throws   ErrorConditionException
        */
       public static void recordOrThrowError(int[]    nums,
                                             String[] texts,
                                             boolean  prefix,
                                             boolean  asMsg)
       throws ErrorConditionException
       {
          recordOrThrowError(nums, texts, prefix, asMsg, true);
       }

       /**
        * Raise an error condition and optionally record the given error (in silent mode).
        * <p>
        * 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
        *           <code>true</code> to prepend a double asterisk prefix to the
        *           formatted error message, <code>false</code> to omit this text.
        * @param    asMsg
        *           <code>true</code> to use the given text as the message text else
        *           <code>false</code> to use buildErrorText.
        * @param    addDot
        *           <code>true</code> 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
       {
          recordOrThrowError(nums, texts, prefix, asMsg, addDot, false);
       }

       /**
        * Raise an error condition and optionally record the given error (in silent mode).
        * <p>
        * 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
        *           <code>true</code> to prepend a double asterisk prefix to the
        *           formatted error message, <code>false</code> to omit this text.
        * @param    asMsg
        *           <code>true</code> to use the given text as the message text else
        *           <code>false</code> to use buildErrorText.
        * @param    addDot
        *           <code>true</code> if a dot is added to the end of the message.
        * @param    isFromLogManager
        *           Flag to indicate the error comes from LOG-MANAGER.
        *
        * @throws   ErrorConditionException
        */
       private static void recordOrThrowError(int[]    nums,
                                              String[] texts,
                                              boolean  prefix,
                                              boolean  asMsg,
                                              boolean  addDot,
                                              boolean  isFromLogManager)
       throws ErrorConditionException
       {
          if (isIgnore())
             return;

          boolean silent = isSilentError();
          boolean manageLegacyError = server.mustThrowLegacyError();
          boolean mustManageError = !manageLegacyError && server.mustManageLegacyError();
          boolean forceExplicitException = false;

          if (!manageLegacyError && nums[0] == 132 && !ProcedureManager.getProcedureHelper().isBlockLevelUndoThrow())
          {
             forceExplicitException = true;
             manageLegacyError = server.mustThrowLegacyError(true);
          }

          boolean throwError = false;

          for (int i = 0; i < nums.length; i++)
          {
             int num = nums[i];
             String text = texts[i];
             boolean last = i == nums.length - 1;

             String errmsg = asMsg ? text : buildErrorText(num, text, prefix, addDot);

             if (!silent && !isFromLogManager && !hasLegacyError())
             {
                writeLog(errmsg);
             }
             log(errmsg, silent);

             if (silent)
             {
                // this may reset the error list so it must be done BEFORE we add
                // the new error to the list
                server.setPending(true);

                server.addRecord(new ErrorEntry(num, errmsg, false));

                // this will be caught by the worker method silent()
                if (last)
                {
                   if (serverSide)
                   {
                      throw new ErrorConditionException(num, errmsg);
                   }
                   else
                   {
                      server.throwErrorConditionException(num, errmsg);
                   }
                }
             }
             else if (isHeadless())
             {
                if (last)
                {
                   if (mustManageError || (manageLegacyError && msaManager.isMsaContext()))
                   {
                      TransactionManager.triggerErrorInCaller(num, text, true, prefix);
                   }
                   throw new ErrorConditionException(num, errmsg, new NumberedException(text, num));
                }
             }
             else
             {
                // TODO: if the current block can catch this condition OR if the current block has
                // ON ERROR UNDO, THROW OR if BLOCK-LEVEL or ROUTINE-LEVEL is active for the current
                // block (Depending on block type), then:
                // - don't display
                // - morph this into a SysError and throw it
                try
                {
                   if (manageLegacyError)
                   {
                      throwError = true;
                      server.recordForLegacyThrow(errmsg, num);
                   }
                   else
                   {
                      // display the error message (because of pause processing, an
                      // endkey, error... even stop condition can be generated here)
                      // it's already been logged earlier, so marking it for not logging
                      displayError(errmsg, false, null, true, isFromLogManager);
                   }
                }

                catch (ConditionException err)
                {
                   if (serverSide && TransactionManager.isNestedMode())
                   {
                      // honor nested mode here
                      TransactionManager.saveNestedError(err);
                   }
                   else
                   {
                      throw err;
                   }
                }

                if (last && throwError)
                {
                   work.get().forceExplicit = forceExplicitException;
                   throw new DeferredLegacyErrorException();
                }

                if (last && !isWarningMode())
                {
                   if (mustManageError)
                   {
                      TransactionManager.triggerErrorInCaller(num, text, true, prefix);
                   }

                   // raise the error condition
                   throw new ErrorConditionException(num, errmsg, new NumberedException(text, nums));
                }
             }
          }
       }

       /**
        * Raise an error condition and optionally record the given error (in silent mode).
        * <p>
        * In silent mode, the {@link #silent} worker will catch the exception and it will not be
        * seen by the converted code.
        *
        * @param    num
        *           The error number.
        * @param    text
        *           The text of the error message.
        * @param    prefix
        *           <code>true</code> to prepend a double asterisk prefix to the
        *           formatted error message, <code>false</code> to omit this text.
        * @param    asMsg
        *           <code>true</code> to use the given text as the message text else
        *           <code>false</code> to use buildErrorText.
        *
        * @throws   ErrorConditionException
        */
       public static void recordOrThrowError(int num, String text, boolean prefix, boolean asMsg)
       {
          recordOrThrowError(new int[] { num }, new String[] { text }, prefix, asMsg);
       }

       /**
        * Raise an error condition and optionally record a chain of errors (in silent mode).
        * <p>
        * In silent mode, the {@link #silent} worker will catch the exception and it will not be
        * seen by the converted code.
        * <p>
        * In silent mode, the error messages and numbers are extracted from <code>error</code>
        * and any other instances of <code>NumberedException</code> present in the chain of root
        * cause exceptions.
        * <p>
        * The first error entry recorded is derived from the given error number and text, however.
        *
        * @param   num
        *          The error number.
        * @param   text
        *          The text of the error message.
        * @param   error
        *          Exception containing an error number, message, and optionally
        *          a root cause chain.
        */
       public static void recordOrThrowError(int num,
                                             String text,
                                             NumberedException error)
       {
          recordOrThrowError(new NumberedException(text, num, error));
       }

       /**
        * Manufacture error text based on the given parameters, if silent error mode is enabled, then
        * this error number and the generated error text will be stored in the error list, otherwise
        * this generated error text will be thrown (and execution of the procedure halted).
        *
        * @param   id
        *          The id of the message. The associated text is filled with {@code params} tokens.
        * @param   params
        *          The parameters for the message. If there are less than two token-parameters
        *          use empty Strings ({@code ""}) to make the compiler select the proper signature.
        */
       public static void recordOrThrowError(int id, String... params)
       {
          recordOrThrowError(new int[] { id }, new String[] { replaceTokens(id, params) }, false, false, true);
       }

       /**
        * Raise an error condition and optionally record a chain of errors (in silent mode).
        * <p>
        * In silent mode, the {@link #silent} worker will catch the exception and it will not be
        * seen by the converted code.
        * <p>
        * In silent mode, the error messages and numbers are extracted from <code>error</code>
        * and any other instances of <code>NumberedException</code> present in the chain of root
        * cause exceptions.
        *
        * @param   error
        *          Exception containing an error number, message, and optionally
        *          a root cause chain.
        */
       public static void recordOrThrowError(NumberedException error)
       {
          if (isIgnore())
          {
             return;
          }

          boolean silent = isSilentError();

          log("stack trace follows", error, silent);

          if (silent)
          {
             // this may reset the error list so it must be done BEFORE we add
             // the new error(s) to the list
             server.setPending(true);

             for (ErrorEntry errorEntry : compileErrorEntries(error))
             {
                server.addRecord(errorEntry);
             }
          }

          throwError(error, silent);
       }

       /**
        * Throws an {@link ErrorConditionException} and if in silent mode, it sets the
        * <code>pending</code> flag to <code>true</code>.  NO error information (messages or
        * error numbers) will be recorded in either case (i.e. silent mode or not).  In silent
        * mode the <code>ERROR-STATUS:ERROR</code> will be <code>true</code> but the other "records"
        * of the error that normally exist, will not exist. No message will be displayed.  Because
        * no message is ever displayed or recorded, the normal error number/message processing
        * is not executed.
        * <p>
        * This only works on the server side.
        *
        * @param    msg
        *           The text to use in throwing the error exception.
        * @param    isFromLogManager
        *           Flag to indicate the error comes from LOG-MANAGER.
        */
       public static void noRecordOrShowError(String msg, boolean isFromLogManager)
       {
          WorkArea wa = work.obtain();

          if (isSilentWorker(wa))
          {
             // ERROR-STATUS:ERROR flag is set, but no error is recorded
             setPendingWorker(wa, true);
             return;
          }

          if (isIgnore())
             return;

          // show the ERROR message
          if (!isHeadless() && errorWriter != null)
          {
             if (server.isSystemAlertBoxes())
             {
                errorWriter.messageBox(msg, "Error", false);
             }
             else
             {
                errorWriter.message(msg);
             }
          }
       }

       /**
        * Throws an {@link ErrorConditionException} and if in silent mode, it sets the
        * <code>pending</code> flag to <code>true</code>.  NO error information (messages or
        * error numbers) will be recorded in either case (i.e. silent mode or not).  In silent
        * mode the <code>ERROR-STATUS:ERROR</code> will be <code>true</code> but the other "records"
        * of the error that normally exist, will not exist. No message will be displayed.  Because
        * no message is ever displayed or recorded, the normal error number/message processing
        * is not executed.
        * <p>
        * This only works on the server side.
        *
        * @param    msg
        *           The text to use in throwing the error exception.
        */
       public static void noRecordOrThrowError(String msg)
       {
          noRecordOrThrowErrorWorker(work.obtain(), msg);
       }

       /**
        * Display error text and throw an exception based upon the given
        * exception.  The error messages and numbers are extracted from
        * <code>error</code> and any other instances of
        * <code>NumberedException</code> present in the chain of root cause
        * exceptions.  The first error entry recorded is derived from the given
        * error number and text, however.
        *
        * @param   num
        *          The error number.
        * @param   text
        *          The text of the error message.
        * @param   error
        *          Exception containing an error number, message, and optionally
        *          a root cause chain.
        */
       public static void throwError(int num, String text, NumberedException error)
       {
          throwError(new NumberedException(text, num, error));
       }

       /**
        * Display error text and throw an exception based upon the given
        * exception.  The error messages and numbers are extracted from
        * <code>error</code> and any other instances of
        * <code>NumberedException</code> present in the chain of root cause
        * exceptions.
        *
        * @param   error
        *          Exception containing an error number, message, and optionally
        *          a root cause chain.
        */
       public static void throwError(NumberedException error)
       {
          throwError(error, isSilent());
       }

       /**
        * Display error text and throw an exception based upon the given exception.  The error
        * messages and numbers are extracted from <code>error</code> and any other instances of
        * <code>NumberedException</code> present in the chain of root cause exceptions.  The first
        * error entry recorded is derived from the given error number and text, however.
        * <p>
        * This version of the method allows explicit control over silent error mode processing. This
        * means that error processing which is normally suppressed, can be explicitly re-enabled
        * by passing {@code suppress} as {@code false} even if {@link #isSilentError} would return
        * {@code true}.  ONLY USE THIS IF YOU REALLY KNOW WHAT YOU ARE DOING. Bypassing silent error
        * mode is usually a bad idea.  There are some use cases in the 4GL where this is needed and
        * so that is why this method exists.
        *
        * @param   num
        *          The error number.
        * @param   text
        *          The text of the error message.
        * @param   error
        *          Exception containing an error number, message, and optionally a root cause chain.
        * @param   suppress
        *          {@code true} to suppress the display of error texts, else {@code false}.
        */
       public static void throwError(int num, String text, NumberedException error, boolean suppress)
       {
          throwError(new NumberedException(text, num, error), suppress);
       }

       /**
        * Display error text and throw an exception based upon the given exception.  The error
        * messages and numbers are extracted from <code>error</code> and any other instances of
        * <code>NumberedException</code> present in the chain of root cause exceptions.
        * <p>
        * This version of the method allows explicit control over silent error mode processing. This
        * means that error processing which is normally suppressed, can be explicitly re-enabled
        * by passing {@code suppress} as {@code false} even if {@link #isSilentError} would return
        * {@code true}.  ONLY USE THIS IF YOU REALLY KNOW WHAT YOU ARE DOING. Bypassing silent error
        * mode is usually a bad idea.  There are some use cases in the 4GL where this is needed and
        * so that is why this method exists.
        *
        * @param   error
        *          Exception containing an error number, message, and optionally a root cause chain.
        * @param   suppress
        *          {@code true} to suppress the display of error texts, else {@code false}.
        */
       public static void throwError(NumberedException error, boolean suppress)
       {
          if (!isHeadless() && !suppress)
          {
             // display the messages
             Iterator<String> iter = errorTexts(error);

             // check if in silent mode
             boolean wasSilent = ErrorManager.isSilent();
             try
             {
                // temporarily disable silent mode
                ErrorManager.setSilent(false);
                while (iter.hasNext())
                {
                   try
                   {
                      // because of possible pause processing, an endkey, error...
                      // even stop condition can be generated here
                      displayError(iter.next());
                   }
                   catch (ConditionException err)
                   {
                      if (serverSide && TransactionManager.isNestedMode())
                      {
                         // honor nested mode here
                         TransactionManager.saveNestedError(err);
                      }
                      else
                      {
                         throw err;
                      }
                   }
                }
             }
             finally
             {
                // restore silent mode if needed
                if (wasSilent)
                {
                   ErrorManager.setSilent(true);
                }
             }
          }

          if (!isWarningMode())
          {
             // raise the error condition
             throw new ErrorConditionException(error).setForce(!suppress);
          }
          else
          {
             addRaisedCondition(error.getNumbers());
          }
       }

       /**
        * Throws 4GL error #5890.
        *
        * @param    method
        *           The method name
        * @param    reason
        *           The error reason
        */
       public static void throwErrorWhileProcessingComponentProperty(String method, String reason)
       {
          recordOrShowError(5890,
                            "Error occuired while processing component property/method: " +
                            method + ".\n" + reason,
                            true);
       }

       /**
        * Throws 4GL error #5677.
        */
       public static void throwErrorUnableToSetComHandleProperty()
       {
          recordOrShowError(5677, "Unable to set com-handle property", true);
       }

       /**
        * Manufactures Progress compatible formatted error text based on the
        * given error data.  The given data is substituted into the following
        * string "** &#37;s. (&#37;i)".
        *
        * @param    num
        *           The error number substituted for &#37;i in the format string.
        * @param    text
        *           The text of the error message substituted for &#37;s in the
        *           format string.
        *
        * @return   The formatted text.
        */
       public static String buildErrorText(int num, String text)
       {
          return buildErrorText(num, text, true);
       }

       /**
        * Manufactures Progress compatible formatted error text based on the
        * given error data.  The given data is substituted into the following
        * string "** &#37;s. (&#37;i)".
        *
        * @param    num
        *           The error number substituted for &#37;i in the format string.
        * @param    text
        *           The text of the error message substituted for &#37;s in the
        *           format string.
        * @param    prefix
        *           <code>true</code> to prepend a double asterisk prefix to the
        *           formatted error message, <code>false</code> to omit this text.
        *
        * @return   The formatted text.
        */
       public static String buildErrorText(int num, String text, boolean prefix)
       {
          return buildErrorText(num, text, prefix, true);
       }

       /**
        * Manufactures Progress compatible formatted error text based on the
        * given error data.  The given data is substituted into the following
        * string "** &#37;s. (&#37;i)".
        *
        * @param    num
        *           The error number substituted for &#37;i in the format string.
        * @param    text
        *           The text of the error message substituted for &#37;s in the
        *           format string.
        * @param    prefix
        *           <code>true</code> to prepend a double asterisk prefix to the
        *           formatted error message, <code>false</code> to omit this text.
        * @param    addDot
        *           <code>true</code> if a dot is added to the end of the message.
        *
        * @return   The formatted text.
        */
       public static String buildErrorText(int num, String text, boolean prefix, boolean addDot)
       {
          // build up the message with StringBuilder rather than String.format, as this code may
          // be called quite often in normal control flow, and String.format is expensive
          StringBuilder buf = (prefix ? new StringBuilder("** ") : new StringBuilder());
          buf.append(text);

          // SysError with error number 0 does not add this
          if (num > 0)
          {
             buf.append(addDot ? ". (" : " (");
             buf.append(num);
             buf.append(')');
          }

          return buf.toString();
       }

       /**
        * Manufactures Progress compatible formatted abend error text based on the
        * given error data.  The given data is substituted into the following
        * string "&#37;s. (&#37;i)".
        *
        * @param    num
        *           The error number substituted for &#37;i in the format string.
        * @param    text
        *           The text of the error message substituted for &#37;s in the
        *           format string.
        *
        * @return   The formatted text.
        */
       public static String buildAbendText(int num, String text)
       {
          return String.format("%s. (%d)", text, num);
       }

       /**
        * Adds an error entry to the error list.
        *
        * @param    num
        *           The error number.
        * @param    text
        *           The text of the error message.
        * @param    errFlag
        *           If <code>false</code> this should add the number and text to
        *           the error list but not set the error flag.  If
        *           <code>true</code> the error flag should also be set.
        */
       public static void addError(int num, String text, boolean errFlag)
       {
          addError(num, text, errFlag, true);
       }

       /**
        * Adds an error entry to the error list.
        *
        * @param    num
        *           The error number.
        * @param    text
        *           The text of the error message.
        * @param    errFlag
        *           If <code>false</code> this should add the number and text to
        *           the error list but not set the error flag.  If
        *           <code>true</code> the error flag should also be set.
        * @param    prefix
        *           <code>true</code> to prepend a double asterisk prefix to the
        *           formatted error message, <code>false</code> to omit this text.
        */
       public static void addError(int num, String text, boolean errFlag, boolean prefix)
       {
          addErrorWorker(work.obtain(), num, text, errFlag, prefix);
       }

       /**
        * Returns the state of the current error flag which denotes if any error has occurred
        * while silent mode is active.  If silent error mode is not active, then this method always
        * will return <code>false</code>.
        *
        * @return    State of the current error flag.
        */
       public static boolean isPendingError()
       {
          return server.isSilent() ? server.isPending() : false;
       }

       /**
        * Obtain the current ERROR:ERROR-STRING attribute.
        *
        * @return  the current value.
        */
       public static character errorString()
       {
          UnimplementedFeature.missing("ERROR-STATUS:ERROR-STRING");
          return new character();
       }

       /**
        * Sets the ERROR:ERROR-STRING attribute.
        *
        * @param   errStr
        *          The new value.
        */
       public static void changeErrorString(String errStr)
       {
          // TODO: 4GL reports this as invalid attribute, but it was found in customer code logic...
          // maybe is related to appserver/rest/web calls?
          UnimplementedFeature.missing("ERROR-STATUS:ERROR-STRING");
       }

       /**
        * Sets the ERROR:ERROR-STRING attribute.
        *
        * @param   errStr
        *          The new value.
        */
       public static void changeErrorString(character errStr)
       {
          UnimplementedFeature.missing("ERROR-STATUS:ERROR-STRING");
       }

       /**
        * Returns the state of the error flag which was set during the last silent mode processing,
        * even if silent error mode is not currently active. If silent error mode was never used,
        * then <code>false</code> will be returned.
        *
        * @return    Current state of the error flag.
        */
       @LegacyAttribute(name = "ERROR", ignore = true)
       public static logical isError()
       {
          return logical.of(work.obtain().errorStatus.error);
       }

       /**
        * Returns the state of the error flag which was set during the last silent mode processing,
        * even if silent error mode is not currently active. If silent error mode was never used,
        * then <code>false</code> will be returned.
        *
        * @return    Current state of the error flag.
        */
       public static logical error()
       {
          return isError();
       }

       /**
        * Returns the number of the errors (recorded in the error list) which have occurred during
        * the last usage of silent error mode.
        *
        * @return    Current number of errors in the error list if silent mode has been enabled.
        *            This will be 0 if no errors have occurred since the last use of silent error
        *            or if silent error mode has never been used.
        */
       public static integer numErrors()
       {
          ArrayList<Object> errors = work.obtain().errorStatus.errorList;
          return new integer(errors == null ? 0 : errors.size());
       }

       /**
        * Undocumented feature encountered in customer code. After all, NUM-MESSAGES (KW_NUM_MSG)
        * attribute seems to be writable.
        *
        * TODO: investigate if any number can be set or if there are some constraints:
        *    Ex: only 0 can be assigned, meaning some kind of clear error status.
        *
        * @param   errCount
        *          New value.
        */
       @LegacyAttribute(name = "NUM-MESSAGES", setter = true)
       public static void setNumErrors(int64 errCount)
       {
          UnimplementedFeature.missing("ERROR-STATUS:NUM-MESSAGES not implemented.");
       }

       /**
        * Throw a StopConditionException condition exception, or differ it as a LegacyStop if
        * -catchStop startup parameter is enabled and CATCH for Progress.Lang.Stop or of of it's
        * subclasses was detected.
        *
        * @param    msg
        *           The error message.
        */
       public static void handleStopException(String msg)
       {
          handleStopException(new StopConditionException(msg));
       }

       /**
        * Throw a StopConditionException condition exception, or differ it as a LegacyStop if
        * -catchStop startup parameter is enabled and CATCH for Progress.Lang.Stop or of of it's
        * subclasses was detected.
        *
        * @param    msg
        *           The error message.
        */
       public static void handleStopException(object<? extends Stop> stopException)
       {
           if (isIgnore())
           {
              return;
           }

           boolean manageLegacyStop = server.mustThrowLegacyStop();

           if (!manageLegacyStop)
           {
              throw new StopConditionException("UNDO, THROW STOP FALLBACK");
           }

           LegacyStopException lex = new LegacyStopException(stopException);
           throw lex;
       }

       /**
        * Throw a StopConditionException condition exception, or differ it as a LegacyStop if
        * -catchStop startup parameter is enabled and CATCH for Progress.Lang.Stop or of it's
        * subclasses was detected.
        *
        * @param    msg
        *           The error message.
        */
       public static void handleStopException(StopConditionException ex)
       {
           if (isIgnore())
           {
              return;
           }

           boolean manageLegacyStop = server.mustThrowLegacyStop();

           if (manageLegacyStop)
           {
              server.recordForLegacyStop(ex);
              throw new DeferredLegacyStopException();
           }

           throw ex;
       }

       /**
        * Returns the Progress compatible error number associated with the error
        * entry at the given index in the error list.  If the list is empty or
        * if the index is invalid, 0 is returned.
        *
        * @param    index
        *           Position in the error list from which to return the entry.
        *           This is 1-based list index, where 1 is the oldest error number
        *           and highest number is the most recent error number.
        *
        * @return   The error number at that index or 0 if the list is empty or
        *           if the index is invalid.
        */
       public static integer getErrorNumber(integer index)
       {
          if (index.isUnknown())
          {
             return new integer();
          }

          return getErrorNumber(index.intValue());
       }

       /**
        * Returns the Progress compatible error number associated with the error
        * entry at the given index in the error list.  If the list is empty or
        * if the index is invalid, 0 is returned.
        *
        * @param    index
        *           Position in the error list from which to return the entry.
        *           This is 1-based list index, where 1 is the oldest error number
        *           and highest number is the most recent error number.
        *
        * @return   The error number at that index or 0 if the list is empty or
        *           if the index is invalid.
        */
       public static integer getErrorNumber(int index)
       {
          int result = 0;

          if (index > 0 && index <= server.getSize())
          {
             ErrorEntry ee = (ErrorEntry) server.getRecord(--index);

             result = ee.num;
          }

          return new integer(result);
       }

       /**
        * Java implementation for the _msg() function.
        * <p>
        * This will return the error number found on the error stack at the given position,
        * or 0 if there isn't any error in the stack at the given position.
        * <p>
        * This implementation always will return 0 for any non-positive number (0 or negative).
        * <p>
        * In the 4GL, some negative numbers will return what appears to be a random number.
        * This is most likely a flaw in the 4GL implementation where there is improper boundary
        * testing and an invalid index value is used to dereference memory that is not associated
        * with the error stack.
        * <p>
        * As such the behavior is non-deterministic and also is a likely security flaw.
        * <p>
        * This implementation is deliberately not duplicating this bad behavior and it seems unlikely
        * that an application would be coded to rely upon it.
        *
        * @param  index
        *         The position of the stack from which we want to get the value error number.
        *         This should be greater than 0 for valid results.
        *         This is 1-based stack offset, where 1 is the most recent error number
        *         and highest number is oldest error number in the stack.
        *
        * @return The error number found at the given position in the error stack, or 0 if there
        *         isn't any error number at the specified position
        */
       public static integer getErrorNumberAtIndex(long index)
       {
          return getErrorNumberAtIndex(new int64(index));
       }

       /**
        * Java implementation for the _msg() function.
        * <p>
        * This will return the error number found on the error stack at the given position,
        * or 0 if there isn't any error in the stack at the given position.
        * <p>
        * This implementation always will return 0 for any non-positive number (0 or negative).
        * <p>
        * In the 4GL, some negative numbers will return what appears to be a random number.
        * This is most likely a flaw in the 4GL implementation where there is improper boundary
        * testing and an invalid index value is used to dereference memory that is not associated
        * with the error stack.
        * <p>
        * As such the behavior is non-deterministic and also is a likely security flaw.
        * <p>
        * This implementation is deliberately not duplicating this bad behavior and it seems unlikely
        * that an application would be coded to rely upon it.
        *
        * @param  index
        *         The position of the stack from which we want to get the value error number.
        *         This should be greater than 0 for valid results.
        *         This is 1-based stack offset, where 1 is the most recent error number
        *         and highest number is oldest error number in the stack.
        *
        * @return The error number found at the given position in the error stack, or 0 if there
        *         isn't any error number at the specified position
        */
       public static integer getErrorNumberAtIndex(integer index)
       {
          return getErrorNumberAtIndex(new int64(index));
       }

       /**
        * Java implementation for the _msg() function.
        * <p>
        * This will return the error number found on the error stack at the given position,
        * or 0 if there isn't any error in the stack at the given position.
        * <p>
        * This implementation always will return 0 for any non-positive number (0 or negative).
        * <p>
        * In the 4GL, some negative numbers will return what appears to be a random number.
        * This is most likely a flaw in the 4GL implementation where there is improper boundary
        * testing and an invalid index value is used to dereference memory that is not associated
        * with the error stack.
        * <p>
        * As such the behavior is non-deterministic and also is a likely security flaw.
        * <p>
        * This implementation is deliberately not duplicating this bad behavior and it seems unlikely
        * that an application would be coded to rely upon it.
        *
        * @param  index
        *         The position of the stack from which we want to get the value error number.
        *         This should be greater than 0 for valid results.
        *         This is 1-based stack offset, where 1 is the most recent error number
        *         and highest number is oldest error number in the stack.
        *
        * @return The error number found at the given position in the error stack, or 0 if there
        *         isn't any error number at the specified position
        */
       public static integer getErrorNumberAtIndex(int64 index)
       {
          // default value for incorrect index or not existent value for it is 0
          integer result = new integer(0);

          int intIdx = index.intValue();

          // check if index is valid
          if (intIdx > 0 && intIdx <= ERR_CONDITIONS_CAPACITY)
          {
             LinkedList<Integer> conds = getRaisedConditionsWorker(work.obtain());
             if (intIdx <= conds.size())
             {
                Integer errorNumber = conds.get(intIdx - 1);

                // check if there are error number exists for specified index
                if (errorNumber != null)
                {
                   result = new integer(errorNumber);
                }
             }
          }

          return result;
       }

       /**
        * Returns the Progress compatible error text associated with the error
        * entry at the given index in the error list.  If the list is empty or
        * if the index is invalid, an empty string "" is returned.
        *
        * @param    index
        *           Position in the error list from which to return the entry.
        *
        * @return   The error text at that index or the empty string "" if the
        *           list is empty or if the index is invalid.
        */
       public static character getErrorText(integer index)
       {
          if (index.isUnknown())
          {
             return new character();
          }

          return getErrorText(index.intValue());
       }

       /**
        * Returns the Progress compatible error text associated with the error
        * entry at the given index in the error list.  If the list is empty or
        * if the index is invalid, an empty string "" is returned.
        *
        * @param    index
        *           Position in the error list from which to return the entry.
        *
        * @return   The error text at that index or the empty string "" if the
        *           list is empty or if the index is invalid.
        */
       public static character getErrorText(int index)
       {
          String result = "";

          if (index > 0 && index <= server.getSize())
          {
             ErrorEntry ee = (ErrorEntry) server.getRecord(--index);

             result = ee.text;
          }

          return new character(result);
       }

       /**
        * Queries the state of <code>silent</code> variable.
        *
        * @return   <code>true</code> if silent mode is enabled, otherwise
        *           <code>false</code>.
        */
       public static boolean isSilent()
       {
          return server.isSilent();
       }

       /**
        * Sets the <code>silent</code> variable.
        *
        * @param    value
        *           The new state of the variable.
        */
       public static void setSilent(boolean value)
       {
          server.setSilent(value);
       }

       /**
        * Queries the state of <code>error</code> variable.
        *
        * @return   <code>true</code> if error flag is turned on, otherwise
        *           <code>false</code>.
        */
       public static boolean isErrorFlag()
       {
          return server.isErrorFlag();
       }

       /**
        * Sets the error variable.
        *
        * @param    value
        *           The new state of the variable.
        */
       @LegacyAttribute(name = "ERROR", setter = true)
       public static void setError(boolean value)
       {
          // this does not affect the recorded error messages or numbers.
          work.obtain().errorStatus.error = value;
       }

       /**
        * Sets the error variable.
        *
        * @param    value
        *           The new state of the variable.
        */
       public static void error(boolean value)
       {
          setError(value);
       }

       /**
        * Sets the error variable.
        *
        * @param    value
        *           The new state of the variable.
        */
       @LegacyAttribute(name = "ERROR", setter = true)
       public static void setError(logical value)
       {
          if (value.isUnknown())
          {
             // unknown value is ignored
             return;
          }

          setError(value.booleanValue());
       }

       /**
        * Sets the error variable.  This is a no-op, as the ERROR-STATUS:ERROR attribute
        * is read-only.
        *
        * @param    value
        *           The new state of the variable.
        */
       public static void error(logical value)
       {
          setError(value);
       }

       /**
        * Queries the state of <code>pendingError</code> variable.
        *
        * @return   <code>true</code> if pending error flag is turned on, otherwise
        *           <code>false</code>.
        */
       public static boolean isPending()
       {
          return server.isPending();
       }

       /**
        * Sets the <code>pendingError</code> variable.
        *
        * @param    value
        *           The new state of the variable.
        */
       public static void setPending(boolean value)
       {
          server.setPending(value);
       }

       /**
        * Determines if headless mode should be supported. In headless mode, no client-side output will occur.
        * If this is running on the client, then the return value is always {@code false} (because by definition
        * there is a client side even in batch mode).
        *
        * @return   <code>true</code> if the headless flag is turned on, otherwise <code>false</code>.
        */
       public static boolean isHeadless()
       {
          return serverSide ? isHeadlessWorker(work.obtain()) : false;
       }

       /**
        * Gets the size of the list of error records.
        *
        * @return   The size of the list.
        */
       public static int getSize()
       {
          return server.getSize();
       }

       /**
        * Adds a record to the list of error records.
        *
        * @param    record
        *           The object to add to the list of error records.
        */
       public static void addRecord(Object record)
       {
          server.addRecord(record);
       }

       /**
        * Gets a record from the list of error records.
        *
        * @param    index
        *           The record index.
        *
        * @return   The object associated with the given index in the list.
        */
       public static Object getRecord(int index)
       {
          return server.getRecord(index);
       }

       // GES_TODO: remove when all references are switched to ErrorHelper
       /**
        * Clears pending error status.
        */
       public static void clearPending()
       {
          work.obtain().pendingErrorStatus.initialize();
       }

       /**
        * Check if the top-level block is ROUTINE-LEVEL UNDO, THROW or the current program is
        * BLOCK-LEVEL UNDO, THROW.
        *
        * @return  see above.
        */
       public static boolean mustManageLegacyError()
       {
          return BlockManager.mustManageLegacyError();
       }
       /**
        * Check if the current ERROR condition must be raised as a legacy {@link SysError}.
        *
        * @param   forceBlockLevelUndoThrow
        *          Flag indicating that, when nothing else matches, a BLOCK-LEVEL UNDO, THROW exists by default.
        *
        * @return  see above.
        */
       public static boolean mustManageLegacyError(boolean forceBlockLevelUndoThrow)
       {
          return BlockManager.mustManageLegacyError(forceBlockLevelUndoThrow);
       }

       /**
        * Check if the current ERROR condition must be raised as a legacy {@link SysError}.
        *
        * @return  see above.
        */
       public static boolean mustThrowLegacyError()
       {
          return mustThrowLegacyError(false);
       }

       /**
        * Check if the current STOP condition must be raised as a legacy {@link LegacyStop}.
        *
        * @return  see above.
        */
       public static boolean mustThrowLegacyStop()
       {
          return BlockManager.hasLegacyStop(Stop.class);
       }

       /**
        * Check if the current ERROR condition must be raised as a legacy {@link SysError}.
        *
        * @param   forceBlockLevelUndoThrow
        *          Flag indicating that, when nothing else matches, a BLOCK-LEVEL UNDO, THROW exists by default.
        *
        * @return  see above.
        */
       public static boolean mustThrowLegacyError(boolean forceBlockLevelUndoThrow)
       {
          return BlockManager.hasLegacyError(SysError.class, 0) ||
                 (BlockManager.mustManageLegacyError(forceBlockLevelUndoThrow) &&
                  (BlockManager.hasLegacyError(SysError.class, -1) ||
                   (TransactionManager.isRootNestingLevel() && AppServerManager.isRemote())));
       }

       /**
        * Check if any CATCH blocks defined at the specified enclosing block can manage this exception.
        *
        * @return   <code>true</code> if this exception can be processed by the current block.
        */
       public static boolean hasLegacyError()
       {
          return BlockManager.hasLegacyError(SysError.class, 0);
       }

       /**
        * Record the specified error message and number, to be later thrown as a
        * {@link LegacyErrorException}.
        *
        * @param    errmsg
        *           The error message.
        * @param    num
        *           The error number.
        */
       public static void recordForLegacyThrow(String errmsg, int num)
       {
          WorkArea wa = work.get();

          if (num == 293 && (wa.legacyError == null || !wa.legacyError._isValid()))
          {
             // StopError is raised when a procedure in a run statement is not found.
             // Catch blocks that handles StopError errors, will not be able to handle
             // the error if raised as a SysError.
             wa.legacyError = StopError.newInstance(errmsg, num);
          }
          else if (wa.legacyError == null || !wa.legacyError._isValid())
          {
             wa.legacyError = SysError.newInstance(errmsg, num);
          }
          else
          {
             wa.legacyError.ref().addMessage(new character(errmsg), new integer(num));
          }
       }

       /**
        * STOP condition registered, to be later thrown as a
        * {@link LegacyStopException}.
        *
        * @param    ex
        *           StopConditionException to be converted in LegacyStop.
        */
       public static void recordForLegacyStop(StopConditionException ex)
       {
          WorkArea wa = work.get();
          StopAfterTimer stopAfterTimer = wa.stopAfterTimer;

          if (wa.legacyStop == null || !wa.legacyStop._isValid())
          {
             if (stopAfterTimer != null && stopAfterTimer.isStopAfterConditionRaised())
             {
                wa.legacyStop = StopAfter.newInstance();
                return;
             }
             else if (ex instanceof UserInterruptConditionException)
             {
                wa.legacyStop = UserInterrupt.newInstance();
                return;
             }
             else if (ex instanceof LockConflictConditionException)
             {
                wa.legacyStop = LockConflict.newInstance();
                return;
             }

             wa.legacyStop = Stop.newInstance();
          }
       }

       /**
        * Throw the specified error message and number.
        *
        * @param    num
        *           The error number.
        * @param    errmsg
        *           The error message.
        */
       public static void throwErrorConditionException(int num, String errmsg)
       {
          throw new ErrorConditionException(num, errmsg);
       }

       /**
        * Get the deferred legacy error from {@link WorkArea#legacyError}, and wrap it as a
        * {@link LegacyErrorException}.
        * <p>
        * This approach allows the FWD client-side to defer throwing a {@link LegacyErrorException},
        * as the FWD {@link object} instances (and any {@link _BaseObject_} instance) are not
        * serializable, and can't be sent over the network.
        *
        * @return   See above.
        */
       public static LegacyErrorException getDeferredLegacyError()
       {
          WorkArea wa = work.get();
          object<? extends SysError> legacyError = wa.legacyError;
          wa.legacyError = null;

          LegacyErrorException lex = new LegacyErrorException(legacyError);
          lex.setExplicit(wa.forceExplicit);
          wa.forceExplicit = false;
          return lex;
       }

       /**
        * Get the deferred legacy stop error from {@link WorkArea#legacyStop}, and wrap it as a
        * {@link LegacyStopException}.
        * <p>
        * This approach allows the FWD client-side to defer throwing a {@link LegacyStopException},
        * as the FWD {@link object} instances (and any {@link _BaseObject_} instance) are not
        * serializable, and can't be sent over the network.
        *
        * @return   See above.
        */
       public static LegacyStopException getDeferredLegacyStop()
       {
          WorkArea wa = work.get();
          object<? extends Stop> legacyStop = wa.legacyStop;
          wa.legacyStop = null;

          LegacyStopException lex = null;

          lex = new LegacyStopException(legacyStop);
          return lex;
       }

       /**
        * Display and raise an ERROR condition indiscriminately of the NO-ERROR state.
        *
        * @param    errors
        *           The error texts to display.
        */
       public static void displayAndRaiseError(List<String> errors)
       {
          boolean silent = ErrorManager.isSilentError();
          try
          {
             ErrorManager.setSilent(false);
             for (String err : errors)
             {
                displayError(err);
             }
             ErrorConditionException exc = new ErrorConditionException("raising multiple errors");
             exc.setForce(true);
             throw exc;
          }
          finally
          {
             ErrorManager.setSilent(silent);
          }
       }

       /**
        * Format the text of the error message and then display that text on
        * on the terminal. Adds the error number to _MSG stack.
        *
        * @param    num
        *           Error number.
        * @param    msg
        *           The text to display in the message line.
        */
       public static void displayError(int num, String msg)
       {
          displayError(num, msg, true);
       }

       /**
        * Format the text of the error message and then display that text on
        * on the terminal. Adds the error number to _MSG stack.
        *
        * @param    num
        *           Error number.
        * @param    msg
        *           The text to display in the message line.
        * @param    prefix
        *           <code>true</code> to prepend a double asterisk prefix to the
        *           formatted error message, <code>false</code> to omit this text.
        */
       public static void displayError(int num, String msg, boolean prefix)
       {
          boolean manageLegacyError = server.mustThrowLegacyError();

          msg = buildErrorText(num, msg, prefix);
          if (manageLegacyError)
          {
             server.recordForLegacyThrow(msg, num);
             throw new DeferredLegacyErrorException();
          }

          displayError(msg);
          addRaisedCondition(new int[] {num});
       }

       /**
        * Format the text of the warning message and then display that text on
        * on the terminal.
        *
        * @param    num
        *           Error number.
        * @param    msg
        *           The text to display.
        */
       public static void displayWarning(int num, String msg)
       {
          displayWarning(num, msg, true);
       }

       /**
        * Format the text of the warning message and then display that text on
        * on the terminal.
        *
        * @param    num
        *           Error number.
        * @param    msg
        *           The text to display.
        * @param    prefix
        *           <code>true</code> to prepend a double asterisk prefix to the
        *           formatted error message, <code>false</code> to omit this text.
        */
       public static void displayWarning(int num, String msg, boolean prefix)
       {
          String errText = buildErrorText(num, msg, prefix);

          // manually using the work area is OK in this case since this method
          // is only used on the server
          WorkArea wa = work.get();

          boolean silentError = isSilentError();

          if (!silentError)
          {
             addRaisedConditionWorker(wa, new int[] {num});
          }

          if (wa.suppressWarnings)
          {
             // when SESSION:SUPPRESS-WARNING set to TRUE the warning is not displayed, output or set in
             // ERROR-STATUS, only added to _MSG
             return;
          }

          if (silentError)
          {
             // no-error will modify ERROR-STATUS, but only when SUPPRESS-WARNINGS not set to TRUE
             wa.pendingErrorStatus.addError(new ErrorEntry(num, errText));
          }
          else
          {
             displayWarning(errText);
          }
       }

       /**
        * Format the text of the error message and then display that text on the terminal.  This is
        * the redirected version of the displayError.  If the output is redirected to file the message
        * is writing to the file. Adds the error number to _MSG stack.
        *
        * @param    num
        *           Error number.
        * @param    msg
        *           The text to display in the message line.
        * @param    prefix
        *           <code>true</code> to prepend a double asterisk prefix to the
        *           formatted error message, <code>false</code> to omit this text.
        */
       public static void displayErrorRedirected(int num, String msg, boolean prefix)
       {
          displayErrorRedirected(buildErrorText(num, msg, prefix));
          addRaisedCondition(new int[] {num});
       }

       /**
        * Format the text of the error message and then display that text on the terminal.  This is
        * the redirected version of the displayError.  If the output is redirected to file the message
        * is writing to the file.
        *
        * @param    msg
        *           The text to display in the message line.
        */
       public static void displayErrorRedirected(String msg)
       {
          displayErrorRedirected(msg, true);
       }

       /**
        * Format the text of the error message and then display that text on the terminal.  This is
        * the redirected version of the displayError.  If the output is redirected to file the message
        * is writing to the file.
        *
        * @param    msg
        *           The text to display in the message line.
        * @param    logCallStack
        *           Add call stack to log. <code>false</code> when ALERT_BOX message.
        */
       public static void displayErrorRedirected(String msg, boolean logCallStack)
       {
          if (errorWriter != null)
          {
             errorWriter.displayErrorRedirected(msg, logCallStack);
          }
          else
          {
             writeLog(msg, logCallStack);
          }
       }

       /**
        * Format the text of the abend message and then display that text on
        * on the terminal.
        *
        * @param    num
        *           Error number.
        * @param    msg
        *           The text to display in the message line.
        */
       public static void displayAbend(int num, String msg)
       {
          displayError(buildAbendText(num, msg));
          addRaisedCondition(new int[]{num});
       }

       /**
        * Display the given error message on the terminal.
        * This method does not add a record to _MSG stack.
        *
        * @param    errmsg
        *           The text to display in the message line.
        */
       public static void displayError(String errmsg)
       {
          displayError(errmsg, false);
       }

       /**
        * Display the given error message on the terminal. This method does not add a record to
        * _MSG stack.
        *
        * @param    errmsg
        *           The text to display in the message line.
        * @param    noMessageBox
        *           <code>true</code> if error is always displayed in message area or logged rather
        *           than displayed as a message box.
        */
       public static void displayError(String errmsg, boolean noMessageBox)
       {
          displayError(errmsg, noMessageBox, null, true);
       }

       /**
        * Display the given error message.
        * This method does not add a record to _MSG stack.
        * @param    num
        *           Error number.
        * @param    msg
        *           The text to display in the message line.
        * @param    prefix
        *           <code>true</code> to prepend a double asterisk prefix to the
        *           formatted error message, <code>false</code> to omit this text.
        * @param    noMessageBox
        *           <code>true</code> if error is always displayed in message area or logged rather
        *           than displayed as a message box.
        */
       public static void displayError(int num, String msg, boolean prefix, boolean noMessageBox)
       {
          msg = buildErrorText(num, msg, prefix);
          displayError(msg, noMessageBox);
       }

       /**
        * Display the given error message on the terminal. This method does not add a record to
        * _MSG stack.
        *
        * @param    errmsg
        *           The text to display in the message line.
        * @param    noMessageBox
        *           <code>true</code> if error is always displayed in message area or logged rather
        *           than displayed as a message box.
        * @param    stack
        *           Progress stack trace
        * @param    logCallStack
        *           Add call stack to log. <code>false</code> when ALERT_BOX message.
        */
       public static void displayError(String errmsg, boolean noMessageBox, String[] stack, boolean logCallStack)
       {
          displayError(errmsg, noMessageBox, stack, logCallStack, false);
       }

       /**
        * Display the given error message on the terminal. This method does not add a record to
        * _MSG stack.
        *
        * @param    errmsg
        *           The text to display in the message line.
        * @param    noMessageBox
        *           <code>true</code> if error is always displayed in message area or logged rather
        *           than displayed as a message box.
        * @param    stack
        *           Progress stack trace
        * @param    logCallStack
        *           Add call stack to log. <code>false</code> when ALERT_BOX message.
        * @param    isFromLogManager
        *           Flag to indicate the error comes from LOG-MANAGER.
        */
       public static void displayError(String errmsg, boolean noMessageBox, String[] stack,
                                       boolean logCallStack, boolean isFromLogManager)
       {
          if (isIgnore() || isHeadless() || isSilent())
          {
             return;
          }

          if (errorWriter != null)
          {
             errorWriter.displayError(errmsg, noMessageBox, stack, logCallStack, isFromLogManager);
          }
       }

       /**
        * Display the given warning message on the terminal.
        * This method does not add a record to _MSG stack.
        *
        * @param    wrnmsg
        *           The text to display in the message line.
        */
       public static void displayWarning(String wrnmsg)
       {
          if (isIgnore() || isHeadless())
          {
             return;
          }

          if (errorWriter != null)
          {
             errorWriter.displayWarning(wrnmsg);
          }
       }

       /**
        * Process a LegacyErrorException, setting the pendingError flag and recording the errors.
        *
        * @param   lex
        *          The LegacyErrorException that is processed.
        * @param   eh
        *          the context-local error helper for adding the error entries.
        */
       public static void processLegacyErrorException(LegacyErrorException lex, ErrorHelper eh)
       {
          eh.srv.setPending(true);

          LegacyError err = lex.getErrorRef();
          if (!(err instanceof AppError) || !((AppError) err).isFromReturn())
          {
             ProError proErr = (ProError) err;
             int numErr = err.getNumMessages().intValue();
             int[] errnums = new int[numErr];
             String[] errmsgs = new String[numErr];

             for (int i = 0; i < numErr; i++)
             {
                errnums[i] = proErr.getMessageNum(new integer(i + 1)).intValue();
                errmsgs[i] = proErr.getMessage(new integer(i + 1)).toStringMessage();
                eh.wa.pendingErrorStatus.addError(new ErrorEntry(errnums[i], errmsgs[i]));
             }
             addRaisedConditionWorker(eh.wa, errnums);
          }

          if (lex.getError()._isValid())
          {
             ObjectOps.delete(err);
          }
       }

       /**
        * Handles a SOAP fault.
        *
        * @param    operationName
        *           The operation that failed.
        * @param    faultMsg
        *           The fault error msg.
        * @param    faultActor
        *           The fault actor.
        * @param    faultCode
        *           The fault code.
        * @param    faultString
        *           The fault string.
        * @param    faultDetailXML
        *           The fault details as xml.
        */
       public static void handleSoapFault(String operationName,
                                          String faultMsg,
                                          String faultActor,
                                          String faultCode,
                                          String faultString,
                                          String faultDetailXML)
       {
          server.handleSoapFault(operationName, faultMsg, faultActor, faultCode, faultString, faultDetailXML);
       }

       /**
        * Writes log record.
        *
        * @param    msg
        *           The message to write.
        */
       public static void writeLog(String msg)
       {
          if (!msaManager.isMsaContext() &&
             (SessionManager.get() == null || SessionManager.get().getSession() == null))
          {
             return; // no context: import or server starting up
          }
          server.writeLog(msg, true, Thread.currentThread().getId());
       }

       /**
        * Writes log record.
        *
        * @param    msg
        *           The log message.
        * @param    logCallStack
        *           Add call stack to log. <code>false</code> when ALERT_BOX message.
        */
       public static void writeLog(String msg, boolean logCallStack)
       {
          server.writeLog(msg, logCallStack, Thread.currentThread().getId());
       }

       /**
        * Writes log record.
        *
        * @param    msg
        *           The log message.
        * @param    logCallStack
        *           Add call stack to log. <code>false</code> when ALERT_BOX message.
        * @param    threadId
        *           The id of the thread recording the error message.
        */
       public static void writeLog(String msg, boolean logCallStack, long threadId)
       {
          server.writeLog(msg, logCallStack, threadId);
       }

       /**
        * Get the type of its associated handle.
        *
        * @return   Always return the PSEUDO-WIDGET value.
        */
       public static character resourceType()
       {
          return new character(LegacyResource.PSEUDO_WIDGET);
       }

       /**
        * Returns a handle for the ERROR-OBJECT-DETAIL attribute, set in cases of SOAP-related faults.
        *
        * @return   See above.
        */
       public static handle getErrorObjectDetail()
       {
          WorkArea wa = work.get();

          return (wa.errorStatus.soapFault == null)
                 ? new handle()
                 : new handle(wa.errorStatus.soapFault);
       }

       /**
        * Reflects the value of SESSION:SUPPRESS-WARNINGS attribute.
        *
        * @return   see above.
        */
       public static boolean isSuppressWarnings()
       {
          return server.isSuppressWarnings();
       }

       /**
        * Reflects the value of SESSION:SUPPRESS-WARNINGS-LIST attribute.
        *
        * @return   see above.
        */
       public static String getSuppressWarningsList()
       {
          return server.getSuppressWarningsList();
       }

       /**
        * This method can be only called on the server.
        *
        * @param   value
        *          The new value of {@code  SESSION:SUPPRESS-WARNINGS} attribute.
        */
       public static void setSuppressWarnings(boolean value)
       {
          work.get().suppressWarnings = value;
       }

       /**
        * This method can be only called on the server.
        *
        * @param   value
        *          The new value of {@code  SESSION:SUPPRESS-WARNINGS-LIST} attribute.
        */
       public static void setSuppressWarningsList(String value)
       {
          WorkArea workArea = work.get();

          for (String s : value.split(","))
          {
             s = s.trim();
             try
             {
                workArea.suppressWarningsSet.add(Integer.parseInt(s));
             }
             catch (Exception e)
             {
                int[] nums = {16478, 3131};
                String[] texts = {"The entry " + s + " in the warning list is invalid",
                   "Unable to set attribute SUPPRESS-WARNINGS-LIST in widget of type PSEUDO-WIDGET"};
                recordOrThrowError(nums, texts, false);

                workArea.suppressWarningsSet.clear();

                return;
             }
          }

          workArea.suppressWarningsList = value;
       }

       /**
        * Get a helper to access certain context-local state required for error support.
        *
        * @return   The context-local helper.
        */
       public static ErrorHelper getErrorHelper()
       {
          // the usage of Stream.putWorker() is used on the client side, which calls TM.setNestedMode() which
          // initializes the TransactionManager on the client; we need safety code here
          ServerErrorDataAccessor errorDataAccessor = server instanceof ServerErrorDataAccessor ?
             (ServerErrorDataAccessor) server :
             null;
          return new ErrorHelper(work.obtain(), errorDataAccessor);
       }

       /**
        * Generate an error if a character is invalid in a format string.
        *
        * @param    idx
        *           The 0-based index of the incorrect character in the format
        *           string (this will be converted to a 1-based index).
        * @param    fmt
        *           The incorrect format string.
        *
        * @return   -1.
        *
        * @throws   ErrorConditionException
        */
       public static int genInvalidCharError(int idx, String fmt)
       throws ErrorConditionException
       {
          String spec = "Character number %d of format %s is invalid";
          recordOrThrowError(22, String.format(spec, idx + 1, fmt));

          // this is the safest thing to return (it should be undone anyway)
          return -1;
       }

       /**
        * Generate an "Invalid ... parameter..." error string.
        *
        * @param type
        *        the type name, e.g "index", "array-value"
        * @param instance
        *        the instance
        * @param method
        *        the method name
        * @param errMsg
        *        the error message format string
        *
        * @return the generated string
        */
       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);
       }

       /**
        * Manufacture error text based on the given parameters. We start with the id of a template
        * and replace the positional placeholders to be replaced with the provided string parameters.
        * <p>
        * Because the errors are inconsistent relative to the double asterisk prefix, the returned
        * value will contain it, if the case; following that when the message is displayed, the
        * prefix to be used to be always {@code false};
        *
        * @param   id
        *          The id of the message. The associated text is filled with {@code params} tokens.
        * @param   params
        *          The parameters for the message.
        */
       public static String replaceTokens(int id, String... params)
       {
    // NOTE: cases without indentation were found while doing investigations but are not YET implemented in code.
          String msg = "";
          switch (id)
          {
             case     0: msg = "%1"; break;
             case    26: msg = "** Array subscript %1 is out of range"; break;
             case    43: msg = "** Cannot find or open file %1, errno = %2"; break;
             case   142: msg = "** Unable to update %1 Field"; break;
             case   143: msg = "** %1: Unable to evaluate field for assignment"; break;
             case   223: msg = "** Incompatible data types in expression or assignment"; break;
             case   257: msg = "** The first character of %1 must be alphabetic"; break;
             case   274: msg = "** The character %1 is not permitted in name %2"; break;
             case   293: msg = "** \"%1\" was not found"; break;
             case   341: msg = "** There is no current %1 record. Delete failed"; break;
             case   912: msg = "Code page attribute table for %1 was not found in %2"; break;
             case  1043: msg = "Collation table for code page %1 and collation name %2 was not found in %3"; break;
             case  1587: msg = "ASC could not find the appropriate conversion table in %1"; break;

             case  2913: msg = "Unknown sequence expression %1"; break;
             case  3131: msg = "Unable to set attribute %1 in widget %2 of type %3"; break;
             case  3133: msg = "SYSTEM ERROR: Sequence %1 not found"; break;
             case  3140: msg = "Cannot access the %1 attribute because the widget does not exist"; break; // when invoking APPLY-CALLBACK on an uninitialized handle.
             case  3147: msg = "Cannot browse query %1 because query is already being browsed"; break;
             case  3588: msg = "Sequence %1 not found"; break;
             case  4052: msg = "**%1 is not a %2 attribute for %3"; break;
             case  4056: msg = "**Attribute %1 for the %2 has an invalid value of UNKNOWN"; break;
             case  4058: msg = "**Attribute %1 for the %2 has an invalid value of %3"; break;
             case  4061: msg = "**Attribute %1 for the %2 has an invalid value"; break;
             case  4065: msg = "**The %1 attribute on the %2 has invalid arguments"; break;
             case  4078: msg = "**Unable to set attribute %1 for %2"; break;
             case  4083: msg = "**Unable to assign UNKNOWN value to attribute %1 on %2"; break;
             case  5368: msg = "Couldn't update field %1 of target in a BUFFER-COPY statement"; break;
             case  5442: msg = "Invalid datatype for argument to method '%1'. Expecting '%2'"; break;
             case  5468: msg = "Application server connect failure"; break;
             case  5482: msg = "Unknown hostname %1"; break;
             case  5484: msg = "Unknown service %1 transport TCP"; break;
             case  5729: msg = "Incompatible datatypes found during runtime conversion"; break;
             case  6063: msg = "Code page conversion table for %1 to %2 was not found in %3."; break;

             case  7319: msg = "ADD/SET-BUFFERS argument %1 was invalid or not found"; break;
             case  7331: msg = "Cannot reposition query %1 to recid/rowid(s) given"; break;
             case  7351: msg = "BUFFER-FIELD %1 was not found in buffer %2"; break;
             case  7352: msg = "You may only invoke EMPTY-TEMP-TABLE on valid temp-tables"; break;
             case  7363: msg = "SKIP-DELETED-RECORD cannot be set on during BROWSE"; break;
             case  7365: msg = "%1 BUFFER-VALUE argument must be valid array index, less than or equal to field extent %2"; break;
             case  7372: msg = "Object of type DATASET widget used where BUFFER-FIELD object required"; break;
             case  9034: msg = "Pair with %1 failed to have a member in each table"; break;
             case  9035: msg = "Pairs list had too many pairs or odd number of entries"; break;
             case  9065: msg = "Cannot set PRIMARY on a TEMP-TABLE that has been prepared with TEMP-TABLE-PREPARE"; break;
             case  9069: msg = "Unable to get TEMP-TABLE handle for parameter"; break;
             case  9092: msg = "Invalid COMPARE operator: %1"; break;
             case  9093: msg = "Invalid COMPARE strength: %2"; break;
             case  9407: msg = "Connection failure for host %1 port %2 transport TCP"; break;

             case 10068: msg = "Lead attributes in a chained-attribute expression (a:b:c) must be type HANDLE or a user-defined type and valid (not UNKNOWN)"; break;

             case 10515: msg = "Handle type not valid as XML input source"; break;

             case 11265: msg = "Attempt to access blob beyond its end"; break;
             case 11279: msg = "File offset %1 is greater than size of file %2 for file '%3'"; break;              // no DOT at the end
             case 11295: msg = "Attempt to access past end of MEMPTR during COPY-LOB"; break;                      // no DOT at the end
             case 11305: msg = "Cannot copy large object starting at %1 for %2 bytes when object size is %3"; break;
             case 11316: msg = "Could not obtain LONGCHAR"; break;
             case 11329: msg = "File offset %1 is greater than size of file %2 for file '%3'"; break;
             case 11330: msg = "File offset %1 plus copy length %2 is greater than size of file %3 for '%4'"; break;
             case 11331: msg = "File offset %1 is greater than size of file %2 for file '%3'"; break;
             case 11332: msg = "Invalid offset specified in COPY-LOB statement"; break;
             case 11334: msg = "Offset supplied for source large object is greater than object size"; break;
             case 11335: msg = "Offset supplied for target large object is greater than object size"; break;
             case 11339: msg = "The source codepage can not be specified for a CLOB/LONGCHAR field"; break;
             case 11343: msg = "Unknown or invalid file offset specified in COPY-LOB statement"; break;
             case 11344: msg = "Unknown or invalid length specified in COPY-LOB statement"; break;
             case 11345: msg = "Unknown or invalid source file specified in COPY-LOB statement"; break;
             case 11341: msg = "The target codepage conflicts with the longchar's fixed codepage"; break;
             case 11346: msg = "Unknown or invalid target file specified in COPY-LOB statement"; break;
             case 11350: msg = "Write to file '%1' failed during COPY-LOB"; break;
             case 11382: msg = "INPUT/OUTPUT operations are not allowed with RAW, ROWID, MEMPTR, BLOB, CLOB or LONGCHAR type variables"; break;
             case 11393: msg = "MEMPTR target of COPY-LOB must be initialized"; break;
             case 11394: msg = "MEMPTR target of COPY-LOB is not big enough"; break;
             case 11395: msg = "Large object assign or copy failed"; break;

             case 11679: msg = "Mismatch between source-codepage argument and LONGCHAR codepage"; break;
             case 11683: msg = "FIX-CODEPAGE can only be run when the LONGCHAR variable is empty"; break;
             case 11851: msg = "COPY-LOB operations between BLOB and CLOB fields are not supported"; break;
             case 11858: msg = "Cannot add/set buffers on a static dataset"; break;
             case 11860: msg = "Buffer %1 may not appear more than once in any dataset"; break;
             case 11863: msg = "FILL is not valid for table %1 when TRACKING-CHANGES is on"; break;
             case 11867: msg = "Query buffers and data-source buffers must match"; break;
             case 11872: msg = "Invalid timezone expression"; break;                                               // no DOT at the end
             case 11874: msg = "FILL requires an attached data-source for buffer %1, or an active before-fill callback procedure"; break;
             case 11875: msg = "FILL of buffer %1 requires a DATA-SOURCE QUERY to be prepared and assigned for this type of buffer"; break;
             case 11876: msg = "FILL of buffer %1 with user-supplied query requires that the query be QUERY-PREPARED"; break;
             case 11878: msg = "FILL got error %1 creating record %2"; break;
             case 11883: msg = "Unable to auto-generate a FILL query predicate for %1 if field %2 in %3 is not mapped"; break;
             case 11884: msg = "Dataset buffer %1 must be for a temp-table"; break;
             case 11885: msg = "A unique primary index is required in the target table, each field of which is mapped to some source field, in order to do a replace-mode or parent-mode COPY/GET/MERGE/FILL type of operation on a dataset table"; break;
             case 11887: msg = "ACCEPT-CHANGES run on table %1 that has no BEFORE-TABLE"; break;
             case 11891: msg = "Found error during GET-CHANGES method"; break;
             case 11892: msg = "Attempt to reference dataset buffer of a data-source before ATTACH-DATA-SOURCE for data-source buffer %1"; break;
             case 11893: msg = "SAVE-WHERE-STRING cannot be generated when there is no BEFORE-TABLE--define one or use TRACKING-CHANGES"; break;
             case 11894: msg = "KEYS phrase for data-source buffer %1 contains ROWID, but no field in %2 was mapped to the rowid during ATTACH-DATA-SOURCE"; break;
             case 11895: msg = "Unable to get SAVE-WHERE-STRING because no KEYS phrase given in DATA-SOURCE definition and no unique index found in %1"; break;
             case 11896: msg = "Unable to generate a save predicate for %1 if field %2 is not mapped in %3"; break;
             case 11897: msg = "Record %1 is not available to set ERROR-STRING"; break;
             case 11906: msg = "Unable to identify data-source buffer for SAVE-ROW-CHANGES"; break;
             case 11909: msg = "Unable to find sequence field  %1 in table %2 during SAVE-ROW-CHANGES"; break;
             case 11910: msg = "SAVE-ROW-CHANGES was unable to update the database table %1"; break;
             case 11912: msg = "Unable to find record for db buffer %1 during SAVE-ROW-CHANGES"; break;
             case 11913: msg = "SAVE-ROW-CHANGES found %1 record with conflicting change by another user"; break;
             case 11916: msg = "%1 record is not available to set ERROR in"; break;
             case 11917: msg = "Cannot set ERROR if there is no BEFORE-TABLE for %1"; break;
             case 11918: msg = "Cannot set ERROR if there is no BEFORE-TABLE row for %1"; break;
             case 11919: msg = "REJECT-CHANGES run on table %1 that has no BEFORE-TABLE"; break;
             case 11922: msg = "MERGE-CHANGES may not be run on table %1 with TRACKING-CHANGES on"; break;
             case 11923: msg = "MERGE-CHANGES run on original table %1 that does not match the ORIGIN-HANDLE in the GET-CHANGES table %2"; break;
             case 11924: msg = "MERGE-CHANGES run on original table %1 and change table %2 whose columns do not match exactly"; break;
             case 11925: msg = "GET/MERGE-CHANGES require a BEFORE-TABLE for table %1"; break;
             case 11926: msg = "Found unexpected error while merging changes"; break;
             case 11928: msg = "INVALID origin-rowid of the change table %1 of %2"; break;
             case 11933: msg = "ACCEPT-ROW-CHANGES must be run on the BEFORE table of buffer %1"; break;
             case 11934: msg = "ACCEPT-ROW-CHANGES buffer %1 not available"; break;
             case 11935: msg = "SAVE-ROW-CHANGES buffer %1 not available"; break;
             case 11936: msg = "REJECT-ROW-CHANGES buffer %1 not available"; break;
             case 11937: msg = "MERGE-ROW-CHANGES buffer %1 not available"; break;
             case 11938: msg = "SAVE-ROW-CHANGES must be run on the BEFORE table of buffer %1"; break;
             case 11939: msg = "REJECT-ROW-CHANGES must be run on the BEFORE table of buffer %1"; break;
             case 11942: msg = "SAVE-ROW-CHANGES for BEFORE buffer %1, requires that the corresponding AFTER dataset member buffer %2 be attached to a DATA-SOURCE"; break;
             case 11943: msg = "First argument to MERGE-ROW-CHANGES must be a valid BUFFER handle"; break;
             case 11944: msg = "MERGE-ROW-CHANGES finds ORIGIN-HANDLE in table %1 is no longer valid"; break;
             case 11945: msg = "BUFFER:FILL is only valid for buffers that are members of a dataset"; break;
             case 11946: msg = "You cannot EMPTY a BEFORE-TABLE, Use ACCEPT-CHANGES or EMPTY the AFTER-TABLE"; break;
             case 11947: msg = "bufferhdl:GET-CHANGES must have a valid buffer handle to the original table, from which changes are coming, as the first argument"; break;
             case 11948: msg = "First argument to MERGE-CHANGES must be a valid BUFFER handle"; break;
             case 11949: msg = "APPLY-CALLBACK is only for buffers that are members of datasets"; break;
             case 11950: msg = "Argument %1 to CALLBACK method must be a valid buffer or dataset event name"; break;
             case 11951: msg = "SET-CALLBACK-PROCEDURE is only valid for buffers that are members of a dataset"; break;
             case 11952: msg = "ATTACH-DATA-SOURCE is only valid for buffers that are members of a dataset"; break;
             case 11953: msg = "Dataset GET-CHANGES and MERGE-CHANGES must have a valid dataset handle as the first argument"; break;
             case 11954: msg = "Dataset GET-CHANGES and MERGE-CHANGES must have a change dataset and an original dataset with the same number of members and the member buffers must have matching schema"; break;
             case 11955: msg = "The argument to Dataset-handle:ADD-RELATION method was invalid"; break;
             case 11956: msg = "ADD-RELATION method requires buffer objects and not table objects"; break;
             case 11957: msg = "ATTACH-DATA-SOURCE must have a valid data-source handle as the first argument"; break;
             case 11958: msg = "ATTACH-DATA-SOURCE may not be called until source buffers have been assigned to the DATA-SOURCE"; break;
             case 11959: msg = "FILL-MODE is only valid for a dataset buffer"; break;
             case 11960: msg = "FILL-MODE must be one of NO-FILL, MERGE, EMPTY, etc."; break;
             case 11961: msg = "Buffers in ADD-RELATION must be members of the dataset"; break;
             case 11962: msg = "Odd number of join pairs in ADD-RELATON"; /* TYPO in ABL */ break;
             case 11963: msg = "ADD-RELATION join pairs list entry %1 not valid--list must have unqualified field names with no embedded blanks, in parent-fld,child-fld order, e.g. 'cust-num,cust-num'"; break;
             case 11964: msg = "ADD-RELATION join pairs list was not valid"; break;
             case 11965: msg = "Attempt to relate dataset buffer %1 to parent with itself in ancestry"; break;
             case 11967: msg = "CLEAR may not be used on a static dataset"; break;
             case 11968: msg = "First argument to DATASET:CREATE-LIKE must be a valid dataset handle or name\""; break; // yes, the double quote is part of error message
             case 11969: msg = "Unable to CLEAR target CREATE-LIKE dataset"; break;
             case 11971: msg = "Cannot delete a relation of a dataset"; break;
             case 11973: msg = "You may not change the buffers for a static data-source"; break;
             case 11974: msg = "The first argument to ADD-SOURCE-BUFFER must be a valid buffer handle"; break;
             case 11975: msg = "ADD-SOURCE-BUFFER key name %1 must be a field in buffer %2"; break;
             case 11976: msg = "Data-source QUERY attribute requires valid query handle or unknown"; break;
             case 11979: msg = "Buffer %1 can have only 1 active parent relation"; break;
             case 11980: msg = "Must assign a user query to a data-source that is a join before filling"; break;
             case 11981: msg = "Failed to auto-prepare an automatic fill query"; break;
             case 12008: msg = "Invalid character code found in data for codepage %1"; break;                         // no DOT at the end!
             case 12012: msg = "Invalid character data found in MEMPTR for codepage %1"; break;                       // no DOT at the end!
             case 12117: msg = "Unacceptable datatype for %1 argument"; break;                                        // no DOT at the end!
             case 12125: msg = "The codepage \"%1\" cannot be used for CLOB or LONGCHAR variables"; break;

             case 12298: msg = "Buffer %1 may be in only one data-source at a time.  Use different buffers for the same table"; break;
             case 12301: msg = "Source and target are the same table in temp-table copy operation for %1"; break;     // dataset-copy (itself)
             case 12303: msg = "COPY-TEMP-TABLE requires %1 and %2 columns to match exactly unless the fourth loose-mode parameter is passed as TRUE"; break; // dataset-copy (table structure not matching)
             case 12304: msg = "Error found during COPY-TEMP-TABLE operation on %1 and %2"; break;
             case 12310: msg = "Caller and called dataset parameters have different number of members"; break;
             case 12311: msg = "Parameters (table %1 and table %2) do not match"; break;
             case 12314: msg = "Invalid DATASET-Handle parameter given"; break;
             case 12323: msg = "A NO-SCHEMA-MARSHAL table cannot be used as a parameter where the receiving side does not have a pre-prepared schema"; break;
             case 12344: msg = "Cannot turn on TRACKING-CHANGES for a static temp-table unless DEFINED with a BEFORE-TABLE phrase"; break;
             case 12349: msg = "Cannot delete a dataset relation auto-generated query"; break;
             case 12352: msg = "Argument %1 to query callback method must be a valid query event name such as OFF-END"; break;
             case 12355: msg = "TRACKING-CHANGES may not be turned on for a non-dataset temp-table"; break;
             case 12360: msg = "LAST-BATCH may only be set for dataset temp-tables"; break;
             case 12361: msg = "BATCH-SIZE may only be set for dataset temp-tables"; break;
             case 12362: msg = "COPY-DATASET must have a valid dataset handle as first argument"; break;

             case 12373: msg = "You may not create BEFORE-TABLE %1 record"; break;
             case 12374: msg = "You may not delete BEFORE-TABLE %1 record.  Use ACCEPT-ROW-CHANGES instead"; break;
             case 12375: msg = "You may not update BEFORE-TABLE %1 record"; break;
             case 12377: msg = "DYNAMIC-NEXT-VALUE and DYNAMIC-CURRENT-VALUE require the second DBNAME argument"; break;

             case 12384: msg = "DATA-RELATION parent and child buffers must also be in the dataset"; break;
             case 12385: msg = "Pairs in DATA-RELATION RELATION-FIELDS phrase must be in the form: parent-field, child-field, parent-field, child-field etc"; break;
             case 12411: msg = "Cannot set attribute for a static object"; break;

             case 12744: msg = "Cannot update fields in a BEFORE-TABLE"; break;   // This is a compile/conversion time error.
             case 12747: msg = "You must provide a valid connected database name to dynamic sequence functions"; break;
             case 12748: msg = "Unable to evaluate sequence name for dynamic sequence function"; break;

             case 12753: msg = "In COPY-TEMP-TABLE operation the static target table %1 must have an empty BEFORE-TABLE if the source table has any BEFORE-TABLE records"; break;
             case 12758: msg = "You may not EMPTY temp-table %1 when TRACKING-CHANGES is true"; break;
             case 12764: msg = "REFERENCE-ONLY temp-table is unexpectedly UNKNOWN for %1"; break;
             case 12766: msg = "%1 Cannot bind BY-REFERENCE parameter tables or datasets unless column datatypes, positions, extents and indexes match for tables %2 and %3"; break;
             case 12778: msg = "GET-PARENT mode not supported for buffer GET-CHANGES"; break;
             case 12779: msg = "Cannot have both exclude list and include list"; break;
             case 12780: msg = "DATA-SOURCE-COMPLETE-MAP only legal for dataset tables"; break;
             case 12781: msg = "DATA-SOURCE-COMPLETE-MAP requires prior ATTACH-DATA-SOURCE method to be run"; break;
             case 12783: msg = "Odd number of tables in pairs list parameter in COPY-DATASET"; break;
             case 12784: msg = "COPY-DATASET pair %1 and %2 must have a table in each dataset"; break;
             case 12785: msg = "Table %1 not found in dataset %2"; break;
             case 12786: msg = "FILL-WHERE-STRING cannot be assigned unless the DATA-SOURCE is attached to some dataset buffer by a prior ATTACH-DATA-SOURCE"; break;
             case 12787: msg = "Attempt to reference uninitialized dataset"; break;
             case 12788: msg = "OFF-END CALLBACK not supported for query %1 whose buffer %2 is not in a dataset"; break;
             case 12790: msg = "Invalid value given for SCHEMA-MARSHAL %1"; break;
             case 12860: msg = "Datatypes in pairlist argument for ATTACH-DATA-SOURCE, BUFFER-COPY or BUFFER-COMPARE methods must match for fields %1 and %2"; break;

             case 13009: msg = "%1 BIND modifier not allowed for cases where neither the caller nor the called TABLE or DATASET parameter %2 has been defined REFERENCE-ONLY and neither caller nor called parameter is a TABLE-HANDLE or DATASET-HANDLE"; break;
             case 13011: msg = "%1 If BIND used when caller TABLE or DATASET parameter is bound and called parameter %2 is also bound, the caller and called parameters must be the same instance"; break;
             case 13012: msg = "%1 BIND or BY-REFERENCE modifier required on RUN, FUNCTION or METHOD invocation parameter when called TABLE or DATASET parameter %2 is REFERENCE-ONLY and is still not bound"; break;
             case 13031: msg = "Cannot have both schema-location and write-schema arguments set"; break;
             case 13032: msg = "Unable to create Temp-Table or dataset schema from XML Schema"; break;
             case 13033: msg = "Verify Temp-Table or dataset schema against XML Schema failed"; break;
             case 13035: msg = "Error reading XML file '%1'"; break;
             case 13055: msg = "Unable to update indexes for table '%1'"; break;

             case 13036: msg = "Error reading XML from a MEMPTR or LONGCHAR"; break;
             case 13048: msg = "The XML for READ-XMLSCHEMA must start with a 'schema' element"; break;
             case 13050: msg = "Unable to create record for '%1' during READ-XML/READ-JSON"; break;
             case 13053: msg = "Unable to update field '%1' in table '%2'"; break;
             case 13054: msg = "Invalid number of array elements for field '%1' in table '%2'"; break;
             case 13058: msg = "No data instance in diffgram to parse"; break;
             case 13063: msg = "REPLACE mode requires a unique primary index in the target table %1"; break;
             case 13064: msg = "READ-XML encountered an error while parsing the XML Document: %1"; break;
             case 13066: msg = "Unable to write XML Schema for temp-table %1"; break;
             case 13078: msg = "Unsupported data type for XML Serialization: %1"; break;
             case 13079: msg = "XML Schema definition for temp-table '%1' not found"; break;
             case 13082: msg = "XML Schema definition for data-relation between '%1' and '%2' not found"; break;
             case 13081: msg = "XML Schema definition for '%1' does not match dataset definition."; break;
             case 13083: msg = "XML Schema contains extra temp-table definition '%1'"; break;
             case 13086: msg = "XML Schema contains extra data-relation definition '%1'"; break;
             case 13089: msg = "XML Schema definition for field '%1' in table '%2' does not match Temp-Table field definition"; break;
             case 13090: msg = "XML Schema definition for field '%1' in table '%2' not found"; break;
             case 13091: msg = "XML Schema definition for table '%1' contains extra field '%2'"; break;
             case 13093: msg = "Write temp-table data failed for WRITE-XML"; break;
             case 13095: msg = "WRITE-XML target-type must be 'FILE' when buffers of a dataset have a different NAMESPACE-URI"; break;
             case 13097: msg = "Write schema failed for WRITE-XMLSCHEMA"; break;
             case 13138: msg = "Temp-table '%1' not found in XML Schema"; break;
             case 13139: msg = "Field '%1' already added from XML Schema"; break;
             case 13140: msg = "Invalid 'maxOccurs' attribute value '%1' for field '%2'"; break;
             case 13141: msg = "Invalid 'maxOccurs' attribute value set for BLOB or CLOB field '%1'"; break;
             case 13142: msg = "Invalid format '%1' in XML Schema for field '%2'"; break;
             case 13143: msg = "Unable to set initial value '%1' from XML Schema for field '%2'"; break;
             case 13158: msg = "XML Methods only allowed on TEMP-TABLE buffers"; break;
             case 13159: msg = "Dynamic temp-table must be in CLEAR or PREPARED state for READ-XML Methods"; break;
             case 13160: msg = "Dynamic temp-table must be in PREPARED state for WRITE-XML Methods"; break;
             case 13161: msg = "%1 If a caller or called REFERENCE-ONLY parameter %2 will become BOUND to its opposite as a result of the call, then the BIND keyword must be supplied on both the caller invocation and on the called parameter definition"; break;
             case 13184: msg = "Invalid source-type for READ-XML: %1"; break;
             case 13185: msg = "Invalid target-type for WRITE-XML: %1"; break;
             case 13186: msg = "Handle type not valid as WRITE-XML target"; break;
             case 13200: msg = "Odd number of values in field-type-mapping argument"; break;
             case 13201: msg = "Invalid data type override '%1' for field '%2'"; break;
             case 13233: msg = "Cannot set XML-NODE-TYPE of field '%1' to 'TEXT'"; break;
             case 13273: msg = "Third argument to SET-CALLBACK must be a valid procedure handle or object reference"; break;
             case 13272: msg = "Use SET-CALLBACK instead of SET-CALLBACK-PROCEDURE for methods in a class"; break;
             case 13452: msg = "Method '%1' for callback must be valid public method"; break;
             case 13511: msg = "The Session-free connection failed because the Appserver is in a session-managed operating mode"; break;
             case 13514: msg = "%1 name '%2' in namespace '%3' not found in XML Document"; break;
             case 13515: msg = "Invalid encoding for WRITE-XML"; break;

             case 14274: msg = "MARK-ROW-STATE and MARK-NEW may only be used on a dataset member buffer with an existing BEFORE-TABLE"; break;
             case 14275: msg = "Dataset record for %1 required for DATA-SOURCE-ROWID"; break;
             case 14276: msg = "Must run ATTACH-DATA-SOURCE on buffer %1 in order to get DATA-SOURCE-ROWID"; break;
             case 14277: msg = "Unable to identify DATA-SOURCE buffer %1 in DATA-SOURCE-ROWID"; break;
             case 14278: msg = "Unable to generate FIND for %1 in DATA-SOURCE-ROWID"; break;
             case 14282: msg = "Unable to recurse during FILL"; break;
             case 14288: msg = "Invalid ROW-STATE given to MARK-ROW-STATE"; break;
             case 14289: msg = "Invalid handle or handle type for MARK-ROW-STATE"; break;
             case 14293: msg = "TOP-NAV-QUERY requires a valid query on top dataset buffer %1"; break;

             case 14435: msg = "Large object fields can only be defined in NO-UNDO Temp-Tables"; break;
             case 14500: msg = "Source file does not contain sufficient data starting at %1 to copy %2 bytes"; break; // no final DOT ?

             case 14902: msg = "Indeterminate extent error"; break;
             case 14904: msg = "Unable to get right-hand-side array for assignment"; break;
             case 14905: msg = "Whole-array assignment target and source must have the same extent unless the target is indeterminate"; break;
             case 14906: msg = "Uninitialized array used as source of assignment"; break;
             case 14907: msg = "Could not convert data for array assignment"; break;
             case 14908: msg = "Could not update indeterminate array in assignment"; break;
             case 14909: msg = "Unable to extract array %1"; break;
             case 14910: msg = "Dynamic BUFFER-VALUE array assignment for field %1 requires that extents match"; break;

             case 15008: msg = "You may not add before-table buffer %1 to a DATASET"; break;
             case 15342: msg = "Invalid encoding '%1' argument for %2"; break;
             case 15344: msg = "Invalid mode '%1' for %2"; break;
             case 15357: msg = "Invalid source-type for READ-JSON: %1"; break;
             case 15358: msg = "Error parsing JSON: expected %1, but found %2"; break;

             case 15360: msg = "Error parsing JSON: unexpected token: %1"; break;
             case 15364: msg = "Array token encountered, but %1 is not an array field"; break;
             case 15366: msg = "Unable to update indexes for table '%1'"; break;
             case 15374: msg = "Unable to infer Temp-Table or dataset schema from JSON Data"; break;
             case 15375: msg = "Dataset name '%1' in JSON does not match '%2'"; break;
             case 15376: msg = "Temp-table name '%1' in JSON does not match '%2'"; break;
             case 15378: msg = "JSON methods only allowed on TEMP-TABLE buffers"; break;
             case 15391: msg = "Unsupported data type for JSON serialization: %1"; break;

             case 15956: msg = "Only super-tenants may use a tenant-id other than their own in a sequence function"; break;
             case 15974: msg = "Second argument to dynamic sequence function must be a database name character expression"; break;

             case 15976: msg = "Database '%1' must be connected for tenant function %2"; break;
             case 15977: msg = "Must supply a connected database name for tenant function %1"; break;
             case 15991: msg = "Attempt to create in multi-tenant table '%1' where the partition (usually default) has not been allocated"; break;

             case 16017: msg = "No record in buffer for SERIALIZE-ROW"; break;
             case 16019: msg = "Invalid target-format '%1' for SERIALIZE-ROW"; break;
             case 16020: msg = "omit-outer-object argument not allowed in SERIALIZE-ROW for XML"; break; // this message DOES NOT have a "." at the end!
             case 16025: msg = "The argument to Dataset-handle:ADD-PARENT-ID-RELATION method was invalid."; break;
             case 16027: msg = "Buffers in ADD-PARENT-ID-RELATION must be members of the dataset"; break;
             case 16028: msg = "ADD-PARENT-ID-RELATION parent-id-field %1 is not valid"; break;
             case 16029: msg = "ADD-PARENT-ID-RELATION parent-fields-before entry %1 is not valid"; break;
             case 16030: msg = "ADD-PARENT-ID-RELATION parent-fields-after entry %1 is not valid"; break;
             case 16031: msg = "Invalid XML-NODE-TYPE %1 for Dataset %2"; break;
             case 16484: msg = "Duplicate field %1 found in PARENT-FIELDS-BEFORE / PARENT-FIELDS-AFTER"; break;

             case 16617: msg = "%1 is not a %2 attribute for %3"; break; // unlike 4052, this does not have ** prefix

             case 17092: msg = "Cannot set the DECIMALS attribute for a database field"; break;
             case 17093: msg = "DECIMALS attribute only allowed on DECIMAL datatypes"; break;
             case 17366: msg = "write-before-image not allowed for TEMP-TABLE or BUFFER object"; break;
             case 17586: msg = "Cannot serialize object of unsupported type '%1'"; break;

             case 19043: msg = "JsonArray is not a valid target-type for %1 method"; break;
             case 19079: msg = "%1 argument for %2 is not valid"; break;
             case 19080: msg = "JsonArray is not a valid source-type for READ-JSON on a ProDataset"; break;

             default:
                LOG.warning("Unknown error id: " + id);
          }

          if (params != null && params.length != 0)
          {
             StringBuilder sb = new StringBuilder();

             boolean[] used = new boolean[params.length];
             int lastWord = 0;
             int scan = msg.indexOf('%');
             while (scan >= 0)
             {
                sb.append(msg, lastWord, scan);
                boolean found = false;
                if (scan + 1 < msg.length())
                {
                   char ch = msg.charAt(scan + 1);
                   if (ch >= '1' && ch <= '9')
                   {
                      int idx = ch - '0' - 1;
                      if (idx < params.length)
                      {
                         sb.append(params[idx]);
                         used[idx] = true;
                      }
                      else
                      {
                         LOG.warning("Not enough positional parameters (" + idx + "+) in ErrorMessage (" + id + ")");
                      }
                      found = true;
                   }
                }

                if (!found)
                {
                   sb.append('%');
                   lastWord = scan + 1;
                }
                else
                {
                   lastWord = scan + 2;
                }

                scan = msg.indexOf('%', lastWord);
             }

             // append the rest of the message, which does not contain any positional markers:
             sb.append(msg.substring(lastWord));

             boolean remains = false;
             for (int i = 0; i < used.length; i++)
             {
                if (!used[i] && params[i] != null && !params[i].isEmpty())
                {
                   remains = true;
                   break;
                }
             }

             if (remains)
             {
                LOG.warning("Unused positional parameter in ErrorMessage (" + id + ")");
             }

             msg = sb.toString();
          }
          return msg;
       }

       /**
        * Set the ERROR-OBJECT-DETAIL attribute, a handle of type SOAP-FAULT.
        * <p>
        * If the handle is valid and the referred resource is not a {@link SOAPFault} instance, then
        * this will throw a {@link RuntimeException}.
        * <p>
        * If the handle is {@code null} or invalid, the {@link WorkArea#pendingErrorStatus} 's
        * <code>soapFault</code> field will be set to {@code null}.
        *
        * @param   fault
        *          The resource representing the soap-fault.
        */
       static void setErrorObjectDetail(SOAPFaultImpl fault)
       {
          handle hFault = new handle();
          // we need to force assignment, as passing the resource at the c'tor will not save it,
          // because at this step we are in no-error mode.
          hFault.set(fault);

          work.get().pendingErrorStatus.soapFault = hFault;
       }

       /**
        * Get an iterator on Progress-style error text strings, derived from the
        * error information stored in <code>error</code> (including its possible
        * chain of root causes).
        *
        * @param   error
        *          A throwable which is an instance of
        *          <code>NumberedException</code> or which contains instances
        *          thereof.
        *
        * @return  Iterator on error text strings.
        */
       static Iterator<String> errorTexts(Throwable error)
       {
          return errorTexts(compileErrorEntries(error));
       }

       /**
        * Get an iterator on Progress-style error text strings, derived from the
        * error information stored in <code>error</code> (including its possible
        * chain of root causes), and from the given error number and text.
        *
        * @param   num
        *          Error number of the first message to occur in the resulting
        *          iteration.
        * @param   text
        *          Error text of the first message to occur in the resulting
        *          iteration.
        * @param   error
        *          A throwable which is an instance of
        *          <code>NumberedException</code> or which contains instances
        *          thereof.
        *
        * @return  Iterator on error text strings.
        */
       static Iterator<String> errorTexts(int num, String text, Throwable error)
       {
          List<ErrorEntry> list = compileErrorEntries(error);
          ErrorEntry entry = new ErrorEntry(num, text);
          list.add(entry);

          return errorTexts(list);
       }

       /**
        * Get an iterator on Progress-style error text strings, based upon the
        * <code>ErrorEntry</code> objects stored in a list.
        *
        * @param   entries
        *          List of error entries.
        *
        * @return  Iterator on error text strings.
        */
       private static Iterator<String> errorTexts(final List<ErrorEntry> entries)
       {
          return new Iterator<String>()
          {
             private final Iterator<ErrorEntry> iter = entries.iterator();

             @Override
             public boolean hasNext()
             {
                return iter.hasNext();
             }

             @Override
             public String next()
             {
                ErrorEntry entry = iter.next();

                return buildErrorText(entry.num, entry.text, entry.prefix);
             }

             @Override
             public void remove()
             {
                throw new UnsupportedOperationException();
             }
          };
       }

       /**
        * Extract from <code>error</code> and any chained root cause exceptions
        * of type {@link com.goldencode.p2j.NumberedException NumberedException}
        * error messages and numbers with which to compile a list of error
        * entries.  This list is compiled in reverse order of the root cause
        * chain traversal, such that the most specific errors appear earliest in
        * the list.  If no instances of <code>NumberedException</code> are
        * detected during the traversal, the list will be empty.
        *
        * @param   error
        *          Error from which to extract error messages and numbers.
        *
        * @return  List of <code>ErrorEntry</code> objects.
        */
       private static List<ErrorEntry> compileErrorEntries(Throwable error)
       {
          List<ErrorEntry> list = new LinkedList<>();

          Throwable next = error;
          do
          {
             if (next instanceof NumberedException)
             {
                NumberedException exc = (NumberedException) next;
                int num        = exc.getNumber();
                String msg     = exc.getMessage();
                boolean prefix = exc.isPrefix();
                ErrorEntry entry = new ErrorEntry(num, msg, prefix);
                list.add(0, entry);
             }
             next = next.getCause();
          }
          while (next != null);

          return list;
       }

       /**
        * Write a formatted log entry with the error text.  In silent error mode
        * generate a warning and in normal mode generate a severe entry.
        *
        * @param    message
        *           The text to insert into the log.
        * @param    silent
        *           <code>true</code> if silent error mode is active.
        */
       private static void log(String message, boolean silent)
       {
          log(message, null, silent);
       }

       /**
        * Write a formatted log entry with the error text.  In silent error mode
        * generate a warning and in normal mode generate a severe entry.
        *
        * @param    message
        *           The text to insert into the log.
        * @param    trw
        *           The cause of the failure.
        * @param    silent
        *           <code>true</code> if silent error mode is active.
        */
       private static void log(String message, Throwable trw, boolean silent)
       {
          if (isHeadless())
          {
             return;
          }

          Level lvl = silent || hasLegacyError() ? Level.FINE : Level.SEVERE;

          if (LOG.isLoggable(lvl))
          {
             String msg = silent ? "(silent mode) " + message : message;
             if (trw == null)
             {
                LOG.log(lvl, msg);
             }
             else
             {
                LOG.log(lvl, trw, msg);
             }
          }
       }

       /**
        * Perform postprocessing for {@link #showErrorAndAbend(int[], String[])}.
        *
        * @param   text
        *          Message text.
        */
       private static void postprocessOnAbend(String[] text)
       {
          StringBuilder sb = new StringBuilder();
          String sep = System.getProperty("line.separator");

          for (int i = 0; i < text.length; i++)
          {
             sb.append(text[i]);

             if (i != text.length - 1)
             {
                sb.append(sep);
             }
          }

          throw new StopConditionException(sb.toString());
       }

       /**
        * Reports on the context-local {@code silent} flag.
        *
        * @param    wa
        *           Context-local error state.
        *
        * @return   {@code true} if silent error mode is active.
        */
       private static boolean isSilentWorker(WorkArea wa)
       {
          return wa.silent;
       }

       /**
        * Throws an {@link ErrorConditionException} and if in silent mode, it sets the
        * <code>pending</code> flag to <code>true</code>.  NO error information (messages or
        * error numbers) will be recorded in either case (i.e. silent mode or not).  In silent
        * mode the <code>ERROR-STATUS:ERROR</code> will be <code>true</code> but the other "records"
        * of the error that normally exist, will not exist. No message will be displayed.  Because
        * no message is ever displayed or recorded, the normal error number/message processing
        * is not executed.
        *
        * @param    wa
        *           Context-local error state.
        * @param    msg
        *           The text to use in throwing the error exception.
        */
       private static void noRecordOrThrowErrorWorker(WorkArea wa, String msg)
       {
          if (isSilentWorker(wa))
          {
             // ERROR-STATUS:ERROR flag is set, but no error is recorded
             setPendingWorker(wa, true);
          }
          else
          {
             writeLog(msg);
          }

          // raise the ERROR condition
          throw new ErrorConditionException(msg);
       }

       /**
        * Worker method that executes a given block of code with silent error mode turned on for
        * the duration of the code block.  Before the code block is executed, silent error mode
        * is turned on and after the code block completes (normally or abnormally), silent error
        * mode will be disabled.
        * <p>
        * This method can only be executed on the server side.
        *
        * @param    code
        *           The block of code to execute in silent mode.
        * @param    srv
        *           Server-side error workers.
        * @param    wa
        *           Context-local error state.
        *
        * @return   The value of the ERROR-STATUS:ERROR flag when the execution is complete.
        */
       private static boolean silentWorker(Runnable code, ServerErrorDataAccessor srv, WorkArea wa)
       {
          // enable silent error processing
          boolean wasSilent = srv.isSilent();
          if (!wasSilent)
          {
             srv.setSilent(wa, true);
          }

          try
          {
             code.run();
          }
          catch (LegacyErrorException lex)
          {
             srv.setPending(true);

             LegacyError err = lex.getErrorRef();
             if (!(err instanceof AppError) || !((AppError) err).isFromReturn())
             {
                ProError proErr = (ProError) err;
                int numErr = err.getNumMessages().intValue();
                int[] errnums = new int[numErr];
                String[] errmsgs = new String[numErr];

                for (int i = 0; i < numErr; i++)
                {
                   errnums[i] = proErr.getMessageNum(new integer(i + 1)).intValue();
                   errmsgs[i] = proErr.getMessage(new integer(i + 1)).toStringMessage();
                   wa.pendingErrorStatus.addError(new ErrorEntry(errnums[i], errmsgs[i]));
                }
                addRaisedConditionWorker(wa, errnums);
             }
             if (lex.getError()._isValid())
             {
                ObjectOps.delete(err);
             }
          }
          catch (ErrorConditionException err)
          {
             // normally we just eat the exception because it is being used to abort processing
             // exactly where the error occurred, which will restart processing after the given
             // code block; however, there is an exception for some deferred error processing use
             // cases where we must bypass silent error mode (throwError(NumberedException, boolean))
             if (err.isForce())
             {
                // don't honor silent mode, no suppression of errors here
                throw err;
             }
          }
          finally
          {
             // disable further silent error processing
             if (!wasSilent)
             {
                srv.setSilent(wa, false);
             }

             forwardPending(wa);
          }

          return wa.errorStatus.error;
       }

       /**
        * Execute the given block while supressing normal error processing by temporarily enabling the
        * ignore flag.
        *
        * @param    code
        *           The block of code to execute in ignore mode.
        * @param    wa
        *           Context-local error state.
        */
       private static void ignoreWorker(Runnable code, WorkArea wa)
       {
          wa.ignore++;

          try
          {
             code.run();
          }

          finally
          {
             wa.ignore--;
          }
       }

       /**
        * Queries the state of <code>ignore</code> mode.
        *
        * @param    wa
        *           Context-local error state.
        *
        * @return   <code>true</code> if ignore mode is enabled, otherwise <code>false</code>.
        */
       private static boolean isIgnoreWorker(WorkArea wa)
       {
          return wa.ignore > 0;
       }

       /**
        * Determines if headless mode should be supported. In headless mode, no
        * client-side output will occur.
        *
        * @param    wa
        *           Context-local error state.
        *
        * @return   <code>true</code> if the headless flag is turned on, otherwise <code>false</code>.
        */
       private static boolean isHeadlessWorker(WorkArea wa)
       {
          return headless || wa.headless || wa.canFindLevel > 0;
       }

       /**
        * Adds an error entry to the error list.
        *
        * @param    wa
        *           Context-local error state.
        * @param    num
        *           The error number.
        * @param    text
        *           The text of the error message.
        * @param    errFlag
        *           If <code>false</code> this should add the number and text to
        *           the error list but not set the error flag.  If
        *           <code>true</code> the error flag should also be set.
        * @param    prefix
        *           <code>true</code> to prepend a double asterisk prefix to the
        *           formatted error message, <code>false</code> to omit this text.
        */
       private static void addErrorWorker(WorkArea wa, int num, String text, boolean errFlag, boolean prefix)
       {
          if (errFlag)
          {
             // this may reset the error list so it must be done BEFORE we add the new error to the list
             setPendingWorker(wa, true);
          }

          String errmsg = buildErrorText(num, text, prefix, true);

          // manually using the work area is OK in this case since this method is only used on the server
          wa.pendingErrorStatus.addError(new ErrorEntry(num, errmsg));

          // add error number to _MSG stack.
          addRaisedConditionWorker(wa, new int[] { num });
       }

       /**
        * Sets the <code>pendingError</code> variable.
        *
        * @param    wa
        *           Context-local error state.
        * @param    value
        *           The new state of the variable.
        */
       private static void setPendingWorker(WorkArea wa, boolean value)
       {
          wa.pendingErrorStatus.error = value;
       }

       /**
        * Makes pending error status current.
        *
        * @param    wa
        *           Context-local error state.
        */
       private static void forwardPending(WorkArea wa)
       {
          wa.errorStatus.set(wa.pendingErrorStatus);
          wa.pendingErrorStatus.initialize();
       }

       /**
        * Gets err nums for raised errors.
        *
        * @param    wa
        *           Context-local error state.
        *
        * @return   stack of errors numbers
        */
       private static LinkedList<Integer> getRaisedConditionsWorker(WorkArea wa)
       {
          return wa.raisedConditions;
       }

       /**
        * Adds error numbers to raised error numbers stack (for _MSG function).
        *
        * @param    errNums
        *           errors numbers.
        */
       private static void addRaisedCondition(int[] errNums)
       {
          addRaisedConditionWorker(work.obtain(), errNums);
       }

       /**
        * Adds error numbers to raised error numbers stack (for _MSG function).
        *
        * @param    wa
        *           Context-local error state.
        * @param    errNums
        *           errors numbers.
        */
       private static void addRaisedConditionWorker(WorkArea wa, int[] errNums)
       {
          if (isSilent() || errNums == null)
          {
             return;
          }

          LinkedList<Integer> raisedConditions = getRaisedConditionsWorker(wa);
          for (int errNum : errNums)
          {
             raisedConditions.addFirst(errNum);
             if (raisedConditions.size() > ERR_CONDITIONS_CAPACITY)
             {
                raisedConditions.removeLast();
             }
          }
       }

       /**
        * Helper to expose error state in a way that avoids context local lookups.
        */
       public static class ErrorHelper
       {
          /** Context-local error state. */
          private WorkArea wa;

          /** Server-side error methods. */
          private ServerErrorDataAccessor srv;

          /**
           * Create a new instance and associate the given WorkArea instance.
           *
           * @param    wa
           *           Context-local error state.
           * @param    srv
           *           Server-side error methods.
           */
          public ErrorHelper(WorkArea wa, ServerErrorDataAccessor srv)
          {
             this.wa  = wa;
             this.srv = srv;
          }

          /**
           * Worker method that executes a given block of code with silent error mode turned on for
           * the duration of the code block.  Before the code block is executed, silent error mode
           * is turned on and after the code block completes (normally or abnormally), silent error
           * mode will be disabled.
           * <p>
           * This method can only be executed on the server side.  It will backup and restore the current
           * {@link WorkArea#errorStatus}, to allow for nested silent-mode usages (internally by FWD).
           *
           * @param    code
           *           The block of code to execute in silent mode.
           * @param    errorProcessor
           *           A consumer of the logged errors in silent mode.
           *
           * @return   The value of the ERROR-STATUS:ERROR flag when the execution is complete.
           */
          public boolean nestedSilent(Runnable code, BiConsumer<String, Integer> errorProcessor)
          {
             return ErrorManager.nestedSilent(wa, code, errorProcessor);
          }

          /**
           * Reflects the value of SESSION:SUPPRESS-WARNINGS attribute.
           *
           * @return   The suppress warnings flag.
           */
          public boolean isSuppressWarnings()
          {
             return wa.suppressWarnings;
          }

          /**
           * Reflects the value of SESSION:SUPPRESS-WARNINGS-LIST attribute.
           *
           * @return The suppress warnings list flag.
           */
          public String getSuppressWarningsList()
          {
             return wa.suppressWarningsList;
          }

          /**
           * Reports on the context-local {@code silent} flag.
           *
           * @return   {@code true} if silent error mode is active.
           */
          public boolean isSilent()
          {
             return isSilentWorker(wa);
          }

          /**
           * Sets the <code>silent</code> variable.
           *
           * @param   value
           *          The new state of the variable.
           */
          public void setSilent(boolean value)
          {
             srv.setSilent(wa, value);
          }

          /**
           * Worker method that executes a given block of code with silent error mode turned on for
           * the duration of the code block.  Before the code block is executed, silent error mode
           * is turned on and after the code block completes (normally or abnormally), silent error
           * mode will be disabled.
           * <p>
           * This method can only be executed on the server side.
           *
           * @param    code
           *           The block of code to execute in silent mode.
           *
           * @return   The value of the ERROR-STATUS:ERROR flag when the execution is complete.
           */
          public boolean silent(Runnable code)
          {
             return silentWorker(code, srv, wa);
          }

          /**
           * Queries the state of <code>pendingError</code> variable.
           *
           * @return   <code>true</code> if pending error flag is turned on, otherwise
           *           <code>false</code>.
           */
          public boolean isPending()
          {
             return wa.pendingErrorStatus.error;
          }

          /**
           * Sets the <code>pendingError</code> variable.
           *
           * @param    value
           *           The new state of the variable.
           */
          public void setPending(boolean value)
          {
             setPendingWorker(wa, value);
          }

          /**
           * Clears pending error status.
           */
          public void clearPending()
          {
             wa.pendingErrorStatus.initialize();
          }

          /**
           * Handles the change of the pending error status, making it
           * into the current error status.
           */
          public void handleForwardPending()
          {
             forwardPending(wa);
          }

          /**
           * Retrieves the ERROR-STATUS:ERROR of the current WorkArea.
           *
           * @return  value of ERROR-STATUS:ERROR of the current WorkArea.
           */
          public boolean getErrorStatus()
          {
             return wa.errorStatus.error;
          }

          /**
           * Determines if headless mode should be supported. In headless mode, no
           * client-side output will occur.
           *
           * @return   <code>true</code> if the headless flag is turned on, otherwise <code>false</code>.
           */
          public boolean isHeadless()
          {
             return isHeadlessWorker(wa);
          }

          /**
           * Set the headless flag for this context.
           *
           * @param    headless
           *           {@code true} if this context has no UI.
           */
          public void setHeadless(boolean headless)
          {
             wa.headless = headless;
          }

          /**
           * Set the headless flag for this context.
           *
           * @param    headless
           *           {@code true} if this context has no UI.
           */
          public void setStopAfterTimer(StopAfterTimer stopAfterTimer)
          {
             wa.stopAfterTimer = stopAfterTimer;
          }

          /**
           * Detects if the temporary CAN-FIND headless mode is active.  This really should be something
           * generic instead of being hard coded to the persistence package.
           *
           * @return   <code>true</code> if this is a CAN-FIND bracket.
           */
          public boolean isInCanFindBracket()
          {
             return wa.canFindLevel > 0;
          }

          /**
           * Sets silent mode during CAN-FIND calls. The semaphore keep tracks of the nested CAN-FIND
           * calls.
           * If the counter is positive <code>ErrorManager</code> will run similar to headless mode
           * and will silently discard error messages.
           * <p>
           * <strong>Note</strong>Always call this method in pairs (one with enter = true before
           * executing the query and, one with enter = false, immediately after) to assure the
           * correct management of the headless context flag.
           *
           * @param   enter
           *          If <code>true</code> will increment the nested CAN-FIND count.
           */
          public void setCanFindBracket(boolean enter)
          {
             if (enter)
             {
                // push a CAN-FIND level
                wa.canFindLevel++;
             }
             else
             {
                // pop put CAN-FIND level:
                wa.canFindLevel--;
             }
          }

          /**
           * Execute the given block while supressing normal error processing by temporarily enabling the
           * ignore flag.
           *
           * @param    code
           *           The code to execute.
           */
          public void ignore(Runnable code)
          {
             ignoreWorker(code, wa);
          }

          /**
           * Adds error numbers to raised error numbers stack
           *
           * @param    e
           *           The condition exception meant to be iterated for causes
           */
          public void addNumbersToConditionException(ConditionException e)
          {
             boolean found = false;

             for (Throwable cause = e; cause != null; cause = cause.getCause())
             {
                if (cause instanceof NumberedException)
                {
                   addRaisedConditionWorker(wa, ((NumberedException)cause).getNumbers());
                   found = true;
                   break;
                }
             }

             if (!found)
             {
                addRaisedConditionWorker(wa, new int[]{0});
             }
          }

          /**
           * Adds an error entry to the error list.
           *
           * @param    num
           *           The error number.
           * @param    text
           *           The text of the error message.
           * @param    errFlag
           *           If <code>false</code> this should add the number and text to
           *           the error list but not set the error flag.  If
           *           <code>true</code> the error flag should also be set.
           * @param    prefix
           *           <code>true</code> to prepend a double asterisk prefix to the
           *           formatted error message, <code>false</code> to omit this text.
           */
          public void addError(int num, String text, boolean errFlag, boolean prefix)
          {
             addErrorWorker(wa, num, text, errFlag, prefix);
          }

          /**
           * Throws an {@link ErrorConditionException} and if in silent mode, it sets the
           * <code>pending</code> flag to <code>true</code>.  NO error information (messages or
           * error numbers) will be recorded in either case (i.e. silent mode or not).  In silent
           * mode the <code>ERROR-STATUS:ERROR</code> will be <code>true</code> but the other "records"
           * of the error that normally exist, will not exist. No message will be displayed.  Because
           * no message is ever displayed or recorded, the normal error number/message processing
           * is not executed.
           * <p>
           * This only works on the server side.
           *
           * @param    msg
           *           The text to use in throwing the error exception.
           */
          public void noRecordOrThrowError(String msg)
          {
             noRecordOrThrowErrorWorker(wa, msg);
          }

          /**
           * Reports if warning mode has been enabled. When in warning mode, error messages are reported to the UI
           * using an alert box.
           *
           * @return   {@code true} if warning mode is enabled, otherwise {@code false}.
           */
          public boolean isWarningMode()
          {
             return wa.errorsAsWarnings;
          }

          /**
           * Configures warning mode, during which errors are non-fatal (when {@code on} is {@code true}), but are
           * reported to the UI of the current context via a call to {@code LogicalTerminal.message()}.
           * <p>
           * Each call to this method must be matched with a corresponding call to {@link #warningModeDisable}.
           *
           * @param   on
           *          Pass {@code true} to enable reported but non-fatal errors, and {@code false} to handle
           *          errors normally.
           */
          public void setWarningMode(boolean on)
          {
             wa.errorsAsWarnings = on;
          }
       }

       /**
        * Stores the data related to a single error.
        */
       public static class ErrorEntry
       implements Serializable
       {
          /** Error number. */
          public final int num;

          /** Error text. */
          public final String text;

          /** Determines if the prefix should be displayed for the error message. */
          public final boolean prefix;

          /**
           * Construct an instance.
           *
           * @param    num
           *           The error number.
           * @param    text
           *           The text of the error message.
           */
          public ErrorEntry(int num, String text)
          {
             this.num  = num;
             this.text = text;
             this.prefix = true;
          }

          /**
           * Construct an instance.
           *
           * @param    num
           *           The error number.
           * @param    text
           *           The text of the error message.
           * @param    prefix
           *           Determines if the prefix should be displayed for the error message.
           */
          public ErrorEntry(int num, String text, boolean prefix)
          {
             this.num  = num;
             this.text = text;
             this.prefix = prefix;
          }
       }

       /**
        * Process errors with ignore.
        */
       private static class SuppressedErrorDataAccessor
       extends ServerErrorDataAccessor
       {
          /**
           * Sets the {@code silent} variable.
           *
           * @param    value
           *           The new state of the variable.
           */
          @Override
          public void setSilent(boolean value)
          {
             setSilent(work.obtain(), value);
          }

          /**
           * Sets the {@code silent} variable.
           *
           * @param    wa
           *           Context local data storage.
           * @param    value
           *           The new state of the variable.
           */
          @Override
          protected void setSilent(WorkArea wa, boolean value)
          {
             wa.silent = value;
          }

          /**
           * Check if the top-level block is ROUTINE-LEVEL UNDO, THROW or the current program is
           * BLOCK-LEVEL UNDO, THROW.
           *
           * @return  see above.
           */
          @Override
          public boolean mustManageLegacyError()
          {
             return false;
          }

          /**
           * Check if the current ERROR condition must be raised as a legacy {@link SysError}.
           *
           * @param   forceBlockLevelUndoThrow
           *          Flag indicating that, when nothing else matches, a BLOCK-LEVEL UNDO, THROW exists by default.
           *
           * @return  see above.
           */
          @Override
          public boolean mustManageLegacyError(boolean forceBlockLevelUndoThrow)
          {
             return false;
          }

          /**
           * Check if the current ERROR condition must be raised as a legacy {@link SysError}.
           *
           * @return  see above.
           */
          @Override
          public boolean mustThrowLegacyError()
          {
             return false;
          }

          /**
           * Check if the current ERROR condition must be raised as a legacy {@link SysError}.
           *
           * @param   forceBlockLevelUndoThrow
           *          Flag indicating that, when nothing else matches, a BLOCK-LEVEL UNDO, THROW exists by default.
           *
           * @return  see above.
           */
          @Override
          public boolean mustThrowLegacyError(boolean forceBlockLevelUndoThrow)
          {
             return false;
          }

          /**
           * Record the specified error message and number, to be later thrown as a
           * {@link LegacyErrorException}.
           *
           * @param    errmsg
           *           The error message.
           * @param    num
           *           The error number.
           */
          @Override
          public void recordForLegacyThrow(String errmsg, int num)
          {
             // no-op
          }

          /**
           * Writes a log record.
           *
           * @param    msg
           *           The log message.
           * @param    logCallStack
           *           Flag to indicate if call stack should be included in log.
           * @param    threadId
           *           The id of the thread recording the error message.
           */
          @Override
          public void writeLog(String msg, boolean logCallStack, long threadId)
          {
             // no-op
          }
       }

       /**
        * Provides access to the error data.
        */
       private static class ServerErrorDataAccessor
       implements RemoteErrorData
       {
          /**
           * Queries the state of <code>silent</code> variable.
           *
           * @return   <code>true</code> if silent mode is enabled, otherwise
           *           <code>false</code>.
           */
          @Override
          public boolean isSilent()
          {
             return work.obtain().silent;
          }

          /**
           * Queries the state of <code>error</code> variable.
           *
           * @return   <code>true</code> if error flag is turned on, otherwise
           *           <code>false</code>.
           */
          @Override
          public boolean isErrorFlag()
          {
             return work.obtain().errorStatus.error;
          }

          /**
           * Sets the <code>pendingError</code> variable.
           *
           * @param    value
           *           The new state of the variable.
           */
          @Override
          public void setPending(boolean value)
          {
             setPendingWorker(work.obtain(), value);
          }

          /**
           * Gets the size of the list of error records.
           *
           * @return   The size of the list.
           */
          @Override
          public int getSize()
          {
             List<Object> errors = work.obtain().errorStatus.errorList;
             return errors == null ? 0 : errors.size();
          }

          /**
           * Adds a record to the list of error records.
           *
           * @param    record
           *           The object to add to the list of error records.
           */
          @Override
          public void addRecord(Object record)
          {
             work.obtain().pendingErrorStatus.addError(record);
          }

          /**
           * Gets a record from the list of error records.
           *
           * @param    index
           *           The record index.
           *
           * @return   The object associated with the given index in the list.
           */
          @Override
          public Object getRecord(int index)
          {
             return work.obtain().errorStatus.getError(index);
          }

          /**
           * Queries the state of <code>pendingError</code> variable.
           *
           * @return   <code>true</code> if pending error flag is turned on, otherwise
           *           <code>false</code>.
           */
          @Override
          public boolean isPending()
          {
             return work.obtain().pendingErrorStatus.error;
          }

          /**
           * Reflects the value of SESSION:SUPPRESS-WARNINGS attribute.
           *
           * @return see above.
           */
          @Override
          public boolean isSuppressWarnings()
          {
             return work.obtain().suppressWarnings;
          }

          /**
           * Reflects the value of SESSION:SUPPRESS-WARNINGS-LIST attribute.
           *
           * @return see above.
           */
          @Override
          public String getSuppressWarningsList()
          {
             return work.obtain().suppressWarningsList;
          }

          /**
           * Get the state of SESSION:SYSTEM-ALERT-BOXES attribute.
           *
           * @return  the state of SESSION:SYSTEM-ALERT-BOXES attribute.
           */
          @Override
          public boolean isSystemAlertBoxes()
          {
             return work.obtain().systemAlertBoxes;
          }

          /**
           * Sets the <code>silent</code> variable.
           *
           * @param   value
           *          The new state of the variable.
           */
          @Override
          public void setSilent(boolean value)
          {
             setSilent(work.obtain(), value);
          }

          /**
           * Sets the {@code silent} variable.
           *
           * @param    wa
           *           Context local data storage.
           * @param    value
           *           The new state of the variable.
           */
          protected void setSilent(WorkArea wa, boolean value)
          {
             wa.silent = value;

             if (value)
             {
                // we can't delete the soapFault handle here, as this might be a call for accessing
                // some soap-fault attribute/method with the NO-ERROR clause set!
             }
             else
             {
                if (wa.errorStatus.soapFault != null && wa.errorStatus.soapFault._isValid())
                {
                   // delete it only if we haven't deleted it already
                   HandleOps.delete(wa.errorStatus.soapFault);
                }

                wa.errorStatus.soapFault = wa.pendingErrorStatus.soapFault;
                wa.pendingErrorStatus.soapFault = null;
             }
          }

          /**
           * Check if the top-level block is ROUTINE-LEVEL UNDO, THROW or the current program is
           * BLOCK-LEVEL UNDO, THROW.
           *
           * @return  see above.
           */
          @Override
          public boolean mustManageLegacyError()
          {
             return ErrorManager.mustManageLegacyError();
          }

          /**
           * Check if the current ERROR condition must be raised as a legacy {@link SysError}.
           *
           * @param   forceBlockLevelUndoThrow
           *          Flag indicating that, when nothing else matches, a BLOCK-LEVEL UNDO, THROW exists by default.
           *
           * @return  see above.
           */
          @Override
          public boolean mustManageLegacyError(boolean forceBlockLevelUndoThrow)
          {
             return ErrorManager.mustManageLegacyError(forceBlockLevelUndoThrow);
          }

          /**
           * Check if the current ERROR condition must be raised as a legacy {@link SysError}.
           *
           * @return  see above.
           */
          @Override
          public boolean mustThrowLegacyError()
          {
             return ErrorManager.mustThrowLegacyError();
          }

          /**
           * Check if the current STOP condition must be raised as a legacy {@link Stop}.
           *
           * @return  see above.
           */
          public boolean mustThrowLegacyStop()
          {
             return ErrorManager.mustThrowLegacyStop();
          }

          /**
           * Check if the current ERROR condition must be raised as a legacy {@link SysError}.
           *
           * @param   forceBlockLevelUndoThrow
           *          Flag indicating that, when nothing else matches, a BLOCK-LEVEL UNDO, THROW exists by default.
           *
           * @return  see above.
           */
          @Override
          public boolean mustThrowLegacyError(boolean forceBlockLevelUndoThrow)
          {
             return ErrorManager.mustThrowLegacyError(forceBlockLevelUndoThrow);
          }

          /**
           * Record the specified error message and number, to be later thrown as a
           * {@link LegacyErrorException}.
           *
           * @param    errmsg
           *           The error message.
           * @param    num
           *           The error number.
           */
          @Override
          public void recordForLegacyThrow(String errmsg, int num)
          {
             ErrorManager.recordForLegacyThrow(errmsg, num);
          }

          /**
           * Record the STOP condition, to be later thrown as a
           * {@link LegacyStopException}.
           *
           * @param    errmsg
           *           The error message.
           * @param    num
           *           The error number.
           */
          public void recordForLegacyStop(StopConditionException ex)
          {
             ErrorManager.recordForLegacyStop(ex);
          }

          /**
           * Throw the specified error message and number.
           *
           * @param    num
           *           The error number.
           * @param    errmsg
           *           The error message.
           */
          @Override
          public void throwErrorConditionException(int num, String errmsg)
          {
             // throw on the Conversation thread
             throw new ErrorConditionException(num, errmsg);
          }

          /**
           * Writes a log record.
           *
           * @param    msg
           *           The log message.
           * @param    logCallStack
           *           Flag to indicate if call stack should be included in log.
           * @param    threadId
           *           The id of the thread recording the error message.
           */
          @Override
          public void writeLog(String msg, boolean logCallStack, long threadId)
          {
             String[] callStack = !logCallStack ? null : ProcedureManager.getStackTrace().toArray(new String[0]);
             LegacyLogOps.logMgr().writeMessage(msg,
                                                null,
                                                null,
                                                LoggingSubsys.EMPTY,
                                                LoggingLevel.ERROR,
                                                callStack,
                                                threadId);
          }

          /**
           * Handles a SOAP fault.
           *
           * @param    operationName
           *           The operation that failed.
           * @param    faultMsg
           *           The fault error msg.
           * @param    faultActor
           *           The fault actor.
           * @param    faultCode
           *           The fault code.
           * @param    faultString
           *           The fault string.
           * @param    faultDetailXML
           *           The fault details as xml.
           */
          @Override
          public void handleSoapFault(String operationName,
                                      String faultMsg,
                                      String faultActor,
                                      String faultCode,
                                      String faultString,
                                      String faultDetailXML)
          {
             final String msg = "Web service operation %s generated a SOAP Fault. SOAP faultstring is: %s";
             recordOrThrowError(new int[] { 11506 },
                                new String[] { String.format(msg, operationName, faultMsg) },
                                false,
                                true);

             // if this is reached, we are in silent-error mode, thus generate the SOAP-fault object
             SOAPFaultDetailImpl soapFaultDetail = SOAPFactory.createSOAPFaultDetail(faultDetailXML);
             SOAPFaultImpl soapFault = SOAPFactory.createSOAPFault(
                faultActor, faultCode, faultString, soapFaultDetail);

             setErrorObjectDetail(soapFault);
          }
       }

       /**
        * Stores global data relating to the state of the current context's
        * transactions.
        */
       private static class WorkArea
       {
          /** The resource's ID. */
          private Long id = null;

          /** Current error status value. */
          private ErrorStatus errorStatus = new ErrorStatus();

          /** Pending error status value. */
          private final ErrorStatus pendingErrorStatus = new ErrorStatus();

          /** No error flag (if <code>true</code> exceptions are silently recorded rather than thrown). */
          private boolean silent = false;

          /**
           * Flags when a query is executed in a CAN-FIND function.
           * In a CAN-FIND, errors are "swallowed" similar to headless mode.
           */
          private int canFindLevel = 0;

          /** <code>true</code> to NO-OP all error recording, display and exception generation. */
          private int ignore = 0;

          /** Whether exceptions are processed as non-fatal, warning messages. */
          private boolean errorsAsWarnings = false;

          /** {@code true} to mark this context as having NO UI. */
          private boolean headless = false;

          /**
           * Raised errors numbers stack. Used for undocumented _MSG function implementation.
           * Can contain &le;25 elements, indexed starting from 1.
           */
          private final LinkedList<Integer> raisedConditions = new LinkedList<>();

          /** Reflects the value of SESSION:SUPPRESS-WARNINGS attribute. */
          private boolean suppressWarnings = false;

          /** Reflects the value of SESSION:SUPPRESS-WARNINGS-LIST attribute. */
          private String suppressWarningsList = "";

          /** Reflects the value of SESSION:SUPPRESS-WARNINGS-LIST attribute. */
          private Set<Integer> suppressWarningsSet = new HashSet<>();

          /** Holds the state of the SESSION:SYSTEM-ALERT-BOXES attribute. */
          private boolean systemAlertBoxes = false;

          /** The legacy-style OE error which is being built. */
          private object<? extends SysError> legacyError = null;

          /** The legacy-style OE STOP error which is being built. */
          private object<? extends Stop> legacyStop = null;

          /** Flag indicating that the deferred {@link #legacyError} must be marked as explicit. */
          private boolean forceExplicit = false;

          /** Object responsible for managing stop-after on Progress blocks. */
          private StopAfterTimer stopAfterTimer = null;
       }

       /**
        * Simple container that stores and returns a context-local instance of
        * the global work area.
        */
       private static class ContextContainer
       extends ContextLocal<WorkArea>
       {
          /**
           * Get the weight of this context-local var.
           *
           * @return   Always {@link WeightFactor#LAST}.
           */
          @Override
          public WeightFactor getWeight()
          {
             return WeightFactor.LAST;
          }

          /**
           * Obtains the context-local instance of the contained global work
           * area.
           *
           * @return   The work area associated with this context.
           */
          public WorkArea obtain()
          {
             return this.get();
          }

          /**
           * Initializes the work area, the first time it is requested within a
           * new context.
           *
           * @return   The newly instantiated work area.
           */
          @Override
          protected synchronized WorkArea initialValue()
          {
             return new WorkArea();
          }
       }

       /**
        * This class corresponds ERROR-STATUS p2j handle, that can contain supressed errors
        * information in case of NO-ERROR expression used in P4GL statement.
        */
       private static class ErrorStatus
       {
          /** List of errors. */
          private ArrayList<Object> errorList = null;

          /** Stores whether an error condition has occurred previously. */
          private boolean error = false;

          /**
           * Handle containing the value of the ERROR-OBJECT-DETAIL attribute set by the last statement
           * executed with a NO-ERROR clause.
           */
          private handle soapFault = null;

          /**
           * Add an error entry to the {@link #errorList}.
           *
           * @param    error
           *           The error to add.
           */
          private void addError(Object error)
          {
             if (errorList == null)
             {
                errorList = new ArrayList<>(1);
             }

             errorList.add(error);
          }

          /**
           * Get an error from the {@link #errorList}.
           *
           * @param    index
           *           The index.
           *
           * @return   See above.
           */
          public Object getError(int index)
          {
             if (errorList == null)
             {
                throw new ArrayIndexOutOfBoundsException();
             }

             return errorList.get(index);
          }

          /**
           * Sets all fields to initial state.
           */
          private void initialize()
          {
             if (this.errorList != null)
             {
                this.errorList.clear();
             }

             this.error = false;
             this.soapFault = null;
          }

          /**
           * Sets all fields with values from specified errStatus.
           *
           * @param   errStatus
           *          source error status.
           */
          private void set(ErrorStatus errStatus)
          {
             if (errStatus.errorList != null && !errStatus.errorList.isEmpty())
             {
                if (this.errorList == null)
                {
                   this.errorList = new ArrayList<>();
                }
                else
                {
                   this.errorList.clear();
                }

                this.errorList.addAll(errStatus.errorList);
             }
             else
             {
                this.errorList = null;
             }

             this.error = errStatus.error;
             this.soapFault = errStatus.soapFault;
          }
       }
    }