In questo articolo Java presenterò il componente software FileFactory che automatizza le più comuni operazioni di I/O su file system e che facilita quindi il compito dello sviluppatore Java.
Premesse
La classe FileFactory nasce in ambito lavorativo dall'esigenza di dover supportare la generazione di files complessi utilizzando come input files di template.
In particolare, per ogni template in input deve essere generato lo stesso output, ma con alcune parti, conosciute a priori e dette anche tokens, differenti perché sostituite alle originali.
Per agevolare la programmazione e garantire il corretto accesso al file system e lo svolgimento di tutte le operazioni necessarie, la classe espone un set di metodi pubblici e statici grazie ai quali è possibile:
- referenziare una risorsa esistente, o crearla;
- verificare l'esistenza di una risorsa;
- eliminare una risorsa;
- leggere il contenuto di un file;
- scrivere in un file;
- modificare il contenuto di un file;
Per mostrare quanto è semplice compiere operazioni di I/O su file system utilizzando la classe FileFactory, ho preparato come esempio la generazione di fantomatici menù di cucina a partire da un file template. Come output, oltre al menù del Lunedì avremo anche il file di log per controllare se tutte le operazioni si sono concluse correttamente.
La classe FileFactory
La classe è fondamentalmente un gestore di servizi per operare su files; non è necessario crearne istanze ed i servizi che offre sono tutti rappresentati dai metodi pubblici e statici accessibili.
Il codice sorgente della classe FileFactory è pulito, ordinato, ben commentato e formattato secondo le regole della sintassi Javadoc.
Come si può notare è presente il metodo main: al suo interno è documentata la logica dell'esempio che ho scelto per mostrare l'utilizzo intuitivo del componente.
Di seguito il codice sorgente della classe Java FileFactory, mentre il file di template menu_template.txt, utilizzato nell'esempio per la creazione dei menù del giorno, è reperibile a questo indirizzo.
/**
* Factory per la creazione/modifica/lettura
* e verifica di risorse su filesystem.
*
* @author Mirko Agrati
* @version 1.2
*/
public class FileFactory {
/**
* Testa l'esistenza di una risorsa su filesystem.
*
* @param path L'indirizzo fisico della risorsa.
* @return <code>true</code> se esiste
* altrimenti <code>false</code>.
*/
public static boolean fileExists(String path){
return new File(path).exists();
}
/**
* Restituisce il contenuto del file
* all'indirizzo <code>path</code>
*
* @param path Indirizzo fisico del file da leggere.
* @return Il contenuto del file in una stringa.
*
* @throws FileNotFoundException
* @throws IOException
*/
public static String getFileContent(String path)
throws FileNotFoundException, IOException{
String content = "";
BufferedReader br =
new BufferedReader(new FileReader(path));
while (br.ready())
content += "\n" + br.readLine();
br.close();
return content;
}
/**
* Restituisce il contenuto del file <code>f</code>
*
* @param f Il file da leggere.
* @return Il contenuto del file in una stringa.
*
* @throws FileNotFoundException
* @throws IOException
*/
public static String getFileContent(File f)
throws FileNotFoundException, IOException{
String content = "";
BufferedReader br =
new BufferedReader(new FileReader(f));
while (br.ready())
content += "\n" + br.readLine();
br.close();
return content;
}
/**
* Ricerca nel file <code>f</code>
* tutte le ricorrenze di ogni chiave della mappa
* <code>h</code>
* per sostituirle con i valori ad esse abbinate.
*
* @param f File in cui eseguire la ricerca
* ed eventuale sostituzione.
* @param h Contiene le chiavi da ricercare
* e i valori con cui sostituirle.
* @return Il contenuto del file con
* le sostituzione apportate.
*
* @throws FileNotFoundException
* @throws IOException
*/
public static String getSubstitutedFileContent
(File f, Hashtable<String, String> h)
throws FileNotFoundException, IOException{
String content = "";
BufferedReader br =
new BufferedReader(new FileReader(f));
while (br.ready())
content += "\n" + substitute(br.readLine(),h);
br.close();
return content;
}
/**
* Esegue le sostituzioni chiavi/valori
* nella stringa ricevuta.
* Nel caso valore contenga chiave allora
* verrà sostituita solo la prima occorrenza.
*
* @param input La stringa su cui operare
* @param h Le chiavi ed i valori necessari.
* @return La stringa con le sostituzioni apportate
*/
private static String substitute
(String input, Hashtable<String, String> h){
if(input.length()<1) return "";
Enumeration<String> keys = h.keys();
while(keys.hasMoreElements()){
String key = keys.nextElement();
String value = h.get(key);
int index = input.indexOf(key);
while(index > -1){
String firstpart =
input.substring(0,index) + value + "";
String lastpart =
input.substring(index+key.length());
input = new String(firstpart + lastpart);
index = (value.indexOf(key)>-1)
? -1 : input.indexOf(key);
}
}
return input;
}
/**
* Inserisce del contenuto in un file.
*
* @param f File in cui scrivere
* @param content Contenuto da scrivere nel file
*
* @throws FileNotFoundException
* @throws IOException
*/
public static void setFileContent
(File f, String content)
throws FileNotFoundException, IOException{
FileOutputStream fos;
fos = new FileOutputStream(f);
fos.write(content.getBytes());
fos.close();
}
/**
* Rimuove un file.
*
* @param path Indirizzo fisico del file da cancellare.
* @return <code>true</code> se il file è stato
* eliminato, altrimenti <code>false</code>.
*/
public static boolean removeFile(String path){
return new File(path).delete();
}
/**
* Crea un file.
*
* @param path Indirizzo fisico del file da creare.
* @param isdirectory Indica se bisogna creare una directory
* @return Il file creato oppure <code>null</code>
*/
public static File createFile
(String path, boolean isdirectory)
throws IOException{
File f = new File(path);
boolean created = (isdirectory)
? f.mkdir() : f.createNewFile();
return (created) ? f : null;
}
/**
* Crea un nuovo file e restituisce il suo riferimento.
*
* @param path Indirizzo del file da creare.
* @return Il file appena creato.
*
* @throws IOException
*/
public static File createFile(String path)
throws IOException{
File f = new File(path);
if(f.exists())
f.delete();
if(!f.createNewFile())
throw new IOException("Non è stato possibile creare il file '"
+ path + "'");
return f;
}
/**
* Crea un nuovo file solo se non esiste
* già all'indirizzo indicato.
*
* @param path Indirizzo del file.
* @param isdirectory Indica se si tratta di una directory.
* @return Il file esistente o creato;
*
* @throws IOException Se non è possibile operare sul file system.
*/
public static File createFileIfNotExists
(String path, boolean isdirectory) throws IOException{
File f = new File(path);
if(!f.exists()){
boolean created = (isdirectory)
? f.mkdir() : f.createNewFile();
return (created) ? f : null;
}
return f;
}
/**
* Utilizza il template dei menu
* per creare il menù del Lunedì.
*
* TEMPLATE:
* PRIMI:
* *PASTA1*
* *RISOTTO1*
* *MINESTRA1*
*
* SECONDI:
* *CARNE1*
* *CARNE2*
* *PESCE1*
*
* CONTORNI:
* *VERDURE1*
* *VERDURE2*
*
* DOLCI:
* *DOLCI1*
* *DOLCI2*
*
* @param args
* @throws IOException
*/
public static void main(String[] args) throws IOException{
//Path del file di template in input
String menu_path = "C://temp//menu_template.txt";
//Path del file che verrà creato per il menù del Lunedì
String lunedi_path = "C://temp//menu_Lunedi.txt";
//Path del file che verrà creato per tracciare
//l'andamento delle operazioni svolte.
String logs_path = "C://temp//menu_logs.txt";
//Inizio creazione dei los applicativi
String logs = "Inizio attività.";
//Referenzio il template dei menù.
File f = createFileIfNotExists(menu_path, false);
//Traccio nel log il contenuto originale
//del file di template
logs += "\nAperto Template dei Menù: " + menu_path;
logs += FileFactory.getFileContent(f);
//Preparo il menù del Lunedì:
//ogni coppia chiave/valore rappresenta
//il token da sostituire/il nuovo contenuto.
Hashtable<String, String> menuLunedi =
new Hashtable<String, String>();
menuLunedi.put("*PASTA1*", "Spaghetti all'amatriciana");
menuLunedi.put("*RISOTTO1*", "Risotto ai funghi porcini");
menuLunedi.put("*MINESTRA1*", "Minestrone di stagione");
menuLunedi.put("*CARNE1*", "Carne di cavallo ai ferri");
menuLunedi.put("*CARNE2*", "Fesa di vitello");
menuLunedi.put("*PESCE1*", "Trota al cartoccio");
menuLunedi.put("*VERDURE1*", "Insalata mista");
menuLunedi.put("*VERDURE2*", "Patatine fritte");
menuLunedi.put("*DOLCI1*", "Tiramisù");
menuLunedi.put("*DOLCI2*", "Torta Margherita");
logs += "\nCaricato il dizionario dei token "
+ "da sostituire il Lunedì.";
//Creo il file con il menù del Lunedì
//sostituendo all'interno del template
//tutte le ricorrenze delle chiavi di
//<code>menuLunedi</code>
//con il relativo valore.
setFileContent(
createFileIfNotExists(lunedi_path, false)
,FileFactory.getSubstitutedFileContent(f,menuLunedi));
//Traccio la corretta esecuzione della generazione
//del menù del Lunedì.
logs += "\nSostituzione eseguita."
+ "\nMenù del Lunedì creato: " + lunedi_path;
logs += "\nAttività terminata correttamente";
//Scrivo il file di logs.
setFileContent(
createFileIfNotExists(logs_path, false), logs);
}
}
Come detto in precedenza, la classe FileFactory permette di fare molte cose, agendo sulle risorse in maniera semplice, intuitiva ed efficace.
E' stata compilata con successo per l'ambiente Java 5 e non fa uso di alcun metodo od oggetto deprecato.
Conclusioni
Come mostrato nell'esempio, con poche righe di codice si creano 2 nuovi files, se ne legge uno e si provvede a sostituirne alcune parti di testo per salvare il tutto in un altro file: mica male :).
Tra l'altro, il codice sorgente è di facile comprensione e ben documentato cosa che rende la classe facilmente implementabile per acquisire nuove funzionalità.
Alla prossima,
MA.
Grazie per aver condiviso il tuo lavoro, è molto interessante.
RispondiEliminaSaluti