Added the base64 decode XPath function; Applied patch from Prabath at SYNAPSE-1012

git-svn-id: https://svn.apache.org/repos/asf/synapse/trunk@1745684 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/java/modules/core/src/main/java/org/apache/synapse/util/xpath/Base64DecodeFunction.java b/java/modules/core/src/main/java/org/apache/synapse/util/xpath/Base64DecodeFunction.java
new file mode 100644
index 0000000..fe6c7cb
--- /dev/null
+++ b/java/modules/core/src/main/java/org/apache/synapse/util/xpath/Base64DecodeFunction.java
@@ -0,0 +1,110 @@
+/*
+ *  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.synapse.util.xpath;
+
+
+import org.apache.commons.codec.binary.Base64;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.jaxen.Context;
+import org.jaxen.Function;
+import org.jaxen.FunctionCallException;
+import org.jaxen.function.StringFunction;
+
+import java.io.UnsupportedEncodingException;
+import java.util.List;
+
+/**
+ * Implements the XPath extension function synapse:base64Decode(string)
+ */
+public class Base64DecodeFunction implements Function {
+
+    private static final Log log = LogFactory.getLog(Base64DecodeFunction.class);
+
+    /**
+     * Returns the base64 decoded string value of the first argument.
+     *
+     * @param context the context at the point in the expression when the function is called
+     * @param args  arguments of the functions
+     * @return The string value of a property
+     * @throws FunctionCallException
+     */
+    public Object call(Context context, List args) throws FunctionCallException {
+        if (args == null || args.size() == 0) {
+            if (log.isDebugEnabled()) {
+                log.debug("Property key value for lookup is not specified");
+            }
+            return SynapseXPathConstants.NULL_STRING;
+        }
+
+        int size = args.size();
+        if (size == 1) {
+            // get the first argument, it can be a function returning a string as well
+            String encodedValue = StringFunction.evaluate(args.get(0), context.getNavigator());
+            // use the default UTF-8 decoding.
+            return decode(log.isDebugEnabled(), SynapseXPathConstants.DEFAULT_CHARSET, encodedValue);
+        } else if (size == 2) {
+            // get the first argument, it can be a function returning a string as well
+            String encodedValue = StringFunction.evaluate(args.get(0), context.getNavigator());
+            // charset is in the second argument
+            String charset = StringFunction.evaluate(args.get(1), context.getNavigator());
+            return decode(log.isDebugEnabled(), charset, encodedValue);
+        } else if (log.isDebugEnabled()) {
+            log.debug("base64Decode function expects only two arguments maximum, returning empty string");
+        }
+        // return empty string if the arguments are wrong
+        return SynapseXPathConstants.NULL_STRING;
+    }
+
+
+    private Object decode(boolean debugOn, String charset, String value) throws FunctionCallException {
+        if (value == null || value.isEmpty()) {
+            if (debugOn) {
+                log.debug("Non empty string value should be provided for decode");
+            }
+            return SynapseXPathConstants.NULL_STRING;
+        }
+
+        byte[] decodedValue;
+        try {
+            decodedValue = new Base64().decode(value.getBytes(charset));
+        } catch (UnsupportedEncodingException e) {
+            String msg = "Unsupported Charset";
+            log.error(msg, e);
+            throw new FunctionCallException(msg, e);
+        }
+
+        String decodedString;
+        try {
+            decodedString = new String(decodedValue, charset).trim();
+        } catch (UnsupportedEncodingException e) {
+            String msg = "Unsupported Charset";
+            log.error(msg, e);
+            throw new FunctionCallException(msg, e);
+        }
+
+        if (debugOn) {
+            log.debug("Decoded base64 encoded value: " + value + " with charset: " + charset +
+                      " to String: " + decodedString);
+        }
+
+        return decodedString;
+    }
+}
diff --git a/java/modules/core/src/main/java/org/apache/synapse/util/xpath/Base64EncodeFunction.java b/java/modules/core/src/main/java/org/apache/synapse/util/xpath/Base64EncodeFunction.java
index 6ee0bf9..3aee2be 100644
--- a/java/modules/core/src/main/java/org/apache/synapse/util/xpath/Base64EncodeFunction.java
+++ b/java/modules/core/src/main/java/org/apache/synapse/util/xpath/Base64EncodeFunction.java
@@ -34,12 +34,9 @@
  * Implements the XPath extension function synapse:base64Encode(string)
  */
 public class Base64EncodeFunction implements Function {
+
     private static final Log log = LogFactory.getLog(Base64EncodeFunction.class);
 
-    public static final String NULL_STRING = "";
-
-    private static final String DEFAULT_CHARSET = "UTF-8";
-
     /**
      * Returns the base64 encoded string value of the first argument.
      *
@@ -55,7 +52,7 @@
             if (debugOn) {
                 log.debug("Property key value for lookup is not specified");
             }
-            return NULL_STRING;
+            return SynapseXPathConstants.NULL_STRING;
         }
 
         int size = args.size();
@@ -64,7 +61,7 @@
             String value = StringFunction.evaluate(args.get(0), context.getNavigator());
 
             // use the default UTF-8 encoding
-            return encode(debugOn, DEFAULT_CHARSET, value);
+            return encode(debugOn, SynapseXPathConstants.DEFAULT_CHARSET, value);
         } else if (size == 2) {
             // get the first argument, it can be a function returning a string as well
             String value = StringFunction.evaluate(args.get(0), context.getNavigator());
@@ -79,7 +76,7 @@
             }
         }
         // return empty string if the arguments are wrong
-        return NULL_STRING;
+        return SynapseXPathConstants.NULL_STRING;
     }
 
     private Object encode(boolean debugOn, String encoding, String value)
@@ -89,7 +86,7 @@
                 log.debug("Non emprty string value should be provided for encoding");
             }
 
-            return NULL_STRING;
+            return SynapseXPathConstants.NULL_STRING;
         }
 
         byte[] encodedValue;
diff --git a/java/modules/core/src/main/java/org/apache/synapse/util/xpath/SynapseXPathConstants.java b/java/modules/core/src/main/java/org/apache/synapse/util/xpath/SynapseXPathConstants.java
index 1b85336..770a5a5 100755
--- a/java/modules/core/src/main/java/org/apache/synapse/util/xpath/SynapseXPathConstants.java
+++ b/java/modules/core/src/main/java/org/apache/synapse/util/xpath/SynapseXPathConstants.java
@@ -30,6 +30,9 @@
     /** base64Encode XPath extension function name */
     public static final String BASE64_ENCODE_FUNCTION = "base64Encode";
 
+    /** base64Decode XPath extension function name */
+    public static final String BASE64_DECODE_FUNCTION = "base64Decode";
+
     /** Body relative XPath variale name for the SOAPBody */
     public static final String SOAP_BODY_VARIABLE = "body";
 
@@ -50,4 +53,8 @@
 
     /** Variable prefix for accessing URL parameters of the message through XPath variables */
     public static final String URL_VARIABLE_PREFIX = "url";
+
+    public static final String DEFAULT_CHARSET = "UTF-8";
+
+    public static final String NULL_STRING = "";
 }
diff --git a/java/modules/core/src/main/java/org/apache/synapse/util/xpath/SynapseXPathFunctionContext.java b/java/modules/core/src/main/java/org/apache/synapse/util/xpath/SynapseXPathFunctionContext.java
index cc5ce8e..2b2afbf 100755
--- a/java/modules/core/src/main/java/org/apache/synapse/util/xpath/SynapseXPathFunctionContext.java
+++ b/java/modules/core/src/main/java/org/apache/synapse/util/xpath/SynapseXPathFunctionContext.java
@@ -82,6 +82,10 @@
                 SynapseXPathConstants.BASE64_ENCODE_FUNCTION.equals(localName)) {
             // create a base64Encode function and set it to the XPath
             return new Base64EncodeFunction();
+        } else if (localName != null &&
+                   SynapseXPathConstants.BASE64_DECODE_FUNCTION.equals(localName)) {
+            // create a base64Decode function and set it to the XPath
+            return new Base64DecodeFunction();
         }
         //We check if custom Xpath extensions are available
         Function extensionFunction = XpathExtensionUtil.getFunctionContext(
diff --git a/java/modules/core/src/test/java/org/apache/synapse/util/xpath/Base64DecodeFunctionTest.java b/java/modules/core/src/test/java/org/apache/synapse/util/xpath/Base64DecodeFunctionTest.java
new file mode 100644
index 0000000..fb7f5ed
--- /dev/null
+++ b/java/modules/core/src/test/java/org/apache/synapse/util/xpath/Base64DecodeFunctionTest.java
@@ -0,0 +1,64 @@
+/*
+ *  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.synapse.util.xpath;
+
+import junit.framework.Assert;
+import junit.framework.TestCase;
+import org.jaxen.Context;
+import org.jaxen.ContextSupport;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class Base64DecodeFunctionTest extends TestCase {
+
+    public void testBase64DecodeFunctionWithCharset() throws Exception {
+        String encodedString = "U3luYXBzZQ==";
+        Base64DecodeFunction base64DecodeFunction = new Base64DecodeFunction();
+        List<String> params = new ArrayList<String>();
+        params.add(encodedString);
+        params.add("UTF-8");
+        Context context = new Context(null);
+        context.setContextSupport(new ContextSupport());
+        String decodedString = (String) base64DecodeFunction.call(context, params);
+        assertEquals("Wrong decoded value found", "Synapse", decodedString);
+
+    }
+
+    public void testBase64DecodeFunctionWithoutCharset() throws Exception {
+        String encodedString = "U3luYXBzZQ==";
+        Base64DecodeFunction base64DecodeFunction = new Base64DecodeFunction();
+        List<String> params = new ArrayList<String>();
+        params.add(encodedString);
+        Context context = new Context(null);
+        context.setContextSupport(new ContextSupport());
+        String decodedString = (String) base64DecodeFunction.call(context, params);
+        assertEquals("Wrong decoded value found", "Synapse", decodedString);
+    }
+
+    public void testBase64DecodeFunctionWithoutParameters() throws Exception {
+        Base64DecodeFunction base64DecodeFunction = new Base64DecodeFunction();
+        List<String> params = new ArrayList<String>();
+        Context context = new Context(null);
+        context.setContextSupport(new ContextSupport());
+        String decodedString = (String) base64DecodeFunction.call(context, params);
+        assertEquals("Wrong decoded value found", "", decodedString);
+    }
+}