|
|
|
2006년 7월호 |
PowerBuilder - The ClassDefinition Object
...or how to create a simple object browser - Part 2
저번 뉴스 내용에서 ClassDefinition 오브젝트에 관한 몇가지 이론들을 살펴보았고, 트리뷰 컨트롤 안에서 PB 어플리케이션의
라이브러리들을 볼수 있었습니다. 이번에는 살펴본 내용을 조사하고 라이브러리로 부터 오브젝트들을 살펴볼 것입니다.
사용자가 Treeview에서 어떤 항목을 확장할 때, 그것이 이미 확장되었는지를 검사해야 하는데, 만일 확장되어 있다면,
별다른 작업을 하지 않아도 됩니다. Treeview 컨트롤의 Itemexpanding 이벤트에서 This.GetItem을 호출함으로써 선택되어져 있는
TreeviewItem를 선택할 수 있습니다.
사전에 확장되어 있지 않았다면, Treeviewitem의 레벨 2와 같은지를 검사해야 하는데, 이것은 라이브러리 이름을 확장하는
것을 뜻합니다. 문장 분석을 위해 모든 로직을 포함하는 NVO를 만들 수도 있으나, 여기서는 간단한 데모이므로 NVO로 만들지 않습니다.
윈도우에 필요한 함수들을 정의할 때는 컨트롤들에서 주어지는 argument들을 이용하면 사용자가 나중에 해당 로직을 NVO로 옮기는
것을 쉽게 할 수 있습니다.
다음은 PBL에서 PB 오브젝트들을 읽을 때 사용되는 of_ShowLibraryItems 함수를 생성할 것입니다.
여기서는 LibraryDirectoryEx 함수를 사용해서 오브젝트들을 추출합니다. LibraryDirectoryEx 함수는 수정된 date/time와 주석뿐만 아니라
오브젝트들의 이름과 타입도 제공해 줍니다. External DataSource를 이용해 DataWindow를 사용합니다.
Figure 1은 해당
DataWindow의 컬럼정보입니다. 정렬(sorting)을 objtype column으로 정하고 d_libobj
이름으로 저장합니다.
byLibrary-DirectoryEx에서 리턴된 스트링은 ImportString에 의해 데이터스토어로 임포트될 것입니다. 데이터를 정렬한 후에, 스크립트는
오브젝트들을 통해 루프를 돌며 treeview에 추가됩니다. Listing 1은 함수 코드입니다. 레벨이 2 일때, treeview 컨트롤의 itemexpanding
이벤트에서 of_ShowLibraryItems 함수를 호출합니다. 이때에 ll_TviCurrent는 핸들 argument에 의해 정의된
treeviewItem입니다.
Listing 1: Adding the Library Items
/* Name: of_ShowLibraryItemsParameters: treeview atv_displong al_Parentstring as_LibraryReturns: (none)*/string ls_Entries, ls_Obj, ls_Typelong ll_Tot, ll_Rowdatastore lds_Sorttreeviewitem ll_TviObj // Get Entriesls_Entries=LibraryDirectoryEx(as_Library,DirAll!)ls_Entries=ls_Entries+"~n" // Add NewLine at the end // Use datastore to sort the objectslds_Sort=CREATE datastorelds_Sort.DataObject="d_libobj"lds_Sort.ImportString(ls_Entries)lds_Sort.Sort() // Loop through all entriesll_Tot=lds_Sort.RowCount()FOR ll_Row=1 TO ll_Tot ls_Obj=lds_Sort.GetItemString(ll_Row,"objname") ls_Type=lds_Sort.GetItemString(ll_Row,"objtype") CHOOSE CASE ls_Type CASE "Application" ll_TviObj.PictureIndex=PICT_APPLICATION CASE "Function" ll_TviObj.PictureIndex=PICT_FUNCTION CASE "Window" ll_TviObj.PictureIndex=PICT_WINDOW CASE "Menu" ll_TviObj.PictureIndex=PICT_MENU CASE "Structure" ll_TviObj.PictureIndex=PICT_STRUCTURE CASE "UserObject" ll_TviObj.PictureIndex=PICT_USEROBJECT CASE ELSE ll_TviObj.PictureIndex=0 END CHOOSE // use classes from above only IF ll_TviObj.PictureIndex>0 THEN ll_TviObj.Label=ls_Obj ll_TviObj.SelectedPictureIndex=ll_TviObj.PictureIndex ll_TviObj.Children=TRUE atv_Disp.InsertItemLast( al_Parent, ll_TviObj) END IFNEXTDESTROY lds_SortRETURN
Parent.of_ShowLibraryItems(
This, handle, ltvi_Current.Data )
On to Class Details
이제까지 어플리케이션은 라이브러리와 그 안에 있는 PB 오브젝트들을 보여주었습니다. 이제는 classdefinitionobject 오브젝트와 그들의
자손들을 볼 차례입니다. 저번 달에 보았던것 같이, 우리는 다양한 자손 엔트리들의 정보를 보여줄 것 입니다. 처음 부분은 Variables라 불리며
여기서 우리는 이 오브젝트의 모든 변수들을 리스트합니다. 두번째 부분에는 스크립트가 있고 세번째 부분에는 nested 클래스들이 있습니다.
마지막으로, 네번째 부분은 조상 클래스의 레퍼런스가 있습니다. 생성한 함수는 순환함수라고 불려질 수 있고 PB 오브젝트의 각 타입으로
쓰여질 수 있습니다.
PB 오브젝트의 ClassDefinition 오브젝트 변수 리스트를 통해 정보를 잘 보여주는 것은 불가능하기 떄문에 다음 단계는 2개의 도움이 되는
함수를 만들 것입니다. 첫번째 함수는 of_ValueString 라고 불리고 열거된(enumerated) 값을 스트링으로 바꾸어줍니다. (Listing 2를 보십시오).
Listing 2: Translating Enumerated Values
/* Name: of_ValueStringParameters: string as_Typeany aa_DataReturns: string*/string ls_Retls_Ret = as_Type + Char(9)CHOOSE CASE as_TypeCASE "CallingConvention" ArgCallingConvention lcca_Temp lcca_Temp=aa_DataCHOOSE CASE lcca_Temp CASE ByReferenceArgument! ls_Ret=ls_Ret+": ByReferenceArgument!" CASE ByValueArgument! ls_Ret=ls_Ret+": ByValueArgument!" CASE ReadOnlyArgument! ls_Ret=ls_Ret+": ReadOnlyArgument!" CASE VarListArgument! ls_Ret=ls_Ret+": VarListArgument!"END CHOOSECASE "Cardinality" VariableCardinalityDefinition lvcd_Temp lvcd_Temp = aa_Data CHOOSE CASE lvcd_Temp.Cardinality CASE ScalarType! ls_Ret = ls_Ret + ": ScalarType!" CASE UnboundedArray! ls_Ret = ls_Ret + ": UnboundedArray!" CASE BoundedArray! ls_Ret = ls_Ret + ": BoundedArray!" END CHOOSECASE "VariableKind" VariableKind lvk_Temp lvk_Temp = aa_Data CHOOSE CASE lvk_Temp CASE VariableGlobal! ls_Ret=ls_Ret+": VariableGlobal!" CASE VariableShared! ls_Ret= ls_Ret+": VariableShared!" CASE VariableInstance! ls_Ret=ls_Ret+": VariableInstance!" CASE VariableArgument! ls_Ret=ls_Ret+": VariableArguement" CASE VariableLocal! ls_Ret=ls_Ret+": VariableLocal!" END CHOOSECASE "ScriptKind" /* Is 0/1 instead of the following ScriptKind lsk_Temp lsk_Temp = aa_Data CHOOSE CASE lsk_Temp CASE ScriptEvent! ls_Ret=ls_Ret+": ScriptEvent!" CASE ScriptFunction! ls_Ret= ls_Ret+": ScriptFunction!" END CHOOSE */ ls_Ret=ls_Ret +": " + String( aa_Data )CASE "ReadAccess", "WriteAccess","Access" VarAccess lva_Temp lva_Temp=aa_Data CHOOSE CASE lva_Temp CASE Private! ls_Ret=ls_Ret+": Private!" CASE Public! ls_Ret=ls_Ret+": Public!" CASE Protected! ls_Ret=ls_Ret+": Protected!" CASE System! ls_Ret=ls_Ret+": System!" CASE ELSE ls_Ret=ls_Ret+":" END CHOOSECASE "Category" TypeCategory ltc_Temp ltc_Temp=aa_Data CHOOSE CASE ltc_Temp CASE SimpleType! ls_Ret=ls_Ret+": SimpleType!" CASE EnumeratedType! ls_Ret=ls_Ret+ ": EnumeratedType!" CASE ClassOrStructureType! ls_Ret=ls_Ret+": ClassOrStructureType!" END CHOOSECASE "InitialValue" IF NOT IsNull( String( aa_Data ) ) THEN ls_Ret=ls_Ret+": "+ String( aa_Data ) END IFCASE "DataTypeOf" TypeDefinition ltd_Temp EnumerationDefinition led_Temp EnumerationItemDefinition lei_Temp[] integer li_Counter ls_Ret=ls_Ret+": "+String(aa_Data) ltd_Temp=FindTypeDefinition(aa_Data) IF ltd_Temp.Category=EnumeratedType! THEN led_Temp=ltd_Temp lei_Temp=led_Temp.Enumeration FOR li_Counter=1 TO UpperBound(lei_Temp) ls_Ret=ls_Ret+"~r~n~t"+"- "+ & lei_Temp[li_Counter].Name+": " + & String( lei_Temp[ li_Counter ].Value) NEXT END IFCASE ELSE IF NOT IsNull( String( aa_Data ) ) THEN ls_Ret=ls_Ret+": "+ String(aa_Data) END IFEND CHOOSERETURN ls_Ret
이 함수의 argument들은 변수 타입(예. Cardinality 혹은 InitialValue)이고 그 자체의 값입니다. 이것은 2개 모두를 읽을 수 있는 스트링으로
리턴합니다. 값을 스트링으로 변환하는 것은 간단하게 불린(Boolean) 값입니다; 그러나 나열된(enumerated) 데이터 타입들은 각각 다루어야합니다.
만약 변수가 나열된(enumerated) 데이터 타입이면, FindTypeDefinition을 사용해서 모든 가능한 데이터타입의 열거(enumeration)들을 얻어냅니다.
Listing 3: Format Information
/* Name: of_FormatInfoParameters: classdefinitionobject acdo_Tempreturns: string*/string ls_RetClassDefinition lcd_TempVariableDefinition lvd_TempScriptDefinition lsd_TempCHOOSE CASE acdo_Temp.TypeOf()CASE ClassDefinition! lcd_Temp = acdo_Temp ls_Ret = This.of_ValueString( "Category", lcd_Temp.Category) + "~r~n" + &This.of_ValueString( "IsAutoinstantiate", lcd_Temp.IsAutoinstantiate) + "~r~n" + &This.of_ValueString( "IsStructure", lcd_Temp.IsStructure) + "~r~n" + &This.of_ValueString( "IsSystemType", lcd_Temp.IsSystemType) + "~r~n" + &This.of_ValueString( "IsVariableLength", lcd_Temp.IsVariableLength) + "~r~n" + &This.of_ValueString( "IsVisualType", lcd_Temp.IsVisualType) + "~r~n" + &This.of_ValueString( "LibraryName", lcd_Temp.LibraryName) CASE VariableDefinition!lvd_Temp = acdo_Templs_Ret = This.of_ValueString( "CallingConvention", lvd_Temp.CallingConvention ) + "~r~n" + & This.of_ValueString( "Cardinality", lvd_Temp.Cardinality ) + "~r~n"IF Lower( lvd_Temp.name ) <> "imemode" AND lvd_Temp.TypeInfo.DataTypeOf <> "string" THEN// Crash otherwise e.g. string s = 'crash' ls_Ret = ls_Ret + This.of_ValueString( "InitialValue", lvd_Temp.InitialValue) + "~r~n"END IF ls_Ret = ls_Ret + This.of_ValueString( "IsConstant", lvd_Temp.IsConstant) + "~r~n" + & This.of_ValueString( "IsControl", lvd_Temp.IsControl) + "~r~n" + & This.of_ValueString( "IsUserDefined", lvd_Temp.IsUserDefined) + "~r~n" + & This.of_ValueString( "VariableKind", lvd_Temp.Kind) + "~r~n" + & This.of_ValueString( "OverridesAncestorValue", lvd_Temp.OverridesAncestorValue) + "~r~n" + & This.of_ValueString( "ReadAccess", lvd_Temp.ReadAccess) + "~r~n" + &This.of_ValueString( "WriteAccess", lvd_Temp.WriteAccess) + "~r~n" + & "~r~nTypeInfo~r~n" + & This.of_ValueString( "Category", lvd_Temp.TypeInfo.Category) + "~r~n" + & This.of_ValueString( "DataTypeOf", lvd_Temp.TypeInfo.DataTypeOf) + "~r~n" + &This.of_ValueString( "IsStructure", lvd_Temp.TypeInfo.IsStructure) + "~r~n" + & This.of_ValueString( "IsSystemType", lvd_Temp.TypeInfo.IsSystemType) + "~r~n" + & This.of_ValueString( "IsVariableLength", lvd_Temp.TypeInfo.IsVariableLength) + "~r~n" +& This.of_ValueString( "IsVisualType", lvd_Temp.TypeInfo.IsVisualType) + "~r~n" +& This.of_ValueString( "LibraryName", lvd_Temp.TypeInfo.LibraryName) + "~r~n" +& This.of_ValueString( "Name", lvd_Temp.TypeInfo.Name) CASE ScriptDefinition!lsd_Temp = acdo_Temp ls_Ret = This.of_ValueString( "Access", lsd_Temp.Access ) + "~r~n" + & This.of_ValueString( "AliasName", lsd_Temp.AliasName) + "~r~n" + & This.of_ValueString( "EventId", lsd_Temp.EventID ) + "~r~n" + & This.of_ValueString( "EventIdName", lsd_Temp.EventID ) + "~r~n" + & This.of_ValueString( "ExternalUserFunction", lsd_Temp.EventID ) + "~r~n" + & This.of_ValueString( "IsExternalEvent", lsd_Temp.IsExternalEvent ) + "~r~n" + & This.of_ValueString( "IsLocallyDefined", lsd_Temp.IsLocallyDefined ) + "~r~n" + & This.of_ValueString( "IsLocallyScripted", lsd_Temp.IsLocallyScripted ) + "~r~n" + & This.of_ValueString( "IsRPCFunction", lsd_Temp.IsRPCFunction ) + "~r~n" + & This.of_ValueString( "IsScripted", lsd_Temp.IsScripted ) + "~r~n" + & This.of_ValueString( "ScriptKind", lsd_Temp.Kind ) + "~r~n" + & This.of_ValueString( "SystemFunction", lsd_Temp.SystemFunction ) IF IsValid( lsd_Temp.ReturnType ) THEN ls_Ret = ls_Ret + "~r~n" + & "~r~nReturnType~r~n" + & This.of_ValueString( "Category", lsd_Temp.ReturnType.Category ) + "~r~n" + & This.of_ValueString( "DataTypeOf", lsd_Temp.ReturnType.DataTypeOf) + "~r~n" + & This.of_ValueString( "IsStructure", lsd_Temp.ReturnType.IsStructure) + "~r~n" + & This.of_ValueString( "IsSystemType", lsd_Temp.ReturnType.IsSystemType) + "~r~n" + & This.of_ValueString( "IsVariableLength", lsd_Temp.ReturnType.IsVariableLength) + "~r~n" +& This.of_ValueString( "IsVisualType", lsd_Temp.ReturnType.IsVisualType) + "~r~n" +& This.of_ValueString( "LibraryName", lsd_Temp.ReturnType.LibraryName) + "~r~n" +& This.of_ValueString( "Name", lsd_Temp.ReturnType.Name) END IFCASE ELSE ls_Ret = "Missing " + ClassName( acdo_Temp )END CHOOSERETURN ls_Ret
이것의 목적은 classdefinitionobject 정보 바깥에서 포멧된 스트링을 만드는 것입니다. Argument는 classdefinitionobject 타입이고, 이는
classdefinition, scriptdefinition, 그리고
variabledefinition 타입의 변수로 전달할 수 있게 합니다.
Display the Information about a Class
다음 함수들을 만들기위해, treeview로 되돌아 갑니다. Treeview에서 PB 오브젝트를 확장했을때, of_ShowClassDefinition 함수를 불러와야 합니다.
함수의 정의는 Listing 4에 있습니다.
Listing 4: Show ClassDefinition
/* Name: of_ShowClassDefinitionParameters: treeview atv_Displong al_Parentclassdefinition acd_Datareturns: (none)*/integer li_Countertreeviewitem ltvi_Templong ll_Handleclassdefinition lcd_Parent// Show Varables. No Controlsll_Handle = atv_disp.InsertItemLast( al_Parent, "Variables", PICT_VARIABLES)FOR li_Counter = 1 TO UpperBound( acd_Data.VariableList ) IF NOT acd_Data.VariableList[li_Counter].IsControl THEN of_ShowVariable( atv_Disp, ll_Handle, acd_Data.VariableList[li_Counter]) END IFNEXT// Add Scriptsll_Handle = atv_disp.InsertItemLast( al_Parent, "Scripts",PICT_FUNCTION)FOR li_Counter = 1 TO UpperBound( acd_Data.ScriptList ) of_ShowScript( atv_Disp, ll_Handle, acd_Data.ScriptList[li_Counter] )NEXT // All the Nested classesll_Handle = atv_disp.InsertItemLast( al_Parent, "Nested Classes", PICT_NESTED )FOR li_Counter = 1 TO UpperBound( acd_Data.nestedclasslist ) // Checking the parent prevents showing of nested classes from an ancestor and recursive information e.g. on structures lcd_Parent = acd_Data.nestedclasslist[li_Counter].ParentClass IF NOT IsNull( lcd_Parent ) THEN IF lcd_Parent.Name = acd_Data.Name THEN ltvi_Temp.Label = acd_Data.nestedclasslist[li_Counter].name ltvi_Temp.Children = TRUE ltvi_Temp.PictureIndex = PICT_USEROBJECT ltvi_Temp.SelectedPictureIndex = ltvi_Temp.PictureIndex atv_disp.InsertItemLast( ll_Handle, ltvi_Temp ) END IF END IFNEXT// Show ancestor entryIF NOT IsNull( acd_Data.Ancestor ) THEN ltvi_Temp.Label = acd_Data.Ancestor.Name ltvi_Temp.Children = TRUE // Get same picture as parent item treeviewitem ltvi_Parent atv_disp.GetItem( al_Parent, ltvi_Parent) ltvi_Temp.PictureIndex = ltvi_Parent.PictureIndex ltvi_Temp.SelectedPictureIndex = ltvi_Temp.PictureIndex atv_disp.InsertItemLast( al_Parent, ltvi_Temp )END IFRETURN
세개의 아귀먼트: treeview 컨트롤, 클릭된 아이템의 처리, 그리고 ClassDefinition 오브젝트를 가지고 있습니다. 소스의 첫 번째 레벨에서
ClassDefinition 오브젝트는 FindClassDefinition 함수를 argument가 treeview 아이템 라벨과 같게 부름으로서 결정됩니다. 여기에 레벨이 2보다
클때의 treeview의 itemexpanding 이벤트에 있는 코드가 있습니다. 변수 lcd_Temp는
ClassDefinition 타입이고 ll_Child은 long 타입입니다:
ll_Child = This.FindItem( ChildTreeItem!, handle )
IF ll_Child = -1 THEN // No children yet
lcd_Temp= FindClassDefinition (&
ltvi_Current.Label,il_LibList )
IF NOT IsNull(lcd_Temp) THEN
Parent.of_ShowClassDefinition(&
tv_1,handle,lcd_Temp)
END IF
END IF
변수 부분은 ClassDefinition의 VariableList
프로퍼티를 통한 반복(looping)으로 채워져있고, IsControl 프로퍼티들을 사용하여 컨트롤들이
추가되는것을 막고 IsUserDefined를 사용해서 treeviewitem의 이미지를 지정합니다. 각 변수에서
of_ShowVarible 함수는 (Listing 5를 보십시오.)
Listing 5: Show Variable
/* Name: of_ShowVariableParameters: treeview atv_Displong al_Parentvariabledefinition avd_Datareturns: (none)*/treeviewitem ltvi_Templtvi_Temp.Label = avd_Data.Nameltvi_Temp.Data = This.of_FormatInfo( avd_Data )// Get picture depending of kind of variable and user defined or notCHOOSE CASE avd_Data.KindCASE VariableGlobal! ltvi_Temp.PictureIndex=PICT_VARGLOBAL CASE VariableInstance! ltvi_Temp.PictureIndex=PICT_VARINSTANCE IF avd_Data.IsUserDefined THEN ltvi_Temp.OverlayPictureIndex = 1 END IF CASE Variableshared! ltvi_Temp.PictureIndex=PICT_VARSHARED CASE VariableArgument! ltvi_Temp.PictureIndex= PICT_VARARGUMENT CASE VariableLocal! ltvi_Temp.PictureIndex=PICT_VARLOCALEND CHOOSEltvi_Temp.SelectedPictureIndex = ltvi_Temp.PictureIndex atv_disp.InsertItemLast( al_Parent, ltvi_Temp )RETURN
이 함수는 변수 정보를 treeviewitem의 데이터 프로퍼티로 채워넣기 위해 호출됩니다. 변수들을 다루고 난 후, 함수는 스크립트들로 넘어갑니다.
ClassDefinition의 ScriptList 프로퍼티에서의 각 엔트리에서, the
function of_ShowScript (Listing 6)의 함수가 불려집니다.
Listing 6: Show Script
/* Name: of_ShowScriptParameters: treeview atv_Displong al_Parentscriptdefinition asd_Datareturns: (none)*/treeviewitem ltvi_Templong ll_Handle, ll_Handle2integer li_Counter ltvi_Temp.Label = asd_Data.Nameltvi_Temp.Data = This.of_FormatInfo( asd_Data )IF asd_Data.IsLocallyScripted THEN ltvi_Temp.OverlayPictureIndex = 1ELSE ltvi_Temp.OverlayPictureIndex = 0END IFIF asd_Data.IsScripted THEN ltvi_Temp.PictureIndex = PICT_SCRIPTYESELSE ltvi_Temp.PictureIndex = PICT_SCRIPTNOEND IFltvi_Temp.SelectedPictureIndex = ltvi_Temp.PictureIndexll_Handle = atv_disp.InsertItemLast( al_Parent, ltvi_Temp ) // Use Picture index and overlay index from parentltvi_Temp.Label = "Source"ltvi_Temp.Data = asd_Data.Sourceltvi_Temp.SelectedPictureIndex = ltvi_Temp.PictureIndexatv_disp.InsertItemLast( ll_Handle, ltvi_Temp ) ltvi_Temp.OverlayPictureIndex = 0ltvi_Temp.Label= "ArgumentList"SetNull( ltvi_Temp.Data )ltvi_Temp.PictureIndex=PICT_VARARGUMENTltvi_Temp.SelectedPictureIndex = ltvi_Temp.PictureIndexll_Handle2 = atv_disp.InsertItemLast( ll_Handle, ltvi_Temp )variabledefinition lvd_Temp[]lvd_Temp = asd_Data.ArgumentListFOR li_Counter = 1 TO UpperBound( lvd_Temp ) This.of_ShowVariable( atv_disp, ll_Handle2,lvd_Temp[li_Counter] )NEXT ltvi_Temp.Label= "LocalVariableList"SetNull( ltvi_Temp.Data )ltvi_Temp.PictureIndex=PICT_VARLOCALltvi_Temp.SelectedPictureIndex = ltvi_Temp.PictureIndexll_Handle2 = atv_disp.InsertItemLast( ll_Handle, ltvi_Temp )lvd_Temp = asd_Data.LocalVariableListFOR li_Counter = 1 TO UpperBound(lvd_Temp ) This.of_ShowVariable( atv_disp, ll_Handle2, lvd_Temp[li_Counter] )NEXTRETURN
여기에서 IsLocallyScripted와 IsScripted 프로퍼티를 사용하여 treeviewitem의 이미지를 지정합니다.
Treeviewitem의 데이터 프로퍼티는 스크립트에 대한 전체적인 정보를 가지고 있습니다. 우리가 추가한 각 스크립트에는 ArgumentList라
불리는 엔트리 Source(스크립트의 소스를 저장)가 있고 다른 하나는 LocalVariableList라 불리는 것이 있습니다. 각 변수와 of_ShowVariable
함수를 불러와 엔트리 아래에 있는 정의를 포함한 각 argument 변수와 로컬 변수를 추가합니다. Compile을 하기 위해서는 of_ShowVariableFirst,
of_ShowScript, 그리고
of_ShowClassDefinition을 순서대로 정의해 주어야 합니다.
그리고 나면 nested 클래스들이 보입니다. Nested 클래스의 이름을 만드는 방법은 objectname`nestedclassname입니다. 우리는 FindClassDefinition을
불러올때 nested 클래스에 대한 많은 정보를 찾기 위해 정확한 이름이 필요합니다. Nested 클래스들에서는 ParentClass 프로퍼티를 검사합니다.
만약 이것이 null이면, 클래스를 추가하지 않습니다. 더 나아가서 NestedClassList는 오브젝트의 컨트롤들 뿐만 아니라 오브젝트 조상의
컨트롤들도 리턴합니다. NestedClass의 ParentClass 이름과 현재 오브젝트의 이름을 비교하여 조상 데이터가 보이는것을 막아줍니다.
이들이 일치하지 않는다면, 컨트롤은 선조로부터 상속된 것입니다. 만약 이것이 존재한다면 리스트의 끝에 선조 오브젝트를 위한 엔트리를 추가합니다.
Treeview 아이템들의 데이터 프로퍼티에 저장된 추가의 정보를 보기위해서 사용자는 treeview의 selectionchanged 이벤트 안에
다음의 코드를 입력해야합니다:
treeviewitem
ltvi_Current
This.Getitem(newhandle,ltvi_Current)
IF ClassName(ltvi_Current.data)="string"THEN
mle_1.Text=ltvi_Current.Data
ELSE
mle_1.Text=""
END IF
RETURN
어플리케이션이 동작하고 라이브러리 엔트리가 확정되어져 있을때 해당 엔트리의 모든 정보는 한번에 로드되어집니다.
우리는 내포되어 있는 클래스와 조상단에 대한 정보를 분석하지 않아도 조회되어 집니다. 크기가 큰 오브젝트는 시간이 좀더 걸릴수 있다.
엔트리를 드릴다운 하기 위해서 사용자는 treeview에서 아이템을 확장하여야 합니다. 만약에 nested 클래스이나 조상이고 데이터가 로드되어있지 않다면,
선택된 엔트리의 ClassDefinition 오브젝트는 FindClassDefiniton에 의해 조회되고 of_ShowClassDefinition 함수가 불려지며 ClassDefinition
오브젝트 argument로 이동합니다. ClassDefinition 오브젝트가 모든 PB 클래스들에서 사용하므로, 더 이상의 코딩은 필요하지 않습니다.
어플리케이션은 검색할 준비가 되어있습니다. PFC-based 어플리케이션을 검색하는 것은 흥미로우며 많은 레벨로 드릴 다운할 수 있게 합니다.
Conclusion
이 내용을 통해 사용자가 ClassDefinition 오브젝트와 이와 관련된 오브젝트들을 기본적으로 이해하였기를 기대합니다. Creating a browser for PB
오브젝트들을 위한 브라우져를 만드는 것은 이러한 오브젝트들을 사용하는것입니다. 오브젝트의 상속이나 어떠한 함수의 가용성(FindMatchingFunction
함수를 참조하십시오)에 대한 정보를 얻기를 원할때에 아마도 사용자는 어플리케이션에서 이들을 사용할 것입니다.
다른 방법으로는 Web Service를 위한
WSDL 파일들을 동적으로 만드는것이 될 수 있습니다.
*원본파일
- http://pbdj.sys-con.com/read/220782.htm, By:Arthur Hefti