I want to make a class diagram in Enterprise Architect ( Version 10.0 ) which implement an inheritance. I add an abstract class then create 3 class which implement abstract class.
Now if I make some change in abstract class, child classes doesn't change. (for example add another method to abstract class, but child class dose not have this method).
How can update(or refresh) a child class when parent class changed?
Select the class and press Ctrl + Shift + o to refresh. However this doesn't perform full refresh, so you will need to remove older versions of operations etc, but EA will display the override methods dialog.
You can use my addin EA-Matic to do so.
EA-Matic catches the add-in events in Enterprise Architect and forwards them the built-in scripting environment.
This allows to create scripts with full blown add-in abilities, synchronizing overrides is just one of the possible applications.
One of the example scripts I wrote to demonstrate the add-in does exactly this: Automatically synchronize overrides in Enterprise Architect with EA-Matic
This example script uses the events EA_OnContextItemChanged to store the overrides of the selected operation
'Event Called when a new element is selected in the context. We use this operation to keep the id of the selected operation and a list of its overrides
'Because now is the only moment we are able to find it's overrides. Once changed we cannot find the overrides anymore because then they already
'have a different signature
function EA_OnContextItemChanged(GUID, ot)
'we only want to do something when the selected element is an operation
if ot = otMethod then
'get the model
dim model
set model = getEAAddingFrameworkModel()
'get the operation
dim operation
set operation = model.getOperationByGUID(GUID)
'remember the operationID
operationID = operation.id
'remember the overrides
set overrides = getOverrides(operation, model)
Repository.WriteOutput "EA-Matic", overrides.Count & " overrides found for: " & operation.name,0
end if
end function
and EA_OnNotifyContextItemModified to modify the overrides in case something changed.
'Event called when an element is changed. Unfortunately EA doesn't call it for an operation, only for the owner so we have to work with that.
function EA_OnNotifyContextItemModified(GUID, ot)
'we only want to do something when the selected element is an operation
if ot = otElement then
'get the operation
'Here we use the EA API object directly as most set methods are not implemented in EA Addin Framework
dim wrappedOperation
set wrappedOperation = Repository.GetMethodByID(operationID)
dim modifiedElement
set modifiedElement = Repository.GetElementByGuid(GUID)
if not wrappedOperation is Nothing and not modifiedElement is Nothing then
'check to be sure we have the same operation
if modifiedElement.ElementID = wrappedOperation.ParentID AND overrides.Count > 0 then
dim synchronizeYes
synchronizeYes = MsgBox("Found " & overrides.Count & " override(s) for operation "& modifiedElement.Name & "." & wrappedOperation.Name & vbNewLine & "Synchronize?" _
,vbYesNo or vbQuestion or vbDefaultButton1, "Synchronize overrides?")
if synchronizeYes = vbYes then
synchronizeOverrides wrappedOperation
'log to output
Repository.WriteOutput "EA-Matic", "Operation: " & wrappedOperation.name &" synchronized" ,0
end if
'reset operationID to avoid doing it all again
operationID = 0
end if
end if
end if
end function
In OnContextItemChanged it gets the overrides by first selecting all operations with the same signature from the model with a query.
'gets the overrides of the given operation by first getting all operations with the same signature and then checking if they are owned by a descendant
function getOverrides(operation, model)
'first get all operations with the exact same signature
dim overrideQuery
overrideQuery = "select distinct op2.OperationID from (((t_operation op " & _
"inner join t_operation op2 on op2.[Name] = op.name) "& _
"left join t_operationparams opp on op.OperationID = opp.OperationID) "& _
"left join t_operationparams opp2 on opp2.OperationID = op2.OperationID) "& _
"where op.OperationID = "& operation.id &" "& _
"and op2.ea_guid <> op.ea_guid "& _
"and (op2.TYPE = op.Type OR (op2.TYPE is null AND op.Type is null)) "& _
"and (op2.Classifier = op.Classifier OR (op2.Classifier is null AND op.Classifier is null)) "& _
"and (opp.Name = opp2.Name OR (opp.Name is null AND opp2.Name is null)) "& _
"and (opp.TYPE = opp2.TYPE OR (opp.TYPE is null AND opp2.Type is null)) "& _
"and (opp.DEFAULT = opp2.DEFAULT OR (opp.DEFAULT is null AND opp2.DEFAULT is null)) "& _
"and (opp.Kind = opp2.Kind OR (opp.Kind is null AND opp2.Kind is null)) "& _
"and (opp.Classifier = opp2.Classifier OR (opp.Classifier is null AND opp2.Classifier is null)) "
dim candidateOverrides
set candidateOverrides = model.ToArrayList(model.getOperationsByQuery(overrideQuery))
'then get the descendants of the owner
dim descendants
dim descendant
'first find all elements that either inherit from the owner or realize it
dim owner
set owner = model.toObject(operation.owner)
set descendants = getDescendants(owner, model)
'then filter the candidates to only those of the descendants
'loop operations backwards
dim i
for i = candidateOverrides.Count -1 to 0 step -1
dim found
found = false
for each descendant in descendants
if descendant.id = model.toObject(candidateOverrides(i).owner).id then
'owner is a descendant, operation can stay
found = true
exit for
end if
next
'remove operation from non descendants
if not found then
candidateOverrides.RemoveAt(i)
end if
next
set getOverrides = candidateOverrides
end function
Then we its all the descendants of the owner of the operation (recursively) and filter the operations to only those owned by a descendant.
'gets all descendant of an element. That is all subclasses and classes that Realize the element.
'Works recursively to get them all.
function getDescendants(element, model)
dim descendants
dim getdescendantsQuery
getdescendantsQuery = "select c.Start_Object_ID as Object_ID from (t_object o " _
& "inner join t_connector c on c.End_Object_ID = o.Object_ID) " _
& "where "_
& "(c.[Connector_Type] like 'Generali_ation' "_
& "or c.[Connector_Type] like 'Reali_ation' )"_
& "and o.Object_ID = " & element.id
set descendants = model.toArrayList(model.getElementWrappersByQuery(getdescendantsQuery))
'get the descendants descendants as well
dim descendant
dim descendantsChildren
for each descendant in descendants
if IsEmpty(descendantsChildren) then
set descendantsChildren = getDescendants(descendant, model)
else
descendantsChildren.AddRange(getDescendants(descendant, model))
end if
next
'add the descendantsChildren to the descendants
if not IsEmpty(descendantsChildren) then
if descendantsChildren.Count > 0 then
descendants.AddRange(descendantsChildren)
end if
end if
set getDescendants = descendants
end function
Finally it synchronizes the operations using the following functions
'Synchronizes the operation with it's overrides
function synchronizeOverrides(wrappedOperation)
dim override
for each override in overrides
dim wrappedOverride
set wrappedOverride = override.WrappedOperation
'synchronize the operation with the override
synchronizeOperation wrappedOperation, wrappedOverride
'tell EA something might have changed
Repository.AdviseElementChange wrappedOverride.ParentID
next
end function
'Synchronizes the operation with the given override
function synchronizeOperation(wrappedOperation, wrappedOverride)
dim update
update = false
'check name
if wrappedOverride.Name <> wrappedOperation.Name then
wrappedOverride.Name = wrappedOperation.Name
update = true
end if
'check return type
if wrappedOverride.ReturnType <> wrappedOperation.ReturnType then
wrappedOverride.ReturnType = wrappedOperation.ReturnType
update = true
end if
'check return classifier
if wrappedOverride.ReturnType <> wrappedOperation.ReturnType then
wrappedOverride.ReturnType = wrappedOperation.ReturnType
update = true
end if
if update then
wrappedOverride.Update
end if
'check parameters
synchronizeParameters wrappedOperation, wrappedOverride
end function
'Synchronizes the parameters of the given operatin with that of the overrride
function synchronizeParameters(wrappedOperation, wrappedOverride)
'first make sure they both have the same number of parameters
if wrappedOverride.Parameters.Count < wrappedOperation.Parameters.Count then
'add parameters as required
dim i
for i = 0 to wrappedOperation.Parameters.Count - wrappedOverride.Parameters.Count -1
dim newParameter
set newParameter = wrappedOverride.Parameters.AddNew("parameter" & i,"")
newParameter.Update
next
wrappedOverride.Parameters.Refresh
elseif wrappedOverride.Parameters.Count > wrappedOperation.Parameters.Count then
'remove parameters as required
for i = wrappedOverride.Parameters.Count -1 to wrappedOperation.Parameters.Count step -1
wrappedOverride.Parameters.DeleteAt i,false
next
wrappedOverride.Parameters.Refresh
end if
'make parameters equal
dim wrappedParameter
dim overriddenParameter
dim j
for j = 0 to wrappedOperation.Parameters.Count -1
dim parameterUpdated
parameterUpdated = false
set wrappedParameter = wrappedOperation.Parameters.GetAt(j)
set overriddenParameter = wrappedOverride.Parameters.GetAt(j)
'name
if overriddenParameter.Name <> wrappedParameter.Name then
overriddenParameter.Name = wrappedParameter.Name
parameterUpdated = true
end if
'type
if overriddenParameter.Type <> wrappedParameter.Type then
overriddenParameter.Type = wrappedParameter.Type
parameterUpdated = true
end if
'default
if overriddenParameter.Default <> wrappedParameter.Default then
overriddenParameter.Default = wrappedParameter.Default
parameterUpdated = true
end if
'kind
if overriddenParameter.Kind <> wrappedParameter.Kind then
overriddenParameter.Kind = wrappedParameter.Kind
parameterUpdated = true
end if
'classifier
if overriddenParameter.ClassifierID <> wrappedParameter.ClassifierID then
overriddenParameter.ClassifierID = wrappedParameter.ClassifierID
parameterUpdated = true
end if
'update the parameter if it was changed
if parameterUpdated then
overriddenParameter.Update
end if
next
end function
When you tell EA to override or implement an operation in a base class or interface, it creates a copy of the operation in the child class. This copy has no reference to the original base class / interface, and subsequent changes are not reflected in the child.
If you draw a realization to an interface and select not to override any operations, EA will generate code for them as you'd expect, but it doesn't do that for abstract classes - even if the child is a leaf.
You can always modify the code generation templates (Tools - Source Code Generation Templates). They're a bit tricky to get your head around the first time, but you'll soon get the hang of it.
Related
I am trying to get the text/label of a child node of a javatree NOT the index.
How do i do that in UFT ?
My Java tree is as below
Code that i have tried :
Dim itemsCount
Dim nodeName
Dim myText
Dim selectItem()
Dim ProgramName
ProgramName = "17030-3 Parameter, programming"
itemsCount = CInt(WpfWindow("Tech Tool").JavaWindow("Program_ECU").JavaTree("Program_Control_Unit").GetROProperty("items count"))
Redim selectItem(itemsCount)
Set objItem = WpfWindow("Tech Tool").JavaWindow("Program_ECU").JavaTree("Program_Control_Unit").Object
For i = 0 To itemsCount-1
'selectItem(i)=WpfWindow("Tech Tool").JavaWindow("Program_ECU").JavaTree("Program_Control_Unit").GetItem(i)
selectItem(i)=WpfWindow("Tech Tool").JavaWindow("Program_ECU").JavaTree("Program_Control_Unit").Select ("#0;#"&i)
If Trim(CStr(ProgramName)) = Trim(CStr(objItem.getItem(i))) Then
objItem.Select(i)
msgbox "Success"
End If
Next
I have also tried using .GetColumnValue("#0;#1") but that also did not work
Traversing JTree in UFT is complicated than it should be. Here is what I did when I had to traverse through a JTree
Set JTree = WpfWindow("Tech Tool").JavaWindow("Program_ECU").JavaTree("Program_Control_Unit").Object
'return how many nodes there are in the tree irrespective of their level
MsgBox JTree.getRowCount
Set JModel = JTree.getModel 'get jtree data model
Set JRoot = JModel.getRoot 'get root so that we can traverse the tree
Call JTreeWalk(JModel, JRoot)
'recursively traverse the jtree
Sub JTreeWalk(JModel, objNode)
ctr = JModel.getChildCount(objNode)
For i = 0 To ctr - 1
Set objChild = JModel.getChild(objNode, i)
If JModel.isLeaf(objChild) Then
Print vbTab & objChild.toString
Else
Print "~~ " & objChild.toString & " ~~"
End If
Call JTreeWalk(JModel, objChild)
Next
End Sub
Useful links
https://docs.oracle.com/javase/tutorial/uiswing/components/tree.html
http://www.rgagnon.com/javadetails/java-0313.html (I used this java code and converted into vbs)
I have an excel workbook where the user imports text files into a "Data Importation Sheet". The number of files imported is dependent on how files the user wants to import. So far my workbook works great but I have hit one bump. When the user imports a file an identifier (i.e. 1, 2, 3, etc) gets assigned to that data set. Then the user selects an option from a dropdown box and calculations and plots will automatically be produced. The user also has the option to "Clear all data" where when this is selected all worksheets are cleared and if the user imports new files (after clicking the "clear all data" button) the identifier value restarts at 1. Here is my code for the identifier/counting how many files have been imported..
Public Sub Macro(Optional reset As Boolean = False)
Static i As Integer
If reset Then
i = -1
i = i + 1
Exit Sub
End If
i = i + 1
Worksheets("Hidden").Cells(i + 1, 1).FormulaR1C1 = "=" & i
Worksheets("Hidden").Cells(2, 2).FormulaR1C1 = "=" & i
End Sub
The problem I have ran into now is data will need to be imported into this sheet at a later date so when I save this file and reopen it then import more files the identifier/count for file imports restarts at 1 which I do not want to happen. I want to be able to just keep adding more files and have the code continue, I do not want to have to clear all the imported data and restart. Any ideas as to how I can do this? TIA.
I'd create a standalone function to manage the sequence. Store the value in a Workbook Name entry.
Note - if you had to manage multiple sequences you could promote the name of the sequence to a parameter instead of using a Constant within the Function.
Function NextSequence(Optional reset As Boolean = False)
Const COUNTER_NAME As String = "NM_COUNTER"
Dim nm As Name, i
On Error Resume Next
'is the name already created?
Set nm = ThisWorkbook.Names(COUNTER_NAME)
On Error GoTo 0
If nm Is Nothing Then
'not there yest - create it...
Set nm = ThisWorkbook.Names.Add(COUNTER_NAME, 0)
End If
If Not reset Then
i = Evaluate(nm.RefersTo)
i = i + 1
nm.RefersTo = i
Else
nm.RefersTo = 0
i = 0 '<< or 1 if you want NextSequence(True) to
' return the first sequence value
End If
NextSequence = i
End Function
Usage:
Public Sub Macro(Optional reset As Boolean = False)
Dim i
i = NextSequence(reset)
If reset Then Exit Sub
With Worksheets("Hidden")
.Cells(i + 1, 1).Value = i
.Cells(2, 2).Value = i
End With
End Sub
A quick fix for this would be to store the value of the identifier/count inside a cell and hide/lock the cell. The value inside the cell won't change upon restart yet you still can manipulate it inside VBA.
Very quick feel of how it should look like (probably innacurate as I don't have every info I need.)
Public Sub Macro(Optional reset As Boolean = False)
Static i As Integer
i = ActiveWorkBook.Sheets("Ressource").Range("A1").Value
If reset Then
i = -1
i = i + 1
Exit Sub
End If
i = i + 1
Worksheets("Hidden").Cells(i + 1, 1).FormulaR1C1 = "=" & i
Worksheets("Hidden").Cells(2, 2).FormulaR1C1 = "=" & i
End Sub
You could also create a CustomDocumentProperty to save the sequence number. You can pass a boolean to the method to reset:
Lastly, a helper function will check if the property exists, in order to be added if not.
Public Sub SequenceNumber(Optional ByVal Reset As Boolean = False)
If Not PropertyExists("Identifier") Then
ThisWorkbook.CustomDocumentProperties.Add Name:="Identifier", _
LinkToContent:=False, _
Type:=msoPropertyTypeNumber, _
Value:=0
End If
Dim p As Object
Set p = ThisWorkbook.CustomDocumentProperties("Identifier")
If Reset Then p.Value = 0 Else p.Value = p.Value + 1
End Sub
'Property Exists?
Private Function PropertyExists(ByVal propertyName As String) As Boolean
Dim p As Object
For Each p In ThisWorkbook.CustomDocumentProperties
If p.Name = propertyName Then
PropertyExists = True
Exit Function
End If
Next p
End Function
To call it:
SequenceNumber
SequenceNumber Reset:=True
I tried reading other posted questions about this error but I am still having trouble understanding it.
I was having errors after renaming variables and posted this question, I use option explicit and debug my code and it fixed my naming problems, But I still am having one problem in that 2 out of my 21 comboboxes in the operations row are still getting this same error. How can they get an error when they are using the same functions as the other 19. I checked their names and they are correct how could this be??
I have a form that I have build that is set up to look and function similar to and excel spreadsheet. This is the layout, Column Names first and then Control type
Operation Time/Min DecTime LaborRate Cost MarkUp Total
ComboBox TextBox Label Label Label TextBox Label
So all together I have 7 columns with 21 rows (not including column names) in which I can input data. Any where there is a label there is no user input its value is simply derived from the other fields.
I used the following naming convention for my form controls cb_op1, cb_op2, cb_op3 ... I just increment it by one to mimic the row number (control name plus row number). Operation = cb_op, Time = tb_time, DecTime = tb_Dectime, LaborRate = tb_LbrRate, Cost = tb_Cost, MarkUp = tb_MU, Total = tb_Total.
I wrote functions for the AfterUpdate events for my controls. This first function gets the value of the control based on its row number and then allows me to assign it to a variable this fuction is used for multiple controls update events.
Private Function GetControlValue(name As String) As Object
Dim obj As Object
For Each obj In Me.Controls
If obj.name = name Then
Set GetControlValue = obj
Exit Function
End If
Next
End Function
The next function is used to actually update the other field in the row based on the after update event. I use the GetControlValue Function listed above in this function. This is just for the operations column.
Private Function OperationsUpdate(RowNumber As Integer)
Dim Operations As Object
Dim Time As Object
Dim DecTime As Object
Dim LaborRate As Object
Set Operations = GetControlValue("cb_op" & RowNumber)
Set Time = GetControlValue("tb_time" & RowNumber)
Set DecTime = GetControlValue("tb_DecTime" & RowNumber)
Set LaborRate = GetControlValue("tb_LbrRate" & RowNumber)
Set Cost = GetControlValue("tb_Cost" & RowNumber)
If IsNull(Time.Value) Then
If IsNull(Operation.Value) Then
Cost.Value = ""
LaborRate.Value = ""
Else
LaborRate.Value = DLookup("LaborRate", "OperationsType", "[operationsID] = " & Operation.Value)
End If
Else
If IsNull(Operation.Value) Then
Cost.Value = ""
LaborRate.Value = ""
Else
LaborRate.Value = DLookup("LaborRate", "OperationsType", "[operationsID] = " & Operation.Value)
Cost.Value = DecTime.Value * LaborRate.Value
End If
End If
FormObjectsUpdate2 (RowNumber)
Set Operations = Nothing
Set Time = Nothing
Set DecTime = Nothing
Set LaborRate = Nothing
Set Cost = Nothing
End Function
Finally this is how my After update events look per control. I post just the first three not all 21 to save space.
Private Sub cb_op1_AfterUpdate()
OperationsUpdate (1)
End Sub
Private Sub cb_op2_AfterUpdate()
OperationsUpdate (2)
End Sub
Private Sub cb_op3_AfterUpdate()
OperationsUpdate (3)
End Sub
I just posted the function and the code for the first column operations all the other after update events are now having the same issues too.
I need to set a parent Control to another control using the VBA code.
Actually i am looping to create differents controls dynamically and i want now to link them by child-parent.
Do someone has an idea ?
Here is the function where i create a new control and i set some values. And the last assignment is where i want to set the parent
Public Function apply_change(ihm_f, oNode, iMyName$, project$)
Dim new_elem
Dim oSubNodes As IXMLDOMNode
If oNode.Attributes.getNamedItem("Type").Text <> "Page" Then
If (oNode.Attributes.getNamedItem("Type").Text = "RefEdit") Then
Set new_elem = ihm_f.Designer.Controls.Add("RefEdit.Ctrl", oNode.nodeName, True)
Else
Set new_elem = ihm_f.Designer.Controls.Add("Forms." & oNode.Attributes.getNamedItem("Type").Text & ".1", oNode.nodeName, True)
End If
With new_elem
On Error Resume Next
.Width = oNode.Attributes.getNamedItem("Width").Text
.Top = oNode.Attributes.getNamedItem("Top").Text
.Left = oNode.Attributes.getNamedItem("Left").Text
.Height = oNode.Attributes.getNamedItem("Height").Text
Set .Parent = get_parent(oNode.ParentNode.nodeName, oNode, ihm_f)
End With
If oNode.Attributes.getNamedItem("Type").Text = "MultiPage" Then
Call new_elem.Pages.Remove(0)
Call new_elem.Pages.Remove(0)
For Each oSubNodes In oNode.ChildNodes()
Call new_elem.Pages.Add(oSubNodes.BaseName, oSubNodes.Attributes.getNamedItem("Caption").Text, oSubNodes.Attributes.getNamedItem("Index").Text)
Next oSubNodes
End If
End If
Set apply_change = ihm_f
End Function
The getparent function return a Controle which can be anything .. like textbox or combo box etc..
You provide so little information in your question that it is difficult to guess your objective.
However, I am guessing that you want to record that Control Xxxxx is the parent of control Yyyyy where the definition of “parent” has nothing to do with Excel’s definition of parent. I am further guessing you do not know how to access controls by number.
The macro below lists the name, type and top position of every control on a form by its index number within the collection Controls. Any property of a control is accessible in this way. If control Xxxxx is the parent of control Yyyyy, you can scan the collection to find their index numbers when the form loads and record this information for use when required.
Private Sub UserForm_Initialize()
Dim InxCtrl As Long
Dim LenNameMax As Long
Dim LenTypeMax As Long
LenNameMax = 0
For InxCtrl = 0 To Controls.Count - 1
If LenNameMax < Len(Controls(InxCtrl).Name) Then
LenNameMax = Len(Controls(InxCtrl).Name)
End If
If LenTypeMax < Len(TypeName(Controls(InxCtrl))) Then
LenTypeMax = Len(TypeName(Controls(InxCtrl)))
End If
Next
Debug.Print PadR("Name", LenNameMax) & "|" & PadR("Type", LenTypeMax) & "| Top"
For InxCtrl = 0 To Controls.Count - 1
Debug.Print PadR(Controls(InxCtrl).Name, LenNameMax) & "|" & _
PadR(TypeName(Controls(InxCtrl)), LenTypeMax) & "|" & _
PadL(Format(Controls(InxCtrl).Top, "#,###.00"), 8)
Next
End Sub
I am trying to understand some code so that i can make modifications to it. I am fairly new at vbscript so any help is appreciated .
I am looking at a Dictionary object that looks like it is behaving like a table with different rows controlled by a key. I am not sure what is happening. commands like
list.add list.count+1 , ListElement are a little over my league right now. The code is able to output data for 1 use with simple statement
Sub DumpList ( list )
' This function dumps all the people in the office and their information
' in the application window. The list is usually sorted by Name.
Dim e, le
For Each e In list ' output listelements...
Set le = list(e)
LogOutput 0, "line: " & (le.num, 3) & " : " & le.Name _
& " " & le.PHNumber1 & " " & le.PHNumber2 _
& " " & le.Email1 & " " & le.Email2 _
& " " & le.DeskNo.1 & " " & le.OFficeCode _
& " " & le.wirecolour & " " & le.wirecross
Next
End Sub
I am not sure how it is working for me to make any changes to it.
Thanks again
The task:
Maintain a collection of persons.
First attempt at a solution:
Use a dictionary with numerical indices=keys, based on dic.Count
As sanwar pointed out, a dictionary stores key-value pairs. To put (info about)
persons in a dictionary, we need a person class from which we can create person
objects/instances to hold the multiple information elements.
Minimal/POC code for a Person class:
Dim g_nPersonId : g_nPersonId = -1
Class cPerson
Private m_nId
Private m_sName
Private m_dtBirth
Private m_dWage
Public Function init( sName, dtBirth, dWage )
Set init = Me
g_nPersonId = g_nPersonId + 1
m_nId = g_nPersonId
Name = sName
Birth = dtBirth
Wage = dWage
End Function
Public Property Let Name( sName ) : m_sName = sName : End Property
Public Property Let Birth( dtBirth ) : m_dtBirth = dtBirth : End Property
Public Property Let Wage( dWage ) : m_dWage = dWage : End Property
Public Property Get Id() : Id = m_nId : End Property
Public Property Get Name() : Name = m_sName : End Property
Public Property Get Birth() : m_dtBirth = m_dtBirth : End Property
Public Property Get Wage() : m_dWage = m_dWage : End Property
Public Function Data()
Data = Array( m_nId, m_sName, m_dtBirth, m_dWage )
End Function
End Class ' cPerson
[The class cPerson defines/blue prints persons, each having an id, a name, a dob, and
a salary. You create persons by calling the init function passing suitable values
for name, doc, and wage members; the id will augmented automagically (by using a global
counter instead of proper class level data as possible in more capable OO languages).]
and a demo script to prove we can create and display persons:
Dim oAdam : Set oAdam = New cPerson.init( "Adam", #1/5/2001#, 1234.56 )
Dim oEve : Set oEve = New cPerson.init( "Eve" , #1/6/2001#, 6543.21 )
Dim oPerson
For Each oPerson In Array( oAdam, oEve )
WScript.Echo Join( oPerson.Data(), " - " )
Next
output:
0 - Adam - 1/5/2001 - 1234.56
1 - Eve - 1/6/2001 - 6543.21
Now lets put them in a dictionary with numerical keys (a VBScript special
feature, other languages have string-key-only dictionaries) based on the
.Count property. The .Count property of an empty dictionary is 0, by adding
the first element (a person object holding all info we need) to the dictionary
its .Count increments to 1 (ready for the next addition). You easily can see
that .Add .Count + 1 is a waste of time/effort:
Dim dicPersons : Set dicPersons = CreateObject( "Scripting.Dictionary" )
Dim aPersons : aPersons = Array( _
Array( "Adam", #1/5/2001#, 1234.56 ) _
, Array( "Eve" , #1/6/2001#, 6543.21 ) _
)
Dim aPerson
For Each aPerson In aPersons
dicPersons.Add dicPersons.Count, New cPerson.init( aPerson( 0 ), aPerson( 1 ), aPerson( 2 ) )
Next
Dim nPerson
WScript.Echo "Adam & Eve"
For Each nPerson In dicPersons
WScript.Echo nPerson, ":", Join( dicPersons( nPerson ).Data(), " - " )
Next
dicPersons.Remove 0 ' how do we know the key of Adam?
WScript.Echo "Adam zaped"
For Each nPerson In dicPersons
WScript.Echo nPerson, ":", Join( dicPersons( nPerson ).Data(), " - " )
Next
WScript.Echo "Trying to add Caine"
On Error Resume Next
dicPersons.Add dicPersons.Count, New cPerson.init( "Caine", Date(), 0.33 )
WScript.Echo Err.Description
On Error GoTo 0
The output
Adam & Eve
0 : 0 - Adam - 1/5/2001 - 1234.56
1 : 1 - Eve - 1/6/2001 - 6543.21
Adam zaped
1 : 1 - Eve - 1/6/2001 - 6543.21
Trying to add Caine
This key is already associated with an element of this collection
shows why dictionaries with numerical indices based on .Count are no solution
for the task: Maintain a collection of persons.
In this case the variable list holds a Scripting.Dictionary.
When you use the VBScript For Each construct on Scripting.Dictionary each key in the dictionary is returned. So in this case the For Each will loop place each key in the variable e on each iteration.
The Line:-
Set le = list(e)
is now using the key for this loop to lookup the value in the dictionary associated with the key. In this case the value is an object that has properties.
Hence a dictionary can be used to quickly and directly lookup a "row" in a "table" using its key. Also as the code you've posted demonstrates you can enumerate each key and lookup each value to "scan" the whole "table".