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/30 00:44:29 UTC

svn commit: r799110 [3/7] - in /struts/sandbox/trunk/struts2-json-plugin: ./ src/main/java/org/apache/struts2/json/ src/main/java/org/apache/struts2/json/annotations/ src/main/java/org/apache/struts2/json/rpc/ src/main/java/org/apache/struts2/json/smd/...

Modified: struts/sandbox/trunk/struts2-json-plugin/src/main/java/org/apache/struts2/json/JSONResult.java
URL: http://svn.apache.org/viewvc/struts/sandbox/trunk/struts2-json-plugin/src/main/java/org/apache/struts2/json/JSONResult.java?rev=799110&r1=799109&r2=799110&view=diff
==============================================================================
--- struts/sandbox/trunk/struts2-json-plugin/src/main/java/org/apache/struts2/json/JSONResult.java (original)
+++ struts/sandbox/trunk/struts2-json-plugin/src/main/java/org/apache/struts2/json/JSONResult.java Wed Jul 29 22:44:26 2009
@@ -1,502 +1,522 @@
-package org.apache.struts2.json;
-
-import java.io.IOException;
-import java.lang.annotation.Annotation;
-import java.lang.reflect.Method;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.regex.Pattern;
-
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import org.apache.struts2.StrutsConstants;
-import org.apache.struts2.StrutsStatics;
-import org.apache.struts2.json.annotations.SMD;
-import org.apache.struts2.json.annotations.SMDMethod;
-import org.apache.struts2.json.annotations.SMDMethodParameter;
-
-import com.opensymphony.xwork2.ActionContext;
-import com.opensymphony.xwork2.ActionInvocation;
-import com.opensymphony.xwork2.Result;
-import com.opensymphony.xwork2.inject.Inject;
-import com.opensymphony.xwork2.util.ValueStack;
-import com.opensymphony.xwork2.util.logging.Logger;
-import com.opensymphony.xwork2.util.logging.LoggerFactory;
-
-/**
- * <!-- START SNIPPET: description --> <p/> This result serializes an action
- * into JSON. <p/> <!-- END SNIPPET: description --> <p/> <p/> <u>Result
- * parameters:</u> <p/> <!-- START SNIPPET: parameters --> <p/>
- * <ul>
- * <p/>
- * <li>excludeProperties - list of regular expressions matching the properties
- * to be excluded. The regular expressions are evaluated against the OGNL
- * expression representation of the properties. </li>
- * <p/>
- * </ul>
- * <p/> <!-- END SNIPPET: parameters --> <p/> <b>Example:</b> <p/>
- * 
- * <pre>
- * &lt;!-- START SNIPPET: example --&gt;
- * &lt;result name=&quot;success&quot; type=&quot;json&quot; /&gt;
- * &lt;!-- END SNIPPET: example --&gt;
- * </pre>
- */
-public class JSONResult implements Result {
-    private static final long serialVersionUID = 8624350183189931165L;
-    private static final Logger LOG = LoggerFactory.getLogger(JSONResult.class);
-
-    private String defaultEncoding = "ISO-8859-1";
-    private List<Pattern> includeProperties;
-    private List<Pattern> excludeProperties;
-    private String root;
-    private boolean wrapWithComments;
-    private boolean prefix;
-    private boolean enableSMD = false;
-    private boolean enableGZIP = false;
-    private boolean ignoreHierarchy = true;
-    private boolean ignoreInterfaces = true;
-    private boolean enumAsBean = JSONWriter.ENUM_AS_BEAN_DEFAULT;
-    private boolean noCache = false;
-    private boolean excludeNullProperties = false;
-    private int statusCode;
-    private int errorCode;
-    private String callbackParameter;
-    private String contentType;
-    private String wrapPrefix;
-    private String wrapSuffix;
-
-    @Inject(StrutsConstants.STRUTS_I18N_ENCODING)
-    public void setDefaultEncoding(String val) {
-        this.defaultEncoding = val;
-    }
-
-    /**
-     * Gets a list of regular expressions of properties to exclude from the JSON
-     * output.
-     * 
-     * @return A list of compiled regular expression patterns
-     */
-    public List<Pattern> getExcludePropertiesList() {
-        return this.excludeProperties;
-    }
-
-    /**
-     * Sets a comma-delimited list of regular expressions to match properties
-     * that should be excluded from the JSON output.
-     * 
-     * @param commaDelim
-     *            A comma-delimited list of regular expressions
-     */
-    public void setExcludeProperties(String commaDelim) {
-        List<String> excludePatterns = JSONUtil.asList(commaDelim);
-        if (excludePatterns != null) {
-            this.excludeProperties = new ArrayList<Pattern>(excludePatterns.size());
-            for (String pattern : excludePatterns) {
-                this.excludeProperties.add(Pattern.compile(pattern));
-            }
-        }
-    }
-
-    /**
-     * @return the includeProperties
-     */
-    public List<Pattern> getIncludePropertiesList() {
-        return includeProperties;
-    }
-
-    /**
-     * @param includedProperties
-     *            the includeProperties to set
-     */
-    public void setIncludeProperties(String commaDelim) {
-        List<String> includePatterns = JSONUtil.asList(commaDelim);
-        if (includePatterns != null) {
-            this.includeProperties = new ArrayList<Pattern>(includePatterns.size());
-
-            HashMap existingPatterns = new HashMap();
-
-            for (String pattern : includePatterns) {
-                // Compile a pattern for each *unique* "level" of the object
-                // hierarchy specified in the regex.
-                String[] patternPieces = pattern.split("\\\\\\.");
-
-                String patternExpr = "";
-                for (String patternPiece : patternPieces) {
-                    if (patternExpr.length() > 0) {
-                        patternExpr += "\\.";
-                    }
-                    patternExpr += patternPiece;
-
-                    // Check for duplicate patterns so that there is no overlap.
-                    if (!existingPatterns.containsKey(patternExpr)) {
-                        existingPatterns.put(patternExpr, patternExpr);
-
-                        // Add a pattern that does not have the indexed property
-                        // matching (ie. list\[\d+\] becomes list).
-                        if (patternPiece.endsWith("\\]")) {
-                            this.includeProperties.add(Pattern.compile(patternExpr.substring(0, patternPiece
-                                    .lastIndexOf("\\["))));
-
-                            if (LOG.isDebugEnabled())
-                                LOG.debug("Adding include property expression:  "
-                                        + patternExpr.substring(0, patternPiece.lastIndexOf("\\[")));
-                        }
-
-                        this.includeProperties.add(Pattern.compile(patternExpr));
-
-                        if (LOG.isDebugEnabled())
-                            LOG.debug("Adding include property expression:  " + patternExpr);
-                    }
-                }
-            }
-        }
-    }
-
-    public void execute(ActionInvocation invocation) throws Exception {
-        ActionContext actionContext = invocation.getInvocationContext();
-        HttpServletRequest request = (HttpServletRequest) actionContext.get(StrutsStatics.HTTP_REQUEST);
-        HttpServletResponse response = (HttpServletResponse) actionContext.get(StrutsStatics.HTTP_RESPONSE);
-
-        try {
-            String json;
-            Object rootObject;
-            if (this.enableSMD) {
-                // generate SMD
-                rootObject = this.writeSMD(invocation);
-            } else {
-                // generate JSON
-                if (this.root != null) {
-                    ValueStack stack = invocation.getStack();
-                    rootObject = stack.findValue(this.root);
-                } else {
-                    rootObject = invocation.getAction();
-                }
-            }
-            json = JSONUtil.serialize(rootObject, excludeProperties, includeProperties, ignoreHierarchy,
-                    enumAsBean, excludeNullProperties);
-            json = addCallbackIfApplicable(request, json);
-
-            boolean writeGzip = enableGZIP && JSONUtil.isGzipInRequest(request);
-
-            writeToResponse(response, json, writeGzip);
-
-        } catch (IOException exception) {
-            LOG.error(exception.getMessage(), exception);
-            throw exception;
-        }
-    }
-
-    protected void writeToResponse(HttpServletResponse response, String json, boolean gzip)
-            throws IOException {
-        JSONUtil.writeJSONToResponse(new SerializationParams(response, getEncoding(), isWrapWithComments(),
-                json, false, gzip, noCache, statusCode, errorCode, prefix, contentType, wrapPrefix,
-                wrapSuffix));
-    }
-
-    @SuppressWarnings("unchecked")
-    protected org.apache.struts2.json.smd.SMD writeSMD(ActionInvocation invocation) {
-        ActionContext actionContext = invocation.getInvocationContext();
-        HttpServletRequest request = (HttpServletRequest) actionContext.get(StrutsStatics.HTTP_REQUEST);
-
-        // root is based on OGNL expression (action by default)
-        Object rootObject = null;
-        if (this.root != null) {
-            ValueStack stack = invocation.getStack();
-            rootObject = stack.findValue(this.root);
-        } else {
-            rootObject = invocation.getAction();
-        }
-
-        Class clazz = rootObject.getClass();
-        org.apache.struts2.json.smd.SMD smd = new org.apache.struts2.json.smd.SMD();
-        // URL
-        smd.setServiceUrl(request.getRequestURI());
-
-        // customize SMD
-        SMD smdAnnotation = (SMD) clazz.getAnnotation(SMD.class);
-        if (smdAnnotation != null) {
-            smd.setObjectName(smdAnnotation.objectName());
-            smd.setServiceType(smdAnnotation.serviceType());
-            smd.setVersion(smdAnnotation.version());
-        }
-
-        // get public methods
-        Method[] methods = JSONUtil.listSMDMethods(clazz, ignoreInterfaces);
-
-        for (Method method : methods) {
-            SMDMethod smdMethodAnnotation = method.getAnnotation(SMDMethod.class);
-
-            // SMDMethod annotation is required
-            if (((smdMethodAnnotation != null) && !this.shouldExcludeProperty(method.getName()))) {
-                String methodName = smdMethodAnnotation.name().length() == 0 ? method.getName()
-                        : smdMethodAnnotation.name();
-
-                org.apache.struts2.json.smd.SMDMethod smdMethod = new org.apache.struts2.json.smd.SMDMethod(
-                        methodName);
-                smd.addSMDMethod(smdMethod);
-
-                // find params for this method
-                int parametersCount = method.getParameterTypes().length;
-                if (parametersCount > 0) {
-                    Annotation[][] parameterAnnotations = method.getParameterAnnotations();
-
-                    for (int i = 0; i < parametersCount; i++) {
-                        // are you ever going to pick shorter names? nope
-                        SMDMethodParameter smdMethodParameterAnnotation = this
-                                .getSMDMethodParameterAnnotation(parameterAnnotations[i]);
-
-                        String paramName = smdMethodParameterAnnotation != null ? smdMethodParameterAnnotation
-                                .name()
-                                : "p" + i;
-
-                        // goog thing this is the end of the hierarchy,
-                        // oitherwise I would need that 21'' LCD ;)
-                        smdMethod.addSMDMethodParameter(new org.apache.struts2.json.smd.SMDMethodParameter(
-                                paramName));
-                    }
-                }
-
-            } else {
-                if (LOG.isDebugEnabled())
-                    LOG.debug("Ignoring property " + method.getName());
-            }
-        }
-        return smd;
-    }
-
-    /**
-     * Find an SMDethodParameter annotation on this array
-     */
-    private org.apache.struts2.json.annotations.SMDMethodParameter getSMDMethodParameterAnnotation(
-            Annotation[] annotations) {
-        for (Annotation annotation : annotations) {
-            if (annotation instanceof org.apache.struts2.json.annotations.SMDMethodParameter)
-                return (org.apache.struts2.json.annotations.SMDMethodParameter) annotation;
-        }
-
-        return null;
-    }
-
-    private boolean shouldExcludeProperty(String expr) {
-        if (this.excludeProperties != null) {
-            for (Pattern pattern : this.excludeProperties) {
-                if (pattern.matcher(expr).matches())
-                    return true;
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Retrieve the encoding <p/>
-     * 
-     * @return The encoding associated with this template (defaults to the value
-     *         of 'struts.i18n.encoding' property)
-     */
-    protected String getEncoding() {
-        String encoding = this.defaultEncoding;
-
-        if (encoding == null) {
-            encoding = System.getProperty("file.encoding");
-        }
-
-        if (encoding == null) {
-            encoding = "UTF-8";
-        }
-
-        return encoding;
-    }
-
-    protected String addCallbackIfApplicable(HttpServletRequest request, String json) {
-        if ((callbackParameter != null) && (callbackParameter.length() > 0)) {
-            String callbackName = request.getParameter(callbackParameter);
-            if ((callbackName != null) && (callbackName.length() > 0))
-                json = callbackName + "(" + json + ")";
-        }
-        return json;
-    }
-
-    /**
-     * @return OGNL expression of root object to be serialized
-     */
-    public String getRoot() {
-        return this.root;
-    }
-
-    /**
-     * Sets the root object to be serialized, defaults to the Action
-     * 
-     * @param root
-     *            OGNL expression of root object to be serialized
-     */
-    public void setRoot(String root) {
-        this.root = root;
-    }
-
-    /**
-     * @return Generated JSON must be enclosed in comments
-     */
-    public boolean isWrapWithComments() {
-        return this.wrapWithComments;
-    }
-
-    /**
-     * Wrap generated JSON with comments
-     * 
-     * @param wrapWithComments
-     */
-    public void setWrapWithComments(boolean wrapWithComments) {
-        this.wrapWithComments = wrapWithComments;
-    }
-
-    /**
-     * @return Result has SMD generation enabled
-     */
-    public boolean isEnableSMD() {
-        return this.enableSMD;
-    }
-
-    /**
-     * Enable SMD generation for action, which can be used for JSON-RPC
-     * 
-     * @param enableSMD
-     */
-    public void setEnableSMD(boolean enableSMD) {
-        this.enableSMD = enableSMD;
-    }
-
-    public void setIgnoreHierarchy(boolean ignoreHierarchy) {
-        this.ignoreHierarchy = ignoreHierarchy;
-    }
-
-    /**
-     * Controls whether interfaces should be inspected for method annotations
-     * You may need to set to this true if your action is a proxy as annotations
-     * on methods are not inherited
-     */
-    public void setIgnoreInterfaces(boolean ignoreInterfaces) {
-        this.ignoreInterfaces = ignoreInterfaces;
-    }
-
-    /**
-     * Controls how Enum's are serialized : If true, an Enum is serialized as a
-     * name=value pair (name=name()) (default) If false, an Enum is serialized
-     * as a bean with a special property _name=name()
-     * 
-     * @param enumAsBean
-     */
-    public void setEnumAsBean(boolean enumAsBean) {
-        this.enumAsBean = enumAsBean;
-    }
-
-    public boolean isEnumAsBean() {
-        return enumAsBean;
-    }
-
-    public boolean isEnableGZIP() {
-        return enableGZIP;
-    }
-
-    public void setEnableGZIP(boolean enableGZIP) {
-        this.enableGZIP = enableGZIP;
-    }
-
-    public boolean isNoCache() {
-        return noCache;
-    }
-
-    /**
-     * Add headers to response to prevent the browser from caching the response
-     * 
-     * @param noCache
-     */
-    public void setNoCache(boolean noCache) {
-        this.noCache = noCache;
-    }
-
-    public boolean isIgnoreHierarchy() {
-        return ignoreHierarchy;
-    }
-
-    public boolean isExcludeNullProperties() {
-        return excludeNullProperties;
-    }
-
-    /**
-     * Do not serialize properties with a null value
-     * 
-     * @param excludeNullProperties
-     */
-    public void setExcludeNullProperties(boolean excludeNullProperties) {
-        this.excludeNullProperties = excludeNullProperties;
-    }
-
-    /**
-     * Status code to be set in the response
-     * 
-     * @param statusCode
-     */
-    public void setStatusCode(int statusCode) {
-        this.statusCode = statusCode;
-    }
-
-    /**
-     * Error code to be set in the response
-     * 
-     * @param errorCode
-     */
-    public void setErrorCode(int errorCode) {
-        this.errorCode = errorCode;
-    }
-
-    public void setCallbackParameter(String callbackParameter) {
-        this.callbackParameter = callbackParameter;
-    }
-
-    public String getCallbackParameter() {
-        return callbackParameter;
-    }
-
-    /**
-     * Prefix JSON with "{} &&"
-     * 
-     * @param prefix
-     */
-    public void setPrefix(boolean prefix) {
-        this.prefix = prefix;
-    }
-
-    /**
-     * Content type to be set in the response
-     * 
-     * @param contentType
-     */
-    public void setContentType(String contentType) {
-        this.contentType = contentType;
-    }
-
-    public String getWrapPrefix() {
-        return wrapPrefix;
-    }
-
-    /**
-     * Text to be inserted at the begining of the response
-     */
-    public void setWrapPrefix(String wrapPrefix) {
-        this.wrapPrefix = wrapPrefix;
-    }
-
-    public String getWrapSuffix() {
-        return wrapSuffix;
-    }
-
-    /**
-     * Text to be inserted at the end of the response
-     */
-    public void setWrapSuffix(String wrapSuffix) {
-        this.wrapSuffix = wrapSuffix;
-    }
-}
+/*
+ * $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.IOException;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.regex.Pattern;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.struts2.StrutsConstants;
+import org.apache.struts2.StrutsStatics;
+import org.apache.struts2.json.annotations.SMD;
+import org.apache.struts2.json.annotations.SMDMethod;
+import org.apache.struts2.json.annotations.SMDMethodParameter;
+
+import com.opensymphony.xwork2.ActionContext;
+import com.opensymphony.xwork2.ActionInvocation;
+import com.opensymphony.xwork2.Result;
+import com.opensymphony.xwork2.inject.Inject;
+import com.opensymphony.xwork2.util.ValueStack;
+import com.opensymphony.xwork2.util.logging.Logger;
+import com.opensymphony.xwork2.util.logging.LoggerFactory;
+
+/**
+ * <!-- START SNIPPET: description --> <p/> This result serializes an action
+ * into JSON. <p/> <!-- END SNIPPET: description --> <p/> <p/> <u>Result
+ * parameters:</u> <p/> <!-- START SNIPPET: parameters --> <p/>
+ * <ul>
+ * <p/>
+ * <li>excludeProperties - list of regular expressions matching the properties
+ * to be excluded. The regular expressions are evaluated against the OGNL
+ * expression representation of the properties. </li>
+ * <p/>
+ * </ul>
+ * <p/> <!-- END SNIPPET: parameters --> <p/> <b>Example:</b> <p/>
+ * 
+ * <pre>
+ * &lt;!-- START SNIPPET: example --&gt;
+ * &lt;result name=&quot;success&quot; type=&quot;json&quot; /&gt;
+ * &lt;!-- END SNIPPET: example --&gt;
+ * </pre>
+ */
+public class JSONResult implements Result {
+    private static final long serialVersionUID = 8624350183189931165L;
+    private static final Logger LOG = LoggerFactory.getLogger(JSONResult.class);
+
+    private String defaultEncoding = "ISO-8859-1";
+    private List<Pattern> includeProperties;
+    private List<Pattern> excludeProperties;
+    private String root;
+    private boolean wrapWithComments;
+    private boolean prefix;
+    private boolean enableSMD = false;
+    private boolean enableGZIP = false;
+    private boolean ignoreHierarchy = true;
+    private boolean ignoreInterfaces = true;
+    private boolean enumAsBean = JSONWriter.ENUM_AS_BEAN_DEFAULT;
+    private boolean noCache = false;
+    private boolean excludeNullProperties = false;
+    private int statusCode;
+    private int errorCode;
+    private String callbackParameter;
+    private String contentType;
+    private String wrapPrefix;
+    private String wrapSuffix;
+
+    @Inject(StrutsConstants.STRUTS_I18N_ENCODING)
+    public void setDefaultEncoding(String val) {
+        this.defaultEncoding = val;
+    }
+
+    /**
+     * Gets a list of regular expressions of properties to exclude from the JSON
+     * output.
+     * 
+     * @return A list of compiled regular expression patterns
+     */
+    public List<Pattern> getExcludePropertiesList() {
+        return this.excludeProperties;
+    }
+
+    /**
+     * Sets a comma-delimited list of regular expressions to match properties
+     * that should be excluded from the JSON output.
+     * 
+     * @param commaDelim
+     *            A comma-delimited list of regular expressions
+     */
+    public void setExcludeProperties(String commaDelim) {
+        List<String> excludePatterns = JSONUtil.asList(commaDelim);
+        if (excludePatterns != null) {
+            this.excludeProperties = new ArrayList<Pattern>(excludePatterns.size());
+            for (String pattern : excludePatterns) {
+                this.excludeProperties.add(Pattern.compile(pattern));
+            }
+        }
+    }
+
+    /**
+     * @return the includeProperties
+     */
+    public List<Pattern> getIncludePropertiesList() {
+        return includeProperties;
+    }
+
+    /**
+     * @param includedProperties
+     *            the includeProperties to set
+     */
+    public void setIncludeProperties(String commaDelim) {
+        List<String> includePatterns = JSONUtil.asList(commaDelim);
+        if (includePatterns != null) {
+            this.includeProperties = new ArrayList<Pattern>(includePatterns.size());
+
+            HashMap existingPatterns = new HashMap();
+
+            for (String pattern : includePatterns) {
+                // Compile a pattern for each *unique* "level" of the object
+                // hierarchy specified in the regex.
+                String[] patternPieces = pattern.split("\\\\\\.");
+
+                String patternExpr = "";
+                for (String patternPiece : patternPieces) {
+                    if (patternExpr.length() > 0) {
+                        patternExpr += "\\.";
+                    }
+                    patternExpr += patternPiece;
+
+                    // Check for duplicate patterns so that there is no overlap.
+                    if (!existingPatterns.containsKey(patternExpr)) {
+                        existingPatterns.put(patternExpr, patternExpr);
+
+                        // Add a pattern that does not have the indexed property
+                        // matching (ie. list\[\d+\] becomes list).
+                        if (patternPiece.endsWith("\\]")) {
+                            this.includeProperties.add(Pattern.compile(patternExpr.substring(0, patternPiece
+                                    .lastIndexOf("\\["))));
+
+                            if (LOG.isDebugEnabled())
+                                LOG.debug("Adding include property expression:  "
+                                        + patternExpr.substring(0, patternPiece.lastIndexOf("\\[")));
+                        }
+
+                        this.includeProperties.add(Pattern.compile(patternExpr));
+
+                        if (LOG.isDebugEnabled())
+                            LOG.debug("Adding include property expression:  " + patternExpr);
+                    }
+                }
+            }
+        }
+    }
+
+    public void execute(ActionInvocation invocation) throws Exception {
+        ActionContext actionContext = invocation.getInvocationContext();
+        HttpServletRequest request = (HttpServletRequest) actionContext.get(StrutsStatics.HTTP_REQUEST);
+        HttpServletResponse response = (HttpServletResponse) actionContext.get(StrutsStatics.HTTP_RESPONSE);
+
+        try {
+            String json;
+            Object rootObject;
+            if (this.enableSMD) {
+                // generate SMD
+                rootObject = this.writeSMD(invocation);
+            } else {
+                // generate JSON
+                if (this.root != null) {
+                    ValueStack stack = invocation.getStack();
+                    rootObject = stack.findValue(this.root);
+                } else {
+                    rootObject = invocation.getAction();
+                }
+            }
+            json = JSONUtil.serialize(rootObject, excludeProperties, includeProperties, ignoreHierarchy,
+                    enumAsBean, excludeNullProperties);
+            json = addCallbackIfApplicable(request, json);
+
+            boolean writeGzip = enableGZIP && JSONUtil.isGzipInRequest(request);
+
+            writeToResponse(response, json, writeGzip);
+
+        } catch (IOException exception) {
+            LOG.error(exception.getMessage(), exception);
+            throw exception;
+        }
+    }
+
+    protected void writeToResponse(HttpServletResponse response, String json, boolean gzip)
+            throws IOException {
+        JSONUtil.writeJSONToResponse(new SerializationParams(response, getEncoding(), isWrapWithComments(),
+                json, false, gzip, noCache, statusCode, errorCode, prefix, contentType, wrapPrefix,
+                wrapSuffix));
+    }
+
+    @SuppressWarnings("unchecked")
+    protected org.apache.struts2.json.smd.SMD writeSMD(ActionInvocation invocation) {
+        ActionContext actionContext = invocation.getInvocationContext();
+        HttpServletRequest request = (HttpServletRequest) actionContext.get(StrutsStatics.HTTP_REQUEST);
+
+        // root is based on OGNL expression (action by default)
+        Object rootObject = null;
+        if (this.root != null) {
+            ValueStack stack = invocation.getStack();
+            rootObject = stack.findValue(this.root);
+        } else {
+            rootObject = invocation.getAction();
+        }
+
+        Class clazz = rootObject.getClass();
+        org.apache.struts2.json.smd.SMD smd = new org.apache.struts2.json.smd.SMD();
+        // URL
+        smd.setServiceUrl(request.getRequestURI());
+
+        // customize SMD
+        SMD smdAnnotation = (SMD) clazz.getAnnotation(SMD.class);
+        if (smdAnnotation != null) {
+            smd.setObjectName(smdAnnotation.objectName());
+            smd.setServiceType(smdAnnotation.serviceType());
+            smd.setVersion(smdAnnotation.version());
+        }
+
+        // get public methods
+        Method[] methods = JSONUtil.listSMDMethods(clazz, ignoreInterfaces);
+
+        for (Method method : methods) {
+            SMDMethod smdMethodAnnotation = method.getAnnotation(SMDMethod.class);
+
+            // SMDMethod annotation is required
+            if (((smdMethodAnnotation != null) && !this.shouldExcludeProperty(method.getName()))) {
+                String methodName = smdMethodAnnotation.name().length() == 0 ? method.getName()
+                        : smdMethodAnnotation.name();
+
+                org.apache.struts2.json.smd.SMDMethod smdMethod = new org.apache.struts2.json.smd.SMDMethod(
+                        methodName);
+                smd.addSMDMethod(smdMethod);
+
+                // find params for this method
+                int parametersCount = method.getParameterTypes().length;
+                if (parametersCount > 0) {
+                    Annotation[][] parameterAnnotations = method.getParameterAnnotations();
+
+                    for (int i = 0; i < parametersCount; i++) {
+                        // are you ever going to pick shorter names? nope
+                        SMDMethodParameter smdMethodParameterAnnotation = this
+                                .getSMDMethodParameterAnnotation(parameterAnnotations[i]);
+
+                        String paramName = smdMethodParameterAnnotation != null ? smdMethodParameterAnnotation
+                                .name()
+                                : "p" + i;
+
+                        // goog thing this is the end of the hierarchy,
+                        // oitherwise I would need that 21'' LCD ;)
+                        smdMethod.addSMDMethodParameter(new org.apache.struts2.json.smd.SMDMethodParameter(
+                                paramName));
+                    }
+                }
+
+            } else {
+                if (LOG.isDebugEnabled())
+                    LOG.debug("Ignoring property " + method.getName());
+            }
+        }
+        return smd;
+    }
+
+    /**
+     * Find an SMDethodParameter annotation on this array
+     */
+    private org.apache.struts2.json.annotations.SMDMethodParameter getSMDMethodParameterAnnotation(
+            Annotation[] annotations) {
+        for (Annotation annotation : annotations) {
+            if (annotation instanceof org.apache.struts2.json.annotations.SMDMethodParameter)
+                return (org.apache.struts2.json.annotations.SMDMethodParameter) annotation;
+        }
+
+        return null;
+    }
+
+    private boolean shouldExcludeProperty(String expr) {
+        if (this.excludeProperties != null) {
+            for (Pattern pattern : this.excludeProperties) {
+                if (pattern.matcher(expr).matches())
+                    return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Retrieve the encoding <p/>
+     * 
+     * @return The encoding associated with this template (defaults to the value
+     *         of 'struts.i18n.encoding' property)
+     */
+    protected String getEncoding() {
+        String encoding = this.defaultEncoding;
+
+        if (encoding == null) {
+            encoding = System.getProperty("file.encoding");
+        }
+
+        if (encoding == null) {
+            encoding = "UTF-8";
+        }
+
+        return encoding;
+    }
+
+    protected String addCallbackIfApplicable(HttpServletRequest request, String json) {
+        if ((callbackParameter != null) && (callbackParameter.length() > 0)) {
+            String callbackName = request.getParameter(callbackParameter);
+            if ((callbackName != null) && (callbackName.length() > 0))
+                json = callbackName + "(" + json + ")";
+        }
+        return json;
+    }
+
+    /**
+     * @return OGNL expression of root object to be serialized
+     */
+    public String getRoot() {
+        return this.root;
+    }
+
+    /**
+     * Sets the root object to be serialized, defaults to the Action
+     * 
+     * @param root
+     *            OGNL expression of root object to be serialized
+     */
+    public void setRoot(String root) {
+        this.root = root;
+    }
+
+    /**
+     * @return Generated JSON must be enclosed in comments
+     */
+    public boolean isWrapWithComments() {
+        return this.wrapWithComments;
+    }
+
+    /**
+     * Wrap generated JSON with comments
+     * 
+     * @param wrapWithComments
+     */
+    public void setWrapWithComments(boolean wrapWithComments) {
+        this.wrapWithComments = wrapWithComments;
+    }
+
+    /**
+     * @return Result has SMD generation enabled
+     */
+    public boolean isEnableSMD() {
+        return this.enableSMD;
+    }
+
+    /**
+     * Enable SMD generation for action, which can be used for JSON-RPC
+     * 
+     * @param enableSMD
+     */
+    public void setEnableSMD(boolean enableSMD) {
+        this.enableSMD = enableSMD;
+    }
+
+    public void setIgnoreHierarchy(boolean ignoreHierarchy) {
+        this.ignoreHierarchy = ignoreHierarchy;
+    }
+
+    /**
+     * Controls whether interfaces should be inspected for method annotations
+     * You may need to set to this true if your action is a proxy as annotations
+     * on methods are not inherited
+     */
+    public void setIgnoreInterfaces(boolean ignoreInterfaces) {
+        this.ignoreInterfaces = ignoreInterfaces;
+    }
+
+    /**
+     * Controls how Enum's are serialized : If true, an Enum is serialized as a
+     * name=value pair (name=name()) (default) If false, an Enum is serialized
+     * as a bean with a special property _name=name()
+     * 
+     * @param enumAsBean
+     */
+    public void setEnumAsBean(boolean enumAsBean) {
+        this.enumAsBean = enumAsBean;
+    }
+
+    public boolean isEnumAsBean() {
+        return enumAsBean;
+    }
+
+    public boolean isEnableGZIP() {
+        return enableGZIP;
+    }
+
+    public void setEnableGZIP(boolean enableGZIP) {
+        this.enableGZIP = enableGZIP;
+    }
+
+    public boolean isNoCache() {
+        return noCache;
+    }
+
+    /**
+     * Add headers to response to prevent the browser from caching the response
+     * 
+     * @param noCache
+     */
+    public void setNoCache(boolean noCache) {
+        this.noCache = noCache;
+    }
+
+    public boolean isIgnoreHierarchy() {
+        return ignoreHierarchy;
+    }
+
+    public boolean isExcludeNullProperties() {
+        return excludeNullProperties;
+    }
+
+    /**
+     * Do not serialize properties with a null value
+     * 
+     * @param excludeNullProperties
+     */
+    public void setExcludeNullProperties(boolean excludeNullProperties) {
+        this.excludeNullProperties = excludeNullProperties;
+    }
+
+    /**
+     * Status code to be set in the response
+     * 
+     * @param statusCode
+     */
+    public void setStatusCode(int statusCode) {
+        this.statusCode = statusCode;
+    }
+
+    /**
+     * Error code to be set in the response
+     * 
+     * @param errorCode
+     */
+    public void setErrorCode(int errorCode) {
+        this.errorCode = errorCode;
+    }
+
+    public void setCallbackParameter(String callbackParameter) {
+        this.callbackParameter = callbackParameter;
+    }
+
+    public String getCallbackParameter() {
+        return callbackParameter;
+    }
+
+    /**
+     * Prefix JSON with "{} &&"
+     * 
+     * @param prefix
+     */
+    public void setPrefix(boolean prefix) {
+        this.prefix = prefix;
+    }
+
+    /**
+     * Content type to be set in the response
+     * 
+     * @param contentType
+     */
+    public void setContentType(String contentType) {
+        this.contentType = contentType;
+    }
+
+    public String getWrapPrefix() {
+        return wrapPrefix;
+    }
+
+    /**
+     * Text to be inserted at the begining of the response
+     */
+    public void setWrapPrefix(String wrapPrefix) {
+        this.wrapPrefix = wrapPrefix;
+    }
+
+    public String getWrapSuffix() {
+        return wrapSuffix;
+    }
+
+    /**
+     * Text to be inserted at the end of the response
+     */
+    public void setWrapSuffix(String wrapSuffix) {
+        this.wrapSuffix = wrapSuffix;
+    }
+}

Propchange: struts/sandbox/trunk/struts2-json-plugin/src/main/java/org/apache/struts2/json/JSONResult.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: struts/sandbox/trunk/struts2-json-plugin/src/main/java/org/apache/struts2/json/JSONResult.java
------------------------------------------------------------------------------
    svn:keywords = Id

Modified: 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=799110&r1=799109&r2=799110&view=diff
==============================================================================
--- struts/sandbox/trunk/struts2-json-plugin/src/main/java/org/apache/struts2/json/JSONUtil.java (original)
+++ struts/sandbox/trunk/struts2-json-plugin/src/main/java/org/apache/struts2/json/JSONUtil.java Wed Jul 29 22:44:26 2009
@@ -1,395 +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);
-    }
-}
+/*
+ * $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);
+    }
+}

Propchange: struts/sandbox/trunk/struts2-json-plugin/src/main/java/org/apache/struts2/json/JSONUtil.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: struts/sandbox/trunk/struts2-json-plugin/src/main/java/org/apache/struts2/json/JSONUtil.java
------------------------------------------------------------------------------
    svn:keywords = Id