[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