/*----------------------------------------------------------------------------*/ /* */ /* Copyright (c) 2011-2014 Rexx Language Association. All rights reserved. */ /* */ /* This program and the accompanying materials are made available under */ /* the terms of the Common Public License v1.0 which accompanies this */ /* distribution. A copy is also available at the following address: */ /* https://www.oorexx.org/license.html */ /* */ /* Redistribution and use in source and binary forms, with or */ /* without modification, are permitted provided that the following */ /* conditions are met: */ /* */ /* Redistributions of source code must retain the above copyright */ /* notice, this list of conditions and the following disclaimer. */ /* Redistributions in binary form must reproduce the above copyright */ /* notice, this list of conditions and the following disclaimer in */ /* the documentation and/or other materials provided with the distribution. */ /* */ /* Neither the name of Rexx Language Association nor the names */ /* of its contributors may be used to endorse or promote products */ /* derived from this software without specific prior written permission. */ /* */ /* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS */ /* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT */ /* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS */ /* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT */ /* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, */ /* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED */ /* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, */ /* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY */ /* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING */ /* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS */ /* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* */ /*----------------------------------------------------------------------------*/ /* ooDialog User Guide Exercise 07: MessageSender.rex v01-00 14Feb13 Contains: classes: "MessageSender", "HRSms" Message Sender is a test/debug/utility support class that enables a user to send a message to a component and see what's returned. Pre-requisites: the Object Manager (objectMgr.rex). In addition, Message Sender should be launched from some other dialog, since it uses popupAsChild. Description: A sample Message Sender utility for sending messages to components. Note that the messages that can be sent are probably limited, since a full test has not been done. Outstanding Problems: None reported. Changes: v01-00 07Jun12: First Version 07Aug12: Changed self~execute() to popupAsChild. Launched via a menu item in the Order Management dialog (OrderMgr.rex) 14Jan13: Commented-out use of ViewMgr (ViewMgr function incomplete) 05Feb13: Changed edit controls to comboboxes for Target and Method. Provided for user add of methods and target components (not saved over a dialog close). 11Feb13: No change to function - minor tidy-up of a few comments. 14Feb13: Correct text in the Help dialog. Description: Target: className instanceName Method: a single method name Data: Either strings separated by "|" (will be sent as an array), or: [name]value [name] value ... (will be sent as a directory), or: space-separated strings (each is interpreted as a separate attribute, so a parameter such as 'aaa bbb' cannot be sent at least not in this version). ------------------------------------------------------------------------------*/ .Application~addToConstDir("..\Exercise07\Support\MessageSender.h") ::REQUIRES "ooDialog.cls" ::CLASS 'MessageSender' SUBCLASS rcDialog PUBLIC /*---------------------------------------------------------------------------- Class Methods - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - newInstance - class method to create a msg sender. */ ::METHOD newInstance CLASS PUBLIC use arg rootDlg --say ".MessageSender-newInstance." dlg = .MessageSender~new("..\Exercise07\Support\MessageSender.rc", "DLG_MESSAGESENDER") --,,"MessageSender.h") dlg~activate(rootDlg) return /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ /*---------------------------------------------------------------------------- init - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ ::METHOD init expose objectMgr --say "MessageSender-init-01." forward class (super) continue .local~my.MsgSender = self return /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ /*---------------------------------------------------------------------------- Activate - Shows the Dialog - i.e. makes it visible to the user. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ ::METHOD activate unguarded expose rootDlg use arg rootDlg --say "MessageSender-activate-01." self~popupAsChild(rootDlg, "SHOWTOP") --, ,"IDI_CUSTLIST_DLGICON") return /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ /*---------------------------------------------------------------------------- initDialog - initialises the MessageSender - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ ::METHOD initDialog expose cbTarget cbMethod ecData ecReply btnSend btnClear stErrorMsg objectMgr - chkStoreTarget chkStoreMethod arrTargets arrMethods --say "MessageSender-initDialog-01." cbTarget = self~newComboBox("IDC_MS_COMPONENT") cbMethod = self~newComboBox("IDC_MS_METHOD") ecData = self~newEdit("IDC_MS_DATA") ecReply = self~newEdit("IDC_MS_REPLY") stErrorMsg = self~newStatic("IDC_MS_ERRORMSG") btnSend = self~newPushButton("IDC_MS_SEND") btnClear = self~newPushButton("IDC_MS_CLEAR") chkStoreTarget = self~newCheckBox("IDC_MS_STORETARGET") chkStoreMethod = self~newCheckBox("IDC_MS_STOREMETHOD") self~connectButtonEvent("IDC_MS_SEND","CLICKED",sendMessage) self~connectButtonEvent("IDC_MS_CLEAR","CLICKED",clearEntries) self~connectHelp(onHelp) errorFont = self~createFontEx("Arial Italic") stErrorMsg~setFont(errorFont) stErrorMsg~settext(.HRSms~helpMsg) -- Get id of ObjectMgr: objectMgr = .local~my.objectMgr --say "MessageSender-initDialog-02 - objectMgr =" objectMgr if objectMgr = .nil then do -- Check if ObjectMgr is present - .nil if not. stErrorMsg~setText(.HRSms~prefix1||.HRSms~noObjectMgr) btnSend~disable() end arrTargets = .array~of("ObjectMgr The","PersonModel PA150") arrMethods = .array~of("showModel","query","list") do i over arrTargets cbTarget~add(i) end do i over arrMethods cbMethod~add(i) end return /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ /*---------------------------------------------------------------------------- Event Handler Methods - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - sendMessage - Sends a message to the specified component. Returns .false if error found. Note 1: the return from the target object must be either a bool or a directory. (Later versions may add array and string). Note 2: The "parseData" method displays any error messages resulting from errors in the data provided by the user. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ ::METHOD sendMessage expose objectMgr ecReply stErrorMsg btnClear chkStoreTarget chkStoreMethod - cbTarget cbMethod arrTargets arrMethods --say; say "---------------------------------------------------------" --say "MessageSender-sendMessage-01." ecReply~setText("") -- Clear any reply data from previous requests. --error = .false -- if true, indicates error in data provided. message = self~parseData -- data is a directory, array, string; .false if error. if message = .false then do -- if errors found in the data. --say "MessageSender-sendMessage-02: message =" message btnClear~enable() -- allow user to clear entries. chkStoreTarget~uncheck; chkStoreMethod~uncheck return .false end if message = "special" then do -- message to ObjectMgr has been dealt with. btnClear~enable chkStoreTarget~uncheck; chkStoreMethod~uncheck return .true end -- Get the component ID (object ref) from the Object Manager: componentRef = objectMgr~getComponentId(message["class"], message["instance"]) if componentRef = .false then do chkStoreTarget~uncheck; chkStoreMethod~uncheck ecReply~setText(.HRSms~rc||" "||componentRef) stErrorMsg~setText(.HRSms~prefix1||" "||.HRSms~noObject) btnClear~enable return .false end -- Send the Message and Display the response: --say "MessageSender-sendMessage-03: message[data] =" message["data"] response = sendMsg(componentRef, message["method"],message["data"]) select when response = "SendMsg - Syntax Error" then do chkStoreTarget~uncheck; chkStoreMethod~uncheck stErrorMsg~setText(.HRSms~noResponse) btnClear~enable return end when response = "SendMsg - No Method" then do chkStoreTarget~uncheck; chkStoreMethod~uncheck stErrorMsg~setText(.HRSms~noTgtMethod) btnClear~enable return end when response = .false then do chkStoreTarget~uncheck; chkStoreMethod~uncheck ecReply~setText(.HRSms~rc||" "||response) return end when response~isa(.String) then do ecReply~setText(response) end when response~isa(.Directory) then do replyText = "" do i over response replyText = replyText||i||":" response[i]||"; " end ecReply~setText(replyText) end otherwise do ecReply~setText(.HRSms~noDir1 response .HRSms~noDir2) end end btnClear~enable -- say "MessageSender-sendMessage-04: response =" response -- Message sent successfully - so now action the checkboxes: self~storeEntry(cbTarget, chkStoreTarget) self~storeEntry(cbMethod, chkStoreMethod) return /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ /*---------------------------------------------------------------------------- storeEntry - Store a new item in either Target or Method comboboxes if a checkbox has been checked. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ ::METHOD storeEntry PRIVATE use strict arg comboBox, checkBox --say "MessageSender-storeEntry-01." if checkBox~getCheckState = "CHECKED" then do newItem = comboBox~getEditControl()~getLine(1) ix = comboBox~find(newitem) -- case-insensitive find if ix = 0 then do -- if item not found, then add it. comboBox~add(newItem) end checkBox~uncheck() end return /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ /*---------------------------------------------------------------------------- clearEntries - Clear user entries and error messages. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ ::METHOD clearEntries expose cbTarget cbMethod ecData ecReply stErrorMsg btnClear cbTarget~setText("") cbMethod~setText("") ecData~setText("") ecReply~setText("") stErrorMsg~setText("") btnClear~disable /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ /*---------------------------------------------------------------------------- onHelp - Display help. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ ::METHOD onHelp cr = .endOfLine msg = .HRSms~help2||cr||.HRSms~help3||cr - ||.HRSms~help4||cr||.HRSms~help5||cr - ||.HRSms~help6||cr||.HRSms~help7||cr - ||.HRSms~help8 title = .HRSms~help1 buttons = "OK" --ans = MessageDialog(msg, 0, title, "OK", "INFORMATION") ans = MessageDialog(msg, self~dlgHandle, title, "OK", "INFORMATION") /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ /*---------------------------------------------------------------------------- Application Methods - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ /*---------------------------------------------------------------------------- parseData - Check the message info provided by the user. Required data formats are: Target component: "class instance". Allowed are: multiple spaces between "class" and "instance"; leading spaces before "class". (Later version may provide for class messages). Method: a method name - a single string, e.d. "doThat". Any leading or trailing spaces are removed. Message Parameters: A string, and array, or a directory, as follows: String, e.g.: "AB123, Joe Bloggs Inc., 124.50" The first non-white-space character must not be "[" or "|", as these characters are used to decide whether the data format is string, array or directory. Array, e.g.: "| AB123 | Joe Bloggs Inc. | 124.50 " The separator between elements is "|". Leading and trailingspaces are removed from each array item. Thus the result of the above example would be: "AB123|Joe Bloggs Inc.|124.50". Directory, e.g.: "[CustNo] AB123 [Name] Joe Bloggs Inc. [Debt] 124.50". The indices are in square brackets. Leading and trailing spaces of both indices and items are removed. Returns: If no errors found, this method returns the message data in the directory "message". This has four indexes: "class", "instance", "method", "data". If format errors are found, a message is displayed, and .false is returned. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ ::METHOD parseData expose cbTarget cbMethod ecData stErrorMsg objectMgr rootDlg stErrorMsg~setText("") -- remove any previous error message. --say; say "MessageSender-parseData-01." message = .Directory~new -- get target component name: --target = cbTarget~getText(1) target = cbTarget~getEditControl()~getLine(1) --say "MessageSender-parseData-02 target =" target parse var target class instance targetError = .false if class = "" | instance = "" then targetError = .true else do message["class"] = class~strip; message["instance"] = instance~strip end -- get Method/Message Name: targetMethodError = .false targetMethod = cbMethod~getEditControl()~getLine(1) --say "MessageSender-parseData-03 targetMethod =" targetMethod if targetMethod~words \= 1 then targetMethodError = .true else message["method"] = targetMethod~strip -- Special Treatments: if message["class"] = "ObjectMgr" & message["instance"] = "The" then do -- Only List and ShowModel allowed (at present). --say "MessageSender-parseData-04: msg to ObjectMgr." method = message["method"] method = method~upper select when method = "LIST" then do objectMgr~list return "special" end when method = "SHOWMODEL" then do -- assume data is a string modelName = ecData~getLine(1) parse var modelName modelClass " " modelInstance modelClass = modelClass~strip; modelInstance = modelInstance~strip --say "MessageSender-parseData-05: ViewMgr~parentOffsetDialog = self." -- Setup self as "Parent" dialog for offsetting the view to be shown --(do it here because other components may have done it previously): .local~my.ViewMgr~parentOffsetDlg = self -- Now show the model: --say "MessageSender-parseData-06: objectMgr~showModel." r = objectMgr~showModel(modelClass, modelInstance, rootDlg) --say "MessageSender-parseData-07: return =" r return "special" end otherwise do --say "MessageSender-parseData-08:" method "is invalid." targetMethodError = .true --return .false end end end -- Following Code does not work - left here in case needed in any following exercises. /* else do --say "MessageSender-parseData-01d." if message["class"] = "ViewMgr" & message["instance"] = "The" then do method = message["method"] method = method~upper if method = "SHOWMODEL" then do modelName = ecData~getLine(1) parse var modelName modelClass " " modelInstance modelClass = modelClass~strip; modelInstance = modelInstance~strip .local~my.ViewMgr~parentOffsetDlg = self -- Now show the model: r = .local~my.ViewMgr~showModel(modelClass, modelInstance, rootDlg) --say "MessageSender-parseData-01e: return =" r return "special" end end end */ -- Normal treatments: -- get Message Data, and put in an array, directory or string: -- Note: ecData~lines returns 1 if control is empty(!). msgData = "" do i=1 to ecData~lines() msgData = msgData||ecData~getLine(i) --say "MessageSender-parseData-02: Data = '"||msgData||"'" end -- Everything now in a single text string. So now check the data type: --say "chars = '"||msgData||"'" msgData = strip(msgData) -- remove leading & trailing blanks. if left(msgData,1) = "" then msgDataType = .false -- i.e. no data else if left(msgData,1) = "[" then msgDataType = "dir" else if left(msgData,1) = "|" then msgDataType = "arr" else msgDataType = "str" --say "msgDataType =" msgDataType; formatError = .false Select when msgDataType = .false then msgData = "" when msgDataType = "dir" then do dirMsgData = .Directory~new separators = msgData~countStr("["); closers = msgData~countStr("]") if separators \= closers then formatError = .true if \formatError then do do i = 1 to separators parse var msgData "[" index "]" msgData parse var msgData item "[" dirMsgData[index] = item~strip end message["data"] = dirMsgData end end when msgDataType = "arr" then do arrMsgData = .Array~new separators = msgData~countStr("|") --say "No. Separators =" separators msgData = msgData~substr(2) -- strip leading "|" do i=1 to separators parse var msgData item "|" msgData item = item~strip arrMsgData[i] = item --say "item =" "'"||item||"'" end message["data"] = arrMsgData end otherwise do -- msgDataType = "str" message["data"] = msgData end end -- Select -- Check for errors: errors = 0 if targetError then errors += 1; if targetMethodError then errors += 1 if formatError then errors += 1 if errors = 1 then errortext = .HRSms~prefix1 if errors > 1 then errortext = .HRSms~prefix2 if errors > 0 then do if targetError then errorText = errorText||.HRSms~badTarget if targetMethodError then errorText = errorText||" "||.HRSms~badMethod if formatError then errorText = errorText||" "||.HRSms~badData stErrorMsg~setText(errorText) return .false end return message /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ /*---------------------------------------------------------------------------- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ ::METHOD getObjectRef expose objectMgr use arg target parse var target class "-" instance ref = objectMgr~getComponentRef(class, instance) /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ /*============================================================================*/ /*////////////////////////////////////////////////////////////////////////////// ============================================================================== sendMsg - a 'Send Message' Routine. v00-01 08May12 -------- This routine sends the message to the specified component instance. = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */ ::ROUTINE sendMsg use arg targetObject, targetMethod, data --say "MessageSender-sendMsg-01: targetObject targetMethod data =" targetObject targetMethod data SIGNAL ON SYNTAX NAME catchIt1 SIGNAL ON NOMETHOD NAME catchIt2 msg = .Message~new(targetObject, targetMethod, i, data) response = msg~send --say "MessageSender-sendMsg-02: response =" response return response catchIt1: say "MessageSender-sendMsg-03: CatchIt1 - Syntax." SIGNAL OFF SYNTAX return "SendMsg - Syntax Error" catchIt2: say "MessageSender-sendMsg-04: CatchIt2 - NoMethod." SIGNAL OFF NOMETHOD return "SendMsg - No Method" /*============================================================================*/ /*////////////////////////////////////////////////////////////////////////////// ============================================================================== Human-Readable Strings (HRSms) v01-00 05May12 -------- This class provides constant character strings for user-visible messages. = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */ ::CLASS HRSms PRIVATE ::CONSTANT prefix1 "*** Error: " ::CONSTANT prefix2 "*** Errors: " ::CONSTANT badTarget "Invalid class or Instance name." ::CONSTANT badMethod "Invalid method." ::CONSTANT badData "Invalid data format." ::CONSTANT noObject "Target component not found." ::CONSTANT noTgtMethod "No such method in target component." ::CONSTANT noResponse "Note: No response received from target object." ::CONSTANT rc "Return Code:" ::CONSTANT noObjectMgr "The Object Manager was not found. Cannot work without it." ::CONSTANT noDir1 "Reply is a" ::CONSTANT noDir2 "- cannot display with this version." ::CONSTANT help1 "Formats for data parameters:" ::CONSTANT help2 "Target: Class Instance - e.g. 'CustomerModel CU0003'" ::CONSTANT help3 "Method: method name - e.g. 'query'" ::CONSTANT help4 "Data: Formats for message data are:" ::CONSTANT help5 " Directory: '[aaa] [bbb] [c cc]'" ::CONSTANT help6 " Array: '|aaa|bbb|c cc'" ::CONSTANT help7 " String: 'aaa bbb'" ::CONSTANT help8 "Note: a 'saved' target or method is not saved over a close." ::CONSTANT helpMsg "Press F1 for help on allowable data formats." /*============================================================================*/