|
Valutare la correttezza sintattica di codice javascript usando un interprete java. Tra gli esercizi più frequenti ed utili per un programmatore sicuramente troviamo la creazione di un interprete. Un interprete, in informatica come nel linguaggio comune, non è altro che un "traduttore" da un linguaggio ad un altro. Per esempio ogni browser è dotato di un "traduttore" per il codice javascript in modo da leggere le funzioni e trasformarle in altre che il browser comprende, vale a dire in azioni a cui corrispondono animazioni, funzioni logiche/aritmetiche (somme, ecc..) in base a quanto richiesto dal programmatore che si è occupato di crearle. I linguaggi interpretati hanno avuto molta fortuna in tempi recenti per la semplicità di utilizzo e la facilità nella scrittura. Non richiedono alcun compilatore perché direttamente letti (=interpretati). Javascript è un ottimo esempio di linguaggio interpretato! BeanShell è un interprete Java che permette l'esecuzione di codice Java. La sua utilità è vastissima: per esempio permette di eseguire test di web application da remoto, eseguire codice java dinamicamente, creare applet per il porting su web di applicazioni già esistenti, … In questo articolo non useremo BeanShell per il suo utilizzo “classico”, ma descriveremo un primo passo per poter utilizzare il suo interprete per validare codice javascript. Una valutazione per BeanShell si presenta nel seguente formato: Interpreter interpreter = new Interpreter(); interpreter.eval("System.out.println(\"Hello world!\")"); Il risultato non lancia eccezioni, dovute ad errori sintattici, e fornisce in console il solito: Hello world! Per utilizzare un interprete Java per javascript dobbiamo fargli credere di valutare codice Java. Dobbiamo allora realizzare un mapping tra i due linguaggi. Lo scopo è trasformare il codice javascript in codice java, l'unico compreso dall'interprete che poi lo valuterà e ci dirà se è corretto. Per raggiungere il nostro obiettivo innanzitutto occorre evidenziare le differenze e le analogie sintattiche tra java e javascript. Javascript non è un linguaggio strettamente tipizzato. Per Java esiste un oggetto dal quale tutti derivano (e questo ci tornerà comodo più avanti): Object. Fanno eccezione i tipi primitivi (int, ecc...) per i quali si possono utilizzare le corrispondenti classi wrapper (Integer, ecc...). Entrambi i linguaggi utilizzano un paradigma ad oggetti. In javascript alcuni di questi sono impliciti, cioé non è necessario dichiararli perché esiste sempre una istanza nel browser: per esempio per "window" non occorre scrivere var window = new Window();. La sintassi è molto simile: cicli, assegnamenti e tutte le istruzioni base sono equivalenti. Analizziamo un semplice esempio di codice javascript: for(i=0; i<9; i++){ alert("ciclo n° "+i); } L'analogo in java è: for(i=0; i<9; i++){ System.out.println("ciclo n° "+i); } Prima di tutto quindi occorre sostituire "alert" con "System.out.println". Allo stesso modo dobbiamo sostituire altre funzioni: confirm, ecc... /** * javascript2java * replacing javascript code with corresponding java code * <b>incomplete</b> * */ public static String javascript2java(String javascriptCode){ // sostituisco tutte le occorrenze di alert con System.out.println javascriptCode = replace(javascriptCode, " alert(", "System.out.println(\"\"+"); // sostituisco var con Object javascriptCode = replace(javascriptCode, " var ", " Object "); return javascriptCode; }// --- javascript2java (il metodo replace sostituisce ricorsivamente parti di testo) Come si può notare oltre a sostituire l'alert operiamo un'altra importante sostituzione. In javascript si usa un solo modo per dichiarare una variabile: "var". Per fortuna tutte le classi in java sono anche Object! Il mapping var - Object è quindi immediato. Rimangono da tradurre in java gli oggetti impliciti. Partiamo da un esempio javascript: var str2write = "<b>nuova stringa</b>"; document.write(str2write); Se vogliamo che l'interprete non produca errore dobbiamo dargli la possibilità di "trovare" la risorsa document e fare in modo che sia dotata di un metodo statico write(Object). Per essere sicuri che trovi ogni risorsa utile, non potendo passare il nome completo della classe, dovremo creare la classe document nel "default package": public class document { public static void write(Object s){ //empty method } }// ------------------------------------- end per poter poi invocare il metodo write direttamente, questo deve essere statico. Allo stesso modo dobbiamo muoverci per la classe window. Notiamo però subito un aspetto fondamentale di javascript. Una finestra può essere chiamata in diversi modi, tutte istanze della Window corrente: le chiamate window.close(); self.close(); producono lo stesso effetto! Anche l'opener (la finestra che ha eseguito una window.open("")) è una istanza di una window! Ne consegue una tassonomia di classi, tutte con le stesse caratteristiche. L'ereditarietà disponibile in java ci viene incontro. /** * @author Francesco Mele * Created on Aug 17, 2004 * * simulating javascript window object and its method and properties */ public class window { public static document document; public static Object open(Object o){ return o; } public static void close(){ //empty method } }// --------------------------------------------- end e /** * @author Francesco Mele * Created on Aug 17, 2004 * * self is a window */ public class self extends window { }// ---------------------------------------- end /** * @author Francesco Mele * Created on Aug 17, 2004 * * opener is a window */ public class opener extends window { }// ------------------------------------ end In questo modo invocando la open su una classe self verrà chiamata la open della classe padre. Passiamo finalmente a valutare un po' di codice javascript. Il metodo della nostra classe Evaluator è /** * eval * just calls the eval method of interpreter * @see bsh.Interpreter * @return true if no errors occurr * */ public static boolean eval(String strToCheck){ Interpreter interpreter = new Interpreter(); try { interpreter.eval(strToCheck); } catch (EvalError e) { System.err.println("// -------------------------- //\n" + "// ---> error is:\n"+e.getMessage()+ "\n// -------------------------- //"); return false; } return true; }// --- eval dove ritroviamo la chiamata all'eval di BeanShell vista in precedenza. Il nostro main è quindi public static void main(String[] args) { String str2check = ""; //str2check += "alert(\"start\"); for (i=0; i<9; i++) { var txt = \"this is a text\"; document.write(i+\": \"+txt);} alert(\"end\"); "; str2check += "location.href(\"http://....\")"; String str2code = Js2JavaUtils.javascript2java(str2check); if (eval(str2code)){ System.out.println("No errors occurred"); }else { System.err.println("Failing checking java code!"); } }// --- main Ho lasciato anche un paio di semplici test; basta decommentare. NOTA L'interprete esegue il codice java risultante, quindi un System.out.println produrrà una riga in console. Nella classe Evaluator ho anche riportato un estratto di codice trovato in rete con il quale potersi divertire nell'uso dell'interprete BeanShell. Provate a far valutare espressioni aritmetiche del tipo a = 3*2 oppure a = 3*2 == 6
|