CATScript - The Beauty of Code

CATScript is a scripting language specific to CATIA, a widely used computer-aided design (CAD) software.

In this article, I’d like to put down some more reusable code snippets to make a programme’s live easier.

Text Concatenation with Array

The following coding example is likely the most efficient way of managing large amounts of text information.
Note when using the Join function, vbNewLine needs to be declared only once.

Option Explicit
Language="VBSCRIPT"

Sub CATMain()
	Dim i As Integer
	Dim msg As Array
	
	msg = Array ( _
		"entia non sunt",_
		"multiplicanda praeter",_
		"necessitatem",_
		"",_
		"Entities should not be",_
		"multiplied beyond necessity."_
	)

	' loop through each individual array item
	' For i = 0 To UBound(msg)	MsgBox msg(i) Next

	' show the array's content
	MsgBox Join (msg, vbNewLine), vbOkOnly + vbInformation, "Message:"
End Sub

If you are short in time, don’t read any further in this chapter, use the above code, modify it as required and you are done.

Similar code but implemented as a Class...

As a comparison, here is an alternative class which basically offers the same functionality but obviously needs a lot more code. I initially coded this class a while a go, at that time I was not aware of the benefits using a simple Array data declaration.

Note that the index of the string array starts at zero (0) in the same way as for array data objects.

Class CustomStrArray
    Private data() As String
	Private is_set As Boolean

	' data index starts at 0
    Private Sub Class_Initialize() : ReDim data(0) : is_set = False : End Sub
    Private Sub Class_Terminate()  : Erase data : End Sub

    Public Property Get Count() As Long
		Count = UBound(data)
	End Property

    Public Sub Add (ByVal str As String)
		If is_set = False Then
			data(0) = str
			is_set = True
		Else
			ReDim Preserve data(UBound(data) + 1)
			data(UBound(data)) = str
		End If
    End Sub

    Public Function Item (ByVal i As Integer) As String
        Item = data(i)
    End Function
	
	Public Function GetAllText () As String
		Dim i As Integer
		Dim txt As String

		If Not is_set Then
			txt = ""
		Else
			txt =  data(0)
			For i = 1 To UBound(data)
				txt = txt + vbNewLine + data(i)
			Next
		End If
		GetAllText = txt
	End Function
End Class

Sub CATMain ()

	Dim i As Integer
	Dim cdata As CustomStrArray
	Set cdata = New CustomStrArray

	cdata.Add "Hello"
	cdata.Add "world"
	cdata.Add "..."

	' class function example call
	MsgBox cdata.GetAllText, vbOkOnly, "Text info:"

	' loop through all individual string items...
	' note that the index starts at zero (same as for arrays)

	For i = 0 To cdata.Count
		MsgBox cdata.Item(i), vbOkOnly, "String data Item:"
	Next

	Set cdata = Nothing
End Sub
Similar code, extended with boundary check ...

Another variation of the code with improved solid rock boundary check. If a boundary given to the Item sub-function might be out of range, an error will be raised.

Class CustomStrArray
    Private data() As String
    Private Sub Class_Initialize() : ReDim data(0) : data(0) = "" : End Sub
    Private Sub Class_Terminate()  : Erase data : End Sub

    Public Property Get Count() As Long
        If UBound(data) = 0 And data(0) = "" Then
            Count = 0
        Else
            Count = UBound(data) + 1
        End If
    End Property

    Public Sub Add (ByVal str As String)
        If UBound(data) = 0 And data(0) = "" Then
            data(0) = str
        Else
            ReDim Preserve data(UBound(data) + 1)
            data(UBound(data)) = str
        End If
    End Sub

    Public Function Item (ByVal i As Integer) As String
        If i >= 0 And i <= UBound(data) Then
            Item = data(i)
        Else
            Err.Raise 9, "CustomStrArray", "Subscript out of range"
        End If
    End Function

    Public Function GetAllText() As String
        Dim i As Integer
        Dim result As String
        If Count = 0 Then
            GetAllText = ""
        Else
            result = data(0)
            For i = 1 To UBound(data)
                result = result & vbNewLine & data(i)
            Next
            GetAllText = result
        End If
    End Function
End Class

Sub CATMain()
    Dim cdata As CustomStrArray
    Set cdata = New CustomStrArray

    cdata.Add "The"
    cdata.Add "quick"
    cdata.Add "brown"
    cdata.Add "fox..."

    MsgBox cdata.GetAllText()

    Set cdata = Nothing
End Sub

Dictionaries

While I typically use the commands built into the CATScript language, there is also additional functionality available via CreateObject. This way, dictionary objects are provided and can be employed to address problems requiring more complex data structures.

Example code, read more...

Sub CATMain ()

	Dim dict As Dictionary
	Set dict = CreateObject("Scripting.Dictionary") ' late binding

	' add items to the collection ...
	
	' The Key can be any data type.
	' The Item can be any data type, an object, array, collection or even a dictionary.
	' So you could have a Dictionary of Dictionaries, Array and Collections.
	' But most of the time it will be a value(date, number or text).
	
	dict.Add 0, "First Item"
	dict.Add 1, "Second Item"

	' access and display items
	Dim item As Variant

	For Each item In dict.Keys : MsgBox dict(item) : Next

	' remove an item
	dict.Remove 0

	' check if an item exists
	If dict.Exists(2) Then MsgBox "Key2 exists in the collection."

End Sub

MsgBox Goodies

The array data type can also be used to implement a clean and lean MsgBox call.

I personally like the “Select Case” statement together with predefined enumerations.
This way, there is no need to hard code return values.

Sub UserSelectionCmd()
	Dim msg As Array
	msg = Array (_
		"This function allows to ....",_
		"<your text description here>",_
		"<some more explanations...>",_
		"","",_
		"YES = " + vbTab + "Do you like to continue?",_
		"No = "  + vbTab + "<Do semething else instead...>")

	Do While True
		Select Case MsgBox ( _
				Join (msg, vbNewLine),_
				vbQuestion + vbYesNoCancel, "User Selection:")
		Case vbCancel
			Exit Sub
		Case vbNo
			Call DoSomethingElseCmd ()
		Case vbYes
			Exit Do
		End Select
	Loop
End Sub

More about Dynamic Arrays

I prefer dynamic array allocation because it is the most flexible approach. At the same time, I try to avoid writing excessive code, so I looked for a clean and concise solution to meet this requirement:

Standard way to set up some more array variables manually.

Sub CATMain ()
	Dim i As Integer
	Dim item, var() As Variant

	ReDim var(0)          : var(0) = 1
	ReDim Preserve var(1) : var(1) = "Hello"
	ReDim Preserve var(2) : var(2) = True

	For i = LBound(var) To UBound(var)
	  MsgBox TypeName (var(i)) + " = " + CStr(var(i))
	Next

	' also possible: 
	For Each item In var
	  MsgBox TypeName (item) + " = " + CStr(item)
	Next
End Sub

Accessing every element in an array can also be achieved using a For Each loop, which is often the most readable and concise option.

The following code mimics a dynamic array. The size of the array is handled on the fly at the time when adding a new array item:

Sub Add (ByRef arr() As String, ByVal new_item As String)
    ReDim Preserve arr (UBound(arr) + 1)
    arr (UBound(arr)) = new_item
End Sub

Sub CATMain()
	Dim arr() As String
	ReDim arr(-1)

	' Add items dynamically
	Add arr, "First"
	Add arr, "Second"
	Add arr, "Third"
	MsgBox Join(arr, vbNewLine)
End Sub

This is likely the most comprehensive version for a dynamically allocated array with the fewest lines of code.

Notes:

  • The array index starts at 0 which is the default behavior for array variables.

  • If the array would be declared as Dim arr() As Variant the array is capable to handle any kind of variable.

    Hint: If an object is assigned as array item, you probably would need to declare the Set keyword as usual.

  • To loop through each item of the array, one can do this as usaully like toe following:

    	Dim arr() As String
    
    	' MsgBox CStr(LBound(arr)) + "/" + CStr(UBound(arr))
    
    	Dim i As Integer
    	For i = LBound(arr) To UBound(arr)
    		MsgBox "Index " & i & ": " & arr(i)
    	Next
    

    and as an alternative:

    	MsgBox Join(arr, vbNewLine)