sabato 29 novembre 2008

Interpreter Design Pattern

INTERPRETER DESIGN PATTERN:

Obiettivo: fornire una definizione di un macro-linguaggio e il parsing di esso in un oggetto.

Questo pattern è particolarmente indicato quando si vuole costruire un parser di stringhe personalizzato, tradurre una specifica espressione o gestire un'informazione strutturata ad albero.

Nel nostro esempio useremo il design pattern Interpreter per definire un parser di una stringa che definisce codice pseudo-sql. Nel listato, per focalizzare l'attenzione sul pattern in esame e quindi definire un codice snello, faremo uso anche dei pattern Visit e Iterator.


package designPatterns.interpreter.*;
import java.util.*;

public class Main {
    public static void main(String[] args) {
         String source = "attributes='id',table='users',"+
                 "conditions='username\"root\" and password=\"mypwd\"'";
         String delimiter = "=,'";
         Parser parser = new Parser(source, delimiter);
         parser.myParser();
         parser.interpret();
         String result = parser.getInterpretedResult();
         System.out.println(result);
     }
}

class Parser{
    private String expression;
    private String delimitator;
    private List result;
    private String interpreted;
    public Parser(String e, String t) {
       expression = e;
       delimitator = t;
    }
    public void myParser() { 
        StringTokenizer holder = new StringTokenizer(expression, delimitator);
        String[] toBeMatched = new String[holder.countTokens()]; 
        int idx = 0;
        while(holder.hasMoreTokens()) {
            String item = holder.nextToken();
            int start = item.indexOf(",");
            if(start==0) {
               item = item.substring(2);
            }  
        toBeMatched[idx] = item;
        idx ++;
        }
        result = Arrays.asList(toBeMatched);
     }
     public List getParseResult() {
        return result;
     }
     public void interpret() {
         StringBuffer buffer = new StringBuffer();
         ListIterator list = result.listIterator();
         while (list.hasNext()){
            String token = (String)list.next();
            if (token.equals("attributes")){
               token = "SELECT";         
            }else if(token.equals("table")) {
               token = "FROM";
            }else if(token.equals("conditions")) {
               token = "WHERE";
            }
            buffer.append(" " + token);
         }
         interpreted = buffer.toString();       
     }
     public String getInterpretedResult() {
        return interpreted;
     }
}


Un esempio molto più concreto di applicazione del pattern Interpreter è dato dal parsing di una query scritta in linguaggio EJB-QL nell'ambito dello sviluppo JEE. Come noto una query di ricerca scritta in tale linguaggio non è altro che una stringa ma il risultato della sua esecuzione, mediante l'utilizzo delle API che gestiscono la persistenza con un database, è un vero e proprio oggetto o una collezione di tali oggetti omogenei. Nel seguente listato viene illustrata una semplificazione del parser che, data una query ejb-ql, restituisce una collezione di oggetti (che saranno poi gli Entity Bean deputati a gestire la persistenza delle informazioni elaborate in un'applicazione enterprise con un database).


package designPatterns.interpreter;
import java.util.*;

public class Main {
    public static void main(String[] args)
            throws NotWellFormedSimpleQuery {
        //String to parse
         String queryString = "FROM interpreter.MyEntity";
         SimpleEJBQueryParser queryParser =
                 new SimpleEJBQueryParser(queryString);
         //interpret String and cast result
         List<MyEntity> resultList =
                    (
List<MyEntity>) queryParser.getResultList();
         Iterator it = resultList.iterator();
         while(it.hasNext()){
            MyEntity tmp = (MyEntity)it.next();
            System.out.println(tmp.getId()+"-"+tmp.getValue());
         }
     }
}

class MyEntity{
    private Integer id;
    private String value;
    public MyEntity(){}
    public MyEntity(Integer id, String value) {
        this.id = id;
        this.value = value;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public void setValue(String value) {
        this.value = value;
    }
    public Integer getId() {
        return id;
    }
    public String getValue() {
        return value;
    }
}

class SimpleEJBQueryParser{
    private Class entityClass;
    private String tableName;
    private List resultList;
    public SimpleEJBQueryParser(String query)
            throws NotWellFormedSimpleQuery{
        StringTokenizer st = new StringTokenizer(query);
        if(!st.nextToken(" ").trim().equals("FROM"))
            throw new NotWellFormedSimpleQuery();
        tableName = st.nextToken(" ");
        try {
            entityClass = Class.forName(tableName);
        } catch (ClassNotFoundException ex) {
            throw new NotWellFormedSimpleQuery(ex.toString());
        }
    }
    public List getResultList(){
        buildResultList();
        return resultList;
    }
    private void buildResultList(){
        resultList = new ArrayList();
        Object obj = null;
        try {
            obj = entityClass.newInstance();
        } catch (Exception ex) {
            System.out.println(ex.toString());
        }
        if(obj instanceof MyEntity){
            MyEntity tmp = (MyEntity)obj;
            /*populate MyEntity directly or setting values
            /*from database table MyEntity for examle:*/
            tmp.setId(1);
            tmp.setValue("ciao");
        }
        resultList.add(obj);
    }
}

class NotWellFormedSimpleQuery extends Exception {
    public NotWellFormedSimpleQuery(){
        super("The simple query is not well formed");
    }
    public NotWellFormedSimpleQuery(String msg){
        super(msg);
    }
}


Nessun commento: