/* AuResponse.java
Purpose:
Description:
History:
Tue May 31 11:35:49 2005, Created by tomyeh
Copyright (C) 2005 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.au;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import org.zkoss.json.JSONArray;
import org.zkoss.json.JSONAware;
import org.zkoss.json.JSONs;
import org.zkoss.lang.Objects;
import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.Page;
/**
* A response sent from the server to the client via
* {@link org.zkoss.zk.ui.sys.UiEngine}.
*
* <p>Application developers rarely need access this class and its derived
* directly.
* Rather, use {@link org.zkoss.zk.ui.util.Clients} instead.
* If you prefer to use the derives directly, you can use them with
* {@link org.zkoss.zk.ui.Execution#addAuResponse}.
*
* @author tomyeh
*/
public class AuResponse {
/** ZK-Error (5501) indicating the request is out-of-sequence.
* ZK-Error is returned as the response header of a failed AU request.
* @since 5.0.4
*/
public static final int SC_OUT_OF_SEQUENCE = 5501;
/** ZK-Error (5502) indicating the request is aborted, because it
* is waiting too long for the completion of the previous request.
* ZK-Error is returned as the response header of a failed AU request.
* @since 6.5.2
*/
public static final int SC_ACTIVATION_TIMEOUT = 5502;
protected String _cmd;
private final Object _depends;
/** Either String or DeferredValue. */
protected Object[] _data;
/** Constructs a component-independent response.
* @since 5.0.0 (becomes public)
*/
public AuResponse(String cmd) {
this(cmd, (Component) null, (Object[]) null);
}
/** Constructs a component-independent response.
*
* @param data the data. It can be null, String, Date,
* and any kind of objects that
* the client accepts (marshaled by JSON).
* @since 5.0.0 (becomes public)
*/
public AuResponse(String cmd, Object data) {
this(cmd, (Component) null, data);
}
/** Constructs a component-independent response.
* @since 5.0.0 (becomes public)
*/
public AuResponse(String cmd, Object[] data) {
this(cmd, (Component) null, data);
}
/** Constructs a response with one or zero data.
*
* @param depends specifies whether this response depends on whether
* the depends component.
* If depends is not null, this response shall be purged if the depends
* component is removed.
* If null, this response is called component-independent, and
* always sent to the client.
*
* <p>Note: info of the depends component doesn't send to the client.
* It is used only to optimize what responses to send.
*
* @param data specifies the data to be sent. If null, no data at all.
* @since 5.0.0 (becomes public)
*/
public AuResponse(String cmd, Component depends, Object data) {
this(cmd, depends, data != null ? new Object[] { data } : null);
}
/** Constructs a response with multiple data.
* @since 5.0.0 (becomes public)
*/
public AuResponse(String cmd, Component depends, Object[] data) {
if (cmd == null || cmd.length() == 0)
throw new IllegalArgumentException("cmd");
_cmd = cmd;
_depends = depends;
_data = data;
}
/** Constructs a response with single data.
* @since 5.0.0 (becomes public)
*/
public AuResponse(String cmd, Page depends, Object data) {
this(cmd, depends, data != null ? new Object[] { data } : null);
}
/** Constructs a response with multiple data.
* @param data an array of data (null to ignore).
* Each element must be an instance of String, {@link DeferredValue}
* or null.
* @exception IllegalArgumentException if an element of data
* is neither String nor DeferredValue.
* @since 5.0.0 (becomes public)
*/
public AuResponse(String cmd, Page depends, Object[] data) {
if (cmd == null || cmd.length() == 0)
throw new IllegalArgumentException("cmd");
_cmd = cmd;
_depends = depends;
_data = data;
}
/** Returns the command of this response (never null).
*/
public String getCommand() {
return _cmd;
}
/** Returns the evaluated result of the associated data of
* this response (might be null).
*
* <p>Note: when this method is called, {@link DeferredValue}
* will be evaluated. Thus, don't call it until the rendering phase.
* If you want to access it other than the render phase,
* use {@link #getRawData} instead.
*
* <p>Note: it is a copy, so any modification to it won't affect
* the data of this response.
*
* @see #getRawData
* @since 5.0.0
*/
public List<Object> getEncodedData() {
if (_data == null)
return null;
final JSONArray encdata = new JSONArray();
for (int j = 0; j < _data.length; ++j) {
Object d = _data[j];
if (d instanceof DeferredValue)
d = ((DeferredValue) d).getValue();
if (d instanceof Component)
d = new JSONComponent((Component) d);
if (d instanceof Date)
d = new JSONDate((Date) d);
encdata.add(d);
}
return encdata;
}
/** Returns the associated data of this response in the original
* format (might be null).
* <p>Note: it is a readonly array. Don't change its value.
* @see #getEncodedData
* @since 3.0.5
*/
public Object[] getRawData() {
return _data;
}
/** Returns the component or page that this response depends on.
* If it is not null and the depends component/page is removed,
* this response shall be removed, too.
*
* <p>Note: the returned object is either a {@link Component} or a
* {@link Page}.
*/
public final Object getDepends() {
return _depends;
}
/** Returns the override key. If null, this response will be appended.
* If not null, it overrides the previous response, if any, with
* the same override key and the same depends ({@link #getDepends}).
* <p>Default: return null.
* The derived class shall override this method if it prefers to send
* the last response in the same category.
* <p>Notice that if depends is null and the override key is not null,
* they are sharing a single namespace of an execution.
* <p>Notice that if {@link org.zkoss.zk.ui.AbstractComponent#response(String,AuResponse)}
* is called, this override key is ignored (and the first argument of
* the invocation is used).
* @since 5.0.2
*/
public String getOverrideKey() {
return null;
}
//-- Object --//
public int hashCode() {
int hash = 1;
if (_data != null) {
hash = 31 * hash + Arrays.hashCode(_data);
}
if (_cmd != null) {
hash = 31 * hash + _cmd.hashCode();
}
return hash;
}
public final boolean equals(Object o) { //prevent override
return this == o;
}
public String toString() {
//Don't call getCommand and getData since it causes
//AuSetDeferredAttribute to evaluate the deferred value
final StringBuffer sb = new StringBuffer(60).append("[cmd=").append(_cmd);
if (_data != null && _data.length > 0) {
sb.append(", data0=").append(_data[0]);
if (_data.length > 1) {
sb.append(", data1=").append(trimOutput(_data[1]));
if (_data.length > 2)
sb.append(", data2=").append(trimOutput(_data[2]));
}
}
return sb.append(']').toString();
}
private static String trimOutput(Object data) {
String s = Objects.toString(data);
if (s == null)
return null;
s = s.trim();
return s.length() <= 36 ? s : s.substring(0, 36) + "...";
}
private static class JSONDate implements JSONAware {
private final Date _d;
private JSONDate(Date d) {
_d = d;
}
public String toJSONString() {
return "jq.j2d('" + JSONs.d2j(_d) + "')";
}
}
private static class JSONComponent implements JSONAware {
private final Component _comp;
private JSONComponent(Component comp) {
_comp = comp;
}
public String toJSONString() {
return _comp.getPage() == null ? "null" : "{$u:'" + _comp.getUuid() + "'}";
}
}
}