You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@deltaspike.apache.org by ja...@apache.org on 2015/07/28 10:56:55 UTC

deltaspike git commit: add ValueExpressionEvaluationInputStream to jsf util (+ tests) from relative-resource-handler project as discussed with gpetracek

Repository: deltaspike
Updated Branches:
  refs/heads/master b11d8174b -> 6e42e6637


add ValueExpressionEvaluationInputStream to jsf util (+ tests) from relative-resource-handler project as discussed with gpetracek


Project: http://git-wip-us.apache.org/repos/asf/deltaspike/repo
Commit: http://git-wip-us.apache.org/repos/asf/deltaspike/commit/6e42e663
Tree: http://git-wip-us.apache.org/repos/asf/deltaspike/tree/6e42e663
Diff: http://git-wip-us.apache.org/repos/asf/deltaspike/diff/6e42e663

Branch: refs/heads/master
Commit: 6e42e66376aab2c39e7714418136440902966e8c
Parents: b11d817
Author: Jakob Korherr <ja...@gmail.com>
Authored: Tue Jul 28 10:55:08 2015 +0200
Committer: Jakob Korherr <ja...@gmail.com>
Committed: Tue Jul 28 10:55:08 2015 +0200

----------------------------------------------------------------------
 deltaspike/modules/jsf/api/pom.xml              |  15 +-
 .../ValueExpressionEvaluationInputStream.java   | 183 +++++++++++++++++++
 ...alueExpressionEvaluationInputStreamTest.java | 180 ++++++++++++++++++
 3 files changed, 377 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/deltaspike/blob/6e42e663/deltaspike/modules/jsf/api/pom.xml
----------------------------------------------------------------------
diff --git a/deltaspike/modules/jsf/api/pom.xml b/deltaspike/modules/jsf/api/pom.xml
index 6a11e4e..bfc087b 100644
--- a/deltaspike/modules/jsf/api/pom.xml
+++ b/deltaspike/modules/jsf/api/pom.xml
@@ -45,8 +45,21 @@
             <groupId>org.apache.myfaces.core</groupId>
             <artifactId>myfaces-api</artifactId>
         </dependency>
+        <dependency>
+            <groupId>org.apache.geronimo.specs</groupId>
+            <artifactId>geronimo-el_2.2_spec</artifactId>
+        </dependency>
 
-
+        <dependency>
+            <groupId>org.apache.myfaces.test</groupId>
+            <artifactId>myfaces-test20</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>commons-logging</groupId>
+            <artifactId>commons-logging</artifactId>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
 
 

http://git-wip-us.apache.org/repos/asf/deltaspike/blob/6e42e663/deltaspike/modules/jsf/api/src/main/java/org/apache/deltaspike/jsf/util/ValueExpressionEvaluationInputStream.java
----------------------------------------------------------------------
diff --git a/deltaspike/modules/jsf/api/src/main/java/org/apache/deltaspike/jsf/util/ValueExpressionEvaluationInputStream.java b/deltaspike/modules/jsf/api/src/main/java/org/apache/deltaspike/jsf/util/ValueExpressionEvaluationInputStream.java
new file mode 100644
index 0000000..7e81aca
--- /dev/null
+++ b/deltaspike/modules/jsf/api/src/main/java/org/apache/deltaspike/jsf/util/ValueExpressionEvaluationInputStream.java
@@ -0,0 +1,183 @@
+/*
+ * 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.deltaspike.jsf.util;
+
+import javax.faces.context.FacesContext;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PushbackInputStream;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * A filtered stream that evaluates value expressions in the original stream while reading from it.
+ */
+public class ValueExpressionEvaluationInputStream extends InputStream
+{
+
+    /**
+     * Logger for this class.
+     */
+    private static final Logger log = Logger.getLogger(ValueExpressionEvaluationInputStream.class.getName());
+
+    private FacesContext facesContext;
+    private PushbackInputStream wrapped;
+    private String currentValue;
+    private int currentValueIndex = -1;
+
+    public ValueExpressionEvaluationInputStream(FacesContext facesContext, InputStream inputStream)
+    {
+        this.facesContext = facesContext;
+        this.wrapped = new PushbackInputStream(inputStream, 512);
+    }
+
+    /**
+     * Reads a byte from the original stream and checks for value expression occurrences.
+     * A value expression has the following format: #{xxx}
+     * If a value expression is found, its occurrence in the stream will replaced with
+     * the evaluated value of the expression.
+     *
+     * @return
+     * @throws java.io.IOException
+     */
+    @Override
+    public int read() throws IOException
+    {
+        // check for a current value
+        if (currentValueIndex != -1)
+        {
+            if (currentValueIndex < currentValue.length())
+            {
+                return currentValue.charAt(currentValueIndex++);
+            }
+            else
+            {
+                // current value exhausted, reset index
+                currentValueIndex = -1;
+            }
+        }
+
+        // read byte and check for value expression begin
+        int c1 = wrapped.read();
+        if (c1 != '#')
+        {
+            return c1;  // can't be a value expression, just return the character
+        }
+        else
+        {
+            // could be a value expression, next character must be '{'
+            int c2 = wrapped.read();
+            if (c2 != '{')
+            {
+                wrapped.unread(c2);  // we did not find a value expression, unread byte that we read too much
+                return c1;   // return original character
+            }
+            else
+            {
+                // read until '}', '\n' or eof occurs (end of value expression or data)
+                List<Integer> possibleValueExpression = new LinkedList<Integer>();
+                int c = wrapped.read();
+                boolean insideString = (c == '\'');  // a '}' inside a string must not terminate the expression string
+                while (c != -1 && c != '\n' && (insideString || c != '}'))
+                {
+                    possibleValueExpression.add(c);
+                    c = wrapped.read();
+                    if (c == '\'')
+                    {
+                        insideString = !insideString;
+                    }
+                }
+
+                if (c != '}')
+                {
+                    // we did not find a value expression, unread bytes that we read too much (in reverse order)
+                    if (c != -1)  // we can't unread eof
+                    {
+                        wrapped.unread(c);
+                    }
+                    ListIterator<Integer> it = possibleValueExpression.listIterator(possibleValueExpression.size());
+                    while (it.hasPrevious())
+                    {
+                        wrapped.unread(it.previous());
+                    }
+                    wrapped.unread(c2);
+                    return c1; // return original character
+                }
+                else
+                {
+                    // we found a value expression #{xxx} (xxx is stored in possibleValueExpression)
+                    // create the expression string
+                    String expressionString = createExpressionString(possibleValueExpression);
+
+                    // evaluate it
+                    String expressionValue = facesContext.getApplication()
+                            .evaluateExpressionGet(facesContext, expressionString, String.class);
+
+                    if (expressionValue == null)
+                    {
+                        if (log.isLoggable(Level.WARNING))
+                        {
+                            log.warning("ValueExpression " + expressionString + " evaluated to null.");
+                        }
+
+                        expressionValue = "null";  // fallback value for null
+                    }
+
+                    // do NOT unread the evaluated value, but rather store it in an internal buffer,
+                    // because otherwise we could recursively evaluate value expressions (a value expression
+                    // that resolves to a string containing "#{...}" would be re-evaluated).
+                    this.currentValue = expressionValue;
+
+                    // return first character of currentValue, if exists (not an empty string)
+                    if (currentValue.length() != 0)
+                    {
+                        this.currentValueIndex = 0;
+                        return currentValue.charAt(currentValueIndex++);
+                    }
+                    else  // currentValue is an empty string
+                    {
+                        // in this case we must recursively start a new read (incl. checks for a new value expression)
+                        this.currentValueIndex = -1;
+                        return read();
+                    }
+                }
+            }
+        }
+    }
+
+    private String createExpressionString(List<Integer> expressionList)
+    {
+        char[] expressionChars = new char[expressionList.size() + 3];  // #{expressionList}
+        int i = 0;
+
+        expressionChars[i++] = '#';
+        expressionChars[i++] = '{';
+        for (Integer c : expressionList)
+        {
+            expressionChars[i++] = (char) c.intValue();
+        }
+        expressionChars[i] = '}';
+
+        return String.valueOf(expressionChars);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/deltaspike/blob/6e42e663/deltaspike/modules/jsf/api/src/test/java/org/apache/deltaspike/jsf/util/ValueExpressionEvaluationInputStreamTest.java
----------------------------------------------------------------------
diff --git a/deltaspike/modules/jsf/api/src/test/java/org/apache/deltaspike/jsf/util/ValueExpressionEvaluationInputStreamTest.java b/deltaspike/modules/jsf/api/src/test/java/org/apache/deltaspike/jsf/util/ValueExpressionEvaluationInputStreamTest.java
new file mode 100644
index 0000000..d83d0f5
--- /dev/null
+++ b/deltaspike/modules/jsf/api/src/test/java/org/apache/deltaspike/jsf/util/ValueExpressionEvaluationInputStreamTest.java
@@ -0,0 +1,180 @@
+/*
+ * 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.deltaspike.jsf.util;
+
+import org.apache.myfaces.test.base.junit4.AbstractJsfTestCase;
+import org.junit.Assert;
+import org.junit.Test;
+
+import javax.faces.context.FacesContext;
+import java.io.ByteArrayInputStream;
+
+/**
+ * Tests for {@link ValueExpressionEvaluationInputStream}.
+ */
+public class ValueExpressionEvaluationInputStreamTest extends AbstractJsfTestCase
+{
+
+    @Test
+    public void testStreamWithoutExpression_mustBeUnmodified() throws Exception
+    {
+        final String data = "aa\nbbbb\ncccc\ndddd\n\n";
+        byte[] dataArray = data.getBytes();
+
+        ValueExpressionEvaluationInputStream inputStream = new ValueExpressionEvaluationInputStream(
+                FacesContext.getCurrentInstance(), new ByteArrayInputStream(dataArray));
+
+        byte[] inputStreamDataArray = new byte[dataArray.length];
+        inputStream.read(inputStreamDataArray);
+
+        // checks
+        Assert.assertArrayEquals(dataArray, inputStreamDataArray);  // data arrays must match
+        Assert.assertEquals(-1, inputStream.read()); // stream must be at eof
+    }
+
+    @Test
+    public void testStreamWithSimpleExpression_mustBeEvaluated() throws Exception
+    {
+        final String data = "aa\nbbbb\ncc#{requestScope.test}cc\ndddd\n\n";
+        final String evaluatedData = "aa\nbbbb\ncctest-valuecc\ndddd\n\n";
+        byte[] dataArray = data.getBytes();
+        byte[] evaluatedDataArray = evaluatedData.getBytes();
+
+        // put test value into scope, so that expression can evaluate to this value
+        FacesContext.getCurrentInstance().getExternalContext().getRequestMap().put("test", "test-value");
+
+        ValueExpressionEvaluationInputStream inputStream = new ValueExpressionEvaluationInputStream(
+                FacesContext.getCurrentInstance(), new ByteArrayInputStream(dataArray));
+
+        byte[] inputStreamDataArray = new byte[evaluatedDataArray.length];
+        inputStream.read(inputStreamDataArray);
+
+        // checks
+        Assert.assertArrayEquals(evaluatedDataArray, inputStreamDataArray);  // evaluated data arrays must match
+        Assert.assertEquals(-1, inputStream.read()); // stream must be at eof
+    }
+
+    @Test
+    public void testStreamWithHalfExpressionAtEnd_mustBeUnmodified() throws Exception
+    {
+        final String data = "aa\nbbbb\ncccc\ndddd\n\n#{requestScope.test"; // } is missing at the end
+        byte[] dataArray = data.getBytes();
+
+        // put test value into scope, so that expression could evaluate to this value
+        FacesContext.getCurrentInstance().getExternalContext().getRequestMap().put("test", "test-value");
+
+        ValueExpressionEvaluationInputStream inputStream = new ValueExpressionEvaluationInputStream(
+                FacesContext.getCurrentInstance(), new ByteArrayInputStream(dataArray));
+
+        byte[] inputStreamDataArray = new byte[dataArray.length];
+        inputStream.read(inputStreamDataArray);
+
+        // checks
+        Assert.assertArrayEquals(dataArray, inputStreamDataArray);  // data arrays must match
+        Assert.assertEquals(-1, inputStream.read()); // stream must be at eof
+    }
+
+    @Test
+    public void testStreamWithHalfExpressionAtLineEnd_mustBeUnmodified() throws Exception
+    {
+        final String data = "aa\nbb#{requestScope.test\n}bb\ncccc\ndddd\n\n"; // } is missing at the end
+        byte[] dataArray = data.getBytes();
+
+        // put test value into scope, so that expression could evaluate to this value
+        FacesContext.getCurrentInstance().getExternalContext().getRequestMap().put("test", "test-value");
+
+        ValueExpressionEvaluationInputStream inputStream = new ValueExpressionEvaluationInputStream(
+                FacesContext.getCurrentInstance(), new ByteArrayInputStream(dataArray));
+
+        byte[] inputStreamDataArray = new byte[dataArray.length];
+        inputStream.read(inputStreamDataArray);
+
+        // checks
+        Assert.assertArrayEquals(dataArray, inputStreamDataArray);  // data arrays must match
+        Assert.assertEquals(-1, inputStream.read()); // stream must be at eof
+    }
+
+    @Test
+    public void testStreamWithExpressionEvaluatingToExpressionString_mustOnlyEvaluateFirstExpression() throws Exception
+    {
+        final String data = "aa\nbbbb\ncc#{requestScope.test}cc\ndddd\n\n";
+        final String evaluatedData = "aa\nbbbb\ncc#{requestScope.test2}cc\ndddd\n\n";
+        byte[] dataArray = data.getBytes();
+        byte[] evaluatedDataArray = evaluatedData.getBytes();
+
+        // put test value into scope, so that expression can evaluate to this value
+        FacesContext.getCurrentInstance().getExternalContext().getRequestMap().put("test", "#{requestScope.test2}");
+        FacesContext.getCurrentInstance().getExternalContext().getRequestMap().put("test2", "test-value");
+
+        ValueExpressionEvaluationInputStream inputStream = new ValueExpressionEvaluationInputStream(
+                FacesContext.getCurrentInstance(), new ByteArrayInputStream(dataArray));
+
+        byte[] inputStreamDataArray = new byte[evaluatedDataArray.length];
+        inputStream.read(inputStreamDataArray);
+
+        // checks
+        Assert.assertArrayEquals(evaluatedDataArray, inputStreamDataArray);  // evaluated data arrays must match
+        Assert.assertEquals(-1, inputStream.read()); // stream must be at eof
+    }
+
+    @Test
+    public void testStreamThatOnlyConsistsOfExpression_mustEvaluateExpression() throws Exception
+    {
+        final String data = "#{requestScope.test}";
+        final String evaluatedData = "test-value";
+        byte[] dataArray = data.getBytes();
+        byte[] evaluatedDataArray = evaluatedData.getBytes();
+
+        // put test value into scope, so that expression can evaluate to this value
+        FacesContext.getCurrentInstance().getExternalContext().getRequestMap().put("test", "test-value");
+
+        ValueExpressionEvaluationInputStream inputStream = new ValueExpressionEvaluationInputStream(
+                FacesContext.getCurrentInstance(), new ByteArrayInputStream(dataArray));
+
+        byte[] inputStreamDataArray = new byte[evaluatedDataArray.length];
+        inputStream.read(inputStreamDataArray);
+
+        // checks
+        Assert.assertArrayEquals(evaluatedDataArray, inputStreamDataArray);  // evaluated data arrays must match
+        Assert.assertEquals(-1, inputStream.read()); // stream must be at eof
+    }
+
+    @Test
+    public void testStreamWithNullExpression_mustEvaluateToEmptyString() throws Exception
+    {
+        final String data = "aa\nbbbb\ncc#{requestScope.test}cc\ndddd\n\n";
+        final String evaluatedData = "aa\nbbbb\ncccc\ndddd\n\n";
+        byte[] dataArray = data.getBytes();
+        byte[] evaluatedDataArray = evaluatedData.getBytes();
+
+        // make sure there is no value
+        FacesContext.getCurrentInstance().getExternalContext().getRequestMap().remove("test");
+
+        ValueExpressionEvaluationInputStream inputStream = new ValueExpressionEvaluationInputStream(
+                FacesContext.getCurrentInstance(), new ByteArrayInputStream(dataArray));
+
+        byte[] inputStreamDataArray = new byte[evaluatedDataArray.length];
+        inputStream.read(inputStreamDataArray);
+
+        // checks
+        Assert.assertArrayEquals(evaluatedDataArray, inputStreamDataArray);  // evaluated data arrays must match
+        Assert.assertEquals(-1, inputStream.read()); // stream must be at eof
+    }
+
+}