Java Getriebe

Java und NetBeans

Ant: Manifest Dateien korrekt als Properties einlesen

Der Ant Task <Property> ist nicht nur in der Lage Java Properties Dateien einzulesen und somit die einzelnen Variablen verfügbar zu machen, sondern auch manifest.mf Dateien, die für Jar-Archivdateien benötigt werden. Das liegt aber nicht unbedingt am Task selbst, sondern viel mehr an der Klasse java.util.Properties und dessen Fähigkeit neben dem Gleichheitszeiche = auch den Doppelpunkt und ein Leerzeichen als Trenner zwischen Schlüssel und Wert erkennt :

The key contains all of the characters in the line starting with the first non-white space character and up to, but not including, the first unescaped ‚=‘, ‚:‘, or white space character other than a line terminator. All of these key termination characters may be included in the key by escaping them with a preceding backslash character;

Leider gibt es in der Spezifikation der Manifestdatei einen kleinen Passus:

Line length: No line may be longer than 72 bytes (not characters), in its UTF8-encoded form. If a value would make the initial line longer than this, it should be continued on extra lines (each starting with a single SPACE).

Diese Einschränkung steht im Gegensatz zur Properties Spezifikation in der Folgezeilen durch einen Backslash \ am Ende der vorherigen Zeile gekennzeichnet werden. Der <Property> kann also nicht einfach so jede Manifestdatei einlesen, ohne dass es zu Problemen führen würde. Ein Beispiel:

Manifest-Version: 1.0
Ant-Version: Apache Ant 1.8.3
Created-By: 1.7.0_01-b08 (Oracle Corporation)
OpenIDE-Module: de.rwthaachen.wzl.gt.platform.manual
OpenIDE-Module-Implementation-Version: 11711
OpenIDE-Module-Layer: de/rwthaachen/wzl/gt/platform/manual/layer.xml
OpenIDE-Module-Localizing-Bundle: de/rwthaachen/wzl/gt/platform/manual
 /Bundle.properties
OpenIDE-Module-Requires: org.netbeans.api.javahelp.Help

Der Wert von OpenIDE-Module-Localizing-Bundle ist über zwei Zeilen verteilt und würde vom <Property> Task nur den Wert de/rwthaachen/wzl/gt/platform/manual zugewiesen bekommen.

Abhilfe schafft hier nur der Task <LoadProperties> mit seiner Fähigkeit einen so genannten „FilterChain“ definieren zu können. Dem Filter replaceregex kann man einen regulären Ausdruck mitgeben, der die Folgezeilen einer Manifestdatei zuverlässig erkennen und diese auch gleichzeitig an die vorherige Zeile anhängen kann.

1
2
3
4
5
6
7
8
<loadproperties srcfile="manifest.mf">
  <filterchain>
    <replaceregex pattern="[\n\r]+ ([^ ][^\n\r]*)"
                  replace="\1"
                  flags="g"
                  byline="false"/>
  </filterchain>
</loadproperties>

Das Suchmuster in pattern sucht nach einem einzelnen Leerzeichen nach einer Zeilenschaltung. Der Rest dieser Zeile wird in der 1. Gruppe gespeichert und als „Ersatz“ für den gesamten gefundenen String (also auch der Zeilenschaltung vor dem Leerzeichen!) verwendet. Dadurch wird die Folgezeile automatisch der vorherigen angehängt.

Der Parameter byline="false" sorgt dafür, dass die Datei nicht Zeilenweise ausgewertet wird. Ansonsten könnten wir die Zeilenschaltungen nicht finden. Das Flag flags="g" sorgt dafür, dass nicht nur der erste Treffer in der Manifest Datei ausgetauscht wird, sondern alle Vorkommnisse. Nebenbei werden so auch mehrfache Folgezeilen korrekt verarbeitet.

Mit diesem Aufruf sind die Schlüsseldaten der Manifestdatei als Properties innerhalb des Ant-Skripts verfügbar. Zu bedenken ist, dass die Abschnitte einer Manifest-Datei nicht korrekt verarbeitet werden. Es ist nicht definiert (bzw. ich kann es nicht genau sagen) welcher Eintrag als Property übrig bleibt, wenn in mehreren Abschnitten der gleiche Schlüssel existiert (was in einer Manifest durchaus erlaubt ist).

Außerdem empfehle ich das Attribut prefix bei <LoadProperties> mit anzugeben um eventuelle Konflikte mit bereits bestehenden Properties zu vermeiden.

Makro

Zu guter Letzt kann man diesen Aufruf auch noch als Makro definieren um nicht überall den regulären Ausdruck hinschreiben zu müssen:

<macrodef name="readmf">
  <attribute name="file" default="manifest.mf"/>
  <attribute name="prefix"/>
  <sequential>
    <loadproperties srcfile="@{file}" prefix="@{prefix}">
      <filterchain>
        <replaceregex pattern="[\n\r]+ ([^ ][^\n\r]*)"
                      replace="\1"
                      flags="g"
                      byline="false"/>
      </filterchain>
    </loadproperties>
  </sequential>
</macrodef>

Damit sieht dann der Aufruf schön übersichtlich aus:

<readmf file="${path.to.manifest.mf}" prefix="mein-mf-prefix"/>

One Response to Ant: Manifest Dateien korrekt als Properties einlesen

  1. Thomas says:

    Hallo Jens,
    danke für den Ant-Tip zum Einlesen der Manifest Dateien! :->
    Im regulären Ausdruck scheint mir der [^ ] Ausdruck überflüssig zu sein. Er verhindert z.B. das Attributwerte die vor einem Space in der Manifest Datei umgebrochen werden korrekt geparst werden.

    Z.B. für:

    1
    2
    wert=1, 2, 3,
      4, 5, 6

    wird mit <replaceregex pattern="[\n\r]+ ([^ ][^\n\r]*)"

    1
    2
    wert=1, 2, 3,
    4=5,6

    Mit <replaceregex pattern="[\n\r]+ ([^\n\r]*)" funktionierte es besser.

    Gruß,

    Thomas