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.Reader; 009 import java.io.StringReader; 010 import java.io.StringWriter; 011 import java.io.Writer; 012 import java.util.Collection; 013 import java.util.HashSet; 014 import java.util.Iterator; 015 import java.util.Set; 016 017 import biz.hammurapi.config.Context; 018 019 /** 020 * @author Pavel Vlasov 021 * @version $Revision$ 022 */ 023 public class ExpandingFilterWriter extends FilterWriter { 024 private Context context; 025 private boolean stop; 026 027 /** 028 * When pragma:stop token is encountered subsequent characters are not 029 * expanded until pragma:start token is encountered. pragma:start and pragma:stop are not output to 030 * filtered writer. 031 */ 032 public static final String PRAGMA_STOP = "pragma:stop"; 033 public static final String PRAGMA_START = "pragma:start"; 034 035 // private static final Logger logger = Logger.getLogger(ExpandingFilterWriter.class); 036 037 private Set keySet; 038 039 public ExpandingFilterWriter(Writer out, Context context) { 040 this(out, context, new HashSet()); 041 } 042 043 protected ExpandingFilterWriter(Writer out, Context context, Set keySet) { 044 super(out); 045 this.context = context; 046 this.keySet=keySet; 047 } 048 049 private int lastChar; 050 051 private StringBuffer keyBuffer; 052 053 private static final int STATE_NORMAL = 0; 054 055 private static final int STATE_DOLLAR = 1; 056 057 private static final int STATE_EXPRESSION = 2; 058 059 private static final int STATE_QUOTE = 3; 060 061 private int state = STATE_NORMAL; 062 063 public void write(int c) throws IOException { 064 switch (c) { 065 case '$': 066 switch (state) { 067 case STATE_NORMAL: 068 state = STATE_DOLLAR; 069 break; 070 case STATE_DOLLAR: 071 state = STATE_NORMAL; 072 out.write(c); 073 if (stop) { 074 out.write(c); 075 } 076 // out.write(b); 077 break; 078 case STATE_EXPRESSION: 079 case STATE_QUOTE: 080 toBuffer(c); 081 break; 082 default: 083 throw new IOException("Invalid lexer state: " + state); 084 } 085 break; 086 case '{': 087 switch (state) { 088 case STATE_EXPRESSION: 089 case STATE_QUOTE: 090 toBuffer(c); 091 break; 092 case STATE_NORMAL: 093 out.write(c); 094 break; 095 case STATE_DOLLAR: 096 state = STATE_EXPRESSION; 097 break; 098 default: 099 throw new IOException("Invalid lexer state: " + state); 100 } 101 break; 102 case '}': 103 switch (state) { 104 case STATE_NORMAL: 105 out.write(c); 106 break; 107 case STATE_DOLLAR: 108 state = STATE_NORMAL; 109 out.write(c); 110 case STATE_EXPRESSION: 111 state = STATE_NORMAL; 112 if (keyBuffer == null || keyBuffer.length()==0) { 113 out.write('$'); 114 out.write('{'); 115 out.write('}'); 116 } else if (PRAGMA_START.equals(keyBuffer.toString())){ 117 stop=false; 118 keyBuffer=null; 119 } else if (PRAGMA_STOP.equals(keyBuffer.toString())){ 120 stop=true; 121 keyBuffer=null; 122 } else if (stop) { 123 out.write("${"); 124 out.write(keyBuffer.toString()); 125 out.write("}"); 126 keyBuffer=null; 127 } else { 128 String key = keyBuffer.toString(); 129 keyBuffer = null; 130 131 if (keySet.contains(key)) { 132 throw new CircularReferenceException("Circular reference: "+key); 133 } 134 135 Object o=context.get(key); 136 if (o==null) { 137 out.write("${"); 138 out.write(key); 139 out.write("}"); 140 } else { 141 if (o instanceof Collection) { 142 Iterator it = ((Collection) o).iterator(); 143 while (it.hasNext()) { 144 Object object = it.next(); 145 if (object == null) { 146 out.write("(null)"); 147 } else { 148 try { 149 keySet.add(key); 150 out.write(toString(object, context, keySet)); 151 } finally { 152 keySet.remove(key); 153 } 154 155 if (it.hasNext()) { 156 out.write(' '); 157 } 158 } 159 } 160 } else { 161 try { 162 keySet.add(key); 163 out.write(toString(o, context, keySet)); 164 } finally { 165 keySet.remove(key); 166 } 167 } 168 } 169 } 170 break; 171 case STATE_QUOTE: 172 toBuffer(c); 173 break; 174 default: 175 throw new IOException("Invalid lexer state: " + state); 176 } 177 break; 178 case '"': 179 switch (state) { 180 case STATE_NORMAL: 181 out.write(c); 182 break; 183 case STATE_DOLLAR: 184 state=STATE_NORMAL; 185 out.write('$'); 186 out.write(c); 187 break; 188 case STATE_EXPRESSION: 189 state=STATE_QUOTE; 190 toBuffer(c); 191 break; 192 case STATE_QUOTE: 193 if (lastChar!='\\') { 194 state=STATE_EXPRESSION; 195 } 196 toBuffer(c); 197 break; 198 default: 199 throw new IOException("Invalid lexer state: " + state); 200 } 201 break; 202 default: 203 switch (state) { 204 case STATE_NORMAL: 205 out.write(c); 206 break; 207 case STATE_DOLLAR: 208 state=STATE_NORMAL; 209 out.write('$'); 210 out.write(c); 211 break; 212 case STATE_EXPRESSION: 213 case STATE_QUOTE: 214 toBuffer(c); 215 break; 216 default: 217 throw new IOException("Invalid lexer state: " + state); 218 } 219 } 220 lastChar = c; 221 } 222 223 /** 224 * @param object 225 * @return 226 */ 227 private static String toString(Object object, Context context, Set keySet) { 228 try { 229 StringWriter sw=new StringWriter(); 230 ExpandingFilterWriter efw=new ExpandingFilterWriter(sw, context, keySet); 231 char[] buf=new char[1024]; 232 StringReader reader=new StringReader(object.toString()); 233 int l; 234 while ((l=reader.read(buf))!=-1) { 235 efw.write(buf, 0, l); 236 } 237 efw.close(); 238 sw.close(); 239 reader.close(); 240 return sw.toString(); 241 } catch (IOException e) { 242 throw new EvaluationException("Shall never happen", e); 243 } 244 } 245 246 /** 247 * @param b 248 */ 249 private void toBuffer(int b) { 250 if (keyBuffer == null) { 251 keyBuffer = new StringBuffer(); 252 } 253 keyBuffer.append((char) b); 254 } 255 256 public void close() throws IOException { 257 if (state == STATE_DOLLAR) { 258 out.write('$'); 259 } else if (state == STATE_EXPRESSION /*|| state == STATE_QUOTE */) { 260 out.write('$'); 261 out.write('{'); 262 } 263 264 if (keyBuffer != null) { 265 out.write(keyBuffer.toString()); 266 keyBuffer = null; 267 } 268 out.close(); 269 super.close(); 270 } 271 272 public void write(char[] cbuf, int off, int len) throws IOException { 273 for (int i=0; i<len; i++) { 274 write(cbuf[i+off]); 275 } 276 } 277 278 public void write(String str, int off, int len) throws IOException { 279 for (int i=0; i<len; i++) { 280 write(str.charAt(i+off)); 281 } 282 } 283 284 public static Reader expand(Reader reader, Context context) throws IOException { 285 return new StringReader(expandToString(reader, context)); 286 } 287 288 /** 289 * @param reader 290 * @param context 291 * @return 292 * @throws IOException 293 */ 294 public static String expandToString(Reader reader, Context context) throws IOException { 295 StringWriter sw=new StringWriter(); 296 ExpandingFilterWriter efw=new ExpandingFilterWriter(sw, context); 297 char[] buf=new char[1024]; 298 int l; 299 while ((l=reader.read(buf))!=-1) { 300 efw.write(buf, 0, l); 301 } 302 efw.close(); 303 sw.close(); 304 reader.close(); 305 return sw.toString(); 306 } 307 }