Ich möchte eher eine Variante, die in der Lage ist alle Offsets zu beschreiben, unabhängig davon, welches Offset konkret gemeint ist.
Tut mir Leid, aber an mindestens einer Stelle in deinem Programmcode wirst du nicht drumrum kommen, weil die Schnittstelle (der Stecker) genau das verlangt. Oder wolltest du darauf hinaus, dass du die Offsets nicht so "hart" kodiert in deinem Programm haben möchtest, wie ich es oben vorschlug?
Die Schalter sollen dann am Ende folgende Funktionen haben:
Addieren (auch negatives "addieren") eines Wertes zum aktuellen Wert im Offset.
Setzen eines Wertes im Offset.
Setzen einzelner Bits in bestimmten Offsets.
Dann würde ich vorschlagen, dass du dir solche Schnittstellen schaffst. Du implementierst also nicht nur getInt(), setInt(), getByte(), setByte() usw., sondern auch z.B. setBit() und getBit(). Auf jeden Fall sollte die unterste Schicht nach Möglichkeit durch eine einzige neue Klasse abgedeckt werden, die dann auch die einzige(!) ist, die direkt mit dem vorgegebenen Wrapper kommuniziert. Alle anderen Klassen (die höheren Schichten) kommunizieren über diese neue Klasse.
Eine höhere Schicht könnte dann eben wie von dir vorgeschlagen eine einzige Methode sein, die je nach Fall dann die entsprechende Methode (getInt(), setInt() usw.) der unteren Schicht aufruft. Auf alle Fälle solltest du aber zunächst einmal die Klasse für diese untere (wenn nicht sogar unterste) Schicht bauen. Diese Klasse gibt es sogar schon, dass ist nämlich die
hier erwähnte Klasse FSUIPC. Wie die höheren Schichten aussehen, die darauf zurückgreifen, ist erst einmal vollkommen egal. Das erste Ziel sollte sein, eine Buchse zu schaffen, mit der du die Bytes, wie sie der Wrapper braucht, in primitive Datentypen verwandeln kannst, mit denen Java umgehen kann. (Ich würde auch Strings dazuzählen. Es sind zwar keine primitiven Datentypen, da Strings aber immutable sind, funktionieren sie wie solche.)
Um das ganze flexibel zu halten, habe ich mir nun folgendes überlegt:
Jede Schreibfunktion soll die gleiche Methode benutzen. Der Paramater der Byteanzahl wird ja aus dem Offset übergeben. Diese Angabe fordert der Wrapper ja.
Dann, so habe ich weiter überlegt, benutze ich einfach immer 8 Byte große Arrays.
Dadurch, dass die auszulesende Bytezahl ja mitgeben wird, müsste er dadurch hoffentlich die zu verwendenden Bytes finden. Das muss ich allerdings nochmal testen.
Eigentlich sollte das Testen gar nicht nötig sein. Stattdessen sollte es (wie schon so oft gesagt) eine Spezifikation geben, in der haarklein erklärt ist, wie die Schnittstelle funktioniert. Und an diese Spezifikation solltest du dich unbedingt halten, unabhängig davon, ob du irgendwelche Hacks findest, mit denen man alles schneller machen kann etc. Es ist nämlich durchaus legitim, dass die Implementierung des Wrappers geändert wird, so dass deine Hacks nicht mehr funktionieren, die alte Spezifikation aber immer noch gültig ist.
Niemals irgendetwas benutzen, was nicht spezifiziert (bzw. dokumentiert) ist, ganz egal, wie verlockend es auch sein mag!
Würde ich alle Funktionen sinnvoll in Klassen zusammen fassen, hätte man hinterher riesige Wälzer an Text um das ganze zu Dokumentieren und würde am Ende sicher 85% der programmierten Funktionen nicht nutzen. Ich denke so ist das einfach flexibler.
Du bist ja nicht gezwungen, sofort alle Offsets zu speichern und auch auszunutzen. Du kannst ja Klassen, wie ich sie vorschlug (z.B. Weather), stückchenweise gerade an den Stellen wachsen lassen, wo es nötig ist. Meine Idee war halt, möglichst ohne große Umschweife von den Bytes zu Objekten zu kommen, wie es sich für eine OO-Sprache nun einmal gehört. Aber wenn du meinst, das ist nicht nötig und du kennst da einen für deine Anforderungen geeigneteren Weg, möchte ich dich nicht aufhalten.
Zuerst muss ich aber nun herausfinden, wie das Übertragen genau funktioniert
Ganz recht, und das sollte, wie gesagt, spezifiziert sein.
Wenn der ByteBuffer die ByteOrder Little Endian zugesprochen bekommt, bedeutet das dann, dass die Daten so ankommen? Oder dass sie eben genau nicht so ankommen und der ByteBuffer sie nach Erhalt erst neu sortiert?
Tja, das ist eine gute Frage. Da man sie überhaupt angeben kann, nehme ich mal stark an, dass die Umwandlung on the fly arbeitet und die Bytereihenfolge, die man da festlegt, gerade die Reihenfolge ist, mit der die Daten dann tatsächlich in diesem Puffer stehen. Ein weiterer Hinweis darauf ist der Umstand, dass man sich mit ByteOrder.nativeOrder() die Bytereihenfolge der Maschine holen kann.
Kann es übrigens sein, dass diese Offsets voneinander abhängig sind? Also wenn ich z.B. "ABC" in 0x1234 schreibe, schreibe ich dann in Wirklichkeit ein "A" in 0x1234, ein "B" in 0x1235 und ein "C" in 0x1236? Wenn dem so ist, sollte die Buchse auch kontrollieren, dass Strings nicht zu lang sind usw.
Noch mal kurz:
Versuche, in Schichten zu denken. Nimm dir das
OSI-Schichtenmodell als Vorbild. Darauf basiert nämlich das ganze Internet, und wie du siehst, funktioniert es ganz offensichtlich. Auch Linux orientiert sich stark an einem schichtartig aufgebauten System, und gerade diesem Umstand ist es zu verdanken, dass Linux inhärent sicher ist. Ein Negativbeispiel wäre Windows, denn da verschwimmen diese Schichten, sodass ein ActiveX-Irgendetwas quasi Rechte am System haben kann etc.
Zur Orientierung in deinem Fall:
- Schicht 1 sei der Wrapper. Hier werden nur noch Bytes an bestimmte Offsets transportiert.
- Schicht 2 sorge dafür, dass beliebige Java-Datentypen (byte, short, int long, String etc.) an beliebige Offsets geschrieben werden können. An dieser Stelle spielt noch keine Rolle, ob für den String genug Platzt ist oder ob der Datentyp mit seiner Länge überhaupt zum konkreten Offset passt.
- Schicht 3 stellt sicher, dass die richtigen Offsets mit den richtigen Daten gefüttert werden, also dass auf Offset 0x1234 nur Strings bis zur Länge 10 genutzt werden können, dass auf Offset 0x0303 nur vorzeichenbehaftete ints benutzt werden usw. An dieser Stelle könnte man z.B. eine Methode basteln, die grundsätzlich einen Offset und einen String als Eingabe bekommt und dann z.B. automatisch den String in ein int umwandelt, wenn festgestellt wird, dass am gegebenen Offset eben ein int erwartet wird.
- Schicht 4 kann aus einem Zeichenstrom (Reader) einzelne interessante Strings isolieren und diese der gerade beschriebenen Offset-String-Methode der Schicht 3 zuführen.
- Schicht 5 sorgt dafür, dass aus den Zeilen und Spalten eines Excel-Dokuments ein Zeichenstrom (Reader) gemacht wird, den Schicht 4 benutzen kann usw.
In diesem ganzen System greift die höhere Schicht (Idealfall: pro Schicht eine Klasse!) zur Erfüllung seiner Aufgaben nur gerade auf die Möglichkeiten der nächstniedrigeren Schicht zu. Also Schicht 3 ruft nur Methoden der Schicht 2 auf, diese wiederum nur Methoden der Schicht 1 usw.
Ark