/* JsContentRenderer.java Purpose: Description: History: Wed Oct 1 19:08:57 2008, Created by tomyeh Copyright (C) 2008 Potix Corporation. All Rights Reserved. {{IS_RIGHT This program is distributed under LGPL Version 2.1 in the hope that it will be useful, but WITHOUT ANY WARRANTY. }}IS_RIGHT */ package org.zkoss.zk.ui.sys; import java.math.BigDecimal; import java.util.Date; import java.util.Iterator; import java.util.List; import java.util.Map; import org.zkoss.json.JSONAware; import org.zkoss.json.JSONs; import org.zkoss.lang.Strings; import org.zkoss.zk.ui.Component; import org.zkoss.zk.ui.UiException; /** * An implementation of {@link ContentRenderer} that renders * the content as a JavaScript property (i.e., name: 'value'). * @author tomyeh * @since 5.0.0 */ public class JsContentRenderer implements ContentRenderer { private final StringBuilder _buf = new StringBuilder(128); public JsContentRenderer() { } /** Returns the content being rendered. */ public CharSequence getBuffer() { return _buf; } /** Renders a string property. * @param name the property name. Note: it must be a legal JavaScript * variable name. */ public void render(String name, String value) { renderName(name); renderValue(value); } /** Renders a Date property. * @param name the property name. Note: it must be a legal JavaScript * variable name. */ public void render(String name, Date value) { renderName(name); renderValue(value); } /** Renders an arbitrary object. */ public void render(String name, Object value) { renderName(name); renderValue(value); } /** Renders an integer property. * @param name the property name. Note: it must be a legal JavaScript * variable name. */ public void render(String name, int value) { renderName(name); renderValue(value); } /** Renders a long property. * @param name the property name. Note: it must be a legal JavaScript * variable name. */ public void render(String name, long value) { renderName(name); renderValue(value); } /** Renders a short property. * @param name the property name. Note: it must be a legal JavaScript * variable name. */ public void render(String name, short value) { renderName(name); renderValue(value); } /** Renders a byte property. * @param name the property name. Note: it must be a legal JavaScript * variable name. */ public void render(String name, byte value) { renderName(name); renderValue(value); } /** Renders a boolean property. * @param name the property name. Note: it must be a legal JavaScript * variable name. */ public void render(String name, boolean value) { renderName(name); renderValue(value); } /** Renders a double property. * @param name the property name. Note: it must be a legal JavaScript * variable name. */ public void render(String name, double value) { renderName(name); renderValue(value); } /** Renders a double property. * @param name the property name. Note: it must be a legal JavaScript * variable name. */ public void render(String name, float value) { renderName(name); renderValue(value); } /** Renders a char property. * @param name the property name. Note: it must be a legal JavaScript * variable name. */ public void render(String name, char value) { renderName(name); renderValue(value); } private void renderName(String name) { if (_buf.length() > 0) _buf.append(','); _buf.append(name).append(':'); } private void renderValue(String value) { if (value == null) _buf.append((String) null); else { _buf.append('\''); _buf.append(Strings.escapeJavaScript(value)); _buf.append('\''); } } private void renderValue(Date value) { if (value == null) _buf.append((String) null); else _buf.append("jq.j2d('").append(JSONs.d2j(value)).append("')"); } private void renderValue(Component value) { if (value == null || value.getPage() == null) _buf.append((String) null); else _buf.append("{$u:'").append(value.getUuid()).append("'}"); } private void renderValue(Object value) { if (value == null || value instanceof String) { renderValue((String) value); return; } if (value instanceof Date) { renderValue((Date) value); return; } if (value instanceof Component) { renderValue((Component) value); return; } if (value instanceof Integer) { renderValue(((Integer) value).intValue()); return; } if (value instanceof Long) { renderValue(((Long) value).longValue()); return; } if (value instanceof Double) { renderValue(((Double) value).doubleValue()); return; } if (value instanceof Short) { renderValue(((Short) value).shortValue()); return; } if (value instanceof Byte) { renderValue(((Byte) value).byteValue()); return; } if (value instanceof Boolean) { renderValue(((Boolean) value).booleanValue()); return; } if (value instanceof Float) { renderValue(((Float) value).floatValue()); return; } if (value instanceof Character) { renderValue(((Character) value).charValue()); return; } if (value instanceof BigDecimal) { renderValue(((BigDecimal) value).toString()); return; } if (value instanceof Map) { _buf.append('{'); boolean first = true; for (Iterator it = ((Map) value).entrySet().iterator(); it.hasNext();) { final Map.Entry me = (Map.Entry) it.next(); if (first) first = false; else _buf.append(','); _buf.append('\'').append(me.getKey()).append("':"); renderValue(me.getValue()); } _buf.append('}'); return; } if (value instanceof List) { _buf.append('['); int j = 0; for (Iterator it = ((List) value).iterator(); it.hasNext(); j++) { if (j > 0) _buf.append(','); renderValue(it.next()); } _buf.append(']'); return; } //handle array if (value instanceof Object[]) { _buf.append('['); final Object[] ary = (Object[]) value; for (int j = 0; j < ary.length; ++j) { if (j > 0) _buf.append(','); renderValue(ary[j]); } _buf.append(']'); return; } if (value instanceof int[]) { _buf.append('['); final int[] ary = (int[]) value; for (int j = 0; j < ary.length; ++j) { if (j > 0) _buf.append(','); renderValue(ary[j]); } _buf.append(']'); return; } if (value instanceof long[]) { _buf.append('['); final long[] ary = (long[]) value; for (int j = 0; j < ary.length; ++j) { if (j > 0) _buf.append(','); renderValue(ary[j]); } _buf.append(']'); return; } if (value instanceof short[]) { _buf.append('['); final short[] ary = (short[]) value; for (int j = 0; j < ary.length; ++j) { if (j > 0) _buf.append(','); renderValue(ary[j]); } _buf.append(']'); return; } if (value instanceof float[]) { _buf.append('['); final float[] ary = (float[]) value; for (int j = 0; j < ary.length; ++j) { if (j > 0) _buf.append(','); renderValue(ary[j]); } _buf.append(']'); return; } if (value instanceof double[]) { _buf.append('['); final double[] ary = (double[]) value; for (int j = 0; j < ary.length; ++j) { if (j > 0) _buf.append(','); renderValue(ary[j]); } _buf.append(']'); return; } if (value instanceof byte[]) { _buf.append('['); final byte[] ary = (byte[]) value; for (int j = 0; j < ary.length; ++j) { if (j > 0) _buf.append(','); renderValue(ary[j]); } _buf.append(']'); return; } if (value instanceof char[]) { _buf.append('['); final char[] ary = (char[]) value; for (int j = 0; j < ary.length; ++j) { if (j > 0) _buf.append(','); renderValue(ary[j]); } _buf.append(']'); return; } if (value instanceof JSONAware) _buf.append(((JSONAware) value).toJSONString()); else renderValue(value.toString()); } private void renderValue(int value) { _buf.append(value); } private void renderValue(long value) { _buf.append(value); } private void renderValue(short value) { _buf.append(value); } private void renderValue(byte value) { _buf.append(value); } private void renderValue(boolean value) { _buf.append(value); } private void renderValue(double value) { _buf.append(value); } private void renderValue(float value) { _buf.append(value); } private void renderValue(char value) { _buf.append('\''); switch (value) { case '\'': case '\\': _buf.append('\\'); break; case '\n': _buf.append('\\'); value = 'n'; break; case '\t': _buf.append('\\'); value = 't'; break; case '\r': _buf.append('\\'); value = 'r'; break; case '\f': _buf.append('\\'); value = 'f'; break; } _buf.append(value).append('\''); } /** Renders the JavaScript code snippet. */ public void renderDirectly(String name, Object value) { renderName(name); if (value != null && !(value instanceof String)) throw new UnsupportedOperationException("Only String or null allowed, not " + value.toString()); _buf.append((String) value); } /** Renders the JavaScript code snippet for event listeners * registered for the peer widget. */ public void renderWidgetListeners(Map<String, String> listeners) { if (listeners == null || listeners.isEmpty()) return; renderName("listeners"); _buf.append('{'); for (Iterator it = listeners.entrySet().iterator(); it.hasNext();) { final Map.Entry me = (Map.Entry) it.next(); _buf.append(me.getKey()).append(":function(event){\n").append(me.getValue()).append("\n},"); } _buf.setCharAt(_buf.length() - 1, '}'); } /** Renders the JavaScript codes snippet to override the methods * and properties of the peer widget. * This method uses the widget's setOverrides method (at client), * so, if the value is a method, it will preserve the previous method * as '$' + method_name * * @param overrides a map of methods and properties. Notice that the value * must be a valid JavaScript snippet that can be evaluated to * a value. In fact, the map will be generated as follows.<br/> * <code>{name1: value1, name2: value2}</code>. * Examples of values: <code>function () {}</code>, <code>123</code>, * <code>new Date()</code>, and <code>"a literal string"</code> */ public void renderWidgetOverrides(Map<String, String> overrides) { if (overrides == null || overrides.isEmpty()) return; renderName("overrides"); _buf.append('{'); for (Iterator it = overrides.entrySet().iterator(); it.hasNext();) { final Map.Entry me = (Map.Entry) it.next(); final String name = (String) me.getKey(); final String value = (String) me.getValue(); if (value != null) { //It is too costly to detect if it is a legal expression //so we only check the most common illegal case final String v = value.trim(); char cc; if (v.length() != 0 && ((cc = v.charAt(v.length() - 1)) == ';' || cc == ',' || (v.indexOf("function") < 0 && v.indexOf(';') >= 0))) throw new UiException("Illegal client override: " + v + (name.startsWith("on") ? "\nTo listen an event, remember to captalize the third letter, such as onClick" : "\nIt must be a legal JavaScript expression (not statement)")); } _buf.append(name).append(":\n").append(value.length() == 0 ? "''" : value).append("\n,"); } _buf.setCharAt(_buf.length() - 1, '}'); } public void renderWidgetAttributes(Map<String, String> attrs) { renderClientAttributes(attrs); } public void renderClientAttributes(Map<String, String> attrs) { if (attrs == null || attrs.isEmpty()) return; renderName("domExtraAttrs"); _buf.append('{'); for (Iterator it = attrs.entrySet().iterator(); it.hasNext();) { final Map.Entry me = (Map.Entry) it.next(); renderValue(me.getKey()); //allow ':' or others _buf.append(':'); renderValue(me.getValue()); _buf.append("\n,"); } _buf.setCharAt(_buf.length() - 1, '}'); } }