Question OOABL, BLOCK-LEVEL ON ERROR UNDO, THROW. & CATCH eAppError

Cecil

19+ years progress programming and still learning.
I have a class object which has a set of properties. Some of these properties use the SET implementation for validation. If the validation fails for the setting of the property value, I RETURN ERROR AppError.

I have the CATCH block which catches these errors. But what I would like to be able to do is CATCH the error, do something with it, and move on to the next property. I don't want my code to stop at the first error.

Code:
    CATCH eAppError AS CLASS Progress.Lang.AppError:
        REPEAT ix = 1 TO eAppError:NumMessages:
            MESSAGE "Message: " eAppError:GetMessage(ix) SKIP
                    "Message #: " ix
                    VIEW-AS ALERT-BOX ERROR TITLE eAppError:ReturnValue.
        END.
    END CATCH.

So, if I have following properties being set:

Code:
foobarClassObj:propxxx = 'xx@%'
foobarClassObj:propyyy = '123x&*'

and both have invalid values set and both properties return an error. How do I catch all the errors in one go?
 
Last edited:

RealHeavyDude

Well-Known Member
I would rather throw the app error than return error in the set implementation of the property. Nevertheless, if you assign the properties' values in a single assign statement it will fail when the first assignment fails and won't go any further. Even with the no-error option. You will need individual assign statements with the no-error option to catch the errors individually and then decide what to do with them.

What is you intention to do with the caught errors? Display an error message or invoke some alternative processing?
 

Cecil

19+ years progress programming and still learning.
I should have given an example of one of the property:

Code:
    DEFINE PUBLIC PROPERTY TaxCode                      AS CHARACTER GET.          /* REQUIRED! The tax code of the employee  **/
        SET (INPUT SetTaxCode AS CHARACTER):
            IF LOOKUP(SetTaxCode, {&xTaxCodes} ) GT 0 THEN
                TaxCode = SetTaxCode.       
            ELSE
            DO:
                AppError = NEW Progress.Lang.AppError().           
                AppError:ReturnValue = "Invalid Employee TAX Code".
                AppError:AddMessage( SUBSTITUTE("Invalid Employee TAX Code: '&1'", SetTaxCode) , 307).
                AppError:AddMessage( SUBSTITUTE("Accepted TAX Codes: '&1'", {&xTaxCodes} ) , 308).
                RETURN ERROR AppError.
            END.
        END SET.

The class object is being called as part of a batch run. I wanted to accumulate all the errors and store them in the database for user review. I'm not using an assign statement as part of the setting of the properties, should I be, does look natural??

Code:
    FOR EACH EmployeeFileReturnDetail NO-LOCK
        WHERE EmployeeFileReturnDetail.EmployeeFileReturnID EQ PrepareEmployeeFileReturn.EmployeeFileReturnID:
        
        employeeObj = EIFile:AddEmployee( EmployeeFileReturnDetail.IRDNumber ).
        
        employeeObj:employeeName = EmployeeFileReturnDetail.EmployeeName.
        employeeObj:TaxCode      = EmployeeFileReturnDetail.TaxCode.
        
        employeeObj:TaxCode = "xxx" . /** <---- Invalid TAXCODE, error is raised. Process STOPS at this point **/
        
        employeeObj:payPeriodDate(INPUT EmployeeFileReturnDetail.PayPeriodStartDate, INPUT EmployeeFileReturnDetail.PayPeriodEndDate ). /** Start and End Dates **/
        employeeObj:grossEarnings                   = EmployeeFileReturnDetail.GrossEarnings.
        
        /** Errors Rasied by the Application Code. **/
        CATCH eAppError AS CLASS Progress.Lang.AppError:
            REPEAT ix = 1 TO eAppError:NumMessages:
                MESSAGE SUBSTITUTE("Message &1/&2:",ix ,eAppError:NumMessages) SKIP
                        eAppError:ReturnValue SKIP
                        SUBSTITUTE("Message: &1", eAppError:GetMessage(ix)) SKIP
                        VIEW-AS ALERT-BOX ERROR TITLE  "Prepare File Return".
            END.
        END CATCH.
    END.
 

Cecil

19+ years progress programming and still learning.
I would rather throw the app error than return error in the set implementation of the property. Nevertheless, if you assign the properties' values in a single assign statement it will fail when the first assignment fails and won't go any further. Even with the no-error option. You will need individual assign statements with the no-error option to catch the errors individually and then decide what to do with them.

What is you intention to do with the caught errors? Display an error message or invoke some alternative processing?

"I would rather throw the app error than return error in the set implementation of the property"
Are you saying to use UNDO, THROW AppError. rather than RETURN ERROR AppError.?

When is use the NO-ERROR option, it does suppress the error (naturally, as expected), but the CATCH eAppError AS CLASS Progress.Lang.AppError: block does not execute and ERROR-STATUS:ERROR returns false.
 

RealHeavyDude

Well-Known Member
For one I found out the hard way that a return error, especially when returned from the constructor or a property setter, is not always caught in the caller as I expected. Since I like my code to hold water I changed my habit and always undo and throw a new AppError instead. I think mixing an matching legacy error handling and exception handling is still not fully transparent.

Since I do a lot of Java coding too using the term exception handling for the "new structured error handling" is natural to me.

But to your question regarding catching the exceptions. The only way I see is that you wrap each property setting in its own distinct block with structured exception handling. Because that is the way exceptions are meant to work: They are thrown up the chain util the get caught in a catch block. Plus, the catch and finally blocks are the last executable code when an exception is caught - which means, that the surrounding block with the exception handling is left automatically afterwards.

In other languages like Java you would also need to wrap each statement in its own distinct try / catch block. At least that is my understanding.
 

ForEachInvoiceDelete

Active Member
edit because my original code didn't even work, but try
Code:
FOR EACH EmployeeFileReturnDetail NO-LOCK
        WHERE EmployeeFileReturnDetail.EmployeeFileReturnID EQ PrepareEmployeeFileReturn.EmployeeFileReturnID
         ON ERROR UNDO, THROW:
 
Last edited:
Top