Dynamiek met VO-Script
Dynamiek met VO-Script
Door Bert Dingemans DLA Ontwerp & Software
[email protected]
DLA-Architect homepage
Dynamiek met VO-Script
Inleiding Op de SDGN nieuwsgroep voor Visual Objects ontstond laatst een draad van berichten over de nieuwe libraries bij VO-2.6. Een van deze libraries kwam speciaal aan de orde, VO-Script. Dit product heb ik in de afgelopen periode naar volle tevredenheid toegepast in een aantal VO-applicaties. In dit artikel wil ik dan ook ingaan op hoe VO-Script te gebruiken is.
Installatie en werking Installatie van de library is eenvoudig. Het bestaat uit een Aef bestand dat geimporteerd kan worden in een project. Dit moet gecompileerd worden en je kunt de library gaan gebruiken. Dat betekent dan ook dat het gebruik van een extra DLL of iets dergelijks verder niet nodig is. Het enige wat je moet doen is de VO-Script library opnemen in je applicatie library list. De opzet van VO-Script is eenvoudig. Aan de functies van VO-Script wordt een tekst meegegeven waarin een stukje VO programma code is opgenomen. De library heeft een interface die bestaat uit een viertal functies te weten compilecode en compilescript en executecode en executescript. De script varianten zijn daarbij ook nog eenvoudige uitbreidingen op basis van een memoread waarbij een tekstfile gelezen wordt die vervolgens weer met behulp van de code varianten verder verwerkt worden. De compilecode doet iets vreemds, allereerst wordt de tekststrng met de programmacode omgezet naar een array waarin de regels opgenomen worden waarin de regels code opgenomen worden. Daarin zit dan nog een functie ervoor zorgt dat selectie en interatie functies als IF ENDIF en DO ENDDO worden omgezet naar meerdimensionale arrays. Vervolgens wordt deze array weer omgezet naar een tekststring. Dit is de return waarde van de functie. De tekststring, noem het gecompileerde code, wordt vervolgens als parameter meegegeven aan de executecode functie. Hierbinnen wordt de string weer omgezet naar een array die regel voor regel als codeblock uitgevoerd. Vandaar dat de omzet van iteraties en selecties naar een multidimensionale array verklaarbaar is. Reden voor het omzetten van programmacode naar een string van omgezette programmaregels is denk ik tweeledig. De eerste is dat de programmacode niet meer leesbaar is en dus gebruikt kan worden om scripts versleuteld te gebruiken. Daarnaast heeft de compilecode routine de mogelijkheid om de programmacode op correctheid te controleren. Waarbij de executecode sneller kan werken omdat compileren al heeft plaatsgevonden. In principe is dit alles wat te vertellen is over VO-Script. Er is nog te noemen dat er ook een class variant is voor de scripts. Echter mijn ervaringen met scripting zijn zodanig dat ik steeds meer mogelijkheden zag om scripts te gebruiken om mijn applicaties dynamiek te geven. In de rest van dit artikel wil ik ingaan op deze mogelijkheden. Hierbij worden voorbeelden gebruikt die ik heb toegepast in het ontwikkelen van een CASE tool.
Code genereren De eerste opzet waarvoor ik VO-Script ben gaan gebruiken is voor het genereren van programma code. VO-Script is hiervoor een goed hulpmiddel. De scripts zijn aan te passen zonder dat de toepassing zelf veranderd hoeft te worden. Dit maakt de werking van een CASE tool flexibeler, omdat gebruikers vaak eigen programmeerstandaards (en zelfs ontwikkelomgevingen) hebben. Vanuit de applicatie wordt een script met behulp van de volgende methode aangeroepen.
METHOD ExecuteText( ) CLASS WndScript LOCAL cCode AS STRING SELF:Pointer := Pointer{ POINTERHOURGLASS } cCode := oDCActionMLE:TextValue
DLA-Architect homepage
Dynamiek met VO-Script
cCode:=CompileCode(cCode) IF !Empty(cCode) ExecuteCode(cCode, oMainWindow) ENDIF SELF:Pointer := Pointer{ POINTERARROW }
Zoals te zien is de code recht toe recht aan. Er zit één bijzonderheid in dat is de parameter die meegegeven wordt aan de ExecuteCode functie. De eerste parameter is de gecompileerde code in de vorm van een tekstvariabele. De tweede is een verwijzing naar het shellwindow van de actieve applicatie. Door deze parameter is het mogelijk om gebruik te maken van de GUI objecten die in de applicatie zitten, met name de windows, maar ook het menu van de applicatie. Hierdoor kunnen we vanuit het script windows openen en parameteriseren die binnen de applicatie zitten. Zo heb ik in mijn applicatie een aantal generieke windows opgenomen waarmee je uit lijstjes één of meerdere items kunt selecteren, een waarde invullen of een JaNee vraag stellen. In onderstaande afbeelding is te zien hoe een generiek window vanuit het script geopend en geinstantieerd is.
Het bijbehorende VO-script ziet er als volgt uit:
FUNCTION SybaseStatic(oShell) LOCAL aOptions LOCAL oWndOptions
DLA-Architect homepage
Dynamiek met VO-Script
aOptions := {} Aadd(aOptions, "Create Tables ") Aadd(aOptions, "Create Constraints") Aadd(aOptions, "Drop Tables") oWndOptions:=WndMultiSelect{ oShell, aOptions } oWndOptions:Caption:="Select the actions to execute" IF oWndOptions:Show() = 0 RETURN ENDIF aOptions:=oWndOptions:Result
Het script laat een aantal eigenschappen van VO-script zien. Allereerst zie je dat een script met het woord function begint. Ook andere worden zoals procedure en method zijn mogelijk. Vervolgens is te zien dat je variabelen kunt declareren. Echter je kunt de objecten niet benoemen met AS. Dat komt doordat het script met behulp van macro’s wordt verwerkt. Macro’s zijn altijd late binding omdat ze tijdens runtime worden uitgevoerd. Dat betekent voor objecten dat er geen problemen zijn. De meeste objecten in VO zijn ook op basis van late binding opgezet om overerving te implementeren. Bij functies kunnen problemen optreden. Functies die strong typed zijn kunnen niet in het script opgenomen worden. Dit vanwege de early binding wat met macro’s dus niet mogelijk is. Er is wel een eenvoudige workaround voor deze functies, neem in je VO-applicatie weak typed functies op als een schil om de strong typed functie met een iets andere naam. Als laatste is te zien dat de code in VO-script 100% VO is waarbij het eenvoudig is om gebruik te maken van zelf gedefinieerde objecten.
DbServers openen Naast de toegang die je hebt tot de windows en menu’s via het shellwindow kun je met VO-Script ook toegang krijgen tot je eigen DbServer objecten. Gewoon door ze te declareren als object en te bewerken in je script. In het code voorbeeld een stukje code met mijn eigen dbserver objecten. Hierbij is het belangrijk te weten dat dit objecten zijn waarin mijn eigen overervingsstructuur is opgenomen. Dus Classserver overerft van AbstractDbServer wat overerft van DbServer etc.
LOCAL oClass // AS ClassServer LOCAL oTF // AS TextFile LOCAL cLine // AS STRING oClass:=ClassServer{} oTF:=TextFile{} oTF:Open(DefaultFile("VOCODE.PRG") oClass:MakeRelations() oClass:GoTop() DO WHILE .NOT. oClass:Eof cLine:= "CLASS " + Alltrim(oClass:ClassName) … oClass:PropertyServer:GoTop() DO WHILE .NOT. oClass:PropertyServer:Eof … oTF:WriteLine(cLine) oClass:PropertyServer:Skip() ENDDO
DLA-Architect homepage
Dynamiek met VO-Script
oClass:Skip(1) ENDDO …
Het voorbeeld laat zien hoe je met een DO-WHILE loop door het class object kunt lopen en vervolgens stukjes code kunt aanmaken die weggeschreven worden naar een textfile object. De navigatie kan gewoon met de standaard methoden zoals skip en gotop. Ook is in het voorbeeld te zien hoe ook eigen methoden aangeroepen kunnen worden (makerelations).
DbServers aanpassen Binnen de CASE toepassing die ik ontwikkeld heb is een routine gemaakt waarbij je bij het openen van de applicatie een directory kunt opgeven waar de repository (een set DBF bestanden) staat. Geef je hier een niet bestaande directory op dan maakt de applicatie deze directory aan en maakt daarin vervolgens op basis van de interne structuur, zoals deze in mijn DbServer objecten zit, zelf aan. Hiermee is het mogelijk geworden om meerdere repositories naast elkaar te gebruiken. Echter er ontstond al snel behoefte aan extra functionaliteit. Zo is in het ene project behoefte aan extra eigenschappen voor bijvoorbeeld webpagina’s terwijl in het andere project tijdens het modelleren meer behoefte bestaat aan extra projectmanagement gegevens. Het moest dus mogelijk zijn om extra velden toe te voegen en deze zichtbaar te maken binnen de toepassing. Nu was het een geluk dat ik de DbServer objecten al gebruikte als container voor allerlei zaken zoals: • Validatie van invoer, • Het tonen van controls op basis van voorwaarden • Definiëren van een domein voor velden (voor comboboxen) • Opbouw van databrowser objecten. Door gebruik te maken van VO-Script was het mogelijk om deze eigenschappen van mijn DbServer objecten aan te passen, en daarnaast om nieuwe eigenschappen toe te voegen aan het dbserver object. De opzet is eenvoudig. In de postinit methode wordt gecontroleerd of er een tekstbestand is met Server aanpassingen. Is dit het geval dan wordt het script uitgevoerd, waarbij het dbserver object als parameter wordt meegegeven. Dit script bestaat uit een DO CASE die voor ieder object kijkt of er aanpassingen zijn. Zie als voorbeeld het onderstaande script:
FUNCTION LoadExtraInfo(oServer) LOCAL cCurrentServer AS STRING cCurrentServer := oServer:GetClassName() DO CASE CASE cCurrentServer ="CLASSSERVER" oServer:SetDisplayExpression("oS:CLASSNAME") oServer:SetScriptExpression([SaveTrim(oS:CLASSName)]) oServer:ReturnField := "CLASSNAME" // nieuw veld definieren oServer:AddFieldDescription(“MDW”, “Naam medewerker”, “Geef de naam van de medewerker”, “C”, 25, 0) //verplichte velden instellen oServer:AddNotNullField("CLASSNAME") // default waarde instellen
DLA-Architect homepage
Dynamiek met VO-Script
oServer:AddInitVal("CONCRETE", .T.) // sorteervolgorde instellen oServer:AddOrderInfo("CLASSNAME", "Upper(CLASSNAME)", "Classname") // domein opgeven kan zowel statisch als een dbserverobject zijn oServer:AddFieldDomain("INHEFROM", "CLASSSERVER") oServer:AddFieldDomain("LAYER", { { "Business domain", "1" }, { "Working Organisation", "2" }, { "Presentation", "3" } }) // instellen databrowser kolommen oServer:AddBrowserColumn("CLASSNAME") oServer:AddBrowserColumn("LAYER") ….
Zoals te zien in de code is het mogelijk om de belangrijkste zaken voor velden binnen het DbServer object in te stellen en eventueel aan te passen. De opzet is dat er eerst een standaard script wordt uitgevoerd en dat vervolgens het tekstbestand met script wordt verwerkt. Hierdoor kunnen default instellen eventueel worden overschreven. Op basis van de instellingen zoals hierboven ingesteld worden allerhande zaken ingesteld zoals indexen en domeinen, maar ook databrowsers en invoer/bewerk windows. De applicatie is hiermee bijzonder dynamisch geworden.
DLA-Architect homepage