Java Getriebe

Java und NetBeans

try-with-resources dekompiliert

Durch einen ungünstigen Zwischenfall war ich gestern gezwungen die Arbeit des Tages durch dekompilieren noch vorhandener .class-Dateien zu retten. Nicht alle aktuellen Tools zum dekompilieren sind momentan in der Lage mit den Spracherweiterungen von Java 8 umzugehen. Mit CFR habe ich aber dennoch eine funktionierende Version des Tagewerks wiederherstellen können. Zumindest funktionieren die wiederhergestellten Tests.

Interessant fand ich eine Methode, die ich in der Form nicht in Erinnerung hatte geschrieben zu haben: Recht lang und dirverse geschachtelte try-catch-finally Blöcke. Read More

Simple XML/HTML Tag Writer

Eine einfache „Builder“-Klasse, mit der sich vergleichsweise leicht XML/HTML Tags erstellen lassen. Ich brauche die Klasse immer mal wieder, wenn ich einzelne HTML-Ausgaben erstellen muss (hier aus einem „@todo„-Taglet)

1
2
3
4
5
6
7
8
9
10
11
    String dt = new TagWriter("dt")
        .setAttribute("class", "simpleTagLabel")
        .setRawContent(new TagWriter("span")
            .setAttribute("style", "background-color:orange;padding:.5em;")
            .setContent("Zu Erledigen:")
            .toString())
        .toString();
    String dd = new TagWriter("dd")
        .setContent(tag.text())
        .toString();
    return dt + dd;

Die Klasse kommt auch mit „leeren“ Tag zurecht und schließt diese korrekt.
Read More

Streams und Maps

Es gibt gefühlt hunderte Seiten, die sich mit der Streams API aus Java 8 befassen. Alle haben furchtbar tolle Tipps und vor allem Beispiele, wie man die Daten aus seinen Listen über Streams und den unausweichlichen Lambdas verarbeiten, filter und umwandeln kann. In unserer (gewachsenen) Anwendung verwenden wir allerdings weniger oft Listen und Sets als viel mehr etliches an Maps. Der Großteil unserer Daten sind als Schlüssel-Wert Paare gespeichert. Wenig ist eine „beliebige“ Datenmenge sondern explizit benannte Daten.

Die Stream sind allerdings auf einzeln aufgereihte Objekte ausgelegt und nicht auf Name-Value-Datenpaare. Dies macht das Handling innerhalb der Streams etwas unhandlich, vor allem, wenn man zwar durch die Values iterieren möchte, diese auch gerne per map() in andere Datentypen umwandeln möchte, aber der Schlüssel darf dabei nicht verloren gehen, da dieser unbedingt mit den zugehörigen Daten bzw. Objekten verknüpft bleiben sollte oder gar muss.

Für die Verarbeitung dieser Paare innerhalb der Streams bedeutet dies meist, dass man irgendwie mit Map.Entry<> 1 hantiert. Diese sollte man in der Regel allerdings als Immutable betrachten, so dass man bei jeder Änderung von Key oder (häufiger) Value eine neue Instanz erstellen muss. Leserlicher wird der Code dadurch selten.

Mein Ansatz für einen etwas leserlicheren Code ist der folgende:

1
2
3
4
5
6
7
8
9
10
11
12
13
public static <K, V> Map.Entry<K, V> toEntry(K key, V value) {
  return new AbstractMap.SimpleImmutableEntry<>(key, value);
}

public static <K, V, M> Map.Entry<M, V> mapKey(
        Map.Entry<K, V> org, Function<K, M> mapper) {
  return toEntry(mapper.apply(org.getKey()), org.getValue());
}

public static <K, V, M> Map.Entry<K, M> mapValue(
        Map.Entry<K, V> org, Function<V, M> mapper) {
  return toEntry(org.getKey(), mapper.apply(org.getValue()));
}

Damit lässt sich der Quelltext mit den Streams deutlich kürzer schreiben und enthält keine new Aufrufe die in diesem Zusammenhang nur den Lesefluss stören.

Ein Beispiel

In „parameters“ ist eine Menge von „Parameter“ Objekten die jeweils ein „Name“-„Wert“ Paar darstellen. Für eine Fremdbibliothek müssen unsere Namen in „deren“ Namen umgewandelt werden und dann diese neuen Schlüssel mit allen nicht-null Werten von „Parameter“ in eine bestehende Map „data“ (die bereits andere Daten enthält) geschrieben werden.

1
2
3
4
5
6
7
8
9
10
11
parameters.stream()
    // get all Mapping (m) to all Parameter (p)
    .map(p -> toEntry(p, mappingManager.get(p)))
    .filter(pm -> pm.getValue() != null)
    .map(pm -> mapValue(vm, m -> m.getKey()))
    .map(pk -> mapKey(pk, p -> p.getValue()))
    .filter(vk -> vk.getKey() != null)
    // write all Values (v) with the mapped keys (k) to data
    .collect(() -> data,
        (map, vk) -> map.put(vk.getValue(), vk.getKey()),
        Map::putAll);

ps: Ich will nicht behaupten, dass das Beispiel guter Code ist. Er soll nur zeigen, dass die Hilfsmethoden die Übersichtlichkeit beim Arbeiten mit Key-Value-Paaren erhöht.

Is An Agile Java Standard Possible? – Simon Ritter

Java Champion und JUG Leader der JUG London, Simon Ritter hat in einem Blogpost auf „DZone Java“ über die Zukunft der Entwicklung von Java und seine Plattform geschrieben. In Is An Agile Java Standard Possible? schreibt er davon, dass es denkbar sei, dass nicht mehr bis zu einem „Major Release“ der Java Plattform gewartet wird bis ein „JDK Enhancement Proposal“ (JEP) für jederman nutzbar wird, sondern dass diese JEPs bereits vorher schon in die aktuelle JDK eingepflegt werden.

Even programming languages need to adapt to the more modern methodologies. See how Java, through JEPs, is making the change and what challenges lie ahead.

Read More

NetBeans will ein Apache Projekt werden

Quelle: NetBeansProposal – Incubator Wiki

Im Incubator Wiki der Apache Foundation steht seit heute morgen ein Antrag von Oracle um das Projekt NetBeans komplett unter der Schirmherrschaft der Foundation zu stellen.

Es stehen in der Liste der Unterstützer neben diversen Mitgliedern des NetBeans DreamTeams auch etliche Mitarbeiter von Oracle auf der Liste. Somit wird der aktuelle Besitzer wohl auch weiterhin der der NetBeans IDE und Plattform weiter arbeiten.

Spannend ist dies auf jeden Fall. Vor allem auf so kurz vor der JavaOne Konferenz. Über die Gründe von Oracle lässt sich nur spekulieren, aber ich persönlich hoffen, dass die NetBeans IDE auch weiterhin die Referenz-IDE für neue Java Versionen bleibt.

Nicht lokalisierte Zeichenketten finden in NetBeans Anwendungen

Jeder Anwendungsentwickler wird früher oder (selten) später auf das Thema der Mehrsprachigkeit stoßen. Java bietet mit den ResouceBundle Konzept dankenswerterweise eine sehr einfach Lösung hierzu an. Tools wie OmegaT erlauben eine einfache Übersetzung dieser Properties-Dateien so dass auch größere Anwendungen mit vergleichsweise geringem Aufwand zu übersetzen sind.

Die entscheidende Voraussetzung für eine Übersetzung ist allerdings, dass die Zeichenketten in der Anwendung nicht „hardcodiert“ im Quelltext stehen, sondern schön über die Klasse ResouceBundle aus den Propertiesdateien gelesen werden. Die Suche nach diesen „hardcodierten“ String kann gerade in komplexen und modularen Anwendungen wie einer NetBeans Rich Client Anwendung recht mühsam sein.
Read More

Korrekte Icongröße bei ProjectSensitiveActions

Mit einer ProjectSensitiveAction kann man wunderbar eine Action erzeugen, die zu einem bestimmten Projekt ausgeführt werden soll. Das schöne daran ist. dass diese Action auch dann funktionieren, wenn im Projektbaum ein Unterknoten des Projektes ausgewählt ist.

Leider hat die Utilities-Methode projectSensitiveAction den Haken, dass hier explizit ein Icon angegeben werden muss statt einer iconBase. Dies führt dazu, dass für eine solche Action in der Toolbar und im Menü nur ein einzelnes Icon anzeigt wird. Also ist das Icon entweder in der Toolbar zu klein oder im Menü zu groß. Auch die Funktion, dass man die Icongröße in der Toolbar verändern kann, kann nicht sinnvoll genutzt werden.

Bei der Suche durch die NetBeans Quellen ist mir dann aufgefallen, dass für die Buttons in der Toolbar nach einem Action-Attribut "iconBase" gesucht wird um die korrekte Größe für das Icon zu bestimmen. Setzt man dieses Attribut bei der generierten ProjectSensitiveAction, so wird auch dessen Icon korrekt in der Toolbar angezeigt:

1
2
3
4
5
ImageIcon menuIcon = ImageUtilities.loadImageIcon(iconPath, true);
Action action = ProjectSensitiveActions.projectSensitiveAction(
    performer, displayName, menuIcon);
//Workaround um in Toolbar die korrekte Groesse anzuzeigen.
action.putValue("iconBase", iconPath);

menuIcon ist dabei das „Grund-Icon“ in der Größe 16×16 Pixel, wie man es von den Actions in NetBeans gewohnt ist. Dessen iconPath ist der Resourcenpfad, welcher auch für die "iconBase" genutzt werden kann.