Inselupdate: XPath mit JDOM
Um XPath-Anfragen mit JDOM durchzuführen, stehen die Typen org.jdom2.xpath. XPathFactory und XPathExpression im Zentrum. Im Hintergrund arbeitet standardmäßig Jaxen. JDOM bringt das nötige Java-Archiv für Jaxen mit. Damit die folgenden Beispiele laufen, muss aus dem Ordner lib des Archivs jdom-xyz.zip die Jar-Datei jaxen.jar in den Klassenpfad aufgenommen werden. Unter http://jaxen.codehaus.org/releases.html lässt sich die jeweils aktuelle Version beziehen, die sich jedoch seit langen nicht mehr aktualisiert hat.
Zu einem XPathFactory-Objekt führt die Fabrikmethode XPathFactory.instance(). Diesem Objekt wird im nächsten Schritt der eigentliche XPath-Ausdruck übergeben: XPathFactory.instance().compile(xpath). Das Ergebnis ist ein XPathExpression-Objekt, welches wir fragen können, welche Knoten in der Ergebnismenge liegen. Der XPath-Ausdruck legt fest, was aus dem XML-Dokument gewünscht ist. Die Liste kann Elemente, Attribute oder Strings enthalten, daher sind keine Typinformationen vorhanden und Generics helfen nicht viel.
Beispiel: Gib die Namen aller Gäste aus:
Listing 1.38: com/tutego/insel/jdom/xpath/XPathDemo1.java, Ausschnitt
XPathExpression<Object> xpath = XPathFactory.instance().compile( "/party/gast/@name" );
List<Object> names = xpath.evaluate( doc );
for ( Object object : names ) {
Attribute attribute = (Attribute) object;
System.out.println( attribute.getValue() );
}
Da es keine Typinformationen gibt, liefert evaluate() immer nur eine Liste von unbekannten Objekten, die von uns in ein über eine explizite Typanpassung in etwas Sinnvolles gebracht werden muss.
Beispiel: Selektiere mit einem XPath-Ausduck die Getränke der Gäste, und gib sie auf den Bildschirm aus:
Listing 1.38: com/tutego/insel/jdom/xpath/XPathDemo2.java, Ausschnitt
XPathExpression<Object> xpath = XPathFactory.instance().compile( "/party/gast/getraenk" );
for ( Object object : xpath.evaluate( doc ) )
System.out.println( ((Element)object).getValue() );
Das Ergebnis dieser beiden Aufrufe ist immer eine Knotenmenge. Es gibt aber auch Situationen, in denen nur das erste Element der Ergebnisliste verarbeitet werden soll oder nur ein Element als Ergebnis bei einem XPath-Ausdruck möglich ist, wie zum Beispiel bei der Abfrage von Elementen mit Index-Angabe. Für diesen Fall bietet die Klasse XPathExpression die praktische Methode evaluateFirst(Object context).
Beispiel: Gib den Namen des ersten Gastes aus:
Listing 1.38: com/tutego/insel/jdom/xpath/XPathDemo3.java, Ausschnitt
XPathExpression<Object> xpath = XPathFactory.instance().compile( "/party/gast[1]/@name" );
Object firstGuest = xpath.evaluateFirst( doc );
System.out.println( ((Attribute) firstGuest).getValue() );
Nutzen von XPath-Ausdrücken
Die Möglichkeiten von XPath können als Alternative zu den Zugriffen über die Datenstrukturen von Java betrachtet werden. Es ist häufig einfacher, mit einem XPath-Ausdruck als mit einzelnen Methodenaufrufen den Pfad zu den Inhalten zu kodieren. Eine Anwendung, die dem Benutzer einen Zugriff auf die XML-Daten bietet, sollte auf jeden Fall XPath anbieten, weil dies der Standard für den Zugriff ist.
JDOM 2.0.1 freigegeben
http://www.jdom.org/news/index.html. Von der Webseite:
04.28.2012: JDOM 2.0.1 Released!
JDOM 2.0.1 is here!
JDOM 2.0.1 Introduces official support for Android! See the JDOM and Android page. JDOM 2.0.1 also fixes a bug in the ‘Compact’ output of XML.
Get JDOM 2.0.0 Here! or from the maven-central repository here: Group: org.jdom, Articact: jdom
04.08.2012: JDOM 2.0.0 Released!
JDOM 2.0.0 is here!
JDOM 2.0.0 brings JDOM in to the world of Generics and other Java language items introduced with Java 5. As a result, JDOM 2.0.0 requires Java 5 or later, but is only fully supported on Java 6 and later.
Get JDOM 2.0.0 Here! or from the maven-central repository here: Group: org.jdom, Articact: jdom
In der Insel habe ich meine Programme nun auf JDOM 2 gebracht – ohne große Probleme. Nur die Generics muss ich in der Doku dokumentieren. Das einzige, was ich umschreiben musste, was das Kapitel über XPath, dazu gleich ein eigener Beitrag.
Inselraus: identityHashCode() ist für eine System-eindeutige Objekt-IDs nicht geeignet
Stellen wir uns vor, wir hätten eine Objekthierarchie im Speicher, die zum Beispiel jeder Socke einen Besitzer zuspricht. Wenn wir im Speicher Assoziationen abbilden, dann sollen diese Verweise auch noch nach dem Tod des Programms überleben. Eine Lösung ist die Serialisierung, eine andere eine Objekt-Datenbank oder aber auch eine XML-Datei. Doch überlegen wir selbst, wo bei der Abbildung auf eine Datenbank oder eine Datei das Problem besteht. Zunächst stehen ganz unterschiedliche Objekte mit ihren Eigenschaften im Speicher. Das Speichern der Zustände ist kein Problem, denn nur die Attribute müssten abgespeichert werden. Doch wenn ein Objekt auf ein anderes verweist, muss dieser Verweis gesichert werden. Aber in Java ist ein Verweis durch eine Referenz gegeben, und was sollte es da zu speichern geben? Eine Lösung für das Problem ist, jedem Objekt im Speicher einen Zähler zu geben und beim Speichen etwa zu sagen: »Der Besitzer 2 kennt Socke 5«.
Der Identifizierer für die Objekte muss eindeutig sein, und wir können überlegen, System.identityHashCode() zu nutzen. In der Implementierung der virtuellen Maschine von Oracle geht in den Wert von identityHashCode() die Information über den wahren Ort des Objekts im Speicher ein. Bei einer 64-Bit-Implementierung würden auch 32 Bit abgeschnitten, und die Eindeutigkeit wäre somit automatisch nicht mehr gewährleistet. Ein weiteres Problem besteht darin, dass zwar die Implementierung von Oracles identityHashCode() auf die eindeutige Objektspeicheradresse abbildet, aber dass das nicht jeder Hersteller so machen muss. Damit ist identityHashCode() nicht überall gesichert unterschiedlich. Zudem ist es prinzipiell denkbar, dass die Speicherverwaltung die Objekte verschiebt. Was sollte identityHashCode() dann machen? Wenn die neue Speicheradresse dahinter steckt, würde sich der Hashcode ändern, und das darf nicht sein. Es käme ebenfalls zu einem Problem, wenn mehr als Integer.MAX_INTEGER viele Objekte im Speicher stünden. (Doch wenn wir uns die große Zahl 2^32 = 4.294.967.296 vor Augen halten, dann es ist unwahrscheinlich, dass sich mehr als 4 Milliarden Objekte im Speicher tummeln. Zudem bräuchten wir 4 Gigabyte Speicher, wenn jedes Objekt auch nur 1 Byte kosten würde.)
Es ist gar nicht so schwierig, zwei unterschiedliche Objekte mit gleichen identityHashCode()-Resultat zu bekommen. Wir erzeugen ein paar String-Objekte und testen, jedes mit jedem, ob identityHashCode() den gleichen Wert ergibt:
String[] strings = new String[5000];
for ( int i = 0; i < strings.length; i++ )
strings[i] = Integer.toString( i );
int cnt = 0;
for ( int i = 0; i < strings.length; i++ ) {
for ( int j = i + 1; j < strings.length; j++ ) {
int id1 = System.identityHashCode( strings[i] );
int id2 = System.identityHashCode( strings[j] );
if ( id1 == id2 ) {
out.println( "Zwei Objekte mit identityHashCode() = " + id1 );
out.println( " Objekt 1: \"" + strings[i] + "\"" );
out.println( " Objekt 2: \"" + strings[j] + "\"" );
out.println( " Object1.hashCode(): " + strings[i].hashCode() );
out.println( " Object2.hashCode(): " + strings[j].hashCode() );
out.println( " Object1.equals(Object2): " + strings[i].equals( strings[j] ) );
cnt++;
}
}
}
System.out.println( cnt + " Objekte mit gleichem identityHashCode() gefunden." );
Ein Durchlauf bringt schnell Ergebnisse wie:
Zwei Objekte mit identityHashCode() = 9578500
Objekt 1: "541"
Objekt 2: "2066"
Object1.hashCode(): 52594
Object2.hashCode(): 1537406
Object1.equals(Object2): false
Zwei Objekte mit identityHashCode() = 14850080
Objekt 1: "2085"
Objekt 2: "2365"
Object1.hashCode(): 1537467
Object2.hashCode(): 1540288
Object1.equals(Object2): false
2 Objekte mit gleichem identityHashCode() gefunden.
Das Ergebnis ist also, dass identityHashCode() nicht sicher bei der Vergabe von Identifizierern ist. Um wirklich allen Problemen aus dem Weg zu gehen, ist ein Zählerobjekt oder eine ID über zum Beispiel die Klasse java.util.UUID nötig.
Java 8 Developer Preview mit Modulsystem Jigsaw
Download unter http://jdk8.java.net/jigsaw/, Kurzeinführung unter http://openjdk.java.net/projects/jigsaw/doc/quickstart.html.
NetBeans 7.1.2 Update
Neues unter http://netbeans.org/community/releases/71/, Support für JavaFX 2.1, neuer Debugger, CSS 3 und mehr
JavaFX 2.1 ist fertig
Download unter http://www.oracle.com/technetwork/java/javafx/downloads/index.html. Aus den http://docs.oracle.com/javafx/release-documentation.html:
Media H.264 and AAC support
Mac OS X support
Applications must be packaged for the desktop, Web and Web Start applications are not yet supported.
LCD text
UI enhancements, including controls for Combo Box, Stacked Chart, and application-wide menu bar
Webview to support JavaScript to Java method calls
