Ant e le espressioni regolari

Mentre oggi ero intento a generare un po’ di web services tramite axis2 il mio capo mi chiede di fare un lavoretto per conto di altre persone in azienda.

In pratica chiedono di modificare all’incirca 100.000 file di testo in formato XML, di 2kb, modificando il testo all’interno di un tag. La modifica è uguale per tutti: il testo da modificare in pratica contiene un path ad un file pdf e si  tratta aggiungere alla fine del path l’estensione “.pdf”.

Mi sono detto, siccome sto studiando Ant, perchè non farlo con questo ?

La cosa buona di Ant è che si puo’ installare ovunque ci sia una java virtual machine, fino ad ora avevo usato i miei script bash ma il mio capo mi disse sarebbe bello poterli far girare su Windows cosi’ ho preso a studiarmi Ant che non credo (fino ad ora) sia potente come la bash ma scopro di giorno in giorno le sue potenzialità. Qualcuno dirà che anche il perl e il python possono essere usati ma ho preferito questo.

In realtà Ant è una applicazione java e i suoi script sono file xml. I tag di questi file corrispondono a relative classi java permettendo quindi la loro estensione.

Ant può copiare e spostare file e directory, creare jar e war file per il deploy, calcolare l’md5 dei file, agire su variabili da usare come condizioni, collegarsi ad un CVS, creare test con Junit e tra le altre cose lavorare con le espressioni regolari.

Se vi serve una introduzione ad Ant, che vi collega al mio precedente articolo sull’integrazione continua, la redazione di Mokabyte ne scrisse una interessante:  eccovela !

Una volta installato (estratto) Ant ho configurato le variabili di ambiente ANT_HOME che punta alla cartella estratta e PATH che punta alla sottocartella bin.

Ho poi creato il file “build.xml” e l’ho messo insieme a file da modificare; il fatto di metterlo nella stessa cartella non è strettamente necessario perchè poi gli do la cartella di lavoro come parametro.

Ed eccovi il file:

<?xml version="1.0" encoding="UTF-8"?>
<project name="program" default="convert" basedir=".">
<property name="folder" value="." />
<property name="backup_folder" value="backup" />
<property name="file_extension" value="inf" />
<property name="tag" value="PrintFileName" />
<property name="extension" value="pdf" />

<target name="backup" description="backup folder">
<mkdir dir="${folder}/${backup_folder}" />
<copy todir="${folder}/${backup_folder}" >
<fileset file="${folder}"/**.*${file_extension}" />
</copy>
</target>

<target name="del_extension">
<replaceregexp
  match="<${tag}>(.*).${extension}<\/${tag}>"
  replace="<${tag}>\1<\/${tag}>">
<fileset dir="${folder}" includes="*.${file_extension}" />
</replaceregexp>
</target>

<target name="add_extension">
<replaceregexp
  match="<${tag}>[^(\.${extension})]<\/${tag}>"
  replace="<${tag}>\1.${extension}<\/${tag}>">
<fileset dir="${folder}" includes="*.${file_extension}" />
</replaceregexp>
</target>

<target name="convert" description="open files and add an extension to the text inside the selected tag">
<antcall target="backup" />
<antcall target="add_extension" />
</target>

</project>

Intestazione XML a parte, nella seconda riga dichiaro che il target (funzione o metodo) da richiamare di default è convert. All’interno di questo target richiamo dapprima il target backup che realizza il backup della directory di lavoro e poi richiamo add_extension per modificare i file xml.

I parametri di default quali la cartella di lavoro (folder), la cartella di backup (backup_folder), l’estensione dei file che vado a modificare (file_extension), il tag xml che seleziono (tag) e l’estensione che aggiungo nel path (extension) sono definiti all’inizio del file e possono anche essere passati come parametro ad Ant.

Ad esempio se do:

ant -Dfolder=./test

Indico che la cartella dei file da modificare è la sottodirectory test, attenzione che se nel path ci sono degli spazi dovete usare le virgolette ” ” prima e dopo il path.

Il target backup crea quindi una cartella (mkdir) di backup dentro alla cartella di lavoro e copia in essa (copy) i file dalla cartella di lavoro con l’estensione che ho indicato (fileset). Se non volete fare il backup potete semplicemente rimuovere il suo richiamo nel target convert.

Andiamo alla parte delicata, come avete notato il target del_extension non viene richiamato, me lo sono lasciato per convienenza e vi permette di capire più facilmente il target add_extension.

Con il target del_extension si vuole selezionare tutti i path con il tag selezionato che già contengono la stringa “.pdf” e rimuoverla. Per fare ciò usiamo il task replaceregexp che ha 2 attributi: match e replace.

Con l’attributo match selezionamo il tag XML, di default PrintFileName, che contiene la stringa mentre con il tag replace sostituiamo la stringa trovata. I caratteri < e > mi servono per sostituire < e > in quanto non vengono accettati direttamente, poi vedete la variabile ${tag} che viene aperta e chiusa (usando \/ in quanto il simbolo / è particolare) e all’interno l’espressione (.*) che indica tutti i caratteri con a seguire l’estensione .${extension}.

Quello che vogliamo fare è sostituire tutti i caratteri trovati nel mezzo togliendo l’estensione, il risultato della stringa trovata in match con i caratteri (.*) è indicata con \1.

Applico infine questa trasformazione all’insieme di file tramite il task fileset.

Il target add_extension dovrebbe funzionare al contrario se non che può capitare che alcuni path finiscano già per “.pdf” e quindi corriamo il rischio di aggiungerli nuovamente perciò nel mezzo è stato aggiunta l’espressione [^(\.${extension})] che indica di escludere tali path.

Semplice no ?

Ora non sono un esperto di espressioni regolari, me le sono giuste viste un pò, ma la cosa mi ha fatto apprezzare di più Ant.