Siehe http://www.creditreform-magazin.de/content/news/wie-viel-sie-von-java-verstehen-sollten;216294;0.
Autor: Christian Ullenboom
Selbst definierte Cursor in AWT/Swing
Bei einem Cursor sind wir nicht allein auf die vordefinierten Muster angewiesen. Leicht lässt sich aus einer Grafik ein eigener Cursor definieren. Dazu bietet das Toolkit die Methode createCustomCursor() an. Als Argument geben wir ein Image-Objekt, einen Hotspot und einen Namen an. Der Hotspot definiert eine Art Nullpunkt, der die Spitze angibt. Zeigt etwa der Standardcursor mit einem Pfeil nicht wie üblich nach oben, sondern nach unten, so gibt der untere Punkt den Nullpunkt an. Der Name ist nur nötig, wenn Java-Accessibility genutzt wird, also eine Möglichkeit gegeben ist, den Cursor zum Beispiel ohne Maus anzusprechen.
Beispiel: Weise einer java.awt.Component den Cursor mit der Grafik cursor.gif und dem Hotspot auf (10,10) zu.
Cursor c = getToolkit().createCustomCursor( new ImageIcon( "cursor.gif" ).getImage(), new Point(10,10), "Cursor" ); component.setCursor( c );
Hinweis Animierte Cursor bietet die Java-Umgebung nicht an. Wir könnten selbstständig in einem Thread immer wieder mit setCursor() unterschiedliche Cursor setzen, um etwa eine drehende Sanduhr oder eine rotierende Festplatte zu bekommen.
Da grafische Oberflächen in der Regel keine Cursor beliebiger Auflösung und Farbanzahl zulassen, lässt sich das Toolkit auch über diese Größen erfragen. Die Methode getBestCursorSize() liefert die mögliche Größe des Cursors zurück. Es ist sinnvoll, diese Methode vorher aufzurufen, um ein passendes Bild auszuwählen. Ähnlich wie bei den Icons in der Titelleiste werden die Grafiken sonst skaliert, was nicht unbedingt schön aussehen muss. Gleiches gilt für die Farben. Nicht alle Systeme erlauben beliebig viele Farben für den Cursor. Die maximale Farbanzahl liefert die Funktion getMaximumCursorColors(). Notfalls wird der Cursor auf die Farbanzahl heruntergerechnet.
Tipp: Unterstützt die Plattform Cursor beliebiger Größe, so lässt sich dadurch einfach eine Bubble-Help realisieren, die nicht rechteckig ist. An Stelle des Cursors wird eine Grafik mit dem Cursor zusammen mit einer Hilfe angezeigt. Das Betriebssystem verwaltet den Cursor, und wir müssen den Hintergrund nicht sichern und mit der Hilfe verknüpfen.
abstract class java.awt.Toolkit
- Cursor createCustomCursor( Image cursor, Point hotSpot, String name )
throws IndexOutOfBoundsExceptionErzeugt ein neues Cursor-Objekt. Liegt der Hotspot außerhalb der Grenzen der Grafik, wird eine IndexOutOfBoundsException ausgelöst.
- Dimension getBestCursorSize( int preferredWidth, int preferredHeight )
Liefert die unterstützte Cursor-Größe, die den gewünschten Ausmaßen am nächsten liegt. Oft werden die Argumente ignoriert, wenn die Umgebung keine beliebige Cursor-Größe zulässt. Erlaubt das System überhaupt keine selbst definierten Cursor, erhalten wir ein Objekt der Dimension (0,0). - int getMaximumCursorColors()
Liefert das Maximum an Farben, das das Toolkit für Cursor unterstützt. Der Rückgabewert ist null, wenn selbst definierte Cursor nicht gestattet sind.
public abstract class java.awt.Component implements ImageObserver, MenuContainer, Serializable
- void setCursor( Cursor cursor )
Weise einen neuen Cursor zu.
In Servlets Seiten über HTTP-Redirect umlenken
Ist eine Seite nicht mehr korrekt, kann sie umgelenkt werden. Hierfür wird ein spezieller Header gesetzt.
sendRedirect()
Dazu dient die Methode sendRedirect(String), die auf eine neue Seite verweist. Als Argument kann eine relative oder absolute URL aufgeführt werden, die auf eine temporäre neue Seite weist. Wir könnten auch mit setHeader() arbeiten, müssten dann aber von Hand den Statuscode ändern, der für Umleitungen auf 302 gesetzt sein muss. Die Arbeit können wir uns sparen. Nach dem Setzen der Umleitung sollte nicht mehr in die Ausgabe geschrieben werden.
Wozu kann nun diese Umleitung eingesetzt werden? Zum Beispiel, um über Formular-Parameter zu externen Seiten weiterzuleiten:
response.sendRedirect( url );
Nach der Umleitung steht der Ort der neuen Seite in der URL-Zeile des Browsers. Das folgende Programm verweist nun einfach auf ein anderes Servlet. Die Pfadangabe kann absolut oder relativ sein.
String url = "http://www.tutego.de/"; response.sendRedirect( url );
Was passiert beim Umlenken?
Technisch gesehen ist eine Umlenkseite eine ganz normale Webseite. Das wirkliche Umlenken ist eine Fähigkeit des Browsers und nicht des Servers. Dies ist wichtig anzumerken, weil eigene Programme, die URL-Verweise aufbauen, hier oft nicht korrekt vorgehen.
Das Servlet setzt beim sendRedirect() den Content-Type auf "text/html". Wichtig sind zwei weitere Informationen: die eine in der Statuszeile und die andere im Header. In der Statuszeile wird die Nummer 302 gesendet, die das Umlenken bezeichnet. Die Information darüber, wohin verwiesen wird, steht in einem weiteren Header namens »Location«. Somit können wir unser Redirect prinzipiell auch selbst ausformulieren, indem wir Folgendes schreiben:
response.setStatus( 302 ); response.setContentType( "text/html" ); response.setHeader( "Location", url );
Der String url ist dann eine Referenz auf die neue Seite. Der Verweis auf die externe Seite muss dann natürlich absolut sein. Dies regelt jedoch sendRedirect() automatisch.
Rechte (Permissions) und Mengenbeziehungen
Für jede geladene Klassen gilt eine Sammlung von Rechten, die für diese Klasse vergeben wurden. In der Regel wurden sie per Policy-Datei vergeben, doch natürlich sind auch andere Möglichkeiten denkbar. Diese Sammlung selbst wird in einem PermissionCollection-Objekt gespeichert, welches einer ProtectionDomain zugeordnet ist.
ProtectionDomain domain = ListPermissions.class.getProtectionDomain(); PermissionCollection permissonColl = Policy.getPolicy().getPermissions( domain );
Dem PermissionCollection-Objekt lässt sich mit einer Enumeration die gespeicherten Permissions rauskitzeln. Ein System.out liefert ebenso eine schöne Ausgabe, etwa für das eigene Programm ListPermissions ohne Sicherheitsmanager:
java.security.Permissions@c21495 ( (java.util.PropertyPermission java.specification.vendor read) (java.util.PropertyPermission java.vm.specification.vendor read) (java.util.PropertyPermission path.separator read) (java.util.PropertyPermission java.vm.name read) (java.util.PropertyPermission java.class.version read) (java.util.PropertyPermission os.name read) (java.util.PropertyPermission java.vendor.url read) (java.util.PropertyPermission java.vendor read) (java.util.PropertyPermission java.vm.vendor read) (java.util.PropertyPermission file.separator read) (java.util.PropertyPermission os.version read) (java.util.PropertyPermission java.vm.version read) (java.util.PropertyPermission java.version read) (java.util.PropertyPermission line.separator read) (java.util.PropertyPermission java.vm.specification.version read) (java.util.PropertyPermission java.specification.name read) (java.util.PropertyPermission java.vm.specification.name read) (java.util.PropertyPermission java.specification.version read) (java.util.PropertyPermission os.arch read) (java.lang.RuntimePermission exitVM) (java.lang.RuntimePermission stopThread) (java.net.SocketPermission localhost:1024- listen,resolve) (java.io.FilePermission \D:\JavaBook\programme\24_Sicherheitskonzepte\- read) )
Die Rechte sind natürlich genau diejenigen, die vom System bereitgestellt worden sind. Sie beziehen sich genau auf unsere Klasse ListPermissions. Für die Systemklassen, wie java.lang.Object oderString gelten keine Einschränkungen. Ersetzen wie ListPermissions durch Objekt, so würde ein System.out genau die alles-erlaubende Permission ergeben:
(java.security.AllPermission <all permissions> <all actions>)
Schließt eine Permission eine andere Permission ein?
Permission-Objekte definieren selbst nicht nur Rechte für spezielle Eigenschaften (Lesen in einem speziellen Verzeichnis), sondern ziehen auch Recht für andere Eigenschaften nach sich (Lesen aller Dateien ab einem Verzeichnis). Wird etwa für ein Verzeichnis das Recht auf Lesen und Schreiben gesetzt, dann impliziert dies auch das Lesen. Die Fähigkeit, dass ein Recht ein anderes bedingt, ist eine Fähigkeit der Permission-Objekte. Die Klasse bietet eine implies()-Funktion, die testet, ob eine Permission eine andere Permission einschließt.
Beispiel: Wir wollen zwei FilePermission-Objetke anlegen, wobei das erste (perm1) das zweite (perm2) einschließt.
import java.io.FilePermission; import java.security.Permission; public class PermissionImplies { public static void main( String args[] ) { Permission perm1 = new FilePermission( "c:\\windows\\*", "read,write" ); Permission perm2 = new FilePermission( "c:\\windows\\fonts", "read" ); if ( perm1.implies(perm2) ) System.out.println( perm1 + " implies " + perm2 ); if ( !perm2.implies( new FilePermission("c:\\windows\\fonts", "write") ) ) System.out.println( perm1 + " not implies " + perm2 ); } }
Die Ausgabe zeigt, dass diese Rechte vom System korrekt unterstützt werden.
(java.io.FilePermission c:\windows\* read,write) implies (java.io.FilePermission c:\windows\fonts read) (java.io.FilePermission c:\windows\* read,write) not implies (java.io.FilePermission c:\windows\fonts read)
Parametersammlungen im Servlet mit getParameterValues() auslesen
Da ein Parameter auch mehr als einen Wert haben kann, hilft getParameter() nicht weiter, da dieser nur jeweils einen Wert liefert. Hier führt die Methode getParameterValues() zum Ziel, die ein Feld von Strings zurückgibt. (Damit ist kein zusammengesetzter String etwa für Suchmaschinen gemeint.) Sind wir an einer vollständigen Aufzählung der Schlüssel interessiert, liefert getParameterNames() ein Objekt vom Typ Enumeration zurück, mit dem wir durch das Feld wandern und die Werte mit getParameter() erfragen können.
<% java.util.Enumeration paramNames = request.getParameterNames(); while ( paramNames.hasMoreElements() ) { String param = (String) paramNames.nextElement(); out.print( "<p>" + param + " = " ); String[] paramValues = request.getParameterValues( param ); if ( paramValues.length == 1 ) { String paramValue = paramValues[0]; if ( paramValue.length() == 0 ) out.println( "unbestimmt" ); else out.println( paramValue ); } else { for ( int i = 0; i < paramValues.length; i++ ) out.print( paramValues[i] + " " ) ; out.println( "<p>" ); } } %>
Wenn wir das Programm mit der Zeile
http://localhost:8080/jt/parameterNames.jsp?a=1&b=2&c=&a=2
im Browser starten, erzeugt das Servlet folgende Ausgabe:
b = 2 a = 1 2 c = unbestimmt
Wir sehen, dass alle Parameter hier aufgeführt sind, doch in unbestimmter Reihenfolge. Dies ist aber egal. Das Programm erkennt, ob ein Wert gesetzt ist oder nicht beziehungsweise ob einem Schlüssel ein Wert zweimal zugewiesen wurde.
Wie heißt die Klasse mit der Methode main()?
In C(++) ist das erste Element des Felds der Funktion main(int argc, char **argv) der Name des Programms. Das ist in Java anders. Die Methode enthält als ersten Parameter nicht den Namen der Klasse beziehungsweise des Programms, sondern einfach den ersten Parameter – sofern einer auf der Kommandozeile übergeben wurde. Auf einem kleinen Umweg ist das auch für manche Klassen möglich.
Der zu einer Klasse gehörende Klassenlader lässt sich mit dem Class-Objekt erfragen. Mit der Methode getResource() erhalten wir von einem Klassennamen ein URL-Objekt zurück, das (unter gewissen Voraussetzungen) die Position der Klassendatei im Dateisystem anzeigt. Das folgende Programmbeispiel zeigt, wie wir von einer Klasse den vollständigen Dateipfad zurückbekommen.
package com.tutego.insel.lang; import java.net.*; public class FindClassLocation { static String findLocation( Class<?> clazz ) { ClassLoader loader = clazz.getClassLoader(); if ( loader == null ) return null; URL url = loader.getResource( clazz.getName().replace('.', '/' ) + ".class" ); return ( url != null ) ? url.toString() : null; } public static void main( String[] args ) throws Exception { Class<?> c = Class.forName( "com.tutego.insel.lang.FindClassLocation" ); System.out.println( "Class: " + c.getName() ); System.out.println( "Filename: " + findLocation(c) ); } }
Unter meinem Dateisystem liefert das Programm die Ausgabe:
Class: com.tutego.insel.lang.FindClassLocation Filename: file:/S:/Insel/programme/09_Funktionsbibliothek/com/tutego/insel/lang/FindClassLocation.class
Achtung! Die Lösung funktioniert natürlich nur unter gewissen Voraussetzungen. Es geht nur für Klassen, die in kein Jar-Archiv eingebunden sind und nicht den Standardbibliotheken entstammen. Auch ist eine Dateiangabe unmöglich, wenn wir etwa einen eigenen Klassenlader schreiben, der die Klassen aus einer Datenbank bezieht; dann gibt es keinen Pfad mehr.
getResourceAsStream()
Benötigen wir den Ort einer Klasse, um mit dieser Information auf weitere Dateien im Verzeichnis zuzugreifen, geht es mit der Class.getResourceAsStream(String) einfacher. Diese Methode dient insbesondere dazu, Ressourcen wie Bilder oder Musik aus einem Jar-Archiv auszulesen. Auch der ClassLoader bietet die Methode getResourceAsStream(String) an. Diese Methoden funktionieren ebenfalls für Klassen aus Jar-Archiven, wenn die Ressource im Archiv liegt.
Durch Null-Cursor Flackern des Mauszeigers bei Animationen vermeiden
Einige Betriebssysteme haben bei Java-Animationen das Problem, dass der Mauszeiger unruhig flackert. Zur Lösung kann man einen Cursor ohne Pixel an die Stelle der Grafik setzen.
Es ist Sache der grafischen Oberfläche, den Mauszeiger mit dem Hintergrund zu verbinden. Um ein unruhiges Bild zu vermeiden, greifen wir zu einem Trick und schalten den Mauszeiger einfach ab. Dazu soll createCustomCursor() einen neuen Cursor mit transparentem Image-Objekt erzeugen. Da wir kein leeres transparentes GIF-Bild nutzen wollen, legen wir einfach mit der Klasse BufferedImage ein Bild im Speicher an. Das Argument muss dabei TYPE_INT_ARGB sein, sonst ist das Bild nicht transparent. Damit ist die Arbeit getan, der letzte Schritt besteht darin, den Cursor mit setCursor() einer Komponente zuzuweisen. Im Fall einer Animation wäre das zum Beispiel ein JComponent, im folgenden Beispiel wird das eine Schaltfläche sein:
import javax.swing.*; import java.awt.*; import java.awt.image.*; public class NullCursor { public static void main( String[] args ) { JFrame f = new JFrame(); f.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE ); JButton b = new JButton( "Kein Cursor" ); f.add( b, BorderLayout.PAGE_START ); b.setCursor( Toolkit.getDefaultToolkit().createCustomCursor( new BufferedImage( 16, 16, BufferedImage.TYPE_INT_ARGB ), new Point(0,0), "" ) ); f.setSize( 200, 200 ); f.setVisible( true ); } }
NetBeans Tools für Vaadin
https://blogs.oracle.com/geertjan/entry/official_netbeans_tools_for_vaadin
Mal sehen wie lange das bleibt. Uml tool und ruby Unterstützung fielen auch irgendwann wieder raus.
Letzte aktuelle Version von Lambda-Status
Algorithmen für Rastergrafik, 1. Linien zeichnen mit Bresenham
Der Bildschirm ist in eine Reihe von Punkten zerlegt, die alle mit verschiedenen Intensität oder Farbe gesetzt sind. Die Rastergrafik versucht, die geometrischen Beschreibungen von Objekten auf die Pixel des Bildschirmes abzubilden. Diese Umrechnung muss schnell und in einigen Fällen speichersparend sein. In den Letzen Jahren wurden eine ganze Reihe von Algorithmen entwickelt, die mittlerweile in Hardware Einzug gehalten hat. Die modernen Grafikkarten unterstützen neben grafischen Operationen wie Pixel und Farbe mischen auch Funktionen zum Zeichen von Linien, Kreisen, Polygonen, gefüllten Flächen und weiteres. Daneben sind dreidimensionale Koordinatentransformationen in Hardware gegossen wie das Abbilden einer Bitmap auf eine Fläche, das Erzeugen von Nebel (engl. Fogging) oder auch perspektivische Korrektionen (engl. Perspective Correction). Natürlich überlassen wir das Zeichen der Linien der Grafikkarte und die dreidimensionalen Transformationen der 2D-Bibliothek. Java ist allerdings eine einfache Sprache und die Arbeitsweise kann somit schnell erklärt werden. Notfalls können wir Funktionen, die nicht in der Grafikbibliothek vorliegen, nachbilden.
Linien zeichnen
Linien sind ein einfachen Beispiel dafür, wie Punkte auf dem Raster verteilt werden. Beachten wir dazu die Grafik. Wie zeichnen eine Linie von den Koordinaten (x1,y1) zum Punkt (x2,y2).
Um nun die Punkte auf dem physikalischen Bildschirm zu setzen, müssen die Punkte so verteilt werden, dass sie möglichst nah an der wirklichen Linien liegen. Gehen wir die X-Achse von xl nach x2 ab, so müssen wir die Y-Punkte wählen. Bei der Wahl können wir folgende Entscheidung fällen: An jedem Punkt müssen wir einen Pixel wählen, nämlich diesen, der den geringsten Abstand zum Pixelraster besitzt. Ist die Linie horizontal, so ist der Abstand zwischen wirklicher Linie und Rasterpunkt immer Null und die Wahl ist einfach.
Ein Algorithmus kann nun wie folgt arbeiten: Es ist der Abstand zwischen der wirklichen Linie und einem Pixel auf der Y-Achse gleich einem Fehlerwert. Es ist der Punkt auf der Y-Achse auszuwählen,
der zur wirklichen Linie den kleinsten Fehlerwert aufweist. Einen schnellen Algorithmus, der genau mit diesem Fehlerwert arbeitet, wurde zuerst von j. E. Bresenham (1965) veröffentlicht. Bresenham
betrachtet Linien im ersten kartesischen Oktanten, die Steigung der Linie ist also kleiner Eins, formal: 0 < Δy / Δx < 1. Die Rasterung ist von links nach rechts. Betrachten wir nun ein Programmsegment in
Pseudocode das, welches eine Linie mit Steigung kleiner Eins zeichnet:
Δx = x2 - x1
Δy = y2 - y1
error = - Δx / 2;
setPixel( x1, y1 );
while( x < x2 ) {
error = error + Δy;
if ( error >= 0 ) {
y = y + 1;
error = error - Δx;
}
x = x + 1;
setPixel( x, y );
}
Liegen die Linien in anderen Oktanten, so müssen wir den Algorithmus nur leicht abändern. So sind durch einfache Spiegelungen an Winkelhalbierenden oder Ordinate bzw. Abszisse die Punkte zu verschieben. Liegt die Linie beispielsweise im siebten Oktanten, so rastern wir entlang der Y-Achse und vertauschen dx mit -dx und liegt die Linie im achten Oktanten, so rastern wir wir im ersten Oktanten, nur dass dy mit -dy vertauscht wird.
Bresenhams Linienalgorithmus arbeitet schnell und optimal (was bedeutet, die Fehler sind am kleinsten), weil alle Berechnungen auf Ganzzahlen ausgeführt werden. Dies war in den sechziger Jahren
noch wichtiger als heute, wo eine Fließkommaoperation schon genauso viel kostet wie eine lnteger-Operation. Da der Beweis langwierig — aber für die mathematisch geschulten nicht schwierig — ist, wollen wir an dieser Stelle auf den Formalismus verzichten. Anstelle dessen betrachten den Quellcode einer Klasse, die eine einfaches Linienmuster schreibt. Ein nicht unwesentlicher Teil wird dafür aufgewendet, den Algorithmus für die verschiedenen Lagen im kartesischen Koordinatensystem anzupassen. Da wir später noch andere grafische Objekte zeichnen, dient dieses Programm gleichzeitig als Rahmen.
import java.awt.*;
import java.awt.event.*;
public class GDV extends Frame
{
private Graphics g;
static final int CLASSIC = 0;
static final int BRESENHAM = 1;
public int lineType = CLASSIC;
public GDV() {
setSize( 400, 400 );
addWindowListener( new WindowAdapter() {
public void windowClosing ( WindowEvent e ) { System.exit(0); }
});
}
/**
* Draws a line with algorithm of Bresenham
*/
private void drawBresenhamLine( int x1, int y1, int x2, int y2 )
{
int xIncrement = 1,
yIncrement = 1,
dy = 2*(y2-y1),
dx = 2*(x1-x2),
tmp;
if ( x1 > x2 ) { // Spiegeln an Y-Achse
xIncrement = -1;
dx = -dx;
}
if ( y1 > y2 ) { // Spiegeln an X-Achse
yIncrement= -1;
dy= -dy;
}
int e = 2*dy + dx;
int x = x1; // Startpunkte setzen
int y = y1;
if ( dy < -dx ) // Steigung < 1
{
while( x != (x2+1) )
{
e += dy;
if ( e > 0)
{
e += dx;
y += yIncrement;
}
g.drawLine( x, y, x, y );
x += xIncrement;
}
}
else // ( dy >= -dx ) Steigung >=1
{
// an der Winkelhalbierenden spiegeln
tmp = -dx;
dx = -dy;
dy = tmp;
e = 2*dy + dx;
while( y != (y2+1) )
{
e += dy;
if( e > 0 ) {
e += dx;
x += xIncrement;
}
g.drawLine( x, y, x, y );
y += yIncrement;
}
}
}
private void line( int x1, int y1, int x2, int y2 ) {
if ( lineType == BRESENHAM )
drawBresenhamLine( x1, y1, x2, y2 );
else
g.drawLine( x1, y1, x2, y2 );
}
public void paint( Graphics g ) {
this.g = g;
for ( int i=30; i < 300; i+=20 )
line( 10+i, 40, 300-i, 100 );
}
public static void main( String[] args ) {
GDV line = new GDV();
// line.lineType = CLASSIC;
line.lineType = BRESENHAM;
line.show();
}
}
Bresenhams Algorithmus arbeitet ohne Frage schnell. Wir können aber noch einige Erweiterungen programmieren: Wie wird eine antialiased Linie, also eine weiche Linie gezeichnet? Es gibt mehrere
Mögichkeiten, wobei aber nicht alle schnell sind. Eine Lösung besteht darin, auf jeden Punkt einen Filter loszulassen. Dies läuft aber auf viele Multiplikationen hinaus. Bekannt geworden ist ein anderer
Ansatz, ein Algoritmus von Gupta—Sproull (1981). Dieser ist in allen bekannten Grafik-Büchern nachzulesen.
Da der Algorithmus von Bresenham sehr schnell ist, wundert es einen, wenn wir noch von einer Geschwindigkeitsoptimierung sprechen. Das ist tatsächlich möglich und wurde von Angel, Edward
und Don Morrison in einem Aufsatz in den “IEEE Computer Graphics and Applications” auf zwei Seiten Ende 1991 beschrieben. Der Hintergrund ist einfach und kann daher kurz mit einer Programmieraufgabe umgesetzt werden: Die Pixelbewegungen wiederholen sich zyklisch. In einer Linie der Steigung 1, ist mit jedem Erhöhen des X-Zählers auch ein Erhöhen des Y-Zählers Verbunden. Eine Gerade mit der Steigung 1/8 setzt vier Punkte auf der Horizontalen, geht dann einen Pixel nach oben und setzt wieder vier Punkte. Können wir den Rhythmus erkennen, dann haben wir es einfach, denn dann müssen uns nur die Zeichentechnik merken und dann immer wieder kopieren. Für parallele Prozesse gibt es nichts schöneres. Wir berechnen dazu den Größten Gemeinsamen Teiler von Δy und -Δx. Ist dieser echt größer als Eins, dann haben wir damit den ersten Punkt, an dem die Berechnungsfolge von vorne beginnt. Der Punkt zeichnet sich durch die Koordinaten (x1 + a/ggT(Δy, -Δx, y1 + b/ggT(Δy/-yΔx) aus.
Warum man von Grafiken mit Text Abstand nehmen sollte
Thema der Woche: XPath und XSLT
- Lies http://docs.oracle.com/javase/tutorial/jaxp/xslt/index.html komplett
- Lade die Beispiele herunter, und starte die Beispiele (
download a ZIP file of the XSLT samples here
). - Erweitere die Adressen-Datei, so dass man XPath-Ausdrucke testen kann. Finde Beispiele für "/", ".", "..", Selektion und Wildcards.
- Welche wichtigen XPath-Funktionen gibt es? Wo findet man alle Funktionen beschrieben?
- Wie sind SQL und XPath verwandt?
- Wie kann man JDOM mit XPath nutzen?
Refactoring mit Alt + Shift + R geht nicht mehr? RadioSure kann Schuld sein
Seit einiger Zeit gab es einen merkwürden Effekt: Zeitweilig ging das Refactoring über den Shortcut Alt + Shift + R nicht mehr. Ich dachte erst an ein komisches Eclipse-Plugin, aber selbst Eclipse wollte zu dem Shortcut gar keine Operation anzeigen, es ging bis Alt + Shift und das R schluckte das System schon. Damit war klar, dass die Tastenkombination erst gar nicht bei Eclipse ankam – es musste ein anderes Windows-Programm sein. Ich schaute auf die Liste und dann fiel mir sofort RadioSure auf, und dann jetzt verstand ich auch, warum ich immer wieder unbeabsichtigte Aufnahmen hatte: Die Recording-Funktionalität wird exakt mit Alt + Shift + R getoggelt.
Nach dem Deaktivieren war dann auch mit dem Refactoring alles wieder in Ordnung.
JSR 337: Java SE 8
Einige Beispiele der Java 8 Stream API
command-line tool to find static dependencies in java 8 könnte kommen
Automatisch Fehler finden
Das verspricht ein neues Google Werkzeug
„error-prone – Catch common Java mistakes as compile-time errors – Google Project Hosting“ http://feedly.com/k/18e1CMT
Was sind eure Ergebnisse?
Die SQL3-Datentypen ARRAY, STRUCT und REF
Seitdem JDBC 2.0 die SQL3-Datentypen unterstützt, sind weitere Spaltentypen über Java-Programme zugänglich:
- ARRAY. Der Datentyp erlaubt eine Aufzählung mehrerer Werte wie ein Feld in einer Spalte.
- STRUCT. Neue benutzerdefinierte Typen – auch UDTs (user-defined types) genannt –, die auf der Datenbankseite mit der SQL-Anweisung CREATE TYPE angelegt werden.
- REF. Verweise auf strukturierte Typen.
Am Nützlichsten ist das Feld.
ARRAY
SQL-Felder werden in JDBC durch die Schnittstelle java.sql.Array behandelt. Nehmen wir in einer Tabelle eine Spalte Aktienverlauf an, die eine Liste (Array) von Zahlen enthält. Aus dem ResultSet gibt dann getArray() Zugriff auf die Informationen:
Array verlauf = rs.getArray( "Aktienverlauf" );
Die Variable verlauf verweist jetzt auf das Array-Objekt, was dem SQL ARRAY entspricht. Die Werte können nun entnommen werden, doch nicht mit einem Iterator, und auch nicht mit get(index) Funktionen, sondern wieder mit einem Aufruf getArray(). Es liefert ein Feld genau des passenden Typs zurück.
String[] verläufe = (String[]) verlauf.getArray(); for ( int i = 0; i < verläufe.length; i++ ) ...
Neben dem lesenden getArray() setzt setArray() ein Feld, und updateArray() aktualisiert die Werte.
Kreditkartennummern in Java testen
Sind Kreditkartennummern korrekt aufgebaut? Wie lassen sich sich Kreditkartennummern generieren?
E-Commerce-Lösungen sind im Internet mittlerweile häufig anzutreffen. Lassen sich für kleine Beträge Sonderlösungen finden, werden für größere Beträge immer noch Kreditkarten verwendet. Grund genug für uns Java-Programmierer, die Nummern der Karten zu testen, um zu überprüfen, ob uns nicht ein Anwender täuschen wollte.
Die Nummer einer Kreditkarte setzt sich nicht willkürlich zusammen. Die Nummern von Karten eines bestimmten Herstellers bestehen aus einer festen Anzahl von meistens 14 bis 16 Ziffern. Als Kennung für einen Hersteller (Visa (Veni, Vidi, VISA: I came, I saw, I did a little shopping.) , MasterCard, American Express) ist jeder Nummer eine zusätzliche Kennung von einer bis vier Ziffern vorangestellt. Die Ziffern der Kartennummer werden durch mathematische Verfahren überprüft. Wir wollen eines dieser Verfahren auch kennen lernen; den so genannten Luhn-Algorithmus. Dieser Algorithmus testet die Korrektheit des Aufbaus einer Nummernfolge. Die letzte Ziffer ist oft eine berechnete Checksummen-Ziffer.
Die folgende Tabelle gibt eine Übersicht über einige Kartenhersteller. Sie führt die Kennung, die Länge der Kartennummer und ein gültiges Beispiel auf:
Hersteller |
Anfang |
Gesamtlänge |
Beispiel |
Visa |
4 |
13,16 |
4111 1111 1111 1111 |
Master |
51,52,53,54,55 |
16 |
5500 0000 0000 0004 |
Diner’s Club11 |
30,36,38 |
14 |
3000 0000 0000 04 |
American Express12 |
34,37 |
15 |
3400 0000 0000 009 |
Discover |
6011 |
16 |
6011 0000 0000 0004 |
en Route |
2014,21 |
15 |
2014 0000 0000 009 |
JCB |
3088,3096,3112,3158,3337,3528 |
16 |
3088 0000 0000 0009 |
Neben den Herstellernummern sind auch folgende Nummern von den ausgebenden Banken im Umlauf: Manufacturers Hanover Trust (1033), Citibank (1035), Chemical Bank (1263), Chase Manhattan (1665), Bank of America (4024), Citicorp (4128), New Era Bank (4209), HHBC (4302), Imperial Savings (4310), MBNA (4313), California Federal (4317), Wells Fargo (5282), Citibank (5424), Wells Fargo (5410), Bank of New York (5432), MBNA (6017). Carte Blanche und Diner’s Club haben die gleichen Nummern.
Einen Abend im Februar 1950 vergaß Frank MacNamara seine Brieftasche. Er kam auf die Idee, eine Kreditkarte aus Karton anzubieten. Mit seinen Freunden gründete er am 28.2.1950 den Diner’s Club, der im Gründerjahr mehr als 10.000 Mitglieder und 1.000 Vertragspartner hatte. So war die erste Kreditkarte geboren. Im Jahre 1958 entschloss sich das internationale Transport-, Reise-, und Finanzierungsunternehmen American Express, eine eigene Karte herauszugeben.
Die Überprüfung mit dem Luhn-Algorithmus
Der Luhn-Algorithmus (auch modulus 10 oder mod 10-Algorithmus genannt) basiert auf dem ANSI-Vorschlag X4.13. Er wurde Ende 1960 von einer Gruppe Mathematiker ausgearbeitet und veröffentlicht. Danach nutzten Kreditkartenhersteller dieses Verfahren zur Prüfung der Kreditkartennummern. Auch die Versichertennummer in Kanada, die Canadian Social Insurance Number (CSIN), wird über das Luhn-Verfahren geprüft.
Der Algorithmus testet, ob die letzte Ziffer der Kreditkartennummer korrekt zu den angegebenen Zahlen passt. Die Testziffer wird von allen Ziffern außer der letzten Ziffer berechnet und anschließend mit der angegebenen Testziffer verglichen. Stimmt sie überein, ist die Karte seitens der Prüfnummer in Ordnung. Wir wollen das Verfahren hier nicht näher vertiefen, sondern einfach den Algorithmus angeben:
class LuhnTest
{
static boolean luhnTest( String s )
{
int len = s.length();
int digits[] = new int[len];
for ( int i = 0; i < len; i++ )
{
try {
digits[i] = Integer.parseInt( s.substring(i,i+1) );
}
catch ( NumberFormatException e ) {
System.err.println( e );
return false;
}
}
int sum=0;
while ( len > 0 )
{
sum += digits[len-1];
len--;
if ( len > 0 )
{
int digit = 2*digits[len-1];
sum += ( digit>9) ? digit-9 : digit;
len--;
}
}
return ( sum%10 == 0 );
}
static boolean isVisa( String s )
{
if ( ( (s.length() == 16) || (s.length() == 13) ) &&
(s.charAt(0) == '4') )
return luhnTest( s );
return false;
}
public static void main( String args[] )
{
System.out.println( luhnTest( "4111111111111111" ) );
System.out.println( luhnTest( "5500000000000004" ) );
System.out.println( luhnTest( "340000000000009" ) );
System.out.println( luhnTest( "30000000000004" ) );
System.out.println( luhnTest( "601100000000000" ) );
System.out.println( luhnTest( "201400000000009" ) );
System.out.println( luhnTest( "3088000000000009" ) );
System.out.println( luhnTest( "9238475098787444" ) );
System.out.println( isVisa( "4111111111111111" ) );
System.out.println( isVisa( "5500000000000004" ) );
// Böse: Visa-Nummer generieren
char[] c = "4123456789123456".toCharArray();
while ( !isVisa(new String(c)) )
c[(int)(Math.random()*c.length-1)+1] = (char)('0'+Math.random()*9.9);
System.out.println( c );
}
}
Im Quelltext ist eine zusätzliche Methode eingebaut, die testet, ob die Karte von Visa ist. Dazu müssen wir nur überprüfen, ob die erste Ziffer eine 4 und die gesamte Zahl nach dem Luhn-Verfahren gültig ist. Andere Tests sind genauso einfach durchzuführen. Eine mögliche Erweiterung wäre, die Methode fehlertoleranter zu gestalten, indem Trennzeichen herausgefiltert werden. Dies und die Implementierung der übrigen Tests überlasse ich als Übung den Lesern.
Wir beginnen mit einer vorgegebenen, unsinnigen Kartennummer, deren erste Stelle "4" ist, wie für eine Visa-Karte erforderlich. Anschließend ändern wir in einer Schleife eine zufällig ausgewählte Stelle der Kartennummer (außer der ersten) in eine ebenfalls zufällig bestimmte Ziffer aus dem Bereich "0" bis "9". Das wiederholen wir so lange, bis die abgewandelte Zahl irgendwann passt.
Beispiel Mit diesen Methoden ist es natürlich leicht möglich, Nummern zu erzeugen. Betrachten wir Folgendes:
char c[] = "4123456789123456".toCharArray();
while ( !isVisa(new String(c)) )
c[(int)(Math.random()*c.length-1)+1] = (char)('0'+Math.random()*9.9);
System.out.println( c );
JDBC LOBs (Large Objects)
Auf der Datenbankseite gibt es zwei Typen für besonders große Daten: BLOB und CLOB. »B« steht für Binary und »C« für Character, also steht ein SQL BLOB für beliebig große Binärdaten und ein CLOB für beliebig große Textdaten.
Die LOBs unterscheiden sich von den anderen Datentypen dadurch, dass der Treiber erst dann die Daten überträgt, wenn sie auch angesprochen werden: Wird eine Zeile mit einer Zahl übertragen, so wird der Treiber auch diese Zahl immer mitschicken. Bei den LOBs sieht das anders aus: Intern steckt dort eine Art Verweis (LOCATION), die mitgeschickt wird, aber nicht die Daten selbst. Durch die Kapselung im Treiber fällt das allerdings nicht auf.
Für den BLOB gibt es in JDBC die Schnittstelle Blob und für CLOB Clob. In beiden Fällen können die großen Daten erfragt, aktualisiert und angelegt werden. Außerdem lässt sich die Länge erfragen und lassen sich ab einer bestimmten Position Daten auslesen.
Einen BLOB besorgen
Einem BLOB/CLOB steht genau, wie es für andere Datentypen entsprechende getXXX()-Funktionen gibt, eine getBlob()/getClob()-Funktion zur Verfügung. Der Unterschied besteht nur darin, dass getInt() direkt ein int zurückgibt, während getBlob() nur eine Referenz auf ein Blob-Objekt liefert, über die im zweiten Schritt die Daten zu beziehen sind.
Beispiel: Eine Tabelle Persons weist eine Spalte Image auf, die eine Grafik einer Person speichert. Die Grafik ist in einer Spalte vom Typ BLOB.
ResultSet rs = stmt.executeQuery( "SELECT * FROM Persons" );
rs.first();
Blob data = rs.getBlob( "Image" );
data bezieht sich jetzt auf das Blob-Objekt. Es enthält noch nicht die Daten. Sie lassen sich zum Beispiel über die Methode data.getBinaryStream() beziehen. Damit lässt sich dieser InputStream toll im Konstruktor von ImageIcon() einsetzen, der aus den Daten dann gleich eine Swing-taugliche Grafik konstruiert. Teile des Datenfeldes werden mit byte[] getBytes(start, ende) angefordert. data.length() liefert die Anzahl der Elemente des LOBs.