[Allegro] Views revisited (sehr lang)
Thomas Berger
ThB at Gymel.com
So Mär 30 20:15:14 CEST 2014
Liebe Liste, lieber Herr Eversberg,
Views als eine Art Funktionalitaet von a99 wurden im Mai 2000
eingefuehrt, und es sah damals so aus, als seien sie eine
vielversprechende, neue Art der listenartigen Aufbereitung
von Datensaetzen: Als Abloesung fuer den "Cockpit-Listendruck-
mechanismus oder fuer die Praesentation der lokalen Aufstellungs-
systematik oder anderer Klassifikationen.
Im Prinzip hat sich an dieser Einschaetzung nichts geaendert,
allerdings ist an den View-Flexen ausser einer kleinen
Ueberarbeitung um Jahr 2003 nicht viel passiert, die Mehrzahl
der Menues in a99 ist allerdings mittels (statischer und nicht
datensatzbezogener) Views realisiert, insbesondere die
Picklists in ORDER (und m.E. auch ALF).
In allegro-NRW hatte ich allerdings seinerzeit einen Ansatz
realisiert, der Views im Rahmen der "normalen" Listenproduktion
nutzt: Die Sortierung der Ergebnismenge wird nicht anhand
"klassischer" Sortierparameter s-*.apr (dort natuerlich .mpr)
realisiert, sondern ueber die Produktion eines Views: Dieser
kann dann interaktiv gesichtet (und ausgeduennt) werden, dann
erst erfolgt die Weiterverarbeitung in die eigentliche Liste
mittels der "Print"-Parameter p-*.apr
Neulich erreichten mich dazu aber klagen, der Mechanismus
fragt an irgendeiner Stelle sinngemaess "Wollen Sie die ViewListe
aufbewahren? Dann geben Sie ihr jetzt einen Namen!". Frueher
wusste man wohl eher, dass die Frage nicht ganz ernst gemeint
war (es gibt selten die Situation, dass man eine /Ergebnismenge/
- und ein View ist ja nicht viel anderes - langfristig aufbewahren
will, und die fertige Liste kann man je nach Format ja auch
noch ohne Hilfestellung durch a99 direkt aus dem Browser oder
der Textverarbeitung oder dem Windows-Explorer irgendwo dort
abspeichern, wo man /Dokumente/ normalerweise speichert), jetzt
allerdings stellte sich heraus, dass der Versuch, einen Namen
wie "Neuerwerbungen Frühjahr 2014" zu vergeben, scheiterte und
zu einer nicht produzierten bzw. nicht auffindbaren Liste fuehrte.
Es zeigte sich, dass das "rename" von a99 nicht mit Spatien in
Dateinamen oder -pfaden umgehen konnte (korrigiert in v34.0, ein
analoges Problem mit fcopy wird in v34.1 korrigiert sein), aber
unabhaengig davon auch Umlaute in Dateinamen fuer viele Flexe
(z.B. auch param.flx, der bei "allgemeine Dateiverwaltung" hilft)
ein ernstes Problem darstellen: /Guenstigstenfalls/ werden sie
manchmal falsch dargestellt.
% --
Der Umgang mit Dateinamen ist plattformabhaengig und auch vom
konkreten Computer: Wir wissen ja nicht, ob ein "natives" NTFS-
Laufwerk per SMB uebers Netz eingebunden ist oder nicht doch
ein irgendwie unbekannt organsiertes bzw. die Einbindung ueber
Novell (gibt es das noch) oder NFS erfolgt. Und ob Linux-Hardlinks,
Symlinks, "Junktions", multiple Mountpoints etc. vorliegen. D.h.
wir koennen niemals sicher erkennen, ob zwei Pfade oder Dateinamen
gleich sind, oder ob sie "legal" sind: Wenn das Betriebssystem
uns sagt, eine Datei existiert unter einem bestimmten Namen, dann
wird das so stimmen, und wenn wir das Betriebssystem dazu bringen
koennen, eine Datei unter einem gegebenen Namen anzulegen oder zu
oeffnen, ohne dass es eine Fehlermeldung gibt, dann ist der Name
ein legaler Dateiname. Nach meinen Experimenten erwarten bzw.
liefern die entsprechenden Flex-Befehle, die solche Betriebssystem-
operationen umsetzen (etwa "open" oder "rename") als Zeichencodierung
die jeweilige "Windows-Codepage" des Rechners, hier also typischerweise
CP 1252. /Normalerweise/ ist die Zeichencodierung, die man in Flex-
Dateien nutzt, jedoch die der Datenbank, und das ist meist eine
Codierung, die dem DOS-Zeichensatz CP 850 noch am ehesten entspricht.
(noch eher dem Zeichensatz CP437, aber der ist seit ca. 20 Jahren
voellig irrelevant: Bereits MSDOS 6 nutzte ihn eigentlich nicht)
[arbeitet man hingegen mit dem Kommandointerpreter cmd.exe, so
erwartet das Betriebssystem Codierung entsprechend der korrespondierenden
DOS-Codepage, hier typischerweise CP 850. Das muss man beachten, wenn
man on the fly .bat-Dateien in Flexen zusammenbastelt. Die /Argumente/
aus einem call-Aufruf hingegen werden automatisch (ob von a99 mittels
o-Tabelle oder von Windows mittels eingebauter Konkordanz) in den
DOS-Zeichensatz gewandelt: Die muessen also anders codiert sein als das
fuer on-the-fly-Dateien zusammengebastelte, naemlich so, wie man es
fuer ein direkt im Flex ausgefuehrtes "open" etc. benoetigt.]
Die involvierten vier Zeichensaetze:
1. Datenbank (etwa allegro-OSTWEST)
2. a99-Anzeige (etwa allegro-Windows)
3. MS-DOS-Codepage des Rechners (typisch CP 850)
4. Windows-Codepage des Rechners (typisch CP 1252, "Windows Latin" als
Superset von ISO 8859-1 aka "ISO Latin")
sind untereinander nur teilweise kompatibel: Zwischen 1. und 2. vermittelt
die o-Tabelle bzw. ihr inverses, zu 3. und 4. gibt es im Betriebssystem
verankerte Unicode-Konkordanzen, ueber die es fallweise umrechnet: Auf
einer tieferen Ebene das Betriebssystems liegen Dateinamen ohnehin "in
Unicode" vor, was dann tatsaechlich auf dem Geraet gespeichert ist (man
denke an FAT-16 formatierte USB-Sticks oder Speicherkarten) mag wieder
etwas anderes sein...
Fazit: In jeglichen Flexen sollten Dateinamen so vorliegen, dass "es"
funktioniert (also in Windows Latin), und Umformung mittels "ascii" oder
"ansi", die ja die o-Tabelle nutzt und nicht die zutreffendere Konkordanz
CP 1252<->CP 850, sollte moeglichst Umstaenden vermieden werden: Bei
Grundbuchstaben unterscheiden sich die vier Zeichensaetze ohehin nicht,
bei "normalen" Umlauten und akzentuierten Kleinbuchstaben sind sie
noch kompatibel, bei Grossbuchstaben mit Aigu oder Grave hingegen gibt
es Unterschiede: Eingabe ueber die regulaere Tastatur erzeugt merkwuerdige
allegro-Anzeigen aber "korrekte" Dateinamen und umgekehrt (und das
Speichern von Dateinamen in Datensaetzen sollte man aus diesen Gruenden
ebenfalls moeglichst vermeiden).
Ich habe versucht, in einer Art Flex-Library "fnutil.flx" die Handhabung
von Dateinamen zu buendeln, denn im Prinzip gibt es ja nur wenige
/Operationen/, die damit zu erfolgen haben. Etwa: Extrahiere den
Verzeichnisnamen oder eigentlichen Dateinamen oder die Extension aus
einem gegebenen Objekt, unternehme eine gewisse Normalisierung (loesche
bekannte spezielle Zeichen wie "|", ">", "<", "*", "?" und "&" sowie
schuetze Dateinamen mit Spatien durch Umschliessen mit
Doppelanfuehrungszeichen), stelle einem Dateinamen einen Pfad voran oder
tausche die Extension gegen eine andere Extension aus: Die Idee dahinter
ist, dass solche Operationen nicht ganz trivial sind ("Extension ist alles
hinter dem letzten Punkt" etwa beruecksichtigt fatalerweise nicht, dass evtl.
eine Datei ohne Extension zusammen mit einem Pfadnamen mit Punkt vorliegen
kann oder die ganze Angelegenheit mit Anfuehrungszeichen umschlossen ist),
vor allem aber, dass man in gewisse Zeichencodierungsfettnaepfchen nicht
so leicht faellt, wenn man konsequent nur solche Manipulationen an Dateinamen
vornimmt, wie sie diese Library anbietet.
Der Versuch der Ueberarbeitung der View-Flexe im Hinblick auf die Nutzung
dieser Library brachte dann in Erinnerung, dass hier ein komplexes
Wechselspiel zwischen Flexen, Parameterdateien und RTF-Menueseiten vorliegt,
die Daten untereinander ueber die "traditionelle" Methode von Anwender-
variablen austauschen. Eine in einer RTF-Seite eingebettete #u-Variable
wird allerdings automatisch der Umwandlung ascii->ansi unterzogen, handelt
es sich um einen Dateinamen, den wir eigentlich in CP1252 aufbewahren wollen,
muss er /fuer die Anzeige/ vorab ansi->ascii codiert werden: /Eine/ Variable
kann das nicht gut leisten, es sei denn wir fuehren zusaetzlich ganz genau
Buch darueber, wie sie gerade hin- oder hercodiert worden ist. Analoges
etwa beim Namen der Parameterdatei (der kann beim Weg ueber viewpara.flx
auch ganz untypisch Umlaute enthalten): Der ist in damit gebildeten .vw-
Dateien (ViewListen) in der ersten Zeile hinterlegt, Codierung dieser
Dateien ist aber gemeinhin der "DOS"-Zeichensatz der eigentlichen Daten).
Da Anwendervariablen in Flexen potentiell mit denen aus Parameterdateien
interferieren (auch Anzeige- oder Indexparameterdatei), und 2005 bereits
die "freien Variablen" ($-Variablen) fuer a99 eingefuehrt wurden, bot es
sich an, die Flexe so zu ueberarbeiten, dass auf #u-Variable moeglichst
verzichtet wird, d.h. sie nur noch in den folgenden Situationen zu nutzen:
- Kommunikation mit Parameterdateien
- fuer Anzeigen in view.rtf (und Verwandten)
- als Uebergabeparameter beim Aufruf der Flexe, sowie fuer Ergebnisse
(sie sollten kompatibel mit frueheren Versionen bleiben, sonst koennte
man dies auch ueber die sitzungs-globalen Grossbuchstaben-$-Variablen
regeln)
Die diversen view*.flx kombinieren im uebrigen recht aehnliche Bausteine
auf oft vergleichbare Weise: Parameter werden geladen, es wird gefragt,
ob die Ergebnismenge oder die Datenbank exportiert werden sollen, es
wird dann exportiert, das Ergebnis als View aufbereitet, gefragt ob das
umzubenenne ist und ob irgendeine Form der Weiterverarbeitung erwuenscht
ist. Es ist mir gelungen, einiges davon in eine zweite Library namens
"viewutil.inc" auszulagern.
Das Resultat findet sich (Betatester bitte aufstehen) als flaches Verzeichnis
unter der Adresse < http://svn.gymel.com/acxt/produkt/viewsdir/ >
bzw. gepackt als < http://svn.gymel.com/acxt/produkt/viewsdir.zip > (auch ein
im geschilderten Sinn ueberarbeiteter param.flx ist darin enthalten). Nutzung
in Produktivsystemen ist nicht zu empfehlen: Insbesondere fuer .rtf-Dateien
haben Standard-Orte Praeferenz vor dem Arbeitsverzeichnis, d.h. damit die
Dateien garantiert wirken, muessen sie entweder alternativlos ins
Datenverzeichnis oder aber man muss darauf achten, vorhandene Dateien im
gleichen Namens im Programmverzeichnis und seinen Unterverzeichnissen
zu loeschen bzw. gezielt zu ersetzen: Da gibt es dann kein Zurueck mehr ausser
zum Backup...
Caveat 1: die "Bausteine" view1.apt und view2.apt werden unbedingt im
Programmverzeichnis erwartet.
Caveat 2: "Externe Aufrufe" benutzen a99wrap.bat/a99wrap.inc sowie cmdwrap.bat
(und ein modifiziertes? winstart.flx) aus
< http://svn.gymel.com/acxt/produkt/envirdir/ > bzw.
< http://svn.gymel.com/acxt/produkt/envirdir.zip >
(die sind recht erprobt und bei vier Dateien kann man auch ein testweises
Abwerfen im Blick behalten, finde ich)
Die von mir ueberarbeiteten Dateien koennten langfristig ein Ersatz der
view*.flx aus inst-all werden, denn sie sind (weitestgehend) kompatibel mit
den Originalen. Das bedeutet aber auch, dass sie auch keine /offensichtliche/
Verbesserung darstellen, nur dort, wo die alten Flexe eventuell scheiterten,
kommen die neuen eventuell besser klar bzw. geben (hoffentlich) fruehzeitiger
eine Fehlermeldung aus...
Sie koennen aber auch eine Diskussionsgrundlage dafuer bilden, wie die View-
funktionalitaet bzw. diese nutzende Flexe sinnvoll weiterentwickelt werden
koennten.
Ich mache damit einmal den Anfang und stelle folgende Bemerkungen / Meldungen
zur Diskussion, die mir von der Ueberarbeitung der Flexdateien in Erinnerung
geblieben sind:
1. "export Foot" scheint Schmutz zu exportieren, wenn die geladene
Exportparameterdatei keinen Fussabschnitt besitzt (sie hat allerdings
einen Kopfabschnitt)
2. der Cstring "Err" waere gerade bei "rename" und anderen Dateinahen
Operationen sehr nuetzlich zur Ausdifferenzierung eines "klappt nicht",
ich habe nicht systematisch getestet, in a99 scheint er aber
"im allgemeinen" nicht besetzt zu werden
3. der Cstring "e" repliziert genau die Zeichenkette, mittels der
die Parameterdatei aktiviert wurde. Hatte ich also gegeben
var "v-rak"
xport p
dann ist "e" hinterher "v-rak" und ich muss ueber eigenen Flex-Code
versuchen herauszufinden, wo die Datei wirklich wohnt und wie sie
heisst: v-rak. at pr oder "D"\v-rak.apr oder ...
Es waere gut, wenn "e" (gibt es analoge Faelle, etwa bei "help" und "view"?)
die tatsaechlich geladene Datei mit vollem Pfad und Extension wiedergeben
koennte (Konkreter Kontext: die view*.flx wollen durch Auslesen der
ersten Zeile der geladenen v-*-Parameterdatei einen Hinweis zur
Funktionsweise geben)
4. Die Frage nach dem "Aufbewahren" unter einem anzugebenden Namen ist
eigentlich ein klassischer "Speichern unter"-Dialog. Der sieht
normalerweise wie ein Dateiauswahldialog aus (a99 kennt ihn als fnam),
nur dass man auch bzw. gerade nicht existierende Dateien auswaehlen
kann: fnam in a99 erlaubt aber nur die Auswahl existierender Dateien,
insofern muss der Benutzer seine Eingaben ueber einen Schreibschlitz
abwickeln und kann dabei nicht komfortabel Verzeichnisse auswaehlen
und bekommt auch kein unmittelbares Feedback, dass eine Datei bereits
vorhanden ist bzw. aus anderen Gruenden der angegebene Name nicht
funktioniert: Das kann er derzeit nur im Anschluss an seine Eingabe
scheibchenweise vom Flex mitgeteilt bekommen.
Also: eine Variante von fname mit Dateiauswahldialog fuer ggfls. noch
nicht exisitierende Dateien haette viele Vorteile!
5. In RTF-Dateien eingebettete Flexe sind recht nuetzlich. Das bedeutet
aber, dass ich solche RTF-Dateien nicht mehr aus einem Flex heraus
aufrufen kann, denn dann werden die Einbettungen ja nicht ausgefuehrt,
nur die simplen Referenzen auf #u-Variable werden ausgefuehrt.
Ich konnte das im konkreten Fall so loesen, dass ich vorzugsweise
anstelle
h view
ein
exec x help view
am Ende von Flexen genutzt habe. Soll aber view.rtf bereits waehrend
des Ablaufs aktualisiert angezeigt werden, gibt es zusaetzlich ein
Unterprogramm in viewutil.inc, das die in view.rtf eingebetteten
Flexe repliziert: Hier muss also Funktionatlitaet umstaendlich (und
fehlertraechtig) gedoppelt werden, damit etwas scheinbar mueheloses
funktioniert (vgl. auch 3. oben, wo etwas aehnliches erforderlich ist)
6. Flex-Unterprogramme koennen keine anderen Flex-Unterprogramme aufrufen.
Die Baustein-Funktionen in viewutil.inc rufen aber die Namensmanipulations-
Funktionen in fnutil.inc haeufig auf. Ich konnte das so loesen, indem
ich die Funktionen in fntutil.inc so programmiert habe, dass sie wahlweise
mit "return" enden oder aber mit Ruecksprung auf ein in einer verabredeten
Variablen hinterlegtes Label. Dementsprechend muessen die Funktionen in
viewutils.inc die Namensfunktionen auf fnutil.flx nicht per "perform"
aufrufen, sondern durch "jump" mit aufwendiger Vor- und Nachbearbeitung:
$fnutil_psr=_view_prepcreate_setextension
var $viewutil_tmp "|.vw"
jump fnutil_setextension
:_view_prepcreate_setextension
$fnutil_psr=
var $fnutil_result
realisiert hierbei ein eigentlich ganz triviales
var $viewutil_tmp "|.vw"
perform fnutil_setextension
Eigentlich sind die Bausteine in viewutil.inc fuer praktische Zwecke zu
kleinschrittig geraten und ich wuerde sie gerne in hoeher integrierten
Routinen zusammenfassen, z.B. damit es sich lohnt, die Fehlerbehandlung
etwas ausfuehrlicher zu gestalten:
...
close x
// close view; .vw und .vwn vorab loeschen
var $rawvvv
perform view_prepcreate
// Sortierbefehl zusammenstellen und ausfuehren (dabei Sortierkopf #u1 beseitigen)
// Gruppierung gewuenscht (wir kennen v-stand2.cpr)!
var $rawvvv "|" "j"
perform view_createvwfromraw
if "" mess "Something went wrong!";end
ins $vwcreated
Fertig sortierte ViewListe anzeigen
set v1
View
if no var "ViewListe " $vwcreated " existiert nicht!";mess;end
// .vwn ggfls. in .vw zurueckbenennen
var $vwcreated
perform view_postviewconsolidate
...
Das waere dann aber ein dreistufiger Aufruf, der ein uebles Aufblaehen
der bereits aufgeblaehten Routinen der zweiten Stufe in viewutil.inc
erfordern wuerde: Davor bin ich dann doch zurueckgeschreckt.
Fazit: Vorhandene Flexe sind durchaus instruktiv und gut geeignet als
Anschauungsmaterial. Das steht aber in der Regel in Konflikt mit dem
Ziel, "gut ausgearbeitete" Flex zu haben, die in moeglichst vielen
Sitationen funktionieren bzw. auch abfangen, wenn etwas schief geht,
und dann den Benutzer auf geeignete Massnahmen leiten: M.E. geht das
nur ueber echte Unterprogramme, die dann auch geschachtelt werden
muessen, denn sonst wird es sofort uneinheitlich und schlechter lesbar
als noetig wird es ohnehin (ueberall ist der stets gleiche Code
einzufuegen).
Speziell zu den View-Flexen:
* Die "Standard-Views" v-stand1 und v-stand2 sind bei Licht gesehen
nur merkwuerdige Demos (warum sonst sollte die Seitenzaehl auf
zwei Nachkommastellen aufsummiert werden, ausser man will kaschieren,
dass die "Gruppierung/Summierung" fuer die Zwischensummen zwei
festverdrahtete Stellen nutzt?). Die Frage waere, ob fuer den
dadurch geleisteten Mix aus Sortierung/Gruppierung und Praesentation
einer Kurzliste nicht einleuchtendere Beispiele gefunden werden
koennten (oder Soertierung und View-Gestaltung trennbar zu sein
haetten)
* Weiterverarbeitung gibt es als Variante auch "tabellarisch": Dafuer
gibt es ohnehin die spaeter entstandenen tab*-Flexe. In den v-*.apr
gibt es aber traditionell noch Abschnitte, die Tabellen erzeugen.
Die sollten aber vermutlich nicht allegro-Windows-codiert sein, sondern
im Windows-Zeichensatz (/in/ a99 gibt es ja gar keine Moeglichkeit,
Tabellen einzulesen) und die Auswahl der produzierten Spalten ist
eigentlich voellig unabhaengig von der durch dieselbe Parameterdatei
produzierten Sortierung und Kurzaufbereitung. Hier ist die Frage,
ob diese Doppelfunktion der v-*-Parameterdateien ueberhaupt nuetzlich
ist oder ein allgemeinerer Mechanismus benoetigt wird (mein Paket
enthaelt eine i-vw.apr, die aus der View-Zeile versucht, #u1 und
#u2 so zu rekonstruieren, dass sich der Export in eine "regulaere"
Listenproduktion mittels p-*.apr einfaedeln laesst: Klarerweise
keine Loesung fuer tabellarische Exporte)
[Teilaspekt: die view-Flexe setzen dann vorher set c1, damit sind
die Ausgangsdaten aus der Datenbank bereits allegro-Windows-codiert
und die Parameterdatei hat nichts zu tun. Sobald da aber komplexere
Tests passieren, bricht alles zusammen, denn niemand behaelt beim
Parametrieren den Ueberblick darueber, dass in derselben Parameterdatei
gewisse Abschnitte Originaldaten sehen und andere Abschnitte
umcodierte (Parameterdateien fuer nicht-allegro-Daten versuchen wir
ja normalerweise zu vermeiden, ausser beim Import)]
* Dass seinerzeit das Unterverzeichnis "views/" des Programmverzeichnisses
als Aufbewahrungsort fuer ViewListen eingefuehrt wurde, war
mit damals wohl entgangen. Genutzt wurde es wohl nie, inzwischen
ist es auch eher Konsens, dass Programmverzeichnis und seine
Unterverzeichnisse nicht fuer "generierten Content" zu nutzen
sind, ausser evtl. der Systemverwalter legt dort etwas hinein
(dabei duerften ihm a99-basierte Mechanismen natuerlich helfen)
viele Gruesse
Thomas Berger
Mehr Informationen über die Mailingliste Allegro