WiX: Escape echo

CustomAction führt ein Kommando aus, und das ist bspw. ein Shell-Befehl. In diesem Fall ein echo in eine Datei:

Will man als Teil von “foobar” jetzt Sonderzeichen mit ausgeben, muss man XML-, WiX-/MSI-, und CMD-Escaping kombinieren – oder man hält sich einfach eine Waffe an den Kopf, das geht schneller:

  • " gibt " aus, & ein & – so weit, so klar. Wenn man davon absieht, dass & eigentlich auch ein CMD-Steuerzeichen ist 🤷🏼‍♂️
  • ^&lt; gibt ein < aus, ^&gt; ein > (man beachte die ^, via)
  • [\[] gibt [ aus, [\]] ein ] (via)

AS3: Lesbarer “link-report”

Mit dem mxmlc lassen sich “Link-Reports” erstellen; entfernt vergleichbar mit dem “Größenbericht” aus der Flash-IDE: Welche Klassen sind in meiner SWF enthalten, und wie viel Platz belegen sie. Die Compileroption dazu (in Ant-Schreibweise aus FDT):

Allerdings sind diese Linkreports schlecht lesbar. Ich möchte auf einen Blick sehen, welche Klasse der Grund dafür ist, dass mein SWF 2 MB statt 500 KB groß ist. Gut, dass es Theo Hultberg gibt: Der hat eine XSL geschrieben, um den Linkreport in eine verständliche Form zu bringen (hier der Blogeintrag und die lokale Kopie der XSL). Ant kann XSL von haus aus:

Das Ergebnis kann sich sehen lassen: Nach Größe sortiert und mit Verlinkung auf die Klasse/SWC

linkreport

Apache Ant: Ja/nein-Dialog

Apache kennt von haus aus nur Input Dialoge (korrigiert mich), und damit kann man natürlich “j”/”n”-Eingaben bauen (siehe Examples).  Schöner ist aber ein richtiger Ja/Nein-Dialog, wie Javascripts confirm. Außerdem gibt es if/else nur mit Ant contrib, das nicht zwingend überall installiert ist. Bitte nennt mir eine schönere Lösung als die folgende, aber damit geht’s – Confirm-Dialoge und if/else-Verzweigung abhängig von der User-Eingabe:

Die Targets “setURLs” und “setURLsAndProxy” werden nur abhängig von ${useProxy} ausgeführt, bzw nicht ausgeführt (if/unless).

Apache Ant: replaceregexp und HTML

Wer mit Ants replaceregexp HTML suchen und ersetzen möchte, wird recht schnell in Fehler wie

  1. Wert des Attributs “match”, das mit Elementtyp “null” verknüpft ist, darf nicht das Zeichen “<” enthalten.
  2. Öffnendes Anführungszeichen wird für Attribut “{1}” erwartet, das mit Elementtyp “id” verknüpft ist.
  3. Auf Elementtyp “replaceregexp” müssen entweder Attributspezifikationen, “>” oder “/>” folgen.

bzw deren englischer Entsprechung

  1. The value of attribute “match” associated with an element type “null” must not contain the ‘<‘ character.
  2. Open quote is expected for attribute “{1}” associated with an element type “id”.
  3. Element type “replaceregexp” must be followed by either attribute specifications, “>” or “/>”.

stoßen. Lösung:

  1. alle “<” durch &lt; ersetzen. Mit “” escapen genügt nicht.
  2. Geöffnete Anführungszeichen müssen geschlossen werden m(
  3. Alle Anführungszeichen durch “&quot;” ersetzen. Mit “” escapen genügt nicht.

PS:

  • Um mehrere Zeilen gleichzeitig zu ersetzen, muss man – etwas uneingängig – das Flag “s” setzen.
  • Um (potentiell) verschachtelte Tags (z.B. DIV) zu ersetzen, immer nicht-“greedy” arbeiten, also z.B. mit (.*?) statt mit (.*)

net.sf.json.xml.XMLSerializer und eckige Klammern

Es gibt einen extrem hartnäckigen Bug in net.sf.json.xml.XMLSerializer: Ein XML-Knoten

wird nicht zum String

sondern zum leeren Array

Jetzt könnte man vermuten, dass CDATA hilft, oder HTML-Sonderzeichen, oder Escapen, oder oder oder. Alles falsch. Der Serializer ist so “klug”, dass er alles trimt optimiert und ersetzt, bis er wieder den ursprünglichen String findet, und der Bug zuschlägt. Das meine ich mit “hartnäckig 🙂

Einzige Lösung, die mir eingefallen ist (Verbesserungen werden gerne gesehen!):

PS: Das ist deshalb geringfügig weniger dirty, als es aussieht, weil spitze Klammern Teil des Suchstrings sind. Spitze nicht-escapte Klammern dürfen in (validen) XMLs nur Teil der Struktur sein, deshalb werden hier tatsächlich nur Knoteninhalte, die ausschließlich “[]” lauten, ersetzt.

PPS: Ein ähnliches Problem gibt es mit

aus dem ein Object wird. Ich ersetze es deshalb einfach analog, siehe oben.

Liferay: Portlet Lokalisierung [UPDATE]

Laut Liferay-Wiki legt man im Classpath (dort ist es explizit ein Package …/com/my/portlets/p1/…) eine Datei “Language.properties”, plus für jede “foreign language” (=Locale) ein “Language_*.properties” (mit *=locale). Das kann ich so nicht bestätigen: Meine Dateien müssen direkt in classes liegen, also ohne Package. Andernfalls bekomme ich ein

java.util.MissingResourceException: Can’t find bundle for base name Language, locale de_DE

Auf Dateien direkt in classes konnte ich per

zugreifen. Groß-/Kleinschreibung oder der ClassLoader waren bei mir nicht das Problem. Vermutlich muss ich beim Laden das Package mit angeben… allerdings sagt das Wiki das so nicht, und ich bin gerade zu faul 🙂

Achtung: Prinzipiell gibt es eine Fallback-Datei, d.h. wenn ich eine Language.properties und eine Language_de.properties habe, ist Language.properties der Fallback. Aber: Der Fallback wird auf Basis des Locale-Defaults ausgewählt! Sprich: Für Language.properties und Language_de.properties ist auf deutschen Rechnern Language_de.properties der Fallback! Das zu vermeiden, bzw. das auf allen Rechnern einheitlich zu haben, schafft man mit einem

Interessanterweise muss ich die Language.properties auch nicht in der portlet.xml angeben!? Das Wiki spricht von <supported-locale> und <resource-bundle>; bei mir geht es ohne. Vielleicht mache ich hier einfach noch was falsch. Anyway, kurz zum Aufbau der portlet.xml: Wer einen

14:05:30,735 ERROR [PortletLocalServiceImpl:704] com.liferay.portal.kernel.xml.DocumentException: Error on line 12 of document : cvc-complex-type.2.4.a: Invalid content was found starting with element ‘supported-locale’. One of ‘{…}’ is expected.

bekommt, der möge auf die Reihenfolge der Knoten achten – so einfach kann’s manchmal sein 🙂 Zum Beispiel muss <supported-locale> vor <resource-bundle> deklariert werden. Für mich funktioniert die Reihenfolge

AS3: Eingebettete XML

Wer Fehler a la

identifier vor xmltagstartend erforderlich.

oder anderen kryptischen Kram bekommt, sollte die XML so einbinden:

Also: mimeType beachten, und dass die eingebettete Klasse nicht direkt auf XML gecastet wird. Außerdem ignoreWhiteSpace, das’ ja klar 🙂

AS3/E4X: Rekursiv alle Unterknoten mit einem Attribut finden

Nerd-Porn:

Liefert das Attribut “myAttribute” des Unterknotens (nicht begrenzt auf direkte Kind-Knoten), dessen Attribut myID gleich “abc” ist. Man beachte: Den doppelten Punkt hinter “xml”, sowie den Sternchen-Selektor. Quelle, man beachte dort auch die unten verlinkten weiterführenden Artikel.

UPDATE: Eine Struktur wie die Folgende

kann man so durchsuchen:

text:

slide.text: