Thema der Woche: Reguläre Ausdrücke

Reguläre Ausdrücke vereinfachen die Abfragen an Strings radikal. Lese zunächst zur Einleitung

Anschließend sollte man die die API-Doku lesen:

In einer Code-Suchmaschine kann man nun einige Beispiele für Pattern-Nutzung ablesen:

Mehr Beispiele (allerdings auch mit Perl-RegEx, die Java nicht unterstützt) gibt die Seite

Hier sollte man sich ein paar Beispiele anschauen.

Zum Schluss eine Übung: Schreibe einen RegEx-Ersetzer, der aus einem Satz mit der „Basic text formatting“ Regel der Wiki-Syntax (http://wiki.splitbrain.org/wiki:syntax) HTML erzeugt. Also soll aus

**bold**

folgendes werden:

<b>bold</b>

Thema der Woche: Java 5 und Java 6

Java 5 ist mittlerweile weit verbreitet, und Entwickler sollten auf der einen Seite die neuen Spracheigenschaften kennenlernen, auf der anderen Seite sich mit dem Update der Bibliotheken beschäftigen. Als Startlinks gelten:

Von jeder neue Klasse und Schnittstelle sollte man zumindest den API-Kopf gelesen haben. Besonderes Augenmerk sollten Entwickler auf Generics legen. Hier gilt hervorzuheben das Tutorial http://java.sun.com/j2se/1.5/pdf/generics-tutorial.pdf. Die Begriffe

  • Generischer Typ
  • Typparameter
  • formalter Typparameter
  • parametrisierter Typ
  • Typargument
  • Wildcard-Typ
  • Bounded Wildcard
  • upper bound, lower bound
  • generische Methode
  • unchecked Warnung
  • erasure
  • Wildcard capture
  • multiple bound

solle man sofort einordnen und beschreiben können.

Jeder Entwickler sollte insbesondere alle Anwendungen von Generics bei der Utility-Klasse Collections verstehen:

PS: Beginne bei <T>/<E> bzw. <K,V>, dann <?> und dann den restlichen. max/min sind dann die Generics-„Bonbons“.

Wer danach die Nase von Generics immer noch nicht voll hat, beginnt http://www.angelikalanger.com/GenericsFAQ/JavaGenericsFAQ.html zu lesen. Das ist die umfangreichste Quelle zu dem Thema im Netz.

Neue Rubrik "Die wöchentliche Dosis Java"

Nach mehr als 10 Jahren in der Java-Weiterbildung wiederholen sich doch die Fragen. Die Klassiker: „Wie haben Sie Java gelernt?“, „Wie bilden Sie sich weiter?“, „Was für Java-Bibliotheken nutzen Sie?“, „Wie verbreitet ist eigentlich XZY?“ Aus diesen Anfragen heraus möchte ich eine neue Rubrik „Die wöchentliche Dosis Java“ vorstellen. Die Idee ist, die kontinuierliche Weiterbildung von Software-Entwicklern zu fördern. In der (hoffentlich) wöchentlichen Rubrik stelle ich Technologien, Bibliotheken, Tools, Sprachupdates, … vor, in der sich Programmierer in einer Woche beschäftigen können und somit ein neues Gebiet kennenlernen. Der Bereich umfasst dabei die gesamte Java-Landschaft. Mal werden es Sprach-Erweiterungen sein, mal XML-Technologien, dann wieder etwas von Swing und Gui-Frameworks, dann REST, Web-Toolkits oder Open-Source Bibliotheken.

Java-API für Google-Calendar/Google Documents

Mit der Google Java API (http://code.google.com/apis/gdata/javadoc/) lässt sich leicht auf die Dokumente oder Kalender zurückgreifen. Ein erstes Beispiel für eine Liste der Google-Calender ist schnell formuliert:

import java.net.URL;
import com.google.gdata.client.calendar.CalendarService;
import com.google.gdata.client.docs.DocsService;
import com.google.gdata.data.calendar.CalendarEntry;
import com.google.gdata.data.calendar.CalendarFeed;
import com.google.gdata.data.docs.DocumentListEntry;
import com.google.gdata.data.docs.DocumentListFeed;

public class GoogleExample
{
  public static void main( String[] args ) throws Exception
  {
    String user = „user@gmail.com“, pass = „pass“;

    CalendarService calService = new CalendarService( „Mein Kalender“ );
    calService.setUserCredentials( user, pass );

    CalendarFeed resultFeed = calService.getFeed(
                                new URL( „http://www.google.com/calendar/feeds/default/allcalendars/full“ ),
                                CalendarFeed.class );

    for ( CalendarEntry entry : resultFeed.getEntries() )
      System.out.println( entry.getTitle().getPlainText() );

    DocsService service = new DocsService( „Meine Dokumente“ );
    service.setUserCredentials( user, pass );

   DocumentListFeed feed = service.getFeed(
                              new URL( „http://docs.google.com/feeds/documents/private/full“ ),
                              DocumentListFeed.class );

    for ( DocumentListEntry doc : feed.getEntries() )
      System.out.println( doc.getId() + „/“ + doc.getTitle().getPlainText() );
  }
}

Die notwendigen Java-Archive nimmt man aus dem lib-Verzeichnis des Archivs http://gdata-java-client.googlecode.com/files/gdata.java-1.15.2.zip. Um auf die Dokumente zurückzugreifen (oder auch nur aufzulisten), ist mail.jar ebenfalls nötig.

Code style: Remove unnecessary statements and keywords

Bad

Good

import java.lang.*;

import java.util.*;

import java.util.Scanner;

import java.util.*;

class MyClass extends Object { }

class MyClass { }

class MyClass {

int i = 0;

boolean b = false;

char c = ‚\u0000‘;

String s = null;

double d = 0.0;

}

class MyClass {

int i;

boolean b;

char c;

String s;

double d;

}

class MyClass {

MyClass() { super(); }

}

class MyClass {

MyClass() { }

}

interface MyInterface {

public abstract void foo();

}

interface MyInterface {

void foo();

}

void lollipop() {

return;

}

void lollipop() {

}

java.lang.String s;

String s;

Object[] param = new Object[ 1 ];

param[ 0 ] = i;

Object[] param = { i };

Something missing?

Buchkritik: Beautiful Code: Leading Programmers Explain How They Think

Beautiful Code: Leading Programmers Explain How They Think
Andy Oram, Greg Wilson. O’Reilly. 2007. 618 Seiten
Bekannten Autoren und Software-Architekten geben in 33 Kapiteln viele Beispiele ihrer Kunst, und Einblicke in ihr Schaffen. Doch sehr spezielle Beispiele in diversen Programmiersprachen machen es für Java-Programmierer schwer, hier „schönen Code“ zu entdecken. Unter dem Strich bleibt nicht viel für Java-Entwickler über, wenn Programmiersprachen wie C, Ruby, LISP ihren Platz finden und Algorithmen vorgestellt werden, die für die allermeisten kaum relevant sind. Interessant ist es allemal, und wer sich viel Zeit für die Einarbeitung nimmt, wird auch sicherlich viel entdecken, zumal das Design einen größeren Stellenwert einnimmt, als der Quellcode an sich. Bei den wenigen Java-Programmen gibt leider etwas zu bemängeln. Einmal ein einfacher Fehler auf Seite Seite 478, in dem die Klammerung falsch ist:

class Sample {}
public static void main(String[] argv) {
System.out.println("Hello World");
}

Der zweite Hammer haut aber stärker rein und ist definitiv kein Beispiel für „Beautiful Code“. Seite 332 stellt eine EJB Session-Bean FileReaderBean vor, die über eine FileInputStream Daten vom Dateisystem holt – das ist nun wirklich nicht in Ordnung. Toll finde ich, dass die Erlöse an Amnesty International gehen: „All royalties from this book will be donated to Amnesty International.”. Das nette Kapitel Code in Motion ist online verfügbar.

JNotify: Was tut sich im Dateisystem?

Die kleine (native) unter Linux und Windows arbeitende Bibliothek JNotify meldet Änderungen an Dateien. Ein Beispiel:

String path = "c:/";

int mask = JNotify.FILE_CREATED | JNotify.FILE_DELETED | JNotify.FILE_MODIFIED | JNotify.FILE_RENAMED;


boolean watchSubtree = true;


int watchID = JNotify.addWatch(path, mask, watchSubtree, new JNotifyListener()
{
@Override
public void fileCreated( int arg0, String arg1, String arg2 )
{
System.out.println( "created" );
}


@Override
public void fileDeleted( int arg0, String arg1, String arg2 )
{
System.out.println( "deleted" );
}

@Override
public void fileModified( int arg0, String arg1, String arg2 )
{
System.out.println( "modified" );
}

@Override
public void fileRenamed( int arg0, String arg1, String arg2, String arg3 )
{
System.out.println( "renamed" );
}
} );

Und am Ende:

JNotify.removeWatch(watchID);

jodconverter für OpenOffice Dateiformatkonvertierungen

Der zweite Schritt meiner Rechnungserstellung ist die Konvertierung in PDF. Auch dazu gibt es eine prima Bibliothek: http://www.artofsolving.com/opensource/jodconverter. (Einziger Nachteil: viele Jars.) Damit kann man Word, PowerPoint, RTF und alles andere in ein bliebiges Zielformat bringen.

private static void ensureStartedOpenOfficeService()
{
try
{
new Socket( "127.0.0.1", 8100 );
}
catch ( Exception e )
{
String path = "C:/Programme/OpenOffice.org 2.3/program/";
ProcessBuilder processBuilder = new ProcessBuilder( path+"soffice", "-headless", "-accept=\"socket,host=127.0.0.1,port=8100;urp;\"", "-nofirststartwizard" );
try
{
processBuilder.start();
}
catch ( IOException ioe )
{
throw new RuntimeException( ioe );
}
}
}
public static void convert( String source, String destination )
{
ensureStartedOpenOfficeService();

OpenOfficeConnection connection = null;
try
{
connection = new SocketOpenOfficeConnection( 8100 );
connection.connect();
DocumentConverter converter = new OpenOfficeDocumentConverter( connection );
File inputFile = new File( source );
File outputFile = new File( destination );
converter.convert( inputFile, outputFile );
}
catch ( ConnectException e )
{
throw new RuntimeException( e );
}
finally
{
connection.disconnect();
}
}

Für meine Rechnungen also:

String destination    = "S:/Private/Traida/Bills/bill1";
String destinationOds = destination + ".ods";
String destinationPdf = destination + ".pdf";
convert( destinationOds, destinationPdf );

OpenOffice als Template-Engine: Dokument einlesen, verändern, schreiben

Da ich meine Rechnungen automatisch generiert und als PDF konvertiert haben möchte, habe nach einer Lösung gesucht, wie ich die Rechnungsdaten aus einer Datenquelle in der Template einsetzten kann, und am Ende eine PDF bekomme. Klar sind Report-Generatoren dafür auf der Welt, aber meine Vorlagen möchte ich nicht für Eclipse BIRT oder Jasper schreiben, sondern in Word bzw. OpenOffice. RTF ist relativ leicht zu schreiben und für ein Template meines Erachtens ganz gut. Meine Gedanken kreisten daher einige Zeit um RTF->PDF, doch da gibt es keine freie Lösung. Auch Wege wie RTF->FO->PDF sind möglich, aber dafür gibt es ebenfalls keine leichtgewichtigen freien Lösungen.

Schon für meinen PowerPoint->PDF-Konverter habe ich mit OpenOffice gearbeitet und das klappte ganz gut. Das habe ich für meine Templates nun wieder überlegt — ein recht harter Weg für simple RTFs zwar, aber es funktioniert. Doch anstatt RTF zu nutzen, wollte ich gleich das XML-Format verwenden. Dazu muss man wissen, dass OO ein Zip-Archiv für das OO-Dokument vorsieht und dort in einer XML-Datei den Content ablegt. Mit der großartigen Open-Source-Bibliothek https://truezip.dev.java.net/ ist der Zugriff auf Archive sehr einfach.

Mit TrueZIP ist eine einfache Lösung entstanden, ein OO-Dokument mit Template-Anweisungen wie ${address} zu lesen, Ersetzungen vorzunehmen, und alles wieder zu schreiben. Diesen Text hier zu schreiben hat länger gedauert, als die 90 Zeilen Quellcode. Also los:


import java.io.*;
import java.nio.channels.FileChannel;
import de.schlichtherle.io.ArchiveDetector;

public class OpenOfficeUtils
{
public static void main( String[] args )
{
String source = "S:/Private/Traida/Bills/template.ods";
String destination = "S:/Private/Traida/Bills/bill1.ods";

copyFile( source, destination );

String content = readOpenOfficeContent( source );

content = content.replace( "${addressline1}", "Christian Ullenboom" );

writeOpenOfficeContent( destination, content );
}

public static String readOpenOfficeContent( String filename )
{
Reader is = null;

try
{
de.schlichtherle.io.File file = new de.schlichtherle.io.File( filename + "/content.xml", ArchiveDetector.ALL );
char[] fileContent = new char[ (int) file.length() ];
is = new de.schlichtherle.io.FileReader( file ); // TODO: <?xml version="1.0" encoding="UTF-8"?>
is.read( fileContent );

return new String(fileContent);
}
catch ( IOException e )
{
throw new IllegalArgumentException( e );
}
finally
{
try { is.close(); } catch ( Exception e ) { }
}
}

public static void writeOpenOfficeContent( String filename, String content )
{
Writer os = null;

try
{
de.schlichtherle.io.File file = new de.schlichtherle.io.File( filename + "/content.xml", ArchiveDetector.ALL );
os = new de.schlichtherle.io.FileWriter( file );
os.write( content );
}
catch ( IOException e )
{
throw new IllegalArgumentException( e );
}
finally
{
try { os.close(); } catch ( Exception e ) { }
}
}

public static void copyFile( String in, String out )
{
FileChannel inChannel = null;
FileChannel outChannel = null;

try
{
inChannel = new FileInputStream( new File(in) ).getChannel();
outChannel = new FileOutputStream( new File(out) ).getChannel();
inChannel.transferTo( 0, inChannel.size(), outChannel );
}
catch ( IOException e )
{
throw new IllegalArgumentException( e );
}
finally
{
try { inChannel.close(); } catch ( Exception e ) { }
try { outChannel.close(); } catch ( Exception e ) { }
}
}
}

Strg+F11: Noch so eine komische Eclipse-Änderung

Vor nicht allzulanger Zeit startete Eclipse mit Strg+F11 das letzte Programm. Dann änderte man dies und Eclipse versuchte automatisch das im Editor aktive Programm zu starten. Ich denke, dass diese Umstellung eher lästig ist und eines der ersten Sachen sein dürfte, die man abschaltet. Das geht so: In den Preferences unter Run/Debug > Launching den Schalter bei Always launch the … einstellen.

image

1 Stunde Suchen für eine doofe Eclipse-Einstellung

<Sarkasmus>Dankenswerterweise</Sarkasmus> hat Eclipse eine neue Einstellung bekommen, in dem gewissen Daten vom Kopieren in den Output-Folder ausgenommen werden können. Dass sich diese Einstellung unter Java > Compiler > Building befindet, fand ich erst nach langem Suchen hier: http://www.codecommit.com/blog/eclipse/wtps-crazy-and-undocumented-setting-change

wtp-screwup

Mein Problem: Bei der Entwicklung mit Wicket stehen die HTML-Seiten Seite an Seite mit den Java-Klassen. In den classes-Folder für die Web-Anwendung gingen aber nur die Klassen und nicht die HTML-Dateien. Da gestern auf einer anderen Eclipse-Version alles noch lief, war ich heute lange verzweifelt und kann glücklicherweise nach dieser Einstellung wieder an die Arbeit gehen.

SQL für Objekte: JoSQL (SQL for Java Objects)

JoSQL (SQL for Java Objects) unter http://josql.sourceforge.net/index.html ist eine Open-Source Biblitohek, um SQL-Anfragen an Objektgrafen zu stellen. Die Webseite gibt interessante Beispiele.

String query = "SELECT * FROM   java.io.File WHERE  name LIKE '%.mp3'";
Query q = new Query();
q.parse( query );
QueryResults results = q.execute( Arrays.asList( new File("C:/Data/Musik/").listFiles() ) );
System.out.println( results.getResults() );

Oder für Ausdrücke:

/*
* Use as a file filter.
*/
String exp = "lastModified BETWEEN toDate('10/May/2007') AND toDate('28/Jun/2007') " +
"AND " +
"length >= 10 * 1024" +
"AND " +
"path LIKE '%/subdir/%'";

ExpressionEvaluator ee = new ExpressionEvaluator (exp, File.class);

if (ee.isTrue (myfile))
{
// Process the file.
}

Buchkritik: Das Java Codebook

Marcus Wiederstein, Marco Skulschus, Mark Donnermeyer, Dirk Brodersen, Benjamin Rusch. Addison-Wesley. ISBN 3-8273-2059-3. 2003. 858 Seiten
Programmabschnitte, sog. Snippets, gibt es im Internet genug (wir wäre es alternativ mit dem Java-Almanac?) und wer eine Suchmaschine bedienen kann, wird immer, ja immer, die Bibliothek oder den Quellcode-Schnippsel finden, die er sucht. Um so erstaunlicher finde ich es, wenn Verlage Quellcode-Kochbücher veröffentlichen, ob sie nun Codebook oder Cookbook heißen. Ernsthaft: Wer eine Klasse zur Bruchrechnung sucht oder ein Beispielprogramm zum Aufbau eines Swing-Fensters, geht sicherlich nicht zum Regal, sucht im Index nach „Bruch“ und holt sich dann die Klasse von der CD. Daher die Frage – ist ein Buch wie „Das Java Codebook“ notwendig? Das Positive zuerst: Die Kapitel über reguläre Ausdrücke und Applets (Daten im Cookie speichern, Uploads, Applet-Konverter für Tags), die kleinen Beispielprogramme, in denen man etwa über ein BufferedImageOp.filter Bilder heller/dunkler macht, sowie Hinweise zur Transparenz und der Index finde ich prima. Eigentlich sind Bücher mit Quellcode gut, denn Software-Entwickler lesen im Allgemeinen zu wenig. Das Studium von Quellcode hilft jedem Entwickler, neue Idiome zu lernen und seine Fähigkeiten zu verbessern. Je nach Schwerpunkt des Buches lernt man weitere Möglichkeiten der Standardbibliothek kennen oder neue Open-Source-Bibliotheken. Soweit die Möglichkeiten guter Codebooks. Leider hat das Java Codebook dann doch die eine oder andere Schwachstelle, so dass man Software-Entwicklern vielleicht doch besser direkt zur Google-Suche raten sollte. (Immerhin gibt es das Buch als PDF.) Die Probleme im Detail: Die Klasse

public class CalcExample {
static final BigDecimal ZERO = new BigDecimal(0);
static final BigDecimal ONE = new BigDecimal(1);
static final BigDecimal FOUR = new BigDecimal(4);

deklariert drei Variablen (Sichtbarkeit könnte auch anders sein), doch vergisst, dass es ZERO, ONE, TEN schon in BigDecimal gibt. In anderen Beispielen gefällt mir die Schreibweise der Felder nicht. Einiges ist nach der Bauart String []array, also Typ, Leerzeichen, Variablenname aufgebaut. Das habe ich nach 11 Jahren Java hier zum ersten Mal gelesen. Zumal sich die Autoren hier uneins zu sein scheinen, denn sie verwenden auch String[]. Die fünf Autoren des Buchs folgen demnach keiner einheitlichen Coding-Konvention. Ein weiteres Thema betrifft die Methodennamen: Überwiegend werden englische Bezeichner (setNumber, less100, …) gebraucht, doch lassen sich innerhalb eines Beispiels Vermischungen mit deutschen Variablennamen entdecken, wie das Feld ZEHN. Einige Autoren mögen (immer noch) die Klassen Hashtable und Vector, andere nutzen doch besser List und Map – hat denn keiner der Autoren die anderen Kapitel gelesen und kommentiert? Bei den Datenstrukturen frage ich mich wirklich, was denn arrayList.add(new String(new java.util.Date().toString())); für eine Anweisung sein soll. Und warum wird ein Stack noch mal neu implementiert? OK, der Original-Stack erbt von Vector, was vom Design total verpfuscht wurde, aber dann könnte man doch immer noch den neuen Stack so implementieren, das er an den alten Stack delegiert, anstatt alle Operationen komplett mit einer ArrayList neu zu implementieren. (Und dann noch die fragwürdige Methode search() zu übernehmen.) Eine weitere Schwachstelle, die ich in der Insel weitestgehend versuche zu vermieden, sind Windows-spezifische Pfadangaben, wie etwa bei new FileOutputStream(„c:\\buecher.ser“). Bei IO-Beispielen fehlt oft das close() im finally. Vor allem bei HTTP-Servern ist das kritisch. Im JSP-Kapitel findet sich plötzlich ein JDBC-Close im finally; also hat es nicht jeder der Autoren vergessen. Frage: Was gibt dieses Programm bei Argumenten auf der Kommandozeile aus?

package javacodebook.io.stdout;
public class StdOut {
public static void main(String[] args) {
String text = "Hallo Welt";
//Wenn Text angegeben, dann diesen ausgeben
if(args.length < 1)
for(int i = 0; i < args.length; i++)
System.out.println(args[i]);
else
System.out.println(text);
}
}

Dass ist wohl eher eine Zertifizierungsfrage statt eines sinnvollen Codebeispiels, denn ist die Anzahl der Argumente kleiner 1, gibt es keine Argumente und die Schleife ist nutzlos. Genauso falsch ist der Satz „JDBC benutzt dabei die Interfaces Connection, Driver und DriverManager.“ Schade, denn DriverManager ist eine Klasse. Sollte es auch, denn aus dieser Zentrale erhält man durch die statische Methode getConnection() eine Verbindung. Im Anhang finden sich dann noch ein paar Dreher in der Groß-/Kleinschreibung. Z. B. beginnen einige Bezeichner in der Tabelle „Java Native Interface Typen“ mit Großbuchstaben. Besser wären „int“ statt „Int“ sowie „void“ statt „Void“ usw. Ähnliches gilt für JscrollPane, was zu JScrollPane werden sollte. Unter „Relationale Operatoren“ findet sich folgender Satz bei ==-Operator: „a == b ergibt true, wenn a gleich b ist. Handelt es sich bei a und b um Referenztypen, so ist der Rückgabewert dann true, wenn beide auf dasselbe Objekt zeigen.“ Wir sollten den Sonderfall a = b = NaN aber nicht Außeracht lassen, denn gerade dann ist a = b, aber das Ergebnis nicht true. In der Tabelle „Klassenobjekte für den primitiven Datentyp“ sollte Void besser nicht auftauchen, da es sich nicht um einen primitiven Datentyp handelt. Oder Void stehen lassen, aber die Überschrift anpassen. Einige Kritikpunkte betreffen die Auswahl der Klassen. Die Klasse Fraction sollte vielleicht besser einer bekannten Open-Source-Bibliothek entstammen und die Matrizen sowie der umfangreiche Quellcode nicht unbedingt abgebildet werden. Für CSV-Dateien wären populäre Standard-Klassen sinnvoller. Der Tipp 186 für Timer sollte diesen nicht selbst implementieren, sondern die zwei (!) Timer-Klassen der Java-Lib vorstellen. Da ein Codebook grundsätzlich eine unendliche Sache ist, könnten weitere Snippets eingebracht werden. Bei den Zufallszahlen wäre z. B. SecureRandom gut untergebracht. Das Beispiel FindInFile, was händisch nach einem String in einer Datei sucht, lässt sich viel effektiver mit NIO (mit Standard-Beispiel von Sun) lösen. Das NIO-Kapitel kommt mit nur zwei Beispielen daher (Datei kopieren und File-Lock). Das ist eine magere Ausbeute. Später kommt noch ein NIO-Server dazu. Der Verweis auf eine einfache NIO-Bibliothek (wie xSocket) wäre dann wünschenswert. Um ein Fenster mittig zu positionieren, muss man nicht rechnen, hier reicht eine Java-Funktion. Wenn es sich schon um ein Java 1.4 Buch handelt, dann sollte ImageIO Grafiken laden. Die Swing-Beispiele sind relativ unspektakulär. Hier wäre es wohl sinnvoller statt „wie erzeuge ich einen Button“ oder „wie etwas mehr mit den Modellen zu machen“, einen Dateibaum in ein JTree zu setzen. Und stilistisch wertvoll ist der Hinweis mit der JDBC-Verbindung in einem JSP-Tag sicher nicht. Und wo sind die DAOs? Bei JMS stimmt es zwar grundsätzlich, dass in einer Tabelle bei Programmiersprachenunabhängigkeit „Nein“ steht und bei HTTP „Ja“, doch allgemeingültig ist das nicht. Für ActiveMQ gibt es ein standardisiertes Protokollformat (OpenWire) und APIs für diverse andere Programmiersprachen. Das gleiche gilt auch für IBMs WebSphere MQ (früher MQSeries) – sicherlich eines der besten MOMs -, das heterogene Plattformen verbindet. Einen XML-Parser über die Anweisung org.apache.xerces.parsers.DOMParser parser = new org.apache.xerces.parsers.DOMParser(); statt über JAXP zu erfragen ist nicht schön sondern fast grob fahrlässig. Zudem haben auch die JAXP-Parser Features, obwohl die String-Kennungen dann immer ein wenig anders aussehen. Zu Tipp 196: Wann immer man sich mit getGraphics() einen Grafik-Kontext holt, sollte man ihn auch wieder freigeben. Dieser Hinweis fehlt leider in den Beispielen. Farbkonstanten sollten groß geschrieben sein, also Color.WHITE, statt Color.white, zumindest seitdem Sun diese Konvention in Java 1.4 eingeführt hat.

Erst wenn der letzte Programmierer eingesperrt und die letzte Idee patentiert ist, werdet ihr merken, dass Anwälte nicht programmieren können.

TeX-Formulate in Webseiten mit jsMath

http://www.math.union.edu/~dpvc/jsMath/ ist eine Layout-Engine, die TeX-Formulare in Webseiten rendert. Dabei werden nicht, wie bei anderen Ansätzen, Grafiken generiert, sondern die Ausdrücke mit CSS positioniert. Damit skaliert das Ganze schön. Das Ergebnis ist beeindruckend und die Engine wird auch von einigen Wiki-Systemen verwendet. Einige Beispiele: Examples of jsMath. Im Interactive jsMath lab kann man Formen eingeben und es kommt HTML raus, was man nur noch zusammen mit dem CSS auf die eigene Webseite setzen muss. Aus x_i^2 wird etwa:

<SPAN CLASS=“typeset“><nobr><span class=“scale“><span class=“icmmi10″>x</span><span style=“position: relative; top:0.372em;“><span class=“size2″><span class=“icmmi10″>i</span></span><span class=“spacer“ style=“margin-left:0.05em“></span></span><span style=“position: relative; margin-left:-0.260em; top:-0.362em;“><span class=“size2″><span class=“icmr10″>2</span></span><span class=“spacer“ style=“margin-left:0.05em“></span></span><span class=“blank“ style=“height:1.331em;vertical-align:0.908em“></span></span></nobr></SPAN>

Jetzt fehlen nur noch jsMath.js und, falls etwa Summen-Zeichen oder sonstiges Sonderzeichen verwendet werden, im fonts-Ordner die TeX-Fonts.

Das ANTLR Eclipse-Plugin

Für den beliebten Parsergenerator ANTLR gibt es unter http://www.javadude.com/tools/antlr3-eclipse/ ein praktisches Plugin. Der Update-Manger von Eclipse wird auf die URL http://javadude.com/eclipse/update gelegt, installiert und neu gestartet. Anschließend geht man im Projekt auf das Kontextmenü und aktiviert Add/Remove ANTLR 3 Nature.

Als nächstes kann man eine ANTLR-Datei ablegen. Auf der Doku-Seite vom Plugin wird eine Grammatik (Datei Expr.g3) angegeben, die hier — leicht überarbeitet — angegeben werden soll:

grammar Expr;

@header {
package com.tutego.script.fp;

import java.util.HashMap;
}

@lexer::header {
package com.tutego.script.fp;
}

@members {
HashMap<String,Integer> memory = new HashMap<String,Integer>();
}

prog:
stat +
;

stat:
expr NEWLINE
{
System.out.println( $expr.value );
}

| ID '=' expr NEWLINE
{
memory.put( $ID.text, $expr.value );
}

| NEWLINE
;

expr returns [int value]:

e = multExpr { $value = $e.value; }
(
'+' e = multExpr { $value += $e.value; }
| '-' e = multExpr { $value -= $e.value; }
)*
;

multExpr returns [int value]:

e = atom { $value = $e.value; }
(
'*' e = atom { $value *= $e.value; }
)*
;

atom returns [int value]:

INT
{
$value = Integer.parseInt($INT.text);
}

| ID
{
Integer v = memory.get( $ID.text );
if ( v!=null ) $value = v.intValue();
else System.err.println( "Undefined variable " + $ID.text );
}

| '(' expr ')' {$value = $expr.value;}
;

ID:
('a'..'z' | 'A'..'Z') +
;

INT:
'0'..'9' +
;

NEWLINE:
'\r' ? '\n'
;

WS:
(' ' | '\t') +
{
skip();
}
;

Bearbeiten lässt sich die Grammatik in Eclipse nicht ordentlich, doch dafür dient ja die ANTLRWorks: The ANTLR GUI Development Environment.

Das Schöne beim Plugin: Es generiert automatisch im Hintergrund den Parser und Lexer. Das Testprogramm ist schnell geschrieben:

package com.tutego.script.fp; 

import org.antlr.runtime.*;

public class FpRunner
{
public static void main( String[] args ) throws Exception
{
ExprLexer lex = new ExprLexer( new ANTLRStringStream("age=34\nage*2\n12*111\n") );

ExprParser parser = new ExprParser( new CommonTokenStream( lex ) );

try
{
parser.prog();
}
catch ( RecognitionException e )
{
e.printStackTrace();
}
}
}

Die Ausgabe ist dann

68
1332