001    /*
002    @license.text@
003     */
004    package biz.hammurapi.eval;
005    
006    import java.io.FilterWriter;
007    import java.io.IOException;
008    import java.io.PrintWriter;
009    import java.io.Writer;
010    import java.util.ArrayList;
011    import java.util.Collection;
012    import java.util.HashMap;
013    import java.util.HashSet;
014    import java.util.Iterator;
015    import java.util.Map;
016    import java.util.Set;
017    
018    import org.apache.log4j.Logger;
019    
020    import biz.hammurapi.config.Context;
021    import biz.hammurapi.config.MapContext;
022    import biz.hammurapi.convert.CompositeConverter;
023    
024    
025    /**
026     * @author Pavel Vlasov
027     * @version $Revision$
028     */
029    public class EvaluatingFilterWriter extends FilterWriter {
030        private int mode;
031    
032        private Context context;
033    
034        private CompositeConverter converter;
035    
036        private static final Logger logger = Logger.getLogger(EvaluatingFilterWriter.class);
037    
038            private Set keySet;
039    
040        public EvaluatingFilterWriter(
041                Writer out, 
042                int mode,
043                Context context, 
044                CompositeConverter converter) {
045                    this(out,mode,context, converter,new HashSet());
046        }
047        
048        private EvaluatingFilterWriter(
049                Writer out, 
050                int mode,
051                Context context, 
052                CompositeConverter converter,
053                            Set keySet) {
054            super(out);
055            this.mode = mode;
056            this.context = context;
057            this.converter = converter;
058            this.keySet=keySet;
059        }
060    
061        private int lastChar;
062    
063        private StringBuffer expressionBuffer;
064    
065        private static final int STATE_NORMAL = 0;
066    
067        private static final int STATE_DOLLAR = 1;
068    
069        private static final int STATE_EXPRESSION = 2;
070    
071        private static final int STATE_QUOTE = 3;
072    
073        public static final int MODE_LENIENT = 0;
074    
075        public static final int MODE_MESSAGE = 1;
076    
077        public static final int MODE_STRICT = 2;
078    
079        private int state = STATE_NORMAL;
080    
081        public void write(int c) throws IOException {
082            switch (c) {
083                    case '$':
084                        switch (state) {
085                                case STATE_NORMAL:
086                                    state = STATE_DOLLAR;
087                                    break;
088                                case STATE_DOLLAR:
089                                    state = STATE_NORMAL;
090                                    out.write(c);
091    //                              out.write(b);
092                                    break;
093                                case STATE_EXPRESSION:
094                                case STATE_QUOTE:
095                                    toBuffer(c);
096                                    break;
097                                default:
098                                    throw new IOException("Invalid lexer state: " + state);
099                        }
100                        break;
101                    case '{':
102                        switch (state) {
103                                case STATE_EXPRESSION:
104                                case STATE_QUOTE:
105                                    toBuffer(c);
106                                    break;
107                                case STATE_NORMAL:
108                                    out.write(c);
109                                    break;
110                                case STATE_DOLLAR:
111                                    state = STATE_EXPRESSION;
112                                    break;
113                                default:
114                                    throw new IOException("Invalid lexer state: " + state);
115                        }
116                        break;
117                    case '}':
118                        switch (state) {
119                                case STATE_NORMAL:
120                                    out.write(c);
121                                    break;
122                                case STATE_DOLLAR:
123                                    state = STATE_NORMAL;
124                                    out.write(c);
125                                case STATE_EXPRESSION:
126                                    state = STATE_NORMAL;
127                                    if (expressionBuffer == null) {
128                                        out.write('$');
129                                        out.write('{');
130                                        out.write('}');
131                                    } else {
132                                        String expression = expressionBuffer.toString();
133                                        expressionBuffer = null;
134                                        try {
135                                            Evaluator evaluator = new Evaluator(expression, null, converter);
136                                            Iterator it = evaluator.evaluate(context).iterator();
137                                            while (it.hasNext()) {
138                                                Object object = it.next();
139                                                if (object == null) {
140                                                    out.write("(null)");
141                                                } else {
142                                                    out.write(object.toString());
143                                                    if (it.hasNext()) {
144                                                        out.write(' ');
145                                                    }
146                                                }
147                                            }               
148                                        } catch (EvaluationException e) {
149                                            switch (mode) {
150                                                    case MODE_LENIENT:
151                                                        logger.warn("Evaluation failed for: " + expression,  e);
152                                                        out.write("${"+expression+"}");
153                                                        break;
154                                                    case MODE_MESSAGE:
155                                                        logger.warn("Evaluation failed for: " + expression,  e);
156                                                        out.write(" *** Evaluation failed for: " + expression+" *** ");
157                                                        break;                                  
158                                                    case MODE_STRICT:
159                                                        logger.error("Evaluation failed for: " + expression,  e);                               
160                                                        throw new IOException("Evaluation failed for: '" + expression+"' - " + e);
161                                                default:
162                                                    throw new IOException("Invalid mode: "+mode);
163                                            }
164                                        }
165                                    }
166                                    break;
167                                case STATE_QUOTE:
168                                    toBuffer(c);
169                                    break;
170                                default:
171                                    throw new IOException("Invalid lexer state: " + state);
172                            }
173                        break;
174                    case '"':
175                        switch (state) {
176                                case STATE_NORMAL:
177                                    out.write(c);
178                                    break;
179                                case STATE_DOLLAR:
180                                    state=STATE_NORMAL;
181                                    out.write(c);
182                                    break;
183                                case STATE_EXPRESSION:
184                                    state=STATE_QUOTE;
185                                    toBuffer(c);
186                                    break;
187                                case STATE_QUOTE:
188                                    if (lastChar!='\\') {
189                                        state=STATE_EXPRESSION;
190                                    }
191                                toBuffer(c);
192                                break;
193                                default:
194                                    throw new IOException("Invalid lexer state: " + state);
195                        }
196                        break;
197                    default:
198                        switch (state) {
199                                case STATE_NORMAL:
200                                    out.write(c);
201                                    break;
202                                case STATE_DOLLAR:
203                                    state=STATE_NORMAL;
204                                    out.write(c);
205                                    break;
206                                case STATE_EXPRESSION:
207                                case STATE_QUOTE:
208                                    toBuffer(c);
209                                    break;
210                                default:
211                                    throw new IOException("Invalid lexer state: " + state);
212                        }
213            }
214            lastChar = c;
215        }
216    
217        /**
218         * @param b
219         */
220        private void toBuffer(int b) {
221            if (expressionBuffer == null) {
222                expressionBuffer = new StringBuffer();
223            }
224            expressionBuffer.append((char) b);
225        }
226    
227        public void close() throws IOException {
228            if (state == STATE_DOLLAR) {
229                out.write('$');
230            } else if (state == STATE_EXPRESSION || state == STATE_QUOTE) {
231                out.write('$');
232                out.write('{');
233            }
234    
235            if (expressionBuffer != null) {
236                out.write(expressionBuffer.toString());
237                expressionBuffer = null;
238            }
239            out.close();
240            super.close();
241        }
242        
243        public static void main(String[] args) throws Exception {
244                    Map ctx=new HashMap();
245                    ctx.put("request", "Mama mila ramu");
246                    ctx.put("a", new String[] {"a", "b", "c", "d"});
247                    Collection values=new ArrayList();
248                    values.add("Pasha");
249                    values.add("Olga");
250                    values.add("Daniel");
251                    
252            EvaluatingFilterWriter efw=new EvaluatingFilterWriter(
253                    new PrintWriter(System.out), 
254                    MODE_STRICT, 
255                    new MapContext(ctx),
256                    CompositeConverter.getDefaultConverter());
257            
258            efw.write("Simple ${a[1].length()} test ${zz}");
259            efw.close();
260        }
261    
262        public void write(char[] cbuf, int off, int len) throws IOException {
263            for (int i=0; i<len; i++) {
264                write(cbuf[i+off]);
265            }
266        }
267        
268        public void write(String str, int off, int len) throws IOException {
269            for (int i=0; i<len; i++) {
270                write(str.charAt(i+off));
271            }
272        }
273    }