001: /*
002: * argun 1.0
003: * Web 2.0 delivery framework
004: * Copyright (C) 2007 Hammurapi Group
005: *
006: * This program is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU Lesser General Public
008: * License as published by the Free Software Foundation; either
009: * version 2 of the License, or (at your option) any later version.
010: *
011: * This program is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * Lesser General Public License for more details.
015: *
016: * You should have received a copy of the GNU Lesser General Public
017: * License along with this library; if not, write to the Free Software
018: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
019: *
020: * URL: http://www.hammurapi.biz
021: * e-Mail: support@hammurapi.biz
022: */
023: package biz.hammurapi.web;
024:
025: import java.io.ByteArrayOutputStream;
026: import java.io.IOException;
027: import java.io.OutputStream;
028: import java.util.ArrayList;
029: import java.util.Collection;
030: import java.util.HashMap;
031: import java.util.HashSet;
032: import java.util.Iterator;
033: import java.util.Map;
034: import java.util.Set;
035:
036: import javax.servlet.ServletOutputStream;
037:
038: import org.apache.log4j.Logger;
039:
040: import biz.hammurapi.config.Context;
041: import biz.hammurapi.config.MapContext;
042: import biz.hammurapi.eval.EvaluationException;
043: import biz.hammurapi.eval.ExpandingFilterWriter;
044:
045: /**
046: * @author Pavel Vlasov
047: * @version $Revision$
048: */
049: public class ExpandingServletOutputStream extends ServletOutputStream {
050: private OutputStream out;
051: private Context context;
052: private boolean stop;
053: private static final Logger logger = Logger
054: .getLogger(ExpandingServletOutputStream.class);
055:
056: private Set keySet;
057:
058: public ExpandingServletOutputStream(OutputStream out,
059: Context context) {
060: this (out, context, new HashSet());
061: }
062:
063: private ExpandingServletOutputStream(OutputStream out,
064: Context context, Set keySet) {
065: super ();
066: this .out = out;
067: this .context = context;
068: this .keySet = keySet;
069: }
070:
071: private int lastByte;
072:
073: private ByteArrayOutputStream keyBuffer;
074:
075: private static final int STATE_NORMAL = 0;
076:
077: private static final int STATE_DOLLAR = 1;
078:
079: private static final int STATE_EXPRESSION = 2;
080:
081: private static final int STATE_QUOTE = 3;
082:
083: private int state = STATE_NORMAL;
084:
085: public void write(int b) throws IOException {
086: switch (b) {
087: case '$':
088: switch (state) {
089: case STATE_NORMAL:
090: state = STATE_DOLLAR;
091: break;
092: case STATE_DOLLAR:
093: state = STATE_NORMAL;
094: out.write(b);
095: if (stop) {
096: out.write(b);
097: }
098: // out.write(b);
099: break;
100: case STATE_EXPRESSION:
101: case STATE_QUOTE:
102: toBuffer(b);
103: break;
104: default:
105: throw new IOException("Invalid lexer state: " + state);
106: }
107: break;
108: case '{':
109: switch (state) {
110: case STATE_EXPRESSION:
111: case STATE_QUOTE:
112: toBuffer(b);
113: break;
114: case STATE_NORMAL:
115: out.write(b);
116: break;
117: case STATE_DOLLAR:
118: state = STATE_EXPRESSION;
119: break;
120: default:
121: throw new IOException("Invalid lexer state: " + state);
122: }
123: break;
124: case '}':
125: switch (state) {
126: case STATE_NORMAL:
127: out.write(b);
128: break;
129: case STATE_DOLLAR:
130: state = STATE_NORMAL;
131: out.write(b);
132: case STATE_EXPRESSION:
133: state = STATE_NORMAL;
134: if (keyBuffer == null || keyBuffer.size() == 0) {
135: out.write('$');
136: out.write('{');
137: out.write('}');
138: } else if (ExpandingFilterWriter.PRAGMA_START
139: .equals(keyBuffer.toString())) {
140: stop = false;
141: keyBuffer = null;
142: } else if (ExpandingFilterWriter.PRAGMA_STOP
143: .equals(keyBuffer.toString())) {
144: stop = true;
145: keyBuffer = null;
146: } else if (stop) {
147: out.write("${".getBytes());
148: keyBuffer.close();
149: out.write(keyBuffer.toByteArray());
150: out.write('}');
151: keyBuffer = null;
152: } else {
153: String key = keyBuffer.toString();
154: keyBuffer = null;
155:
156: if (keySet.add(key)) {
157: Object o = context.get(key);
158: if (o == null) {
159: out.write(("${" + key + "}").getBytes());
160: } else {
161: if (o instanceof Collection) {
162: Iterator it = ((Collection) o)
163: .iterator();
164: while (it.hasNext()) {
165: Object object = it.next();
166: if (object == null) {
167: out.write("(null)".getBytes());
168: } else {
169: out.write(toBytes(object));
170: if (it.hasNext()) {
171: out.write(' ');
172: }
173: }
174: }
175: } else {
176: out.write(toBytes(o));
177: }
178: }
179: } else {
180: out.write(("${" + key + "}").getBytes());
181: logger.warn("Circular reference: " + key);
182: }
183: }
184: break;
185: case STATE_QUOTE:
186: toBuffer(b);
187: break;
188: default:
189: throw new IOException("Invalid lexer state: " + state);
190: }
191: break;
192: case '"':
193: switch (state) {
194: case STATE_NORMAL:
195: out.write(b);
196: break;
197: case STATE_DOLLAR:
198: state = STATE_NORMAL;
199: out.write('$');
200: out.write(b);
201: break;
202: case STATE_EXPRESSION:
203: state = STATE_QUOTE;
204: toBuffer(b);
205: break;
206: case STATE_QUOTE:
207: if (lastByte != '\\') {
208: state = STATE_EXPRESSION;
209: }
210: toBuffer(b);
211: break;
212: default:
213: throw new IOException("Invalid lexer state: " + state);
214: }
215: break;
216: default:
217: switch (state) {
218: case STATE_NORMAL:
219: out.write(b);
220: break;
221: case STATE_DOLLAR:
222: state = STATE_NORMAL;
223: out.write('$');
224: out.write(b);
225: break;
226: case STATE_EXPRESSION:
227: case STATE_QUOTE:
228: toBuffer(b);
229: break;
230: default:
231: throw new IOException("Invalid lexer state: " + state);
232: }
233: }
234: lastByte = b;
235: }
236:
237: /**
238: * @param b
239: */
240: private void toBuffer(int b) {
241: if (keyBuffer == null) {
242: keyBuffer = new ByteArrayOutputStream();
243: }
244: keyBuffer.write(b);
245: }
246:
247: public void close() throws IOException {
248: if (state == STATE_DOLLAR) {
249: out.write('$');
250: } else if (state == STATE_EXPRESSION || state == STATE_QUOTE) {
251: out.write('$');
252: out.write('{');
253: }
254:
255: if (keyBuffer != null) {
256: keyBuffer.close();
257: out.write(keyBuffer.toByteArray());
258: keyBuffer = null;
259: }
260: out.close();
261: super .close();
262: }
263:
264: /**
265: * @param object
266: * @return
267: */
268: private byte[] toBytes(Object object) {
269: try {
270: ByteArrayOutputStream baos = new ByteArrayOutputStream();
271: ExpandingServletOutputStream es = new ExpandingServletOutputStream(
272: baos, context, keySet);
273: es.write(object.toString().getBytes());
274: es.close();
275: baos.close();
276: return baos.toByteArray();
277: } catch (IOException e) {
278: throw new EvaluationException("Shall never happen", e);
279: }
280: }
281:
282: public static void main(String[] args) throws Exception {
283: Map ctx = new HashMap();
284: ctx.put("request", "Mama mila ramu");
285: ctx.put("a", new String[] { "a", "b", "c", "d" });
286: Collection values = new ArrayList();
287: values.add("Pasha");
288: values.add("Olga");
289: values.add("Daniel");
290:
291: ExpandingServletOutputStream esos = new ExpandingServletOutputStream(
292: System.out, new MapContext(ctx));
293:
294: esos.write("Simple ${a[1].length()} test ${zz".getBytes());
295: esos.close();
296: }
297:
298: }
|