You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@struts.apache.org by mu...@apache.org on 2009/07/29 20:33:08 UTC
svn commit: r799003 [2/4] - in /struts/sandbox/trunk/struts2-json-plugin: ./
src/ src/main/ src/main/java/ src/main/java/org/ src/main/java/org/apache/
src/main/java/org/apache/struts2/ src/main/java/org/apache/struts2/json/
src/main/java/org/apache/st...
Added: struts/sandbox/trunk/struts2-json-plugin/src/main/java/org/apache/struts2/json/JSONUtil.java
URL: http://svn.apache.org/viewvc/struts/sandbox/trunk/struts2-json-plugin/src/main/java/org/apache/struts2/json/JSONUtil.java?rev=799003&view=auto
==============================================================================
--- struts/sandbox/trunk/struts2-json-plugin/src/main/java/org/apache/struts2/json/JSONUtil.java (added)
+++ struts/sandbox/trunk/struts2-json-plugin/src/main/java/org/apache/struts2/json/JSONUtil.java Wed Jul 29 18:33:05 2009
@@ -0,0 +1,395 @@
+/*
+ * $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.struts2.json;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PrintWriter;
+import java.io.Reader;
+import java.io.Writer;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.regex.Pattern;
+import java.util.zip.GZIPOutputStream;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.commons.lang.xwork.StringUtils;
+import org.apache.struts2.json.annotations.SMDMethod;
+
+import com.opensymphony.xwork2.util.logging.Logger;
+import com.opensymphony.xwork2.util.logging.LoggerFactory;
+
+/**
+ * Wrapper for JSONWriter with some utility methods.
+ */
+public class JSONUtil {
+ final static String RFC3339_FORMAT = "yyyy-MM-dd'T'HH:mm:ss";
+ private static final Logger LOG = LoggerFactory.getLogger(JSONUtil.class);
+
+ /**
+ * Serializes an object into JSON.
+ *
+ * @param object
+ * to be serialized
+ * @return JSON string
+ * @throws JSONException
+ */
+ public static String serialize(Object object) throws JSONException {
+ JSONWriter writer = new JSONWriter();
+
+ return writer.write(object);
+ }
+
+ /**
+ * Serializes an object into JSON, excluding any properties matching any of
+ * the regular expressions in the given collection.
+ *
+ * @param object
+ * to be serialized
+ * @param excludeProperties
+ * Patterns matching properties to exclude
+ * @param ignoreHierarchy
+ * whether to ignore properties defined on base classes of the
+ * root object
+ * @return JSON string
+ * @throws JSONException
+ */
+ public static String serialize(Object object, Collection<Pattern> excludeProperties,
+ Collection<Pattern> includeProperties, boolean ignoreHierarchy, boolean excludeNullProperties)
+ throws JSONException {
+ JSONWriter writer = new JSONWriter();
+ writer.setIgnoreHierarchy(ignoreHierarchy);
+ return writer.write(object, excludeProperties, includeProperties, excludeNullProperties);
+ }
+
+ /**
+ * Serializes an object into JSON, excluding any properties matching any of
+ * the regular expressions in the given collection.
+ *
+ * @param object
+ * to be serialized
+ * @param excludeProperties
+ * Patterns matching properties to exclude
+ * @param ignoreHierarchy
+ * whether to ignore properties defined on base classes of the
+ * root object
+ * @param enumAsBean
+ * whether to serialized enums a Bean or name=value pair
+ * @return JSON string
+ * @throws JSONException
+ */
+ public static String serialize(Object object, Collection<Pattern> excludeProperties,
+ Collection<Pattern> includeProperties, boolean ignoreHierarchy, boolean enumAsBean,
+ boolean excludeNullProperties) throws JSONException {
+ JSONWriter writer = new JSONWriter();
+ writer.setIgnoreHierarchy(ignoreHierarchy);
+ writer.setEnumAsBean(enumAsBean);
+ return writer.write(object, excludeProperties, includeProperties, excludeNullProperties);
+ }
+
+ /**
+ * Serializes an object into JSON to the given writer.
+ *
+ * @param writer
+ * Writer to serialize the object to
+ * @param object
+ * object to be serialized
+ * @throws IOException
+ * @throws JSONException
+ */
+ public static void serialize(Writer writer, Object object) throws IOException, JSONException {
+ writer.write(serialize(object));
+ }
+
+ /**
+ * Serializes an object into JSON to the given writer, excluding any
+ * properties matching any of the regular expressions in the given
+ * collection.
+ *
+ * @param writer
+ * Writer to serialize the object to
+ * @param object
+ * object to be serialized
+ * @param excludeProperties
+ * Patterns matching properties to ignore
+ * @throws IOException
+ * @throws JSONException
+ */
+ public static void serialize(Writer writer, Object object, Collection<Pattern> excludeProperties,
+ Collection<Pattern> includeProperties, boolean excludeNullProperties) throws IOException,
+ JSONException {
+ writer.write(serialize(object, excludeProperties, includeProperties, true, excludeNullProperties));
+ }
+
+ /**
+ * Deserializes a object from JSON
+ *
+ * @param json
+ * string in JSON
+ * @return desrialized object
+ * @throws JSONException
+ */
+ public static Object deserialize(String json) throws JSONException {
+ JSONReader reader = new JSONReader();
+ return reader.read(json);
+ }
+
+ /**
+ * Deserializes a object from JSON
+ *
+ * @param reader
+ * Reader to read a JSON string from
+ * @return deserialized object
+ * @throws JSONException
+ * when IOException happens
+ */
+ public static Object deserialize(Reader reader) throws JSONException {
+ // read content
+ BufferedReader bufferReader = new BufferedReader(reader);
+ String line = null;
+ StringBuilder buffer = new StringBuilder();
+
+ try {
+ while ((line = bufferReader.readLine()) != null) {
+ buffer.append(line);
+ }
+ } catch (IOException e) {
+ throw new JSONException(e);
+ }
+
+ return deserialize(buffer.toString());
+ }
+
+ public static void writeJSONToResponse(SerializationParams serializationParams) throws IOException {
+ StringBuilder stringBuilder = new StringBuilder();
+ if (StringUtils.isNotBlank(serializationParams.getSerializedJSON()))
+ stringBuilder.append(serializationParams.getSerializedJSON());
+
+ if (StringUtils.isNotBlank(serializationParams.getWrapPrefix()))
+ stringBuilder.insert(0, serializationParams.getWrapPrefix());
+ else if (serializationParams.isWrapWithComments()) {
+ stringBuilder.insert(0, "/* ");
+ stringBuilder.append(" */");
+ } else if (serializationParams.isPrefix())
+ stringBuilder.insert(0, "{}&& ");
+
+ if (StringUtils.isNotBlank(serializationParams.getWrapSuffix()))
+ stringBuilder.append(serializationParams.getWrapSuffix());
+
+ String json = stringBuilder.toString();
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("[JSON]" + json);
+ }
+
+ HttpServletResponse response = serializationParams.getResponse();
+
+ // status or error code
+ if (serializationParams.getStatusCode() > 0)
+ response.setStatus(serializationParams.getStatusCode());
+ else if (serializationParams.getErrorCode() > 0)
+ response.sendError(serializationParams.getErrorCode());
+
+ // content type
+ if (serializationParams.isSmd())
+ response.setContentType("application/json-rpc;charset=" + serializationParams.getEncoding());
+ else
+ response.setContentType(serializationParams.getContentType() + ";charset="
+ + serializationParams.getEncoding());
+
+ if (serializationParams.isNoCache()) {
+ response.setHeader("Cache-Control", "no-cache");
+ response.setHeader("Expires", "0");
+ response.setHeader("Pragma", "No-cache");
+ }
+
+ if (serializationParams.isGzip()) {
+ response.addHeader("Content-Encoding", "gzip");
+ GZIPOutputStream out = null;
+ InputStream in = null;
+ try {
+ out = new GZIPOutputStream(response.getOutputStream());
+ in = new ByteArrayInputStream(json.getBytes());
+ byte[] buf = new byte[1024];
+ int len;
+ while ((len = in.read(buf)) > 0) {
+ out.write(buf, 0, len);
+ }
+ } finally {
+ if (in != null)
+ in.close();
+ if (out != null) {
+ out.finish();
+ out.close();
+ }
+ }
+
+ } else {
+ response.setContentLength(json.getBytes(serializationParams.getEncoding()).length);
+ PrintWriter out = response.getWriter();
+ out.print(json);
+ }
+ }
+
+ public static List<String> asList(String commaDelim) {
+ if ((commaDelim == null) || (commaDelim.trim().length() == 0))
+ return null;
+ List<String> list = new ArrayList<String>();
+ String[] split = commaDelim.split(",");
+ for (int i = 0; i < split.length; i++) {
+ String trimmed = split[i].trim();
+ if (trimmed.length() > 0) {
+ list.add(trimmed);
+ }
+ }
+ return list;
+ }
+
+ /**
+ * List visible methods carrying the
+ *
+ * @SMDMethod annotation
+ *
+ * @param ignoreInterfaces
+ * if true, only the methods of the class are examined. If false,
+ * annotations on every interfaces' methods are examined.
+ */
+ @SuppressWarnings("unchecked")
+ public static Method[] listSMDMethods(Class clazz, boolean ignoreInterfaces) {
+ final List<Method> methods = new LinkedList<Method>();
+ if (ignoreInterfaces) {
+ for (Method method : clazz.getMethods()) {
+ SMDMethod smdMethodAnnotation = method.getAnnotation(SMDMethod.class);
+ if (smdMethodAnnotation != null) {
+ methods.add(method);
+ }
+ }
+ } else {
+ // recurse the entire superclass/interface hierarchy and add in
+ // order encountered
+ JSONUtil.visitInterfaces(clazz, new JSONUtil.ClassVisitor() {
+ public boolean visit(Class aClass) {
+ for (Method method : aClass.getMethods()) {
+ SMDMethod smdMethodAnnotation = method.getAnnotation(SMDMethod.class);
+ if ((smdMethodAnnotation != null) && !methods.contains(method)) {
+ methods.add(method);
+ }
+ }
+ return true;
+ }
+ });
+ }
+
+ Method[] methodResult = new Method[methods.size()];
+ return methods.toArray(methodResult);
+ }
+
+ /**
+ * Realizes the visit(Class) method called by vistInterfaces for all
+ * encountered classes/interfaces
+ */
+ public static interface ClassVisitor {
+
+ /**
+ * Called when a new interface/class is encountered
+ *
+ * @param aClass
+ * the encountered class/interface
+ * @return true if the recursion should continue, false to stop
+ * recursion immediately
+ */
+ @SuppressWarnings("unchecked")
+ boolean visit(Class aClass);
+ }
+
+ /**
+ * Visit all the interfaces realized by the specified object, its
+ * superclasses and its interfaces <p/> Visitation is performed in the
+ * following order: aClass aClass' interfaces the interface's superclasses
+ * (interfaces) aClass' superclass superclass' interfaces superclass'
+ * interface's superclasses (interfaces) super-superclass and so on <p/> The
+ * Object base class is base excluded. Classes/interfaces are only visited
+ * once each
+ *
+ * @param aClass
+ * the class to start recursing upwards from
+ * @param visitor
+ * this vistor is called for each class/interface encountered
+ * @return true if all classes/interfaces were visited, false if it was
+ * exited early as specified by a ClassVisitor result
+ */
+ @SuppressWarnings("unchecked")
+ public static boolean visitInterfaces(Class aClass, ClassVisitor visitor) {
+ List<Class> classesVisited = new LinkedList<Class>();
+ return visitUniqueInterfaces(aClass, visitor, classesVisited);
+ }
+
+ /**
+ * Recursive method to visit all the interfaces of a class (and its
+ * superclasses and super-interfaces) if they haven't already been visited.
+ * <p/> Always visits itself if it hasn't already been visited
+ *
+ * @param thisClass
+ * the current class to visit (if not already done so)
+ * @param classesVisited
+ * classes already visited
+ * @param visitor
+ * this vistor is called for each class/interface encountered
+ * @return true if recursion can continue, false if it should be aborted
+ */
+ private static boolean visitUniqueInterfaces(Class thisClass, ClassVisitor visitor,
+ List<Class> classesVisited) {
+ boolean okayToContinue = true;
+
+ if (!classesVisited.contains(thisClass)) {
+ classesVisited.add(thisClass);
+ okayToContinue = visitor.visit(thisClass);
+
+ if (okayToContinue) {
+ Class[] interfaces = thisClass.getInterfaces();
+ int index = 0;
+ while ((index < interfaces.length) && (okayToContinue)) {
+ okayToContinue = visitUniqueInterfaces(interfaces[index++], visitor, classesVisited);
+ }
+
+ if (okayToContinue) {
+ Class superClass = thisClass.getSuperclass();
+ if ((superClass != null) && (!Object.class.equals(superClass))) {
+ okayToContinue = visitUniqueInterfaces(superClass, visitor, classesVisited);
+ }
+ }
+ }
+ }
+ return okayToContinue;
+ }
+
+ public static boolean isGzipInRequest(HttpServletRequest request) {
+ String header = request.getHeader("Accept-Encoding");
+ return (header != null) && (header.indexOf("gzip") >= 0);
+ }
+}
Added: struts/sandbox/trunk/struts2-json-plugin/src/main/java/org/apache/struts2/json/JSONWriter.java
URL: http://svn.apache.org/viewvc/struts/sandbox/trunk/struts2-json-plugin/src/main/java/org/apache/struts2/json/JSONWriter.java?rev=799003&view=auto
==============================================================================
--- struts/sandbox/trunk/struts2-json-plugin/src/main/java/org/apache/struts2/json/JSONWriter.java (added)
+++ struts/sandbox/trunk/struts2-json-plugin/src/main/java/org/apache/struts2/json/JSONWriter.java Wed Jul 29 18:33:05 2009
@@ -0,0 +1,549 @@
+/*
+ * $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.struts2.json;
+
+import java.beans.BeanInfo;
+import java.beans.Introspector;
+import java.beans.PropertyDescriptor;
+import java.lang.reflect.Array;
+import java.lang.reflect.Method;
+import java.text.CharacterIterator;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.text.StringCharacterIterator;
+import java.util.Calendar;
+import java.util.Collection;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Stack;
+import java.util.regex.Pattern;
+
+import org.apache.struts2.json.annotations.JSON;
+
+import com.opensymphony.xwork2.util.logging.Logger;
+import com.opensymphony.xwork2.util.logging.LoggerFactory;
+
+/**
+ * <p>
+ * Serializes an object into JavaScript Object Notation (JSON). If cyclic
+ * references are detected they will be nulled out.
+ * </p>
+ */
+class JSONWriter {
+ private static final Logger LOG = LoggerFactory.getLogger(JSONWriter.class);
+
+ /**
+ * By default, enums are serialzied as name=value pairs
+ */
+ public static final boolean ENUM_AS_BEAN_DEFAULT = false;
+
+ static char[] hex = "0123456789ABCDEF".toCharArray();
+ private StringBuilder buf = new StringBuilder();
+ private Stack stack = new Stack();
+ private boolean ignoreHierarchy = true;
+ private Object root;
+ private boolean buildExpr = true;
+ private String exprStack = "";
+ private Collection<Pattern> excludeProperties;
+ private Collection<Pattern> includeProperties;
+ private DateFormat formatter;
+ private boolean enumAsBean = ENUM_AS_BEAN_DEFAULT;
+ private boolean excludeNullProperties;
+
+ /**
+ * @param object
+ * Object to be serialized into JSON
+ * @return JSON string for object
+ * @throws JSONException
+ */
+ public String write(Object object) throws JSONException {
+ return this.write(object, null, null, false);
+ }
+
+ /**
+ * @param object
+ * Object to be serialized into JSON
+ * @return JSON string for object
+ * @throws JSONException
+ */
+ public String write(Object object, Collection<Pattern> excludeProperties,
+ Collection<Pattern> includeProperties, boolean excludeNullProperties) throws JSONException {
+ this.excludeNullProperties = excludeNullProperties;
+ this.buf.setLength(0);
+ this.root = object;
+ this.exprStack = "";
+ this.buildExpr = ((excludeProperties != null) && !excludeProperties.isEmpty())
+ || ((includeProperties != null) && !includeProperties.isEmpty());
+ this.excludeProperties = excludeProperties;
+ this.includeProperties = includeProperties;
+ this.value(object, null);
+
+ return this.buf.toString();
+ }
+
+ /**
+ * Detect cyclic references
+ */
+ private void value(Object object, Method method) throws JSONException {
+ if (object == null) {
+ this.add("null");
+
+ return;
+ }
+
+ if (this.stack.contains(object)) {
+ Class clazz = object.getClass();
+
+ // cyclic reference
+ if (clazz.isPrimitive() || clazz.equals(String.class)) {
+ this.process(object, method);
+ } else {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Cyclic reference detected on " + object);
+ }
+
+ this.add("null");
+ }
+
+ return;
+ }
+
+ this.process(object, method);
+ }
+
+ /**
+ * Serialize object into json
+ */
+ private void process(Object object, Method method) throws JSONException {
+ this.stack.push(object);
+
+ if (object instanceof Class) {
+ this.string(object);
+ } else if (object instanceof Boolean) {
+ this.bool(((Boolean) object).booleanValue());
+ } else if (object instanceof Number) {
+ this.add(object);
+ } else if (object instanceof String) {
+ this.string(object);
+ } else if (object instanceof Character) {
+ this.string(object);
+ } else if (object instanceof Map) {
+ this.map((Map) object, method);
+ } else if (object.getClass().isArray()) {
+ this.array(object, method);
+ } else if (object instanceof Iterable) {
+ this.array(((Iterable) object).iterator(), method);
+ } else if (object instanceof Date) {
+ this.date((Date) object, method);
+ } else if (object instanceof Calendar) {
+ this.date(((Calendar) object).getTime(), method);
+ } else if (object instanceof Locale) {
+ this.string(object);
+ } else if (object instanceof Enum) {
+ this.enumeration((Enum) object);
+ } else {
+ this.bean(object);
+ }
+
+ this.stack.pop();
+ }
+
+ /**
+ * Instrospect bean and serialize its properties
+ */
+ private void bean(Object object) throws JSONException {
+ this.add("{");
+
+ BeanInfo info;
+
+ try {
+ Class clazz = object.getClass();
+
+ info = ((object == this.root) && this.ignoreHierarchy) ? Introspector.getBeanInfo(clazz, clazz
+ .getSuperclass()) : Introspector.getBeanInfo(clazz);
+
+ PropertyDescriptor[] props = info.getPropertyDescriptors();
+
+ boolean hasData = false;
+ for (int i = 0; i < props.length; ++i) {
+ PropertyDescriptor prop = props[i];
+ String name = prop.getName();
+ Method accessor = prop.getReadMethod();
+ Method baseAccessor = null;
+ if (clazz.getName().indexOf("$$EnhancerByCGLIB$$") > -1) {
+ try {
+ baseAccessor = Class.forName(
+ clazz.getName().substring(0, clazz.getName().indexOf("$$"))).getMethod(
+ accessor.getName(), accessor.getParameterTypes());
+ } catch (Exception ex) {
+ LOG.debug(ex.getMessage(), ex);
+ }
+ } else
+ baseAccessor = accessor;
+
+ if (baseAccessor != null) {
+
+ JSON json = baseAccessor.getAnnotation(JSON.class);
+ if (json != null) {
+ if (!json.serialize())
+ continue;
+ else if (json.name().length() > 0)
+ name = json.name();
+ }
+
+ // ignore "class" and others
+ if (this.shouldExcludeProperty(clazz, prop)) {
+ continue;
+ }
+ String expr = null;
+ if (this.buildExpr) {
+ expr = this.expandExpr(name);
+ if (this.shouldExcludeProperty(expr)) {
+ continue;
+ }
+ expr = this.setExprStack(expr);
+ }
+
+ Object value = accessor.invoke(object, new Object[0]);
+ boolean propertyPrinted = this.add(name, value, accessor, hasData);
+ hasData = hasData || propertyPrinted;
+ if (this.buildExpr) {
+ this.setExprStack(expr);
+ }
+ }
+ }
+
+ // special-case handling for an Enumeration - include the name() as
+ // a property */
+ if (object instanceof Enum) {
+ Object value = ((Enum) object).name();
+ this.add("_name", value, object.getClass().getMethod("name"), hasData);
+ }
+ } catch (Exception e) {
+ throw new JSONException(e);
+ }
+
+ this.add("}");
+ }
+
+ /**
+ * Instrospect an Enum and serialize it as a name/value pair or as a bean
+ * including all its own properties
+ */
+ private void enumeration(Enum enumeration) throws JSONException {
+ if (enumAsBean) {
+ this.bean(enumeration);
+ } else {
+ this.string(enumeration.name());
+ }
+ }
+
+ /**
+ * Ignore "class" field
+ */
+ private boolean shouldExcludeProperty(Class clazz, PropertyDescriptor prop) throws SecurityException,
+ NoSuchFieldException {
+ String name = prop.getName();
+
+ if (name.equals("class") || name.equals("declaringClass") || name.equals("cachedSuperClass")
+ || name.equals("metaClass")) {
+ return true;
+ }
+
+ return false;
+ }
+
+ private String expandExpr(int i) {
+ return this.exprStack + "[" + i + "]";
+ }
+
+ private String expandExpr(String property) {
+ if (this.exprStack.length() == 0)
+ return property;
+ return this.exprStack + "." + property;
+ }
+
+ private String setExprStack(String expr) {
+ String s = this.exprStack;
+ this.exprStack = expr;
+ return s;
+ }
+
+ private boolean shouldExcludeProperty(String expr) {
+ if (this.excludeProperties != null) {
+ for (Pattern pattern : this.excludeProperties) {
+ if (pattern.matcher(expr).matches()) {
+ if (LOG.isDebugEnabled())
+ LOG.debug("Ignoring property because of exclude rule: " + expr);
+ return true;
+ }
+ }
+ }
+
+ if (this.includeProperties != null) {
+ for (Pattern pattern : this.includeProperties) {
+ if (pattern.matcher(expr).matches()) {
+ return false;
+ }
+ }
+
+ if (LOG.isDebugEnabled())
+ LOG.debug("Ignoring property because of include rule: " + expr);
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Add name/value pair to buffer
+ */
+ private boolean add(String name, Object value, Method method, boolean hasData) throws JSONException {
+ if (!excludeNullProperties || (value != null)) {
+ if (hasData) {
+ this.add(',');
+ }
+ this.add('"');
+ this.add(name);
+ this.add("\":");
+ this.value(value, method);
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Add map to buffer
+ */
+ private void map(Map map, Method method) throws JSONException {
+ this.add("{");
+
+ Iterator it = map.entrySet().iterator();
+
+ boolean warnedNonString = false; // one report per map
+ boolean hasData = false;
+ while (it.hasNext()) {
+ Map.Entry entry = (Map.Entry) it.next();
+ Object key = entry.getKey();
+ String expr = null;
+ if (this.buildExpr) {
+ if (key == null) {
+ LOG.error("Cannot build expression for null key in " + this.exprStack);
+ continue;
+ } else {
+ expr = this.expandExpr(key.toString());
+ if (this.shouldExcludeProperty(expr)) {
+ continue;
+ }
+ expr = this.setExprStack(expr);
+ }
+ }
+ if (hasData) {
+ this.add(',');
+ }
+ hasData = true;
+ if (!warnedNonString && !(key instanceof String)) {
+ LOG.warn("JavaScript doesn't support non-String keys, using toString() on "
+ + key.getClass().getName());
+ warnedNonString = true;
+ }
+ this.value(key.toString(), method);
+ this.add(":");
+ this.value(entry.getValue(), method);
+ if (this.buildExpr) {
+ this.setExprStack(expr);
+ }
+ }
+
+ this.add("}");
+ }
+
+ /**
+ * Add date to buffer
+ */
+ private void date(Date date, Method method) {
+ JSON json = null;
+ if (method != null)
+ json = method.getAnnotation(JSON.class);
+ if (this.formatter == null)
+ this.formatter = new SimpleDateFormat(JSONUtil.RFC3339_FORMAT);
+
+ DateFormat formatter = (json != null) && (json.format().length() > 0) ? new SimpleDateFormat(json
+ .format()) : this.formatter;
+ this.string(formatter.format(date));
+ }
+
+ /**
+ * Add array to buffer
+ */
+ private void array(Iterator it, Method method) throws JSONException {
+ this.add("[");
+
+ boolean hasData = false;
+ for (int i = 0; it.hasNext(); i++) {
+ String expr = null;
+ if (this.buildExpr) {
+ expr = this.expandExpr(i);
+ if (this.shouldExcludeProperty(expr)) {
+ it.next();
+ continue;
+ }
+ expr = this.setExprStack(expr);
+ }
+ if (hasData) {
+ this.add(',');
+ }
+ hasData = true;
+ this.value(it.next(), method);
+ if (this.buildExpr) {
+ this.setExprStack(expr);
+ }
+ }
+
+ this.add("]");
+ }
+
+ /**
+ * Add array to buffer
+ */
+ private void array(Object object, Method method) throws JSONException {
+ this.add("[");
+
+ int length = Array.getLength(object);
+
+ boolean hasData = false;
+ for (int i = 0; i < length; ++i) {
+ String expr = null;
+ if (this.buildExpr) {
+ expr = this.expandExpr(i);
+ if (this.shouldExcludeProperty(expr)) {
+ continue;
+ }
+ expr = this.setExprStack(expr);
+ }
+ if (hasData) {
+ this.add(',');
+ }
+ hasData = true;
+ this.value(Array.get(object, i), method);
+ if (this.buildExpr) {
+ this.setExprStack(expr);
+ }
+ }
+
+ this.add("]");
+ }
+
+ /**
+ * Add boolean to buffer
+ */
+ private void bool(boolean b) {
+ this.add(b ? "true" : "false");
+ }
+
+ /**
+ * escape characters
+ */
+ private void string(Object obj) {
+ this.add('"');
+
+ CharacterIterator it = new StringCharacterIterator(obj.toString());
+
+ for (char c = it.first(); c != CharacterIterator.DONE; c = it.next()) {
+ if (c == '"') {
+ this.add("\\\"");
+ } else if (c == '\\') {
+ this.add("\\\\");
+ } else if (c == '/') {
+ this.add("\\/");
+ } else if (c == '\b') {
+ this.add("\\b");
+ } else if (c == '\f') {
+ this.add("\\f");
+ } else if (c == '\n') {
+ this.add("\\n");
+ } else if (c == '\r') {
+ this.add("\\r");
+ } else if (c == '\t') {
+ this.add("\\t");
+ } else if (Character.isISOControl(c)) {
+ this.unicode(c);
+ } else {
+ this.add(c);
+ }
+ }
+
+ this.add('"');
+ }
+
+ /**
+ * Add object to buffer
+ */
+ private void add(Object obj) {
+ this.buf.append(obj);
+ }
+
+ /**
+ * Add char to buffer
+ */
+ private void add(char c) {
+ this.buf.append(c);
+ }
+
+ /**
+ * Represent as unicode
+ *
+ * @param c
+ * character to be encoded
+ */
+ private void unicode(char c) {
+ this.add("\\u");
+
+ int n = c;
+
+ for (int i = 0; i < 4; ++i) {
+ int digit = (n & 0xf000) >> 12;
+
+ this.add(hex[digit]);
+ n <<= 4;
+ }
+ }
+
+ public void setIgnoreHierarchy(boolean ignoreHierarchy) {
+ this.ignoreHierarchy = ignoreHierarchy;
+ }
+
+ /**
+ * If true, an Enum is serialized as a bean with a special property
+ * _name=name() as all as all other properties defined within the enum.<br/>
+ * If false, an Enum is serialized as a name=value pair (name=name())
+ *
+ * @param enumAsBean
+ * true to serialize an enum as a bean instead of as a name=value
+ * pair (default=false)
+ */
+ public void setEnumAsBean(boolean enumAsBean) {
+ this.enumAsBean = enumAsBean;
+ }
+}
Added: struts/sandbox/trunk/struts2-json-plugin/src/main/java/org/apache/struts2/json/SerializationParams.java
URL: http://svn.apache.org/viewvc/struts/sandbox/trunk/struts2-json-plugin/src/main/java/org/apache/struts2/json/SerializationParams.java?rev=799003&view=auto
==============================================================================
--- struts/sandbox/trunk/struts2-json-plugin/src/main/java/org/apache/struts2/json/SerializationParams.java (added)
+++ struts/sandbox/trunk/struts2-json-plugin/src/main/java/org/apache/struts2/json/SerializationParams.java Wed Jul 29 18:33:05 2009
@@ -0,0 +1,100 @@
+package org.apache.struts2.json;
+
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.commons.lang.xwork.StringUtils;
+
+public class SerializationParams {
+ private static final String DEFAULT_CONTENT_TYPE = "application/json";
+
+ private final HttpServletResponse response;
+ private final String encoding;
+ private final boolean wrapWithComments;
+ private final String serializedJSON;
+ private final boolean smd;
+ private final boolean gzip;
+ private final boolean noCache;
+ private final int statusCode;
+ private final int errorCode;
+ private final boolean prefix;
+ private String contentType = DEFAULT_CONTENT_TYPE;
+ private String wrapPrefix;
+ private String wrapSuffix;
+
+ public SerializationParams(HttpServletResponse response, String encoding, boolean wrapWithComments,
+ String serializedJSON, boolean smd, boolean gzip, boolean noCache, int statusCode, int errorCode,
+ boolean prefix, String contentType, String wrapPrefix, String wrapSuffix) {
+ this.response = response;
+ this.encoding = encoding;
+ this.wrapWithComments = wrapWithComments;
+ this.serializedJSON = serializedJSON;
+ this.smd = smd;
+ this.gzip = gzip;
+ this.noCache = noCache;
+ this.statusCode = statusCode;
+ this.errorCode = errorCode;
+ this.prefix = prefix;
+ this.contentType = StringUtils.defaultString(contentType, DEFAULT_CONTENT_TYPE);
+ this.wrapPrefix = wrapPrefix;
+ this.wrapSuffix = wrapSuffix;
+ }
+
+ public SerializationParams(HttpServletResponse response, String defaultEncoding,
+ boolean wrapWithComments, String json, boolean b, boolean b1, boolean noCache, int i, int i1,
+ boolean prefix, String contentType) {
+ this(response, defaultEncoding, wrapWithComments, json, b, b1, noCache, i, i1, prefix, contentType,
+ null, null);
+ }
+
+ public String getWrapSuffix() {
+ return wrapSuffix;
+ }
+
+ public String getWrapPrefix() {
+ return wrapPrefix;
+ }
+
+ public HttpServletResponse getResponse() {
+ return response;
+ }
+
+ public String getEncoding() {
+ return encoding;
+ }
+
+ public boolean isWrapWithComments() {
+ return wrapWithComments;
+ }
+
+ public String getSerializedJSON() {
+ return serializedJSON;
+ }
+
+ public boolean isSmd() {
+ return smd;
+ }
+
+ public boolean isGzip() {
+ return gzip;
+ }
+
+ public boolean isNoCache() {
+ return noCache;
+ }
+
+ public int getStatusCode() {
+ return statusCode;
+ }
+
+ public int getErrorCode() {
+ return errorCode;
+ }
+
+ public boolean isPrefix() {
+ return prefix;
+ }
+
+ public String getContentType() {
+ return contentType;
+ }
+}
Added: struts/sandbox/trunk/struts2-json-plugin/src/main/java/org/apache/struts2/json/annotations/JSON.java
URL: http://svn.apache.org/viewvc/struts/sandbox/trunk/struts2-json-plugin/src/main/java/org/apache/struts2/json/annotations/JSON.java?rev=799003&view=auto
==============================================================================
--- struts/sandbox/trunk/struts2-json-plugin/src/main/java/org/apache/struts2/json/annotations/JSON.java (added)
+++ struts/sandbox/trunk/struts2-json-plugin/src/main/java/org/apache/struts2/json/annotations/JSON.java Wed Jul 29 18:33:05 2009
@@ -0,0 +1,18 @@
+package org.apache.struts2.json.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface JSON {
+ String name() default "";
+
+ boolean serialize() default true;
+
+ boolean deserialize() default true;
+
+ String format() default "";
+}
Added: struts/sandbox/trunk/struts2-json-plugin/src/main/java/org/apache/struts2/json/annotations/SMD.java
URL: http://svn.apache.org/viewvc/struts/sandbox/trunk/struts2-json-plugin/src/main/java/org/apache/struts2/json/annotations/SMD.java?rev=799003&view=auto
==============================================================================
--- struts/sandbox/trunk/struts2-json-plugin/src/main/java/org/apache/struts2/json/annotations/SMD.java (added)
+++ struts/sandbox/trunk/struts2-json-plugin/src/main/java/org/apache/struts2/json/annotations/SMD.java Wed Jul 29 18:33:05 2009
@@ -0,0 +1,36 @@
+/*
+ * $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.struts2.json.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface SMD {
+ String version() default org.apache.struts2.json.smd.SMD.DEFAULT_VERSION;
+
+ String objectName();
+
+ String serviceType() default org.apache.struts2.json.smd.SMD.DEFAULT_SERVICE_TYPE;
+}
Added: struts/sandbox/trunk/struts2-json-plugin/src/main/java/org/apache/struts2/json/annotations/SMDMethod.java
URL: http://svn.apache.org/viewvc/struts/sandbox/trunk/struts2-json-plugin/src/main/java/org/apache/struts2/json/annotations/SMDMethod.java?rev=799003&view=auto
==============================================================================
--- struts/sandbox/trunk/struts2-json-plugin/src/main/java/org/apache/struts2/json/annotations/SMDMethod.java (added)
+++ struts/sandbox/trunk/struts2-json-plugin/src/main/java/org/apache/struts2/json/annotations/SMDMethod.java Wed Jul 29 18:33:05 2009
@@ -0,0 +1,32 @@
+/*
+ * $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.struts2.json.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface SMDMethod {
+ String name() default "";
+}
Added: struts/sandbox/trunk/struts2-json-plugin/src/main/java/org/apache/struts2/json/annotations/SMDMethodParameter.java
URL: http://svn.apache.org/viewvc/struts/sandbox/trunk/struts2-json-plugin/src/main/java/org/apache/struts2/json/annotations/SMDMethodParameter.java?rev=799003&view=auto
==============================================================================
--- struts/sandbox/trunk/struts2-json-plugin/src/main/java/org/apache/struts2/json/annotations/SMDMethodParameter.java (added)
+++ struts/sandbox/trunk/struts2-json-plugin/src/main/java/org/apache/struts2/json/annotations/SMDMethodParameter.java Wed Jul 29 18:33:05 2009
@@ -0,0 +1,12 @@
+package org.apache.struts2.json.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target(ElementType.PARAMETER)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface SMDMethodParameter {
+ String name();
+}
Added: struts/sandbox/trunk/struts2-json-plugin/src/main/java/org/apache/struts2/json/rpc/RPCError.java
URL: http://svn.apache.org/viewvc/struts/sandbox/trunk/struts2-json-plugin/src/main/java/org/apache/struts2/json/rpc/RPCError.java?rev=799003&view=auto
==============================================================================
--- struts/sandbox/trunk/struts2-json-plugin/src/main/java/org/apache/struts2/json/rpc/RPCError.java (added)
+++ struts/sandbox/trunk/struts2-json-plugin/src/main/java/org/apache/struts2/json/rpc/RPCError.java Wed Jul 29 18:33:05 2009
@@ -0,0 +1,86 @@
+package org.apache.struts2.json.rpc;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+import com.opensymphony.xwork2.util.logging.Logger;
+import com.opensymphony.xwork2.util.logging.LoggerFactory;
+
+public class RPCError {
+ private static final Logger LOG = LoggerFactory.getLogger(RPCError.class);
+
+ private int code;
+ private String name;
+ private String message;
+ private String stack;
+
+ public RPCError() {
+ }
+
+ public RPCError(String message, int code) {
+ this.code = code;
+ this.message = message;
+
+ LOG.error(message);
+ }
+
+ public RPCError(String message, RPCErrorCode code) {
+ this(message, code.code());
+ }
+
+ public RPCError(Throwable t, int code, boolean debug) {
+ while (t.getCause() != null) {
+ t = t.getCause();
+ }
+
+ this.code = code;
+ this.message = t.getMessage();
+ this.name = t.getClass().getName();
+
+ if (debug) {
+ StringWriter s = new StringWriter();
+ PrintWriter w = new PrintWriter(s);
+ t.printStackTrace(w);
+ w.flush();
+ this.stack = s.toString();
+ }
+
+ LOG.error(t.getMessage(), t);
+ }
+
+ public RPCError(Throwable t, RPCErrorCode code, boolean debug) {
+ this(t, code.code(), debug);
+ }
+
+ public int getCode() {
+ return code;
+ }
+
+ public void setCode(int code) {
+ this.code = code;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getMessage() {
+ return message;
+ }
+
+ public void setMessage(String message) {
+ this.message = message;
+ }
+
+ public String getStack() {
+ return stack;
+ }
+
+ public void setStack(String stack) {
+ this.stack = stack;
+ }
+}
Added: struts/sandbox/trunk/struts2-json-plugin/src/main/java/org/apache/struts2/json/rpc/RPCErrorCode.java
URL: http://svn.apache.org/viewvc/struts/sandbox/trunk/struts2-json-plugin/src/main/java/org/apache/struts2/json/rpc/RPCErrorCode.java?rev=799003&view=auto
==============================================================================
--- struts/sandbox/trunk/struts2-json-plugin/src/main/java/org/apache/struts2/json/rpc/RPCErrorCode.java (added)
+++ struts/sandbox/trunk/struts2-json-plugin/src/main/java/org/apache/struts2/json/rpc/RPCErrorCode.java Wed Jul 29 18:33:05 2009
@@ -0,0 +1,25 @@
+package org.apache.struts2.json.rpc;
+
+public enum RPCErrorCode {
+ MISSING_METHOD(100, "'method' parameter is missing in request"), MISSING_ID(100,
+ "'id' parameter is missing in request"), INVALID_PROCEDURE_CALL(0, "Invalid procedure call"), METHOD_NOT_FOUND(
+ 101, "Procedure not found"), PARAMETERS_MISMATCH(102,
+ "Parameters count in request does not patch parameters count on method"), EXCEPTION(103,
+ "An exception was thrown"), SMD_DISABLED(104, "SMD is disabled");
+
+ private int code;
+ private String message;
+
+ RPCErrorCode(int code, String message) {
+ this.code = code;
+ this.message = message;
+ }
+
+ public int code() {
+ return code;
+ }
+
+ public String message() {
+ return this.message;
+ }
+}
Added: struts/sandbox/trunk/struts2-json-plugin/src/main/java/org/apache/struts2/json/rpc/RPCResponse.java
URL: http://svn.apache.org/viewvc/struts/sandbox/trunk/struts2-json-plugin/src/main/java/org/apache/struts2/json/rpc/RPCResponse.java?rev=799003&view=auto
==============================================================================
--- struts/sandbox/trunk/struts2-json-plugin/src/main/java/org/apache/struts2/json/rpc/RPCResponse.java (added)
+++ struts/sandbox/trunk/struts2-json-plugin/src/main/java/org/apache/struts2/json/rpc/RPCResponse.java Wed Jul 29 18:33:05 2009
@@ -0,0 +1,34 @@
+package org.apache.struts2.json.rpc;
+
+/**
+ * Class that will be serialized as a response to an RPC call
+ */
+public class RPCResponse {
+ private String id;
+ private Object result;
+ private RPCError error;
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public Object getResult() {
+ return result;
+ }
+
+ public void setResult(Object result) {
+ this.result = result;
+ }
+
+ public RPCError getError() {
+ return error;
+ }
+
+ public void setError(RPCError error) {
+ this.error = error;
+ }
+}
Added: struts/sandbox/trunk/struts2-json-plugin/src/main/java/org/apache/struts2/json/smd/SMD.java
URL: http://svn.apache.org/viewvc/struts/sandbox/trunk/struts2-json-plugin/src/main/java/org/apache/struts2/json/smd/SMD.java?rev=799003&view=auto
==============================================================================
--- struts/sandbox/trunk/struts2-json-plugin/src/main/java/org/apache/struts2/json/smd/SMD.java (added)
+++ struts/sandbox/trunk/struts2-json-plugin/src/main/java/org/apache/struts2/json/smd/SMD.java Wed Jul 29 18:33:05 2009
@@ -0,0 +1,75 @@
+/*
+ * $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.struts2.json.smd;
+
+import java.util.Set;
+import java.util.TreeSet;
+
+public class SMD {
+ public static final String DEFAULT_VERSION = ".1";
+ public static final String DEFAULT_SERVICE_TYPE = "JSON-RPC";
+
+ private String version = DEFAULT_VERSION;
+ private String objectName;
+ private String serviceType = DEFAULT_SERVICE_TYPE;
+ private String serviceUrl;
+ private Set<SMDMethod> methods = new TreeSet<SMDMethod>();
+
+ public void addSMDMethod(SMDMethod method) {
+ this.methods.add(method);
+ }
+
+ public String getVersion() {
+ return this.version;
+ }
+
+ public void setVersion(String version) {
+ this.version = version;
+ }
+
+ public String getObjectName() {
+ return this.objectName;
+ }
+
+ public void setObjectName(String objectName) {
+ this.objectName = objectName;
+ }
+
+ public String getServiceType() {
+ return this.serviceType;
+ }
+
+ public void setServiceType(String serviceType) {
+ this.serviceType = serviceType;
+ }
+
+ public String getServiceUrl() {
+ return this.serviceUrl;
+ }
+
+ public void setServiceUrl(String serviceUrl) {
+ this.serviceUrl = serviceUrl;
+ }
+
+ public Set<SMDMethod> getMethods() {
+ return this.methods;
+ }
+}
Added: struts/sandbox/trunk/struts2-json-plugin/src/main/java/org/apache/struts2/json/smd/SMDMethod.java
URL: http://svn.apache.org/viewvc/struts/sandbox/trunk/struts2-json-plugin/src/main/java/org/apache/struts2/json/smd/SMDMethod.java?rev=799003&view=auto
==============================================================================
--- struts/sandbox/trunk/struts2-json-plugin/src/main/java/org/apache/struts2/json/smd/SMDMethod.java (added)
+++ struts/sandbox/trunk/struts2-json-plugin/src/main/java/org/apache/struts2/json/smd/SMDMethod.java Wed Jul 29 18:33:05 2009
@@ -0,0 +1,75 @@
+/*
+ * $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.struts2.json.smd;
+
+import java.util.Set;
+import java.util.TreeSet;
+
+public class SMDMethod implements Comparable {
+ private String name;
+ private Set<SMDMethodParameter> parameters = new TreeSet<SMDMethodParameter>();
+
+ public SMDMethod(String name) {
+ this.name = name;
+ }
+
+ public void addSMDMethodParameter(SMDMethodParameter parameter) {
+ this.parameters.add(parameter);
+ }
+
+ public String getName() {
+ return this.name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public Set<SMDMethodParameter> getParameters() {
+ return this.parameters;
+ }
+
+ public int compareTo(Object o) {
+ if (!(o instanceof SMDMethod))
+ return 1;
+ if (o == null)
+ return 1;
+ SMDMethod other = (SMDMethod) o;
+ if ((name == null) && (other.name == null))
+ return 0;
+ if (name == null)
+ return -1;
+ if (name.equals(other.name))
+ return parameters.size() - other.parameters.size();
+
+ return name.compareTo(other.name);
+ }
+
+ public boolean equals(Object obj) {
+ if (!(obj instanceof SMDMethod))
+ return false;
+ SMDMethod toCompare = (SMDMethod) obj;
+ if ((name == null) && (toCompare.name == null))
+ return true;
+ return (name != null) && name.equals(toCompare.name)
+ && (parameters.size() == toCompare.parameters.size());
+ }
+}
Added: struts/sandbox/trunk/struts2-json-plugin/src/main/java/org/apache/struts2/json/smd/SMDMethodParameter.java
URL: http://svn.apache.org/viewvc/struts/sandbox/trunk/struts2-json-plugin/src/main/java/org/apache/struts2/json/smd/SMDMethodParameter.java?rev=799003&view=auto
==============================================================================
--- struts/sandbox/trunk/struts2-json-plugin/src/main/java/org/apache/struts2/json/smd/SMDMethodParameter.java (added)
+++ struts/sandbox/trunk/struts2-json-plugin/src/main/java/org/apache/struts2/json/smd/SMDMethodParameter.java Wed Jul 29 18:33:05 2009
@@ -0,0 +1,57 @@
+/*
+ * $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.struts2.json.smd;
+
+public class SMDMethodParameter implements Comparable {
+ private String name;
+
+ public SMDMethodParameter(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return this.name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public int compareTo(Object o) {
+ if (!(o instanceof SMDMethodParameter))
+ return 1;
+ if (o == null)
+ return 1;
+ if ((name == null) && (((SMDMethodParameter) o).name == null))
+ return 0;
+ if (name == null)
+ return -1;
+ return name.compareTo(((SMDMethodParameter) o).name);
+ }
+
+ public boolean equals(Object o) {
+ if (!(o instanceof SMDMethodParameter))
+ return false;
+ if ((name == null) && (((SMDMethodParameter) o).name == null))
+ return true;
+ return (name != null) && name.equals(((SMDMethodParameter) o).name);
+ }
+}
Added: struts/sandbox/trunk/struts2-json-plugin/src/main/resources/JsonPlugin.tld
URL: http://svn.apache.org/viewvc/struts/sandbox/trunk/struts2-json-plugin/src/main/resources/JsonPlugin.tld?rev=799003&view=auto
==============================================================================
--- struts/sandbox/trunk/struts2-json-plugin/src/main/resources/JsonPlugin.tld (added)
+++ struts/sandbox/trunk/struts2-json-plugin/src/main/resources/JsonPlugin.tld Wed Jul 29 18:33:05 2009
@@ -0,0 +1,25 @@
+<?xml version="1.0"?>
+<taglib xmlns="http://java.sun.com/xml/ns/j2ee"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd"
+ version="2.0">
+
+ <description>
+ This exposes JSON util functions from the Struts JSON plugin
+ </description>
+
+ <tlib-version>1.0</tlib-version>
+
+ <short-name>json</short-name>
+
+ <uri>/struts-json-tags</uri>
+
+ <function>
+ <name>serialize</name>
+ <function-class>org.apache.struts2.json.JSONUtil</function-class>
+ <function-signature>
+ java.lang.String serialize(java.lang.Object)
+ </function-signature>
+ </function>
+
+</taglib>
Added: struts/sandbox/trunk/struts2-json-plugin/src/main/resources/struts-plugin.xml
URL: http://svn.apache.org/viewvc/struts/sandbox/trunk/struts2-json-plugin/src/main/resources/struts-plugin.xml?rev=799003&view=auto
==============================================================================
--- struts/sandbox/trunk/struts2-json-plugin/src/main/resources/struts-plugin.xml (added)
+++ struts/sandbox/trunk/struts2-json-plugin/src/main/resources/struts-plugin.xml Wed Jul 29 18:33:05 2009
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<!DOCTYPE struts PUBLIC
+ "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
+ "http://struts.apache.org/dtds/struts-2.0.dtd">
+
+<struts>
+ <package name="json-default" extends="struts-default">
+ <result-types>
+ <result-type name="json" class="org.apache.struts2.json.JSONUtil"/>
+ </result-types>
+ <interceptors>
+ <interceptor name="json" class="org.apache.struts2.json.JSONInterceptor"/>
+ </interceptors>
+ </package>
+</struts>
Added: struts/sandbox/trunk/struts2-json-plugin/src/test/java/org/apache/struts2/json/AnEnum.java
URL: http://svn.apache.org/viewvc/struts/sandbox/trunk/struts2-json-plugin/src/test/java/org/apache/struts2/json/AnEnum.java?rev=799003&view=auto
==============================================================================
--- struts/sandbox/trunk/struts2-json-plugin/src/test/java/org/apache/struts2/json/AnEnum.java (added)
+++ struts/sandbox/trunk/struts2-json-plugin/src/test/java/org/apache/struts2/json/AnEnum.java Wed Jul 29 18:33:05 2009
@@ -0,0 +1,8 @@
+package org.apache.struts2.json;
+
+/**
+ * An enumeration for JSON serialization testing
+ */
+public enum AnEnum {
+ ValueA, ValueB, ValueC
+}
Added: struts/sandbox/trunk/struts2-json-plugin/src/test/java/org/apache/struts2/json/AnEnumBean.java
URL: http://svn.apache.org/viewvc/struts/sandbox/trunk/struts2-json-plugin/src/test/java/org/apache/struts2/json/AnEnumBean.java?rev=799003&view=auto
==============================================================================
--- struts/sandbox/trunk/struts2-json-plugin/src/test/java/org/apache/struts2/json/AnEnumBean.java (added)
+++ struts/sandbox/trunk/struts2-json-plugin/src/test/java/org/apache/struts2/json/AnEnumBean.java Wed Jul 29 18:33:05 2009
@@ -0,0 +1,25 @@
+package org.apache.struts2.json;
+
+/**
+ * A more complicated Enum with additional properties.
+ */
+public enum AnEnumBean {
+
+ One("A", "B"), Two("C", "D"), Three("E", "F");
+
+ private String propA;
+ private String propB;
+
+ AnEnumBean(String propA, String propB) {
+ this.propA = propA;
+ this.propB = propB;
+ }
+
+ public String getPropA() {
+ return propA;
+ }
+
+ public String getPropB() {
+ return propB;
+ }
+}
Added: struts/sandbox/trunk/struts2-json-plugin/src/test/java/org/apache/struts2/json/Bean.java
URL: http://svn.apache.org/viewvc/struts/sandbox/trunk/struts2-json-plugin/src/test/java/org/apache/struts2/json/Bean.java?rev=799003&view=auto
==============================================================================
--- struts/sandbox/trunk/struts2-json-plugin/src/test/java/org/apache/struts2/json/Bean.java (added)
+++ struts/sandbox/trunk/struts2-json-plugin/src/test/java/org/apache/struts2/json/Bean.java Wed Jul 29 18:33:05 2009
@@ -0,0 +1,131 @@
+package org.apache.struts2.json;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+public class Bean {
+ private String stringField;
+ private int intField;
+ private boolean booleanField;
+ private char charField;
+ private long longField;
+ private float floatField;
+ private double doubleField;
+ private Object objectField;
+ private byte byteField;
+ private AnEnum enumField;
+ private AnEnumBean enumBean;
+ private BigDecimal bigDecimal;
+ private BigInteger bigInteger;
+
+ /**
+ * @return the byteField
+ */
+ public byte getByteField() {
+ return this.byteField;
+ }
+
+ /**
+ * @param byteField
+ * the byteField to set
+ */
+ public void setByteField(byte byteField) {
+ this.byteField = byteField;
+ }
+
+ public boolean isBooleanField() {
+ return this.booleanField;
+ }
+
+ public void setBooleanField(boolean booleanField) {
+ this.booleanField = booleanField;
+ }
+
+ public char getCharField() {
+ return this.charField;
+ }
+
+ public void setCharField(char charField) {
+ this.charField = charField;
+ }
+
+ public double getDoubleField() {
+ return this.doubleField;
+ }
+
+ public void setDoubleField(double doubleField) {
+ this.doubleField = doubleField;
+ }
+
+ public float getFloatField() {
+ return this.floatField;
+ }
+
+ public void setFloatField(float floatField) {
+ this.floatField = floatField;
+ }
+
+ public int getIntField() {
+ return this.intField;
+ }
+
+ public void setIntField(int intField) {
+ this.intField = intField;
+ }
+
+ public long getLongField() {
+ return this.longField;
+ }
+
+ public void setLongField(long longField) {
+ this.longField = longField;
+ }
+
+ public Object getObjectField() {
+ return this.objectField;
+ }
+
+ public void setObjectField(Object objectField) {
+ this.objectField = objectField;
+ }
+
+ public String getStringField() {
+ return this.stringField;
+ }
+
+ public void setStringField(String stringField) {
+ this.stringField = stringField;
+ }
+
+ public AnEnum getEnumField() {
+ return enumField;
+ }
+
+ public void setEnumField(AnEnum enumField) {
+ this.enumField = enumField;
+ }
+
+ public AnEnumBean getEnumBean() {
+ return enumBean;
+ }
+
+ public void setEnumBean(AnEnumBean enumBean) {
+ this.enumBean = enumBean;
+ }
+
+ public BigInteger getBigInteger() {
+ return bigInteger;
+ }
+
+ public void setBigInteger(BigInteger bigInteger) {
+ this.bigInteger = bigInteger;
+ }
+
+ public BigDecimal getBigDecimal() {
+ return bigDecimal;
+ }
+
+ public void setBigDecimal(BigDecimal bigDecimal) {
+ this.bigDecimal = bigDecimal;
+ }
+}
Added: struts/sandbox/trunk/struts2-json-plugin/src/test/java/org/apache/struts2/json/JSONEnumTest.java
URL: http://svn.apache.org/viewvc/struts/sandbox/trunk/struts2-json-plugin/src/test/java/org/apache/struts2/json/JSONEnumTest.java?rev=799003&view=auto
==============================================================================
--- struts/sandbox/trunk/struts2-json-plugin/src/test/java/org/apache/struts2/json/JSONEnumTest.java (added)
+++ struts/sandbox/trunk/struts2-json-plugin/src/test/java/org/apache/struts2/json/JSONEnumTest.java Wed Jul 29 18:33:05 2009
@@ -0,0 +1,107 @@
+package org.apache.struts2.json;
+
+import java.util.Map;
+
+import junit.framework.TestCase;
+
+/**
+ * Test serialization of an Enum in the two supported modes: - Enum as a
+ * name=value pair; - Enum as a bean
+ */
+public class JSONEnumTest extends TestCase {
+
+ /**
+ * Asserts that a bean can be serialized to JSON and restored as a map
+ */
+ public void testEnumAsNameValue() throws Exception {
+ Bean bean1 = new Bean();
+
+ bean1.setStringField("str");
+ bean1.setBooleanField(true);
+ bean1.setCharField('s');
+ bean1.setDoubleField(10.1);
+ bean1.setFloatField(1.5f);
+ bean1.setIntField(10);
+ bean1.setLongField(100);
+ bean1.setEnumField(AnEnum.ValueA);
+ bean1.setEnumBean(AnEnumBean.Two);
+
+ JSONWriter jsonWriter = new JSONWriter();
+ jsonWriter.setEnumAsBean(false);
+ String json = jsonWriter.write(bean1);
+
+ Map result = (Map) JSONUtil.deserialize(json);
+ assertEquals("str", result.get("stringField"));
+ assertEquals(true, result.get("booleanField"));
+ assertEquals("s", result.get("charField")); // note: this is a
+ // String
+ assertEquals(10.1, result.get("doubleField"));
+ assertEquals(1.5, result.get("floatField")); // note: this is a
+ // Double
+ assertEquals(10L, result.get("intField")); // note: this is a
+ // Long
+ assertEquals(AnEnum.ValueA, AnEnum.valueOf((String) result.get("enumField"))); // note:
+ // this
+ // is a
+ // String
+ assertEquals(AnEnumBean.Two, AnEnumBean.valueOf((String) result.get("enumBean"))); // note:
+ // this
+ // is a
+ // String
+ }
+
+ /**
+ * Asserts that a bean can be serialized to JSON and restored as a map <p/>
+ * In this case, the name of the enum is in _name and the two properties of
+ * AnEnumBean are also serialized
+ */
+ public void testEnumAsBean() throws Exception {
+ Bean bean1 = new Bean();
+
+ bean1.setStringField("str");
+ bean1.setBooleanField(true);
+ bean1.setCharField('s');
+ bean1.setDoubleField(10.1);
+ bean1.setFloatField(1.5f);
+ bean1.setIntField(10);
+ bean1.setLongField(100);
+ bean1.setEnumField(AnEnum.ValueA);
+ bean1.setEnumBean(AnEnumBean.Two);
+
+ JSONWriter jsonWriter = new JSONWriter();
+ jsonWriter.setEnumAsBean(true);
+ String json = jsonWriter.write(bean1);
+
+ Map result = (Map) JSONUtil.deserialize(json);
+ assertEquals("str", result.get("stringField"));
+ assertEquals(true, result.get("booleanField"));
+ assertEquals("s", result.get("charField")); // note: this is a
+ // String
+ assertEquals(10.1, result.get("doubleField"));
+ assertEquals(1.5, result.get("floatField")); // note: this is a
+ // Double
+ assertEquals(10L, result.get("intField")); // note: this is a
+ // Long
+ Map enumBean1 = (Map) result.get("enumField");
+ assertNotNull(enumBean1);
+ assertEquals(AnEnum.ValueA, AnEnum.valueOf((String) enumBean1.get("_name"))); // get
+ // the
+ // special
+ // name
+ // property
+ Map enumBean2 = (Map) result.get("enumBean");
+ assertEquals(AnEnumBean.Two, AnEnumBean.valueOf((String) enumBean2.get("_name"))); // get
+ // the
+ // special
+ // name
+ // property
+ assertEquals(AnEnumBean.Two.getPropA(), (String) enumBean2.get("propA")); // get
+ // the
+ // propA
+ // property
+ assertEquals(AnEnumBean.Two.getPropB(), (String) enumBean2.get("propB")); // get
+ // the
+ // propA
+ // property
+ }
+}