Regionsspezifische Zeichen unter Java ermitteln

Auf der Welt existieren eine Reihe von Konventionen rund um das Thema Formatierung von Zahlen und anderen Werten. So existieren z.B. unterschiedlichste Reglungen für Dezimaltrennzeichen. Unter Java lassen sich diese dank der entsprechenden Unterstützung problemlos einbinden. So können Werte anhand der Locale, welche eine definierte Region repräsentiert, einfach formatiert werden:

double value = 42.2324;
String format = "#.00";

DecimalFormat decimalFormat = new DecimalFormat(format, DecimalFormatSymbols.getInstance(Locale.GERMANY));
String formattedString = decimalFormat.format(value);

System.out.println(formattedString);

In diesem Beispiel wird der Wert 42.2324 anhand der Locale und einer Formatierung in den Wert 42,23 konvertiert. Manchmal ist es allerdings notwendig Informationen über die einzelnen Trennzeichen und ähnliches zu erlangen. Auch diese Informationen können unter Java ermittelt werden:

Locale locale = new Locale("en", "GB");
DecimalFormat format = (DecimalFormat) DecimalFormat.getInstance(locale);

char decimalSeparator = format.getDecimalFormatSymbols().getDecimalSeparator();
char groupingSeparator = format.getDecimalFormatSymbols().getGroupingSeparator();
Currency currency = format.getDecimalFormatSymbols().getCurrency();

Hier wird eine Locale angelegt und mit dieser eine Instanz der Klasse DecimalFormat erzeugt. Anschließend kann diese Instanz genutzt werden, um die regionsspezifischen Zeichen wie z.B. den Dezimalseparator zu ermitteln.

Freier Java-Decompiler

In jeder besseren Entwicklungsumgebung für Java ist ein Decompiler integriert. Daneben existieren auch Standalone-Decompiler wie der Java Decompiler. Der Decompiler ermöglicht es den Quelltext aus dem Kompilat wieder herzustellen. Dies funktioniert bei Java relativ gut, da dort der Quelltext nur in Bytecode für die JVM umgewandelt wird. Dieser Bytecode ist noch relativ High-Level, so das die Rückumwandlung einfacher vonstattengeht. Neben dem Standalone-GUI, existieren Plugins für Eclipse und IntelliJ IDEA.

Die grafische Oberfläche des Decompiler

Zu finden ist das Projekt unter java-decompiler.github.io, der entsprechenden Quelltext befindet sich ebenfalls auf GitHub. Lizenziert ist der Decompiler unter der GPL in der Version 3 und damit freie Software.

Vergleichen von Fließkommawerten unter Java

Einen Integerwert unter Java zu vergleichen ist trivial. Man nehme zwei Variablen vom Typ Integer und vergleiche sie, unter Zuhilfenahme des entsprechenden Operators, miteinander:

int a = 7;
int b = 7;

boolean equal = a == b;

Am Ende ist der Wert in der Variable equal true, da die Werte in der Variable a und b identisch sind. Nun könnte das Ganze bei Fließkommazahlen ebenso funktionieren:

double a = 7;
double b = 7;

boolean equal = a == b;

Auch in diesem Fall ist das Ergebnis am Ende true. Trotzdem sollten Fließkommazahlen niemals auf diese Weise verglichen werden. Spätestens bei folgendem Beispiel fällt dies auf:

double a = 4.1 + 0.8;
double b = 5.7 - 0.8;

boolean equal = a == b;

Obwohl beide Rechnungen eigentlich 5,9 ergeben sollten, wird in der Variable equal der Wert false enthalten sein. Hintergrund hierfür ist die Speicherung der Fließkommazahlen. Diese ist im Standard IEEE 754 festgelegt. Bei dieser Darstellung besteht die Zahl aus einem Vorzeichen, einem Exponent und einer Mantisse. Bei der Umwandlung der Dezimalzahl in ihre binäre Darstellung und der Verrechnung kann es zu Ungenauigkeiten (z.B. bei der Rundung im Prozessor) kommen. Wenn diese Werte nun binär verglichen werden, sind sie nicht identisch. Statt durch direkten Vergleich, sollte die “Gleichheit” durch eine Differenzrechnung ermittelt werden:

public static boolean compare(double a, double b) {
    return Math.abs(a - b) < 0.0001; // Diff smaller epsilon?
}

Nachdem die Differenz der beiden Werte gebildet wurde, wird überprüft ob diese kleiner als Epsilon sind. Epsilon stellt den Threshold bzw. eine Schwelle oder Toleranz dar. Solange die Differenz unter diesem Wert liegt, werden beide Werte als identisch angenommen. Natürlich fehlt in dieser Methode noch eine Absicherung gegenüber NaN-Werten.

Statt sich eine solche Methode selber zu schreiben, können Methoden aus bekannten Utility-Bibliotheken wie Guava (DoubleMath.fuzzyCompare) genutzt werden.

Java-Bibliothek für das Minecraft-Protokoll

Wenn sich der Minecraft-Client mit dem entsprechenden Server verbindet, so kommunizieren diese über ein festgelegtes Protokoll. Mit einer eigenen Implementation dieses Protokolls ist es möglich sich mit einem Minecraft-Server zu verbinden und entsprechende Aktionen durchzuführen. Zum Beispiel könnte diese Möglichkeit genutzt werden um einen Bot für Minecraft zu schreiben. Eine solche Implementation des Minecraft-Protokolls ist die Java-Bibliothek MCProtocolLib von Steven Smith.

Central City auf meinem eigenen Minecraft-Server

Ein minimales Beispiel, für den Login auf dem Server (basierend auf einem Unit-Test der Bibliothek), mit besagter Bibliothek könnte dabei wie folgt aussehen:

public class SimpleBot {

    private static final String HOST = "example.org";
    private static final int PORT = 25565;
    private static final Proxy PROXY = Proxy.NO_PROXY;
    private static final Proxy AUTH_PROXY = Proxy.NO_PROXY;
    private static final String USERNAME = "user@example.org;
    private static final String PASSWORD = "password";

    public static void main(String[] args) throws FileNotFoundException, RequestException {

        MinecraftProtocol protocol = new MinecraftProtocol(USERNAME, PASSWORD);
        Client client = new Client(HOST, PORT, protocol, new TcpSessionFactory(PROXY));
        client.getSession().setFlag(MinecraftConstants.AUTH_PROXY_KEY, AUTH_PROXY);

        client.getSession().addListener(new SessionAdapter() {
            @Override
            public void packetReceived(PacketReceivedEvent event) {
                if(event.getPacket() instanceof ServerJoinGamePacket) {
                    event.getSession().send(new ClientChatPacket("Hello, World!"));
                } else if(event.getPacket() instanceof ServerChatPacket) {
                    Message message = event.getPacket().getMessage();
                    System.out.println("Received Message: " + message.getFullText());
                    if(message instanceof TranslationMessage) {
                        System.out.println("Received Translation Components: " + Arrays.toString(((TranslationMessage) message).getTranslationParams()));
                    }

                    event.getSession().disconnect("Finished");
                }
            }

            @Override
            public void disconnected(DisconnectedEvent event) {
                System.out.println("Disconnected: " + Message.fromString(event.getReason()).getFullText());
                if(event.getCause() != null) {
                    event.getCause().printStackTrace();
                }
            }
        });

        client.getSession().connect();
    }
}

In diesem Beispiel wird sich mit dem Server verbunden und nach erfolgreicher Verbindung eine Chatnachricht gesendet. Danach loggt sich der Bot wieder aus. Der Quelltext der Bibliothek ist auf GitHub zu finden. Das Projekt ist unter der MIT-Lizenz lizenziert und damit freie Software.

Refactoring von Zeichenketten unter IntelliJ IDEA unterbinden

Die Java-IDE IntelliJ IDEA verfügt über eine Reihe von Refactoring-Methoden. So können Variablen umbenannt, Methoden extrahiert und vieles mehr mit Hilfe der Refactoring-Werkzeuge bewerkstelligt werden. Manchmal schießt die IDE beim Refactoring über das Ziel hinaus. So kann es passieren, das bei der Umbenennung einer Variable nicht nur diese, sondern auch Zeichenketten mit dem gleichen Namen umbenannt werden.

Der Rename-Dialog der IDE

Verantwortlich hierfür ist eine optionale Funktionalität in der Rename-Funktionalität. Um diese zu deaktivieren muss der Rename-Dialog geöffnet werden. Dies geschieht indem die Tastenkombinationen Shift + F6 knapp eine habe Sekunde gedrückt wird, bis der entsprechende Dialog erscheint. In diesem Dialog muss nun die Checkbox mit der Beschreibung Search in comments and strings deaktiviert werden. Anschließend werden nur noch die gewünschten Strukturen im Quellcode umbenannt, ohne dass sich die Umbenennung auf weitere Zeichenketten auswirkt.