001: /*
002: * Copyright 2004-2008 H2 Group. Licensed under the H2 License, Version 1.0
003: * (http://h2database.com/html/license.html).
004: * Initial Developer: H2 Group
005: */
006: package org.h2.server.web;
007:
008: import java.util.ArrayList;
009: import java.util.HashMap;
010: import java.util.Map;
011:
012: /**
013: * A page parser can parse an HTML page and replace the tags there.
014: * This class is used by the H2 Console.
015: */
016: public class PageParser {
017: private WebServer server;
018: private String page;
019: private int pos;
020: private Map settings;
021: private int len;
022: private StringBuffer result;
023:
024: public static String parse(WebServer server, String page,
025: Map settings) {
026: PageParser block = new PageParser(server, page, settings, 0);
027: return block.parse();
028: }
029:
030: private PageParser(WebServer server, String page, Map settings,
031: int pos) {
032: this .server = server;
033: this .page = page;
034: this .pos = pos;
035: this .len = page.length();
036: this .settings = settings;
037: result = new StringBuffer(len);
038: }
039:
040: private void setError(int i) {
041: String s = page.substring(0, i) + "####BUG####"
042: + page.substring(i);
043: s = PageParser.escapeHtml(s);
044: result = new StringBuffer(s);
045: }
046:
047: private String parseBlockUntil(String end) throws Exception {
048: PageParser block = new PageParser(server, page, settings, pos);
049: block.parseAll();
050: if (!block.readIf(end)) {
051: throw new Exception();
052: }
053: pos = block.pos;
054: return block.result.toString();
055: }
056:
057: public String parse() {
058: try {
059: parseAll();
060: if (pos != len) {
061: setError(pos);
062: }
063: } catch (Exception e) {
064: // TODO log error
065: setError(pos);
066: }
067: return result.toString();
068: }
069:
070: private void parseAll() throws Exception {
071: StringBuffer buff = result;
072: String p = page;
073: int i = pos;
074: for (; i < len; i++) {
075: char c = p.charAt(i);
076: switch (c) {
077: case '<': {
078: // if (p.charAt(i + 1) == '%') {
079: // // TODO <%@include %>: never used
080: // if (p.charAt(i + 2) == '@') {
081: // i += 3;
082: // pos = i;
083: // read("include");
084: // String file = readParam("file");
085: // read("%>");
086: // String s = server.getTextFile(file);
087: // append(s);
088: // i = pos;
089: // } else {
090: // buff.append(c);
091: // }
092: // break;
093: // } else
094: if (p.charAt(i + 3) == ':' && p.charAt(i + 1) == '/') {
095: // end tag
096: pos = i;
097: return;
098: } else if (p.charAt(i + 2) == ':') {
099: pos = i;
100: if (readIf("<c:forEach")) {
101: String var = readParam("var");
102: String items = readParam("items");
103: read(">");
104: int start = pos;
105: ArrayList list = (ArrayList) get(items);
106: if (list == null) {
107: result.append("?items?");
108: list = new ArrayList();
109: }
110: if (list.size() == 0) {
111: parseBlockUntil("</c:forEach>");
112: }
113: for (int j = 0; j < list.size(); j++) {
114: settings.put(var, list.get(j));
115: pos = start;
116: String block = parseBlockUntil("</c:forEach>");
117: result.append(block);
118: }
119: } else if (readIf("<c:if")) {
120: String test = readParam("test");
121: int eq = test.indexOf("=='");
122: if (eq < 0) {
123: setError(i);
124: return;
125: }
126: String val = test.substring(eq + 3, test
127: .length() - 1);
128: test = test.substring(0, eq);
129: String value = (String) get(test);
130: read(">");
131: String block = parseBlockUntil("</c:if>");
132: pos--;
133: if (value.equals(val)) {
134: result.append(block);
135: }
136: } else {
137: setError(i);
138: return;
139: }
140: i = pos;
141: } else {
142: buff.append(c);
143: }
144: break;
145: }
146: case '$':
147: if (p.charAt(i + 1) == '{') {
148: i += 2;
149: int j = p.indexOf('}', i);
150: if (j < 0) {
151: setError(i);
152: return;
153: }
154: String item = p.substring(i, j).trim();
155: i = j;
156: String s = (String) get(item);
157: append(s);
158: } else {
159: buff.append(c);
160: }
161: break;
162: default:
163: buff.append(c);
164: }
165: }
166: pos = i;
167: }
168:
169: private Object get(String item) {
170: int dot = item.indexOf('.');
171: if (dot >= 0) {
172: String sub = item.substring(dot + 1);
173: item = item.substring(0, dot);
174: HashMap map = (HashMap) settings.get(item);
175: if (map == null) {
176: return "?" + item + "?";
177: }
178: return map.get(sub);
179: } else {
180: return settings.get(item);
181: }
182: }
183:
184: private void append(String s) {
185: if (s != null) {
186: result.append(PageParser.parse(server, s, settings));
187: }
188: }
189:
190: private String readParam(String name) throws Exception {
191: read(name);
192: read("=");
193: read("\"");
194: int start = pos;
195: while (page.charAt(pos) != '"') {
196: pos++;
197: }
198: int end = pos;
199: read("\"");
200: String s = page.substring(start, end);
201: return PageParser.parse(server, s, settings);
202: }
203:
204: private void skipSpaces() {
205: while (page.charAt(pos) == ' ') {
206: pos++;
207: }
208: }
209:
210: private void read(String s) throws Exception {
211: if (!readIf(s)) {
212: throw new Exception();
213: }
214: }
215:
216: private boolean readIf(String s) {
217: skipSpaces();
218: if (page.regionMatches(pos, s, 0, s.length())) {
219: pos += s.length();
220: skipSpaces();
221: return true;
222: }
223: return false;
224: }
225:
226: /**
227: * Convert data to HTML, but don't convert newlines and multiple spaces.
228: *
229: * @param s the data
230: * @return the escaped html text
231: */
232: public static String escapeHtmlData(String s) {
233: return escapeHtml(s, false);
234: }
235:
236: /**
237: * Convert data to HTML, including newlines and multiple spaces.
238: *
239: * @param s the data
240: * @return the escaped html text
241: */
242: public static String escapeHtml(String s) {
243: return escapeHtml(s, true);
244: }
245:
246: private static String escapeHtml(String s,
247: boolean convertBreakAndSpace) {
248: if (s == null) {
249: return null;
250: }
251: if (convertBreakAndSpace) {
252: if (s.length() == 0) {
253: return " ";
254: }
255: }
256: StringBuffer buff = new StringBuffer(s.length());
257: boolean convertSpace = true;
258: for (int i = 0; i < s.length(); i++) {
259: char c = s.charAt(i);
260: if (c == ' ') {
261: if (convertSpace && convertBreakAndSpace) {
262: buff.append(" ");
263: } else {
264: buff.append(' ');
265: convertSpace = true;
266: }
267: continue;
268: } else {
269: convertSpace = false;
270: }
271: switch (c) {
272: case '$':
273: // so that ${ } in the text is interpreted correctly
274: buff.append("$");
275: break;
276: case '<':
277: buff.append("<");
278: break;
279: case '>':
280: buff.append(">");
281: break;
282: case '&':
283: buff.append("&");
284: break;
285: case '"':
286: buff.append(""");
287: break;
288: case '\'':
289: buff.append("'");
290: break;
291: case '\n':
292: if (convertBreakAndSpace) {
293: buff.append("<br />");
294: convertSpace = true;
295: } else {
296: buff.append(c);
297: }
298: break;
299: default:
300: if (c >= 128) {
301: buff.append("&#");
302: buff.append((int) c);
303: buff.append(';');
304: } else {
305: buff.append(c);
306: }
307: }
308: }
309: return buff.toString();
310: }
311:
312: public static String escapeJavaScript(String s) {
313: if (s == null) {
314: return null;
315: }
316: if (s.length() == 0) {
317: return "";
318: }
319: StringBuffer buff = new StringBuffer(s.length());
320: for (int i = 0; i < s.length(); i++) {
321: char c = s.charAt(i);
322: switch (c) {
323: case '"':
324: buff.append("\\\"");
325: break;
326: case '\'':
327: buff.append("\\'");
328: break;
329: case '\\':
330: buff.append("\\\\");
331: break;
332: case '\n':
333: buff.append("\\n");
334: break;
335: case '\r':
336: buff.append("\\r");
337: break;
338: case '\t':
339: buff.append("\\t");
340: break;
341: default:
342: buff.append(c);
343: }
344: }
345: return buff.toString();
346: // return escapeHtml(buff.toString());
347: }
348: }
|