added whole new module for JSF3, without copying old sources because of removed classes
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..5582ffa
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,6 @@
+/target/
+/test12/target/
+/test20/target/
+/test22/target/
+/test23/target/
+/test30/target/
\ No newline at end of file
diff --git a/assembly/pom.xml b/assembly/pom.xml
index 5f9a5c6..1f49635 100644
--- a/assembly/pom.xml
+++ b/assembly/pom.xml
@@ -69,6 +69,12 @@
<version>${project.version}</version>
<classifier>javadoc</classifier>
</artifactItem>
+ <artifactItem>
+ <groupId>org.apache.myfaces.test</groupId>
+ <artifactId>myfaces-test30</artifactId>
+ <version>${project.version}</version>
+ <classifier>javadoc</classifier>
+ </artifactItem>
</artifactItems>
<outputDirectory>${project.build.directory}/javadoc</outputDirectory>
</configuration>
@@ -111,7 +117,7 @@
<version>${project.version}</version>
<type>zip</type>
<classifier>source-release</classifier>
- </artifactItem>
+ </artifactItem>
</artifactItems>
<outputDirectory>${project.build.directory}/src</outputDirectory>
</configuration>
@@ -137,7 +143,7 @@
<phase>package</phase>
<goals>
<goal>attached</goal>
- </goals>
+ </goals>
</execution>
<execution>
<id>make_assembly_bin</id>
@@ -219,7 +225,7 @@
<artifactId>myfaces-test22</artifactId>
<version>${project.version}</version>
</dependency>
-
+
<dependency>
<groupId>org.apache.myfaces.test</groupId>
<artifactId>myfaces-test23</artifactId>
diff --git a/pom.xml b/pom.xml
index 71d3e0d..2abd17d 100644
--- a/pom.xml
+++ b/pom.xml
@@ -47,6 +47,7 @@
<module>test20</module>
<module>test22</module>
<module>test23</module>
+ <module>test30</module>
</modules>
<!--
<repositories>
diff --git a/src/site/site.xml b/src/site/site.xml
index b643cfa..ab352b1 100644
--- a/src/site/site.xml
+++ b/src/site/site.xml
@@ -22,15 +22,15 @@
<src>img/banners/MyFaces_logo.jpg</src>
<href>http://myfaces.apache.org/index.html</href>
</bannerLeft>
-
+
<bannerRight>
<name>Apache Banner</name>
<src>img/banners/apache_banner.png</src>
<href>http://www.apache.org</href>
</bannerRight>
-
+
<publishDate format="dd MMM yyyy" />
-
+
<skin>
<groupId>org.apache.myfaces.maven</groupId>
<artifactId>myfaces-site-skin</artifactId>
@@ -44,11 +44,13 @@
<item name="1.2 Javadocs" href="./myfaces-test12/apidocs/index.html"/>
<item name="2.0 Javadocs" href="./myfaces-test20/apidocs/index.html"/>
<item name="2.2 Javadocs" href="./myfaces-test22/apidocs/index.html"/>
+ <item name="2.3 Javadocs" href="./myfaces-test23/apidocs/index.html"/>
+ <item name="3.0 Javadocs" href="./myfaces-test30/apidocs/index.html"/>
<item name="Download Myfaces Test" href="./download.html"/>
<item name="Apache" href="http://www.apache.org"/>
<item name="MyFaces" href="http://myfaces.apache.org/index.html"/>
</links>
-
+
<menu name="Apache MyFaces" inherit="top">
<item name="Overview" href="http://myfaces.apache.org/index.html"/>
<item name="Download" href="http://myfaces.apache.org/download.html"/>
@@ -68,7 +70,7 @@
<item name="dummy" href="dummy"/>
</item>
</menu>
-
+
<menu name="UI-Component Sets" inherit="top">
<item name="Trinidad" href="../trinidad/index.html"/>
<item name="Tobago" href="../tobago/index.html"/>
@@ -76,7 +78,7 @@
<item name="dummy" href="dummy"/>
</item>
</menu>
-
+
<menu name="Add-ons and Extensions" inherit="top">
<item name="CODI" href="../extensions/cdi/index.html"/>
<item name="Orchestra" href="../orchestra/index.html"/>
diff --git a/test30/pom.xml b/test30/pom.xml
new file mode 100644
index 0000000..42012f7
--- /dev/null
+++ b/test30/pom.xml
@@ -0,0 +1,209 @@
+<!--
+/*
+ * 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.
+ *
+ * $Id: pom.xml 1326869 2012-04-17 01:30:30Z lu4242 $
+ */
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.apache.myfaces.test</groupId>
+ <artifactId>myfaces-test-project</artifactId>
+ <version>1.0.9-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>myfaces-test30</artifactId>
+ <packaging>jar</packaging>
+ <name>Myfaces Test Framework for JSF 3.0</name>
+
+ <dependencies>
+
+ <!-- Required only for using the org.apache.shale.test.config package -->
+ <dependency>
+ <groupId>commons-digester</groupId>
+ <artifactId>commons-digester</artifactId>
+ <version>1.8</version>
+ <optional>true</optional>
+ </dependency>
+
+ <dependency>
+ <groupId>net.sourceforge.htmlunit</groupId>
+ <artifactId>htmlunit</artifactId>
+ <version>1.14</version>
+ <optional>true</optional>
+ </dependency>
+
+ <dependency>
+ <groupId>jmock</groupId>
+ <artifactId>jmock</artifactId>
+ <version>1.0.1</version>
+ <optional>true</optional>
+ </dependency>
+
+ <dependency>
+ <groupId>jmock</groupId>
+ <artifactId>jmock-cglib</artifactId>
+ <version>1.0.1</version>
+ <optional>true</optional>
+ </dependency>
+
+ <!-- For the "org.apache.shale.test.cargo" package, we need to have -->
+ <!-- JUnit as a compile time dependency, not just scope="test". -->
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <version>4.5</version>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.codehaus.cargo</groupId>
+ <artifactId>cargo-core-uberjar</artifactId>
+ <version>0.8</version>
+ <optional>true</optional>
+ </dependency>
+
+ <dependency>
+ <groupId>org.codehaus.cargo</groupId>
+ <artifactId>cargo-ant</artifactId>
+ <version>0.8</version>
+ <optional>true</optional>
+ </dependency>
+
+ <dependency>
+ <groupId>javax.portlet</groupId>
+ <artifactId>portlet-api</artifactId>
+ <version>1.0</version>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.myfaces.core</groupId>
+ <artifactId>myfaces-api</artifactId>
+ <version>${jsf-myfaces.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.myfaces.core</groupId>
+ <artifactId>myfaces-impl</artifactId>
+ <version>${jsf-myfaces.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <!--
+ <dependency>
+ <groupId>javax.faces</groupId>
+ <artifactId>javax.faces-api</artifactId>
+ <version>2.2</version>
+ <scope>provided</scope>
+ </dependency>-->
+
+ <!-- Servlet 3.0 -->
+ <dependency>
+ <groupId>org.apache.geronimo.specs</groupId>
+ <artifactId>geronimo-servlet_3.0_spec</artifactId>
+ <version>1.0</version>
+ <scope>provided</scope>
+ </dependency>
+ <!-- JSP 2.1 -->
+ <dependency>
+ <groupId>org.apache.geronimo.specs</groupId>
+ <artifactId>geronimo-jsp_2.1_spec</artifactId>
+ <version>1.0.1</version>
+ <scope>provided</scope>
+ </dependency>
+ <!-- EL 1.0 -->
+ <dependency>
+ <groupId>org.apache.geronimo.specs</groupId>
+ <artifactId>geronimo-el_1.0_spec</artifactId>
+ <version>1.0.2</version>
+ <scope>provided</scope>
+ </dependency>
+
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <configuration>
+ <source>1.6</source>
+ <target>1.6</target>
+ </configuration>
+ </plugin>
+ <plugin>
+ <artifactId>maven-source-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>attach-source</id>
+ <goals>
+ <goal>jar</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+ <profiles>
+ <profile>
+ <id>generate-assembly</id>
+ <activation>
+ <property>
+ <name>performRelease</name>
+ <value>true</value>
+ </property>
+ </activation>
+ <build>
+ <plugins>
+
+ <plugin>
+ <artifactId>maven-javadoc-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>attach-javadoc</id>
+ <goals>
+ <goal>jar</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+
+ </plugins>
+ </build>
+ </profile>
+ </profiles>
+ <reporting>
+ <plugins>
+ <plugin>
+ <artifactId>maven-javadoc-plugin</artifactId>
+ <reportSets>
+ <reportSet>
+ <id>non-aggregate</id>
+ <configuration>
+ </configuration>
+ <reports>
+ <report>javadoc</report>
+ </reports>
+ </reportSet>
+ </reportSets>
+ </plugin>
+ </plugins>
+ </reporting>
+ <properties>
+ <jsf-myfaces.version>3.0.0-SNAPSHOT</jsf-myfaces.version>
+ </properties>
+</project>
diff --git a/test30/src/main/java/org/apache/myfaces/test/base/AbstractJsfTestCase.java b/test30/src/main/java/org/apache/myfaces/test/base/AbstractJsfTestCase.java
new file mode 100644
index 0000000..4f76c28
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/base/AbstractJsfTestCase.java
@@ -0,0 +1,357 @@
+/*
+ * 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.myfaces.test.base;
+
+import java.net.URL;
+import java.net.URLClassLoader;
+
+import javax.faces.FactoryFinder;
+import javax.faces.application.ApplicationFactory;
+import javax.faces.component.UIViewRoot;
+import javax.faces.lifecycle.LifecycleFactory;
+import javax.faces.render.RenderKitFactory;
+
+import junit.framework.TestCase;
+
+import org.apache.myfaces.test.config.ResourceBundleVarNames;
+import org.apache.myfaces.test.mock.MockApplication;
+import org.apache.myfaces.test.mock.MockExternalContext;
+import org.apache.myfaces.test.mock.MockFacesContext;
+import org.apache.myfaces.test.mock.MockFacesContextFactory;
+import org.apache.myfaces.test.mock.MockHttpServletRequest;
+import org.apache.myfaces.test.mock.MockHttpServletResponse;
+import org.apache.myfaces.test.mock.MockHttpSession;
+import org.apache.myfaces.test.mock.MockRenderKit;
+import org.apache.myfaces.test.mock.MockServletConfig;
+import org.apache.myfaces.test.mock.MockServletContext;
+import org.apache.myfaces.test.mock.lifecycle.MockLifecycle;
+import org.apache.myfaces.test.mock.lifecycle.MockLifecycleFactory;
+
+/**
+ * <p>Abstract JUnit test case base class, which sets up the JavaServer Faces
+ * mock object environment for a particular simulated request. The following
+ * protected variables are initialized in the <code>setUp()</code> method, and
+ * cleaned up in the <code>tearDown()</code> method:</p>
+ * <ul>
+ * <li><code>application</code> (<code>MockApplication</code>)</li>
+ * <li><code>config</code> (<code>MockServletConfig</code>)</li>
+ * <li><code>externalContext</code> (<code>MockExternalContext</code>)</li>
+ * <li><code>facesContext</code> (<code>MockFacesContext</code>)</li>
+ * <li><code>lifecycle</code> (<code>MockLifecycle</code>)</li>
+ * <li><code>request</code> (<code>MockHttpServletRequest</code></li>
+ * <li><code>response</code> (<code>MockHttpServletResponse</code>)</li>
+ * <li><code>servletContext</code> (<code>MockServletContext</code>)</li>
+ * <li><code>session</code> (<code>MockHttpSession</code>)</li>
+ * </ul>
+ *
+ * <p>In addition, appropriate factory classes will have been registered with
+ * <code>javax.faces.FactoryFinder</code> for <code>Application</code> and
+ * <code>RenderKit</code> instances. The created <code>FacesContext</code>
+ * instance will also have been registered in the proper thread local
+ * variable, to simulate what a servlet container would do.</p>
+ *
+ * <p><strong>WARNING</strong> - If you choose to subclass this class, be sure
+ * your <code>setUp()</code> and <code>tearDown()</code> methods call
+ * <code>super.setUp()</code> and <code>super.tearDown()</code> respectively,
+ * and that you implement your own <code>suite()</code> method that exposes
+ * the test methods for your test case.</p>
+ *
+ * @since 1.0.0
+ */
+
+public abstract class AbstractJsfTestCase extends TestCase
+{
+
+ // ------------------------------------------------------------ Constructors
+
+ /**
+ * <p>Construct a new instance of this test case.</p>
+ *
+ * @param name Name of this test case
+ */
+ public AbstractJsfTestCase(String name)
+ {
+ super(name);
+ }
+
+ // ---------------------------------------------------- Overall Test Methods
+
+ /**
+ * <p>Set up instance variables required by this test case.</p>
+ */
+ protected void setUp() throws Exception
+ {
+ // Set up a new thread context class loader
+ setUpClassloader();
+
+ // Set up Servlet API Objects
+ setUpServletObjects();
+
+ // Set up JSF API Objects
+ FactoryFinder.releaseFactories();
+
+ setFactories();
+
+ setUpJSFObjects();
+ }
+
+ /**
+ * Set up the thread context classloader. JSF uses the this classloader
+ * in order to find related factory classes and other resources, but in
+ * some selected cases, the default classloader cannot be properly set.
+ *
+ * @throws Exception
+ */
+ protected void setUpClassloader() throws Exception
+ {
+ threadContextClassLoader = Thread.currentThread()
+ .getContextClassLoader();
+ Thread.currentThread()
+ .setContextClassLoader(
+ new URLClassLoader(new URL[0], this.getClass()
+ .getClassLoader()));
+ classLoaderSet = true;
+ }
+
+ /**
+ * <p>Setup JSF object used for the test. By default it calls to the following
+ * methods in this order:</p>
+ *
+ * <ul>
+ * <li><code>setUpExternalContext();</code></li>
+ * <li><code>setUpLifecycle();</code></li>
+ * <li><code>setUpFacesContext();</code></li>
+ * <li><code>setUpView();</code></li>
+ * <li><code>setUpApplication();</code></li>
+ * <li><code>setUpRenderKit();</code></li>
+ * </ul>
+ *
+ * @throws Exception
+ */
+ protected void setUpJSFObjects() throws Exception
+ {
+ setUpExternalContext();
+ setUpLifecycle();
+ setUpFacesContext();
+ setUpView();
+ setUpApplication();
+ setUpRenderKit();
+ }
+
+ /**
+ * <p>Setup servlet objects that will be used for the test:</p>
+ *
+ * <ul>
+ * <li><code>config</code> (<code>MockServletConfig</code>)</li>
+ * <li><code>servletContext</code> (<code>MockServletContext</code>)</li>
+ * <li><code>request</code> (<code>MockHttpServletRequest</code></li>
+ * <li><code>response</code> (<code>MockHttpServletResponse</code>)</li>
+ * <li><code>session</code> (<code>MockHttpSession</code>)</li>
+ * </ul>
+ *
+ * @throws Exception
+ */
+ protected void setUpServletObjects() throws Exception
+ {
+ servletContext = new MockServletContext();
+ config = new MockServletConfig(servletContext);
+ session = new MockHttpSession();
+ session.setServletContext(servletContext);
+ request = new MockHttpServletRequest(session);
+ request.setServletContext(servletContext);
+ response = new MockHttpServletResponse();
+ }
+
+ /**
+ * <p>Set JSF factories using FactoryFinder method setFactory.</p>
+ *
+ * @throws Exception
+ */
+ protected void setFactories() throws Exception
+ {
+ FactoryFinder.setFactory(FactoryFinder.APPLICATION_FACTORY,
+ "org.apache.myfaces.test.mock.MockApplicationFactory");
+ FactoryFinder.setFactory(FactoryFinder.FACES_CONTEXT_FACTORY,
+ "org.apache.myfaces.test.mock.MockFacesContextFactory");
+ FactoryFinder.setFactory(FactoryFinder.LIFECYCLE_FACTORY,
+ "org.apache.myfaces.test.mock.lifecycle.MockLifecycleFactory");
+ FactoryFinder.setFactory(FactoryFinder.RENDER_KIT_FACTORY,
+ "org.apache.myfaces.test.mock.MockRenderKitFactory");
+ FactoryFinder.setFactory(FactoryFinder.EXCEPTION_HANDLER_FACTORY,
+ "org.apache.myfaces.test.mock.MockExceptionHandlerFactory");
+ FactoryFinder.setFactory(FactoryFinder.PARTIAL_VIEW_CONTEXT_FACTORY,
+ "org.apache.myfaces.test.mock.MockPartialViewContextFactory");
+ FactoryFinder.setFactory(FactoryFinder.VISIT_CONTEXT_FACTORY,
+ "org.apache.myfaces.test.mock.visit.MockVisitContextFactory");
+ FactoryFinder.setFactory(FactoryFinder.CLIENT_WINDOW_FACTORY,
+ "org.apache.myfaces.test.mock.MockClientWindowFactory");
+ }
+
+ /**
+ * Setup the <code>externalContext</code> variable, using the
+ * servlet variables already initialized.
+ *
+ * @throws Exception
+ */
+ protected void setUpExternalContext() throws Exception
+ {
+ externalContext = new MockExternalContext(servletContext, request,
+ response);
+ }
+
+ /**
+ * Setup the <code>lifecycle</code> and <code>lifecycleFactory</code>
+ * variables.
+ *
+ * @throws Exception
+ */
+ protected void setUpLifecycle() throws Exception
+ {
+ lifecycleFactory = (MockLifecycleFactory) FactoryFinder
+ .getFactory(FactoryFinder.LIFECYCLE_FACTORY);
+ lifecycle = (MockLifecycle) lifecycleFactory
+ .getLifecycle(LifecycleFactory.DEFAULT_LIFECYCLE);
+ }
+
+ /**
+ * Setup the <code>facesContextFactory</code> and <code>facesContext</code>
+ * variable. Before end, by default it override <code>externalContext</code>
+ * variable from the value retrieved from facesContext.getExternalContext(),
+ * because sometimes it is possible facesContext overrides externalContext
+ * internally.
+ *
+ * @throws Exception
+ */
+ protected void setUpFacesContext() throws Exception
+ {
+ facesContextFactory = (MockFacesContextFactory) FactoryFinder
+ .getFactory(FactoryFinder.FACES_CONTEXT_FACTORY);
+ facesContext = (MockFacesContext) facesContextFactory.getFacesContext(
+ servletContext, request, response, lifecycle);
+ if (facesContext.getExternalContext() != null)
+ {
+ externalContext = (MockExternalContext) facesContext
+ .getExternalContext();
+ }
+ }
+
+ /**
+ * By default, create an instance of UIViewRoot, set its viewId as "/viewId"
+ * and assign it to the current facesContext.
+ *
+ * @throws Exception
+ */
+ protected void setUpView() throws Exception
+ {
+ UIViewRoot root = new UIViewRoot();
+ root.setViewId("/viewId");
+ root.setRenderKitId(RenderKitFactory.HTML_BASIC_RENDER_KIT);
+ facesContext.setViewRoot(root);
+ }
+
+ /**
+ * Setup the <code>application</code> variable and before
+ * the end by default it is assigned to the <code>facesContext</code>
+ * variable, calling <code>facesContext.setApplication(application)</code>
+ *
+ * @throws Exception
+ */
+ protected void setUpApplication() throws Exception
+ {
+ ApplicationFactory applicationFactory = (ApplicationFactory) FactoryFinder
+ .getFactory(FactoryFinder.APPLICATION_FACTORY);
+ application = (MockApplication) applicationFactory.getApplication();
+ facesContext.setApplication(application);
+ }
+
+ /**
+ * Setup the <code>renderKit</code> variable. This is a good place to use
+ * <code>ConfigParser</code> to register converters, validators, components
+ * or renderkits.
+ *
+ * @throws Exception
+ */
+ protected void setUpRenderKit() throws Exception
+ {
+ RenderKitFactory renderKitFactory = (RenderKitFactory) FactoryFinder
+ .getFactory(FactoryFinder.RENDER_KIT_FACTORY);
+ renderKit = new MockRenderKit();
+ renderKitFactory.addRenderKit(RenderKitFactory.HTML_BASIC_RENDER_KIT,
+ renderKit);
+ }
+
+ /**
+ * <p>Tear down instance variables required by this test case.</p>
+ */
+ protected void tearDown() throws Exception
+ {
+
+ application = null;
+ config = null;
+ externalContext = null;
+ if (facesContext != null)
+ {
+ facesContext.release();
+ }
+ facesContext = null;
+ lifecycle = null;
+ lifecycleFactory = null;
+ renderKit = null;
+ request = null;
+ response = null;
+ servletContext = null;
+ session = null;
+ FactoryFinder.releaseFactories();
+ ResourceBundleVarNames.resetNames();
+
+ tearDownClassloader();
+ }
+
+ protected void tearDownClassloader() throws Exception
+ {
+ if (classLoaderSet)
+ {
+ Thread.currentThread().setContextClassLoader(threadContextClassLoader);
+ threadContextClassLoader = null;
+ classLoaderSet = false;
+ }
+ }
+
+ // ------------------------------------------------------ Instance Variables
+
+ // Mock object instances for our tests
+ protected MockApplication application = null;
+ protected MockServletConfig config = null;
+ protected MockExternalContext externalContext = null;
+ protected MockFacesContext facesContext = null;
+ protected MockFacesContextFactory facesContextFactory = null;
+ protected MockLifecycle lifecycle = null;
+ protected MockLifecycleFactory lifecycleFactory = null;
+ protected MockRenderKit renderKit = null;
+ protected MockHttpServletRequest request = null;
+ protected MockHttpServletResponse response = null;
+ protected MockServletContext servletContext = null;
+ protected MockHttpSession session = null;
+
+ // Thread context class loader saved and restored after each test
+ private ClassLoader threadContextClassLoader = null;
+ private boolean classLoaderSet = false;
+
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/base/AbstractViewControllerTestCase.java b/test30/src/main/java/org/apache/myfaces/test/base/AbstractViewControllerTestCase.java
new file mode 100644
index 0000000..aefc876
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/base/AbstractViewControllerTestCase.java
@@ -0,0 +1,101 @@
+/*
+ * 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.myfaces.test.base;
+
+import java.util.Iterator;
+
+/**
+ * <p>Abstract base class for testing <code>ViewController</code>
+ * implementations.</p>
+ *
+ * <p><strong>WARNING</strong> - If you choose to subclass this class, be sure
+ * your <code>setUp()</code> and <code>tearDown()</code> methods call
+ * <code>super.setUp()</code> and <code>super.tearDown()</code> respectively,
+ * and that you implement your own <code>suite()</code> method that exposes
+ * the test methods for your test case.</p>
+ *
+ * @since 1.0.0
+ */
+public abstract class AbstractViewControllerTestCase extends
+ AbstractJsfTestCase
+{
+
+ // ------------------------------------------------------------ Constructors
+
+ /**
+ * <p>Construct a new instance of this test case.</p>
+ *
+ * @param name Test case name
+ */
+ public AbstractViewControllerTestCase(String name)
+ {
+ super(name);
+ }
+
+ // ---------------------------------------------------- Overall Test Methods
+
+ // ------------------------------------------------------ Instance Variables
+
+ // ------------------------------------------------------- Protected Methods
+
+ /**
+ * <p>Test that the specified number of messages have been queued on the
+ * <code>FacesContext</code> instance, without regard to matching a
+ * particular client identifier.</p>
+ *
+ * @param expected The expected number of messages
+ */
+ protected void checkMessageCount(int expected)
+ {
+
+ int actual = 0;
+ Iterator messages = facesContext.getMessages();
+ while (messages.hasNext())
+ {
+ messages.next();
+ actual++;
+ }
+ assertEquals("Complete message count", expected, actual);
+
+ }
+
+ /**
+ * <p>Test that the specified number of messages have been queued on the
+ * <code>FacesContext</code> instance, for the specified client id.</p>
+ *
+ * @param clientId Client identifier of the component for which to
+ * count queued messages
+ * @param expected The expected number of messages
+ */
+ protected void checkMessageCount(String clientId, int expected)
+ {
+
+ int actual = 0;
+ Iterator messages = facesContext.getMessages(clientId);
+ while (messages.hasNext())
+ {
+ messages.next();
+ actual++;
+ }
+ assertEquals("Complete message count", expected, actual);
+
+ }
+
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/base/junit4/AbstractJsfConfigurableMockTestCase.java b/test30/src/main/java/org/apache/myfaces/test/base/junit4/AbstractJsfConfigurableMockTestCase.java
new file mode 100644
index 0000000..7f65067
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/base/junit4/AbstractJsfConfigurableMockTestCase.java
@@ -0,0 +1,359 @@
+/*
+ * 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.myfaces.test.base.junit4;
+
+import java.net.URL;
+import java.net.URLClassLoader;
+
+import javax.faces.FactoryFinder;
+import javax.faces.application.Application;
+import javax.faces.application.ApplicationFactory;
+import javax.faces.component.UIViewRoot;
+import javax.faces.context.ExternalContext;
+import javax.faces.context.FacesContext;
+import javax.faces.context.FacesContextFactory;
+import javax.faces.lifecycle.Lifecycle;
+import javax.faces.lifecycle.LifecycleFactory;
+import javax.faces.render.RenderKit;
+import javax.faces.render.RenderKitFactory;
+
+import org.apache.myfaces.test.config.ResourceBundleVarNames;
+import org.apache.myfaces.test.mock.MockExternalContext;
+import org.apache.myfaces.test.mock.MockFacesContext;
+import org.apache.myfaces.test.mock.MockHttpServletRequest;
+import org.apache.myfaces.test.mock.MockHttpServletResponse;
+import org.apache.myfaces.test.mock.MockHttpSession;
+import org.apache.myfaces.test.mock.MockRenderKit;
+import org.apache.myfaces.test.mock.MockServletConfig;
+import org.apache.myfaces.test.mock.MockServletContext;
+import org.junit.After;
+import org.junit.Before;
+
+/**
+ * <p>Abstract JUnit 4.5 test case base class, which sets up the JavaServer Faces
+ * mock object environment for a particular simulated request. The following
+ * protected variables are initialized in the <code>setUp()</code> method, and
+ * cleaned up in the <code>tearDown()</code> method:</p>
+ * <ul>
+ * <li><code>application</code> (<code>Application</code>)</li>
+ * <li><code>config</code> (<code>MockServletConfig</code>)</li>
+ * <li><code>externalContext</code> (<code>ExternalContext</code>)</li>
+ * <li><code>facesContext</code> (<code>FacesContext</code>)</li>
+ * <li><code>lifecycle</code> (<code>Lifecycle</code>)</li>
+ * <li><code>request</code> (<code>MockHttpServletRequest</code></li>
+ * <li><code>response</code> (<code>MockHttpServletResponse</code>)</li>
+ * <li><code>servletContext</code> (<code>MockServletContext</code>)</li>
+ * <li><code>session</code> (<code>MockHttpSession</code>)</li>
+ * </ul>
+ *
+ * <p>In addition, appropriate factory classes will have been registered with
+ * <code>javax.faces.FactoryFinder</code> for <code>Application</code> and
+ * <code>RenderKit</code> instances. The created <code>FacesContext</code>
+ * instance will also have been registered in the proper thread local
+ * variable, to simulate what a servlet container would do.</p>
+ *
+ * <p><strong>WARNING</strong> - If you choose to subclass this class, be sure
+ * your <code>setUp()</code> and <code>tearDown()</code> methods call
+ * <code>super.setUp()</code> and <code>super.tearDown()</code> respectively,
+ * and that you implement your own <code>suite()</code> method that exposes
+ * the test methods for your test case.</p>
+ *
+ * @since 1.0.0
+ */
+
+public abstract class AbstractJsfConfigurableMockTestCase
+{
+
+ // ------------------------------------------------------------ Constructors
+
+ /**
+ * <p>Construct a new instance of this test case.</p>
+ *
+ * @param name Name of this test case
+ */
+ public AbstractJsfConfigurableMockTestCase()
+ {
+ }
+
+ // ---------------------------------------------------- Overall Test Methods
+
+ /**
+ * <p>Set up instance variables required by this test case.</p>
+ */
+ @Before
+ public void setUp() throws Exception
+ {
+ // Set up a new thread context class loader
+ setUpClassloader();
+
+ // Set up Servlet API Objects
+ setUpServletObjects();
+
+ // Set up JSF API Objects
+ FactoryFinder.releaseFactories();
+
+ setFactories();
+
+ setUpJSFObjects();
+ }
+
+ /**
+ * Set up the thread context classloader. JSF uses the this classloader
+ * in order to find related factory classes and other resources, but in
+ * some selected cases, the default classloader cannot be properly set.
+ *
+ * @throws Exception
+ */
+ protected void setUpClassloader() throws Exception
+ {
+ threadContextClassLoader = Thread.currentThread()
+ .getContextClassLoader();
+ Thread.currentThread()
+ .setContextClassLoader(
+ new URLClassLoader(new URL[0], this.getClass()
+ .getClassLoader()));
+ classLoaderSet = true;
+ }
+
+ /**
+ * <p>Setup JSF object used for the test. By default it calls to the following
+ * methods in this order:</p>
+ *
+ * <ul>
+ * <li><code>setUpExternalContext();</code></li>
+ * <li><code>setUpLifecycle();</code></li>
+ * <li><code>setUpFacesContext();</code></li>
+ * <li><code>setUpView();</code></li>
+ * <li><code>setUpApplication();</code></li>
+ * <li><code>setUpRenderKit();</code></li>
+ * </ul>
+ *
+ * @throws Exception
+ */
+ protected void setUpJSFObjects() throws Exception
+ {
+ setUpExternalContext();
+ setUpLifecycle();
+ setUpFacesContext();
+ setUpView();
+ setUpApplication();
+ setUpRenderKit();
+ }
+
+ /**
+ * <p>Setup servlet objects that will be used for the test:</p>
+ *
+ * <ul>
+ * <li><code>config</code> (<code>MockServletConfig</code>)</li>
+ * <li><code>servletContext</code> (<code>MockServletContext</code>)</li>
+ * <li><code>request</code> (<code>MockHttpServletRequest</code></li>
+ * <li><code>response</code> (<code>MockHttpServletResponse</code>)</li>
+ * <li><code>session</code> (<code>MockHttpSession</code>)</li>
+ * </ul>
+ *
+ * @throws Exception
+ */
+ protected void setUpServletObjects() throws Exception
+ {
+ servletContext = new MockServletContext();
+ config = new MockServletConfig(servletContext);
+ session = new MockHttpSession();
+ session.setServletContext(servletContext);
+ request = new MockHttpServletRequest(session);
+ request.setServletContext(servletContext);
+ response = new MockHttpServletResponse();
+ }
+
+ /**
+ * <p>Set JSF factories using FactoryFinder method setFactory.</p>
+ *
+ * @throws Exception
+ */
+ protected void setFactories() throws Exception
+ {
+ FactoryFinder.setFactory(FactoryFinder.APPLICATION_FACTORY,
+ "org.apache.myfaces.test.mock.MockApplicationFactory");
+ FactoryFinder.setFactory(FactoryFinder.FACES_CONTEXT_FACTORY,
+ "org.apache.myfaces.test.mock.MockFacesContextFactory");
+ FactoryFinder.setFactory(FactoryFinder.LIFECYCLE_FACTORY,
+ "org.apache.myfaces.test.mock.lifecycle.MockLifecycleFactory");
+ FactoryFinder.setFactory(FactoryFinder.RENDER_KIT_FACTORY,
+ "org.apache.myfaces.test.mock.MockRenderKitFactory");
+ FactoryFinder.setFactory(FactoryFinder.EXCEPTION_HANDLER_FACTORY,
+ "org.apache.myfaces.test.mock.MockExceptionHandlerFactory");
+ FactoryFinder.setFactory(FactoryFinder.PARTIAL_VIEW_CONTEXT_FACTORY,
+ "org.apache.myfaces.test.mock.MockPartialViewContextFactory");
+ FactoryFinder.setFactory(FactoryFinder.VISIT_CONTEXT_FACTORY,
+ "org.apache.myfaces.test.mock.visit.MockVisitContextFactory");
+ FactoryFinder.setFactory(FactoryFinder.CLIENT_WINDOW_FACTORY,
+ "org.apache.myfaces.test.mock.MockClientWindowFactory");
+ }
+
+ /**
+ * Setup the <code>externalContext</code> variable, using the
+ * servlet variables already initialized.
+ *
+ * @throws Exception
+ */
+ protected void setUpExternalContext() throws Exception
+ {
+ externalContext = new MockExternalContext(servletContext, request,
+ response);
+ }
+
+ /**
+ * Setup the <code>lifecycle</code> and <code>lifecycleFactory</code>
+ * variables.
+ *
+ * @throws Exception
+ */
+ protected void setUpLifecycle() throws Exception
+ {
+ lifecycleFactory = (LifecycleFactory) FactoryFinder
+ .getFactory(FactoryFinder.LIFECYCLE_FACTORY);
+ lifecycle = lifecycleFactory
+ .getLifecycle(LifecycleFactory.DEFAULT_LIFECYCLE);
+ }
+
+ /**
+ * Setup the <code>facesContextFactory</code> and <code>facesContext</code>
+ * variable. Before end, by default it override <code>externalContext</code>
+ * variable from the value retrieved from facesContext.getExternalContext(),
+ * because sometimes it is possible facesContext overrides externalContext
+ * internally.
+ *
+ * @throws Exception
+ */
+ protected void setUpFacesContext() throws Exception
+ {
+ facesContextFactory = (FacesContextFactory) FactoryFinder
+ .getFactory(FactoryFinder.FACES_CONTEXT_FACTORY);
+ facesContext = (FacesContext) facesContextFactory.getFacesContext(
+ servletContext, request, response, lifecycle);
+ if (facesContext.getExternalContext() != null)
+ {
+ externalContext = facesContext.getExternalContext();
+ }
+ }
+
+ /**
+ * By default, create an instance of UIViewRoot, set its viewId as "/viewId"
+ * and assign it to the current facesContext.
+ *
+ * @throws Exception
+ */
+ protected void setUpView() throws Exception
+ {
+ UIViewRoot root = new UIViewRoot();
+ root.setViewId("/viewId");
+ root.setRenderKitId(RenderKitFactory.HTML_BASIC_RENDER_KIT);
+ facesContext.setViewRoot(root);
+ }
+
+ /**
+ * Setup the <code>application</code> variable and before
+ * the end by default it is assigned to the <code>facesContext</code>
+ * variable, calling <code>facesContext.setApplication(application)</code>
+ *
+ * @throws Exception
+ */
+ protected void setUpApplication() throws Exception
+ {
+ ApplicationFactory applicationFactory = (ApplicationFactory) FactoryFinder
+ .getFactory(FactoryFinder.APPLICATION_FACTORY);
+ application = applicationFactory.getApplication();
+ ((MockFacesContext) facesContext).setApplication(application);
+ }
+
+ /**
+ * Setup the <code>renderKit</code> variable. This is a good place to use
+ * <code>ConfigParser</code> to register converters, validators, components
+ * or renderkits.
+ *
+ * @throws Exception
+ */
+ protected void setUpRenderKit() throws Exception
+ {
+ RenderKitFactory renderKitFactory = (RenderKitFactory) FactoryFinder
+ .getFactory(FactoryFinder.RENDER_KIT_FACTORY);
+ renderKit = new MockRenderKit();
+ renderKitFactory.addRenderKit(RenderKitFactory.HTML_BASIC_RENDER_KIT,
+ renderKit);
+ }
+
+ /**
+ * <p>Tear down instance variables required by this test case.</p>
+ */
+ @After
+ public void tearDown() throws Exception
+ {
+
+ application = null;
+ config = null;
+ externalContext = null;
+ if (facesContext != null)
+ {
+ facesContext.release();
+ }
+ facesContext = null;
+ lifecycle = null;
+ lifecycleFactory = null;
+ renderKit = null;
+ request = null;
+ response = null;
+ servletContext = null;
+ session = null;
+ FactoryFinder.releaseFactories();
+ ResourceBundleVarNames.resetNames();
+
+ tearDownClassloader();
+ }
+
+ protected void tearDownClassloader() throws Exception
+ {
+ if (classLoaderSet)
+ {
+ Thread.currentThread().setContextClassLoader(threadContextClassLoader);
+ threadContextClassLoader = null;
+ classLoaderSet = false;
+ }
+ }
+
+ // ------------------------------------------------------ Instance Variables
+
+ // Mock object instances for our tests
+ protected Application application = null;
+ protected MockServletConfig config = null;
+ protected ExternalContext externalContext = null;
+ protected FacesContext facesContext = null;
+ protected FacesContextFactory facesContextFactory = null;
+ protected Lifecycle lifecycle = null;
+ protected LifecycleFactory lifecycleFactory = null;
+ protected RenderKit renderKit = null;
+ protected MockHttpServletRequest request = null;
+ protected MockHttpServletResponse response = null;
+ protected MockServletContext servletContext = null;
+ protected MockHttpSession session = null;
+
+ // Thread context class loader saved and restored after each test
+ private ClassLoader threadContextClassLoader = null;
+ private boolean classLoaderSet = false;
+
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/base/junit4/AbstractJsfConfigurableMultipleRequestsTestCase.java b/test30/src/main/java/org/apache/myfaces/test/base/junit4/AbstractJsfConfigurableMultipleRequestsTestCase.java
new file mode 100644
index 0000000..6e52e3c
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/base/junit4/AbstractJsfConfigurableMultipleRequestsTestCase.java
@@ -0,0 +1,399 @@
+/*
+ * 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.myfaces.test.base.junit4;
+
+import java.net.URL;
+import java.net.URLClassLoader;
+
+import javax.faces.FactoryFinder;
+import javax.faces.application.Application;
+import javax.faces.application.ApplicationFactory;
+import javax.faces.component.UIViewRoot;
+import javax.faces.context.ExternalContext;
+import javax.faces.context.FacesContext;
+import javax.faces.context.FacesContextFactory;
+import javax.faces.lifecycle.Lifecycle;
+import javax.faces.lifecycle.LifecycleFactory;
+import javax.faces.render.RenderKit;
+import javax.faces.render.RenderKitFactory;
+
+import org.apache.myfaces.test.config.ResourceBundleVarNames;
+import org.apache.myfaces.test.mock.MockExternalContext;
+import org.apache.myfaces.test.mock.MockFacesContext;
+import org.apache.myfaces.test.mock.MockFacesContextFactory;
+import org.apache.myfaces.test.mock.MockHttpServletRequest;
+import org.apache.myfaces.test.mock.MockHttpServletResponse;
+import org.apache.myfaces.test.mock.MockHttpSession;
+import org.apache.myfaces.test.mock.MockRenderKit;
+import org.apache.myfaces.test.mock.MockServletConfig;
+import org.apache.myfaces.test.mock.MockServletContext;
+import org.apache.myfaces.test.mock.lifecycle.MockLifecycle;
+import org.apache.myfaces.test.mock.lifecycle.MockLifecycleFactory;
+import org.junit.After;
+import org.junit.Before;
+
+/**
+ * <p>Abstract JUnit 4.5 test case base class, which sets up the JavaServer Faces
+ * mock object environment for multiple simulated request. The following
+ * protected variables are initialized in the <code>setUp()</code> method, and
+ * cleaned up in the <code>tearDown()</code> method:</p>
+ * <ul>
+ * <li><code>application</code> (<code>Application</code>)</li>
+ * <li><code>config</code> (<code>MockServletConfig</code>)</li>
+ * <li><code>externalContext</code> (<code>ExternalContext</code>)</li>
+ * <li><code>facesContext</code> (<code>FacesContext</code>)</li>
+ * <li><code>lifecycle</code> (<code>Lifecycle</code>)</li>
+ * <li><code>request</code> (<code>MockHttpServletRequest</code></li>
+ * <li><code>response</code> (<code>MockHttpServletResponse</code>)</li>
+ * <li><code>servletContext</code> (<code>MockServletContext</code>)</li>
+ * <li><code>session</code> (<code>MockHttpSession</code>)</li>
+ * </ul>
+ *
+ * <p>In addition, appropriate factory classes will have been registered with
+ * <code>javax.faces.FactoryFinder</code> for <code>Application</code> and
+ * <code>RenderKit</code> instances. The created <code>FacesContext</code>
+ * instance will also have been registered in the proper thread local
+ * variable, to simulate what a servlet container would do.</p>
+ *
+ * <p><strong>WARNING</strong> - If you choose to subclass this class, be sure
+ * your <code>setUp()</code> and <code>tearDown()</code> methods call
+ * <code>super.setUp()</code> and <code>super.tearDown()</code> respectively,
+ * and that you implement your own <code>suite()</code> method that exposes
+ * the test methods for your test case. Additionally, check on each test
+ * that setupRequest() and tearDownRequest() are called correctly.</p>
+ *
+ * @since 1.0.3
+ */
+
+public abstract class AbstractJsfConfigurableMultipleRequestsTestCase
+{
+
+ // ------------------------------------------------------------ Constructors
+
+ /**
+ * <p>Construct a new instance of this test case.</p>
+ *
+ * @param name Name of this test case
+ */
+ public AbstractJsfConfigurableMultipleRequestsTestCase()
+ {
+ }
+
+ // ---------------------------------------------------- Overall Test Methods
+
+ /**
+ * <p>Set up instance variables required by this test case.</p>
+ */
+ @Before
+ public void setUp() throws Exception
+ {
+ // Set up a new thread context class loader
+ setUpClassloader();
+
+ // Set up JSF Factories
+ FactoryFinder.releaseFactories();
+
+ setFactories();
+
+ // Setup servlet context and session
+ setUpServletContextAndSession();
+
+ setUpLifecycle();
+
+ setUpApplication();
+
+ setUpRenderKit();
+ }
+
+ /**
+ * Set up the thread context classloader. JSF uses the this classloader
+ * in order to find related factory classes and other resources, but in
+ * some selected cases, the default classloader cannot be properly set.
+ *
+ * @throws Exception
+ */
+ protected void setUpClassloader() throws Exception
+ {
+ threadContextClassLoader = Thread.currentThread()
+ .getContextClassLoader();
+ Thread.currentThread()
+ .setContextClassLoader(
+ new URLClassLoader(new URL[0], this.getClass()
+ .getClassLoader()));
+ classLoaderSet = true;
+ }
+
+ /**
+ * This method initialize an new empty request.
+ */
+ public void setupRequest() throws Exception
+ {
+ // Set up Servlet API Objects
+ setUpServletRequestAndResponse();
+
+ setUpJSFRequestObjects();
+ }
+
+ /**
+ * <p>Setup JSF object used for the test. By default it calls to the following
+ * methods in this order:</p>
+ *
+ * <ul>
+ * <li><code>setUpExternalContext();</code></li>
+ * <li><code>setUpLifecycle();</code></li>
+ * <li><code>setUpFacesContext();</code></li>
+ * <li><code>setUpView();</code></li>
+ * <li><code>setUpApplication();</code></li>
+ * <li><code>setUpRenderKit();</code></li>
+ * </ul>
+ *
+ * @throws Exception
+ */
+ protected void setUpJSFRequestObjects() throws Exception
+ {
+ setUpExternalContext();
+ setUpFacesContext();
+ setUpView();
+ }
+
+ /**
+ * <p>Setup servlet objects that will be used for the test:</p>
+ *
+ * <ul>
+ * <li><code>config</code> (<code>MockServletConfig</code>)</li>
+ * <li><code>servletContext</code> (<code>MockServletContext</code>)</li>
+ * <li><code>request</code> (<code>MockHttpServletRequest</code></li>
+ * <li><code>response</code> (<code>MockHttpServletResponse</code>)</li>
+ * <li><code>session</code> (<code>MockHttpSession</code>)</li>
+ * </ul>
+ *
+ * @throws Exception
+ */
+ protected void setUpServletContextAndSession() throws Exception
+ {
+ servletContext = new MockServletContext();
+ config = new MockServletConfig(servletContext);
+ session = new MockHttpSession();
+ session.setServletContext(servletContext);
+ }
+
+ protected void setUpServletRequestAndResponse() throws Exception
+ {
+ request = new MockHttpServletRequest(session);
+ request.setServletContext(servletContext);
+ response = new MockHttpServletResponse();
+ }
+
+ /**
+ * <p>Set JSF factories using FactoryFinder method setFactory.</p>
+ *
+ * @throws Exception
+ */
+ protected void setFactories() throws Exception
+ {
+ FactoryFinder.setFactory(FactoryFinder.APPLICATION_FACTORY,
+ "org.apache.myfaces.test.mock.MockApplicationFactory");
+ FactoryFinder.setFactory(FactoryFinder.FACES_CONTEXT_FACTORY,
+ "org.apache.myfaces.test.mock.MockFacesContextFactory");
+ FactoryFinder.setFactory(FactoryFinder.LIFECYCLE_FACTORY,
+ "org.apache.myfaces.test.mock.lifecycle.MockLifecycleFactory");
+ FactoryFinder.setFactory(FactoryFinder.RENDER_KIT_FACTORY,
+ "org.apache.myfaces.test.mock.MockRenderKitFactory");
+ FactoryFinder.setFactory(FactoryFinder.EXCEPTION_HANDLER_FACTORY,
+ "org.apache.myfaces.test.mock.MockExceptionHandlerFactory");
+ FactoryFinder.setFactory(FactoryFinder.PARTIAL_VIEW_CONTEXT_FACTORY,
+ "org.apache.myfaces.test.mock.MockPartialViewContextFactory");
+ FactoryFinder.setFactory(FactoryFinder.VISIT_CONTEXT_FACTORY,
+ "org.apache.myfaces.test.mock.visit.MockVisitContextFactory");
+ FactoryFinder.setFactory(FactoryFinder.CLIENT_WINDOW_FACTORY,
+ "org.apache.myfaces.test.mock.MockClientWindowFactory");
+ }
+
+ /**
+ * Setup the <code>externalContext</code> variable, using the
+ * servlet variables already initialized.
+ *
+ * @throws Exception
+ */
+ protected void setUpExternalContext() throws Exception
+ {
+ externalContext = new MockExternalContext(servletContext, request,
+ response);
+ }
+
+ /**
+ * Setup the <code>lifecycle</code> and <code>lifecycleFactory</code>
+ * variables.
+ *
+ * @throws Exception
+ */
+ protected void setUpLifecycle() throws Exception
+ {
+ lifecycleFactory = (MockLifecycleFactory) FactoryFinder
+ .getFactory(FactoryFinder.LIFECYCLE_FACTORY);
+ lifecycle = (MockLifecycle) lifecycleFactory
+ .getLifecycle(LifecycleFactory.DEFAULT_LIFECYCLE);
+ }
+
+ /**
+ * Setup the <code>facesContextFactory</code> and <code>facesContext</code>
+ * variable. Before end, by default it override <code>externalContext</code>
+ * variable from the value retrieved from facesContext.getExternalContext(),
+ * because sometimes it is possible facesContext overrides externalContext
+ * internally.
+ *
+ * @throws Exception
+ */
+ protected void setUpFacesContext() throws Exception
+ {
+ facesContextFactory = (MockFacesContextFactory) FactoryFinder
+ .getFactory(FactoryFinder.FACES_CONTEXT_FACTORY);
+ facesContext = (MockFacesContext) facesContextFactory.getFacesContext(
+ servletContext, request, response, lifecycle);
+ if (facesContext.getExternalContext() != null)
+ {
+ externalContext = (MockExternalContext) facesContext
+ .getExternalContext();
+ }
+ if (facesContext instanceof MockFacesContext)
+ {
+ ((MockFacesContext) facesContext).setApplication(application);
+ }
+ }
+
+ /**
+ * By default, create an instance of UIViewRoot, set its viewId as "/viewId"
+ * and assign it to the current facesContext.
+ *
+ * @throws Exception
+ */
+ protected void setUpView() throws Exception
+ {
+ UIViewRoot root = new UIViewRoot();
+ root.setViewId("/viewId");
+ root.setRenderKitId(RenderKitFactory.HTML_BASIC_RENDER_KIT);
+ facesContext.setViewRoot(root);
+ }
+
+ /**
+ * Setup the <code>application</code> variable and before
+ * the end by default it is assigned to the <code>facesContext</code>
+ * variable, calling <code>facesContext.setApplication(application)</code>
+ *
+ * @throws Exception
+ */
+ protected void setUpApplication() throws Exception
+ {
+ ApplicationFactory applicationFactory = (ApplicationFactory) FactoryFinder
+ .getFactory(FactoryFinder.APPLICATION_FACTORY);
+ application = applicationFactory.getApplication();
+ }
+
+ /**
+ * Setup the <code>renderKit</code> variable. This is a good place to use
+ * <code>ConfigParser</code> to register converters, validators, components
+ * or renderkits.
+ *
+ * @throws Exception
+ */
+ protected void setUpRenderKit() throws Exception
+ {
+ RenderKitFactory renderKitFactory = (RenderKitFactory) FactoryFinder
+ .getFactory(FactoryFinder.RENDER_KIT_FACTORY);
+ renderKit = new MockRenderKit();
+ renderKitFactory.addRenderKit(RenderKitFactory.HTML_BASIC_RENDER_KIT,
+ renderKit);
+ }
+
+ /**
+ * <p>Tear down instance variables required by this test case.</p>
+ */
+ @After
+ public void tearDown() throws Exception
+ {
+
+ application = null;
+ config = null;
+ externalContext = null;
+ if (facesContext != null)
+ {
+ facesContext.release();
+ }
+ facesContext = null;
+ lifecycle = null;
+ lifecycleFactory = null;
+ renderKit = null;
+ request = null;
+ response = null;
+ servletContext = null;
+ session = null;
+ FactoryFinder.releaseFactories();
+ ResourceBundleVarNames.resetNames();
+
+ tearDownClassloader();
+ }
+
+ protected void tearDownClassloader() throws Exception
+ {
+ if (classLoaderSet)
+ {
+ Thread.currentThread().setContextClassLoader(threadContextClassLoader);
+ threadContextClassLoader = null;
+ classLoaderSet = false;
+ }
+ }
+
+
+ /**
+ * This method ends the current request.
+ */
+ public void tearDownRequest()
+ {
+ externalContext = null;
+ if (facesContext != null)
+ {
+ facesContext.release();
+ }
+ facesContext = null;
+ request = null;
+ response = null;
+ }
+
+ // ------------------------------------------------------ Instance Variables
+
+ // Mock object instances for our tests
+ protected Application application = null;
+ protected MockServletConfig config = null;
+ protected ExternalContext externalContext = null;
+ protected FacesContext facesContext = null;
+ protected FacesContextFactory facesContextFactory = null;
+ protected Lifecycle lifecycle = null;
+ protected LifecycleFactory lifecycleFactory = null;
+ protected RenderKit renderKit = null;
+ protected MockHttpServletRequest request = null;
+ protected MockHttpServletResponse response = null;
+ protected MockServletContext servletContext = null;
+ protected MockHttpSession session = null;
+
+ // Thread context class loader saved and restored after each test
+ private ClassLoader threadContextClassLoader = null;
+ private boolean classLoaderSet = false;
+
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/base/junit4/AbstractJsfTestCase.java b/test30/src/main/java/org/apache/myfaces/test/base/junit4/AbstractJsfTestCase.java
new file mode 100644
index 0000000..78f6694
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/base/junit4/AbstractJsfTestCase.java
@@ -0,0 +1,360 @@
+/*
+ * 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.myfaces.test.base.junit4;
+
+import java.net.URL;
+import java.net.URLClassLoader;
+
+import javax.faces.FactoryFinder;
+import javax.faces.application.ApplicationFactory;
+import javax.faces.component.UIViewRoot;
+import javax.faces.lifecycle.LifecycleFactory;
+import javax.faces.render.RenderKitFactory;
+
+import org.apache.myfaces.test.config.ResourceBundleVarNames;
+import org.apache.myfaces.test.mock.MockApplication;
+import org.apache.myfaces.test.mock.MockExternalContext;
+import org.apache.myfaces.test.mock.MockFacesContext;
+import org.apache.myfaces.test.mock.MockFacesContextFactory;
+import org.apache.myfaces.test.mock.MockHttpServletRequest;
+import org.apache.myfaces.test.mock.MockHttpServletResponse;
+import org.apache.myfaces.test.mock.MockHttpSession;
+import org.apache.myfaces.test.mock.MockRenderKit;
+import org.apache.myfaces.test.mock.MockServletConfig;
+import org.apache.myfaces.test.mock.MockServletContext;
+import org.apache.myfaces.test.mock.lifecycle.MockLifecycle;
+import org.apache.myfaces.test.mock.lifecycle.MockLifecycleFactory;
+import org.junit.After;
+import org.junit.Before;
+
+/**
+ * <p>Abstract JUnit 4.5 test case base class, which sets up the JavaServer Faces
+ * mock object environment for a particular simulated request. The following
+ * protected variables are initialized in the <code>setUp()</code> method, and
+ * cleaned up in the <code>tearDown()</code> method:</p>
+ * <ul>
+ * <li><code>application</code> (<code>MockApplication</code>)</li>
+ * <li><code>config</code> (<code>MockServletConfig</code>)</li>
+ * <li><code>externalContext</code> (<code>MockExternalContext</code>)</li>
+ * <li><code>facesContext</code> (<code>MockFacesContext</code>)</li>
+ * <li><code>lifecycle</code> (<code>MockLifecycle</code>)</li>
+ * <li><code>request</code> (<code>MockHttpServletRequest</code></li>
+ * <li><code>response</code> (<code>MockHttpServletResponse</code>)</li>
+ * <li><code>servletContext</code> (<code>MockServletContext</code>)</li>
+ * <li><code>session</code> (<code>MockHttpSession</code>)</li>
+ * </ul>
+ *
+ * <p>In addition, appropriate factory classes will have been registered with
+ * <code>javax.faces.FactoryFinder</code> for <code>Application</code> and
+ * <code>RenderKit</code> instances. The created <code>FacesContext</code>
+ * instance will also have been registered in the apppriate thread local
+ * variable, to simulate what a servlet container would do.</p>
+ *
+ * <p><strong>WARNING</strong> - If you choose to subclass this class, be sure
+ * your <code>setUp()</code> and <code>tearDown()</code> methods call
+ * <code>super.setUp()</code> and <code>super.tearDown()</code> respectively,
+ * and that you implement your own <code>suite()</code> method that exposes
+ * the test methods for your test case.</p>
+ *
+ * @since 1.0.0
+ */
+
+public abstract class AbstractJsfTestCase
+{
+
+ // ------------------------------------------------------------ Constructors
+
+ /**
+ * <p>Construct a new instance of this test case.</p>
+ *
+ * @param name Name of this test case
+ */
+ public AbstractJsfTestCase()
+ {
+ }
+
+ // ---------------------------------------------------- Overall Test Methods
+
+ /**
+ * <p>Set up instance variables required by this test case.</p>
+ */
+ @Before
+ public void setUp() throws Exception
+ {
+
+ // Set up a new thread context class loader
+ setUpClassloader();
+
+ // Set up Servlet API Objects
+ setUpServletObjects();
+
+ // Set up JSF API Objects
+ FactoryFinder.releaseFactories();
+
+ setFactories();
+
+ setUpJSFObjects();
+ }
+
+ /**
+ * Set up the thread context classloader. JSF uses the this classloader
+ * in order to find related factory classes and other resources, but in
+ * some selected cases, the default classloader cannot be properly set.
+ *
+ * @throws Exception
+ */
+ protected void setUpClassloader() throws Exception
+ {
+ threadContextClassLoader = Thread.currentThread()
+ .getContextClassLoader();
+ Thread.currentThread()
+ .setContextClassLoader(
+ new URLClassLoader(new URL[0], this.getClass()
+ .getClassLoader()));
+ classLoaderSet = true;
+ }
+
+ /**
+ * <p>Setup JSF object used for the test. By default it calls to the following
+ * methods in this order:</p>
+ *
+ * <ul>
+ * <li><code>setUpExternalContext();</code></li>
+ * <li><code>setUpLifecycle();</code></li>
+ * <li><code>setUpFacesContext();</code></li>
+ * <li><code>setUpView();</code></li>
+ * <li><code>setUpApplication();</code></li>
+ * <li><code>setUpRenderKit();</code></li>
+ * </ul>
+ *
+ * @throws Exception
+ */
+ protected void setUpJSFObjects() throws Exception
+ {
+ setUpExternalContext();
+ setUpLifecycle();
+ setUpFacesContext();
+ setUpView();
+ setUpApplication();
+ setUpRenderKit();
+ }
+
+ /**
+ * <p>Setup servlet objects that will be used for the test:</p>
+ *
+ * <ul>
+ * <li><code>config</code> (<code>MockServletConfig</code>)</li>
+ * <li><code>servletContext</code> (<code>MockServletContext</code>)</li>
+ * <li><code>request</code> (<code>MockHttpServletRequest</code></li>
+ * <li><code>response</code> (<code>MockHttpServletResponse</code>)</li>
+ * <li><code>session</code> (<code>MockHttpSession</code>)</li>
+ * </ul>
+ *
+ * @throws Exception
+ */
+ protected void setUpServletObjects() throws Exception
+ {
+ servletContext = new MockServletContext();
+ config = new MockServletConfig(servletContext);
+ session = new MockHttpSession();
+ session.setServletContext(servletContext);
+ request = new MockHttpServletRequest(session);
+ request.setServletContext(servletContext);
+ response = new MockHttpServletResponse();
+ }
+
+ /**
+ * <p>Set JSF factories using FactoryFinder method setFactory.</p>
+ *
+ * @throws Exception
+ */
+ protected void setFactories() throws Exception
+ {
+ FactoryFinder.setFactory(FactoryFinder.APPLICATION_FACTORY,
+ "org.apache.myfaces.test.mock.MockApplicationFactory");
+ FactoryFinder.setFactory(FactoryFinder.FACES_CONTEXT_FACTORY,
+ "org.apache.myfaces.test.mock.MockFacesContextFactory");
+ FactoryFinder.setFactory(FactoryFinder.LIFECYCLE_FACTORY,
+ "org.apache.myfaces.test.mock.lifecycle.MockLifecycleFactory");
+ FactoryFinder.setFactory(FactoryFinder.RENDER_KIT_FACTORY,
+ "org.apache.myfaces.test.mock.MockRenderKitFactory");
+ FactoryFinder.setFactory(FactoryFinder.EXCEPTION_HANDLER_FACTORY,
+ "org.apache.myfaces.test.mock.MockExceptionHandlerFactory");
+ FactoryFinder.setFactory(FactoryFinder.PARTIAL_VIEW_CONTEXT_FACTORY,
+ "org.apache.myfaces.test.mock.MockPartialViewContextFactory");
+ FactoryFinder.setFactory(FactoryFinder.VISIT_CONTEXT_FACTORY,
+ "org.apache.myfaces.test.mock.visit.MockVisitContextFactory");
+ FactoryFinder.setFactory(FactoryFinder.CLIENT_WINDOW_FACTORY,
+ "org.apache.myfaces.test.mock.MockClientWindowFactory");
+ }
+
+ /**
+ * Setup the <code>externalContext</code> variable, using the
+ * servlet variables already initialized.
+ *
+ * @throws Exception
+ */
+ protected void setUpExternalContext() throws Exception
+ {
+ externalContext = new MockExternalContext(servletContext, request,
+ response);
+ }
+
+ /**
+ * Setup the <code>lifecycle</code> and <code>lifecycleFactory</code>
+ * variables.
+ *
+ * @throws Exception
+ */
+ protected void setUpLifecycle() throws Exception
+ {
+ lifecycleFactory = (MockLifecycleFactory) FactoryFinder
+ .getFactory(FactoryFinder.LIFECYCLE_FACTORY);
+ lifecycle = (MockLifecycle) lifecycleFactory
+ .getLifecycle(LifecycleFactory.DEFAULT_LIFECYCLE);
+ }
+
+ /**
+ * Setup the <code>facesContextFactory</code> and <code>facesContext</code>
+ * variable. Before end, by default it override <code>externalContext</code>
+ * variable from the value retrieved from facesContext.getExternalContext(),
+ * because sometimes it is possible facesContext overrides externalContext
+ * internally.
+ *
+ * @throws Exception
+ */
+ protected void setUpFacesContext() throws Exception
+ {
+ facesContextFactory = (MockFacesContextFactory) FactoryFinder
+ .getFactory(FactoryFinder.FACES_CONTEXT_FACTORY);
+ facesContext = (MockFacesContext) facesContextFactory.getFacesContext(
+ servletContext, request, response, lifecycle);
+ if (facesContext.getExternalContext() != null)
+ {
+ externalContext = (MockExternalContext) facesContext
+ .getExternalContext();
+ }
+ }
+
+ /**
+ * By default, create an instance of UIViewRoot, set its viewId as "/viewId"
+ * and assign it to the current facesContext.
+ *
+ * @throws Exception
+ */
+ protected void setUpView() throws Exception
+ {
+ UIViewRoot root = new UIViewRoot();
+ root.setViewId("/viewId");
+ root.setRenderKitId(RenderKitFactory.HTML_BASIC_RENDER_KIT);
+ facesContext.setViewRoot(root);
+ }
+
+ /**
+ * Setup the <code>application</code> variable and before
+ * the end by default it is assigned to the <code>facesContext</code>
+ * variable, calling <code>facesContext.setApplication(application)</code>
+ *
+ * @throws Exception
+ */
+ protected void setUpApplication() throws Exception
+ {
+ ApplicationFactory applicationFactory = (ApplicationFactory) FactoryFinder
+ .getFactory(FactoryFinder.APPLICATION_FACTORY);
+ application = (MockApplication) applicationFactory.getApplication();
+ facesContext.setApplication(application);
+ }
+
+ /**
+ * Setup the <code>renderKit</code> variable. This is a good place to use
+ * <code>ConfigParser</code> to register converters, validators, components
+ * or renderkits.
+ *
+ * @throws Exception
+ */
+ protected void setUpRenderKit() throws Exception
+ {
+ RenderKitFactory renderKitFactory = (RenderKitFactory) FactoryFinder
+ .getFactory(FactoryFinder.RENDER_KIT_FACTORY);
+ renderKit = new MockRenderKit();
+ renderKitFactory.addRenderKit(RenderKitFactory.HTML_BASIC_RENDER_KIT,
+ renderKit);
+ }
+
+ /**
+ * <p>Tear down instance variables required by this test case.</p>
+ */
+ @After
+ public void tearDown() throws Exception
+ {
+
+ application = null;
+ config = null;
+ externalContext = null;
+ if (facesContext != null)
+ {
+ facesContext.release();
+ }
+ facesContext = null;
+ lifecycle = null;
+ lifecycleFactory = null;
+ renderKit = null;
+ request = null;
+ response = null;
+ servletContext = null;
+ session = null;
+ FactoryFinder.releaseFactories();
+ ResourceBundleVarNames.resetNames();
+
+ tearDownClassloader();
+
+ }
+
+ protected void tearDownClassloader() throws Exception
+ {
+ if (classLoaderSet)
+ {
+ Thread.currentThread().setContextClassLoader(threadContextClassLoader);
+ threadContextClassLoader = null;
+ classLoaderSet = false;
+ }
+ }
+
+ // ------------------------------------------------------ Instance Variables
+
+ // Mock object instances for our tests
+ protected MockApplication application = null;
+ protected MockServletConfig config = null;
+ protected MockExternalContext externalContext = null;
+ protected MockFacesContext facesContext = null;
+ protected MockFacesContextFactory facesContextFactory = null;
+ protected MockLifecycle lifecycle = null;
+ protected MockLifecycleFactory lifecycleFactory = null;
+ protected MockRenderKit renderKit = null;
+ protected MockHttpServletRequest request = null;
+ protected MockHttpServletResponse response = null;
+ protected MockServletContext servletContext = null;
+ protected MockHttpSession session = null;
+
+ // Thread context class loader saved and restored after each test
+ private ClassLoader threadContextClassLoader = null;
+ private boolean classLoaderSet = false;
+
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/base/junit4/AbstractViewControllerTestCase.java b/test30/src/main/java/org/apache/myfaces/test/base/junit4/AbstractViewControllerTestCase.java
new file mode 100644
index 0000000..2f2e7c2
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/base/junit4/AbstractViewControllerTestCase.java
@@ -0,0 +1,103 @@
+/*
+ * 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.myfaces.test.base.junit4;
+
+import java.util.Iterator;
+
+import org.junit.Assert;
+
+/**
+ * <p>Abstract base class for testing <code>ViewController</code>
+ * implementations.</p>
+ *
+ * <p><strong>WARNING</strong> - If you choose to subclass this class, be sure
+ * your <code>setUp()</code> and <code>tearDown()</code> methods call
+ * <code>super.setUp()</code> and <code>super.tearDown()</code> respectively,
+ * and that you implement your own <code>suite()</code> method that exposes
+ * the test methods for your test case.</p>
+ *
+ * @since 1.0.0
+ */
+public abstract class AbstractViewControllerTestCase extends
+ AbstractJsfTestCase
+{
+
+ // ------------------------------------------------------------ Constructors
+
+ /**
+ * <p>Construct a new instance of this test case.</p>
+ *
+ * @param name Test case name
+ */
+ public AbstractViewControllerTestCase()
+ {
+ super();
+ }
+
+ // ---------------------------------------------------- Overall Test Methods
+
+ // ------------------------------------------------------ Instance Variables
+
+ // ------------------------------------------------------- Protected Methods
+
+ /**
+ * <p>Test that the specified number of messages have been queued on the
+ * <code>FacesContext</code> instance, without regard to matching a
+ * particular client identifier.</p>
+ *
+ * @param expected The expected number of messages
+ */
+ protected void checkMessageCount(int expected)
+ {
+
+ int actual = 0;
+ Iterator messages = facesContext.getMessages();
+ while (messages.hasNext())
+ {
+ messages.next();
+ actual++;
+ }
+ Assert.assertEquals("Complete message count", expected, actual);
+
+ }
+
+ /**
+ * <p>Test that the specified number of messages have been queued on the
+ * <code>FacesContext</code> instance, for the specified client id.</p>
+ *
+ * @param clientId Client identifier of the component for which to
+ * count queued messages
+ * @param expected The expected number of messages
+ */
+ protected void checkMessageCount(String clientId, int expected)
+ {
+
+ int actual = 0;
+ Iterator messages = facesContext.getMessages(clientId);
+ while (messages.hasNext())
+ {
+ messages.next();
+ actual++;
+ }
+ Assert.assertEquals("Complete message count", expected, actual);
+
+ }
+
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/cargo/CargoTestSetup.java b/test30/src/main/java/org/apache/myfaces/test/cargo/CargoTestSetup.java
new file mode 100644
index 0000000..103fe4f
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/cargo/CargoTestSetup.java
@@ -0,0 +1,179 @@
+/*
+ * 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.myfaces.test.cargo;
+
+import java.io.File;
+
+import junit.extensions.TestSetup;
+import junit.framework.Test;
+
+import org.codehaus.cargo.container.InstalledLocalContainer;
+import org.codehaus.cargo.container.ContainerType;
+import org.codehaus.cargo.container.tomcat.Tomcat5xInstalledLocalContainer;
+import org.codehaus.cargo.container.deployable.Deployable;
+import org.codehaus.cargo.container.deployable.DeployableType;
+import org.codehaus.cargo.container.configuration.LocalConfiguration;
+import org.codehaus.cargo.container.configuration.ConfigurationType;
+import org.codehaus.cargo.generic.deployable.DefaultDeployableFactory;
+import org.codehaus.cargo.generic.configuration.ConfigurationFactory;
+import org.codehaus.cargo.generic.configuration.DefaultConfigurationFactory;
+import org.codehaus.cargo.generic.DefaultContainerFactory;
+import org.codehaus.cargo.util.log.FileLogger;
+
+/**
+ * <p>Convenience <code>TestSetup</code> class which uses Cargo to start
+ * and stop a Servlet container.</p>
+ *
+ * @since 1.0.0
+ */
+public class CargoTestSetup extends TestSetup
+{
+
+ // ------------------------------------------------------------ Constructors
+
+ /**
+ * <p>Construct a new instance of this test setup.</p>
+ *
+ * @param test Tests to be run within this test setup.
+ */
+ public CargoTestSetup(Test test)
+ {
+ super(test);
+ }
+
+ // ------------------------------------------------------ Instance Variables
+
+ /**
+ * <p>The installed local container for this test setup.</p>
+ */
+ private InstalledLocalContainer container;
+
+ // ------------------------------------------------------ Test Setup Methods
+
+ /**
+ * <p>Start the container prior to running the tests.</p>
+ * <p>The following System properties are used:
+ * <ul>
+ * <li>cargo.container.id - ID of the container to use. [tomcat5x]</li>
+ * <li>cargo.container.home - Full path to a local installation of the container.
+ * If not set, uses the value of the TOMCAT_HOME environment variable.
+ * One of cargo.container.home or TOMCAT_HOME is REQUIRED.</li>
+ * <li>cargo.deployable - Full path to the war file to deploy. REQUIRED.</li>
+ * <li>cargo.container.output - Full path to a file to use for output. [none]</li>
+ * <li>cargo.container.log - Full path to a file to use for logging. [none]</li>
+ * <li>cargo.servlet.port - The port on which the container should listen. [8080]</li>
+ * </ul>
+ * </p>
+ *
+ * @throws Exception if an error occurs.
+ */
+ protected void setUp() throws Exception
+ {
+
+ super.setUp();
+
+ // If there is no container id, default to Tomcat 5x
+ String containerId = System.getProperty("cargo.container.id");
+ if (containerId == null)
+ {
+ containerId = Tomcat5xInstalledLocalContainer.ID;
+ }
+ System.out.println("[INFO] container id: " + containerId);
+
+ // Construct the war, using the container id and the path to the war file
+ String deployablePath = System.getProperty("cargo.deployable");
+ System.out.println("[INFO] deployable: " + deployablePath);
+ Deployable war = new DefaultDeployableFactory().createDeployable(
+ containerId, deployablePath, DeployableType.WAR);
+
+ // Container configuration
+ ConfigurationFactory configurationFactory = new DefaultConfigurationFactory();
+
+ LocalConfiguration configuration = (LocalConfiguration) configurationFactory
+ .createConfiguration(containerId, ConfigurationType.STANDALONE);
+
+ // Find and (if provided) set the port to use for the container.
+ String servletPort = System.getProperty("cargo.servlet.port");
+ if (servletPort != null)
+ {
+ configuration.setProperty("cargo.servlet.port", servletPort);
+ System.out.println("[INFO] servlet port: " + servletPort);
+ }
+
+ configuration.addDeployable(war);
+
+ container = (InstalledLocalContainer) new DefaultContainerFactory()
+ .createContainer(containerId, ContainerType.INSTALLED,
+ configuration);
+
+ // If 'cargo.container.home' is not set, or if an expression was
+ // passed through, try to use the TOMCAT_HOME environment variable.
+ String containerHome = System.getProperty("cargo.container.home");
+ if (containerHome == null || containerHome.startsWith("$"))
+ {
+ containerHome = System.getenv("TOMCAT_HOME");
+ }
+ System.out.println("[INFO] container home: " + containerHome);
+ container.setHome(new File(containerHome));
+
+ // Find and (if provided) set the path to a log file
+ String containerLog = System.getProperty("cargo.container.log");
+ if (containerLog != null)
+ {
+ System.out.println("[INFO] container log: " + containerLog);
+ container.setLogger(new FileLogger(containerLog, false));
+ }
+
+ // Find and (if provided) set the path to an output file
+ String containerOutput = System.getProperty("cargo.container.output");
+ if (containerOutput != null)
+ {
+ System.out.println("[INFO] container output: " + containerOutput);
+ container.setOutput(new File(containerOutput));
+ }
+
+ container.start();
+ }
+
+ /**
+ * Stop the container after running the tests.
+ *
+ * @throws Exception if an error occurs.
+ */
+ protected void tearDown() throws Exception
+ {
+ container.stop();
+ super.tearDown();
+ }
+
+ /**
+ * Return the name of the test setup.
+ * (Temporarily required due to MSUREFIRE-119.)
+ *
+ * @return the name of the test setup.
+ * @deprecated No replacement.
+ */
+
+ public String getName()
+ {
+ return "CargoTestSetup";
+ }
+
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/config/ConfigParser.java b/test30/src/main/java/org/apache/myfaces/test/config/ConfigParser.java
new file mode 100644
index 0000000..b0ce0d6
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/config/ConfigParser.java
@@ -0,0 +1,709 @@
+/*
+ * 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.myfaces.test.config;
+
+import java.io.IOException;
+import java.net.URL;
+
+import javax.faces.FactoryFinder;
+import javax.faces.application.Application;
+import javax.faces.application.ApplicationFactory;
+import javax.faces.render.ClientBehaviorRenderer;
+import javax.faces.render.RenderKit;
+import javax.faces.render.RenderKitFactory;
+import javax.faces.render.Renderer;
+
+import org.apache.commons.digester.Digester;
+import org.apache.commons.digester.Rule;
+import org.apache.myfaces.test.mock.MockRenderKit;
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+
+/**
+ * <p>Utility class to parse JavaServer Faces configuration resources, and
+ * register JSF artifacts with the mock object hierarchy.</p>
+ *
+ * <p>The following artifacts are registered:</p>
+ * <ul>
+ * <li><code>Converter</code> (by-id and by-class)</li>
+ * <li><code>RenderKit</code> and <code>Renderer</code>,<code>ClientBehaviorRenderer</code></li>
+ * <li><code>UIComponent</code></li>
+ * <li><code>Validator</code></li>
+ * <li><code>Behavior</code></li>
+ * </ul>
+ *
+ * <p>Note that any declared <em>factory</em> instances are explicitly
+ * <strong>NOT</strong> registered, allowing the mock object hierarchy
+ * of the Myfaces Test Framework to manage these APIs.</p>
+ *
+ * <p><strong>USAGE NOTE</strong> - If you are using an instance of this
+ * class within a subclass of <code>AbstractJsfTestCase</code> or
+ * <code>AbstractJmockJsfTestCase</code>, be sure you have completed the
+ * <code>setUp()</code> processing in this base class before calling one
+ * of the <code>parse()</code> methods.</p>
+ *
+ * @since 1.0.0
+ */
+public class ConfigParser
+{
+
+ // ------------------------------------------------------------ Constructors
+
+ /** Creates a new instance of ConfigParser */
+ public ConfigParser()
+ {
+ }
+
+ // ------------------------------------------------------ Manifest Constants
+
+ /**
+ * <p>Configuration resource URLs for the JSF RI.</p>
+ */
+ private static final String[] JSFRI_RESOURCES = { "/com/sun/faces/jsf-ri-runtime.xml", };
+
+ /**
+ * <p>Configuration resource URLs for Apache MyFaces.</p>
+ */
+ private static final String[] MYFACES_RESOURCES = { "/org/apache/myfaces/resource/standard-faces-config.xml", };
+
+ /**
+ * <p>Configuration resource URLs for Apache MyFaces 1.2.</p>
+ */
+ private static final String[] MYFACES_RESOURCES12 = { "/META-INF/standard-faces-config.xml", };
+
+ // ------------------------------------------------------ Instance Variables
+
+ /**
+ * <p>The <code>Digester</code> instance we will use for parsing.</p>
+ */
+ private Digester digester = null;
+
+ // ------------------------------------------------------- Public Properties
+
+ /**
+ * <p>Return the URLs of the platform configuration resources for this
+ * application. The following platforms are currently supported:</p>
+ * <ul>
+ * <li>JavaServer Faces Reference Implementation (version 1.0 - 1.2)</li>
+ * <li>MyFaces (version 1.1)</li>
+ * </ul>
+ *
+ * <p>If MyFaces (version 1.2), currently under development, does not change
+ * the name of the configuration resource, it will be supported as well.</p>
+ */
+ public URL[] getPlatformURLs()
+ {
+
+ URL[] urls = translate(JSFRI_RESOURCES);
+ if (urls[0] == null)
+ {
+ urls = translate(MYFACES_RESOURCES12);
+ if (urls[0] == null)
+ {
+ urls = translate(MYFACES_RESOURCES);
+ }
+ }
+ return urls;
+
+ }
+
+ // ---------------------------------------------------------- Public Methods
+
+ /**
+ * <p>Parse the specified JavaServer Faces configuration resource, causing
+ * the appropriate JSF artifacts to be registered with the mock object
+ * hierarchy.</p>
+ *
+ * @param url <code>URL</code> of the configuration resource to parse
+ *
+ * @exception IOException if an input/output error occurs
+ * @exception SAXException if a parsing error occurs
+ */
+ public void parse(URL url) throws IOException, SAXException
+ {
+
+ // Acquire and configure the Digester instance we will use
+ Digester digester = digester();
+ ApplicationFactory factory = (ApplicationFactory) FactoryFinder
+ .getFactory(FactoryFinder.APPLICATION_FACTORY);
+ Application application = factory.getApplication();
+ digester.push(application);
+
+ // Perform the required parsing
+ try
+ {
+ digester.parse(url);
+ }
+ finally
+ {
+ digester.clear();
+ }
+
+ }
+
+ /**
+ * <p>Parse the specified set of JavaServer Faces configuration resources,
+ * in the listed order, causing the appropriate JSF artifacts to be registered
+ * with the mock object hierarchy.</p>
+ *
+ * @param urls <code>URL</code>s of the configuration resources to parse
+ *
+ * @exception IOException if an input/output error occurs
+ * @exception SAXException if a parsing error occurs
+ */
+ public void parse(URL[] urls) throws IOException, SAXException
+ {
+
+ for (int i = 0; i < urls.length; i++)
+ {
+ parse(urls[i]);
+ }
+
+ }
+
+ // --------------------------------------------------------- Private Methods
+
+ /**
+ * <p>Return the <code>Digester</code> instance we will use for parsing,
+ * creating and configuring a new instance if necessary.</p>
+ */
+ private Digester digester()
+ {
+
+ if (this.digester == null)
+ {
+ this.digester = new Digester();
+ digester.addRule("faces-config/component", new ComponentRule());
+ digester.addCallMethod("faces-config/component/component-type",
+ "setComponentType", 0);
+ digester.addCallMethod("faces-config/component/component-class",
+ "setComponentClass", 0);
+ digester.addRule("faces-config/converter", new ConverterRule());
+ digester.addCallMethod("faces-config/converter/converter-id",
+ "setConverterId", 0);
+ digester.addCallMethod("faces-config/converter/converter-class",
+ "setConverterClass", 0);
+ digester.addCallMethod(
+ "faces-config/converter/converter-for-class",
+ "setConverterForClass", 0);
+ digester.addRule("faces-config/render-kit", new RenderKitRule());
+ digester.addRule("faces-config/render-kit/render-kit-id",
+ new RenderKitIdRule());
+ digester.addRule("faces-config/render-kit/renderer",
+ new RendererRule());
+ digester.addCallMethod(
+ "faces-config/render-kit/renderer/component-family",
+ "setComponentFamily", 0);
+ digester.addCallMethod(
+ "faces-config/render-kit/renderer/renderer-class",
+ "setRendererClass", 0);
+ digester.addCallMethod(
+ "faces-config/render-kit/renderer/renderer-type",
+ "setRendererType", 0);
+ digester.addRule(
+ "faces-config/render-kit/client-behavior-renderer",
+ new ClientBehaviorRendererRule());
+ digester
+ .addCallMethod(
+ "faces-config/render-kit/client-behavior-renderer/client-behavior-renderer-type",
+ "setClientBehaviorRendererType", 0);
+ digester
+ .addCallMethod(
+ "faces-config/render-kit/client-behavior-renderer/client-behavior-renderer-class",
+ "setClientBehaviorRendererClass", 0);
+ digester.addRule("faces-config/validator", new ValidatorRule());
+ digester.addCallMethod("faces-config/validator/validator-id",
+ "setValidatorId", 0);
+ digester.addCallMethod("faces-config/validator/validator-class",
+ "setValidatorClass", 0);
+ digester.addRule("faces-config/behavior", new BehaviorRule());
+ digester.addCallMethod("faces-config/behavior/behavior-id",
+ "setBehaviorId", 0);
+ digester.addCallMethod("faces-config/behavior/behavior-class",
+ "setBehaviorClass", 0);
+ }
+ return this.digester;
+
+ }
+
+ /**
+ * <p>Translate an array of resource names into an array of resource URLs.</p>
+ *
+ * @param names Resource names to translate
+ */
+ private URL[] translate(String[] names)
+ {
+
+ URL[] results = new URL[names.length];
+ for (int i = 0; i < names.length; i++)
+ {
+ results[i] = this.getClass().getResource(names[i]);
+ }
+ return results;
+
+ }
+
+ // --------------------------------------------------------- Private Classes
+
+ /**
+ * <p>Data bean that stores information related to a component.</p>
+ */
+ class ComponentBean
+ {
+
+ private String componentClass;
+
+ public String getComponentClass()
+ {
+ return this.componentClass;
+ }
+
+ public void setComponentClass(String componentClass)
+ {
+ this.componentClass = componentClass;
+ }
+
+ private String componentType;
+
+ public String getComponentType()
+ {
+ return this.componentType;
+ }
+
+ public void setComponentType(String componentType)
+ {
+ this.componentType = componentType;
+ }
+
+ }
+
+ /**
+ * <p>Digester <code>Rule</code> for processing components.</p>
+ */
+ class ComponentRule extends Rule
+ {
+
+ public void begin(String namespace, String name, Attributes attributes)
+ {
+ getDigester().push(new ComponentBean());
+ }
+
+ public void end(String namespace, String name)
+ {
+ ComponentBean bean = (ComponentBean) getDigester().pop();
+ Application application = (Application) getDigester().peek();
+ application.addComponent(bean.getComponentType(), bean
+ .getComponentClass());
+ }
+
+ }
+
+ /**
+ * <p>Data bean that stores information related to a converter.</p>
+ */
+ class ConverterBean
+ {
+
+ private String converterClass;
+
+ public String getConverterClass()
+ {
+ return this.converterClass;
+ }
+
+ public void setConverterClass(String converterClass)
+ {
+ this.converterClass = converterClass;
+ }
+
+ private String converterForClass;
+
+ public String getConverterForClass()
+ {
+ return this.converterForClass;
+ }
+
+ public void setConverterForClass(String converterForClass)
+ {
+ this.converterForClass = converterForClass;
+ }
+
+ private String converterId;
+
+ public String getConverterId()
+ {
+ return this.converterId;
+ }
+
+ public void setConverterId(String converterId)
+ {
+ this.converterId = converterId;
+ }
+
+ }
+
+ /**
+ * <p>Digester <code>Rule</code> for processing converers.</p>
+ */
+ class ConverterRule extends Rule
+ {
+
+ public void begin(String namespace, String name, Attributes attributes)
+ {
+ getDigester().push(new ConverterBean());
+ }
+
+ public void end(String namespace, String name)
+ {
+ ConverterBean bean = (ConverterBean) getDigester().pop();
+ Application application = (Application) getDigester().peek();
+ if (bean.getConverterId() != null)
+ {
+ application.addConverter(bean.getConverterId(), bean
+ .getConverterClass());
+ }
+ else
+ {
+ Class clazz = null;
+ try
+ {
+ clazz = classForName(bean.getConverterForClass());
+ }
+ catch (ClassNotFoundException e)
+ {
+ throw new IllegalArgumentException(
+ "java.lang.ClassNotFoundException: "
+ + bean.getConverterForClass());
+ }
+ application.addConverter(clazz, bean.getConverterClass());
+ }
+ }
+
+ }
+
+ private Class classForName(String type) throws ClassNotFoundException
+ {
+ try
+ {
+ // Try WebApp ClassLoader first
+ return Class.forName(type, false, // do not initialize for faster startup
+ Thread.currentThread().getContextClassLoader());
+ }
+ catch (ClassNotFoundException ignore)
+ {
+ // fallback: Try ClassLoader for ClassUtils (i.e. the myfaces.jar lib)
+ return Class.forName(type, false, // do not initialize for faster startup
+ this.getClass().getClassLoader());
+ }
+ }
+
+ /**
+ * <p>Digester <code>Rule</code> for processing render kits.</p>
+ */
+ class RenderKitRule extends Rule
+ {
+
+ public void begin(String namespace, String name, Attributes attributes)
+ {
+ RenderKitFactory factory = (RenderKitFactory) FactoryFinder
+ .getFactory(FactoryFinder.RENDER_KIT_FACTORY);
+ getDigester().push(
+ factory.getRenderKit(null,
+ RenderKitFactory.HTML_BASIC_RENDER_KIT));
+ }
+
+ public void end(String namespace, String name)
+ {
+ getDigester().pop();
+ }
+
+ }
+
+ /**
+ * <p>Digester <code>Rule</code> for processing render kit identifiers.</p>
+ */
+ class RenderKitIdRule extends Rule
+ {
+
+ public void body(String namespace, String name, String text)
+ {
+ String renderKitId = text.trim();
+ RenderKitFactory factory = (RenderKitFactory) FactoryFinder
+ .getFactory(FactoryFinder.RENDER_KIT_FACTORY);
+ RenderKit renderKit = factory.getRenderKit(null, renderKitId);
+ if (renderKit == null)
+ {
+ renderKit = new MockRenderKit();
+ factory.addRenderKit(renderKitId, renderKit);
+ }
+ getDigester().pop();
+ getDigester().push(renderKit);
+ }
+
+ }
+
+ /**
+ * <p>Data bean that stores information related to a renderer.</p>
+ */
+ class RendererBean
+ {
+
+ private String componentFamily;
+
+ public String getComponentFamily()
+ {
+ return this.componentFamily;
+ }
+
+ public void setComponentFamily(String componentFamily)
+ {
+ this.componentFamily = componentFamily;
+ }
+
+ private String rendererClass;
+
+ public String getRendererClass()
+ {
+ return this.rendererClass;
+ }
+
+ public void setRendererClass(String rendererClass)
+ {
+ this.rendererClass = rendererClass;
+ }
+
+ private String rendererType;
+
+ public String getRendererType()
+ {
+ return this.rendererType;
+ }
+
+ public void setRendererType(String rendererType)
+ {
+ this.rendererType = rendererType;
+ }
+
+ }
+
+ /**
+ * <p>Digester <code>Rule</code> for processing renderers.</p>
+ */
+ class RendererRule extends Rule
+ {
+
+ public void begin(String namespace, String name, Attributes attributes)
+ {
+ getDigester().push(new RendererBean());
+ }
+
+ public void end(String namespace, String name)
+ {
+ RendererBean bean = (RendererBean) getDigester().pop();
+ RenderKit kit = (RenderKit) getDigester().peek();
+ Renderer renderer = null;
+ Class clazz = null;
+ try
+ {
+ clazz = classForName(bean.getRendererClass());
+ renderer = (Renderer) clazz.newInstance();
+ }
+ catch (Exception e)
+ {
+ throw new IllegalArgumentException(
+ "Exception while trying to instantiate"
+ + " renderer class '" + bean.getRendererClass()
+ + "' : " + e.getMessage());
+ }
+ kit.addRenderer(bean.getComponentFamily(), bean.getRendererType(),
+ renderer);
+ }
+
+ }
+
+ /**
+ * <p>Data bean that stores information related to a validator.</p>
+ */
+ class ValidatorBean
+ {
+
+ private String validatorClass;
+
+ public String getValidatorClass()
+ {
+ return this.validatorClass;
+ }
+
+ public void setValidatorClass(String validatorClass)
+ {
+ this.validatorClass = validatorClass;
+ }
+
+ private String validatorId;
+
+ public String getValidatorId()
+ {
+ return this.validatorId;
+ }
+
+ public void setValidatorId(String validatorId)
+ {
+ this.validatorId = validatorId;
+ }
+
+ }
+
+ /**
+ * <p>Digester <code>Rule</code> for processing validators.</p>
+ */
+ class ValidatorRule extends Rule
+ {
+
+ public void begin(String namespace, String name, Attributes attributes)
+ {
+ getDigester().push(new ValidatorBean());
+ }
+
+ public void end(String namespace, String name)
+ {
+ ValidatorBean bean = (ValidatorBean) getDigester().pop();
+ Application application = (Application) getDigester().peek();
+ application.addValidator(bean.getValidatorId(), bean
+ .getValidatorClass());
+ }
+
+ }
+
+ class ClientBehaviorRendererBean
+ {
+ private String clientBehaviorRendererType;
+
+ private String clientBehaviorRendererClass;
+
+ public String getClientBehaviorRendererType()
+ {
+ return clientBehaviorRendererType;
+ }
+
+ public void setClientBehaviorRendererType(
+ String clientBehaviorRendererType)
+ {
+ this.clientBehaviorRendererType = clientBehaviorRendererType;
+ }
+
+ public String getClientBehaviorRendererClass()
+ {
+ return clientBehaviorRendererClass;
+ }
+
+ public void setClientBehaviorRendererClass(
+ String clientBehaviorRendererClass)
+ {
+ this.clientBehaviorRendererClass = clientBehaviorRendererClass;
+ }
+ }
+
+ class ClientBehaviorRendererRule extends Rule
+ {
+ public void begin(String namespace, String name, Attributes attributes)
+ {
+ getDigester().push(new ClientBehaviorRendererBean());
+ }
+
+ public void end(String namespace, String name)
+ {
+ ClientBehaviorRendererBean bean = (ClientBehaviorRendererBean) getDigester()
+ .pop();
+ RenderKit kit = (RenderKit) getDigester().peek();
+ ClientBehaviorRenderer renderer = null;
+ Class clazz = null;
+ try
+ {
+ clazz = classForName(bean.getClientBehaviorRendererClass());
+ renderer = (ClientBehaviorRenderer) clazz.newInstance();
+ }
+ catch (Exception e)
+ {
+ throw new IllegalArgumentException(
+ "Exception while trying to instantiate"
+ + " client behavior renderer class '"
+ + bean.getClientBehaviorRendererClass()
+ + "' : " + e.getMessage());
+ }
+ kit.addClientBehaviorRenderer(bean.getClientBehaviorRendererType(),
+ renderer);
+ }
+ }
+
+ /**
+ * <p>Data bean that stores information related to a behavior.</p>
+ */
+ class BehaviorBean
+ {
+
+ private String behaviorClass;
+
+ public String getBehaviorClass()
+ {
+ return this.behaviorClass;
+ }
+
+ @SuppressWarnings("unused")
+ public void setBehaviorClass(String behaviorClass)
+ {
+ this.behaviorClass = behaviorClass;
+ }
+
+ private String behaviorId;
+
+ public String getBehaviorId()
+ {
+ return this.behaviorId;
+ }
+
+ @SuppressWarnings("unused")
+ public void setBehaviorId(String behaviorId)
+ {
+ this.behaviorId = behaviorId;
+ }
+
+ }
+
+ /**
+ * <p>Digester <code>Rule</code> for processing behaviors.</p>
+ */
+ class BehaviorRule extends Rule
+ {
+
+ public void begin(String namespace, String name, Attributes attributes)
+ {
+ getDigester().push(new BehaviorBean());
+ }
+
+ public void end(String namespace, String name)
+ {
+ BehaviorBean bean = (BehaviorBean) getDigester().pop();
+ Application application = (Application) getDigester().peek();
+ application.addBehavior(bean.getBehaviorId(), bean
+ .getBehaviorClass());
+ }
+
+ }
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/config/ResourceBundleVarNames.java b/test30/src/main/java/org/apache/myfaces/test/config/ResourceBundleVarNames.java
new file mode 100644
index 0000000..2edc340
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/config/ResourceBundleVarNames.java
@@ -0,0 +1,50 @@
+/*
+ * 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.myfaces.test.config;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author Rudy De Busscher
+ */
+public final class ResourceBundleVarNames
+{
+
+ private static Map<String, String> varNames = new HashMap<String, String>();
+
+ private ResourceBundleVarNames()
+ {
+ }
+
+ public static void addVarName(String varName, String fullyQualifiedBaseName)
+ {
+ varNames.put(varName, fullyQualifiedBaseName);
+ }
+
+ public static String getVarName(String varName)
+ {
+ return varNames.get(varName);
+ }
+
+ public static void resetNames()
+ {
+ varNames.clear();
+ }
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/el/AbstractELResolver.java b/test30/src/main/java/org/apache/myfaces/test/el/AbstractELResolver.java
new file mode 100644
index 0000000..6ec43c2
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/el/AbstractELResolver.java
@@ -0,0 +1,78 @@
+/*
+ * 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.myfaces.test.el;
+
+import java.beans.FeatureDescriptor;
+import javax.el.ELResolver;
+
+/**
+ * <p>Convenience base class for EL resolvers.</p>
+ *
+ * @since 1.0.0
+ */
+abstract class AbstractELResolver extends ELResolver
+{
+
+ // ------------------------------------------------------- Protected Methods
+
+ /**
+ * <p>Create and return a <code>FeatureDescriptor</code> configured with
+ * the specified arguments.</p>
+ *
+ * @param name Feature name
+ * @param displayName Display name
+ * @param description Short description
+ * @param expert Flag indicating this feature is for experts
+ * @param hidden Flag indicating this feature should be hidden
+ * @param preferred Flag indicating this feature is the preferred one
+ * among features of the same type
+ * @param type Runtime type of this feature
+ * @param designTime Flag indicating feature is resolvable at design time
+ */
+ protected FeatureDescriptor descriptor(String name, String displayName,
+ String description, boolean expert, boolean hidden,
+ boolean preferred, Object type, boolean designTime)
+ {
+
+ FeatureDescriptor descriptor = new FeatureDescriptor();
+
+ descriptor.setName(name);
+ descriptor.setDisplayName(displayName);
+ descriptor.setShortDescription(description);
+ descriptor.setExpert(expert);
+ descriptor.setHidden(hidden);
+ descriptor.setPreferred(preferred);
+ descriptor.setValue(ELResolver.TYPE, type);
+ if (designTime)
+ {
+ descriptor.setValue(ELResolver.RESOLVABLE_AT_DESIGN_TIME,
+ Boolean.TRUE);
+ }
+ else
+ {
+ descriptor.setValue(ELResolver.RESOLVABLE_AT_DESIGN_TIME,
+ Boolean.FALSE);
+ }
+
+ return descriptor;
+
+ }
+
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/el/ExpressionTokenizer.java b/test30/src/main/java/org/apache/myfaces/test/el/ExpressionTokenizer.java
new file mode 100644
index 0000000..6798cec
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/el/ExpressionTokenizer.java
@@ -0,0 +1,193 @@
+/*
+ * 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.myfaces.test.el;
+
+import java.io.IOException;
+import java.io.StringWriter;
+import java.text.ParsePosition;
+import java.util.ArrayList;
+
+/**
+ * Expression Tokenizer
+ */
+class ExpressionTokenizer
+{
+ private ExpressionTokenizer()
+ {
+ }
+
+ public static String[] tokenize(CharSequence expr)
+ {
+ final ArrayList<String> tokens = new ArrayList<String>();
+ ParsePosition pos = new ParsePosition(0);
+ int len = expr.length();
+ boolean sep = true;
+ while (pos.getIndex() < len)
+ {
+ int here = pos.getIndex();
+ char c = expr.charAt(here);
+ switch (c)
+ {
+ case ' ':
+ next(pos);
+ break;
+ case ']':
+ throw new IllegalStateException(String.format("Position %s: unexpected '%s'", here, c));
+ case '[':
+ tokens.add(parseIndex(expr, next(pos)));
+ break;
+ case '.':
+ if (sep)
+ {
+ throw new IllegalStateException(String.format(
+ "Position %s: expected property, index/key, or end of expression", here));
+ }
+ sep = true;
+ next(pos);
+ // fall through:
+ default:
+ if (!sep)
+ {
+ throw new IllegalStateException(String.format(
+ "Position %s: expected property path separator, index/key, or end of expression", here));
+ }
+ tokens.add(parseProperty(expr, pos));
+ }
+ sep = false;
+ }
+ return tokens.toArray(new String[tokens.size()]);
+ }
+
+ private static ParsePosition next(ParsePosition pos)
+ {
+ pos.setIndex(pos.getIndex() + 1);
+ return pos;
+ }
+
+ private static String parseProperty(CharSequence expr, ParsePosition pos)
+ {
+ int len = expr.length();
+ int start = pos.getIndex();
+ loop: while (pos.getIndex() < len)
+ {
+ switch (expr.charAt(pos.getIndex()))
+ {
+ case '[':
+ case ']':
+ case '.':
+ break loop;
+ default:
+ }
+ next(pos);
+ }
+ if (pos.getIndex() > start)
+ {
+ return expr.subSequence(start, pos.getIndex()).toString();
+ }
+ throw new IllegalStateException(String.format("Position %s: expected property", start));
+ }
+
+ /**
+ * Handles an index/key. If the text contained between [] is surrounded by a
+ * pair of " or ', these will be stripped.
+ *
+ * @param expr Expression string to tokenize
+ * @param pos current position of parser, will be updated by the method.
+ * @return token found on the position
+ */
+ private static String parseIndex(CharSequence expr, ParsePosition pos)
+ {
+ int len = expr.length();
+ int start = pos.getIndex();
+ if (start < len)
+ {
+ char first = expr.charAt(pos.getIndex());
+ if (first == '"' || first == '\'')
+ {
+ String s = parseQuotedString(expr, pos);
+ if (s != null && expr.charAt(pos.getIndex()) == ']')
+ {
+ next(pos);
+ return s;
+ }
+ }
+ // no quoted string; match ] greedily and trim
+ while (pos.getIndex() < len)
+ {
+ int here = pos.getIndex();
+ try
+ {
+ if (expr.charAt(here) == ']')
+ {
+ return expr.subSequence(start, here).toString().trim();
+ }
+ }
+ finally
+ {
+ next(pos);
+ }
+ }
+ }
+ throw new IllegalStateException(String.format("Position %s: unparsable index", start));
+ }
+
+ private static String parseQuotedString(CharSequence expr, ParsePosition pos)
+ {
+ int len = expr.length();
+ int start = pos.getIndex();
+ if (start < len)
+ {
+ char quote = expr.charAt(start);
+ next(pos);
+ StringWriter w = new StringWriter();
+ while (pos.getIndex() < len)
+ {
+ int here = pos.getIndex();
+ char c = expr.charAt(here);
+ boolean esc = false;
+ if (c == '\\' && here + 1 < len && expr.charAt(here + 1) == quote)
+ {
+ esc = true;
+ here = next(pos).getIndex();
+ }
+ try
+ {
+ // look for matching quote
+ if (c == quote && !esc)
+ {
+ return w.toString();
+ }
+ w.write(Character.toChars(Character.codePointAt(expr, here)));
+ }
+ catch (IOException e)
+ {
+ throw new RuntimeException(e);
+ }
+ finally
+ {
+ next(pos);
+ }
+ }
+ // if reached, reset due to no ending quote found
+ pos.setIndex(start);
+ }
+ return null;
+ }
+
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/el/FacesImplicitObjectELResolver.java b/test30/src/main/java/org/apache/myfaces/test/el/FacesImplicitObjectELResolver.java
new file mode 100644
index 0000000..94e7155
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/el/FacesImplicitObjectELResolver.java
@@ -0,0 +1,317 @@
+/*
+ * 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.myfaces.test.el;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import javax.el.ELContext;
+import javax.el.PropertyNotFoundException;
+import javax.el.PropertyNotWritableException;
+import javax.faces.component.UIViewRoot;
+import javax.faces.context.ExternalContext;
+import javax.faces.context.FacesContext;
+
+/**
+ * <p><code>ELResolver</code> implementation that accesses implicit objects
+ * in the current request context. See the JSF 1.2 Specification, section
+ * 5.6.2.1, for requirements implemented by this class.</p>
+ *
+ * @since 1.0.0
+ */
+public class FacesImplicitObjectELResolver extends AbstractELResolver
+{
+
+ /**
+ * <p>The names of all implicit objects recognized by this resolver.</p>
+ */
+ private static final String[] NAMES = { "application", "applicationScope",
+ "cookie", "facesContext", "header", "headerValues", "initParam",
+ "param", "paramValues", "request", "requestScope", "session",
+ "sessionScope", "view" };
+
+ /**
+ * <p>The property types corresponding to the implicit object names.</p>
+ */
+ private static final Class[] TYPES = { Object.class, Map.class, Map.class,
+ FacesContext.class, Map.class, Map.class, Map.class, Map.class,
+ Map.class, Object.class, Map.class, Object.class, Map.class,
+ UIViewRoot.class };
+
+ /**
+ * <p>The settable value types corresponding to the implicit
+ * object names.</p>
+ */
+ private static final Class[] VALUES = { null, Object.class, null, null,
+ null, null, null, null, null, null, Object.class, null,
+ Object.class, null };
+
+ /**
+ * <p>Return the most general type this resolver accepts for the
+ * <code>property</code> argument.</p>
+ */
+ public Class getCommonPropertyType(ELContext context, Object base)
+ {
+
+ if (base != null)
+ {
+ return null;
+ }
+ else
+ {
+ return String.class;
+ }
+
+ }
+
+ /**
+ * <p>Return an <code>Iterator</code> over the attributes that this
+ * resolver knows how to deal with.</p>
+ *
+ * @param context <code>ELContext</code> for evaluating this value
+ * @param base Base object against which this evaluation occurs
+ */
+ public Iterator getFeatureDescriptors(ELContext context, Object base)
+ {
+
+ if (base != null)
+ {
+ return null;
+ }
+
+ // Create the variables we will need
+ FacesContext fcontext = (FacesContext) context
+ .getContext(FacesContext.class);
+ List descriptors = new ArrayList();
+
+ // Add feature descriptors for each implicit object
+ for (int i = 0; i < NAMES.length; i++)
+ {
+ descriptors.add(descriptor(NAMES[i], NAMES[i], NAMES[i], false,
+ false, true, TYPES[i], true));
+ }
+
+ // Return the accumulated descriptors
+ return descriptors.iterator();
+
+ }
+
+ /**
+ * <p>Return the Java type of the specified property.</p>
+ *
+ * @param context <code>ELContext</code> for evaluating this value
+ * @param base Base object against which this evaluation occurs
+ * (must be null because we are evaluating a top level variable)
+ * @param property Property name to be accessed
+ */
+ public Class getType(ELContext context, Object base, Object property)
+ {
+
+ if (base != null)
+ {
+ return null;
+ }
+ if (property == null)
+ {
+ throw new PropertyNotFoundException("No property specified");
+ }
+ String name = property.toString();
+ for (int i = 0; i < NAMES.length; i++)
+ {
+ if (name.equals(NAMES[i]))
+ {
+ context.setPropertyResolved(true);
+ return VALUES[i];
+ }
+ }
+ return null;
+
+ }
+
+ /**
+ * <p>Return an existing scoped object for the specified name (if any);
+ * otherwise, return <code>null</code>.</p>
+ *
+ * @param context <code>ELContext</code> for evaluating this value
+ * @param base Base object against which this evaluation occurs
+ * (must be null because we are evaluating a top level variable)
+ * @param property Property name to be accessed
+ */
+ public Object getValue(ELContext context, Object base, Object property)
+ {
+
+ if (base != null)
+ {
+ return null;
+ }
+ if (property == null)
+ {
+ throw new PropertyNotFoundException("No property specified");
+ }
+
+ FacesContext fcontext = (FacesContext) context
+ .getContext(FacesContext.class);
+ ExternalContext econtext = fcontext.getExternalContext();
+ String name = property.toString();
+
+ if (name.equals("application"))
+ {
+ context.setPropertyResolved(true);
+ return econtext.getContext();
+ }
+ else if (name.equals("applicationScope"))
+ {
+ context.setPropertyResolved(true);
+ return econtext.getApplicationMap();
+ }
+ else if (name.equals("cookie"))
+ {
+ context.setPropertyResolved(true);
+ return econtext.getRequestCookieMap();
+ }
+ else if (name.equals("facesContext"))
+ {
+ context.setPropertyResolved(true);
+ return fcontext;
+ }
+ else if (name.equals("header"))
+ {
+ context.setPropertyResolved(true);
+ return econtext.getRequestHeaderMap();
+ }
+ else if (name.equals("headerValues"))
+ {
+ context.setPropertyResolved(true);
+ return econtext.getRequestHeaderValuesMap();
+ }
+ else if (name.equals("initParam"))
+ {
+ context.setPropertyResolved(true);
+ return econtext.getInitParameterMap();
+ }
+ else if (name.equals("param"))
+ {
+ context.setPropertyResolved(true);
+ return econtext.getRequestParameterMap();
+ }
+ else if (name.equals("paramValues"))
+ {
+ context.setPropertyResolved(true);
+ return econtext.getRequestParameterValuesMap();
+ }
+ else if (name.equals("request"))
+ {
+ context.setPropertyResolved(true);
+ return econtext.getRequest();
+ }
+ else if (name.equals("requestScope"))
+ {
+ context.setPropertyResolved(true);
+ return econtext.getRequestMap();
+ }
+ else if (name.equals("session"))
+ {
+ context.setPropertyResolved(true);
+ return econtext.getSession(true);
+ }
+ else if (name.equals("sessionScope"))
+ {
+ context.setPropertyResolved(true);
+ return econtext.getSessionMap();
+ }
+ else if (name.equals("view"))
+ {
+ context.setPropertyResolved(true);
+ return fcontext.getViewRoot();
+ }
+
+ return null;
+
+ }
+
+ /**
+ * <p>Return <code>true</code> if the specified property is read only.</p>
+ *
+ * @param context <code>ELContext</code> for evaluating this value
+ * @param base Base object against which this evaluation occurs
+ * (must be null because we are evaluating a top level variable)
+ * @param property Property name to be accessed
+ */
+ public boolean isReadOnly(ELContext context, Object base, Object property)
+ {
+
+ if (base != null)
+ {
+ return false;
+ }
+ if (property == null)
+ {
+ throw new PropertyNotFoundException("No property specified");
+ }
+ String name = property.toString();
+ for (int i = 0; i < NAMES.length; i++)
+ {
+ if (name.equals(NAMES[i]))
+ {
+ context.setPropertyResolved(true);
+ return true;
+ }
+ }
+ return false;
+
+ }
+
+ /**
+ * <p>Set the value of a scoped object for the specified name.</p>
+ *
+ * @param context <code>ELContext</code> for evaluating this value
+ * @param base Base object against which this evaluation occurs
+ * (must be null because we are evaluating a top level variable)
+ * @param property Property name to be accessed
+ * @param value New value to be set
+ */
+ public void setValue(ELContext context, Object base, Object property,
+ Object value)
+ {
+
+ if (base != null)
+ {
+ return;
+ }
+ if (property == null)
+ {
+ throw new PropertyNotFoundException("No property specified");
+ }
+
+ String name = property.toString();
+ for (int i = 0; i < NAMES.length; i++)
+ {
+ if (name.equals(NAMES[i]))
+ {
+ context.setPropertyResolved(true);
+ throw new PropertyNotWritableException(name);
+ }
+ }
+
+ }
+
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/el/FacesResourceBundleELResolver.java b/test30/src/main/java/org/apache/myfaces/test/el/FacesResourceBundleELResolver.java
new file mode 100644
index 0000000..e7adb25
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/el/FacesResourceBundleELResolver.java
@@ -0,0 +1,236 @@
+/*
+ * 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.myfaces.test.el;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.ResourceBundle;
+import java.util.Map.Entry;
+
+import javax.el.ELContext;
+import javax.el.PropertyNotFoundException;
+import javax.el.PropertyNotWritableException;
+import javax.faces.context.FacesContext;
+
+import org.apache.myfaces.test.mock.MockApplication12;
+
+/**
+ * <p><code>ELResolver</code> implementation that accesses resource bundles
+ * in the current application. See the JSF 1.2 Specification, section
+ * 5.6.1.3, for requirements implemented by this class.</p>
+ *
+ * @since 1.0.0
+ */
+public class FacesResourceBundleELResolver extends AbstractELResolver
+{
+
+ /**
+ * <p>Return the most general type this resolver accepts for the
+ * <code>property</code> argument.</p>
+ */
+ public Class getCommonPropertyType(ELContext context, Object base)
+ {
+
+ if (base != null)
+ {
+ return null;
+ }
+ else
+ {
+ return String.class;
+ }
+
+ }
+
+ /**
+ * <p>Return an <code>Iterator</code> over the attributes that this
+ * resolver knows how to deal with.</p>
+ *
+ * @param context <code>ELContext</code> for evaluating this value
+ * @param base Base object against which this evaluation occurs
+ */
+ public Iterator getFeatureDescriptors(ELContext context, Object base)
+ {
+
+ if (base != null)
+ {
+ return null;
+ }
+
+ // Create the variables we will need
+ List descriptors = new ArrayList();
+ FacesContext fcontext = (FacesContext) context
+ .getContext(FacesContext.class);
+ MockApplication12 application = (MockApplication12) fcontext
+ .getApplication();
+ String key = null;
+ Object value = null;
+
+ // Create a feature descriptor for each configured resource bundle
+ Iterator entries = application.getResourceBundles().entrySet()
+ .iterator();
+ while (entries.hasNext())
+ {
+ Entry entry = (Entry) entries.next();
+ key = (String) entry.getKey();
+ value = entry.getValue();
+ descriptors.add(descriptor(key, key, "Resource Bundle " + key,
+ false, false, true, ResourceBundle.class, true));
+ }
+
+ // Return the accumulated descriptors
+ return descriptors.iterator();
+
+ }
+
+ /**
+ * <p>Return the Java type of the specified property.</p>
+ *
+ * @param context <code>ELContext</code> for evaluating this value
+ * @param base Base object against which this evaluation occurs
+ * (must be null because we are evaluating a top level variable)
+ * @param property Property name to be accessed
+ */
+ public Class getType(ELContext context, Object base, Object property)
+ {
+
+ if (base != null)
+ {
+ return null;
+ }
+ if (property == null)
+ {
+ throw new PropertyNotFoundException("No property specified");
+ }
+ FacesContext fcontext = (FacesContext) context
+ .getContext(FacesContext.class);
+ ResourceBundle bundle = fcontext.getApplication().getResourceBundle(
+ fcontext, property.toString());
+ if (bundle != null)
+ {
+ context.setPropertyResolved(true);
+ return ResourceBundle.class;
+ }
+ return null;
+
+ }
+
+ /**
+ * <p>Return a resource bundle for the specified name (if any);
+ * otherwise, return <code>null</code>.</p>
+ *
+ * @param context <code>ELContext</code> for evaluating this value
+ * @param base Base object against which this evaluation occurs
+ * (must be null because we are evaluating a top level variable)
+ * @param property Property name to be accessed
+ */
+ public Object getValue(ELContext context, Object base, Object property)
+ {
+
+ if (base != null)
+ {
+ return null;
+ }
+ if (property == null)
+ {
+ throw new PropertyNotFoundException("No property specified");
+ }
+
+ FacesContext fcontext = (FacesContext) context
+ .getContext(FacesContext.class);
+ ResourceBundle bundle = fcontext.getApplication().getResourceBundle(
+ fcontext, property.toString());
+ if (bundle != null)
+ {
+ context.setPropertyResolved(true);
+ return bundle;
+ }
+ return null;
+
+ }
+
+ /**
+ * <p>Return <code>true</code> if the specified property is read only.</p>
+ *
+ * @param context <code>ELContext</code> for evaluating this value
+ * @param base Base object against which this evaluation occurs
+ * (must be null because we are evaluating a top level variable)
+ * @param property Property name to be accessed
+ */
+ public boolean isReadOnly(ELContext context, Object base, Object property)
+ {
+
+ if (base != null)
+ {
+ return false;
+ }
+ if (property == null)
+ {
+ throw new PropertyNotFoundException("No property specified");
+ }
+ FacesContext fcontext = (FacesContext) context
+ .getContext(FacesContext.class);
+ ResourceBundle bundle = fcontext.getApplication().getResourceBundle(
+ fcontext, property.toString());
+ if (bundle != null)
+ {
+ context.setPropertyResolved(true);
+ return true;
+ }
+ return false;
+
+ }
+
+ /**
+ * <p>Set the value of a scoped object for the specified name.</p>
+ *
+ * @param context <code>ELContext</code> for evaluating this value
+ * @param base Base object against which this evaluation occurs
+ * (must be null because we are evaluating a top level variable)
+ * @param property Property name to be accessed
+ * @param value New value to be set
+ */
+ public void setValue(ELContext context, Object base, Object property,
+ Object value)
+ {
+
+ if (base != null)
+ {
+ return;
+ }
+ if (property == null)
+ {
+ throw new PropertyNotFoundException("No property specified");
+ }
+
+ FacesContext fcontext = (FacesContext) context
+ .getContext(FacesContext.class);
+ ResourceBundle bundle = fcontext.getApplication().getResourceBundle(
+ fcontext, property.toString());
+ if (bundle != null)
+ {
+ context.setPropertyResolved(true);
+ throw new PropertyNotWritableException(property.toString());
+ }
+
+ }
+
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/el/FacesScopedAttributeELResolver.java b/test30/src/main/java/org/apache/myfaces/test/el/FacesScopedAttributeELResolver.java
new file mode 100644
index 0000000..bd2e6df
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/el/FacesScopedAttributeELResolver.java
@@ -0,0 +1,269 @@
+/*
+ * 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.myfaces.test.el;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.Map.Entry;
+
+import javax.el.ELContext;
+import javax.el.PropertyNotFoundException;
+import javax.faces.context.ExternalContext;
+import javax.faces.context.FacesContext;
+
+/**
+ * <p><code>ELResolver</code> implementation that accesses scoped variables
+ * in the current request context. See the JSF 1.2 Specification, section
+ * 5.6.2.7, for requirements implemented by this class.</p>
+ *
+ * @since 1.0.0
+ */
+public class FacesScopedAttributeELResolver extends AbstractELResolver
+{
+
+ /**
+ * <p>Return the most general type this resolver accepts for the
+ * <code>property</code> argument.</p>
+ */
+ public Class getCommonPropertyType(ELContext context, Object base)
+ {
+
+ if (base != null)
+ {
+ return null;
+ }
+ else
+ {
+ return String.class;
+ }
+
+ }
+
+ /**
+ * <p>Return an <code>Iterator</code> over the attributes that this
+ * resolver knows how to deal with.</p>
+ *
+ * @param context <code>ELContext</code> for evaluating this value
+ * @param base Base object against which this evaluation occurs
+ */
+ public Iterator getFeatureDescriptors(ELContext context, Object base)
+ {
+
+ if (base != null)
+ {
+ return null;
+ }
+
+ // Create the variables we will need
+ FacesContext fcontext = (FacesContext) context
+ .getContext(FacesContext.class);
+ ExternalContext econtext = fcontext.getExternalContext();
+ List descriptors = new ArrayList();
+ Map map = null;
+ Set set = null;
+ Iterator items = null;
+ String key = null;
+ Object value = null;
+
+ // Add feature descriptors for request scoped attributes
+ set = econtext.getRequestMap().entrySet();
+ items = set.iterator();
+ while (items.hasNext())
+ {
+ Entry item = (Entry) items.next();
+ key = (String) item.getKey();
+ value = item.getValue();
+ descriptors.add(descriptor(key, key, "Request Scope Attribute "
+ + key, false, false, true, value.getClass(), true));
+ }
+
+ // Add feature descriptors for session scoped attributes
+ set = econtext.getSessionMap().entrySet();
+ items = set.iterator();
+ while (items.hasNext())
+ {
+ Entry item = (Entry) items.next();
+ key = (String) item.getKey();
+ value = item.getValue();
+ descriptors.add(descriptor(key, key, "Session Scope Attribute "
+ + key, false, false, true, value.getClass(), true));
+ }
+
+ // Add feature descriptors for application scoped attributes
+ set = econtext.getApplicationMap().entrySet();
+ items = set.iterator();
+ while (items.hasNext())
+ {
+ Entry item = (Entry) items.next();
+ key = (String) item.getKey();
+ value = item.getValue();
+ descriptors.add(descriptor(key, key, "Application Scope Attribute "
+ + key, false, false, true, value.getClass(), true));
+ }
+
+ // Return the accumulated descriptors
+ return descriptors.iterator();
+
+ }
+
+ /**
+ * <p>Return the Java type of the specified property.</p>
+ *
+ * @param context <code>ELContext</code> for evaluating this value
+ * @param base Base object against which this evaluation occurs
+ * (must be null because we are evaluating a top level variable)
+ * @param property Property name to be accessed
+ */
+ public Class getType(ELContext context, Object base, Object property)
+ {
+
+ if (base != null)
+ {
+ return null;
+ }
+ if (property == null)
+ {
+ throw new PropertyNotFoundException("No property specified");
+ }
+ context.setPropertyResolved(true);
+ return Object.class;
+
+ }
+
+ /**
+ * <p>Return an existing scoped object for the specified name (if any);
+ * otherwise, return <code>null</code>.</p>
+ *
+ * @param context <code>ELContext</code> for evaluating this value
+ * @param base Base object against which this evaluation occurs
+ * (must be null because we are evaluating a top level variable)
+ * @param property Property name to be accessed
+ */
+ public Object getValue(ELContext context, Object base, Object property)
+ {
+
+ if (base != null)
+ {
+ return null;
+ }
+ if (property == null)
+ {
+ throw new PropertyNotFoundException("No property specified");
+ }
+
+ FacesContext fcontext = (FacesContext) context
+ .getContext(FacesContext.class);
+ ExternalContext econtext = fcontext.getExternalContext();
+ Object value = null;
+ value = econtext.getRequestMap().get(property);
+ if (value != null)
+ {
+ context.setPropertyResolved(true);
+ return value;
+ }
+ value = econtext.getSessionMap().get(property);
+ if (value != null)
+ {
+ context.setPropertyResolved(true);
+ return value;
+ }
+ value = econtext.getApplicationMap().get(property);
+ if (value != null)
+ {
+ context.setPropertyResolved(true);
+ return value;
+ }
+
+ return null;
+
+ }
+
+ /**
+ * <p>Return <code>true</code> if the specified property is read only.</p>
+ *
+ * @param context <code>ELContext</code> for evaluating this value
+ * @param base Base object against which this evaluation occurs
+ * (must be null because we are evaluating a top level variable)
+ * @param property Property name to be accessed
+ */
+ public boolean isReadOnly(ELContext context, Object base, Object property)
+ {
+
+ if (base == null)
+ {
+ context.setPropertyResolved(true);
+ return false;
+ }
+ return false;
+
+ }
+
+ /**
+ * <p>Set the value of a scoped object for the specified name.</p>
+ *
+ * @param context <code>ELContext</code> for evaluating this value
+ * @param base Base object against which this evaluation occurs
+ * (must be null because we are evaluating a top level variable)
+ * @param property Property name to be accessed
+ * @param value New value to be set
+ */
+ public void setValue(ELContext context, Object base, Object property,
+ Object value)
+ {
+
+ if (base != null)
+ {
+ return;
+ }
+ if (property == null)
+ {
+ throw new PropertyNotFoundException("No property specified");
+ }
+
+ context.setPropertyResolved(true);
+ String key = property.toString();
+ Object result = null;
+ FacesContext fcontext = (FacesContext) context
+ .getContext(FacesContext.class);
+ ExternalContext econtext = fcontext.getExternalContext();
+
+ if (econtext.getRequestMap().containsKey(property))
+ {
+ econtext.getRequestMap().put(key, value);
+ }
+ else if (econtext.getSessionMap().containsKey(property))
+ {
+ econtext.getSessionMap().put(key, value);
+ }
+ else if (econtext.getApplicationMap().containsKey(property))
+ {
+ econtext.getApplicationMap().put(key, value);
+ }
+ else
+ {
+ econtext.getRequestMap().put(key, value);
+ }
+
+ }
+
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/el/FacesVariableResolverChainWrapper.java b/test30/src/main/java/org/apache/myfaces/test/el/FacesVariableResolverChainWrapper.java
new file mode 100644
index 0000000..e3b89de
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/el/FacesVariableResolverChainWrapper.java
@@ -0,0 +1,143 @@
+/*
+ * 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.myfaces.test.el;
+
+import java.util.Iterator;
+
+import javax.el.ELContext;
+import javax.el.PropertyNotFoundException;
+import javax.faces.FacesException;
+
+/**
+ * <p><code>ELResolver</code> implementation that wraps the legacy (JSF 1.1)
+ * <code>VariableResolver</code> chain. See the JSF 1.2 Specification, section
+ * 5.6.1.5, for requirements implemented by this class.</p>
+ *
+ * @since 1.0.0
+ */
+public class FacesVariableResolverChainWrapper extends AbstractELResolver
+{
+
+ /**
+ * <p>Return the most general type this resolver accepts for the
+ * <code>property</code> argument.</p>
+ */
+ public Class getCommonPropertyType(ELContext context, Object base)
+ {
+
+ if (base != null)
+ {
+ return null;
+ }
+ else
+ {
+ return String.class;
+ }
+
+ }
+
+ /**
+ * <p>Return an <code>Iterator</code> over the attributes that this
+ * resolver knows how to deal with.</p>
+ *
+ * @param context <code>ELContext</code> for evaluating this value
+ * @param base Base object against which this evaluation occurs
+ */
+ public Iterator getFeatureDescriptors(ELContext context, Object base)
+ {
+
+ return null;
+
+ }
+
+ /**
+ * <p>Return the Java type of the specified property.</p>
+ *
+ * @param context <code>ELContext</code> for evaluating this value
+ * @param base Base object against which this evaluation occurs
+ * (must be null because we are evaluating a top level variable)
+ * @param property Property name to be accessed
+ */
+ public Class getType(ELContext context, Object base, Object property)
+ {
+
+ if ((base == null) && (property == null))
+ {
+ throw new PropertyNotFoundException("No property specified");
+ }
+ return null;
+
+ }
+
+ /**
+ * <p>Evaluate with the legacy variable resolver chain and return
+ * the value.</p>
+ *
+ * @param context <code>ELContext</code> for evaluating this value
+ * @param base Base object against which this evaluation occurs
+ * (must be null because we are evaluating a top level variable)
+ * @param property Property name to be accessed
+ */
+ public Object getValue(ELContext context, Object base, Object property)
+ {
+ throw new FacesException("not supported anymore");
+
+ }
+
+ /**
+ * <p>Return <code>true</code> if the specified property is read only.</p>
+ *
+ * @param context <code>ELContext</code> for evaluating this value
+ * @param base Base object against which this evaluation occurs
+ * (must be null because we are evaluating a top level variable)
+ * @param property Property name to be accessed
+ */
+ public boolean isReadOnly(ELContext context, Object base, Object property)
+ {
+
+ if ((base == null) && (property == null))
+ {
+ throw new PropertyNotFoundException("No property specified");
+ }
+ return false;
+
+ }
+
+ /**
+ * <p>Set the value of a scoped object for the specified name.</p>
+ *
+ * @param context <code>ELContext</code> for evaluating this value
+ * @param base Base object against which this evaluation occurs
+ * (must be null because we are evaluating a top level variable)
+ * @param property Property name to be accessed
+ * @param value New value to be set
+ */
+ public void setValue(ELContext context, Object base, Object property,
+ Object value)
+ {
+
+ if ((base == null) && (property == null))
+ {
+ throw new PropertyNotFoundException("No property specified");
+ }
+
+ }
+
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/el/MockCompositeValueExpression.java b/test30/src/main/java/org/apache/myfaces/test/el/MockCompositeValueExpression.java
new file mode 100644
index 0000000..ce77bab
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/el/MockCompositeValueExpression.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.myfaces.test.el;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.el.ELContext;
+import javax.el.ValueExpression;
+
+/**
+ * A value expression implementation that is capable of handling composite expressions.
+ * It handles composites expressions but creating a list of 'simple' expressions which
+ * are 'pure', only literal text or only references like #{}
+ *
+ * @author Rudy De Busscher
+ * @since 1.0.0
+ */
+public class MockCompositeValueExpression extends MockValueExpression
+{
+
+ private static final long serialVersionUID = 2645070462654392076L;
+
+ private List<ValueExpression> valueExpressionChain;
+
+ public MockCompositeValueExpression(String expression, Class expectedType)
+ {
+
+ super("#{}", expectedType);
+ buildExpressionChain(expression, expectedType);
+
+ }
+
+ private void buildExpressionChain(String expression, Class expectedType)
+ {
+ valueExpressionChain = new ArrayList<ValueExpression>();
+ StringBuilder parser = new StringBuilder(expression);
+ int pos = getStartPositionOfReference(parser);
+ while (pos > -1 || parser.length() > 0)
+ {
+ // We have a constant first
+ if (pos > 0)
+ {
+ valueExpressionChain.add(new MockValueExpression(parser
+ .substring(0, pos), expectedType));
+ parser.delete(0, pos);
+ }
+ // We have an el, maybe literal at the end
+ if (pos == 0)
+ {
+ int posBracket = parser.indexOf("}");
+ valueExpressionChain.add(new MockValueExpression(parser
+ .substring(0, posBracket + 1), expectedType));
+
+ parser.delete(0, posBracket + 1);
+ }
+ // Only literal
+ if (pos == -1)
+ {
+ valueExpressionChain.add(new MockValueExpression(parser
+ .toString(), expectedType));
+
+ parser.setLength(0);
+ }
+ pos = getStartPositionOfReference(parser);
+ }
+ }
+
+ @Override
+ public Class getType(ELContext context)
+ {
+ switch (valueExpressionChain.size())
+ {
+ case 0:
+ return null;
+ case 1:
+ return valueExpressionChain.get(0).getType(context);
+ default:
+ return String.class;
+ }
+ }
+
+ @Override
+ public Object getValue(ELContext context)
+ {
+ if (valueExpressionChain.size() > 1)
+ {
+ // Well only composite strings are supported.
+
+ StringBuilder result = new StringBuilder();
+ for (ValueExpression valueExpression : valueExpressionChain)
+ {
+ result.append(valueExpression.getValue(context));
+ }
+ return result.toString();
+ }
+ else
+ {
+ if (valueExpressionChain.size() == 1)
+ {
+ return valueExpressionChain.get(0).getValue(context);
+ }
+ else
+ {
+ return null;
+ }
+ }
+ }
+
+ @Override
+ public void setValue(ELContext context, Object value)
+ {
+ if (!isReadOnly(context))
+ {
+ valueExpressionChain.get(0).setValue(context, value);
+ }
+ else
+ {
+ throw new IllegalArgumentException(
+ "We can only set value on NON composite expressions like #{foo}");
+ }
+ }
+
+ @Override
+ public String getExpressionString()
+ {
+ StringBuilder result = new StringBuilder();
+ for (ValueExpression valueExpression : valueExpressionChain)
+ {
+ result.append(valueExpression.getExpressionString());
+ }
+ return result.toString();
+ }
+
+ @Override
+ public boolean isReadOnly(ELContext context)
+ {
+ return valueExpressionChain.size() > 1;
+ }
+
+ public static int getStartPositionOfReference(StringBuilder expressionPart)
+ {
+ int result;
+ int pos1 = expressionPart.indexOf("#{");
+ int pos2 = expressionPart.indexOf("${");
+
+ if (pos1 == -1)
+ {
+ result = pos1;
+ }
+ else if (pos2 == -1)
+ {
+ result = pos1;
+ }
+ else
+ {
+ result = Math.min(pos1, pos2);
+ }
+ return result;
+
+ }
+
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/el/MockELContext.java b/test30/src/main/java/org/apache/myfaces/test/el/MockELContext.java
new file mode 100644
index 0000000..2b2130f
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/el/MockELContext.java
@@ -0,0 +1,122 @@
+/*
+ * 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.myfaces.test.el;
+
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+import javax.el.ELContext;
+import javax.el.ELResolver;
+import javax.el.FunctionMapper;
+import javax.el.VariableMapper;
+import javax.faces.context.FacesContext;
+
+/**
+ * <p>Mock implementation of <code>ELContext</code>.</p>
+ *
+ * @since 1.0.0
+ */
+
+public class MockELContext extends ELContext
+{
+
+ // ------------------------------------------------------------ Constructors
+
+ /** Creates a new instance of MockELContext */
+ public MockELContext()
+ {
+ }
+
+ // ------------------------------------------------------ Instance Variables
+
+ private Map contexts = new HashMap();
+ private FunctionMapper functionMapper = new MockFunctionMapper();
+ private Locale locale = Locale.getDefault();
+ private boolean propertyResolved;
+ private VariableMapper variableMapper = new MockVariableMapper();
+
+ // ----------------------------------------------------- Mock Object Methods
+
+ // ------------------------------------------------------- ELContext Methods
+
+ /** {@inheritDoc} */
+ public Object getContext(Class key)
+ {
+ if (key == null)
+ {
+ throw new NullPointerException();
+ }
+ return contexts.get(key);
+ }
+
+ /** {@inheritDoc} */
+ public ELResolver getELResolver()
+ {
+ FacesContext context = FacesContext.getCurrentInstance();
+ return context.getApplication().getELResolver();
+ }
+
+ /** {@inheritDoc} */
+ public FunctionMapper getFunctionMapper()
+ {
+ return this.functionMapper;
+ }
+
+ /** {@inheritDoc} */
+ public Locale getLocale()
+ {
+ return this.locale;
+ }
+
+ /** {@inheritDoc} */
+ public boolean isPropertyResolved()
+ {
+ return this.propertyResolved;
+ }
+
+ /** {@inheritDoc} */
+ public void putContext(Class key, Object value)
+ {
+ if ((key == null) || (value == null))
+ {
+ throw new NullPointerException();
+ }
+ contexts.put(key, value);
+ }
+
+ /** {@inheritDoc} */
+ public void setPropertyResolved(boolean propertyResolved)
+ {
+ this.propertyResolved = propertyResolved;
+ }
+
+ /** {@inheritDoc} */
+ public VariableMapper getVariableMapper()
+ {
+ return this.variableMapper;
+ }
+
+ /** {@inheritDoc} */
+ public void setLocale(Locale locale)
+ {
+ this.locale = locale;
+ }
+
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/el/MockExpressionFactory.java b/test30/src/main/java/org/apache/myfaces/test/el/MockExpressionFactory.java
new file mode 100644
index 0000000..34e5c74
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/el/MockExpressionFactory.java
@@ -0,0 +1,365 @@
+/*
+ * 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.myfaces.test.el;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import javax.el.ELContext;
+import javax.el.ExpressionFactory;
+import javax.el.MethodExpression;
+import javax.el.ValueExpression;
+
+/**
+ * <p>Mock implementation of <code>ExpressionFactory</code>.</p>
+ *
+ * @since 1.0.0
+ */
+public class MockExpressionFactory extends ExpressionFactory
+{
+
+ // ------------------------------------------------------------ Constructors
+
+ /** Creates a new instance of MockExpressionFactory */
+ public MockExpressionFactory()
+ {
+ }
+
+ // ------------------------------------------------------ Instance Variables
+
+ /**
+ * <p>Literal numeric value for zero.</p>
+ */
+ private static final Integer ZERO = new Integer(0);
+
+ // ----------------------------------------------------- Mock Object Methods
+
+ // ----------------------------------------------- ExpressionFactory Methods
+
+ /** {@inheritDoc} */
+ public Object coerceToType(Object object, Class targetType)
+ {
+
+ // Check for no conversion necessary
+ if ((targetType == null) || Object.class.equals(targetType))
+ {
+ return object;
+ }
+
+ // Coerce to String if appropriate
+ if (String.class.equals(targetType))
+ {
+ if (object == null)
+ {
+ return "";
+ }
+ else if (object instanceof String)
+ {
+ return (String) object;
+ }
+ else
+ {
+ return object.toString();
+ }
+ }
+
+ // Coerce to Number (or a subclass of Number) if appropriate
+ if (isNumeric(targetType))
+ {
+ if (object == null)
+ {
+ return coerce(ZERO, targetType);
+ }
+ else if ("".equals(object))
+ {
+ return coerce(ZERO, targetType);
+ }
+ else if (object instanceof String)
+ {
+ return coerce((String) object, targetType);
+ }
+ else if (isNumeric(object.getClass()))
+ {
+ return coerce((Number) object, targetType);
+ }
+ throw new IllegalArgumentException("Cannot convert " + object
+ + " to Number");
+ }
+
+ // Coerce to Boolean if appropriate
+ if (Boolean.class.equals(targetType) || (Boolean.TYPE == targetType))
+ {
+ if (object == null)
+ {
+ return Boolean.FALSE;
+ }
+ else if ("".equals(object))
+ {
+ return Boolean.FALSE;
+ }
+ else if ((object instanceof Boolean)
+ || (object.getClass() == Boolean.TYPE))
+ {
+ return (Boolean) object;
+ }
+ else if (object instanceof String)
+ {
+ return Boolean.valueOf((String) object);
+ }
+ throw new IllegalArgumentException("Cannot convert " + object
+ + " to Boolean");
+ }
+
+ // Coerce to Character if appropriate
+ if (Character.class.equals(targetType)
+ || (Character.TYPE == targetType))
+ {
+ if (object == null)
+ {
+ return new Character((char) 0);
+ }
+ else if ("".equals(object))
+ {
+ return new Character((char) 0);
+ }
+ else if (object instanceof String)
+ {
+ return new Character(((String) object).charAt(0));
+ }
+ else if (isNumeric(object.getClass()))
+ {
+ return new Character((char) ((Number) object).shortValue());
+ }
+ else if ((object instanceof Character)
+ || (object.getClass() == Character.TYPE))
+ {
+ return (Character) object;
+ }
+ throw new IllegalArgumentException("Cannot convert " + object
+ + " to Character");
+ }
+
+ if (targetType.isEnum())
+ {
+ if (object == null || "".equals(object))
+ {
+ return null;
+ }
+ if (targetType.isAssignableFrom(object.getClass()))
+ {
+ return (Enum) object;
+ }
+
+ if (!(object instanceof String))
+ {
+ throw new IllegalArgumentException("Cannot convert " + object + " to Enum");
+ }
+
+ Enum<?> result;
+ try
+ {
+ result = Enum.valueOf(targetType, (String) object);
+ return result;
+ }
+ catch (IllegalArgumentException iae)
+ {
+ throw new IllegalArgumentException("Cannot convert " + object + " to Enum");
+ }
+ }
+
+ // Is the specified value type-compatible already?
+ if ((object != null) && targetType.isAssignableFrom(object.getClass()))
+ {
+ return object;
+ }
+
+ // new to spec
+ if (object == null)
+ {
+ return null;
+ }
+
+ // We do not know how to perform this conversion
+ throw new IllegalArgumentException("Cannot convert " + object + " to "
+ + targetType.getName());
+
+ }
+
+ /** {@inheritDoc} */
+ public MethodExpression createMethodExpression(ELContext context,
+ String expression, Class expectedType, Class[] signature)
+ {
+
+ return new MockMethodExpression(expression, signature, expectedType);
+
+ }
+
+ /** {@inheritDoc} */
+ public ValueExpression createValueExpression(ELContext context,
+ String expression, Class expectedType)
+ {
+
+ return new MockCompositeValueExpression(expression, expectedType);
+
+ }
+
+ /** {@inheritDoc} */
+ public ValueExpression createValueExpression(Object instance,
+ Class expectedType)
+ {
+
+ return new MockVariableValueExpression(instance, expectedType);
+
+ }
+
+ // --------------------------------------------------------- Private Methods
+
+ /**
+ * <p>Coerce the specified value to the specified Number subclass.</p>
+ *
+ * @param value Value to be coerced
+ * @param type Destination type
+ */
+ private Number coerce(Number value, Class type)
+ {
+
+ if ((type == Byte.TYPE) || (type == Byte.class))
+ {
+ return new Byte(value.byteValue());
+ }
+ else if ((type == Double.TYPE) || (type == Double.class))
+ {
+ return new Double(value.doubleValue());
+ }
+ else if ((type == Float.TYPE) || (type == Float.class))
+ {
+ return new Float(value.floatValue());
+ }
+ else if ((type == Integer.TYPE) || (type == Integer.class))
+ {
+ return new Integer(value.intValue());
+ }
+ else if ((type == Long.TYPE) || (type == Long.class))
+ {
+ return new Long(value.longValue());
+ }
+ else if ((type == Short.TYPE) || (type == Short.class))
+ {
+ return new Short(value.shortValue());
+ }
+ else if (type == BigDecimal.class)
+ {
+ if (value instanceof BigDecimal)
+ {
+ return (BigDecimal) value;
+ }
+ else if (value instanceof BigInteger)
+ {
+ return new BigDecimal((BigInteger) value);
+ }
+ else
+ {
+ return new BigDecimal(((Number) value).doubleValue());
+ }
+ }
+ else if (type == BigInteger.class)
+ {
+ if (value instanceof BigInteger)
+ {
+ return (BigInteger) value;
+ }
+ else if (value instanceof BigDecimal)
+ {
+ return ((BigDecimal) value).toBigInteger();
+ }
+ else
+ {
+ return BigInteger.valueOf(((Number) value).longValue());
+ }
+ }
+ throw new IllegalArgumentException("Cannot convert " + value + " to "
+ + type.getName());
+
+ }
+
+ /**
+ * <p>Coerce the specified value to the specified Number subclass.</p>
+ *
+ * @param value Value to be coerced
+ * @param type Destination type
+ */
+ private Number coerce(String value, Class type)
+ {
+
+ if ((type == Byte.TYPE) || (type == Byte.class))
+ {
+ return Byte.valueOf(value);
+ }
+ else if ((type == Double.TYPE) || (type == Double.class))
+ {
+ return Double.valueOf(value);
+ }
+ else if ((type == Float.TYPE) || (type == Float.class))
+ {
+ return Float.valueOf(value);
+ }
+ else if ((type == Integer.TYPE) || (type == Integer.class))
+ {
+ return Integer.valueOf(value);
+ }
+ else if ((type == Long.TYPE) || (type == Long.class))
+ {
+ return Long.valueOf(value);
+ }
+ else if ((type == Short.TYPE) || (type == Short.class))
+ {
+ return Short.valueOf(value);
+ }
+ else if (type == BigDecimal.class)
+ {
+ return new BigDecimal(value);
+ }
+ else if (type == BigInteger.class)
+ {
+ return new BigInteger(value);
+ }
+ throw new IllegalArgumentException("Cannot convert " + value + " to "
+ + type.getName());
+
+ }
+
+ /**
+ * <p>Return <code>true</code> if the specified type is numeric.</p>
+ *
+ * @param type Type to check
+ */
+ private boolean isNumeric(Class type)
+ {
+
+ return (type == Byte.TYPE) || (type == Byte.class)
+ || (type == Double.TYPE) || (type == Double.class)
+ || (type == Float.TYPE) || (type == Float.class)
+ || (type == Integer.TYPE) || (type == Integer.class)
+ || (type == Long.TYPE) || (type == Long.class)
+ || (type == Short.TYPE) || (type == Short.class)
+ || (type == BigDecimal.class) || (type == BigInteger.class);
+
+ }
+
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/el/MockFunctionMapper.java b/test30/src/main/java/org/apache/myfaces/test/el/MockFunctionMapper.java
new file mode 100644
index 0000000..4d7d15d
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/el/MockFunctionMapper.java
@@ -0,0 +1,74 @@
+/*
+ * 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.myfaces.test.el;
+
+import java.lang.reflect.Method;
+import java.util.HashMap;
+import java.util.Map;
+import javax.el.FunctionMapper;
+
+/**
+ * <p>Mock implementation of <code>FunctionMapper</code>.</p>
+ *
+ * @since 1.0.0
+ */
+
+public class MockFunctionMapper extends FunctionMapper
+{
+
+ // ------------------------------------------------------------ Constructors
+
+ /** Creates a new instance of MockFunctionMapper */
+ public MockFunctionMapper()
+ {
+ }
+
+ // ------------------------------------------------------ Instance Variables
+
+ /**
+ * <p>Map of <code>Method</code> descriptors for static methods, keyed by
+ * a string composed of the prefix (or "" if none), a ":", and the local name.</p>
+ */
+ private Map functions = new HashMap();
+
+ // ----------------------------------------------------- Mock Object Methods
+
+ /**
+ * <p>Store a mapping of the specified prefix and localName to the
+ * specified method, which must be static.</p>
+ */
+ public void mapFunction(String prefix, String localName, Method method)
+ {
+
+ functions.put(prefix + ":" + localName, method);
+
+ }
+
+ // -------------------------------------------------- FunctionMapper Methods
+
+ /** {@inheritDoc} */
+ public Method resolveFunction(String prefix, String localName)
+ {
+
+ return (Method) functions.get(prefix + ":" + localName);
+
+ }
+
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/el/MockMethodExpression.java b/test30/src/main/java/org/apache/myfaces/test/el/MockMethodExpression.java
new file mode 100644
index 0000000..04b065a
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/el/MockMethodExpression.java
@@ -0,0 +1,267 @@
+/*
+ * 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.myfaces.test.el;
+
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
+import javax.el.ELContext;
+import javax.el.ELException;
+import javax.el.ELResolver;
+import javax.el.MethodExpression;
+import javax.el.MethodInfo;
+import javax.el.MethodNotFoundException;
+import javax.faces.context.FacesContext;
+
+/**
+ * <p>Mock implementation of <code>MethodExpression</code>.</p>
+ *
+ * @since 1.0.0
+ */
+public class MockMethodExpression extends MethodExpression
+{
+
+ // ------------------------------------------------------------ Constructors
+
+ /**
+ * Serial version UID.
+ */
+ private static final long serialVersionUID = 5694105394290316715L;
+
+ /**
+ * <p>Construct a new expression for the specified expression string.</p>
+ *
+ * @param expression Expression string to be evaluated
+ * @param signature Parameter signature of the method to be called
+ * @param expectedType Expected type of the result
+ */
+ public MockMethodExpression(String expression, Class[] signature,
+ Class expectedType)
+ {
+
+ if (expression == null)
+ {
+ throw new NullPointerException("Expression string cannot be null");
+ }
+ this.expression = expression;
+ this.signature = signature;
+ this.expectedType = expectedType;
+ parse();
+
+ }
+
+ // ------------------------------------------------------ Instance Variables
+
+ /**
+ * <p>The parsed elements of this expression.</p>
+ */
+ private String[] elements = null;
+
+ /**
+ * <p>The expected result type for <code>getValue()</code> calls.</p>
+ */
+ private Class expectedType = null;
+
+ /**
+ * <p>The original expression string used to create this expression.</p>
+ */
+ private String expression = null;
+
+ /**
+ * <p>The method signature of the method to be called.</p>
+ */
+ private Class[] signature = null;
+
+ // ------------------------------------------------------ Expression Methods
+
+ /**
+ * <p>Return <code>true</code> if this expression is equal to the
+ * specified expression.</p>
+ *
+ * @param obj Object to be compared
+ */
+ public boolean equals(Object obj)
+ {
+
+ if ((obj != null) & (obj instanceof MethodExpression))
+ {
+ return expression.equals(((MethodExpression) obj)
+ .getExpressionString());
+ }
+ else
+ {
+ return false;
+ }
+
+ }
+
+ /**
+ * <p>Return the original String used to create this expression,
+ * unmodified.</p>
+ */
+ public String getExpressionString()
+ {
+
+ return this.expression;
+
+ }
+
+ /**
+ * <p>Return the hash code for this expression.</p>
+ */
+ public int hashCode()
+ {
+
+ return this.expression.hashCode();
+
+ }
+
+ /**
+ * <p>Return <code>true</code> if the expression string for this expression
+ * contains only literal text.</p>
+ */
+ public boolean isLiteralText()
+ {
+
+ return (expression.indexOf("${") < 0) && (expression.indexOf("#{") < 0);
+
+ }
+
+ // ------------------------------------------------ MethodExpression Methods
+
+ /**
+ * <p>Evaluate the expression relative to the specified context,
+ * and return information about the actual implementation method.</p>
+ *
+ * @param context ELContext for this evaluation
+ */
+ public MethodInfo getMethodInfo(ELContext context)
+ {
+
+ if (context == null)
+ {
+ throw new NullPointerException();
+ }
+ return new MethodInfo(elements[elements.length - 1], expectedType,
+ signature);
+
+ }
+
+ /**
+ * <p>Evaluate the expression relative to the specified ocntext,
+ * and return the result after coercion to the expected result type.</p>
+ *
+ * @param context ELContext for this evaluation
+ * @param params Parameters for this method call
+ */
+ public Object invoke(ELContext context, Object[] params)
+ {
+
+ if (context == null)
+ {
+ throw new NullPointerException();
+ }
+ if (isLiteralText())
+ {
+ return expression;
+ }
+
+ FacesContext fcontext = (FacesContext) context
+ .getContext(FacesContext.class);
+ ELResolver resolver = fcontext.getApplication().getELResolver();
+ Object base = null;
+ for (int i = 0; i < elements.length - 1; i++)
+ {
+ base = resolver.getValue(context, base, elements[i]);
+ }
+
+ try
+ {
+ Method method = base.getClass().getMethod(
+ elements[elements.length - 1], signature);
+ Object result = method.invoke(base, params);
+ return fcontext.getApplication().getExpressionFactory()
+ .coerceToType(result, expectedType);
+ }
+ catch (RuntimeException e)
+ {
+ throw e;
+ }
+ catch (NoSuchMethodException e)
+ {
+ throw new MethodNotFoundException(e);
+ }
+ catch (Exception e)
+ {
+ throw new ELException(e);
+ }
+
+ }
+
+ // --------------------------------------------------------- Private Methods
+
+ /**
+ * <p>Parse the expression string into its constituent elemetns.</p>
+ */
+ private void parse()
+ {
+
+ if (isLiteralText())
+ {
+ elements = new String[0];
+ return;
+ }
+
+ if (expression.startsWith("${") || expression.startsWith("#{"))
+ {
+ if (expression.endsWith("}"))
+ {
+ String temp = expression.substring(2, expression.length() - 1)
+ .replaceAll(" ", "");
+ List names = new ArrayList();
+ while (temp.length() > 0)
+ {
+ int period = temp.indexOf(".");
+ if (period >= 0)
+ {
+ names.add(temp.substring(0, period));
+ temp = temp.substring(period + 1);
+ }
+ else
+ {
+ names.add(temp);
+ temp = "";
+ }
+ }
+ elements = (String[]) names.toArray(new String[names.size()]);
+ }
+ else
+ {
+ throw new IllegalArgumentException(expression);
+ }
+ }
+ else
+ {
+ throw new IllegalArgumentException(expression);
+ }
+
+ }
+
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/el/MockValueExpression.java b/test30/src/main/java/org/apache/myfaces/test/el/MockValueExpression.java
new file mode 100644
index 0000000..8489ea7
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/el/MockValueExpression.java
@@ -0,0 +1,307 @@
+/*
+ * 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.myfaces.test.el;
+
+import javax.el.ELContext;
+import javax.el.ELResolver;
+import javax.el.ValueExpression;
+import javax.faces.context.FacesContext;
+
+/**
+ * <p>Mock implementation of <code>ValueExpression</code>.</p>
+ *
+ * <p>This implementation supports a limited subset of overall expression functionality:</p>
+ * <ul>
+ * <li>A literal string that contains no expression delimiters.</li>
+ * <li>An expression that starts with "#{" or "${", and ends with "}".</li>
+ * </ul>
+ *
+ * @since 1.0.0
+ */
+public class MockValueExpression extends ValueExpression
+{
+
+ // ------------------------------------------------------------ Constructors
+
+ /**
+ * Serial version UID.
+ */
+ private static final long serialVersionUID = -8649071428507512623L;
+
+ /**
+ * <p>Construct a new expression for the specified expression string.</p>
+ *
+ * @param expression Expression string to be evaluated
+ * @param expectedType Expected type of the result
+ */
+ public MockValueExpression(String expression, Class expectedType)
+ {
+
+ if (expression == null)
+ {
+ throw new NullPointerException("Expression string cannot be null");
+ }
+ this.expression = expression;
+ this.expectedType = expectedType;
+ parse();
+
+ }
+
+ // ------------------------------------------------------ Instance Variables
+
+ /**
+ * <p>The parsed elements of this expression.</p>
+ */
+ private String[] elements = null;
+
+ /**
+ * <p>The expected result type for <code>getValue()</code> calls.</p>
+ */
+ private Class expectedType = null;
+
+ /**
+ * <p>The original expression string used to create this expression.</p>
+ */
+ private String expression = null;
+
+ // ------------------------------------------------------ Expression Methods
+
+ /**
+ * <p>Return <code>true</code> if this expression is equal to the
+ * specified expression.</p>
+ *
+ * @param obj Object to be compared
+ */
+ public boolean equals(Object obj)
+ {
+
+ if ((obj != null) & (obj instanceof ValueExpression))
+ {
+ return expression.equals(((ValueExpression) obj)
+ .getExpressionString());
+ }
+ else
+ {
+ return false;
+ }
+
+ }
+
+ /**
+ * <p>Return the original String used to create this expression,
+ * unmodified.</p>
+ */
+ public String getExpressionString()
+ {
+
+ return this.expression;
+
+ }
+
+ /**
+ * <p>Return the hash code for this expression.</p>
+ */
+ public int hashCode()
+ {
+
+ return this.expression.hashCode();
+
+ }
+
+ /**
+ * <p>Return <code>true</code> if the expression string for this expression
+ * contains only literal text.</p>
+ */
+ public boolean isLiteralText()
+ {
+
+ return (expression.indexOf("${") < 0) && (expression.indexOf("#{") < 0);
+
+ }
+
+ // ------------------------------------------------- ValueExpression Methods
+
+ /**
+ * <p>Return the type that the result of this expression will
+ * be coerced to.</p>
+ */
+ public Class getExpectedType()
+ {
+
+ return this.expectedType;
+
+ }
+
+ /**
+ * <p>Evaluate this expression relative to the specified context,
+ * and return the most general type that is acceptable for the
+ * value passed in a <code>setValue()</code> call.</p>
+ *
+ * @param context ELContext for this evaluation
+ */
+ public Class getType(ELContext context)
+ {
+
+ if (context == null)
+ {
+ throw new NullPointerException();
+ }
+ Object value = getValue(context);
+ if (value == null)
+ {
+ if (isLiteralText())
+ {
+ return String.class;
+ }
+
+ ELResolver resolver = context.getELResolver();
+ Object base = null;
+ for (int i = 0; i < elements.length - 1; i++)
+ {
+ base = resolver.getValue(context, base, elements[i]);
+ }
+ return resolver.getType(context, base, elements[elements.length - 1]);
+ }
+ else
+ {
+ return value.getClass();
+ }
+ }
+
+ /**
+ * <p>Evaluate this expression relative to the specified context,
+ * and return the result.</p>
+ *
+ * @param context ELContext for this evaluation
+ */
+ public Object getValue(ELContext context)
+ {
+
+ if (context == null)
+ {
+ throw new NullPointerException();
+ }
+ if (isLiteralText())
+ {
+ return expression;
+ }
+
+ FacesContext fcontext = (FacesContext) context
+ .getContext(FacesContext.class);
+
+ ELResolver resolver = context.getELResolver();
+ Object base = null;
+ for (int i = 0; i < elements.length; i++)
+ {
+ base = resolver.getValue(context, base, elements[i]);
+ }
+ return fcontext.getApplication().getExpressionFactory().coerceToType(
+ base, getExpectedType());
+
+ }
+
+ /**
+ * <p>Evaluate this expression relative to the specified context,
+ * and return <code>true</code> if a call to <code>setValue()</code>
+ * will always fail.</p>
+ *
+ * @param context ELContext for this evaluation
+ */
+ public boolean isReadOnly(ELContext context)
+ {
+
+ if (context == null)
+ {
+ throw new NullPointerException();
+ }
+ if (isLiteralText())
+ {
+ return true;
+ }
+
+ ELResolver resolver = context.getELResolver();
+ Object base = null;
+ for (int i = 0; i < elements.length - 1; i++)
+ {
+ base = resolver.getValue(context, base, elements[i]);
+ }
+ return resolver
+ .isReadOnly(context, base, elements[elements.length - 1]);
+
+ }
+
+ /**
+ * <p>Evaluate this expression relative to the specified context,
+ * and set the result to the specified value.</p>
+ *
+ * @param context ELContext for this evaluation
+ * @param value Value to which the result should be set
+ */
+ public void setValue(ELContext context, Object value)
+ {
+
+ if (context == null)
+ {
+ throw new NullPointerException();
+ }
+
+ ELResolver resolver = context.getELResolver();
+ Object base = null;
+ for (int i = 0; i < elements.length - 1; i++)
+ {
+ base = resolver.getValue(context, base, elements[i]);
+ }
+ resolver.setValue(context, base, elements[elements.length - 1], value);
+
+ }
+
+ // --------------------------------------------------------- Private Methods
+
+ /**
+ * <p>Parse the expression string into its constituent elemetns.</p>
+ */
+ private void parse()
+ {
+
+ if (isLiteralText())
+ {
+ elements = new String[0];
+ return;
+ }
+
+ if (expression.startsWith("${") || expression.startsWith("#{"))
+ {
+ if (expression.endsWith("}"))
+ {
+ elements = ExpressionTokenizer.tokenize(expression.substring(2, expression.length() - 1));
+ }
+ else
+ {
+ throw new IllegalArgumentException(expression);
+ }
+ }
+ else
+ {
+ throw new IllegalArgumentException(expression);
+ }
+
+ }
+
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/el/MockVariableMapper.java b/test30/src/main/java/org/apache/myfaces/test/el/MockVariableMapper.java
new file mode 100644
index 0000000..b19e7bd
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/el/MockVariableMapper.java
@@ -0,0 +1,80 @@
+/*
+ * 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.myfaces.test.el;
+
+import java.util.HashMap;
+import java.util.Map;
+import javax.el.ValueExpression;
+import javax.el.VariableMapper;
+
+/**
+ * <p>Mock implementation of <code>VariableMapper</code>.</p>
+ *
+ * @since 1.0.0
+ */
+
+public class MockVariableMapper extends VariableMapper
+{
+
+ // ------------------------------------------------------------ Constructors
+
+ /** Creates a new instance of MockVariableMapper */
+ public MockVariableMapper()
+ {
+ }
+
+ // ------------------------------------------------------ Instance Variables
+
+ /**
+ * <p>Map of <code>ValueExpression</code>s, keyed by variable name.</p>
+ */
+ private Map expressions = new HashMap();
+
+ // ----------------------------------------------------- Mock Object Methods
+
+ // -------------------------------------------------- FunctionMapper Methods
+
+ /** {@inheritDoc} */
+ public ValueExpression resolveVariable(String variable)
+ {
+
+ return (ValueExpression) expressions.get(variable);
+
+ }
+
+ /** {@inheritDoc} */
+ public ValueExpression setVariable(String variable,
+ ValueExpression expression)
+ {
+
+ ValueExpression original = (ValueExpression) expressions.get(variable);
+ if (expression == null)
+ {
+ expressions.remove(variable);
+ }
+ else
+ {
+ expressions.put(variable, expression);
+ }
+ return original;
+
+ }
+
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/el/MockVariableValueExpression.java b/test30/src/main/java/org/apache/myfaces/test/el/MockVariableValueExpression.java
new file mode 100644
index 0000000..734d911
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/el/MockVariableValueExpression.java
@@ -0,0 +1,215 @@
+/*
+ * 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.myfaces.test.el;
+
+import javax.el.ELContext;
+import javax.el.PropertyNotWritableException;
+import javax.el.ValueExpression;
+import javax.faces.context.FacesContext;
+
+/**
+ * <p>Mock implementation of <code>ValueExpression</code> that wraps a variable.</p>
+ *
+ * @since 1.0.0
+ */
+public class MockVariableValueExpression extends ValueExpression
+{
+
+ // ------------------------------------------------------------ Constructors
+
+ /**
+ * Serial version UID.
+ */
+ private static final long serialVersionUID = 4475919948345298291L;
+
+ /**
+ * <p>Construct a new expression for the specified instance.</p>
+ *
+ * @param instance Variable instance to be wrapped
+ * @param expectedType Expected type of the result
+ */
+ public MockVariableValueExpression(Object instance, Class expectedType)
+ {
+
+ if (instance == null)
+ {
+ throw new NullPointerException("Instance cannot be null");
+ }
+ this.instance = instance;
+ this.expectedType = expectedType;
+
+ }
+
+ // ------------------------------------------------------ Instance Variables
+
+ /**
+ * <p>The expected result type for <code>getValue()</code> calls.</p>
+ */
+ private Class expectedType = null;
+
+ /**
+ * <p>The variable instance being wrapped by this expression.</p>
+ */
+ private Object instance = null;
+
+ // ------------------------------------------------------ Expression Methods
+
+ /**
+ * <p>Return <code>true</code> if this expression is equal to the
+ * specified expression.</p>
+ *
+ * @param obj Object to be compared
+ */
+ public boolean equals(Object obj)
+ {
+
+ if ((obj != null) & (obj instanceof ValueExpression))
+ {
+ return instance.toString().equals(
+ ((ValueExpression) obj).getExpressionString());
+ }
+ else
+ {
+ return false;
+ }
+
+ }
+
+ /**
+ * <p>Return the original String used to create this expression,
+ * unmodified.</p>
+ */
+ public String getExpressionString()
+ {
+
+ return this.instance.toString();
+
+ }
+
+ /**
+ * <p>Return the hash code for this expression.</p>
+ */
+ public int hashCode()
+ {
+
+ return this.instance.toString().hashCode();
+
+ }
+
+ /**
+ * <p>Return <code>true</code> if the expression string for this expression
+ * contains only literal text.</p>
+ */
+ public boolean isLiteralText()
+ {
+
+ return true;
+
+ }
+
+ // ------------------------------------------------- ValueExpression Methods
+
+ /**
+ * <p>Return the type that the result of this expression will
+ * be coerced to.</p>
+ */
+ public Class getExpectedType()
+ {
+
+ return this.expectedType;
+
+ }
+
+ /**
+ * <p>Evaluate this expression relative to the specified context,
+ * and return the most general type that is acceptable for the
+ * value passed in a <code>setValue()</code> call.</p>
+ *
+ * @param context ELContext for this evaluation
+ */
+ public Class getType(ELContext context)
+ {
+
+ if (context == null)
+ {
+ throw new NullPointerException();
+ }
+ return this.instance.getClass();
+
+ }
+
+ /**
+ * <p>Evaluate this expression relative to the specified context,
+ * and return the result.</p>
+ *
+ * @param context ELContext for this evaluation
+ */
+ public Object getValue(ELContext context)
+ {
+
+ if (context == null)
+ {
+ throw new NullPointerException();
+ }
+ FacesContext fcontext = (FacesContext) context
+ .getContext(FacesContext.class);
+ return fcontext.getApplication().getExpressionFactory().coerceToType(
+ instance, expectedType);
+
+ }
+
+ /**
+ * <p>Evaluate this expression relative to the specified context,
+ * and return <code>true</code> if a call to <code>setValue()</code>
+ * will always fail.</p>
+ *
+ * @param context ELContext for this evaluation
+ */
+ public boolean isReadOnly(ELContext context)
+ {
+
+ if (context == null)
+ {
+ throw new NullPointerException();
+ }
+ return true;
+
+ }
+
+ /**
+ * <p>Evaluate this expression relative to the specified context,
+ * and set the result to the specified value.</p>
+ *
+ * @param context ELContext for this evaluation
+ * @param value Value to which the result should be set
+ */
+ public void setValue(ELContext context, Object value)
+ {
+
+ if (context == null)
+ {
+ throw new NullPointerException();
+ }
+
+ throw new PropertyNotWritableException();
+
+ }
+
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/el/ReservedWordsELResolver.java b/test30/src/main/java/org/apache/myfaces/test/el/ReservedWordsELResolver.java
new file mode 100644
index 0000000..2c048b5
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/el/ReservedWordsELResolver.java
@@ -0,0 +1,122 @@
+/*
+ * 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.myfaces.test.el;
+
+import java.beans.FeatureDescriptor;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import javax.el.ELContext;
+import javax.el.PropertyNotWritableException;
+
+/**
+ * {@code ELResolver} for reserved words.
+ */
+public class ReservedWordsELResolver extends AbstractELResolver
+{
+
+ private static final Map<String, Object> VALUES;
+
+ static
+ {
+ HashMap<String, Object> values = new HashMap<String, Object>();
+ values.put("true", Boolean.TRUE);
+ values.put("false", Boolean.FALSE);
+ values.put("null", null);
+ VALUES = Collections.unmodifiableMap(values);
+ }
+
+ private List<FeatureDescriptor> featureDescriptors;
+
+ @Override
+ public Object getValue(ELContext context, Object base, Object property)
+ {
+ if (base == null && VALUES.containsKey(property))
+ {
+ context.setPropertyResolved(true);
+ return VALUES.get(property);
+ }
+ return null;
+ }
+
+ @Override
+ public Class<?> getType(ELContext context, Object base, Object property)
+ {
+ if (base == null && VALUES.containsKey(property))
+ {
+ context.setPropertyResolved(true);
+ Object value = VALUES.get(property);
+ return value == null ? null : value.getClass();
+ }
+ return null;
+ }
+
+ @Override
+ public void setValue(ELContext context, Object base, Object property,
+ Object value)
+ {
+ if (base == null && VALUES.containsKey(property))
+ {
+ context.setPropertyResolved(true);
+ throw new PropertyNotWritableException(property.toString());
+ }
+ }
+
+ @Override
+ public boolean isReadOnly(ELContext context, Object base, Object property)
+ {
+ if (base == null && VALUES.containsKey(property))
+ {
+ context.setPropertyResolved(true);
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public synchronized Iterator<FeatureDescriptor> getFeatureDescriptors(
+ ELContext context, Object base)
+ {
+ if (featureDescriptors == null)
+ {
+ featureDescriptors = new ArrayList<FeatureDescriptor>();
+ for (Map.Entry<String, Object> e : VALUES.entrySet())
+ {
+ final Class<?> type
+ = e.getValue() == null ? null : e.getValue().getClass();
+ featureDescriptors.add(descriptor(e.getKey(), e.getKey(),
+ e.getKey(), false, false, true, type, true));
+ }
+ featureDescriptors
+ = Collections.unmodifiableList(featureDescriptors);
+ }
+ return featureDescriptors.iterator();
+ }
+
+ @Override
+ public Class<?> getCommonPropertyType(ELContext context, Object base)
+ {
+ return base == null ? String.class : null;
+ }
+
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/htmlunit/AbstractHtmlUnitTestCase.java b/test30/src/main/java/org/apache/myfaces/test/htmlunit/AbstractHtmlUnitTestCase.java
new file mode 100644
index 0000000..b1e62b0
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/htmlunit/AbstractHtmlUnitTestCase.java
@@ -0,0 +1,333 @@
+/*
+ * 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.myfaces.test.htmlunit;
+
+import com.gargoylesoftware.htmlunit.ElementNotFoundException;
+import com.gargoylesoftware.htmlunit.WebClient;
+import com.gargoylesoftware.htmlunit.html.HtmlAnchor;
+import com.gargoylesoftware.htmlunit.html.HtmlBody;
+import com.gargoylesoftware.htmlunit.html.HtmlElement;
+import com.gargoylesoftware.htmlunit.html.HtmlForm;
+import com.gargoylesoftware.htmlunit.html.HtmlHead;
+import com.gargoylesoftware.htmlunit.html.HtmlPage;
+import com.gargoylesoftware.htmlunit.html.HtmlSubmitInput;
+import java.io.IOException;
+import java.net.URL;
+import java.util.Iterator;
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+/**
+ * <p>Abstract base class for system integration tests based on HtmlUnit.
+ * These tests will expect a system property named <code>url</code> to be
+ * present, which will define the URL (including the context path, but
+ * without a trailing slash) of the application to be tested.</p>
+ *
+ * @since 1.0.0
+ */
+public abstract class AbstractHtmlUnitTestCase extends TestCase
+{
+
+ // ------------------------------------------------------------ Constructors
+
+ /**
+ * <p>Construct a new instance of this test case.</p>
+ *
+ * @param name Name of the new test case
+ */
+ public AbstractHtmlUnitTestCase(String name)
+ {
+
+ super(name);
+
+ }
+
+ // ------------------------------------------------------ Instance Variables
+
+ /**
+ * <p>The most recently retrieved page from the server.</p>
+ */
+ protected HtmlPage page = null;
+
+ /**
+ * <p>The calculated URL for the installed "systest" web application.
+ * This value is based on a system property named <code>url</code>,
+ * which must be defined as part of the command line that executes
+ * each test case.</p>
+ */
+ protected URL url = null;
+
+ /**
+ * <p>The web client for this test case.</p>
+ */
+ protected WebClient webClient = null;
+
+ // ------------------------------------------------------ Test Setup Methods
+
+ /**
+ * <p>Set up the instance variables required for this test case.</p>
+ *
+ * @exception Exception if an error occurs
+ */
+ protected void setUp() throws Exception
+ {
+
+ // Calculate the URL for the installed "systest" web application
+ String url = System.getProperty("url");
+ this.url = new URL(url + "/");
+
+ // Initialize HtmlUnit constructs for this test case
+ webClient = new WebClient();
+
+ }
+
+ /**
+ * <p>Return the set of tests included in this test suite.</p>
+ */
+ public static Test suite()
+ {
+
+ return (new TestSuite(AbstractHtmlUnitTestCase.class));
+
+ }
+
+ /**
+ * <p>Tear down instance variables required by this test case.</p>
+ */
+ protected void tearDown() throws Exception
+ {
+
+ page = null;
+ url = null;
+ webClient = null;
+
+ }
+
+ // ------------------------------------------------------- Protected Methods
+
+ /**
+ * <p>Return the body element for the most recently retrieved page.
+ * If there is no such element, return <code>null</code>.</p>
+ *
+ * @exception Exception if an error occurs
+ */
+ protected HtmlBody body() throws Exception
+ {
+
+ Iterator elements = page.getAllHtmlChildElements();
+ while (elements.hasNext())
+ {
+ HtmlElement element = (HtmlElement) elements.next();
+ if (element instanceof HtmlBody)
+ {
+ return ((HtmlBody) element);
+ }
+ }
+ return (null);
+
+ }
+
+ /**
+ * <p>Return the HTML element with the specified <code>id</code> from the
+ * most recently retrieved page. If there is no such element, return
+ * <code>null</code>.</p>
+ *
+ * @param id Identifier of the requested element.
+ *
+ * @exception Exception if an error occurs
+ */
+ protected HtmlElement element(String id) throws Exception
+ {
+
+ try
+ {
+ return (page.getHtmlElementById(id));
+ }
+ catch (ElementNotFoundException e)
+ {
+ return (null);
+ }
+
+ }
+
+ /**
+ * <p>Return the form with the specified <code>id</code> from the most
+ * recently retrieved page. If there is no such form, return
+ * <code>null</code>.<p>
+ *
+ * @param id Identifier of the requested form.
+ *
+ * @exception Exception if an error occurs
+ */
+ protected HtmlForm form(String id) throws Exception
+ {
+
+ Iterator forms = page.getForms().iterator();
+ while (forms.hasNext())
+ {
+ HtmlForm form = (HtmlForm) forms.next();
+ if (id.equals(form.getAttributeValue("id")))
+ {
+ return (form);
+ }
+ }
+ return (null);
+
+ }
+
+ /**
+ * <p>Return the head element for the most recently retrieved page.
+ * If there is no such element, return <code>null</code>.</p>
+ *
+ * @exception Exception if an error occurs
+ */
+ protected HtmlHead head() throws Exception
+ {
+
+ Iterator elements = page.getAllHtmlChildElements();
+ while (elements.hasNext())
+ {
+ HtmlElement element = (HtmlElement) elements.next();
+ if (element instanceof HtmlHead)
+ {
+ return ((HtmlHead) element);
+ }
+ }
+ return (null);
+
+ }
+
+ /**
+ * <p>Click the specified hyperlink, and retrieve the subsequent page,
+ * saving a reference so that other utility methods may be used to
+ * retrieve information from it.</p>
+ *
+ * @param anchor Anchor component to click
+ *
+ * @exception IOException if an input/output error occurs
+ */
+ protected HtmlPage link(HtmlAnchor anchor) throws IOException
+ {
+
+ HtmlPage page = (HtmlPage) anchor.click();
+ this.page = page;
+ return page;
+
+ }
+
+ /**
+ * <p>Return the currently stored page reference.</p>
+ */
+ protected HtmlPage page()
+ {
+
+ return this.page;
+
+ }
+
+ /**
+ * <p>Retrieve and return the page at the specified context relative path.
+ * Save a reference to this page so that other utility methods may be used
+ * to retrieve information from it.</p>
+ *
+ * @param path Context relative path
+ *
+ * @exception IllegalArgumentException if the context relative path
+ * does not begin with a '/' character
+ * @exception Exception if a different error occurs
+ */
+ protected HtmlPage page(String path) throws Exception
+ {
+
+ HtmlPage page = (HtmlPage) webClient.getPage(url(path));
+ this.page = page;
+ return (page);
+
+ }
+
+ /**
+ * <p>Reset the stored page reference to the specified value. This is
+ * useful for scenarios testing resubmit of the same page (simulating the
+ * user pressing the back button and then submitting again).</p>
+ *
+ * @param page Previously saved page to which to reset
+ */
+ protected void reset(HtmlPage page)
+ {
+
+ this.page = page;
+
+ }
+
+ /**
+ * <p>Submit the current page, using the specified component, and retrieve
+ * the subsequent page, saving a reference so that other utility methods
+ * may be used to retrieve information from it.</p>
+ *
+ * @param submit Submit button component to click
+ *
+ * @exception IOException if an input/output error occurs
+ */
+ protected HtmlPage submit(HtmlSubmitInput submit) throws IOException
+ {
+
+ HtmlPage page = (HtmlPage) submit.click();
+ this.page = page;
+ return page;
+
+ }
+
+ /**
+ * <p>Return the page title from the most recently retrieved page.
+ * Any leading and trailing whitespace will be trimmed.</p>
+ *
+ * @exception Exception if an error occurs
+ */
+ protected String title() throws Exception
+ {
+
+ return (page.getTitleText().trim());
+
+ }
+
+ /**
+ * <p>Calculate and return an absolute URL for the specified context
+ * relative path, which must begin with a '/' character.</p>
+ *
+ * @param path Context relative path
+ *
+ * @exception IllegalArgumentException if the context relative path
+ * does not begin with a '/' character
+ * @exception Exception if a different error ocurs
+ */
+ protected URL url(String path) throws Exception
+ {
+
+ if (path.charAt(0) != '/')
+ {
+ throw new IllegalArgumentException("Context path '" + path
+ + "' does not start with '/'");
+ }
+ return new URL(url, path.substring(1));
+
+ }
+
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/htmlunit/junit4/AbstractHtmlUnitTestCase.java b/test30/src/main/java/org/apache/myfaces/test/htmlunit/junit4/AbstractHtmlUnitTestCase.java
new file mode 100644
index 0000000..648b1de
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/htmlunit/junit4/AbstractHtmlUnitTestCase.java
@@ -0,0 +1,322 @@
+/*
+ * 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.myfaces.test.htmlunit.junit4;
+
+import java.io.IOException;
+import java.net.URL;
+import java.util.Iterator;
+
+import org.junit.After;
+import org.junit.Before;
+
+import com.gargoylesoftware.htmlunit.ElementNotFoundException;
+import com.gargoylesoftware.htmlunit.WebClient;
+import com.gargoylesoftware.htmlunit.html.HtmlAnchor;
+import com.gargoylesoftware.htmlunit.html.HtmlBody;
+import com.gargoylesoftware.htmlunit.html.HtmlElement;
+import com.gargoylesoftware.htmlunit.html.HtmlForm;
+import com.gargoylesoftware.htmlunit.html.HtmlHead;
+import com.gargoylesoftware.htmlunit.html.HtmlPage;
+import com.gargoylesoftware.htmlunit.html.HtmlSubmitInput;
+
+/**
+ * <p>Abstract base class for system integration tests based on HtmlUnit.
+ * These tests will expect a system property named <code>url</code> to be
+ * present, which will define the URL (including the context path, but
+ * without a trailing slash) of the application to be tested.</p>
+ *
+ * @since 1.0.0
+ */
+public abstract class AbstractHtmlUnitTestCase
+{
+
+ // ------------------------------------------------------------ Constructors
+
+ /**
+ * <p>Construct a new instance of this test case.</p>
+ *
+ * @param name Name of the new test case
+ */
+ public AbstractHtmlUnitTestCase()
+ {
+ }
+
+ // ------------------------------------------------------ Instance Variables
+
+ /**
+ * <p>The most recently retrieved page from the server.</p>
+ */
+ protected HtmlPage page = null;
+
+ /**
+ * <p>The calculated URL for the installed "systest" web application.
+ * This value is based on a system property named <code>url</code>,
+ * which must be defined as part of the command line that executes
+ * each test case.</p>
+ */
+ protected URL url = null;
+
+ /**
+ * <p>The web client for this test case.</p>
+ */
+ protected WebClient webClient = null;
+
+ // ------------------------------------------------------ Test Setup Methods
+
+ /**
+ * <p>Set up the instance variables required for this test case.</p>
+ *
+ * @exception Exception if an error occurs
+ */
+ @Before
+ protected void setUp() throws Exception
+ {
+
+ // Calculate the URL for the installed "systest" web application
+ String url = System.getProperty("url");
+ this.url = new URL(url + "/");
+
+ // Initialize HtmlUnit constructs for this test case
+ webClient = new WebClient();
+
+ }
+
+ /**
+ * <p>Tear down instance variables required by this test case.</p>
+ */
+ @After
+ protected void tearDown() throws Exception
+ {
+
+ page = null;
+ url = null;
+ webClient = null;
+
+ }
+
+ // ------------------------------------------------------- Protected Methods
+
+ /**
+ * <p>Return the body element for the most recently retrieved page.
+ * If there is no such element, return <code>null</code>.</p>
+ *
+ * @exception Exception if an error occurs
+ */
+ protected HtmlBody body() throws Exception
+ {
+
+ Iterator elements = page.getAllHtmlChildElements();
+ while (elements.hasNext())
+ {
+ HtmlElement element = (HtmlElement) elements.next();
+ if (element instanceof HtmlBody)
+ {
+ return ((HtmlBody) element);
+ }
+ }
+ return (null);
+
+ }
+
+ /**
+ * <p>Return the HTML element with the specified <code>id</code> from the
+ * most recently retrieved page. If there is no such element, return
+ * <code>null</code>.</p>
+ *
+ * @param id Identifier of the requested element.
+ *
+ * @exception Exception if an error occurs
+ */
+ protected HtmlElement element(String id) throws Exception
+ {
+
+ try
+ {
+ return (page.getHtmlElementById(id));
+ }
+ catch (ElementNotFoundException e)
+ {
+ return (null);
+ }
+
+ }
+
+ /**
+ * <p>Return the form with the specified <code>id</code> from the most
+ * recently retrieved page. If there is no such form, return
+ * <code>null</code>.<p>
+ *
+ * @param id Identifier of the requested form.
+ *
+ * @exception Exception if an error occurs
+ */
+ protected HtmlForm form(String id) throws Exception
+ {
+
+ Iterator forms = page.getForms().iterator();
+ while (forms.hasNext())
+ {
+ HtmlForm form = (HtmlForm) forms.next();
+ if (id.equals(form.getAttributeValue("id")))
+ {
+ return (form);
+ }
+ }
+ return (null);
+
+ }
+
+ /**
+ * <p>Return the head element for the most recently retrieved page.
+ * If there is no such element, return <code>null</code>.</p>
+ *
+ * @exception Exception if an error occurs
+ */
+ protected HtmlHead head() throws Exception
+ {
+
+ Iterator elements = page.getAllHtmlChildElements();
+ while (elements.hasNext())
+ {
+ HtmlElement element = (HtmlElement) elements.next();
+ if (element instanceof HtmlHead)
+ {
+ return ((HtmlHead) element);
+ }
+ }
+ return (null);
+
+ }
+
+ /**
+ * <p>Click the specified hyperlink, and retrieve the subsequent page,
+ * saving a reference so that other utility methods may be used to
+ * retrieve information from it.</p>
+ *
+ * @param anchor Anchor component to click
+ *
+ * @exception IOException if an input/output error occurs
+ */
+ protected HtmlPage link(HtmlAnchor anchor) throws IOException
+ {
+
+ HtmlPage page = (HtmlPage) anchor.click();
+ this.page = page;
+ return page;
+
+ }
+
+ /**
+ * <p>Return the currently stored page reference.</p>
+ */
+ protected HtmlPage page()
+ {
+
+ return this.page;
+
+ }
+
+ /**
+ * <p>Retrieve and return the page at the specified context relative path.
+ * Save a reference to this page so that other utility methods may be used
+ * to retrieve information from it.</p>
+ *
+ * @param path Context relative path
+ *
+ * @exception IllegalArgumentException if the context relative path
+ * does not begin with a '/' character
+ * @exception Exception if a different error occurs
+ */
+ protected HtmlPage page(String path) throws Exception
+ {
+
+ HtmlPage page = (HtmlPage) webClient.getPage(url(path));
+ this.page = page;
+ return (page);
+
+ }
+
+ /**
+ * <p>Reset the stored page reference to the specified value. This is
+ * useful for scenarios testing resubmit of the same page (simulating the
+ * user pressing the back button and then submitting again).</p>
+ *
+ * @param page Previously saved page to which to reset
+ */
+ protected void reset(HtmlPage page)
+ {
+
+ this.page = page;
+
+ }
+
+ /**
+ * <p>Submit the current page, using the specified component, and retrieve
+ * the subsequent page, saving a reference so that other utility methods
+ * may be used to retrieve information from it.</p>
+ *
+ * @param submit Submit button component to click
+ *
+ * @exception IOException if an input/output error occurs
+ */
+ protected HtmlPage submit(HtmlSubmitInput submit) throws IOException
+ {
+
+ HtmlPage page = (HtmlPage) submit.click();
+ this.page = page;
+ return page;
+
+ }
+
+ /**
+ * <p>Return the page title from the most recently retrieved page.
+ * Any leading and trailing whitespace will be trimmed.</p>
+ *
+ * @exception Exception if an error occurs
+ */
+ protected String title() throws Exception
+ {
+
+ return (page.getTitleText().trim());
+
+ }
+
+ /**
+ * <p>Calculate and return an absolute URL for the specified context
+ * relative path, which must begin with a '/' character.</p>
+ *
+ * @param path Context relative path
+ *
+ * @exception IllegalArgumentException if the context relative path
+ * does not begin with a '/' character
+ * @exception Exception if a different error ocurs
+ */
+ protected URL url(String path) throws Exception
+ {
+
+ if (path.charAt(0) != '/')
+ {
+ throw new IllegalArgumentException("Context path '" + path
+ + "' does not start with '/'");
+ }
+ return new URL(url, path.substring(1));
+
+ }
+
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/jmock/AbstractJmockJsfTestCase.java b/test30/src/main/java/org/apache/myfaces/test/jmock/AbstractJmockJsfTestCase.java
new file mode 100644
index 0000000..2bd5f95
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/jmock/AbstractJmockJsfTestCase.java
@@ -0,0 +1,354 @@
+/*
+ * 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.myfaces.test.jmock;
+
+import java.net.URL;
+import java.net.URLClassLoader;
+
+import javax.faces.FactoryFinder;
+import javax.faces.application.ApplicationFactory;
+import javax.faces.component.UIViewRoot;
+import javax.faces.lifecycle.LifecycleFactory;
+import javax.faces.render.RenderKitFactory;
+
+import org.apache.myfaces.test.config.ResourceBundleVarNames;
+import org.apache.myfaces.test.mock.MockApplication;
+import org.apache.myfaces.test.mock.MockExternalContext;
+import org.apache.myfaces.test.mock.MockFacesContext;
+import org.apache.myfaces.test.mock.MockFacesContextFactory;
+import org.apache.myfaces.test.mock.MockHttpServletRequest;
+import org.apache.myfaces.test.mock.MockHttpServletResponse;
+import org.apache.myfaces.test.mock.MockHttpSession;
+import org.apache.myfaces.test.mock.MockRenderKit;
+import org.apache.myfaces.test.mock.MockServletConfig;
+import org.apache.myfaces.test.mock.MockServletContext;
+import org.apache.myfaces.test.mock.lifecycle.MockLifecycle;
+import org.apache.myfaces.test.mock.lifecycle.MockLifecycleFactory;
+import org.jmock.cglib.MockObjectTestCase;
+
+/**
+ * <p>Abstract JMock test case base class, which sets up the JavaServer Faces
+ * mock object environment for a particular simulated request. The following
+ * protected variables are initialized in the <code>setUp()</code> method, and
+ * cleaned up in the <code>tearDown()</code> method:</p>
+ * <ul>
+ * <li><code>application</code> (<code>MockApplication</code>)</li>
+ * <li><code>config</code> (<code>MockServletConfig</code>)</li>
+ * <li><code>externalContext</code> (<code>MockExternalContext</code>)</li>
+ * <li><code>facesContext</code> (<code>MockFacesContext</code>)</li>
+ * <li><code>lifecycle</code> (<code>MockLifecycle</code>)</li>
+ * <li><code>request</code> (<code>MockHttpServletRequest</code></li>
+ * <li><code>response</code> (<code>MockHttpServletResponse</code>)</li>
+ * <li><code>servletContext</code> (<code>MockServletContext</code>)</li>
+ * <li><code>session</code> (<code>MockHttpSession</code>)</li>
+ * </ul>
+ *
+ * <p>In addition, appropriate factory classes will have been registered with
+ * <code>javax.faces.FactoryFinder</code> for <code>Application</code> and
+ * <code>RenderKit</code> instances. The created <code>FacesContext</code>
+ * instance will also have been registered in the proper thread local
+ * variable, to simulate what a servlet container would do.</p>
+ *
+ * <p><strong>WARNING</strong> - If you choose to subclass this class, be sure
+ * your <code>setUp()</code> and <code>tearDown()</code> methods call
+ * <code>super.setUp()</code> and <code>super.tearDown()</code> respectively,
+ * and that you implement your own <code>suite()</code> method that exposes
+ * the test methods for your test case.</p>
+ *
+ * @since 1.0.0
+ */
+
+public abstract class AbstractJmockJsfTestCase extends MockObjectTestCase
+{
+
+ // ------------------------------------------------------------ Constructors
+
+ /**
+ * <p>Construct a new instance of this test case.</p>
+ *
+ * @param name Name of this test case
+ */
+ public AbstractJmockJsfTestCase(String name)
+ {
+ setName(name);
+ }
+
+ // ---------------------------------------------------- Overall Test Methods
+
+ /**
+ * <p>Set up instance variables required by this test case.</p>
+ */
+ protected void setUp() throws Exception
+ {
+ // Set up a new thread context class loader
+ setUpClassloader();
+
+ // Set up Servlet API Objects
+ setUpServletObjects();
+
+ // Set up JSF API Objects
+ FactoryFinder.releaseFactories();
+
+ setFactories();
+
+ setUpJSFObjects();
+ }
+
+ /**
+ * Set up the thread context classloader. JSF uses the this classloader
+ * in order to find related factory classes and other resources, but in
+ * some selected cases, the default classloader cannot be properly set.
+ *
+ * @throws Exception
+ */
+ protected void setUpClassloader() throws Exception
+ {
+ threadContextClassLoader = Thread.currentThread()
+ .getContextClassLoader();
+ Thread.currentThread()
+ .setContextClassLoader(
+ new URLClassLoader(new URL[0], this.getClass()
+ .getClassLoader()));
+ classLoaderSet = true;
+ }
+
+ /**
+ * <p>Setup JSF object used for the test. By default it calls to the following
+ * methods in this order:</p>
+ *
+ * <ul>
+ * <li><code>setUpExternalContext();</code></li>
+ * <li><code>setUpLifecycle();</code></li>
+ * <li><code>setUpFacesContext();</code></li>
+ * <li><code>setUpView();</code></li>
+ * <li><code>setUpApplication();</code></li>
+ * <li><code>setUpRenderKit();</code></li>
+ * </ul>
+ *
+ * @throws Exception
+ */
+ protected void setUpJSFObjects() throws Exception
+ {
+ setUpExternalContext();
+ setUpLifecycle();
+ setUpFacesContext();
+ setUpView();
+ setUpApplication();
+ setUpRenderKit();
+ }
+
+ /**
+ * <p>Setup servlet objects that will be used for the test:</p>
+ *
+ * <ul>
+ * <li><code>config</code> (<code>MockServletConfig</code>)</li>
+ * <li><code>servletContext</code> (<code>MockServletContext</code>)</li>
+ * <li><code>request</code> (<code>MockHttpServletRequest</code></li>
+ * <li><code>response</code> (<code>MockHttpServletResponse</code>)</li>
+ * <li><code>session</code> (<code>MockHttpSession</code>)</li>
+ * </ul>
+ *
+ * @throws Exception
+ */
+ protected void setUpServletObjects() throws Exception
+ {
+ servletContext = new MockServletContext();
+ config = new MockServletConfig(servletContext);
+ session = new MockHttpSession();
+ session.setServletContext(servletContext);
+ request = new MockHttpServletRequest(session);
+ request.setServletContext(servletContext);
+ response = new MockHttpServletResponse();
+ }
+
+ /**
+ * <p>Set JSF factories using FactoryFinder method setFactory.</p>
+ *
+ * @throws Exception
+ */
+ protected void setFactories() throws Exception
+ {
+ FactoryFinder.setFactory(FactoryFinder.APPLICATION_FACTORY,
+ "org.apache.myfaces.test.mock.MockApplicationFactory");
+ FactoryFinder.setFactory(FactoryFinder.FACES_CONTEXT_FACTORY,
+ "org.apache.myfaces.test.mock.MockFacesContextFactory");
+ FactoryFinder.setFactory(FactoryFinder.LIFECYCLE_FACTORY,
+ "org.apache.myfaces.test.mock.lifecycle.MockLifecycleFactory");
+ FactoryFinder.setFactory(FactoryFinder.RENDER_KIT_FACTORY,
+ "org.apache.myfaces.test.mock.MockRenderKitFactory");
+ FactoryFinder.setFactory(FactoryFinder.EXCEPTION_HANDLER_FACTORY,
+ "org.apache.myfaces.test.mock.MockExceptionHandlerFactory");
+ FactoryFinder.setFactory(FactoryFinder.PARTIAL_VIEW_CONTEXT_FACTORY,
+ "org.apache.myfaces.test.mock.MockPartialViewContextFactory");
+ FactoryFinder.setFactory(FactoryFinder.VISIT_CONTEXT_FACTORY,
+ "org.apache.myfaces.test.mock.visit.MockVisitContextFactory");
+ }
+
+ /**
+ * Setup the <code>externalContext</code> variable, using the
+ * servlet variables already initialized.
+ *
+ * @throws Exception
+ */
+ protected void setUpExternalContext() throws Exception
+ {
+ externalContext = new MockExternalContext(servletContext, request,
+ response);
+ }
+
+ /**
+ * Setup the <code>lifecycle</code> and <code>lifecycleFactory</code>
+ * variables.
+ *
+ * @throws Exception
+ */
+ protected void setUpLifecycle() throws Exception
+ {
+ lifecycleFactory = (MockLifecycleFactory) FactoryFinder
+ .getFactory(FactoryFinder.LIFECYCLE_FACTORY);
+ lifecycle = (MockLifecycle) lifecycleFactory
+ .getLifecycle(LifecycleFactory.DEFAULT_LIFECYCLE);
+ }
+
+ /**
+ * Setup the <code>facesContextFactory</code> and <code>facesContext</code>
+ * variable. Before end, by default it override <code>externalContext</code>
+ * variable from the value retrieved from facesContext.getExternalContext(),
+ * because sometimes it is possible facesContext overrides externalContext
+ * internally.
+ *
+ * @throws Exception
+ */
+ protected void setUpFacesContext() throws Exception
+ {
+ facesContextFactory = (MockFacesContextFactory) FactoryFinder
+ .getFactory(FactoryFinder.FACES_CONTEXT_FACTORY);
+ facesContext = (MockFacesContext) facesContextFactory.getFacesContext(
+ servletContext, request, response, lifecycle);
+ if (facesContext.getExternalContext() != null)
+ {
+ externalContext = (MockExternalContext) facesContext
+ .getExternalContext();
+ }
+ }
+
+ /**
+ * By default, create an instance of UIViewRoot, set its viewId as "/viewId"
+ * and assign it to the current facesContext.
+ *
+ * @throws Exception
+ */
+ protected void setUpView() throws Exception
+ {
+ UIViewRoot root = new UIViewRoot();
+ root.setViewId("/viewId");
+ root.setRenderKitId(RenderKitFactory.HTML_BASIC_RENDER_KIT);
+ facesContext.setViewRoot(root);
+ }
+
+ /**
+ * Setup the <code>application</code> variable and before
+ * the end by default it is assigned to the <code>facesContext</code>
+ * variable, calling <code>facesContext.setApplication(application)</code>
+ *
+ * @throws Exception
+ */
+ protected void setUpApplication() throws Exception
+ {
+ ApplicationFactory applicationFactory = (ApplicationFactory) FactoryFinder
+ .getFactory(FactoryFinder.APPLICATION_FACTORY);
+ application = (MockApplication) applicationFactory.getApplication();
+ facesContext.setApplication(application);
+ }
+
+ /**
+ * Setup the <code>renderKit</code> variable. This is a good place to use
+ * <code>ConfigParser</code> to register converters, validators, components
+ * or renderkits.
+ *
+ * @throws Exception
+ */
+ protected void setUpRenderKit() throws Exception
+ {
+ RenderKitFactory renderKitFactory = (RenderKitFactory) FactoryFinder
+ .getFactory(FactoryFinder.RENDER_KIT_FACTORY);
+ renderKit = new MockRenderKit();
+ renderKitFactory.addRenderKit(RenderKitFactory.HTML_BASIC_RENDER_KIT,
+ renderKit);
+ }
+
+ /**
+ * <p>Tear down instance variables required by this test case.</p>
+ */
+ protected void tearDown() throws Exception
+ {
+
+ application = null;
+ config = null;
+ externalContext = null;
+ if (facesContext != null)
+ {
+ facesContext.release();
+ }
+ facesContext = null;
+ lifecycle = null;
+ lifecycleFactory = null;
+ renderKit = null;
+ request = null;
+ response = null;
+ servletContext = null;
+ session = null;
+ FactoryFinder.releaseFactories();
+ ResourceBundleVarNames.resetNames();
+
+ tearDownClassloader();
+ }
+
+ protected void tearDownClassloader() throws Exception
+ {
+ if (classLoaderSet)
+ {
+ Thread.currentThread().setContextClassLoader(threadContextClassLoader);
+ threadContextClassLoader = null;
+ classLoaderSet = false;
+ }
+ }
+
+ // ------------------------------------------------------ Instance Variables
+
+ // Mock object instances for our tests
+ protected MockApplication application = null;
+ protected MockServletConfig config = null;
+ protected MockExternalContext externalContext = null;
+ protected MockFacesContext facesContext = null;
+ protected MockFacesContextFactory facesContextFactory = null;
+ protected MockLifecycle lifecycle = null;
+ protected MockLifecycleFactory lifecycleFactory = null;
+ protected MockRenderKit renderKit = null;
+ protected MockHttpServletRequest request = null;
+ protected MockHttpServletResponse response = null;
+ protected MockServletContext servletContext = null;
+ protected MockHttpSession session = null;
+
+ // Thread context class loader saved and restored after each test
+ private ClassLoader threadContextClassLoader = null;
+ private boolean classLoaderSet = false;
+
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/mock/MockActionListener.java b/test30/src/main/java/org/apache/myfaces/test/mock/MockActionListener.java
new file mode 100644
index 0000000..3aa9937
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/mock/MockActionListener.java
@@ -0,0 +1,65 @@
+/*
+ * 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.myfaces.test.mock;
+
+import javax.faces.event.AbortProcessingException;
+import javax.faces.event.ActionEvent;
+import javax.faces.event.ActionListener;
+
+/**
+ * <p>Mock implementation of the default <code>ActionListener</code>.</p>
+ *
+ * $Id$
+ * @since 1.0.0
+ */
+
+public class MockActionListener implements ActionListener
+{
+
+ // ------------------------------------------------------------ Constructors
+
+ /**
+ * <p>Construct a default instance.</p>
+ */
+ public MockActionListener()
+ {
+ }
+
+ // ----------------------------------------------------- Mock Object Methods
+
+ // ------------------------------------------------------ Instance Variables
+
+ // -------------------------------------------------- ActionListener Methods
+
+ /**
+ * <p>Process the specified <code>ActionEvent</code>.</p>
+ *
+ * @param event Event to be processed
+ *
+ * @exception AbortProcessingException if further event firing
+ * should be skipped
+ */
+ public void processAction(ActionEvent event)
+ throws AbortProcessingException
+ {
+ // FIXME - provide default implementation
+ }
+
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/mock/MockApplication.java b/test30/src/main/java/org/apache/myfaces/test/mock/MockApplication.java
new file mode 100644
index 0000000..e32f2a8
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/mock/MockApplication.java
@@ -0,0 +1,407 @@
+/*
+ * 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.myfaces.test.mock;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Locale;
+import java.util.Map;
+
+import javax.faces.FacesException;
+import javax.faces.application.Application;
+import javax.faces.application.NavigationHandler;
+import javax.faces.application.StateManager;
+import javax.faces.application.ViewHandler;
+import javax.faces.component.UIComponent;
+import javax.faces.convert.Converter;
+import javax.faces.event.ActionListener;
+import javax.faces.render.RenderKitFactory;
+import javax.faces.validator.Validator;
+
+/**
+ * <p>Mock implementation of <code>Application</code>.</p>
+ *
+ * $Id$
+ * @since 1.0.0
+ */
+
+public class MockApplication extends Application
+{
+
+ // ------------------------------------------------------------ Constructors
+
+ /**
+ * <p>Construct a default instance.</p>
+ */
+ public MockApplication()
+ {
+
+ setActionListener(new MockActionListener());
+ components = new HashMap();
+ converters = new HashMap();
+ converters1 = new HashMap();
+ setDefaultLocale(Locale.getDefault());
+ setDefaultRenderKitId(RenderKitFactory.HTML_BASIC_RENDER_KIT);
+ setNavigationHandler(new MockNavigationHandler());
+ setStateManager(new MockStateManager());
+ setSupportedLocales(new ArrayList());
+ validators = new HashMap();
+ setViewHandler(new MockViewHandler());
+
+ // Register the standard by-id converters
+ addConverter("javax.faces.BigDecimal",
+ "javax.faces.convert.BigDecimalConverter");
+ addConverter("javax.faces.BigInteger",
+ "javax.faces.convert.BigIntegerConverter");
+ addConverter("javax.faces.Boolean",
+ "javax.faces.convert.BooleanConverter");
+ addConverter("javax.faces.Byte", "javax.faces.convert.ByteConverter");
+ addConverter("javax.faces.Character",
+ "javax.faces.convert.CharacterConverter");
+ addConverter("javax.faces.DateTime",
+ "javax.faces.convert.DateTimeConverter");
+ addConverter("javax.faces.Double",
+ "javax.faces.convert.DoubleConverter");
+ addConverter("javax.faces.Float", "javax.faces.convert.FloatConverter");
+ addConverter("javax.faces.Integer",
+ "javax.faces.Convert.IntegerConverter");
+ addConverter("javax.faces.Long", "javax.faces.convert.LongConverter");
+ addConverter("javax.faces.Number",
+ "javax.faces.convert.NumberConverter");
+ addConverter("javax.faces.Short", "javax.faces.convert.ShortConverter");
+
+ // Register the standard by-type converters
+ addConverter(Boolean.class, "javax.faces.convert.BooleanConverter");
+ addConverter(Boolean.TYPE, "javax.faces.convert.BooleanConverter");
+ addConverter(Byte.class, "javax.faces.convert.ByteConverter");
+ addConverter(Byte.TYPE, "javax.faces.convert.ByteConverter");
+ addConverter(Character.class, "javax.faces.convert.CharacterConverter");
+ addConverter(Character.TYPE, "javax.faces.convert.CharacterConverter");
+ addConverter(Double.class, "javax.faces.convert.DoubleConverter");
+ addConverter(Double.TYPE, "javax.faces.convert.DoubleConverter");
+ addConverter(Float.class, "javax.faces.convert.FloatConverter");
+ addConverter(Float.TYPE, "javax.faces.convert.FloatConverter");
+ addConverter(Integer.class, "javax.faces.convert.IntegerConverter");
+ addConverter(Integer.TYPE, "javax.faces.convert.IntegerConverter");
+ addConverter(Long.class, "javax.faces.convert.LongConverter");
+ addConverter(Long.TYPE, "javax.faces.convert.LongConverter");
+ addConverter(Short.class, "javax.faces.convert.ShortConverter");
+ addConverter(Short.TYPE, "javax.faces.convert.ShortConverter");
+
+ }
+
+ // ----------------------------------------------------- Mock Object Methods
+
+ // ------------------------------------------------------ Instance Variables
+
+ private ActionListener actionListener = null;
+ private Map components = null;
+ private Map converters = null; // By id
+ private Map converters1 = null; // By type
+ private Locale defaultLocale = null;
+ private String defaultRenderKitId = null;
+ private String messageBundle = null;
+ private NavigationHandler navigationHandler = null;
+ private StateManager stateManager = null;
+ private Collection supportedLocales = null;
+ private Map validators = null;
+ private ViewHandler viewHandler = null;
+
+ // ----------------------------------------------------- Application Methods
+
+ /** {@inheritDoc} */
+ public ActionListener getActionListener()
+ {
+
+ return this.actionListener;
+
+ }
+
+ /** {@inheritDoc} */
+ public void setActionListener(ActionListener actionListener)
+ {
+ this.actionListener = actionListener;
+ }
+
+ /** {@inheritDoc} */
+ public Locale getDefaultLocale()
+ {
+
+ return this.defaultLocale;
+
+ }
+
+ /** {@inheritDoc} */
+ public void setDefaultLocale(Locale defaultLocale)
+ {
+
+ this.defaultLocale = defaultLocale;
+
+ }
+
+ /** {@inheritDoc} */
+ public String getDefaultRenderKitId()
+ {
+
+ return this.defaultRenderKitId;
+
+ }
+
+ /** {@inheritDoc} */
+ public void setDefaultRenderKitId(String defaultRenderKitId)
+ {
+
+ this.defaultRenderKitId = defaultRenderKitId;
+
+ }
+
+ /** {@inheritDoc} */
+ public String getMessageBundle()
+ {
+
+ return this.messageBundle;
+
+ }
+
+ /** {@inheritDoc} */
+ public void setMessageBundle(String messageBundle)
+ {
+
+ this.messageBundle = messageBundle;
+
+ }
+
+ /** {@inheritDoc} */
+ public NavigationHandler getNavigationHandler()
+ {
+
+ return this.navigationHandler;
+
+ }
+
+ /** {@inheritDoc} */
+ public void setNavigationHandler(NavigationHandler navigationHandler)
+ {
+
+ this.navigationHandler = navigationHandler;
+
+ }
+
+ /** {@inheritDoc} */
+ public StateManager getStateManager()
+ {
+
+ return this.stateManager;
+
+ }
+
+ /** {@inheritDoc} */
+ public void setStateManager(StateManager stateManager)
+ {
+
+ this.stateManager = stateManager;
+
+ }
+
+ /** {@inheritDoc} */
+ public Iterator getSupportedLocales()
+ {
+
+ return this.supportedLocales.iterator();
+
+ }
+
+ /** {@inheritDoc} */
+ public void setSupportedLocales(Collection supportedLocales)
+ {
+
+ this.supportedLocales = supportedLocales;
+
+ }
+
+ /** {@inheritDoc} */
+ public ViewHandler getViewHandler()
+ {
+
+ return this.viewHandler;
+
+ }
+
+ /** {@inheritDoc} */
+ public void setViewHandler(ViewHandler viewHandler)
+ {
+
+ this.viewHandler = viewHandler;
+
+ }
+
+ /** {@inheritDoc} */
+ public void addComponent(String componentType, String componentClass)
+ {
+
+ components.put(componentType, componentClass);
+
+ }
+
+ /** {@inheritDoc} */
+ public UIComponent createComponent(String componentType)
+ {
+
+ if (componentType == null)
+ {
+ throw new NullPointerException("Requested component type is null");
+ }
+ String componentClass = (String) components.get(componentType);
+ if (componentClass == null)
+ {
+ throw new FacesException(
+ "No component class registered for component type '"
+ + componentType + "'");
+ }
+ try
+ {
+ Class clazz = Class.forName(componentClass);
+ return ((UIComponent) clazz.newInstance());
+ }
+ catch (Exception e)
+ {
+ throw new FacesException(e);
+ }
+
+ }
+
+
+ /** {@inheritDoc} */
+ public Iterator getComponentTypes()
+ {
+
+ return (components.keySet().iterator());
+
+ }
+
+ /** {@inheritDoc} */
+ public void addConverter(String converterId, String converterClass)
+ {
+
+ converters.put(converterId, converterClass);
+
+ }
+
+ /** {@inheritDoc} */
+ public void addConverter(Class targetClass, String converterClass)
+ {
+
+ converters1.put(targetClass, converterClass);
+
+ }
+
+ /** {@inheritDoc} */
+ public Converter createConverter(String converterId)
+ {
+
+ String converterClass = (String) converters.get(converterId);
+ if (converterClass == null)
+ {
+ return null;
+ }
+ try
+ {
+ Class clazz = Class.forName(converterClass);
+ return ((Converter) clazz.newInstance());
+ }
+ catch (Exception e)
+ {
+ throw new FacesException(e);
+ }
+
+ }
+
+ /** {@inheritDoc} */
+ public Converter createConverter(Class targetClass)
+ {
+
+ String converterClass = (String) converters1.get(targetClass);
+ if (converterClass == null)
+ {
+ return null;
+ }
+ try
+ {
+ Class clazz = Class.forName(converterClass);
+ return ((Converter) clazz.newInstance());
+ }
+ catch (Exception e)
+ {
+ throw new FacesException(e);
+ }
+
+ }
+
+ /** {@inheritDoc} */
+ public Iterator getConverterIds()
+ {
+
+ return (converters.keySet().iterator());
+
+ }
+
+ /** {@inheritDoc} */
+ public Iterator getConverterTypes()
+ {
+
+ return (converters1.keySet().iterator());
+
+ }
+
+
+ /** {@inheritDoc} */
+ public void addValidator(String validatorId, String validatorClass)
+ {
+
+ validators.put(validatorId, validatorClass);
+
+ }
+
+ /** {@inheritDoc} */
+ public Validator createValidator(String validatorId)
+ {
+
+ String validatorClass = (String) validators.get(validatorId);
+ try
+ {
+ Class clazz = Class.forName(validatorClass);
+ return ((Validator) clazz.newInstance());
+ }
+ catch (Exception e)
+ {
+ throw new FacesException(e);
+ }
+
+ }
+
+ /** {@inheritDoc} */
+ public Iterator getValidatorIds()
+ {
+ return (validators.keySet().iterator());
+ }
+
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/mock/MockApplication12.java b/test30/src/main/java/org/apache/myfaces/test/mock/MockApplication12.java
new file mode 100644
index 0000000..d13da73
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/mock/MockApplication12.java
@@ -0,0 +1,307 @@
+/*
+ * 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.myfaces.test.mock;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+import javax.el.ArrayELResolver;
+import javax.el.BeanELResolver;
+import javax.el.CompositeELResolver;
+import javax.el.ELContextListener;
+import javax.el.ELException;
+import javax.el.ELResolver;
+import javax.el.ExpressionFactory;
+import javax.el.ListELResolver;
+import javax.el.MapELResolver;
+import javax.el.ResourceBundleELResolver;
+import javax.el.ValueExpression;
+import javax.faces.FacesException;
+import javax.faces.component.UIComponent;
+import javax.faces.component.UIViewRoot;
+import javax.faces.context.FacesContext;
+
+import org.apache.myfaces.test.config.ResourceBundleVarNames;
+import org.apache.myfaces.test.el.FacesImplicitObjectELResolver;
+import org.apache.myfaces.test.el.FacesResourceBundleELResolver;
+import org.apache.myfaces.test.el.FacesScopedAttributeELResolver;
+import org.apache.myfaces.test.el.MockExpressionFactory;
+import org.apache.myfaces.test.el.ReservedWordsELResolver;
+
+/**
+ * <p>Mock implementation of <code>Application</code> that includes the semantics
+ * added by JavaServer Faces 1.2.</p>
+ *
+ * $Id$
+ *
+ * @since 1.0.0
+ */
+public class MockApplication12 extends MockApplication
+{
+
+ // ------------------------------------------------------------ Constructors
+
+ /**
+ * <p>Construct a default instance.</p>
+ */
+ public MockApplication12()
+ {
+
+ super();
+
+ // Configure our expression factory and EL resolvers
+ expressionFactory = new MockExpressionFactory();
+
+ }
+
+ // ------------------------------------------------------ Instance Variables
+
+ /**
+ * <p>A list of resource bundles configured for this application.</p>
+ */
+ private Map bundles = new HashMap();
+
+ /**
+ * <p>The set of configured ELContextListener instances.</p>
+ */
+ private List elContextListeners = new ArrayList();
+
+ /**
+ * <p>Expression factory for this instance.</p>
+ */
+ private ExpressionFactory expressionFactory = null;
+
+ /**
+ * <p>The configured composite resolver to be returned by <code>getELResolver()</code>.
+ * This value is lazily instantiated.</p>
+ */
+ private ELResolver resolver = null;
+
+ /**
+ * <p>The set of ELResolver instances configured on this instance.</p>
+ */
+ private List resolvers = new ArrayList();
+
+ // ----------------------------------------------------- Mock Object Methods
+
+ /**
+ * <p>Add the specified resource bundle to those associated with
+ * this application.</p>
+ *
+ * @param name Name under which to add this resource bundle
+ * @param bundle ResourceBundle to add
+ */
+ public void addResourceBundle(String name, ResourceBundle bundle)
+ {
+ bundles.put(name, bundle);
+ }
+
+ /**
+ * <p>Return a <code>Map</code> of the resource bundles configured
+ * for this application, keyed by name.</p>
+ */
+ public Map getResourceBundles()
+ {
+ return bundles;
+ }
+
+ /**
+ * Set the current ExpressionFactory to be returned by this mock object
+ *
+ * @param expressionFactory
+ */
+ public void setExpressionFactory(ExpressionFactory expressionFactory)
+ {
+ this.expressionFactory = expressionFactory;
+ }
+
+ // ----------------------------------------------------- Application Methods
+
+ /** {@inheritDoc} */
+ public void addELContextListener(ELContextListener listener)
+ {
+
+ elContextListeners.add(listener);
+
+ }
+
+ /** {@inheritDoc} */
+ public void addELResolver(ELResolver resolver)
+ {
+
+ // Simulate the restriction that you cannot add resolvers after
+ // the first request has been processed.
+ if (this.resolver != null)
+ {
+ throw new IllegalStateException("Cannot add resolvers now");
+ }
+
+ resolvers.add(resolver);
+
+ }
+
+ /** {@inheritDoc} */
+ public UIComponent createComponent(ValueExpression expression,
+ FacesContext context, String componentType)
+ {
+
+ UIComponent component = null;
+ try
+ {
+ component = (UIComponent) expression.getValue(context
+ .getELContext());
+ if (component == null)
+ {
+ component = createComponent(componentType);
+ expression.setValue(context.getELContext(), component);
+ }
+
+ }
+ catch (Exception e)
+ {
+ throw new FacesException(e);
+ }
+ return component;
+
+ }
+
+ /** {@inheritDoc} */
+ public Object evaluateExpressionGet(FacesContext context,
+ String expression, Class expectedType) throws ELException
+ {
+
+ ValueExpression ve = getExpressionFactory().createValueExpression(
+ context.getELContext(), expression, expectedType);
+ return ve.getValue(context.getELContext());
+
+ }
+
+ /** {@inheritDoc} */
+ public ELContextListener[] getELContextListeners()
+ {
+
+ return (ELContextListener[]) elContextListeners
+ .toArray(new ELContextListener[elContextListeners.size()]);
+
+ }
+
+ /** {@inheritDoc} */
+ public ELResolver getELResolver()
+ {
+
+ if (resolver == null)
+ {
+
+ // Configure a default ELResolver per Section 5.6.2 of JSF 1.2
+ CompositeELResolver composite = new CompositeELResolver();
+
+ composite.add(new FacesImplicitObjectELResolver());
+
+ CompositeELResolver nested = new CompositeELResolver();
+ Iterator items = resolvers.iterator();
+ while (items.hasNext())
+ {
+ nested.add((ELResolver) items.next());
+ }
+ composite.add(nested);
+
+ // composite.add(new faces.ManagedBeanELResolver()); // FIXME
+ composite.add(new ResourceBundleELResolver());
+ composite.add(new FacesResourceBundleELResolver());
+ composite.add(new MapELResolver());
+ composite.add(new ListELResolver());
+ composite.add(new ArrayELResolver());
+ composite.add(new BeanELResolver());
+ composite.add(new FacesScopedAttributeELResolver());
+ composite.add(new ReservedWordsELResolver());
+
+ // Make the resolver we have configured the application wide one
+ resolver = composite;
+
+ }
+ return resolver;
+
+ }
+
+ /** {@inheritDoc} */
+ public ExpressionFactory getExpressionFactory()
+ {
+
+ return this.expressionFactory;
+
+ }
+
+ /** {@inheritDoc} */
+ public ResourceBundle getResourceBundle(FacesContext context, String name)
+ {
+
+ if ((context == null) || (name == null))
+ {
+ throw new NullPointerException();
+ }
+ Locale locale = null;
+ UIViewRoot viewRoot = context.getViewRoot();
+ if (viewRoot != null)
+ {
+ locale = viewRoot.getLocale();
+ }
+ if (locale == null)
+ {
+ locale = Locale.getDefault();
+ }
+ try
+ {
+ return ResourceBundle.getBundle(name, locale);
+ }
+ catch (MissingResourceException e)
+ {
+
+ String newName = ResourceBundleVarNames.getVarName(name);
+ if (newName == null)
+ {
+ return null;
+ }
+
+ try
+ {
+ return ResourceBundle.getBundle(newName, locale);
+ }
+ catch (MissingResourceException exc)
+ {
+ return null;
+ }
+
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void removeELContextListener(ELContextListener listener)
+ {
+
+ elContextListeners.remove(listener);
+
+ }
+
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/mock/MockApplication20.java b/test30/src/main/java/org/apache/myfaces/test/mock/MockApplication20.java
new file mode 100644
index 0000000..ad24f1a
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/mock/MockApplication20.java
@@ -0,0 +1,1188 @@
+/*
+ * 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.myfaces.test.mock;
+
+import java.beans.BeanDescriptor;
+import java.beans.BeanInfo;
+import java.lang.reflect.Constructor;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import javax.el.ELContext;
+import javax.el.ValueExpression;
+import javax.faces.FacesException;
+import javax.faces.application.Application;
+import javax.faces.application.ProjectStage;
+import javax.faces.application.Resource;
+import javax.faces.application.ResourceDependencies;
+import javax.faces.application.ResourceDependency;
+import javax.faces.application.ResourceHandler;
+import javax.faces.component.UIComponent;
+import javax.faces.component.UINamingContainer;
+import javax.faces.component.UIOutput;
+import javax.faces.component.UIViewRoot;
+import javax.faces.component.behavior.Behavior;
+import javax.faces.context.FacesContext;
+import javax.faces.convert.Converter;
+import javax.faces.event.AbortProcessingException;
+import javax.faces.event.ComponentSystemEventListener;
+import javax.faces.event.ListenerFor;
+import javax.faces.event.ListenersFor;
+import javax.faces.event.SystemEvent;
+import javax.faces.event.SystemEventListener;
+import javax.faces.event.SystemEventListenerHolder;
+import javax.faces.render.Renderer;
+import javax.faces.validator.Validator;
+import javax.faces.view.ViewDeclarationLanguage;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.myfaces.test.mock.resource.MockResourceHandler;
+
+/**
+ * <p>Mock implementation of <code>Application</code> that includes the semantics
+ * added by JavaServer Faces 2.0.</p>
+ *
+ * @author Leonardo Uribe
+ * @since 1.0.0
+ *
+ */
+public class MockApplication20 extends MockApplication12
+{
+
+ // ------------------------------------------------------------ Constructors
+
+ public MockApplication20()
+ {
+ super();
+
+ // install the 2.0-ViewHandler-Mock
+ this.setViewHandler(new MockViewHandler20());
+ this.setResourceHandler(new MockResourceHandler());
+ }
+
+ private static class SystemListenerEntry
+ {
+ private List<SystemEventListener> _lstSystemEventListener;
+ private Map<Class<?>, List<SystemEventListener>> _sourceClassMap;
+
+ public SystemListenerEntry()
+ {
+ }
+
+ public void addListener(SystemEventListener listener)
+ {
+ assert listener != null;
+
+ addListenerNoDuplicate(getAnySourceListenersNotNull(), listener);
+ }
+
+ public void addListener(SystemEventListener listener, Class<?> source)
+ {
+ assert listener != null;
+
+ if (source == null)
+ {
+ addListener(listener);
+ }
+ else
+ {
+ addListenerNoDuplicate(
+ getSpecificSourceListenersNotNull(source), listener);
+ }
+ }
+
+ public void removeListener(SystemEventListener listener)
+ {
+ assert listener != null;
+
+ if (_lstSystemEventListener != null)
+ {
+ _lstSystemEventListener.remove(listener);
+ }
+ }
+
+ public void removeListener(SystemEventListener listener,
+ Class<?> sourceClass)
+ {
+ assert listener != null;
+
+ if (sourceClass == null)
+ {
+ removeListener(listener);
+ }
+ else
+ {
+ if (_sourceClassMap != null)
+ {
+ List<SystemEventListener> listeners = _sourceClassMap
+ .get(sourceClass);
+ if (listeners != null)
+ {
+ listeners.remove(listener);
+ }
+ }
+ }
+ }
+
+ public void publish(Class<? extends SystemEvent> systemEventClass,
+ Class<?> classSource, Object source, SystemEvent event)
+ {
+ if (source != null && _sourceClassMap != null)
+ {
+ event = _traverseListenerList(_sourceClassMap.get(classSource),
+ systemEventClass, source, event);
+ }
+
+ _traverseListenerList(_lstSystemEventListener, systemEventClass,
+ source, event);
+ }
+
+ private void addListenerNoDuplicate(
+ List<SystemEventListener> listeners,
+ SystemEventListener listener)
+ {
+ if (!listeners.contains(listener))
+ {
+ listeners.add(listener);
+ }
+ }
+
+ private synchronized List<SystemEventListener> getAnySourceListenersNotNull()
+ {
+ if (_lstSystemEventListener == null)
+ {
+ /*
+ * TODO: Check if modification occurs often or not, might have to use a synchronized list instead.
+ *
+ * Registrations found:
+ */
+ _lstSystemEventListener = new CopyOnWriteArrayList<SystemEventListener>();
+ }
+
+ return _lstSystemEventListener;
+ }
+
+ private synchronized List<SystemEventListener> getSpecificSourceListenersNotNull(
+ Class<?> sourceClass)
+ {
+ if (_sourceClassMap == null)
+ {
+ _sourceClassMap = new ConcurrentHashMap<Class<?>, List<SystemEventListener>>();
+ }
+
+ List<SystemEventListener> list = _sourceClassMap.get(sourceClass);
+ if (list == null)
+ {
+ /*
+ * TODO: Check if modification occurs often or not, might have to use a synchronized list instead.
+ *
+ * Registrations found:
+ */
+ list = new CopyOnWriteArrayList<SystemEventListener>();
+ _sourceClassMap.put(sourceClass, list);
+ }
+
+ return list;
+ }
+ }
+
+ // ------------------------------------------------------ Instance Variables
+
+ private static final Log log = LogFactory.getLog(MockApplication20.class);
+
+ private final Map<Class<? extends SystemEvent>, SystemListenerEntry> _systemEventListenerClassMap =
+ new ConcurrentHashMap<Class<? extends SystemEvent>, SystemListenerEntry>();
+
+ private Map<String, String> _defaultValidatorsIds = new HashMap<String, String>();
+
+ private ProjectStage _projectStage;
+
+ private final Map<String, Class<?>> _behaviorClassMap = new ConcurrentHashMap<String, Class<?>>();
+
+ private final Map<String, Class<?>> _validatorClassMap = new ConcurrentHashMap<String, Class<?>>();
+
+ private ResourceHandler _resourceHandler;
+
+ // ----------------------------------------------------- Mock Object Methods
+
+ public Map<String, String> getDefaultValidatorInfo()
+ {
+ return Collections.unmodifiableMap(_defaultValidatorsIds);
+ }
+
+ private void _handleAnnotations(FacesContext context, Object inspected, UIComponent component)
+ {
+ // determine the ProjectStage setting via the given FacesContext
+ // note that a local getProjectStage() could cause problems in wrapped environments
+ boolean isProduction = context.isProjectStage(ProjectStage.Production);
+
+ Class<?> inspectedClass = inspected.getClass();
+ _handleListenerForAnnotations(context, inspected, inspectedClass, component, isProduction);
+
+ _handleResourceDependencyAnnotations(context, inspectedClass, component, isProduction);
+ }
+
+ private void _handleListenerForAnnotations(FacesContext context, Object inspected, Class<?> inspectedClass,
+ UIComponent component, boolean isProduction)
+ {
+ List<ListenerFor> listenerForList = null;
+
+ if(listenerForList == null) //not in production or the class hasn't been inspected yet
+ {
+ ListenerFor listener = inspectedClass.getAnnotation(ListenerFor.class);
+ ListenersFor listeners = inspectedClass.getAnnotation(ListenersFor.class);
+ if(listener != null || listeners != null)
+ {
+ //listeners were found using one or both annotations, create and build a new list
+ listenerForList = new ArrayList<ListenerFor>();
+
+ if(listener != null)
+ {
+ listenerForList.add(listener);
+ }
+
+ if(listeners != null)
+ {
+ listenerForList.addAll(Arrays.asList(listeners.value()));
+ }
+ }
+ }
+
+ if (listenerForList != null) //listeners were found through inspection or from cache, handle them
+ {
+ for (ListenerFor listenerFor : listenerForList)
+ {
+ _handleListenerFor(context, inspected, component, listenerFor);
+ }
+ }
+ }
+
+ private void _handleListenerFor(FacesContext context, Object inspected, UIComponent component,
+ ListenerFor annotation)
+ {
+ // If this annotation is not present on the class in question, no action must be taken.
+ if (annotation != null)
+ {
+ // Determine the "target" on which to call subscribeToEvent.
+ // If the class to which this annotation is attached implements ComponentSystemEventListener
+ if (inspected instanceof ComponentSystemEventListener)
+ {
+ // If the class to which this annotation is attached is a UIComponent instance, "target" is the
+ // UIComponent instance.
+
+ // If the class to which this annotation is attached is a Renderer instance, "target" is the
+ // UIComponent instance.
+
+ /*
+ * If "target" is a UIComponent call UIComponent.subscribeToEvent(Class, ComponentSystemEventListener)
+ * passing the systemEventClass() of the annotation as the first argument and the instance of the class
+ * to which this annotation is attached (which must implement ComponentSystemEventListener) as the
+ * second argument.
+ */
+ component.subscribeToEvent(annotation.systemEventClass(), (ComponentSystemEventListener) inspected);
+ }
+ // If the class to which this annotation is attached implements SystemEventListener and does not implement
+ // ComponentSystemEventListener, "target" is the Application instance.
+ else if (component instanceof SystemEventListener)
+ {
+ // use the Application object from the FacesContext (note that a
+ // direct use of subscribeToEvent() could cause problems if the
+ // Application is wrapped)
+ Application application = context.getApplication();
+
+ // If "target" is the Application instance, inspect the value of the sourceClass() annotation attribute
+ // value.
+ if (Void.class.equals(annotation.sourceClass()))
+ {
+ /*
+ * If the value is Void.class, call Application.subscribeToEvent(Class, SystemEventListener),
+ * passing the value of systemEventClass() as the first argument and the instance of the class to
+ * which this annotation is attached (which must implement SystemEventListener) as the second
+ * argument.
+ */
+ application.subscribeToEvent(annotation.systemEventClass(), (SystemEventListener) inspected);
+ }
+ else
+ {
+ /*
+ * Otherwise, call Application.subscribeToEvent(Class, Class, SystemEventListener), passing the
+ * value of systemEventClass() as the first argument, the value of sourceClass() as the second
+ * argument, and the instance of the class to which this annotation is attached (which must
+ * implement SystemEventListener) as the third argument.
+ */
+ application.subscribeToEvent(annotation.systemEventClass(), annotation.sourceClass(),
+ (SystemEventListener) inspected);
+ }
+ }
+
+ /*
+ * If the class to which this annotation is attached implements ComponentSystemEventListener and is neither
+ * an instance of Renderer nor UIComponent, the action taken is unspecified. This case must not trigger any
+ * kind of error.
+ */
+ }
+ }
+
+ private void _handleResourceDependencyAnnotations(FacesContext context, Class<?> inspectedClass,
+ UIComponent component, boolean isProduction)
+ {
+ List<ResourceDependency> dependencyList = null;
+
+ if(dependencyList == null) //not in production or the class hasn't been inspected yet
+ {
+ ResourceDependency dependency = inspectedClass.getAnnotation(ResourceDependency.class);
+ ResourceDependencies dependencies = inspectedClass.getAnnotation(ResourceDependencies.class);
+ if(dependency != null || dependencies != null)
+ {
+ //resource dependencies were found using one or both annotations, create and build a new list
+ dependencyList = new ArrayList<ResourceDependency>();
+
+ if(dependency != null)
+ {
+ dependencyList.add(dependency);
+ }
+
+ if(dependencies != null)
+ {
+ dependencyList.addAll(Arrays.asList(dependencies.value()));
+ }
+ }
+ }
+
+ if (dependencyList != null) //resource dependencies were found through inspection or from cache, handle them
+ {
+ for (ResourceDependency dependency : dependencyList)
+ {
+ _handleResourceDependency(context, component, dependency);
+ }
+ }
+ }
+
+ private void _handleResourceDependency(FacesContext context, UIComponent component, ResourceDependency annotation)
+ {
+ // If this annotation is not present on the class in question, no action must be taken.
+ if (annotation != null)
+ {
+ // Create a UIOutput instance by passing javax.faces.Output. to
+ // Application.createComponent(java.lang.String).
+ UIOutput output = (UIOutput) createComponent(UIOutput.COMPONENT_TYPE);
+
+ // Get the annotation instance from the class and obtain the values of the name, library, and
+ // target attributes.
+ String name = annotation.name();
+ if (name != null && name.length() > 0)
+ {
+ name = _ELText.parse(getExpressionFactory(), context.getELContext(), name).toString(
+ context.getELContext());
+ }
+
+ // Obtain the renderer-type for the resource name by passing name to
+ // ResourceHandler.getRendererTypeForResourceName(java.lang.String).
+ // (note that we can not use this.getResourceHandler(), because the Application might be wrapped)
+ String rendererType = context.getApplication().getResourceHandler().getRendererTypeForResourceName(name);
+
+ // Call setRendererType on the UIOutput instance, passing the renderer-type.
+ output.setRendererType(rendererType);
+
+ // Obtain the Map of attributes from the UIOutput component by calling UIComponent.getAttributes().
+ Map<String, Object> attributes = output.getAttributes();
+
+ // Store the name into the attributes Map under the key "name".
+ attributes.put("name", name);
+
+ // If library is the empty string, let library be null.
+ String library = annotation.library();
+ if (library != null && library.length() > 0)
+ {
+ library = _ELText.parse(getExpressionFactory(), context.getELContext(), library).toString(
+ context.getELContext());
+ // If library is non-null, store it under the key "library".
+ if ("this".equals(library))
+ {
+ // Special "this" behavior
+ Resource resource = (Resource)component.getAttributes().get(Resource.COMPONENT_RESOURCE_KEY);
+ if (resource != null)
+ {
+ attributes.put("library", resource.getLibraryName());
+ }
+ }
+ else
+ {
+ attributes.put("library", library);
+ }
+ }
+
+ // If target is the empty string, let target be null.
+ String target = annotation.target();
+ if (target != null && target.length() > 0)
+ {
+ target = _ELText.parse(getExpressionFactory(), context.getELContext(), target).toString(
+ context.getELContext());
+ // If target is non-null, store it under the key "target".
+ attributes.put("target", target);
+ context.getViewRoot().addComponentResource(context, output, target);
+ }
+ else
+ {
+ // Otherwise, if target is null, call UIViewRoot.addComponentResource(javax.faces.context.FacesContext,
+ // javax.faces.component.UIComponent), passing the UIOutput instance as the second argument.
+ context.getViewRoot().addComponentResource(context, output);
+ }
+ }
+ }
+
+ private void _inspectRenderer(FacesContext context, UIComponent component, String componentType,
+ String rendererType)
+ {
+ /*
+ * The Renderer instance to inspect must be obtained by calling FacesContext.getRenderKit() and calling
+ * RenderKit.getRenderer(java.lang.String, java.lang.String) on the result, passing the argument componentFamily
+ * of the newly created component as the first argument and the argument rendererType as the second argument.
+ */
+ Renderer renderer = context.getRenderKit().getRenderer(component.getFamily(), rendererType);
+ if (renderer == null)
+ {
+ // If no such Renderer can be found, a message must be logged with a helpful error message.
+ log.error("renderer cannot be found for component type " + componentType + " and renderer type "
+ + rendererType);
+ }
+ else
+ {
+ // Otherwise, UIComponent.setRendererType(java.lang.String) must be called on the newly created
+ // UIComponent instance, passing the argument rendererType as the argument.
+ component.setRendererType(rendererType);
+
+ /*
+ * except the Renderer for the component to be returned must be inspected for the annotations mentioned in
+ * createComponent(ValueExpression, FacesContext, String) as specified in the documentation for that method.
+ */
+ _handleAnnotations(context, renderer, component);
+ }
+ }
+
+ private static SystemEvent _traverseListenerList(
+ List<? extends SystemEventListener> listeners,
+ Class<? extends SystemEvent> systemEventClass, Object source,
+ SystemEvent event)
+ {
+ if (listeners != null && !listeners.isEmpty())
+ {
+ for (SystemEventListener listener : listeners)
+ {
+ // Call SystemEventListener.isListenerForSource(java.lang.Object), passing the source argument.
+ // If this returns false, take no action on the listener.
+ if (listener.isListenerForSource(source))
+ {
+ // Otherwise, if the event to be passed to the listener instances has not yet been constructed,
+ // construct the event, passing source as the argument to the one-argument constructor that takes
+ // an Object. This same event instance must be passed to all listener instances.
+ event = _createEvent(systemEventClass, source, event);
+
+ // Call SystemEvent.isAppropriateListener(javax.faces.event.FacesListener), passing the listener
+ // instance as the argument. If this returns false, take no action on the listener.
+ if (event.isAppropriateListener(listener))
+ {
+ // Call SystemEvent.processListener(javax.faces.event.FacesListener), passing the listener
+ // instance.
+ event.processListener(listener);
+ }
+ }
+ }
+ }
+
+ return event;
+ }
+
+ private static SystemEvent _createEvent(
+ Class<? extends SystemEvent> systemEventClass, Object source,
+ SystemEvent event)
+ {
+ if (event == null)
+ {
+ try
+ {
+ Constructor<?>[] constructors = systemEventClass.getConstructors();
+ Constructor<? extends SystemEvent> constructor = null;
+ for (Constructor<?> c : constructors)
+ {
+ if (c.getParameterTypes().length == 1)
+ {
+ // Safe cast, since the constructor belongs
+ // to a class of type SystemEvent
+ constructor = (Constructor<? extends SystemEvent>) c;
+ break;
+ }
+ }
+ if (constructor != null)
+ {
+ event = constructor.newInstance(source);
+ }
+
+ }
+ catch (Exception e)
+ {
+ throw new FacesException("Couldn't instanciate system event of type " + systemEventClass.getName(), e);
+ }
+ }
+
+ return event;
+ }
+
+ private void checkNull(final Object param, final String paramName)
+ {
+ if (param == null)
+ {
+ throw new NullPointerException(paramName + " cannot be null.");
+ }
+ }
+
+ private void checkEmpty(final String param, final String paramName)
+ {
+ if (param.length() == 0)
+ {
+ throw new NullPointerException("String " + paramName
+ + " cannot be empty.");
+ }
+ }
+
+ public void publishEvent(FacesContext facesContext,
+ Class<? extends SystemEvent> systemEventClass,
+ Class<?> sourceBaseType, Object source)
+ {
+ checkNull(systemEventClass, "systemEventClass");
+ checkNull(source, "source");
+
+ //Call events only if event processing is enabled.
+ if (!facesContext.isProcessingEvents())
+ {
+ return;
+ }
+
+ try
+ {
+ SystemEvent event = null;
+ if (source instanceof SystemEventListenerHolder)
+ {
+ SystemEventListenerHolder holder = (SystemEventListenerHolder) source;
+
+ // If the source argument implements SystemEventListenerHolder, call
+ // SystemEventListenerHolder.getListenersForEventClass(java.lang.Class) on it,
+ // passing the systemEventClass
+ // argument. If the list is not empty, perform algorithm traverseListenerList on the list.
+ event = _traverseListenerList(holder
+ .getListenersForEventClass(systemEventClass),
+ systemEventClass, source, event);
+ }
+
+ UIViewRoot uiViewRoot = facesContext.getViewRoot();
+ if (uiViewRoot != null)
+ {
+ //Call listeners on view level
+ event = _traverseListenerList(uiViewRoot.getViewListenersForEventClass(systemEventClass),
+ systemEventClass, source, event);
+ }
+
+ SystemListenerEntry systemListenerEntry = _systemEventListenerClassMap
+ .get(systemEventClass);
+ if (systemListenerEntry != null)
+ {
+ systemListenerEntry.publish(systemEventClass, sourceBaseType,
+ source, event);
+ }
+ }
+ catch (AbortProcessingException e)
+ {
+ // If the act of invoking the processListener method causes an AbortProcessingException to be thrown,
+ // processing of the listeners must be aborted, no further processing of the listeners for this event must
+ // take place, and the exception must be logged with Level.SEVERE.
+ log.error("Event processing was aborted", e);
+ }
+ }
+
+ public void publishEvent(FacesContext facesContext,
+ Class<? extends SystemEvent> systemEventClass, Object source)
+ {
+ publishEvent(facesContext, systemEventClass, source.getClass(), source);
+ }
+
+ public ProjectStage getProjectStage()
+ {
+ // If the value has already been determined by a previous call to this
+ // method, simply return that value.
+ if (_projectStage == null)
+ {
+
+ FacesContext context = FacesContext.getCurrentInstance();
+ String stageName = context.getExternalContext().getInitParameter(
+ ProjectStage.PROJECT_STAGE_PARAM_NAME);
+
+ // If a value is found found
+ if (stageName != null)
+ {
+ /*
+ * see if an enum constant can be obtained by calling ProjectStage.valueOf(), passing the value from the
+ * initParamMap. If this succeeds without exception, save the value and return it.
+ */
+ try
+ {
+ _projectStage = ProjectStage.valueOf(stageName);
+ return _projectStage;
+ }
+ catch (IllegalArgumentException e)
+ {
+ //log.log(Level.SEVERE, "Couldn't discover the current project stage", e);
+ }
+ }
+
+ _projectStage = ProjectStage.Production;
+ }
+
+ return _projectStage;
+ }
+
+ public void addBehavior(String behaviorId, String behaviorClass)
+ {
+ checkNull(behaviorId, "behaviorId");
+ checkEmpty(behaviorId, "behaviorId");
+ checkNull(behaviorClass, "behaviorClass");
+ checkEmpty(behaviorClass, "behaviorClass");
+
+ try
+ {
+ _behaviorClassMap.put(behaviorId, Class.forName(behaviorClass));
+ }
+ catch (ClassNotFoundException ignore)
+ {
+
+ }
+
+ }
+
+ public Iterator<String> getBehaviorIds()
+ {
+ return _behaviorClassMap.keySet().iterator();
+ }
+
+ public Behavior createBehavior(String behaviorId) throws FacesException
+ {
+ checkNull(behaviorId, "behaviorId");
+ checkEmpty(behaviorId, "behaviorId");
+
+ final Class<?> behaviorClass = this._behaviorClassMap.get(behaviorId);
+ if (behaviorClass == null)
+ {
+ throw new FacesException(
+ "Could not find any registered behavior-class for behaviorId : "
+ + behaviorId);
+ }
+
+ try
+ {
+ final Behavior behavior = (Behavior) behaviorClass.newInstance();
+ _handleAttachedResourceDependencyAnnotations(FacesContext.getCurrentInstance(), behaviorClass);
+ return behavior;
+ }
+ catch (Exception e)
+ {
+ throw new FacesException("Could not instantiate behavior: "
+ + behaviorClass, e);
+ }
+ }
+
+ @Override
+ public void addValidator(String validatorId, String validatorClass)
+ {
+ super.addValidator(validatorId, validatorClass);
+
+ try
+ {
+ _validatorClassMap.put(validatorId, Class.forName(validatorClass));
+ }
+ catch (ClassNotFoundException ex)
+ {
+ throw new FacesException(ex.getMessage());
+ }
+
+ }
+
+ public void addDefaultValidatorId(String validatorId)
+ {
+ if (_validatorClassMap.containsKey(validatorId))
+ {
+ _defaultValidatorsIds.put(validatorId, _validatorClassMap.get(
+ validatorId).getName());
+ }
+ }
+
+ public final ResourceHandler getResourceHandler()
+ {
+ return _resourceHandler;
+ }
+
+ public final void setResourceHandler(ResourceHandler resourceHandler)
+ {
+ checkNull(resourceHandler, "resourceHandler");
+
+ _resourceHandler = resourceHandler;
+ }
+
+ public void subscribeToEvent(Class<? extends SystemEvent> systemEventClass,
+ SystemEventListener listener)
+ {
+ subscribeToEvent(systemEventClass, null, listener);
+ }
+
+ public void subscribeToEvent(Class<? extends SystemEvent> systemEventClass,
+ Class<?> sourceClass, SystemEventListener listener)
+ {
+ checkNull(systemEventClass, "systemEventClass");
+ checkNull(listener, "listener");
+
+ SystemListenerEntry systemListenerEntry;
+ synchronized (_systemEventListenerClassMap)
+ {
+ systemListenerEntry = _systemEventListenerClassMap
+ .get(systemEventClass);
+ if (systemListenerEntry == null)
+ {
+ systemListenerEntry = new SystemListenerEntry();
+ _systemEventListenerClassMap.put(systemEventClass,
+ systemListenerEntry);
+ }
+ }
+
+ systemListenerEntry.addListener(listener, sourceClass);
+ }
+
+ public void unsubscribeFromEvent(
+ Class<? extends SystemEvent> systemEventClass,
+ SystemEventListener listener)
+ {
+ unsubscribeFromEvent(systemEventClass, null, listener);
+ }
+
+ public void unsubscribeFromEvent(
+ Class<? extends SystemEvent> systemEventClass,
+ Class<?> sourceClass, SystemEventListener listener)
+ {
+ checkNull(systemEventClass, "systemEventClass");
+ checkNull(listener, "listener");
+
+ SystemListenerEntry systemListenerEntry = _systemEventListenerClassMap
+ .get(systemEventClass);
+ if (systemListenerEntry != null)
+ {
+ systemListenerEntry.removeListener(listener, sourceClass);
+ }
+ }
+
+ @Override
+ public UIComponent createComponent(String componentType)
+ {
+ UIComponent component = super.createComponent(componentType);
+ _handleAnnotations(FacesContext.getCurrentInstance(), component, component);
+ return component;
+ }
+
+
+ @Override
+ public UIComponent createComponent(ValueExpression componentExpression,
+ FacesContext facesContext, String componentType)
+ throws FacesException, NullPointerException
+ {
+
+ /*
+ * Before the component instance is returned, it must be inspected for the presence of a ListenerFor (or
+ * ListenersFor) or ResourceDependency (or ResourceDependencies) annotation. If any of these annotations are
+ * present, the action listed in ListenerFor or ResourceDependency must be taken on the component, before it is
+ * returned from this method. This variant of createComponent must not inspect the Renderer for the component to
+ * be returned for any of the afore mentioned annotations. Such inspection is the province of
+ */
+
+ checkNull(componentExpression, "componentExpression");
+ checkNull(facesContext, "facesContext");
+ checkNull(componentType, "componentType");
+
+ ELContext elContext = facesContext.getELContext();
+
+ try
+ {
+ Object retVal = componentExpression.getValue(elContext);
+
+ UIComponent createdComponent;
+
+ if (retVal instanceof UIComponent)
+ {
+ createdComponent = (UIComponent) retVal;
+ _handleAnnotations(facesContext, createdComponent, createdComponent);
+ }
+ else
+ {
+ createdComponent = createComponent(componentType);
+ componentExpression.setValue(elContext, createdComponent);
+ }
+
+ return createdComponent;
+ }
+ catch (FacesException e)
+ {
+ throw e;
+ }
+ catch (Exception e)
+ {
+ throw new FacesException(e);
+ }
+ }
+
+ @Override
+ public UIComponent createComponent(ValueExpression componentExpression, FacesContext context, String componentType,
+ String rendererType)
+ {
+ // Like createComponent(ValueExpression, FacesContext, String)
+ UIComponent component = createComponent(componentExpression, context, componentType);
+
+ _inspectRenderer(context, component, componentType, rendererType);
+
+ return component;
+ }
+
+
+ @Override
+ public UIComponent createComponent(FacesContext context, String componentType, String rendererType)
+ {
+ checkNull(context, "context");
+ checkNull(componentType, "componentType");
+
+ // Like createComponent(String)
+ UIComponent component = createComponent(componentType);
+
+ _inspectRenderer(context, component, componentType, rendererType);
+
+ return component;
+ }
+
+ @Override
+ public UIComponent createComponent(FacesContext context, Resource componentResource)
+ {
+ checkNull(context, "context");
+ checkNull(componentResource, "componentResource");
+
+ UIComponent component = null;
+ Resource resource;
+ String fqcn;
+ Class<? extends UIComponent> componentClass = null;
+
+ /*
+ * Obtain a reference to the ViewDeclarationLanguage for this Application instance by calling
+ * ViewHandler.getViewDeclarationLanguage(javax.faces.context.FacesContext, java.lang.String), passing the
+ * viewId found by calling UIViewRoot.getViewId() on the UIViewRoot in the argument FacesContext.
+ */
+ UIViewRoot view = context.getViewRoot();
+ Application application = context.getApplication();
+ ViewDeclarationLanguage vdl = application.getViewHandler().getViewDeclarationLanguage(
+ context, view.getViewId());
+
+ /*
+ * Obtain a reference to the composite component metadata for this composite component by calling
+ * ViewDeclarationLanguage.getComponentMetadata(javax.faces.context.FacesContext,
+ * javax.faces.application.Resource), passing the facesContext and componentResource arguments to this method.
+ * This version of JSF specification uses JavaBeans as the API to the component metadata.
+ */
+ BeanInfo metadata = vdl.getComponentMetadata(context, componentResource);
+ if (metadata == null)
+ {
+ throw new FacesException("Could not get component metadata for "
+ + componentResource.getResourceName()
+ + ". Did you forget to specify <composite:interface>?");
+ }
+
+ /*
+ * Determine if the component author declared a component-type for this component instance by obtaining the
+ * BeanDescriptor from the component metadata and calling its getValue() method, passing
+ * UIComponent.COMPOSITE_COMPONENT_TYPE_KEY as the argument. If non-null, the result must be a ValueExpression
+ * whose value is the component-type of the UIComponent to be created for this Resource component. Call through
+ * to createComponent(java.lang.String) to create the component.
+ */
+ BeanDescriptor descriptor = metadata.getBeanDescriptor();
+ ValueExpression componentType = (ValueExpression) descriptor.getValue(UIComponent.COMPOSITE_COMPONENT_TYPE_KEY);
+ boolean annotationsApplied = false;
+ if (componentType != null)
+ {
+ component = application.createComponent((String) componentType.getValue(context.getELContext()));
+ annotationsApplied = true;
+ }
+ else
+ {
+ /*
+ * Otherwise, determine if a script based component for this Resource can be found by calling
+ * ViewDeclarationLanguage.getScriptComponentResource(javax.faces.context.FacesContext,
+ * javax.faces.application.Resource). If the result is non-null, and is a script written in one of the
+ * languages listed in JSF 4.3 of the specification prose document, create a UIComponent instance from the
+ * script resource.
+ */
+ resource = vdl.getScriptComponentResource(context, componentResource);
+ if (resource != null)
+ {
+ String name = resource.getResourceName();
+ String className = name.substring(0, name.lastIndexOf('.'));
+
+ Class clazz;
+ try
+ {
+ clazz = Class.forName(className);
+ component = (UIComponent)clazz.newInstance();
+ }
+ catch (Exception e)
+ {
+ throw new FacesException(e);
+ }
+ }
+ else
+ {
+ /*
+ * Otherwise, let library-name be the return from calling Resource.getLibraryName() on the argument
+ * componentResource and resource-name be the return from calling Resource.getResourceName() on the
+ * argument componentResource. Create a fully qualified Java class name by removing any file extension
+ * from resource-name and let fqcn be library-name + "." + resource-name. If a class with the name of
+ * fqcn cannot be found, take no action and continue to the next step. If any of InstantiationException,
+ * IllegalAccessException, or ClassCastException are thrown, wrap the exception in a FacesException and
+ * re-throw it. If any other exception is thrown, log the exception and continue to the next step.
+ */
+
+ String name = componentResource.getResourceName();
+ String className = name.substring(0, name.lastIndexOf('.'));
+ fqcn = componentResource.getLibraryName() + "." + className;
+
+ try
+ {
+ componentClass = classForName(fqcn);
+ }
+ catch (ClassNotFoundException e)
+ {
+ }
+
+ if (componentClass != null)
+ {
+ try
+ {
+ component = componentClass.newInstance();
+ }
+ catch (InstantiationException e)
+ {
+ log.error("Could not instantiate component class name = " + fqcn, e);
+ throw new FacesException("Could not instantiate component class name = " + fqcn, e);
+ }
+ catch (IllegalAccessException e)
+ {
+ log.error("Could not instantiate component class name = " + fqcn, e);
+ throw new FacesException("Could not instantiate component class name = " + fqcn, e);
+ }
+ catch (Exception e)
+ {
+ log.error("Could not instantiate component class name = " + fqcn, e);
+ }
+ }
+
+ /*
+ * If none of the previous steps have yielded a UIComponent instance, call
+ * createComponent(java.lang.String) passing "javax.faces.NamingContainer" as the argument.
+ */
+ if (component == null)
+ {
+ component = application.createComponent(UINamingContainer.COMPONENT_TYPE);
+ annotationsApplied = true;
+ }
+ }
+ }
+
+ /*
+ * Call UIComponent.setRendererType(java.lang.String) on the UIComponent instance, passing
+ * "javax.faces.Composite" as the argument.
+ */
+ component.setRendererType("javax.faces.Composite");
+
+ /*
+ * Store the argument Resource in the attributes Map of the UIComponent under the key,
+ * Resource.COMPONENT_RESOURCE_KEY.
+ */
+ component.getAttributes().put(Resource.COMPONENT_RESOURCE_KEY, componentResource);
+
+ /*
+ * Store composite component metadata in the attributes Map of the UIComponent under the key,
+ * UIComponent.BEANINFO_KEY.
+ */
+ component.getAttributes().put(UIComponent.BEANINFO_KEY, metadata);
+
+ /*
+ * Before the component instance is returned, it must be inspected for the presence of a ListenerFor annotation.
+ * If this annotation is present, the action listed in ListenerFor must be taken on the component, before it is
+ * returned from this method.
+ */
+ if (!annotationsApplied)
+ {
+ _handleAnnotations(context, component, component);
+ }
+
+ return component;
+ }
+
+ private static Class classForName(String type)
+ throws ClassNotFoundException
+ {
+ if (type == null)
+ {
+ throw new NullPointerException("type");
+ }
+ try
+ {
+ // Try WebApp ClassLoader first
+ return Class.forName(type,
+ false, // do not initialize for faster startup
+ Thread.currentThread().getContextClassLoader());
+ }
+ catch (ClassNotFoundException ignore)
+ {
+ // fallback: Try ClassLoader for ClassUtils (i.e. the myfaces.jar lib)
+ return Class.forName(type,
+ false, // do not initialize for faster startup
+ MockApplication20.class.getClassLoader());
+ }
+ }
+
+ private void _handleAttachedResourceDependencyAnnotations(FacesContext context, Object inspected)
+ {
+ if (inspected == null)
+ {
+ return;
+ }
+
+ ResourceDependency annotation = inspected.getClass().getAnnotation(ResourceDependency.class);
+
+ if (annotation == null)
+ {
+ // If the ResourceDependency annotation is not present, the argument must be inspected for the presence
+ // of the ResourceDependencies annotation.
+ ResourceDependencies dependencies = inspected.getClass().getAnnotation(ResourceDependencies.class);
+ if (dependencies != null)
+ {
+ // If the ResourceDependencies annotation is present, the action described in ResourceDependencies
+ // must be taken.
+ for (ResourceDependency dependency : dependencies.value())
+ {
+ _handleAttachedResourceDependency(context, dependency);
+ }
+ }
+ }
+ else
+ {
+ // If the ResourceDependency annotation is present, the action described in ResourceDependency must be
+ // taken.
+ _handleAttachedResourceDependency(context, annotation);
+ }
+ }
+
+ private void _handleAttachedResourceDependency(FacesContext context, ResourceDependency annotation)
+ {
+ // If this annotation is not present on the class in question, no action must be taken.
+ if (annotation != null)
+ {
+ Application application = context.getApplication();
+
+ // Create a UIOutput instance by passing javax.faces.Output. to
+ // Application.createComponent(java.lang.String).
+ UIOutput output = (UIOutput) application.createComponent(UIOutput.COMPONENT_TYPE);
+
+ // Get the annotation instance from the class and obtain the values of the name, library, and
+ // target attributes.
+ String name = annotation.name();
+ if (name != null && name.length() > 0)
+ {
+ name = _ELText.parse(getExpressionFactory(), context.getELContext(), name).toString(
+ context.getELContext());
+ }
+
+ // Obtain the renderer-type for the resource name by passing name to
+ // ResourceHandler.getRendererTypeForResourceName(java.lang.String).
+ String rendererType = application.getResourceHandler().getRendererTypeForResourceName(name);
+
+ // Call setRendererType on the UIOutput instance, passing the renderer-type.
+ output.setRendererType(rendererType);
+
+ // Obtain the Map of attributes from the UIOutput component by calling UIComponent.getAttributes().
+ Map<String, Object> attributes = output.getAttributes();
+
+ // Store the name into the attributes Map under the key "name".
+ attributes.put("name", name);
+
+ // If library is the empty string, let library be null.
+ String library = annotation.library();
+ if (library != null && library.length() > 0)
+ {
+ library = _ELText.parse(getExpressionFactory(), context.getELContext(), library).toString(
+ context.getELContext());
+ // If library is non-null, store it under the key "library".
+ attributes.put("library", library);
+ }
+
+ // If target is the empty string, let target be null.
+ String target = annotation.target();
+ if (target != null && target.length() > 0)
+ {
+ target = _ELText.parse(getExpressionFactory(), context.getELContext(), target).toString(
+ context.getELContext());
+ // If target is non-null, store it under the key "target".
+ attributes.put("target", target);
+ context.getViewRoot().addComponentResource(context, output, target);
+ }
+ else
+ {
+ // Otherwise, if target is null, call UIViewRoot.addComponentResource(javax.faces.context.FacesContext,
+ // javax.faces.component.UIComponent), passing the UIOutput instance as the second argument.
+ context.getViewRoot().addComponentResource(context, output);
+ }
+ }
+ }
+
+ @Override
+ public Converter createConverter(String converterId)
+ {
+ Converter converter = super.createConverter(converterId);
+ _handleAttachedResourceDependencyAnnotations(FacesContext.getCurrentInstance(), converter);
+ return converter;
+ }
+
+ @Override
+ public Validator createValidator(String validatorId)
+ {
+ Validator validator = super.createValidator(validatorId);
+ _handleAttachedResourceDependencyAnnotations(FacesContext.getCurrentInstance(), validator);
+ return validator;
+ }
+
+
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/mock/MockApplication22.java b/test30/src/main/java/org/apache/myfaces/test/mock/MockApplication22.java
new file mode 100644
index 0000000..68dc315
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/mock/MockApplication22.java
@@ -0,0 +1,62 @@
+/*
+ * 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.myfaces.test.mock;
+
+import javax.faces.flow.FlowHandler;
+
+/**
+ * <p>Mock implementation of <code>Application</code> that includes the semantics
+ * added by JavaServer Faces 2.0.</p>
+ *
+ * @author Leonardo Uribe
+ * @since 1.0.0
+ *
+ */
+public class MockApplication22 extends MockApplication20
+{
+ // ------------------------------------------------------------ Constructors
+
+ public MockApplication22()
+ {
+ super();
+
+ // install flow Handler
+ setFlowHandler(new MockFlowHandler());
+ }
+
+ // ------------------------------------------------------ Instance Variables
+
+ private FlowHandler _flowHandler;
+
+ // ----------------------------------------------------- Mock Object Methods
+
+ @Override
+ public FlowHandler getFlowHandler()
+ {
+ return _flowHandler;
+ }
+
+ @Override
+ public void setFlowHandler(FlowHandler flowHandler)
+ {
+ this._flowHandler = flowHandler;
+ }
+
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/mock/MockApplication30.java b/test30/src/main/java/org/apache/myfaces/test/mock/MockApplication30.java
new file mode 100644
index 0000000..382c8f0
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/mock/MockApplication30.java
@@ -0,0 +1,62 @@
+/*
+ * 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.myfaces.test.mock;
+
+import javax.faces.flow.FlowHandler;
+
+/**
+ * <p>Mock implementation of <code>Application</code> that includes the semantics
+ * added by JavaServer Faces 3.0.</p>
+ *
+ * @author Leonardo Uribe
+ * @since 1.0.0
+ *
+ */
+public class MockApplication30 extends MockApplication22
+{
+ // ------------------------------------------------------------ Constructors
+
+ public MockApplication30()
+ {
+ super();
+
+ // install flow Handler
+ setFlowHandler(new MockFlowHandler());
+ }
+
+ // ------------------------------------------------------ Instance Variables
+
+ private FlowHandler _flowHandler;
+
+ // ----------------------------------------------------- Mock Object Methods
+
+ @Override
+ public FlowHandler getFlowHandler()
+ {
+ return _flowHandler;
+ }
+
+ @Override
+ public void setFlowHandler(FlowHandler flowHandler)
+ {
+ this._flowHandler = flowHandler;
+ }
+
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/mock/MockApplicationFactory.java b/test30/src/main/java/org/apache/myfaces/test/mock/MockApplicationFactory.java
new file mode 100644
index 0000000..9a76fdd
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/mock/MockApplicationFactory.java
@@ -0,0 +1,149 @@
+/*
+ * 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.myfaces.test.mock;
+
+import javax.faces.FacesException;
+import javax.faces.application.Application;
+import javax.faces.application.ApplicationFactory;
+
+/**
+ * <p>Mock implementation of <code>ApplicationFactory</code>.</p>
+ *
+ * $Id$
+ * @since 1.0.0
+ */
+
+public class MockApplicationFactory extends ApplicationFactory
+{
+
+ // ------------------------------------------------------------ Constructors
+
+ /**
+ * <p>Construct a default instance.</p>
+ */
+ public MockApplicationFactory()
+ {
+
+ }
+
+ // ----------------------------------------------------- Mock Object Methods
+
+ // ------------------------------------------------------ Instance Variables
+
+ /**
+ * <p>The <code>Application</code> instance to be returned by
+ * this factory.</p>
+ */
+ private Application application = null;
+
+ // --------------------------------------------- AppolicationFactory Methods
+
+ /** {@inheritDoc} */
+ public Application getApplication()
+ {
+
+ if (this.application == null)
+ {
+ Class clazz = null;
+
+ try
+ {
+ clazz = this.getClass().getClassLoader().loadClass(
+ "org.apache.myfaces.test.mock.MockApplication22");
+ if (clazz == null)
+ {
+ clazz = this.getClass().getClassLoader().loadClass(
+ "org.apache.myfaces.test.mock.MockApplication20");
+ }
+ this.application = (MockApplication) clazz.newInstance();
+ }
+ catch (NoClassDefFoundError e)
+ {
+ clazz = null; // We are not running in a JSF 1.2 environment
+ }
+ catch (ClassNotFoundException e)
+ {
+ clazz = null; // Same as above
+ }
+ catch (RuntimeException e)
+ {
+ throw e;
+ }
+ catch (Exception e)
+ {
+ throw new FacesException(e);
+ }
+
+ if (clazz == null)
+ {
+ try
+ {
+ clazz = this.getClass().getClassLoader().loadClass(
+ "org.apache.myfaces.test.mock.MockApplication12");
+ this.application = (MockApplication) clazz.newInstance();
+ }
+ catch (NoClassDefFoundError e)
+ {
+ clazz = null; // We are not running in a JSF 1.2 environment
+ }
+ catch (ClassNotFoundException e)
+ {
+ clazz = null; // Same as above
+ }
+ catch (RuntimeException e)
+ {
+ throw e;
+ }
+ catch (Exception e)
+ {
+ throw new FacesException(e);
+ }
+ }
+ if (clazz == null)
+ {
+ try
+ {
+ clazz = this.getClass().getClassLoader().loadClass(
+ "org.apache.myfaces.test.mock.MockApplication");
+ this.application = (MockApplication) clazz.newInstance();
+ }
+ catch (RuntimeException e)
+ {
+ throw e;
+ }
+ catch (Exception e)
+ {
+ throw new FacesException(e);
+ }
+ }
+ }
+ return this.application;
+
+ }
+
+ /** {@inheritDoc} */
+ public void setApplication(Application application)
+ {
+
+ this.application = application;
+
+ }
+
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/mock/MockClientWindow.java b/test30/src/main/java/org/apache/myfaces/test/mock/MockClientWindow.java
new file mode 100644
index 0000000..4c2b539
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/mock/MockClientWindow.java
@@ -0,0 +1,104 @@
+/*
+ * 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.myfaces.test.mock;
+
+import java.util.HashMap;
+import java.util.Map;
+import javax.faces.context.FacesContext;
+import javax.faces.lifecycle.ClientWindow;
+import javax.faces.render.ResponseStateManager;
+
+/**
+ * Mock ClientWindow implementation that by default set the id with "1".
+ *
+ * @author Leonardo Uribe
+ */
+public class MockClientWindow extends ClientWindow
+{
+ private String windowId;
+
+ private Map<String,String> queryParamsMap;
+
+ public MockClientWindow()
+ {
+ }
+
+ @Override
+ public void decode(FacesContext context)
+ {
+ String windowId = calculateWindowId(context);
+
+ if (windowId != null)
+ {
+ // Store the current windowId.
+ setId(windowId);
+ }
+ else
+ {
+ //Generate a new windowId
+ setId("1");
+ }
+ }
+
+
+ protected String calculateWindowId(FacesContext context)
+ {
+ //1. If it comes as parameter, it takes precedence over any other choice, because
+ // no browser is capable to do a POST and create a new window at the same time.
+ String windowId = context.getExternalContext().getRequestParameterMap().get(
+ ResponseStateManager.CLIENT_WINDOW_PARAM);
+ if (windowId != null)
+ {
+ return windowId;
+ }
+ windowId = context.getExternalContext().getRequestParameterMap().get(
+ ResponseStateManager.CLIENT_WINDOW_URL_PARAM);
+ if (windowId != null)
+ {
+ return windowId;
+ }
+ return null;
+ }
+
+ @Override
+ public String getId()
+ {
+ return this.windowId;
+ }
+
+ public void setId(String id)
+ {
+ this.windowId = id;
+ }
+
+ @Override
+ public Map<String, String> getQueryURLParameters(FacesContext context)
+ {
+ if (queryParamsMap == null)
+ {
+ String id = context.getExternalContext().getClientWindow().getId();
+ if (id != null)
+ {
+ queryParamsMap = new HashMap<String, String>(2,1);
+ queryParamsMap.put(ResponseStateManager.CLIENT_WINDOW_URL_PARAM, id);
+ }
+ }
+ return queryParamsMap;
+ }
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/mock/MockClientWindowFactory.java b/test30/src/main/java/org/apache/myfaces/test/mock/MockClientWindowFactory.java
new file mode 100644
index 0000000..6e57e32
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/mock/MockClientWindowFactory.java
@@ -0,0 +1,91 @@
+/*
+ * 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.myfaces.test.mock;
+
+import javax.faces.context.ExternalContext;
+import javax.faces.context.FacesContext;
+import javax.faces.lifecycle.ClientWindow;
+import javax.faces.lifecycle.ClientWindowFactory;
+
+/**
+ *
+ * @author lu4242
+ */
+public class MockClientWindowFactory extends ClientWindowFactory
+{
+ private static final String WINDOW_MODE_NONE = "none";
+ private static final String WINDOW_MODE_MOCK = "mock";
+
+ private String windowMode;
+
+ @Override
+ public ClientWindow getClientWindow(FacesContext facesContext)
+ {
+ if (WINDOW_MODE_NONE.equals(getWindowMode(facesContext)))
+ {
+ //No need to do anything
+ return null;
+ }
+ else
+ {
+ if (WINDOW_MODE_MOCK.equals(getWindowMode(facesContext)))
+ {
+ return new MockClientWindow();
+ }
+ else
+ {
+ return null;
+ }
+ }
+ }
+
+ private String getWindowMode(FacesContext context)
+ {
+ if (windowMode == null)
+ {
+ windowMode = getStringInitParameter(
+ context.getExternalContext(),
+ ClientWindow.CLIENT_WINDOW_MODE_PARAM_NAME, WINDOW_MODE_NONE);
+ }
+ return windowMode;
+ }
+
+ private static String getStringInitParameter(ExternalContext context, String name, String defaultValue)
+ {
+ if (name == null)
+ {
+ throw new NullPointerException();
+ }
+
+ String param = context.getInitParameter(name);
+
+ if (param == null)
+ {
+ return defaultValue;
+ }
+
+ param = param.trim();
+ if (param.length() == 0)
+ {
+ return defaultValue;
+ }
+
+ return param;
+ }
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/mock/MockEnumeration.java b/test30/src/main/java/org/apache/myfaces/test/mock/MockEnumeration.java
new file mode 100644
index 0000000..ea452e8
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/mock/MockEnumeration.java
@@ -0,0 +1,77 @@
+/*
+ * 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.myfaces.test.mock;
+
+import java.util.Enumeration;
+import java.util.Iterator;
+
+/**
+ * <p>Mock implementation of an <code>Enumeration</code> wrapper around
+ * an <code>Iterator</code>.</p>
+ *
+ * $Id$
+ * @since 1.0.0
+ */
+
+class MockEnumeration implements Enumeration
+{
+
+ // ------------------------------------------------------------ Constructors
+
+ /**
+ * <p>Construct a wrapper instance.</p>
+ *
+ * @param iterator The <code>Iterator</code> to be wrapped
+ */
+ public MockEnumeration(Iterator iterator)
+ {
+
+ this.iterator = iterator;
+
+ }
+
+ // ----------------------------------------------------- Mock Object Methods
+
+ // ------------------------------------------------------ Instance Variables
+
+ /**
+ * <p>The <code>Iterator</code> we are wrapping.</p>
+ */
+ private Iterator iterator;
+
+ // ----------------------------------------------------- Enumeration Methods
+
+ /** {@inheritDoc} */
+ public boolean hasMoreElements()
+ {
+
+ return iterator.hasNext();
+
+ }
+
+ /** {@inheritDoc} */
+ public Object nextElement()
+ {
+
+ return iterator.next();
+
+ }
+
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/mock/MockExceptionHandler.java b/test30/src/main/java/org/apache/myfaces/test/mock/MockExceptionHandler.java
new file mode 100644
index 0000000..2e7a0c0
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/mock/MockExceptionHandler.java
@@ -0,0 +1,220 @@
+/*
+ * 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.myfaces.test.mock;
+
+import javax.el.ELException;
+import javax.faces.FacesException;
+import javax.faces.context.ExceptionHandler;
+import javax.faces.event.AbortProcessingException;
+import javax.faces.event.ExceptionQueuedEvent;
+import javax.faces.event.ExceptionQueuedEventContext;
+import javax.faces.event.SystemEvent;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.Queue;
+
+/**
+ * <p>Mock implementation of <code>ExceptionHandler</code>.</p>
+ * <p/>
+ * $Id$
+ *
+ * @since 1.0.0
+ */
+public class MockExceptionHandler extends ExceptionHandler
+{
+
+ private Queue<ExceptionQueuedEvent> handled;
+ private Queue<ExceptionQueuedEvent> unhandled;
+ private ExceptionQueuedEvent handledAndThrown;
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public ExceptionQueuedEvent getHandledExceptionQueuedEvent()
+ {
+ return handledAndThrown;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Iterable<ExceptionQueuedEvent> getHandledExceptionQueuedEvents()
+ {
+ return handled == null ? Collections.<ExceptionQueuedEvent> emptyList()
+ : handled;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Throwable getRootCause(Throwable t)
+ {
+ if (t == null)
+ {
+ throw new NullPointerException("t");
+ }
+
+ while (t != null)
+ {
+ Class<?> clazz = t.getClass();
+ if (!clazz.equals(FacesException.class)
+ && !clazz.equals(ELException.class))
+ {
+ return t;
+ }
+
+ t = t.getCause();
+ }
+
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Iterable<ExceptionQueuedEvent> getUnhandledExceptionQueuedEvents()
+ {
+ return unhandled == null ? Collections
+ .<ExceptionQueuedEvent> emptyList() : unhandled;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void handle() throws FacesException
+ {
+ if (unhandled != null && !unhandled.isEmpty())
+ {
+ if (handled == null)
+ {
+ handled = new LinkedList<ExceptionQueuedEvent>();
+ }
+
+ FacesException toThrow = null;
+
+ do
+ {
+ // For each ExceptionEvent in the list
+
+ // get the event to handle
+ ExceptionQueuedEvent event = unhandled.peek();
+ try
+ {
+ // call its getContext() method
+ ExceptionQueuedEventContext context = event.getContext();
+
+ // and call getException() on the returned result
+ Throwable exception = context.getException();
+
+ // Upon encountering the first such Exception that is not an instance of
+ // javax.faces.event.AbortProcessingException
+ if (!shouldSkip(exception))
+ {
+ // set handledAndThrown so that getHandledExceptionQueuedEvent() returns this event
+ handledAndThrown = event;
+
+ // Re-wrap toThrow in a ServletException or (PortletException, if in a portlet environment)
+ // and throw it
+ // FIXME: The spec says to NOT use a FacesException to propagate the exception, but I see
+ // no other way as ServletException is not a RuntimeException
+ toThrow = wrap(getRethrownException(exception));
+ break;
+ }
+ }
+ catch (Throwable t)
+ {
+ // A FacesException must be thrown if a problem occurs while performing
+ // the algorithm to handle the exception
+ throw new FacesException(
+ "Could not perform the algorithm to handle the Exception",
+ t);
+ }
+ finally
+ {
+ // if we will throw the Exception or if we just logged it,
+ // we handled it in either way --> add to handled
+ handled.add(event);
+ unhandled.remove(event);
+ }
+ } while (!unhandled.isEmpty());
+
+ // do we have to throw an Exception?
+ if (toThrow != null)
+ {
+ throw toThrow;
+ }
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isListenerForSource(Object source)
+ {
+ return source instanceof ExceptionQueuedEventContext;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void processEvent(SystemEvent exceptionQueuedEvent)
+ throws AbortProcessingException
+ {
+ if (unhandled == null)
+ {
+ unhandled = new LinkedList<ExceptionQueuedEvent>();
+ }
+
+ unhandled.add((ExceptionQueuedEvent) exceptionQueuedEvent);
+ }
+
+ protected Throwable getRethrownException(Throwable exception)
+ {
+ // Let toRethrow be either the result of calling getRootCause() on the Exception,
+ // or the Exception itself, whichever is non-null
+ Throwable toRethrow = getRootCause(exception);
+ if (toRethrow == null)
+ {
+ toRethrow = exception;
+ }
+
+ return toRethrow;
+ }
+
+ protected FacesException wrap(Throwable exception)
+ {
+ if (exception instanceof FacesException)
+ {
+ return (FacesException) exception;
+ }
+ return new FacesException(exception);
+ }
+
+ protected boolean shouldSkip(Throwable exception)
+ {
+ return exception instanceof AbortProcessingException;
+ }
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/mock/MockExceptionHandlerFactory.java b/test30/src/main/java/org/apache/myfaces/test/mock/MockExceptionHandlerFactory.java
new file mode 100644
index 0000000..8072d1f
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/mock/MockExceptionHandlerFactory.java
@@ -0,0 +1,40 @@
+/*
+ * 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.myfaces.test.mock;
+
+import javax.faces.context.ExceptionHandler;
+import javax.faces.context.ExceptionHandlerFactory;
+
+/**
+ * <p>Mock implementation of <code>ExceptionHandlerFactory</code>.</p>
+ * <p/>
+ * $Id$
+ *
+ * @since 1.0.0
+ */
+public class MockExceptionHandlerFactory extends ExceptionHandlerFactory
+{
+
+ @Override
+ public ExceptionHandler getExceptionHandler()
+ {
+ return new MockExceptionHandler();
+ }
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/mock/MockExternalContext.java b/test30/src/main/java/org/apache/myfaces/test/mock/MockExternalContext.java
new file mode 100644
index 0000000..1774fa0
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/mock/MockExternalContext.java
@@ -0,0 +1,540 @@
+/*
+ * 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.myfaces.test.mock;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+
+import javax.faces.FacesException;
+import javax.faces.context.ExternalContext;
+import javax.faces.context.FacesContext;
+import javax.servlet.RequestDispatcher;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * <p>Mock implementation of <code>ExternalContext</code>.</p>
+ *
+ * $Id$
+ * @since 1.0.0
+ */
+public class MockExternalContext extends ExternalContext
+{
+
+ // ------------------------------------------------------------ Constructors
+
+ /**
+ * <p>Construct a wrapper instance.</p>
+ *
+ * @param context <code>ServletContext</code> for this application
+ * @param request <code>HttpServetRequest</code> for this request
+ * @param response <code>HttpServletResponse</code> for this request
+ */
+ public MockExternalContext(ServletContext context,
+ HttpServletRequest request, HttpServletResponse response)
+ {
+
+ this.context = context;
+ this.request = request;
+ this.response = response;
+ this.applicationMap = null;
+ this.initParameterMap = null;
+ this.requestMap = null;
+ this.requestCookieMap = null;
+ this.requestHeaderMap = null;
+ this.requestParameterMap = null;
+ this.requestParameterValuesMap = null;
+ this.requestHeaderValuesMap = null;
+ this.sessionMap = null;
+ }
+
+ // ----------------------------------------------------- Mock Object Methods
+
+ // ------------------------------------------------------ Instance Variables
+
+ protected ServletContext context;
+ protected HttpServletRequest request;
+ protected HttpServletResponse response;
+ private Map applicationMap;
+ private Map initParameterMap;
+ private Map requestMap;
+ private Map requestCookieMap;
+ private Map requestHeaderMap;
+ private Map requestParameterMap;
+ private Map requestParameterValuesMap;
+ private Map requestHeaderValuesMap;
+ private Map sessionMap;
+
+ // ------------------------------------------------- setters for the mock object
+
+ /**
+ * <p>Add a new cookie for this request.</p>
+ *
+ * @param cookie The new cookie
+ */
+ public void addRequestCookieMap(Cookie cookie)
+ {
+ Map map = getRequestCookieMap();
+ if (request instanceof MockHttpServletRequest
+ && map instanceof _CookieMap)
+ {
+ ((MockHttpServletRequest) request).addCookie(cookie);
+ }
+ else
+ {
+ map.put(cookie.getName(), cookie);
+ }
+ }
+
+ /**
+ * <p>Set the request cookie map for this request.</p>
+ *
+ * @param map The new request cookie map
+ */
+ public void setRequestCookieMap(Map map)
+ {
+ requestCookieMap = map;
+ }
+
+ /**
+ * <p>Add the specified request parameter for this request.</p>
+ *
+ * @param key Parameter name
+ * @param value Parameter value
+ */
+ public void addRequestParameterMap(String key, String value)
+ {
+ Map map = getRequestParameterMap();
+ if (request instanceof MockHttpServletRequest
+ && map instanceof _RequestParameterMap)
+ {
+ ((MockHttpServletRequest) request).addParameter(key, value);
+ }
+ else
+ {
+ map.put(key, value);
+ }
+ }
+
+ /**
+ * <p>Set the request parameter map for this request.</p>
+ *
+ * @param map The new request parameter map
+ */
+ public void setRequestParameterMap(Map map)
+ {
+ requestParameterMap = map;
+ }
+
+ /**
+ * <p>Add the specified request header for this request.</p>
+ *
+ * @param key Parameter name
+ * @param value Parameter value
+ */
+ public void addRequestHeader(String key, String value)
+ {
+ Map map = getRequestHeaderMap();
+ if (request instanceof MockHttpServletRequest
+ && map instanceof _RequestHeaderMap)
+ {
+ ((MockHttpServletRequest) request).addHeader(key, value);
+ }
+ else
+ {
+ map.put(key, value);
+ }
+ }
+
+ /**
+ * <p>Set the request header map for this request.</p>
+ *
+ * @param map The new request header map
+ */
+ public void setRequestHeaderMap(Map map)
+ {
+ requestHeaderMap = map;
+ }
+
+ // ------------------------------------------------- ExternalContext Methods
+
+ /** {@inheritDoc} */
+ public void dispatch(String requestURI) throws IOException, FacesException
+ {
+ RequestDispatcher requestDispatcher = request
+ .getRequestDispatcher(requestURI);
+ // If there is no dispatcher, send NOT_FOUND
+ if (requestDispatcher == null)
+ {
+ ((HttpServletResponse) response)
+ .sendError(HttpServletResponse.SC_NOT_FOUND);
+ return;
+ }
+ try
+ {
+ requestDispatcher.forward(request, response);
+ }
+ catch (ServletException e)
+ {
+ if (e.getMessage() != null)
+ {
+ throw new FacesException(e.getMessage(), e);
+ }
+ throw new FacesException(e);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public String encodeActionURL(String sb)
+ {
+ return sb;
+ }
+
+ /** {@inheritDoc} */
+ public String encodeNamespace(String aValue)
+ {
+ return aValue;
+ }
+
+ /** {@inheritDoc} */
+ public String encodeResourceURL(String sb)
+ {
+ return sb;
+ }
+
+ /** {@inheritDoc} */
+ public Map getApplicationMap()
+ {
+ if (applicationMap == null)
+ {
+ applicationMap = new _ApplicationMap(context);
+ }
+ return applicationMap;
+ }
+
+ /** {@inheritDoc} */
+ public String getAuthType()
+ {
+ return request.getAuthType();
+ }
+
+ /** {@inheritDoc} */
+ public Object getContext()
+ {
+ return context;
+ }
+
+ /** {@inheritDoc} */
+ public String getInitParameter(String name)
+ {
+ return context.getInitParameter(name);
+ }
+
+ /** {@inheritDoc} */
+ public Map getInitParameterMap()
+ {
+ if (initParameterMap == null)
+ {
+ initParameterMap = new _InitParameterMap(context);
+ }
+ return initParameterMap;
+ }
+
+ /** {@inheritDoc} */
+ public String getRemoteUser()
+ {
+ return request.getRemoteUser();
+ }
+
+ /** {@inheritDoc} */
+ public Object getRequest()
+ {
+ return request;
+ }
+
+ /** {@inheritDoc} */
+ public String getRequestContextPath()
+ {
+ return request.getContextPath();
+ }
+
+ /** {@inheritDoc} */
+ public Map getRequestCookieMap()
+ {
+ if (requestCookieMap == null)
+ {
+ requestCookieMap = new _CookieMap(request);
+ }
+ return requestCookieMap;
+ }
+
+ /** {@inheritDoc} */
+ public Map getRequestHeaderMap()
+ {
+ if (requestHeaderMap == null)
+ {
+ requestHeaderMap = new _RequestHeaderMap(request);
+ }
+ return requestHeaderMap;
+ }
+
+ /** {@inheritDoc} */
+ public Map getRequestHeaderValuesMap()
+ {
+ if (requestHeaderValuesMap == null)
+ {
+ requestHeaderValuesMap = new _RequestHeaderValuesMap(request);
+ }
+ return requestHeaderValuesMap;
+ }
+
+ /** {@inheritDoc} */
+ public Locale getRequestLocale()
+ {
+ return request.getLocale();
+ }
+
+ /** {@inheritDoc} */
+ public Iterator getRequestLocales()
+ {
+ return new LocalesIterator(request.getLocales());
+ }
+
+ /** {@inheritDoc} */
+ public Map getRequestMap()
+ {
+ if (requestMap == null)
+ {
+ requestMap = new _RequestMap(request);
+ }
+ return requestMap;
+ }
+
+ /** {@inheritDoc} */
+ public Map getRequestParameterMap()
+ {
+ if (requestParameterMap == null)
+ {
+ requestParameterMap = new _RequestParameterMap(request);
+ }
+ return requestParameterMap;
+ }
+
+ /** {@inheritDoc} */
+ public Iterator getRequestParameterNames()
+ {
+ final Enumeration enumer = request.getParameterNames();
+ Iterator it = new Iterator()
+ {
+ public boolean hasNext()
+ {
+ return enumer.hasMoreElements();
+ }
+
+ public Object next()
+ {
+ return enumer.nextElement();
+ }
+
+ public void remove()
+ {
+ throw new UnsupportedOperationException(this.getClass()
+ .getName()
+ + " UnsupportedOperationException");
+ }
+ };
+ return it;
+ }
+
+ /** {@inheritDoc} */
+ public Map getRequestParameterValuesMap()
+ {
+ if (requestParameterValuesMap == null)
+ {
+ requestParameterValuesMap = new _RequestParameterValuesMap(request);
+ }
+ return requestParameterValuesMap;
+ }
+
+ /** {@inheritDoc} */
+ public String getRequestPathInfo()
+ {
+ return request.getPathInfo();
+ }
+
+ /** {@inheritDoc} */
+ public String getRequestServletPath()
+ {
+ return request.getServletPath();
+ }
+
+ /** {@inheritDoc} */
+ public URL getResource(String path) throws MalformedURLException
+ {
+ return context.getResource(path);
+ }
+
+ /** {@inheritDoc} */
+ public InputStream getResourceAsStream(String path)
+ {
+ return context.getResourceAsStream(path);
+ }
+
+ /** {@inheritDoc} */
+ public Set getResourcePaths(String path)
+ {
+ return context.getResourcePaths(path);
+ }
+
+ /** {@inheritDoc} */
+ public Object getResponse()
+ {
+ return response;
+ }
+
+ /** {@inheritDoc} */
+ public Object getSession(boolean create)
+ {
+ return request.getSession(create);
+ }
+
+ /** {@inheritDoc} */
+ public Map getSessionMap()
+ {
+ if (sessionMap == null)
+ {
+ sessionMap = new _SessionMap(request);
+ }
+ return sessionMap;
+ }
+
+ /** {@inheritDoc} */
+ public java.security.Principal getUserPrincipal()
+ {
+ return request.getUserPrincipal();
+ }
+
+ /** {@inheritDoc} */
+ public boolean isUserInRole(String role)
+ {
+ return request.isUserInRole(role);
+ }
+
+ /** {@inheritDoc} */
+ public void log(String message)
+ {
+ context.log(message);
+ }
+
+ /** {@inheritDoc} */
+ public void log(String message, Throwable throwable)
+ {
+ context.log(message, throwable);
+ }
+
+ /** {@inheritDoc} */
+ public void redirect(String requestURI) throws IOException
+ {
+ response.sendRedirect(requestURI);
+ FacesContext.getCurrentInstance().responseComplete();
+ }
+
+ public String encodeWebsocketURL(String baseUrl)
+ {
+ Integer port = 8080;
+ port = (port == 0) ? null : port;
+ if (port != null &&
+ !port.equals(request.getServerPort()))
+ {
+ String scheme = "http";
+ String serverName = request.getServerName();
+ String url;
+ try
+ {
+ url = new URL(scheme, serverName, port, baseUrl).toExternalForm();
+ url = url.replaceFirst("http", "ws");
+ return url;
+ }
+ catch (MalformedURLException ex)
+ {
+ //If cannot build the url, return the base one unchanged
+ return baseUrl;
+ }
+ }
+ else
+ {
+ return baseUrl;
+ }
+ }
+
+ /**
+ * <p>Iterator implementation that wraps an enumeration
+ * of Locales for the current request.</p>
+ */
+ private class LocalesIterator implements Iterator
+ {
+
+ /**
+ * <p>Construct an iterator wrapping the specified
+ * enumeration.</p>
+ *
+ * @param locales Locales enumeration to wrap
+ */
+ public LocalesIterator(Enumeration locales)
+ {
+ this.locales = locales;
+ }
+
+ /**
+ * <p>The enumeration to be wrapped.</p>
+ */
+ private Enumeration locales;
+
+ /** {@inheritDoc} */
+ public boolean hasNext()
+ {
+ return locales.hasMoreElements();
+ }
+
+ /** {@inheritDoc} */
+ public Object next()
+ {
+ return locales.nextElement();
+ }
+
+ /** {@inheritDoc} */
+ public void remove()
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ }
+
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/mock/MockExternalContext12.java b/test30/src/main/java/org/apache/myfaces/test/mock/MockExternalContext12.java
new file mode 100644
index 0000000..dbb9e91
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/mock/MockExternalContext12.java
@@ -0,0 +1,117 @@
+/*
+ * 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.myfaces.test.mock;
+
+import java.io.UnsupportedEncodingException;
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * <p>Mock implementation of <code>ExternalContext</code> that includes the semantics
+ * added by JavaServer Faces 1.2.</p>
+ *
+ * $Id$
+ *
+ * @since 1.0.0
+ */
+public class MockExternalContext12 extends MockExternalContext
+{
+
+ // ------------------------------------------------------------ Constructors
+
+ public MockExternalContext12(ServletContext context,
+ HttpServletRequest request, HttpServletResponse response)
+ {
+ super(context, request, response);
+ }
+
+ // ------------------------------------------------------ Instance Variables
+
+ // ----------------------------------------------------- Mock Object Methods
+
+ // ------------------------------------------------- ExternalContext Methods
+
+ /** {@inheritDoc} */
+ public String getRequestCharacterEncoding()
+ {
+
+ return this.request.getCharacterEncoding();
+
+ }
+
+ /** {@inheritDoc} */
+ public String getRequestContentType()
+ {
+
+ return this.request.getContentType();
+
+ }
+
+ /** {@inheritDoc} */
+ public String getResponseCharacterEncoding()
+ {
+
+ return this.response.getCharacterEncoding();
+
+ }
+
+ /** {@inheritDoc} */
+ public String getResponseContentType()
+ {
+
+ return this.response.getContentType();
+
+ }
+
+ /** {@inheritDoc} */
+ public void setRequest(Object request)
+ {
+
+ this.request = (HttpServletRequest) request;
+
+ }
+
+ /** {@inheritDoc} */
+ public void setRequestCharacterEncoding(String encoding)
+ throws UnsupportedEncodingException
+ {
+
+ this.request.setCharacterEncoding(encoding);
+
+ }
+
+ /** {@inheritDoc} */
+ public void setResponse(Object response)
+ {
+
+ this.response = (HttpServletResponse) response;
+
+ }
+
+ /** {@inheritDoc} */
+ public void setResponseCharacterEncoding(String encoding)
+ {
+
+ this.response.setCharacterEncoding(encoding);
+
+ }
+
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/mock/MockExternalContext20.java b/test30/src/main/java/org/apache/myfaces/test/mock/MockExternalContext20.java
new file mode 100644
index 0000000..a83ff28
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/mock/MockExternalContext20.java
@@ -0,0 +1,358 @@
+/*
+ * 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.myfaces.test.mock;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
+import java.io.Writer;
+import java.net.URLEncoder;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.faces.context.Flash;
+import javax.servlet.ServletContext;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+
+/**
+ * <p>Mock implementation of <code>ExternalContext</code> that includes the semantics
+ * added by JavaServer Faces 2.0.</p>
+ *
+ * @author Leonardo Uribe
+ * @since 1.0.0
+ *
+ */
+public class MockExternalContext20 extends MockExternalContext12
+{
+
+ // ------------------------------------------------------------ Constructors
+
+ public MockExternalContext20(ServletContext context,
+ HttpServletRequest request, HttpServletResponse response)
+ {
+ super(context, request, response);
+ }
+
+ public String getMimeType(String file)
+ {
+ return context.getMimeType(file);
+ }
+
+ // ------------------------------------------------------ Instance Variables
+
+ private static final String URL_PARAM_SEPERATOR = "&";
+ private static final String URL_QUERY_SEPERATOR = "?";
+ private static final String URL_FRAGMENT_SEPERATOR = "#";
+ private static final String URL_NAME_VALUE_PAIR_SEPERATOR = "=";
+
+ // ----------------------------------------------------- Mock Object Methods
+
+ // ------------------------------------------------- ExternalContext Methods
+
+ public String encodeBookmarkableURL(String baseUrl,
+ Map<String, List<String>> parameters)
+ {
+ return response.encodeURL(encodeURL(baseUrl, parameters));
+ }
+
+ private String encodeURL(String baseUrl,
+ Map<String, List<String>> parameters)
+ {
+ String fragment = null;
+ String queryString = null;
+ Map<String, List<String>> paramMap = new HashMap<String, List<String>>();
+
+ //extract any URL fragment
+ int index = baseUrl.indexOf(URL_FRAGMENT_SEPERATOR);
+ if (index != -1)
+ {
+ fragment = baseUrl.substring(index + 1);
+ baseUrl = baseUrl.substring(0, index);
+ }
+
+ //extract the current query string and add the params to the paramMap
+ index = baseUrl.indexOf(URL_QUERY_SEPERATOR);
+ if (index != -1)
+ {
+ queryString = baseUrl.substring(index + 1);
+ baseUrl = baseUrl.substring(0, index);
+ String[] nameValuePairs = queryString.split(URL_PARAM_SEPERATOR);
+ for (int i = 0; i < nameValuePairs.length; i++)
+ {
+ String[] currentPair = nameValuePairs[i]
+ .split(URL_NAME_VALUE_PAIR_SEPERATOR);
+ if (currentPair[1] != null)
+ {
+ ArrayList<String> value = new ArrayList<String>(1);
+ value.add(currentPair[1]);
+ paramMap.put(currentPair[0], value);
+ }
+ }
+ }
+
+ //add/update with new params on the paramMap
+ if (parameters != null && parameters.size() > 0)
+ {
+ for (Map.Entry<String, List<String>> pair : parameters.entrySet())
+ {
+ if (pair.getKey() != null && pair.getKey().trim().length() != 0)
+ {
+ paramMap.put(pair.getKey(), pair.getValue());
+ }
+ }
+ }
+
+ // start building the new URL
+ StringBuilder newUrl = new StringBuilder(baseUrl);
+
+ //now add the updated param list onto the url
+ if (paramMap.size() > 0)
+ {
+ boolean isFirstPair = true;
+ for (Map.Entry<String, List<String>> pair : paramMap.entrySet())
+ {
+ for (String value : pair.getValue())
+ {
+ if (!isFirstPair)
+ {
+ newUrl.append(URL_PARAM_SEPERATOR);
+ }
+ else
+ {
+ newUrl.append(URL_QUERY_SEPERATOR);
+ isFirstPair = false;
+ }
+
+ newUrl.append(pair.getKey());
+ newUrl.append(URL_NAME_VALUE_PAIR_SEPERATOR);
+ try
+ {
+ newUrl.append(URLEncoder.encode(value,
+ getResponseCharacterEncoding()));
+ }
+ catch (UnsupportedEncodingException e)
+ {
+ //shouldn't ever get here
+ throw new UnsupportedOperationException(
+ "Encoding type="
+ + getResponseCharacterEncoding()
+ + " not supported", e);
+ }
+ }
+ }
+ }
+
+ //add the fragment back on (if any)
+ if (fragment != null)
+ {
+ newUrl.append(URL_FRAGMENT_SEPERATOR + fragment);
+ }
+
+ return newUrl.toString();
+ }
+
+ public String encodeRedirectURL(String baseUrl,
+ Map<String, List<String>> parameters)
+ {
+ return response.encodeRedirectURL(encodeURL(baseUrl, parameters));
+ }
+
+ @Override
+ public String encodePartialActionURL(String url)
+ {
+ return ((HttpServletResponse) response).encodeURL(url);
+ }
+
+ public String getContextName()
+ {
+ return context.getServletContextName();
+ }
+
+ public String getRealPath(String path)
+ {
+ return context.getRealPath(path);
+ }
+
+ public void responseSendError(int statusCode, String message)
+ throws IOException
+ {
+ if (message == null)
+ {
+ response.sendError(statusCode);
+ }
+ else
+ {
+ response.sendError(statusCode, message);
+ }
+ }
+
+ public void setResponseHeader(String name, String value)
+ {
+ response.setHeader(name, value);
+ }
+
+ public String getRequestScheme()
+ {
+ return request.getScheme();
+ }
+
+ public String getRequestServerName()
+ {
+ return request.getServerName();
+ }
+
+ public int getRequestServerPort()
+ {
+ return request.getServerPort();
+ }
+
+ public OutputStream getResponseOutputStream() throws IOException
+ {
+ return response.getOutputStream();
+ }
+
+ public Writer getResponseOutputWriter() throws IOException
+ {
+ return response.getWriter();
+ }
+
+ @Override
+ public void setResponseContentType(String contentType)
+ {
+ response.setContentType(contentType);
+ }
+
+ public Flash getFlash()
+ {
+ return MockFlash.getCurrentInstance(this);
+ }
+
+ @Override
+ public void setResponseContentLength(int length)
+ {
+ response.setContentLength(length);
+ }
+
+ @Override
+ public int getRequestContentLength()
+ {
+ return request.getContentLength();
+ }
+
+ @Override
+ public int getResponseBufferSize()
+ {
+ return response.getBufferSize();
+ }
+
+ @Override
+ public void setResponseBufferSize(int size)
+ {
+ response.setBufferSize(size);
+ }
+
+ @Override
+ public void setResponseStatus(int statusCode)
+ {
+ response.setStatus(statusCode);
+ }
+
+ @Override
+ public void invalidateSession()
+ {
+ HttpSession session = request.getSession(false);
+ if (session != null)
+ {
+ session.invalidate();
+ }
+ }
+
+ @Override
+ public boolean isResponseCommitted()
+ {
+ return response.isCommitted();
+ }
+
+ @Override
+ public void responseFlushBuffer() throws IOException
+ {
+ response.flushBuffer();
+ }
+
+ @Override
+ public void responseReset()
+ {
+ response.reset();
+ }
+
+ @Override
+ public void addResponseCookie(String name, String value,
+ Map<String, Object> properties)
+ {
+ Cookie cookie = new Cookie(name, value);
+ if (properties != null)
+ {
+ for (Map.Entry<String, Object> entry : properties.entrySet())
+ {
+ String propertyKey = entry.getKey();
+ Object propertyValue = entry.getValue();
+ if ("comment".equals(propertyKey))
+ {
+ cookie.setComment((String) propertyValue);
+ continue;
+ }
+ else if ("domain".equals(propertyKey))
+ {
+ cookie.setDomain((String)propertyValue);
+ continue;
+ }
+ else if ("maxAge".equals(propertyKey))
+ {
+ cookie.setMaxAge((Integer) propertyValue);
+ continue;
+ }
+ else if ("secure".equals(propertyKey))
+ {
+ cookie.setSecure((Boolean) propertyValue);
+ continue;
+ }
+ else if ("path".equals(propertyKey))
+ {
+ cookie.setPath((String) propertyValue);
+ continue;
+ }
+ throw new IllegalArgumentException("Unused key when creating Cookie");
+ }
+ }
+ response.addCookie(cookie);
+ }
+
+ @Override
+ public void addResponseHeader(String name, String value)
+ {
+ response.addHeader(name, value);
+ }
+
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/mock/MockExternalContext22.java b/test30/src/main/java/org/apache/myfaces/test/mock/MockExternalContext22.java
new file mode 100644
index 0000000..4d45ab9
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/mock/MockExternalContext22.java
@@ -0,0 +1,109 @@
+/*
+ * 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.myfaces.test.mock;
+
+import javax.faces.lifecycle.ClientWindow;
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+
+/**
+ * <p>Mock implementation of <code>ExternalContext</code> that includes the semantics
+ * added by JavaServer Faces 2.2.</p>
+ *
+ * @author Leonardo Uribe
+ * @since 1.0.0
+ *
+ */
+public class MockExternalContext22 extends MockExternalContext20
+{
+
+ // ------------------------------------------------------------ Constructors
+
+ public MockExternalContext22(ServletContext context,
+ HttpServletRequest request, HttpServletResponse response)
+ {
+ super(context, request, response);
+ _clientWindow = null;
+ }
+
+ // ------------------------------------------------------ Instance Variables
+
+ private ClientWindow _clientWindow;
+
+ // ----------------------------------------------------- Mock Object Methods
+
+
+ @Override
+ public boolean isSecure()
+ {
+ return request.isSecure();
+ }
+
+ @Override
+ public int getSessionMaxInactiveInterval()
+ {
+ HttpSession session = request.getSession();
+ return session.getMaxInactiveInterval();
+ }
+
+ @Override
+ public void setSessionMaxInactiveInterval(int interval)
+ {
+ HttpSession session = request.getSession();
+ session.setMaxInactiveInterval(interval);
+ }
+
+ @Override
+ public ClientWindow getClientWindow()
+ {
+ return _clientWindow;
+ }
+
+ @Override
+ public void setClientWindow(ClientWindow window)
+ {
+ _clientWindow = window;
+ }
+
+ @Override
+ public String getSessionId(boolean create)
+ {
+ HttpSession session = ((HttpServletRequest) request).getSession(create);
+ if (session != null)
+ {
+ return session.getId();
+ }
+ else
+ {
+ return "";
+ }
+ }
+
+ @Override
+ public String getApplicationContextPath()
+ {
+ return context.getContextPath();
+ }
+
+ // ------------------------------------------------- ExternalContext Methods
+
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/mock/MockFacesContext.java b/test30/src/main/java/org/apache/myfaces/test/mock/MockFacesContext.java
new file mode 100644
index 0000000..e597361
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/mock/MockFacesContext.java
@@ -0,0 +1,330 @@
+/*
+ * 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.myfaces.test.mock;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import javax.faces.FactoryFinder;
+import javax.faces.application.Application;
+import javax.faces.application.FacesMessage;
+import javax.faces.application.FacesMessage.Severity;
+import javax.faces.component.UIViewRoot;
+import javax.faces.context.ExternalContext;
+import javax.faces.context.FacesContext;
+import javax.faces.context.ResponseStream;
+import javax.faces.context.ResponseWriter;
+import javax.faces.lifecycle.Lifecycle;
+import javax.faces.render.RenderKit;
+import javax.faces.render.RenderKitFactory;
+
+/**
+ * <p>Mock implementation of <code>FacesContext</code>.</p>
+ *
+ * $Id$
+ * @since 1.0.0
+ */
+public class MockFacesContext extends FacesContext
+{
+
+ // ------------------------------------------------------------ Constructors
+
+ public MockFacesContext()
+ {
+ super();
+ setCurrentInstance(this);
+ }
+
+ public MockFacesContext(ExternalContext externalContext)
+ {
+ setExternalContext(externalContext);
+ setCurrentInstance(this);
+ }
+
+ public MockFacesContext(ExternalContext externalContext, Lifecycle lifecycle)
+ {
+ this(externalContext);
+ this.lifecycle = lifecycle;
+ }
+
+ // ----------------------------------------------------- Mock Object Methods
+
+ /**
+ * <p>Set the <code>Application</code> instance for this instance.</p>
+ *
+ * @param application The new Application
+ */
+ public void setApplication(Application application)
+ {
+
+ this.application = application;
+
+ }
+
+ /**
+ * <p>Set the <code>ExternalContext</code> instance for this instance.</p>
+ *
+ * @param externalContext The new ExternalContext
+ */
+ public void setExternalContext(ExternalContext externalContext)
+ {
+
+ this.externalContext = externalContext;
+
+ }
+
+ /**
+ * <p>Set the <code>FacesContext</code> instance for this instance.</p>
+ *
+ * @param facesContext The new FacesContext
+ */
+ public static void setCurrentInstance(FacesContext facesContext)
+ {
+
+ FacesContext.setCurrentInstance(facesContext);
+
+ }
+
+ // ------------------------------------------------------ Instance Variables
+
+ private Application application = null;
+ private ExternalContext externalContext = null;
+ private Lifecycle lifecycle = null;
+ protected Map messages = new HashMap(); // needs to be accessed in subclass MockFacesContext20
+ private boolean renderResponse = false;
+ private boolean responseComplete = false;
+ private ResponseStream responseStream = null;
+ private ResponseWriter responseWriter = null;
+ private UIViewRoot viewRoot = null;
+
+ // ---------------------------------------------------- FacesContext Methods
+
+ /** {@inheritDoc} */
+ public Application getApplication()
+ {
+
+ return this.application;
+
+ }
+
+ /** {@inheritDoc} */
+ public Iterator getClientIdsWithMessages()
+ {
+
+ return messages.keySet().iterator();
+
+ }
+
+ /** {@inheritDoc} */
+ public ExternalContext getExternalContext()
+ {
+
+ return this.externalContext;
+
+ }
+
+ /** {@inheritDoc} */
+ public Severity getMaximumSeverity()
+ {
+
+ Severity severity = null;
+ Iterator messages = getMessages();
+ while (messages.hasNext())
+ {
+ FacesMessage message = (FacesMessage) messages.next();
+ if (severity == null)
+ {
+ severity = message.getSeverity();
+ }
+ else if (message.getSeverity().getOrdinal() > severity.getOrdinal())
+ {
+ severity = message.getSeverity();
+ }
+ }
+ return severity;
+
+ }
+
+ /** {@inheritDoc} */
+ public Iterator getMessages()
+ {
+
+ ArrayList results = new ArrayList();
+ Iterator clientIds = messages.keySet().iterator();
+ while (clientIds.hasNext())
+ {
+ String clientId = (String) clientIds.next();
+ results.addAll((List) messages.get(clientId));
+ }
+ return results.iterator();
+
+ }
+
+ /** {@inheritDoc} */
+ public Iterator getMessages(String clientId)
+ {
+
+ List list = (List) messages.get(clientId);
+ if (list == null)
+ {
+ list = new ArrayList();
+ }
+ return list.iterator();
+
+ }
+
+ /** {@inheritDoc} */
+ public RenderKit getRenderKit()
+ {
+
+ UIViewRoot vr = getViewRoot();
+ if (vr == null)
+ {
+ return null;
+ }
+ String renderKitId = vr.getRenderKitId();
+ if (renderKitId == null)
+ {
+ return null;
+ }
+ RenderKitFactory rkFactory = (RenderKitFactory) FactoryFinder
+ .getFactory(FactoryFinder.RENDER_KIT_FACTORY);
+ return rkFactory.getRenderKit(this, renderKitId);
+
+ }
+
+ /** {@inheritDoc} */
+ public boolean getRenderResponse()
+ {
+
+ return this.renderResponse;
+
+ }
+
+ /** {@inheritDoc} */
+ public boolean getResponseComplete()
+ {
+
+ return this.responseComplete;
+
+ }
+
+ /** {@inheritDoc} */
+ public ResponseStream getResponseStream()
+ {
+
+ return this.responseStream;
+
+ }
+
+ /** {@inheritDoc} */
+ public void setResponseStream(ResponseStream responseStream)
+ {
+
+ this.responseStream = responseStream;
+
+ }
+
+ /** {@inheritDoc} */
+ public ResponseWriter getResponseWriter()
+ {
+
+ return this.responseWriter;
+
+ }
+
+ /** {@inheritDoc} */
+ public void setResponseWriter(ResponseWriter responseWriter)
+ {
+
+ this.responseWriter = responseWriter;
+
+ }
+
+ /** {@inheritDoc} */
+ public UIViewRoot getViewRoot()
+ {
+
+ return this.viewRoot;
+
+ }
+
+ /** {@inheritDoc} */
+ public void setViewRoot(UIViewRoot viewRoot)
+ {
+
+ this.viewRoot = viewRoot;
+
+ }
+
+ /** {@inheritDoc} */
+ public void addMessage(String clientId, FacesMessage message)
+ {
+
+ if (message == null)
+ {
+ throw new NullPointerException();
+ }
+ List list = (List) messages.get(clientId);
+ if (list == null)
+ {
+ list = new ArrayList();
+ messages.put(clientId, list);
+ }
+ list.add(message);
+
+ }
+
+ /** {@inheritDoc} */
+ public void release()
+ {
+
+ application = null;
+ externalContext = null;
+ messages.clear();
+ renderResponse = false;
+ responseComplete = false;
+ responseStream = null;
+ responseWriter = null;
+ viewRoot = null;
+ setCurrentInstance(null);
+
+ }
+
+ /** {@inheritDoc} */
+ public void renderResponse()
+ {
+
+ this.renderResponse = true;
+
+ }
+
+ /** {@inheritDoc} */
+ public void responseComplete()
+ {
+
+ this.responseComplete = true;
+
+ }
+
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/mock/MockFacesContext12.java b/test30/src/main/java/org/apache/myfaces/test/mock/MockFacesContext12.java
new file mode 100644
index 0000000..d92c589
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/mock/MockFacesContext12.java
@@ -0,0 +1,116 @@
+/*
+ * 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.myfaces.test.mock;
+
+import javax.el.ELContext;
+import javax.el.ELContextEvent;
+import javax.el.ELContextListener;
+import javax.faces.context.ExternalContext;
+import javax.faces.context.FacesContext;
+import javax.faces.lifecycle.Lifecycle;
+
+import org.apache.myfaces.test.el.MockELContext;
+
+/**
+ * <p>Mock implementation of <code>FacesContext</code> that includes the semantics
+ * added by JavaServer Faces 1.2.</p>
+ *
+ * $Id$
+ *
+ * @since 1.0.0
+ */
+public class MockFacesContext12 extends MockFacesContext
+{
+
+ // ------------------------------------------------------------ Constructors
+
+ public MockFacesContext12()
+ {
+ super();
+ setCurrentInstance(this);
+ }
+
+ public MockFacesContext12(ExternalContext externalContext)
+ {
+ super(externalContext);
+ }
+
+ public MockFacesContext12(ExternalContext externalContext,
+ Lifecycle lifecycle)
+ {
+ super(externalContext, lifecycle);
+ }
+
+ // ----------------------------------------------------- Mock Object Methods
+
+ /**
+ * <p>Set the <code>ELContext</code> instance for this instance.</p>
+ *
+ * @param elContext The new ELContext
+ */
+ public void setELContext(ELContext elContext)
+ {
+
+ this.elContext = elContext;
+
+ }
+
+ // ------------------------------------------------------ Instance Variables
+
+ private ELContext elContext = null;
+
+ // ---------------------------------------------------- FacesContext Methods
+
+ /** {@inheritDoc} */
+ public ELContext getELContext()
+ {
+
+ if (this.elContext == null)
+ {
+
+ // Initialize a new ELContext
+ this.elContext = new MockELContext();
+ this.elContext.putContext(FacesContext.class, this);
+
+ // Notify interested listeners that this ELContext was created
+ ELContextListener[] listeners = getApplication()
+ .getELContextListeners();
+ if ((listeners != null) && (listeners.length > 0))
+ {
+ ELContextEvent event = new ELContextEvent(this.elContext);
+ for (int i = 0; i < listeners.length; i++)
+ {
+ listeners[i].contextCreated(event);
+ }
+ }
+
+ }
+ return this.elContext;
+
+ }
+
+ /** {@inheritDoc} */
+ public void release()
+ {
+ super.release();
+ this.elContext = null;
+ }
+
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/mock/MockFacesContext20.java b/test30/src/main/java/org/apache/myfaces/test/mock/MockFacesContext20.java
new file mode 100644
index 0000000..23fa4ee
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/mock/MockFacesContext20.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.myfaces.test.mock;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.faces.FactoryFinder;
+import javax.faces.application.FacesMessage;
+import javax.faces.context.ExceptionHandler;
+import javax.faces.context.ExternalContext;
+import javax.faces.context.PartialViewContext;
+import javax.faces.context.PartialViewContextFactory;
+import javax.faces.event.PhaseId;
+import javax.faces.lifecycle.Lifecycle;
+
+/**
+ * <p>Mock implementation of <code>FacesContext</code> that includes the semantics
+ * added by JavaServer Faces 2.0.</p>
+ *
+ * @author Leonardo Uribe
+ * @since 1.0.0
+ *
+ */
+public class MockFacesContext20 extends MockFacesContext12
+{
+
+ // ------------------------------------------------------------ Constructors
+
+ public MockFacesContext20()
+ {
+ super();
+ setCurrentInstance(this);
+ }
+
+ public MockFacesContext20(ExternalContext externalContext)
+ {
+ super(externalContext);
+ }
+
+ public MockFacesContext20(ExternalContext externalContext,
+ Lifecycle lifecycle)
+ {
+ super(externalContext, lifecycle);
+ }
+
+ // ------------------------------------------------------ Instance Variables
+
+ private boolean _processingEvents = true;
+ private ExceptionHandler _exceptionHandler = null;
+ private PhaseId _currentPhaseId = PhaseId.RESTORE_VIEW;
+ private boolean _postback;
+ private PartialViewContext _partialViewContext = null;
+ private Map<Object, Object> attributes;
+ private boolean _validationFailed = false;
+
+ // ----------------------------------------------------- Mock Object Methods
+
+ public boolean isPostback()
+ {
+ return _postback;
+ }
+
+ public void setPostback(boolean value)
+ {
+ _postback = value;
+ }
+
+ public PhaseId getCurrentPhaseId()
+ {
+ return _currentPhaseId;
+ }
+
+ public void setCurrentPhaseId(PhaseId currentPhaseId)
+ {
+ this._currentPhaseId = currentPhaseId;
+ }
+
+ public Map<Object, Object> getAttributes()
+ {
+ if (attributes == null)
+ {
+ attributes = new HashMap<Object, Object>();
+ }
+ return attributes;
+ }
+
+ public PartialViewContext getPartialViewContext()
+ {
+ if (_partialViewContext == null)
+ {
+ //Get through factory finder
+ PartialViewContextFactory factory = (PartialViewContextFactory) FactoryFinder
+ .getFactory(FactoryFinder.PARTIAL_VIEW_CONTEXT_FACTORY);
+ _partialViewContext = factory.getPartialViewContext(this);
+ }
+ return _partialViewContext;
+ }
+
+ public boolean isProcessingEvents()
+ {
+ return _processingEvents;
+ }
+
+ public void setProcessingEvents(boolean processingEvents)
+ {
+ _processingEvents = processingEvents;
+ }
+
+ public ExceptionHandler getExceptionHandler()
+ {
+ return _exceptionHandler;
+ }
+
+ public void setExceptionHandler(ExceptionHandler exceptionHandler)
+ {
+ _exceptionHandler = exceptionHandler;
+ }
+
+ @SuppressWarnings("unchecked")
+ public List<FacesMessage> getMessageList()
+ {
+ if (messages == null)
+ {
+ return Collections.unmodifiableList(Collections
+ .<FacesMessage> emptyList());
+ }
+
+ List<FacesMessage> lst = new ArrayList<FacesMessage>();
+ for (List<FacesMessage> curLst : ((Map<String, List<FacesMessage>>) messages)
+ .values())
+ {
+ lst.addAll(curLst);
+ }
+
+ return Collections.unmodifiableList(lst);
+ }
+
+ @SuppressWarnings("unchecked")
+ public List<FacesMessage> getMessageList(String clientId)
+ {
+ if (messages == null || !messages.containsKey(clientId))
+ {
+ return Collections.unmodifiableList(Collections
+ .<FacesMessage> emptyList());
+ }
+
+ return ((Map<String, List<FacesMessage>>) messages).get(clientId);
+ }
+
+ public boolean isValidationFailed()
+ {
+ return _validationFailed;
+ }
+
+ public void validationFailed()
+ {
+ _validationFailed = true;
+ }
+
+ // ------------------------------------------------- ExternalContext Methods
+
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/mock/MockFacesContext22.java b/test30/src/main/java/org/apache/myfaces/test/mock/MockFacesContext22.java
new file mode 100644
index 0000000..fcde8b8
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/mock/MockFacesContext22.java
@@ -0,0 +1,121 @@
+/*
+ * 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.myfaces.test.mock;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import javax.faces.component.UINamingContainer;
+import javax.faces.context.ExternalContext;
+import javax.faces.lifecycle.Lifecycle;
+
+/**
+ * <p>Mock implementation of <code>FacesContext</code> that includes the semantics
+ * added by JavaServer Faces 2.0.</p>
+ *
+ * @author Leonardo Uribe
+ * @since 1.0.0
+ *
+ */
+public class MockFacesContext22 extends MockFacesContext20
+{
+
+ // ------------------------------------------------------------ Constructors
+
+ public MockFacesContext22()
+ {
+ super();
+ setCurrentInstance(this);
+ }
+
+ public MockFacesContext22(ExternalContext externalContext)
+ {
+ super(externalContext);
+ }
+
+ public MockFacesContext22(ExternalContext externalContext,
+ Lifecycle lifecycle)
+ {
+ super(externalContext, lifecycle);
+ }
+
+ // ------------------------------------------------------ Instance Variables
+
+ private List<String> _resourceLibraryContracts;
+ private Character _separatorChar;
+ protected boolean _released = false;
+
+ // ----------------------------------------------------- Mock Object Methods
+
+ @Override
+ public List<String> getResourceLibraryContracts()
+ {
+ if (_resourceLibraryContracts == null)
+ {
+ return Collections.emptyList();
+ }
+ else
+ {
+ return _resourceLibraryContracts;
+ }
+ }
+
+ @Override
+ public void setResourceLibraryContracts(List<String> contracts)
+ {
+ if (contracts == null)
+ {
+ _resourceLibraryContracts = null;
+ }
+ else if (contracts.isEmpty())
+ {
+ _resourceLibraryContracts = null;
+ }
+ else
+ {
+ _resourceLibraryContracts = new ArrayList<String>(contracts);
+ }
+ }
+
+ @Override
+ public char getNamingContainerSeparatorChar()
+ {
+ if (_separatorChar == null)
+ {
+ _separatorChar = UINamingContainer.getSeparatorChar(this);
+ }
+ return _separatorChar;
+ }
+
+ // ------------------------------------------------- ExternalContext Methods
+
+ @Override
+ public boolean isReleased()
+ {
+ return _released;
+ }
+
+ @Override
+ public void release()
+ {
+ super.release();
+ _released = true;
+ }
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/mock/MockFacesContextFactory.java b/test30/src/main/java/org/apache/myfaces/test/mock/MockFacesContextFactory.java
new file mode 100644
index 0000000..0a62884
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/mock/MockFacesContextFactory.java
@@ -0,0 +1,315 @@
+/*
+ * 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.myfaces.test.mock;
+
+import java.lang.reflect.Constructor;
+import javax.faces.FacesException;
+import javax.faces.context.ExternalContext;
+import javax.faces.context.FacesContext;
+import javax.faces.context.FacesContextFactory;
+import javax.faces.lifecycle.Lifecycle;
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * <p>Mock implementation of <code>FacesContextFactory</code>.</p>
+ *
+ * $Id: MockFacesContextFactory.java 990408 2010-08-28 18:59:21Z lu4242 $
+ * @since 1.0.0
+ */
+
+public class MockFacesContextFactory extends FacesContextFactory
+{
+
+ // ------------------------------------------------------------ Constructors
+
+ /**
+ * <p>Look up the constructor we will use for creating <code>MockFacesContext</code>
+ * instances.</p>
+ */
+ public MockFacesContextFactory()
+ {
+
+ Class clazz = null;
+
+ // Try to load the 2.2 version of our mock FacesContext class
+ try
+ {
+ clazz = this.getClass().getClassLoader().loadClass(
+ "org.apache.myfaces.test.mock.MockFacesContext22");
+ constructor = clazz.getConstructor(facesContextSignature);
+ jsf22 = true;
+ }
+ catch (NoClassDefFoundError e)
+ {
+ // We are not running on JSF 2.0, so go to our fallback
+ clazz = null;
+ constructor = null;
+ }
+ catch (ClassNotFoundException e)
+ {
+ // Same as above
+ clazz = null;
+ constructor = null;
+ }
+ catch (RuntimeException e)
+ {
+ throw e;
+ }
+ catch (Exception e)
+ {
+ throw new FacesException(e);
+ }
+
+ // Try to load the 2.0 version of our mock FacesContext class
+ try
+ {
+ if (clazz == null)
+ {
+ clazz = this.getClass().getClassLoader().loadClass(
+ "org.apache.myfaces.test.mock.MockFacesContext20");
+ constructor = clazz.getConstructor(facesContextSignature);
+ jsf20 = true;
+ }
+ }
+ catch (NoClassDefFoundError e)
+ {
+ // We are not running on JSF 2.0, so go to our fallback
+ clazz = null;
+ constructor = null;
+ }
+ catch (ClassNotFoundException e)
+ {
+ // Same as above
+ clazz = null;
+ constructor = null;
+ }
+ catch (RuntimeException e)
+ {
+ throw e;
+ }
+ catch (Exception e)
+ {
+ throw new FacesException(e);
+ }
+
+ // Try to load the 1.2 version of our mock FacesContext class
+ try
+ {
+ if (clazz == null)
+ {
+ clazz = this.getClass().getClassLoader().loadClass(
+ "org.apache.myfaces.test.mock.MockFacesContext12");
+ constructor = clazz.getConstructor(facesContextSignature);
+ jsf12 = true;
+ }
+ }
+ catch (NoClassDefFoundError e)
+ {
+ // We are not running on JSF 1.2, so go to our fallback
+ clazz = null;
+ constructor = null;
+ }
+ catch (ClassNotFoundException e)
+ {
+ // Same as above
+ clazz = null;
+ constructor = null;
+ }
+ catch (RuntimeException e)
+ {
+ throw e;
+ }
+ catch (Exception e)
+ {
+ throw new FacesException(e);
+ }
+
+ // Fall back to the 1.1 version if we could not load the 1.2 version
+ try
+ {
+ if (clazz == null)
+ {
+ clazz = this.getClass().getClassLoader().loadClass(
+ "org.apache.myfaces.test.mock.MockFacesContext");
+ constructor = clazz.getConstructor(facesContextSignature);
+ jsf12 = false;
+ }
+ }
+ catch (RuntimeException e)
+ {
+ throw e;
+ }
+ catch (Exception e)
+ {
+ throw new FacesException(e);
+ }
+
+ }
+
+ // ----------------------------------------------------- Mock Object Methods
+
+ // ------------------------------------------------------ Instance Variables
+
+ /**
+ * <p>The constructor for creating a <code>FacesContext</code> instance,
+ * taking an <code>ExternalContext</code> and <code>Lifecycle</code>.</p>
+ */
+ private Constructor constructor = null;
+
+ /**
+ * <p>The parameter signature of the ExternalContext constructor we wish to call.</p>
+ */
+ private static Class[] externalContextSignature = new Class[] {
+ ServletContext.class, HttpServletRequest.class,
+ HttpServletResponse.class };
+
+ /**
+ * <p>The parameter signature of the FacesContext constructor we wish to call.</p>
+ */
+ private static Class[] facesContextSignature = new Class[] {
+ ExternalContext.class, Lifecycle.class };
+
+ /**
+ * <p>Flag indicating that we are running in a JSF 1.2 environment.</p>
+ */
+ private boolean jsf12 = false;
+
+ /**
+ * <p>Flag indicating that we are running in a JSF 2.0 environment.</p>
+ */
+ private boolean jsf20 = false;
+
+ /**
+ * <p>Flag indicating that we are running in a JSF 2.2 environment.</p>
+ */
+ private boolean jsf22 = false;
+
+ // --------------------------------------------- FacesContextFactory Methods
+
+ /** {@inheritDoc} */
+ public FacesContext getFacesContext(Object context, Object request,
+ Object response, Lifecycle lifecycle) throws FacesException
+ {
+
+ // Select the appropriate MockExternalContext implementation class
+ Class clazz = MockExternalContext.class;
+
+ if (jsf22)
+ {
+ try
+ {
+ clazz = this.getClass().getClassLoader().loadClass(
+ "org.apache.myfaces.test.mock.MockExternalContext22");
+ }
+ catch (RuntimeException e)
+ {
+ throw e;
+ }
+ catch (Exception e)
+ {
+ throw new FacesException(e);
+ }
+ }
+
+ if (jsf20)
+ {
+ try
+ {
+ clazz = this.getClass().getClassLoader().loadClass(
+ "org.apache.myfaces.test.mock.MockExternalContext20");
+ }
+ catch (RuntimeException e)
+ {
+ throw e;
+ }
+ catch (Exception e)
+ {
+ throw new FacesException(e);
+ }
+ }
+
+ if (jsf12)
+ {
+ try
+ {
+ clazz = this.getClass().getClassLoader().loadClass(
+ "org.apache.myfaces.test.mock.MockExternalContext12");
+ }
+ catch (RuntimeException e)
+ {
+ throw e;
+ }
+ catch (Exception e)
+ {
+ throw new FacesException(e);
+ }
+ }
+
+ // Select the constructor we wish to call
+ Constructor mecConstructor = null;
+ try
+ {
+ mecConstructor = clazz.getConstructor(externalContextSignature);
+ }
+ catch (RuntimeException e)
+ {
+ throw e;
+ }
+ catch (Exception e)
+ {
+ throw new FacesException(e);
+ }
+
+ // Construct an appropriate MockExternalContext instance
+ MockExternalContext externalContext = null;
+ try
+ {
+ externalContext = (MockExternalContext) mecConstructor
+ .newInstance(new Object[] { context, request, response });
+ }
+ catch (RuntimeException e)
+ {
+ throw e;
+ }
+ catch (Exception e)
+ {
+ throw new FacesException(e);
+ }
+
+ // Construct an appropriate MockFacesContext instance and return it
+ try
+ {
+ return (MockFacesContext) constructor.newInstance(new Object[] {
+ externalContext, lifecycle });
+ }
+ catch (RuntimeException e)
+ {
+ throw e;
+ }
+ catch (Exception e)
+ {
+ throw new FacesException(e);
+ }
+
+ }
+
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/mock/MockFlash.java b/test30/src/main/java/org/apache/myfaces/test/mock/MockFlash.java
new file mode 100644
index 0000000..cb592b4
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/mock/MockFlash.java
@@ -0,0 +1,823 @@
+/*
+ * 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.myfaces.test.mock;
+
+import javax.faces.application.FacesMessage;
+import javax.faces.context.ExternalContext;
+import javax.faces.context.FacesContext;
+import javax.faces.context.Flash;
+import javax.faces.event.PhaseId;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletResponse;
+import java.io.Serializable;
+import java.math.BigInteger;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+ * <p>Mock implementation of <code>Flash</code>.</p>
+ * <p/>
+ * $Id$
+ *
+ * @since 1.0.0
+ */
+public class MockFlash extends Flash
+{
+
+ /**
+ * Key on app map to keep current instance
+ */
+ static protected final String FLASH_INSTANCE = MockFlash.class.getName()
+ + ".INSTANCE";
+
+ /**
+ * Key used to check if there is the current request will be or was redirected
+ */
+ static protected final String FLASH_REDIRECT = MockFlash.class.getName()
+ + ".REDIRECT";
+
+ /**
+ * Key used to check if this request should keep messages (like tomahawk sandbox RedirectTracker.
+ * Used when post-redirect-get pattern is used)
+ */
+ static protected final String FLASH_KEEP_MESSAGES = MockFlash.class
+ .getName()
+ + ".KEEP_MESSAGES";
+
+ static protected final String FLASH_KEEP_MESSAGES_LIST = "KEEPMESSAGESLIST";
+
+ /**
+ * Session map prefix to flash maps
+ */
+ static protected final String FLASH_SCOPE_CACHE = MockFlash.class.getName()
+ + ".SCOPE";
+
+ static protected final String FLASH_CURRENT_MAP_CACHE = MockFlash.class
+ .getName()
+ + ".CURRENTMAP.CACHE";
+
+ static protected final String FLASH_CURRENT_MAP_KEY = MockFlash.class
+ .getName()
+ + ".CURRENTMAP.KEY";
+
+ static protected final String FLASH_POSTBACK_MAP_CACHE = MockFlash.class
+ .getName()
+ + ".POSTBACKMAP.CACHE";
+
+ static protected final String FLASH_POSTBACK_MAP_KEY = MockFlash.class
+ .getName()
+ + ".POSTBACKMAP.KEY";
+
+ static private final char SEPARATOR_CHAR = '.';
+
+ // the current token value
+ private final AtomicLong _count;
+
+ public MockFlash()
+ {
+ _count = new AtomicLong(_getSeed());
+ }
+
+ /**
+ * @return a cryptographically secure random number to use as the _count seed
+ */
+ private static long _getSeed()
+ {
+ SecureRandom rng;
+ try
+ {
+ // try SHA1 first
+ rng = SecureRandom.getInstance("SHA1PRNG");
+ }
+ catch (NoSuchAlgorithmException e)
+ {
+ // SHA1 not present, so try the default (which could potentially not be
+ // cryptographically secure)
+ rng = new SecureRandom();
+ }
+
+ // use 48 bits for strength and fill them in
+ byte[] randomBytes = new byte[6];
+ rng.nextBytes(randomBytes);
+
+ // convert to a long
+ return new BigInteger(randomBytes).longValue();
+ }
+
+ /**
+ * @return the next token to be assigned to this request
+ */
+ protected String _getNextToken()
+ {
+ // atomically increment the value
+ long nextToken = _count.incrementAndGet();
+
+ // convert using base 36 because it is a fast efficient subset of base-64
+ return Long.toString(nextToken, 36);
+ }
+
+ /**
+ * Return a Flash instance from the application map
+ *
+ * @param context
+ * @return
+ */
+ public static Flash getCurrentInstance(ExternalContext context)
+ {
+ Map<String, Object> applicationMap = context.getApplicationMap();
+ Flash flash = (Flash) applicationMap.get(FLASH_INSTANCE);
+
+ synchronized (applicationMap)
+ {
+ if (flash == null)
+ {
+ flash = new MockFlash();
+ context.getApplicationMap().put(FLASH_INSTANCE, flash);
+ }
+ }
+ return flash;
+ }
+
+ /**
+ * Return a wrapper from the session map used to implement flash maps
+ * for more information see SubKeyMap doc
+ */
+ @SuppressWarnings("unchecked")
+ private Map<String, Object> _getMapFromSession(FacesContext context,
+ String token, boolean createIfNeeded)
+ {
+ ExternalContext external = context.getExternalContext();
+ Object session = external.getSession(true);
+
+ Map<String, Object> map = null;
+
+ // Synchronize on the session object to ensure that
+ // we don't ever create two different caches
+ synchronized (session)
+ {
+ map = (Map<String, Object>) external.getSessionMap().get(token);
+ if ((map == null) && createIfNeeded)
+ {
+ map = new MockSubKeyMap<Object>(context.getExternalContext()
+ .getSessionMap(), token);
+ }
+ }
+
+ return map;
+ }
+
+ /**
+ * Return the flash map created on this traversal. This one will be sent
+ * on next request, so it will be retrieved as postback map of the next
+ * request.
+ * <p/>
+ * Note it is supposed that FLASH_CURRENT_MAP_KEY is initialized before
+ * restore view phase (see doPrePhaseActions() for details).
+ *
+ * @param context
+ * @return
+ */
+ @SuppressWarnings("unchecked")
+ protected Map<String, Object> getCurrentRequestMap(FacesContext context)
+ {
+ Map<String, Object> requestMap = context.getExternalContext()
+ .getRequestMap();
+ Map<String, Object> map = (Map<String, Object>) requestMap
+ .get(FLASH_CURRENT_MAP_CACHE);
+ if (map == null)
+ {
+ String token = (String) requestMap.get(FLASH_CURRENT_MAP_KEY);
+ String fullToken = FLASH_SCOPE_CACHE + SEPARATOR_CHAR + token;
+ map = _getMapFromSession(context, fullToken, true);
+ requestMap.put(FLASH_CURRENT_MAP_CACHE, map);
+ }
+ return map;
+ }
+
+ @SuppressWarnings("unchecked")
+ protected Map<String, Object> getPostbackRequestMap(FacesContext context)
+ {
+ Map<String, Object> requestMap = context.getExternalContext()
+ .getRequestMap();
+ Map<String, Object> map = (Map<String, Object>) requestMap
+ .get(FLASH_POSTBACK_MAP_CACHE);
+ if (map == null)
+ {
+ String token = (String) requestMap.get(FLASH_POSTBACK_MAP_KEY);
+ if (token == null && isRedirect())
+ {
+ // In post-redirect-get, request values are reset, so we need
+ // to get the postback key again.
+ token = _getPostbackMapKey(context.getExternalContext());
+ }
+ String fullToken = FLASH_SCOPE_CACHE + SEPARATOR_CHAR + token;
+ map = _getMapFromSession(context, fullToken, true);
+ requestMap.put(FLASH_POSTBACK_MAP_CACHE, map);
+ }
+ return map;
+ }
+
+ /**
+ * Get the proper map according to the current phase:
+ * <p/>
+ * Normal case:
+ * <p/>
+ * - First request, restore view phase (create a new one): current map n
+ * - First request, execute phase: Skipped
+ * - First request, render phase: current map n
+ * <p/>
+ * Current map n saved and put as postback map n
+ * <p/>
+ * - Second request, execute phase: postback map n
+ * - Second request, render phase: current map n+1
+ * <p/>
+ * Post Redirect Get case: Redirect is triggered by a call to setRedirect(true) from NavigationHandler
+ * or earlier using c:set tag.
+ * <p/>
+ * - First request, restore view phase (create a new one): current map n
+ * - First request, execute phase: Skipped
+ * - First request, render phase: current map n
+ * <p/>
+ * Current map n saved and put as postback map n
+ * <p/>
+ * POST
+ * <p/>
+ * - Second request, execute phase: postback map n
+ * <p/>
+ * REDIRECT
+ * <p/>
+ * - NavigationHandler do the redirect, requestMap data lost, called Flash.setRedirect(true)
+ * <p/>
+ * Current map n saved and put as postback map n
+ * <p/>
+ * GET
+ * <p/>
+ * - Third request, restore view phase (create a new one): current map n+1
+ * (isRedirect() should return true as javadoc says)
+ * - Third request, execute phase: skipped
+ * - Third request, render phase: current map n+1
+ * <p/>
+ * In this way proper behavior is preserved even in the case of redirect, since the GET part is handled as
+ * the "render" part of the current traversal, keeping the semantic of flash object.
+ *
+ * @return
+ */
+ private Map<String, Object> getCurrentPhaseMap()
+ {
+ FacesContext facesContext = FacesContext.getCurrentInstance();
+ if (PhaseId.RENDER_RESPONSE.equals(facesContext.getCurrentPhaseId())
+ || !facesContext.isPostback() || isRedirect())
+ {
+ return getCurrentRequestMap(facesContext);
+ }
+ else
+ {
+ return getPostbackRequestMap(facesContext);
+ }
+ }
+
+ private void _removeAllChildren(FacesContext facesContext)
+ {
+ Map<String, Object> map = getPostbackRequestMap(facesContext);
+
+ // Clear everything - note that because of naming conventions,
+ // this will in fact automatically recurse through all children
+ // grandchildren etc. - which is kind of a design flaw of SubKeyMap,
+ // but one we're relying on
+ map.clear();
+ }
+
+ /**
+ *
+ */
+ @Override
+ public void doPrePhaseActions(FacesContext facesContext)
+ {
+ Map<String, Object> requestMap = facesContext.getExternalContext()
+ .getRequestMap();
+
+ if (PhaseId.RESTORE_VIEW.equals(facesContext.getCurrentPhaseId()))
+ {
+ // Generate token and put on requestMap
+ // It is necessary to set this token always, because on the next request
+ // it should be possible to change postback map.
+ String currentToken = _getNextToken();
+ requestMap.put(FLASH_CURRENT_MAP_KEY, currentToken);
+
+ if (facesContext.isPostback())
+ {
+ //Retore token
+ String previousToken = _getPostbackMapKey(facesContext
+ .getExternalContext());
+
+ if (previousToken != null)
+ {
+ requestMap.put(FLASH_POSTBACK_MAP_KEY, previousToken);
+ }
+ }
+
+ if (isKeepMessages())
+ {
+ restoreMessages(facesContext);
+ }
+ }
+
+ //
+ if (PhaseId.RENDER_RESPONSE.equals(facesContext.getCurrentPhaseId()))
+ {
+ // Put current map on next previous map
+ // but only if this request is not a redirect
+ if (!isRedirect())
+ {
+ _addPostbackMapKey(facesContext.getExternalContext());
+ }
+ }
+ }
+
+ @Override
+ public void doPostPhaseActions(FacesContext facesContext)
+ {
+ if (PhaseId.RENDER_RESPONSE.equals(facesContext.getCurrentPhaseId()))
+ {
+ //Remove previous flash from session
+ Map<String, Object> requestMap = facesContext.getExternalContext()
+ .getRequestMap();
+ String token = (String) requestMap.get(FLASH_POSTBACK_MAP_KEY);
+
+ if (token != null)
+ {
+ _removeAllChildren(facesContext);
+ }
+
+ if (isKeepMessages())
+ {
+ saveMessages(facesContext);
+ }
+ }
+ else if (isRedirect()
+ && (facesContext.getResponseComplete() || facesContext
+ .getRenderResponse()))
+ {
+ if (isKeepMessages())
+ {
+ saveMessages(facesContext);
+ }
+ }
+ }
+
+ private static class MessageEntry implements Serializable
+ {
+ private final Object clientId;
+ private final Object message;
+
+ public MessageEntry(Object clientId, Object message)
+ {
+ this.clientId = clientId;
+ this.message = message;
+ }
+ }
+
+ protected void saveMessages(FacesContext facesContext)
+ {
+ List<MessageEntry> messageList = null;
+
+ Iterator<String> iterClientIds = facesContext
+ .getClientIdsWithMessages();
+ while (iterClientIds.hasNext())
+ {
+ String clientId = (String) iterClientIds.next();
+ Iterator<FacesMessage> iterMessages = facesContext
+ .getMessages(clientId);
+
+ while (iterMessages.hasNext())
+ {
+ FacesMessage message = iterMessages.next();
+
+ if (messageList == null)
+ {
+ messageList = new ArrayList<MessageEntry>();
+ }
+ messageList.add(new MessageEntry(clientId, message));
+ }
+ }
+
+ if (messageList != null)
+ {
+ if (isRedirect())
+ {
+ getPostbackRequestMap(facesContext).put(
+ FLASH_KEEP_MESSAGES_LIST, messageList);
+ }
+ else
+ {
+ getCurrentRequestMap(facesContext).put(
+ FLASH_KEEP_MESSAGES_LIST, messageList);
+ }
+ }
+ }
+
+ protected void restoreMessages(FacesContext facesContext)
+ {
+ Map<String, Object> postbackMap = getPostbackRequestMap(facesContext);
+ List<MessageEntry> messageList = (List<MessageEntry>) postbackMap
+ .get(FLASH_KEEP_MESSAGES_LIST);
+
+ if (messageList != null)
+ {
+ Iterator iterMessages = messageList.iterator();
+
+ while (iterMessages.hasNext())
+ {
+ MessageEntry message = (MessageEntry) iterMessages.next();
+ facesContext.addMessage((String) message.clientId,
+ (FacesMessage) message.message);
+ }
+
+ postbackMap.remove(FLASH_KEEP_MESSAGES_LIST);
+ }
+ }
+
+ //private void _addPreviousToken
+
+ /**
+ * Retrieve the postback map key
+ */
+ private String _getPostbackMapKey(ExternalContext externalContext)
+ {
+ String token = null;
+ Object response = externalContext.getResponse();
+ if (response instanceof HttpServletResponse)
+ {
+ //Use a cookie
+ Cookie cookie = (Cookie) externalContext.getRequestCookieMap().get(
+ FLASH_POSTBACK_MAP_KEY);
+ if (cookie != null)
+ {
+ token = cookie.getValue();
+ }
+ }
+ else
+ {
+ //Use HttpSession or PortletSession object
+ Map<String, Object> sessionMap = externalContext.getSessionMap();
+ token = (String) sessionMap.get(FLASH_POSTBACK_MAP_KEY);
+ }
+ return token;
+ }
+
+ /**
+ * Take the current map key and store it as a postback key for the next request.
+ *
+ * @param externalContext
+ */
+ private void _addPostbackMapKey(ExternalContext externalContext)
+ {
+ Object response = externalContext.getResponse();
+ String token = (String) externalContext.getRequestMap().get(
+ FLASH_CURRENT_MAP_KEY);
+
+ //Use HttpSession or PortletSession object
+ Map<String, Object> sessionMap = externalContext.getSessionMap();
+ sessionMap.put(FLASH_POSTBACK_MAP_KEY, token);
+ }
+
+ /**
+ * For check if there is a redirect we to take into accout this points:
+ * <p/>
+ * 1. isRedirect() could be accessed many times during the same
+ * request.
+ * 2. According to Post-Redirect-Get pattern, we cannot
+ * ensure request scope values are preserved.
+ */
+ @Override
+ public boolean isRedirect()
+ {
+ FacesContext facesContext = FacesContext.getCurrentInstance();
+ ExternalContext externalContext = facesContext.getExternalContext();
+ Map<String, Object> requestMap = externalContext.getRequestMap();
+ Boolean redirect = (Boolean) requestMap.get(FLASH_REDIRECT);
+ if (redirect == null)
+ {
+ Object response = externalContext.getResponse();
+ if (response instanceof HttpServletResponse)
+ {
+ // Request values are lost after a redirect. We can create a
+ // temporal cookie to pass the params between redirect calls.
+ // It is better than use HttpSession object, because this cookie
+ // is never sent by the server.
+ Cookie cookie = (Cookie) externalContext.getRequestCookieMap()
+ .get(FLASH_REDIRECT);
+ if (cookie != null)
+ {
+ redirect = Boolean.TRUE;
+ HttpServletResponse httpResponse = (HttpServletResponse) response;
+ // A redirect happened, so it is safe to remove the cookie, setting
+ // the maxAge to 0 seconds. The effect is we passed FLASH_REDIRECT param
+ // to this request object
+ cookie.setMaxAge(0);
+ cookie.setValue(null);
+ httpResponse.addCookie(cookie);
+ requestMap.put(FLASH_REDIRECT, redirect);
+ }
+ else
+ {
+ redirect = Boolean.FALSE;
+ }
+ }
+ else
+ {
+ // Note that on portlet world we can't create cookies,
+ // so we are forced to use the session map. Anyway,
+ // according to the Bridge implementation(for example see
+ // org.apache.myfaces.portlet.faces.bridge.BridgeImpl)
+ // session object is created at start faces request
+ Map<String, Object> sessionMap = externalContext
+ .getSessionMap();
+ redirect = (Boolean) sessionMap.get(FLASH_REDIRECT);
+ if (redirect != null)
+ {
+ sessionMap.remove(FLASH_REDIRECT);
+ requestMap.put(FLASH_REDIRECT, redirect);
+ }
+ else
+ {
+ redirect = Boolean.FALSE;
+ }
+ }
+ }
+ return redirect;
+ }
+
+ @Override
+ public void setRedirect(boolean redirect)
+ {
+ FacesContext facesContext = FacesContext.getCurrentInstance();
+ ExternalContext externalContext = facesContext.getExternalContext();
+ Map<String, Object> requestMap = externalContext.getRequestMap();
+
+ Boolean previousRedirect = (Boolean) requestMap.get(FLASH_REDIRECT);
+ previousRedirect = (previousRedirect == null) ? Boolean.FALSE
+ : previousRedirect;
+
+ if (!previousRedirect.booleanValue() && redirect)
+ {
+ // This request contains a redirect. This condition is in general
+ // triggered by a NavigationHandler. After a redirect all request scope
+ // values get lost, so in order to preserve this value we need to
+ // pass it between request. One strategy is use a cookie that is never sent
+ // to the client. Other alternative is use the session map.
+ externalContext.getSessionMap().put(FLASH_REDIRECT, redirect);
+ }
+ requestMap.put(FLASH_REDIRECT, redirect);
+ }
+
+ /**
+ * In few words take a value from request scope map and put it on current request map,
+ * so it is visible on the next request.
+ */
+ @Override
+ public void keep(String key)
+ {
+ FacesContext facesContext = FacesContext.getCurrentInstance();
+ Map<String, Object> requestMap = facesContext.getExternalContext()
+ .getRequestMap();
+ Object value = requestMap.get(key);
+ getCurrentRequestMap(facesContext).put(key, value);
+ }
+
+ /**
+ * This is just an alias for request scope map.
+ */
+ @Override
+ public void putNow(String key, Object value)
+ {
+ FacesContext.getCurrentInstance().getExternalContext().getRequestMap()
+ .put(key, value);
+ }
+
+ @Override
+ public boolean isKeepMessages()
+ {
+ FacesContext facesContext = FacesContext.getCurrentInstance();
+ ExternalContext externalContext = facesContext.getExternalContext();
+ Map<String, Object> requestMap = externalContext.getRequestMap();
+ Boolean keepMessages = (Boolean) requestMap.get(FLASH_KEEP_MESSAGES);
+ if (keepMessages == null)
+ {
+ Object response = externalContext.getResponse();
+ if (response instanceof HttpServletResponse)
+ {
+ // Request values are lost after a redirect. We can create a
+ // temporal cookie to pass the params between redirect calls.
+ // It is better than use HttpSession object, because this cookie
+ // is never sent by the server.
+ Cookie cookie = (Cookie) externalContext.getRequestCookieMap()
+ .get(FLASH_KEEP_MESSAGES);
+ if (cookie != null)
+ {
+ keepMessages = Boolean.TRUE;
+ HttpServletResponse httpResponse = (HttpServletResponse) response;
+ // It is safe to remove the cookie, setting
+ // the maxAge to 0 seconds. The effect is we passed FLASH_KEEP_MESSAGES param
+ // to this request object
+ cookie.setMaxAge(0);
+ cookie.setValue(null);
+ httpResponse.addCookie(cookie);
+ requestMap.put(FLASH_KEEP_MESSAGES, keepMessages);
+ }
+ else
+ {
+ keepMessages = Boolean.FALSE;
+ }
+ }
+ else
+ {
+ // Note that on portlet world we can't create cookies,
+ // so we are forced to use the session map. Anyway,
+ // according to the Bridge implementation(for example see
+ // org.apache.myfaces.portlet.faces.bridge.BridgeImpl)
+ // session object is created at start faces request
+ Map<String, Object> sessionMap = externalContext
+ .getSessionMap();
+ keepMessages = (Boolean) sessionMap.get(FLASH_KEEP_MESSAGES);
+ if (keepMessages != null)
+ {
+ sessionMap.remove(FLASH_KEEP_MESSAGES);
+ requestMap.put(FLASH_KEEP_MESSAGES, keepMessages);
+ }
+ else
+ {
+ keepMessages = Boolean.FALSE;
+ }
+ }
+ }
+ return keepMessages;
+ }
+
+ /**
+ * If this property is true, the messages should be keep for the next request, no matter
+ * if it is a normal postback case or a post-redirect-get case.
+ */
+ @Override
+ public void setKeepMessages(boolean keepMessages)
+ {
+ FacesContext facesContext = FacesContext.getCurrentInstance();
+ ExternalContext externalContext = facesContext.getExternalContext();
+ Map<String, Object> requestMap = externalContext.getRequestMap();
+
+ Boolean previousKeepMessages = (Boolean) requestMap
+ .get(FLASH_KEEP_MESSAGES);
+ previousKeepMessages = (previousKeepMessages == null) ? Boolean.FALSE
+ : previousKeepMessages;
+
+ if (!previousKeepMessages.booleanValue() && keepMessages)
+ {
+ externalContext.getSessionMap().put(FLASH_KEEP_MESSAGES,
+ keepMessages);
+ }
+ requestMap.put(FLASH_KEEP_MESSAGES, keepMessages);
+ }
+
+ public void clear()
+ {
+ getCurrentPhaseMap().clear();
+ }
+
+ public boolean containsKey(Object key)
+ {
+ return getCurrentPhaseMap().containsKey(key);
+ }
+
+ public boolean containsValue(Object value)
+ {
+ return getCurrentPhaseMap().containsValue(value);
+ }
+
+ public Set<Entry<String, Object>> entrySet()
+ {
+ return getCurrentPhaseMap().entrySet();
+ }
+
+ public Object get(Object key)
+ {
+ if (key == null)
+ {
+ return null;
+ }
+
+ if ("keepMessages".equals(key))
+ {
+ return isKeepMessages();
+ }
+ else if ("redirect".equals(key))
+ {
+ return isRedirect();
+ }
+
+ FacesContext context = FacesContext.getCurrentInstance();
+ Map<String, Object> postbackMap = getPostbackRequestMap(context);
+ Object returnValue = null;
+
+ if (postbackMap != null)
+ {
+ returnValue = postbackMap.get(key);
+ }
+
+ return returnValue;
+ }
+
+ public boolean isEmpty()
+ {
+ return getCurrentPhaseMap().isEmpty();
+ }
+
+ public Set<String> keySet()
+ {
+ return getCurrentPhaseMap().keySet();
+ }
+
+ public Object put(String key, Object value)
+ {
+ if (key == null)
+ {
+ return null;
+ }
+
+ if ("keepMessages".equals(key))
+ {
+ Boolean booleanValue = convertToBoolean(value);
+ this.setKeepMessages(booleanValue);
+ return booleanValue;
+ }
+ else if ("redirect".equals(key))
+ {
+ Boolean booleanValue = convertToBoolean(value);
+ this.setRedirect(booleanValue);
+ return booleanValue;
+ }
+ else
+ {
+ Object returnValue = getCurrentPhaseMap().put(key, value);
+ return returnValue;
+ }
+ }
+
+ private Boolean convertToBoolean(Object value)
+ {
+ Boolean booleanValue;
+ if (value instanceof Boolean)
+ {
+ booleanValue = (Boolean) value;
+ }
+ else
+ {
+ booleanValue = Boolean.parseBoolean(value.toString());
+ }
+ return booleanValue;
+ }
+
+ public void putAll(Map<? extends String, ? extends Object> m)
+ {
+ getCurrentPhaseMap().putAll(m);
+ }
+
+ public Object remove(Object key)
+ {
+ return getCurrentPhaseMap().remove(key);
+ }
+
+ public int size()
+ {
+ return getCurrentPhaseMap().size();
+ }
+
+ public Collection<Object> values()
+ {
+ return getCurrentPhaseMap().values();
+ }
+
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/mock/MockFlowHandler.java b/test30/src/main/java/org/apache/myfaces/test/mock/MockFlowHandler.java
new file mode 100644
index 0000000..969f669
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/mock/MockFlowHandler.java
@@ -0,0 +1,90 @@
+/*
+ * 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.myfaces.test.mock;
+
+import java.util.Collections;
+import java.util.Map;
+import javax.faces.context.FacesContext;
+import javax.faces.flow.Flow;
+import javax.faces.flow.FlowCallNode;
+import javax.faces.flow.FlowHandler;
+
+/**
+ *
+ * @author Leonardo Uribe
+ */
+public class MockFlowHandler extends FlowHandler
+{
+
+ @Override
+ public Map<Object, Object> getCurrentFlowScope()
+ {
+ return Collections.emptyMap();
+ }
+
+ @Override
+ public Flow getFlow(FacesContext context, String definingDocumentId, String id)
+ {
+ return null;
+ }
+
+ @Override
+ public void addFlow(FacesContext context, Flow toAdd)
+ {
+ }
+
+ @Override
+ public Flow getCurrentFlow(FacesContext context)
+ {
+ return null;
+ }
+
+ @Override
+ public boolean isActive(FacesContext context, String definingDocument, String id)
+ {
+ return false;
+ }
+
+ @Override
+ public void transition(FacesContext context, Flow sourceFlow, Flow targetFlow,
+ FlowCallNode outboundCallNode, String toViewId)
+ {
+ }
+
+ @Override
+ public void clientWindowTransition(FacesContext context)
+ {
+ }
+
+ @Override
+ public String getLastDisplayedViewId(FacesContext context)
+ {
+ return null;
+ }
+
+ @Override
+ public void pushReturnMode(FacesContext context)
+ {
+ }
+
+ @Override
+ public void popReturnMode(FacesContext context)
+ {
+ }
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/mock/MockHttpServletRequest.java b/test30/src/main/java/org/apache/myfaces/test/mock/MockHttpServletRequest.java
new file mode 100644
index 0000000..ee61e8b
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/mock/MockHttpServletRequest.java
@@ -0,0 +1,1094 @@
+/*
+ * 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.myfaces.test.mock;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.io.UnsupportedEncodingException;
+import java.security.Principal;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.TimeZone;
+import java.util.Vector;
+import javax.servlet.AsyncContext;
+import javax.servlet.DispatcherType;
+
+import javax.servlet.RequestDispatcher;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.ServletInputStream;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletRequestAttributeEvent;
+import javax.servlet.ServletRequestAttributeListener;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import javax.servlet.http.HttpSessionEvent;
+import javax.servlet.http.Part;
+
+/**
+ * <p>Mock implementation of <code>HttpServletContext</code>.</p>
+ *
+ * $Id$
+ * @since 1.0.0
+ */
+public class MockHttpServletRequest implements HttpServletRequest
+{
+
+ // ------------------------------------------------------------ Constructors
+
+ public MockHttpServletRequest()
+ {
+
+ super();
+
+ }
+
+ public MockHttpServletRequest(HttpSession session)
+ {
+
+ super();
+ setHttpSession(session);
+
+ }
+
+ public MockHttpServletRequest(String contextPath, String servletPath,
+ String pathInfo, String queryString)
+ {
+
+ super();
+ setPathElements(contextPath, servletPath, pathInfo, queryString);
+
+ }
+
+ public MockHttpServletRequest(String contextPath, String servletPath,
+ String pathInfo, String queryString, HttpSession session)
+ {
+
+ super();
+ setPathElements(contextPath, servletPath, pathInfo, queryString);
+ setHttpSession(session);
+
+ }
+
+ // ----------------------------------------------------- Mock Object Methods
+
+ /**
+ * <p>Add a new listener instance that should be notified about
+ * attribute changes.</p>
+ *
+ * @param listener The new listener to register
+ */
+ public void addAttributeListener(ServletRequestAttributeListener listener)
+ {
+ attributeListeners.add(listener);
+ }
+
+ /**
+ * <p>Add a date-valued header for this request.</p>
+ *
+ * @param name Header name
+ * @param value Header value
+ */
+ public void addDateHeader(String name, long value)
+ {
+
+ headers.add(name + ": " + formatDate(value));
+
+ }
+
+ /**
+ * <p>Add a String-valued header for this request.</p>
+ *
+ * @param name Header name
+ * @param value Header value
+ */
+ public void addHeader(String name, String value)
+ {
+
+ headers.add(name + ": " + value);
+
+ }
+
+ /**
+ * <p>Add an integer-valued header for this request.</p>
+ *
+ * @param name Header name
+ * @param value Header value
+ */
+ public void addIntHeader(String name, int value)
+ {
+
+ headers.add(name + ": " + value);
+
+ }
+
+ /**
+ * <p>Add a request parameter for this request.</p>
+ *
+ * @param name Parameter name
+ * @param value Parameter value
+ */
+ public void addParameter(String name, String value)
+ {
+
+ String[] values = (String[]) parameters.get(name);
+ if (values == null)
+ {
+ String[] results = new String[] { value };
+ parameters.put(name, results);
+ return;
+ }
+ String[] results = new String[values.length + 1];
+ System.arraycopy(values, 0, results, 0, values.length);
+ results[values.length] = value;
+ parameters.put(name, results);
+
+ }
+
+ public void addCookie(Cookie c)
+ {
+ for (int i = 0; i < cookies.size(); i++)
+ {
+ if (((Cookie) cookies.get(i)).getName().equals(c.getName()))
+ {
+ cookies.set(i, c);
+ return;
+ }
+ }
+ cookies.add(c);
+ }
+
+ /**
+ * <p>Return the <code>ServletContext</code> associated with
+ * this request.</p>
+ */
+ public ServletContext getServletContext()
+ {
+
+ return this.servletContext;
+
+ }
+
+ /**
+ * <p>Set the <code>HttpSession</code> associated with this request.</p>
+ *
+ * @param session The new session
+ */
+ public void setHttpSession(HttpSession session)
+ {
+
+ this.session = session;
+
+ }
+
+ /**
+ * <p>Set the <code>Locale</code> associated with this request.</p>
+ *
+ * @param locale The new locale
+ */
+ public void setLocale(Locale locale)
+ {
+
+ this.locale = locale;
+
+ }
+
+ /**
+ * <p>Set the parsed path elements associated with this request.</p>
+ *
+ * @param contextPath The context path
+ * @param servletPath The servlet path
+ * @param pathInfo The extra path information
+ * @param queryString The query string
+ */
+ public void setPathElements(String contextPath, String servletPath,
+ String pathInfo, String queryString)
+ {
+
+ this.contextPath = contextPath;
+ this.servletPath = servletPath;
+ this.pathInfo = pathInfo;
+ this.queryString = queryString;
+
+ }
+
+ /**
+ * <p>Set the <code>ServletContext</code> associated with this request.</p>
+ *
+ * @param servletContext The new servlet context
+ */
+ public void setServletContext(ServletContext servletContext)
+ {
+
+ this.servletContext = servletContext;
+
+ }
+
+ /**
+ * <p>Set the <code>Principal</code> associated with this request.</p>
+ *
+ * @param principal The new Principal
+ */
+ public void setUserPrincipal(Principal principal)
+ {
+
+ this.principal = principal;
+
+ }
+
+ public void setMethod(String method)
+ {
+ this.method = method;
+ }
+
+ protected MockWebContainer getWebContainer()
+ {
+ if (this.servletContext instanceof MockServletContext)
+ {
+ return ((MockServletContext)this.servletContext).getWebContainer();
+ }
+ return null;
+ }
+
+ /**
+ * @param contextPath the contextPath to set
+ */
+ public void setContextPath(String contextPath)
+ {
+ this.contextPath = contextPath;
+ }
+
+ /**
+ * @param pathInfo the pathInfo to set
+ */
+ public void setPathInfo(String pathInfo)
+ {
+ this.pathInfo = pathInfo;
+ }
+
+ /**
+ * @param servletPath the servletPath to set
+ */
+ public void setServletPath(String servletPath)
+ {
+ this.servletPath = servletPath;
+ }
+
+ public void addUserRole(String role)
+ {
+ if (this.roles == null)
+ {
+ this.roles = new ArrayList<String>();
+ }
+ this.roles.add(role);
+ }
+
+ public void clearUserRoles()
+ {
+ if (this.roles == null)
+ {
+ return;
+ }
+ this.roles.clear();
+ }
+
+ // ------------------------------------------------------ Instance Variables
+
+ private List attributeListeners = new ArrayList();
+ private HashMap attributes = new HashMap();
+ private String contextPath = null;
+ private List headers = new ArrayList();
+ private Locale locale = null;
+ private HashMap parameters = new HashMap();
+ private String pathInfo = null;
+ private Principal principal = null;
+ private String queryString = null;
+ private ServletContext servletContext = null;
+ private String servletPath = null;
+ private HttpSession session = null;
+ private String characterEncoding = null;
+ private ServletInputStream inputStream = null;
+ private List cookies = new ArrayList();
+ private Vector locales = null;
+ private String method = null;
+ private List<String> roles = null;
+
+ // ---------------------------------------------- HttpServletRequest Methods
+
+ /** {@inheritDoc} */
+ public String getAuthType()
+ {
+
+ throw new UnsupportedOperationException();
+
+ }
+
+ /** {@inheritDoc} */
+ public String getContextPath()
+ {
+
+ return contextPath;
+
+ }
+
+ /** {@inheritDoc} */
+ public Cookie[] getCookies()
+ {
+
+ Cookie[] array = new Cookie[cookies.size()];
+ for (int i = 0; i < cookies.size(); i++)
+ {
+ array[i] = (Cookie) cookies.get(i);
+ }
+ return array;
+ }
+
+ /** {@inheritDoc} */
+ public long getDateHeader(String name)
+ {
+
+ String match = name + ":";
+ Iterator headers = this.headers.iterator();
+ while (headers.hasNext())
+ {
+ String header = (String) headers.next();
+ if (header.startsWith(match))
+ {
+ return parseDate(header.substring(match.length() + 1).trim());
+ }
+ }
+ return (long) -1;
+
+ }
+
+ /** {@inheritDoc} */
+ public String getHeader(String name)
+ {
+
+ String match = name + ":";
+ Iterator headers = this.headers.iterator();
+ while (headers.hasNext())
+ {
+ String header = (String) headers.next();
+ if (header.startsWith(match))
+ {
+ return header.substring(match.length() + 1).trim();
+ }
+ }
+ return null;
+
+ }
+
+ /** {@inheritDoc} */
+ public Enumeration getHeaderNames()
+ {
+
+ Vector values = new Vector();
+ Iterator headers = this.headers.iterator();
+ while (headers.hasNext())
+ {
+ String header = (String) headers.next();
+ int colon = header.indexOf(':');
+ if (colon >= 0)
+ {
+ String name = header.substring(0, colon).trim();
+ if (!values.contains(name))
+ {
+ values.add(name);
+ }
+ }
+ }
+ return values.elements();
+
+ }
+
+ /** {@inheritDoc} */
+ public Enumeration getHeaders(String name)
+ {
+
+ String match = name + ":";
+ Vector values = new Vector();
+ Iterator headers = this.headers.iterator();
+ while (headers.hasNext())
+ {
+ String header = (String) headers.next();
+ if (header.startsWith(match))
+ {
+ values.add(header.substring(match.length() + 1).trim());
+ }
+ }
+ return values.elements();
+
+ }
+
+ /** {@inheritDoc} */
+ public int getIntHeader(String name)
+ {
+
+ String match = name + ":";
+ Iterator headers = this.headers.iterator();
+ while (headers.hasNext())
+ {
+ String header = (String) headers.next();
+ if (header.startsWith(match))
+ {
+ return Integer.parseInt(header.substring(match.length() + 1)
+ .trim());
+ }
+ }
+ return -1;
+
+ }
+
+ /** {@inheritDoc} */
+ public String getMethod()
+ {
+
+ return method;
+
+ }
+
+ /** {@inheritDoc} */
+ public String getPathInfo()
+ {
+
+ return pathInfo;
+
+ }
+
+ /** {@inheritDoc} */
+ public String getPathTranslated()
+ {
+
+ throw new UnsupportedOperationException();
+
+ }
+
+ /** {@inheritDoc} */
+ public String getQueryString()
+ {
+
+ return queryString;
+
+ }
+
+ /** {@inheritDoc} */
+ public String getRemoteUser()
+ {
+
+ if (principal != null)
+ {
+ return principal.getName();
+ }
+ else
+ {
+ return null;
+ }
+
+ }
+
+ /** {@inheritDoc} */
+ public String getRequestedSessionId()
+ {
+
+ throw new UnsupportedOperationException();
+
+ }
+
+ /** {@inheritDoc} */
+ public String getRequestURI()
+ {
+
+ StringBuffer sb = new StringBuffer();
+ if (contextPath != null)
+ {
+ sb.append(contextPath);
+ }
+ if (servletPath != null)
+ {
+ sb.append(servletPath);
+ }
+ if (pathInfo != null)
+ {
+ sb.append(pathInfo);
+ }
+ if (sb.length() > 0)
+ {
+ return sb.toString();
+ }
+ throw new UnsupportedOperationException();
+
+ }
+
+ /** {@inheritDoc} */
+ public StringBuffer getRequestURL()
+ {
+
+ throw new UnsupportedOperationException();
+
+ }
+
+ /** {@inheritDoc} */
+ public String getServletPath()
+ {
+
+ return (servletPath);
+
+ }
+
+ /** {@inheritDoc} */
+ public HttpSession getSession()
+ {
+
+ return getSession(true);
+
+ }
+
+ /** {@inheritDoc} */
+ public HttpSession getSession(boolean create)
+ {
+
+ if (create && (session == null))
+ {
+ this.session = new MockHttpSession(this.servletContext);
+ MockWebContainer container = getWebContainer();
+ if (container != null)
+ {
+ HttpSessionEvent se = new HttpSessionEvent(this.session);
+ container.sessionCreated(se);
+ }
+ }
+ return session;
+
+ }
+
+ /** {@inheritDoc} */
+ public Principal getUserPrincipal()
+ {
+
+ return principal;
+
+ }
+
+ /** {@inheritDoc} */
+ public boolean isRequestedSessionIdFromCookie()
+ {
+
+ throw new UnsupportedOperationException();
+
+ }
+
+ /** {@inheritDoc} */
+ public boolean isRequestedSessionIdFromUrl()
+ {
+
+ throw new UnsupportedOperationException();
+
+ }
+
+ /** {@inheritDoc} */
+ public boolean isRequestedSessionIdFromURL()
+ {
+
+ throw new UnsupportedOperationException();
+
+ }
+
+ /** {@inheritDoc} */
+ public boolean isRequestedSessionIdValid()
+ {
+
+ throw new UnsupportedOperationException();
+
+ }
+
+ /** {@inheritDoc} */
+ public boolean isUserInRole(String role)
+ {
+ return (this.roles != null) ? this.roles.contains(role) : false;
+ }
+
+ // ------------------------------------------------- ServletRequest Methods
+
+ /** {@inheritDoc} */
+ public Object getAttribute(String name)
+ {
+
+ return attributes.get(name);
+
+ }
+
+ /** {@inheritDoc} */
+ public Enumeration getAttributeNames()
+ {
+
+ return new MockEnumeration(attributes.keySet().iterator());
+
+ }
+
+ /** {@inheritDoc} */
+ public String getCharacterEncoding()
+ {
+
+ return characterEncoding;
+
+ }
+
+ /** {@inheritDoc} */
+ public int getContentLength()
+ {
+
+ throw new UnsupportedOperationException();
+
+ }
+
+ /** {@inheritDoc} */
+ public String getContentType()
+ {
+
+ throw new UnsupportedOperationException();
+
+ }
+
+ /** {@inheritDoc} */
+ public ServletInputStream getInputStream()
+ {
+ return this.inputStream;
+ }
+
+ public void setInputStream(MockServletInputStream stream)
+ {
+ this.inputStream = stream;
+ }
+
+ /** {@inheritDoc} */
+ public Locale getLocale()
+ {
+
+ return locale;
+
+ }
+
+ /** {@inheritDoc} */
+ public Enumeration getLocales()
+ {
+ if (this.locales == null)
+ {
+ locales = new Vector(Arrays.asList(Locale.getAvailableLocales()));
+ }
+ return this.locales.elements();
+ }
+
+ /** {@inheritDoc} */
+ public String getLocalAddr()
+ {
+
+ throw new UnsupportedOperationException();
+
+ }
+
+ /** {@inheritDoc} */
+ public String getLocalName()
+ {
+
+ throw new UnsupportedOperationException();
+
+ }
+
+ /** {@inheritDoc} */
+ public int getLocalPort()
+ {
+
+ throw new UnsupportedOperationException();
+
+ }
+
+ /** {@inheritDoc} */
+ public String getParameter(String name)
+ {
+
+ String[] values = (String[]) parameters.get(name);
+ if (values != null)
+ {
+ return values[0];
+ }
+ else
+ {
+ return null;
+ }
+
+ }
+
+ /** {@inheritDoc} */
+ public Map getParameterMap()
+ {
+
+ return parameters;
+
+ }
+
+ /** {@inheritDoc} */
+ public Enumeration getParameterNames()
+ {
+
+ return new MockEnumeration(parameters.keySet().iterator());
+
+ }
+
+ /** {@inheritDoc} */
+ public String[] getParameterValues(String name)
+ {
+
+ return (String[]) parameters.get(name);
+
+ }
+
+ /** {@inheritDoc} */
+ public String getProtocol()
+ {
+
+ throw new UnsupportedOperationException();
+
+ }
+
+ /** {@inheritDoc} */
+ public BufferedReader getReader()
+ {
+ if (this.inputStream != null)
+ {
+ try
+ {
+ Reader sourceReader = (this.characterEncoding != null) ? new InputStreamReader(
+ this.inputStream, this.characterEncoding)
+ : new InputStreamReader(this.inputStream);
+ return new BufferedReader(sourceReader);
+ }
+ catch (UnsupportedEncodingException e)
+ {
+ throw new RuntimeException(e);
+ }
+ }
+ else
+ {
+ return null;
+ }
+ }
+
+ /** {@inheritDoc} */
+ public String getRealPath(String path)
+ {
+
+ throw new UnsupportedOperationException();
+
+ }
+
+ /** {@inheritDoc} */
+ public String getRemoteAddr()
+ {
+
+ // i figure testing never assumes a specific remote - so anything works
+ return "1.2.3.4";
+
+ }
+
+ /** {@inheritDoc} */
+ public String getRemoteHost()
+ {
+
+ // i figure testing never assumes a specific remote - so anything works
+ return "MyfacesServer";
+
+ }
+
+ /** {@inheritDoc} */
+ public int getRemotePort()
+ {
+
+ // i figure testing never assumes a specific remote - so anything works
+ return 46123;
+
+ }
+
+ /** {@inheritDoc} */
+ public RequestDispatcher getRequestDispatcher(String path)
+ {
+ return servletContext.getRequestDispatcher(path);
+ }
+
+ /** {@inheritDoc} */
+ public String getScheme()
+ {
+
+ return ("http");
+
+ }
+
+ /** {@inheritDoc} */
+ public String getServerName()
+ {
+
+ return ("localhost");
+
+ }
+
+ /** {@inheritDoc} */
+ public int getServerPort()
+ {
+
+ return (8080);
+
+ }
+
+ /** {@inheritDoc} */
+ public boolean isSecure()
+ {
+
+ return false;
+
+ }
+
+ /** {@inheritDoc} */
+ public void removeAttribute(String name)
+ {
+
+ if (attributes.containsKey(name))
+ {
+ Object value = attributes.remove(name);
+ fireAttributeRemoved(name, value);
+ }
+
+ }
+
+ /** {@inheritDoc} */
+ public void setAttribute(String name, Object value)
+ {
+
+ if (name == null)
+ {
+ throw new IllegalArgumentException("Attribute name cannot be null");
+ }
+ if (value == null)
+ {
+ removeAttribute(name);
+ return;
+ }
+ if (attributes.containsKey(name))
+ {
+ Object oldValue = attributes.get(name);
+ attributes.put(name, value);
+ fireAttributeReplaced(name, oldValue);
+ }
+ else
+ {
+ attributes.put(name, value);
+ fireAttributeAdded(name, value);
+ }
+
+ }
+
+ /** {@inheritDoc} */
+ public void setCharacterEncoding(String characterEncoding)
+ {
+
+ this.characterEncoding = characterEncoding;
+
+ }
+
+ // --------------------------------------------------------- Private Methods
+
+ /**
+ * <p>Fire an attribute added event to interested listeners.</p>
+ *
+ * @param key Attribute key whose value was added
+ * @param value The new attribute value
+ */
+ private void fireAttributeAdded(String key, Object value)
+ {
+ if (attributeListeners.size() < 1)
+ {
+ return;
+ }
+ ServletRequestAttributeEvent event = new ServletRequestAttributeEvent(
+ getServletContext(), this, key, value);
+ Iterator listeners = attributeListeners.iterator();
+ while (listeners.hasNext())
+ {
+ ServletRequestAttributeListener listener = (ServletRequestAttributeListener) listeners
+ .next();
+ listener.attributeAdded(event);
+ }
+ }
+
+ /**
+ * <p>Fire an attribute removed event to interested listeners.</p>
+ *
+ * @param key Attribute key whose value was removed
+ * @param value Attribute value that was removed
+ */
+ private void fireAttributeRemoved(String key, Object value)
+ {
+ if (attributeListeners.size() < 1)
+ {
+ return;
+ }
+ ServletRequestAttributeEvent event = new ServletRequestAttributeEvent(
+ getServletContext(), this, key, value);
+ Iterator listeners = attributeListeners.iterator();
+ while (listeners.hasNext())
+ {
+ ServletRequestAttributeListener listener = (ServletRequestAttributeListener) listeners
+ .next();
+ listener.attributeRemoved(event);
+ }
+ }
+
+ /**
+ * <p>Fire an attribute replaced event to interested listeners.</p>
+ *
+ * @param key Attribute key whose value was replaced
+ * @param value The original value
+ */
+ private void fireAttributeReplaced(String key, Object value)
+ {
+ if (attributeListeners.size() < 1)
+ {
+ return;
+ }
+ ServletRequestAttributeEvent event = new ServletRequestAttributeEvent(
+ getServletContext(), this, key, value);
+ Iterator listeners = attributeListeners.iterator();
+ while (listeners.hasNext())
+ {
+ ServletRequestAttributeListener listener = (ServletRequestAttributeListener) listeners
+ .next();
+ listener.attributeReplaced(event);
+ }
+ }
+
+ /**
+ * <p>The date formatting helper we will use in <code>httpTimestamp()</code>.
+ * Note that usage of this helper must be synchronized.</p>
+ */
+ private static SimpleDateFormat format = new SimpleDateFormat(
+ "EEE, dd MMM yyyy HH:mm:ss zzz");
+ static
+ {
+ format.setTimeZone(TimeZone.getTimeZone("GMT"));
+ }
+
+ /**
+ * <p>Return a properly formatted String version of the specified
+ * date/time, formatted as required by the HTTP specification.</p>
+ *
+ * @param date Date/time, expressed as milliseconds since the epoch
+ */
+ private String formatDate(long date)
+ {
+ return format.format(new Date(date));
+ }
+
+ /**
+ * <p>Return a date/time value, parsed from the specified String.</p>
+ *
+ * @param date Date/time, expressed as a String
+ */
+ private long parseDate(String date)
+ {
+ try
+ {
+ return format.parse(date).getTime();
+ }
+ catch (ParseException e)
+ {
+ throw new IllegalArgumentException(date);
+ }
+ }
+
+ public boolean authenticate(HttpServletResponse hsr) throws IOException, ServletException
+ {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ public Part getPart(String string) throws IOException, ServletException
+ {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ public Collection<Part> getParts() throws IOException, ServletException
+ {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ public void login(String string, String string1) throws ServletException
+ {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ public void logout() throws ServletException
+ {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ public AsyncContext getAsyncContext()
+ {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ public DispatcherType getDispatcherType()
+ {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ public boolean isAsyncStarted()
+ {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ public boolean isAsyncSupported()
+ {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ public AsyncContext startAsync()
+ {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ public AsyncContext startAsync(ServletRequest sr, ServletResponse sr1)
+ {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/mock/MockHttpServletResponse.java b/test30/src/main/java/org/apache/myfaces/test/mock/MockHttpServletResponse.java
new file mode 100644
index 0000000..35b1716
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/mock/MockHttpServletResponse.java
@@ -0,0 +1,474 @@
+/*
+ * 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.myfaces.test.mock;
+
+import java.io.ByteArrayOutputStream;
+import java.io.CharArrayWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.TimeZone;
+
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * <p>Mock implementation of <code>HttpServletResponse</code>.</p>
+ *
+ * $Id$
+ * @since 1.0.0
+ */
+public class MockHttpServletResponse implements HttpServletResponse
+{
+
+ // ------------------------------------------------------------ Constructors
+
+ /**
+ * <p>Return a default instance.</p>
+ */
+ public MockHttpServletResponse()
+ {
+ }
+
+ // ----------------------------------------------------- Mock Object Methods
+
+ /**
+ * <p>Retrieve the first value that was set for the specified header,
+ * if any. Otherwise, return <code>null</code>.</p>
+ *
+ * @param name Header name to look up
+ */
+ public String getHeader(String name)
+ {
+ String match = name + ":";
+ Iterator headers = this.headers.iterator();
+ while (headers.hasNext())
+ {
+ String header = (String) headers.next();
+ if (header.startsWith(match))
+ {
+ return header.substring(match.length() + 1).trim();
+ }
+ }
+ return null;
+ }
+
+ public Cookie getCookie(String name)
+ {
+ return (Cookie) cookies.get(name);
+ }
+
+ public Map<String, Cookie> getCookies()
+ {
+ return cookies;
+ }
+
+ /**
+ * <p>Return the text message for the HTTP status that was set.</p>
+ */
+ public String getMessage()
+ {
+ return this.message;
+ }
+
+ /**
+ * <p>Return the HTTP status code that was set.</p>
+ */
+ public int getStatus()
+ {
+ return this.status;
+ }
+
+ /**
+ * <p>Set the <code>ServletOutputStream</code> to be returned by a call to
+ * <code>getOutputStream()</code>.</p>
+ *
+ * @param stream The <code>ServletOutputStream</code> instance to use
+ *
+ * @deprecated Let the <code>getOutputStream()</code> method create and
+ * return an instance of <code>MockServletOutputStream</code> for you
+ */
+ public void setOutputStream(ServletOutputStream stream)
+ {
+ this.stream = stream;
+ }
+
+ /**
+ * <p>Set the <code>PrintWriter</code> to be returned by a call to
+ * <code>getWriter()</code>.</p>
+ *
+ * @param writer The <code>PrintWriter</code> instance to use
+ *
+ * @deprecated Let the <code>getWriter()</code> method create and return
+ * an instance of <code>MockPrintWriter</code> for you
+ */
+ public void setWriter(PrintWriter writer)
+ {
+ this.writer = writer;
+ }
+
+ // ------------------------------------------------------ Instance Variables
+
+ private String encoding = "ISO-8859-1";
+ private String contentType = "text/html";
+ private List headers = new ArrayList();
+ private String message = null;
+ private int status = HttpServletResponse.SC_OK;
+ private ServletOutputStream stream = null;
+ private PrintWriter writer = null;
+
+ private boolean committed = false;
+ private long contentLength = 0;
+ private int bufferSize = 0;
+ private Locale locale = Locale.getDefault();
+ private Map<String, Cookie> cookies = new HashMap<String, Cookie>(4);
+
+ // -------------------------------------------- HttpServletResponse Methods
+
+ /** {@inheritDoc} */
+ public void addCookie(Cookie cookie)
+ {
+ cookies.put(cookie.getName(), cookie);
+ }
+
+ /** {@inheritDoc} */
+ public void addDateHeader(String name, long value)
+ {
+
+ headers.add(name + ": " + formatDate(value));
+
+ }
+
+ /** {@inheritDoc} */
+ public void addHeader(String name, String value)
+ {
+
+ headers.add(name + ": " + value);
+
+ }
+
+ /** {@inheritDoc} */
+ public void addIntHeader(String name, int value)
+ {
+
+ headers.add(name + ": " + value);
+
+ }
+
+ /** {@inheritDoc} */
+ public boolean containsHeader(String name)
+ {
+
+ return getHeader(name) != null;
+
+ }
+
+ /** {@inheritDoc} */
+ public String encodeRedirectUrl(String url)
+ {
+
+ return encodeRedirectURL(url);
+
+ }
+
+ /** {@inheritDoc} */
+ public String encodeRedirectURL(String url)
+ {
+
+ return url;
+
+ }
+
+ /** {@inheritDoc} */
+ public String encodeUrl(String url)
+ {
+
+ return encodeURL(url);
+
+ }
+
+ /** {@inheritDoc} */
+ public String encodeURL(String url)
+ {
+
+ return url;
+
+ }
+
+ /** {@inheritDoc} */
+ public void sendError(int status)
+ {
+ if (this.committed)
+ {
+ throw new IllegalStateException("Response is already committed");
+ }
+ this.status = status;
+ this.committed = true;
+ }
+
+ /** {@inheritDoc} */
+ public void sendError(int status, String message)
+ {
+ if (this.committed)
+ {
+ throw new IllegalStateException("Response is already committed");
+ }
+ this.status = status;
+ this.message = message;
+ this.committed = true;
+ }
+
+ /** {@inheritDoc} */
+ public void sendRedirect(String location)
+ {
+ if (this.committed)
+ {
+ throw new IllegalStateException("Response is already committed");
+ }
+ setStatus(HttpServletResponse.SC_FOUND);
+ setHeader("Location", location);
+ this.message = location;
+ this.committed = true;
+ }
+
+ /** {@inheritDoc} */
+ public void setDateHeader(String name, long value)
+ {
+
+ removeHeader(name);
+ addDateHeader(name, value);
+
+ }
+
+ /** {@inheritDoc} */
+ public void setHeader(String name, String value)
+ {
+
+ removeHeader(name);
+ addHeader(name, value);
+
+ }
+
+ /** {@inheritDoc} */
+ public void setIntHeader(String name, int value)
+ {
+
+ removeHeader(name);
+ addIntHeader(name, value);
+
+ }
+
+ /** {@inheritDoc} */
+ public void setStatus(int status)
+ {
+ this.status = status;
+ }
+
+ /** {@inheritDoc} */
+ public void setStatus(int status, String message)
+ {
+ this.status = status;
+ this.message = message;
+ }
+
+ // ------------------------------------------------ ServletResponse Methods
+
+ /** {@inheritDoc} */
+ public void flushBuffer()
+ {
+
+ }
+
+ /** {@inheritDoc} */
+ public int getBufferSize()
+ {
+ return bufferSize;
+ }
+
+ /** {@inheritDoc} */
+ public String getCharacterEncoding()
+ {
+
+ return this.encoding;
+
+ }
+
+ /** {@inheritDoc} */
+ public String getContentType()
+ {
+
+ return this.contentType;
+
+ }
+
+ /** {@inheritDoc} */
+ public Locale getLocale()
+ {
+ return this.locale;
+ }
+
+ /** {@inheritDoc} */
+ public ServletOutputStream getOutputStream() throws IOException
+ {
+
+ if (stream == null)
+ {
+ if (writer != null)
+ {
+ throw new IllegalStateException(
+ "Cannot call getOutputStream() after getWriter() has been called");
+ }
+ stream = new MockServletOutputStream(new ByteArrayOutputStream());
+ }
+ return stream;
+
+ }
+
+ /** {@inheritDoc} */
+ public PrintWriter getWriter() throws IOException
+ {
+
+ if (writer == null)
+ {
+ if (stream != null)
+ {
+ throw new IllegalStateException(
+ "Cannot call getWriter() after getOutputStream() was called");
+ }
+ writer = new MockPrintWriter(new CharArrayWriter());
+ }
+ return writer;
+
+ }
+
+ /** {@inheritDoc} */
+ public boolean isCommitted()
+ {
+ return committed;
+ }
+
+ /** {@inheritDoc} */
+ public void reset()
+ {
+ }
+
+ /** {@inheritDoc} */
+ public void resetBuffer()
+ {
+ }
+
+ /** {@inheritDoc} */
+ public void setBufferSize(int size)
+ {
+ this.bufferSize = size;
+ }
+
+ /** {@inheritDoc} */
+ public void setCharacterEncoding(String charset)
+ {
+
+ this.encoding = charset;
+
+ }
+
+ /** {@inheritDoc} */
+ public void setContentLength(int length)
+ {
+ this.contentLength = length;
+ }
+
+ /** {@inheritDoc} */
+ public void setContentType(String type)
+ {
+
+ contentType = type;
+
+ }
+
+ /** {@inheritDoc} */
+ public void setLocale(Locale locale)
+ {
+ this.locale = locale;
+ }
+
+ // --------------------------------------------------------- Private Methods
+
+ /**
+ * <p>The date formatting helper we will use in <code>httpTimestamp()</code>.
+ * Note that usage of this helper must be synchronized.</p>
+ */
+ private static SimpleDateFormat format = new SimpleDateFormat(
+ "EEE, dd MMM yyyy HH:mm:ss zzz");
+ static
+ {
+ format.setTimeZone(TimeZone.getTimeZone("GMT"));
+ }
+
+ /**
+ * <p>Return a properly formatted String version of the specified
+ * date/time, formatted as required by the HTTP specification.</p>
+ *
+ * @param date Date/time, expressed as milliseconds since the epoch
+ */
+ private String formatDate(long date)
+ {
+ return format.format(new Date(date));
+ }
+
+ /**
+ * <p>Remove any header that has been set with the specific name.</p>
+ *
+ * @param name Header name to look up
+ */
+ private void removeHeader(String name)
+ {
+ String match = name + ":";
+ Iterator headers = this.headers.iterator();
+ while (headers.hasNext())
+ {
+ String header = (String) headers.next();
+ if (header.startsWith(match))
+ {
+ headers.remove();
+ return;
+ }
+ }
+ }
+
+ public Collection<String> getHeaderNames()
+ {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ public Collection<String> getHeaders(String string)
+ {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/mock/MockHttpSession.java b/test30/src/main/java/org/apache/myfaces/test/mock/MockHttpSession.java
new file mode 100644
index 0000000..f3d629b
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/mock/MockHttpSession.java
@@ -0,0 +1,454 @@
+/*
+ * 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.myfaces.test.mock;
+
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpSession;
+import javax.servlet.http.HttpSessionAttributeListener;
+import javax.servlet.http.HttpSessionBindingEvent;
+import javax.servlet.http.HttpSessionBindingListener;
+import javax.servlet.http.HttpSessionContext;
+import javax.servlet.http.HttpSessionEvent;
+
+/**
+ * <p>Mock implementation of <code>HttpSession</code>.</p>
+ *
+ * $Id$
+ * @since 1.0.0
+ */
+public class MockHttpSession implements HttpSession
+{
+
+ // ------------------------------------------------------------ Constructors
+
+ /**
+ * <p>Configure a default instance.</p>
+ */
+ public MockHttpSession()
+ {
+
+ super();
+
+ }
+
+ /**
+ * <p>Configure a session instance associated with the specified
+ * servlet context.</p>
+ *
+ * @param servletContext The associated servlet context
+ */
+ public MockHttpSession(ServletContext servletContext)
+ {
+
+ super();
+ setServletContext(servletContext);
+
+ }
+
+ // ----------------------------------------------------- Mock Object Methods
+
+ /**
+ * <p>Add a new listener instance that should be notified about
+ * attribute changes.</p>
+ *
+ * @param listener The new listener to be added
+ */
+ public void addAttributeListener(HttpSessionAttributeListener listener)
+ {
+ MockWebContainer container = getWebContainer();
+ if (container == null)
+ {
+ attributeListeners.add(listener);
+ }
+ else
+ {
+ container.subscribeListener(listener);
+ }
+ }
+
+ /**
+ * <p>Set the ServletContext associated with this session.</p>
+ *
+ * @param servletContext The associated servlet context
+ */
+ public void setServletContext(ServletContext servletContext)
+ {
+
+ this.servletContext = servletContext;
+
+ }
+
+ protected MockWebContainer getWebContainer()
+ {
+ if (this.servletContext instanceof MockServletContext)
+ {
+ return ((MockServletContext)this.servletContext).getWebContainer();
+ }
+ return null;
+ }
+
+ // ------------------------------------------------------ Instance Variables
+
+ private List attributeListeners = new ArrayList();
+ private HashMap attributes = new HashMap();
+ private String id = "123";
+ private ServletContext servletContext = null;
+ private boolean invalid = false;
+ private MockWebContainer webContainer;
+
+ // ---------------------------------------------------------- Public Methods
+
+ /**
+ * <p>Set the session identifier of this session.</p>
+ *
+ * @param id The new session identifier
+ */
+ public void setId(String id)
+ {
+ this.id = id;
+ }
+
+ // ----------------------------------------------------- HttpSession Methods
+
+ /** {@inheritDoc} */
+ public Object getAttribute(String name)
+ {
+
+ assertValidity();
+
+ return attributes.get(name);
+
+ }
+
+ /** {@inheritDoc} */
+ public Enumeration getAttributeNames()
+ {
+
+ assertValidity();
+
+ return new MockEnumeration(attributes.keySet().iterator());
+
+ }
+
+ /** {@inheritDoc} */
+ public long getCreationTime()
+ {
+
+ throw new UnsupportedOperationException();
+
+ }
+
+ /** {@inheritDoc} */
+ public String getId()
+ {
+
+ return this.id;
+
+ }
+
+ /** {@inheritDoc} */
+ public long getLastAccessedTime()
+ {
+
+ throw new UnsupportedOperationException();
+
+ }
+
+ /** {@inheritDoc} */
+ public int getMaxInactiveInterval()
+ {
+
+ throw new UnsupportedOperationException();
+
+ }
+
+ /** {@inheritDoc} */
+ public ServletContext getServletContext()
+ {
+
+ return this.servletContext;
+
+ }
+
+ /** {@inheritDoc} */
+ public HttpSessionContext getSessionContext()
+ {
+
+ throw new UnsupportedOperationException();
+
+ }
+
+ /** {@inheritDoc} */
+ public Object getValue(String name)
+ {
+
+ throw new UnsupportedOperationException();
+
+ }
+
+ /** {@inheritDoc} */
+ public String[] getValueNames()
+ {
+
+ throw new UnsupportedOperationException();
+
+ }
+
+ /** {@inheritDoc} */
+ public void invalidate()
+ {
+
+ assertValidity();
+
+ attributes.clear();
+ invalid = true;
+
+ MockWebContainer container = getWebContainer();
+ if (container != null)
+ {
+ HttpSessionEvent se = new HttpSessionEvent(this);
+ container.sessionDestroyed(se);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public boolean isNew()
+ {
+
+ throw new UnsupportedOperationException();
+
+ }
+
+ /** {@inheritDoc} */
+ public void putValue(String name, Object value)
+ {
+
+ throw new UnsupportedOperationException();
+
+ }
+
+ /** {@inheritDoc} */
+ public void removeAttribute(String name)
+ {
+
+ assertValidity();
+
+ if (attributes.containsKey(name))
+ {
+ Object value = attributes.remove(name);
+ fireAttributeRemoved(name, value);
+ }
+
+ }
+
+ /** {@inheritDoc} */
+ public void removeValue(String name)
+ {
+
+ throw new UnsupportedOperationException();
+
+ }
+
+ /** {@inheritDoc} */
+ public void setAttribute(String name, Object value)
+ {
+
+ assertValidity();
+
+ if (name == null)
+ {
+ throw new IllegalArgumentException("Attribute name cannot be null");
+ }
+ if (value == null)
+ {
+ removeAttribute(name);
+ return;
+ }
+ if (attributes.containsKey(name))
+ {
+ Object oldValue = attributes.get(name);
+ attributes.put(name, value);
+ fireAttributeReplaced(name, oldValue, value);
+ }
+ else
+ {
+ attributes.put(name, value);
+ fireAttributeAdded(name, value);
+ }
+
+ }
+
+ /** {@inheritDoc} */
+ public void setMaxInactiveInterval(int interval)
+ {
+
+ throw new UnsupportedOperationException();
+
+ }
+
+ // --------------------------------------------------------- Support Methods
+
+ /**
+ * <p>Fire an attribute added event to interested listeners.</p>
+ *
+ * @param key Attribute whose value was added
+ * @param value The new value
+ */
+ private void fireAttributeAdded(String key, Object value)
+ {
+ MockWebContainer container = getWebContainer();
+ if (container == null)
+ {
+ if (attributeListeners.size() < 1)
+ {
+ return;
+ }
+ HttpSessionBindingEvent event = new HttpSessionBindingEvent(this, key,
+ value);
+ if (value instanceof HttpSessionBindingListener)
+ {
+ ((HttpSessionBindingListener)value).valueBound(event);
+ }
+ Iterator listeners = attributeListeners.iterator();
+ while (listeners.hasNext())
+ {
+ HttpSessionAttributeListener listener = (HttpSessionAttributeListener) listeners
+ .next();
+ listener.attributeAdded(event);
+ }
+ }
+ else
+ {
+ HttpSessionBindingEvent event = new HttpSessionBindingEvent(this, key,
+ value);
+ if (value instanceof HttpSessionBindingListener)
+ {
+ ((HttpSessionBindingListener)value).valueBound(event);
+ }
+ container.attributeAdded(event);
+ }
+ }
+
+ /**
+ * <p>Fire an attribute removed event to interested listeners.</p>
+ *
+ * @param key Attribute whose value was removed
+ * @param value The removed value
+ */
+ private void fireAttributeRemoved(String key, Object value)
+ {
+ MockWebContainer container = getWebContainer();
+ if (container == null)
+ {
+ if (attributeListeners.size() < 1)
+ {
+ return;
+ }
+ HttpSessionBindingEvent event = new HttpSessionBindingEvent(this, key,
+ value);
+ if (value instanceof HttpSessionBindingListener)
+ {
+ ((HttpSessionBindingListener)value).valueUnbound(event);
+ }
+ Iterator listeners = attributeListeners.iterator();
+ while (listeners.hasNext())
+ {
+ HttpSessionAttributeListener listener = (HttpSessionAttributeListener) listeners
+ .next();
+ listener.attributeRemoved(event);
+ }
+ }
+ else
+ {
+ HttpSessionBindingEvent event = new HttpSessionBindingEvent(this, key,
+ value);
+ if (value instanceof HttpSessionBindingListener)
+ {
+ ((HttpSessionBindingListener)value).valueUnbound(event);
+ }
+ container.attributeRemoved(event);
+ }
+ }
+
+ /**
+ * <p>Fire an attribute replaced event to interested listeners.</p>
+ *
+ * @param key Attribute whose value was replaced
+ * @param value The original value
+ */
+ private void fireAttributeReplaced(String key, Object oldValue, Object value)
+ {
+ if (oldValue instanceof HttpSessionBindingListener)
+ {
+ HttpSessionBindingEvent event = new HttpSessionBindingEvent(this, key,
+ oldValue);
+ ((HttpSessionBindingListener)value).valueUnbound(event);
+ }
+ MockWebContainer container = getWebContainer();
+ if (container == null)
+ {
+ if (attributeListeners.size() < 1)
+ {
+ return;
+ }
+ HttpSessionBindingEvent event = new HttpSessionBindingEvent(this, key,
+ value);
+ if (value instanceof HttpSessionBindingListener)
+ {
+ ((HttpSessionBindingListener)value).valueBound(event);
+ }
+ Iterator listeners = attributeListeners.iterator();
+ while (listeners.hasNext())
+ {
+ HttpSessionAttributeListener listener = (HttpSessionAttributeListener) listeners
+ .next();
+ listener.attributeReplaced(event);
+ }
+ }
+ else
+ {
+ HttpSessionBindingEvent event = new HttpSessionBindingEvent(this, key,
+ value);
+ if (value instanceof HttpSessionBindingListener)
+ {
+ ((HttpSessionBindingListener)value).valueBound(event);
+ }
+ container.attributeReplaced(event);
+ }
+ }
+
+ /**
+ * <p>Throws an {@link IllegalStateException} if this session is invalid.</p>
+ */
+ private void assertValidity()
+ {
+ if (invalid)
+ {
+ throw new IllegalStateException("Session is invalid.");
+ }
+ }
+
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/mock/MockHttpSessionProxy.java b/test30/src/main/java/org/apache/myfaces/test/mock/MockHttpSessionProxy.java
new file mode 100644
index 0000000..181da0d
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/mock/MockHttpSessionProxy.java
@@ -0,0 +1,205 @@
+/*
+ * 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.myfaces.test.mock;
+
+import java.util.Enumeration;
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpSessionAttributeListener;
+import javax.servlet.http.HttpSessionContext;
+import javax.servlet.http.HttpSessionEvent;
+
+/**
+ * Proxy used to trigger session creation when it is accessed
+ */
+public class MockHttpSessionProxy extends MockHttpSession
+{
+ private MockServletContext servletContext;
+ private MockHttpServletRequest request;
+ private MockHttpSession delegate;
+
+ public MockHttpSessionProxy(MockServletContext servletContext,
+ MockHttpServletRequest request)
+ {
+ super(servletContext);
+ this.servletContext = servletContext;
+ }
+
+ @Override
+ public void addAttributeListener(HttpSessionAttributeListener listener)
+ {
+ getWrapped().addAttributeListener(listener);
+ }
+
+ @Override
+ public void setServletContext(ServletContext servletContext)
+ {
+ if (servletContext instanceof MockServletContext)
+ {
+ this.servletContext = (MockServletContext) servletContext;
+ }
+ getWrapped().setServletContext(servletContext);
+ }
+
+ @Override
+ public void setId(String id)
+ {
+ getWrapped().setId(id);
+ }
+
+ @Override
+ public Object getAttribute(String name)
+ {
+ return getWrapped().getAttribute(name);
+ }
+
+ @Override
+ public Enumeration getAttributeNames()
+ {
+ return getWrapped().getAttributeNames();
+ }
+
+ @Override
+ public long getCreationTime()
+ {
+ return getWrapped().getCreationTime();
+ }
+
+ @Override
+ public String getId()
+ {
+ return getWrapped().getId();
+ }
+
+ @Override
+ public long getLastAccessedTime()
+ {
+ return getWrapped().getLastAccessedTime();
+ }
+
+ @Override
+ public int getMaxInactiveInterval()
+ {
+ return getWrapped().getMaxInactiveInterval();
+ }
+
+ @Override
+ public ServletContext getServletContext()
+ {
+ return servletContext == null ? getWrapped().getServletContext() : servletContext;
+ }
+
+ @Override
+ public HttpSessionContext getSessionContext()
+ {
+ return getWrapped().getSessionContext();
+ }
+
+ @Override
+ public Object getValue(String name)
+ {
+ return getWrapped().getValue(name);
+ }
+
+ @Override
+ public String[] getValueNames()
+ {
+ return getWrapped().getValueNames();
+ }
+
+ @Override
+ public void invalidate()
+ {
+ getWrapped().invalidate();
+ }
+
+ @Override
+ public boolean isNew()
+ {
+ return getWrapped().isNew();
+ }
+
+ @Override
+ public void putValue(String name, Object value)
+ {
+ getWrapped().putValue(name, value);
+ }
+
+ @Override
+ public void removeAttribute(String name)
+ {
+ getWrapped().removeAttribute(name);
+ }
+
+ @Override
+ public void removeValue(String name)
+ {
+ getWrapped().removeValue(name);
+ }
+
+ @Override
+ public void setAttribute(String name, Object value)
+ {
+ getWrapped().setAttribute(name, value);
+ }
+
+ @Override
+ public void setMaxInactiveInterval(int interval)
+ {
+ getWrapped().setMaxInactiveInterval(interval);
+ }
+
+ public MockHttpSession getWrapped()
+ {
+ if (delegate == null)
+ {
+ if (request != null)
+ {
+ delegate = (MockHttpSession) request.getSession(true);
+ }
+ else
+ {
+ delegate = new MockHttpSession(this.servletContext);
+ MockWebContainer container = getWebContainer();
+ if (container != null)
+ {
+ HttpSessionEvent se = new HttpSessionEvent(delegate);
+ container.sessionCreated(se);
+ }
+ }
+ }
+ return delegate;
+ }
+
+ /**
+ * @return the request
+ */
+ public MockHttpServletRequest getRequest()
+ {
+ return request;
+ }
+
+ /**
+ * @param request the request to set
+ */
+ public void setRequest(MockHttpServletRequest request)
+ {
+ this.request = request;
+ }
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/mock/MockNavigationHandler.java b/test30/src/main/java/org/apache/myfaces/test/mock/MockNavigationHandler.java
new file mode 100644
index 0000000..07c3567
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/mock/MockNavigationHandler.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.myfaces.test.mock;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.faces.application.NavigationHandler;
+import javax.faces.application.ViewHandler;
+import javax.faces.component.UIViewRoot;
+import javax.faces.context.FacesContext;
+
+/**
+ * <p>Mock implementation of <code>NavigationHandler</code>.</p>
+ *
+ * $Id$
+ * @since 1.0.0
+ */
+
+public class MockNavigationHandler extends NavigationHandler
+{
+
+ // ------------------------------------------------------------ Constructors
+
+ /**
+ * <p>Construct a default instance.</p>
+ */
+ public MockNavigationHandler()
+ {
+ }
+
+ // ----------------------------------------------------- Mock Object Methods
+
+ /**
+ * <p>Add a outcome-viewId pair to the destinations map.</p>
+ *
+ * @param outcome Logical outcome string
+ * @param viewId Destination view identifier
+ */
+ public void addDestination(String outcome, String viewId)
+ {
+
+ destinations.put(outcome, viewId);
+
+ }
+
+ // ------------------------------------------------------ Instance Variables
+
+ /**
+ * <p>Set of destination view ids, keyed by logical outcome String
+ * that will cause navigation to that view id.</p>
+ */
+ private Map destinations = new HashMap();
+
+ // ----------------------------------------------- NavigationHandler Methods
+
+ /**
+ * <p>Process the specified navigation request.</p>
+ *
+ * @param context <code>FacesContext</code> for the current request
+ * @param action Action method being executed
+ * @param outcome Logical outcome from this action method
+ */
+ public void handleNavigation(FacesContext context, String action,
+ String outcome)
+ {
+
+ // Navigate solely based on outcome, if we get a match
+ String viewId = (String) destinations.get(outcome);
+ if (viewId != null)
+ {
+ UIViewRoot view = getViewHandler(context).createView(context,
+ viewId);
+ context.setViewRoot(view);
+ }
+
+ }
+
+ // --------------------------------------------------------- Private Methods
+
+ /**
+ * <p>Return the <code>ViewHandler</code> instance for this application.</p>
+ *
+ * @param context <code>FacesContext</code> for the current request
+ */
+ private ViewHandler getViewHandler(FacesContext context)
+ {
+
+ return context.getApplication().getViewHandler();
+
+ }
+
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/mock/MockPartialViewContext.java b/test30/src/main/java/org/apache/myfaces/test/mock/MockPartialViewContext.java
new file mode 100644
index 0000000..7526526
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/mock/MockPartialViewContext.java
@@ -0,0 +1,375 @@
+/*
+ * 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.myfaces.test.mock;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import javax.faces.component.UIViewRoot;
+import javax.faces.component.visit.VisitContext;
+import javax.faces.context.FacesContext;
+import javax.faces.context.PartialResponseWriter;
+import javax.faces.context.PartialViewContext;
+import javax.faces.context.ResponseWriter;
+import javax.faces.event.PhaseId;
+
+import org.apache.myfaces.test.mock.visit.MockVisitCallback;
+
+/**
+ * <p>Mock implementation of <code>PartialViewContext</code>.</p>
+ * <p/>
+ * $Id$
+ *
+ * @since 1.0.0
+ */
+public class MockPartialViewContext extends PartialViewContext
+{
+
+ private static final String FACES_REQUEST = "Faces-Request";
+ private static final String PARTIAL_AJAX = "partial/ajax";
+ private static final String PARTIAL_PROCESS = "partial/process";
+ private static final String SOURCE_PARAM_NAME = "javax.faces.source";
+ private FacesContext _facesContext = null;
+ private Boolean _ajaxRequest = null;
+ private Collection<String> _executeClientIds = null;
+ private Collection<String> _renderClientIds = null;
+ private Boolean _partialRequest = null;
+ private Boolean _renderAll = null;
+ private PartialResponseWriter _partialResponseWriter = null;
+ private List<String> _evalScripts = new ArrayList<String>();
+
+ public MockPartialViewContext(FacesContext context)
+ {
+ _facesContext = context;
+ }
+
+ @Override
+ public boolean isAjaxRequest()
+ {
+ if (_ajaxRequest == null)
+ {
+ String requestType = _facesContext.getExternalContext()
+ .getRequestHeaderMap().get(FACES_REQUEST);
+ _ajaxRequest = (requestType != null && PARTIAL_AJAX
+ .equals(requestType));
+ }
+ return _ajaxRequest;
+ }
+
+ @Override
+ public boolean isExecuteAll()
+ {
+
+ if (isAjaxRequest())
+ {
+ String executeMode = _facesContext.getExternalContext()
+ .getRequestParameterMap().get(
+ PartialViewContext.PARTIAL_EXECUTE_PARAM_NAME);
+ if (PartialViewContext.ALL_PARTIAL_PHASE_CLIENT_IDS
+ .equals(executeMode))
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public boolean isPartialRequest()
+ {
+
+ if (_partialRequest == null)
+ {
+ String requestType = _facesContext.getExternalContext()
+ .getRequestHeaderMap().get(FACES_REQUEST);
+ _partialRequest = (requestType != null && PARTIAL_PROCESS
+ .equals(requestType));
+ }
+ return isAjaxRequest() || _partialRequest;
+ }
+
+ @Override
+ public boolean isRenderAll()
+ {
+
+ if (_renderAll == null)
+ {
+ if (isAjaxRequest())
+ {
+ String executeMode = _facesContext.getExternalContext()
+ .getRequestParameterMap().get(
+ PartialViewContext.PARTIAL_RENDER_PARAM_NAME);
+ if (PartialViewContext.ALL_PARTIAL_PHASE_CLIENT_IDS
+ .equals(executeMode))
+ {
+ _renderAll = true;
+ }
+ }
+ if (_renderAll == null)
+ {
+ _renderAll = false;
+ }
+ }
+ return _renderAll;
+ }
+
+ @Override
+ public void setPartialRequest(boolean isPartialRequest)
+ {
+
+ _partialRequest = isPartialRequest;
+
+ }
+
+ @Override
+ public void setRenderAll(boolean renderAll)
+ {
+
+ _renderAll = renderAll;
+ }
+
+ @Override
+ public Collection<String> getExecuteIds()
+ {
+
+ if (_executeClientIds == null)
+ {
+ String executeMode = _facesContext.getExternalContext()
+ .getRequestParameterMap().get(
+ PartialViewContext.PARTIAL_EXECUTE_PARAM_NAME);
+
+ if (executeMode != null
+ && !"".equals(executeMode)
+ &&
+ //!PartialViewContext.NO_PARTIAL_PHASE_CLIENT_IDS.equals(executeMode) &&
+ !PartialViewContext.ALL_PARTIAL_PHASE_CLIENT_IDS
+ .equals(executeMode))
+ {
+
+ String[] clientIds = splitShortString(
+ _replaceTabOrEnterCharactersWithSpaces(executeMode),
+ ' ');
+
+ //The collection must be mutable
+ List<String> tempList = new ArrayList<String>();
+ for (String clientId : clientIds)
+ {
+ if (clientId.length() > 0)
+ {
+ tempList.add(clientId);
+ }
+ }
+ // The "javax.faces.source" parameter needs to be added to the list of
+ // execute ids if missing (otherwise, we'd never execute an action associated
+ // with, e.g., a button).
+
+ String source = _facesContext.getExternalContext()
+ .getRequestParameterMap().get(SOURCE_PARAM_NAME);
+
+ if (source != null)
+ {
+ source = source.trim();
+
+ if (!tempList.contains(source))
+ {
+ tempList.add(source);
+ }
+ }
+
+ _executeClientIds = tempList;
+ }
+ else
+ {
+ _executeClientIds = new ArrayList<String>();
+ }
+ }
+ return _executeClientIds;
+ }
+
+ @Override
+ public Collection<String> getRenderIds()
+ {
+
+ if (_renderClientIds == null)
+ {
+ String renderMode = _facesContext.getExternalContext()
+ .getRequestParameterMap().get(
+ PartialViewContext.PARTIAL_RENDER_PARAM_NAME);
+
+ if (renderMode != null
+ && !"".equals(renderMode)
+ &&
+ //!PartialViewContext.NO_PARTIAL_PHASE_CLIENT_IDS.equals(renderMode) &&
+ !PartialViewContext.ALL_PARTIAL_PHASE_CLIENT_IDS
+ .equals(renderMode))
+ {
+ String[] clientIds = splitShortString(
+ _replaceTabOrEnterCharactersWithSpaces(renderMode), ' ');
+
+ //The collection must be mutable
+ List<String> tempList = new ArrayList<String>();
+ for (String clientId : clientIds)
+ {
+ if (clientId.length() > 0)
+ {
+ tempList.add(clientId);
+ }
+ }
+ _renderClientIds = tempList;
+ }
+ else
+ {
+ _renderClientIds = new ArrayList<String>();
+
+ if (PartialViewContext.ALL_PARTIAL_PHASE_CLIENT_IDS
+ .equals(renderMode))
+ {
+ _renderClientIds
+ .add(PartialResponseWriter.RENDER_ALL_MARKER);
+ }
+ }
+ }
+ return _renderClientIds;
+ }
+
+ @Override
+ public PartialResponseWriter getPartialResponseWriter()
+ {
+ if (_partialResponseWriter == null)
+ {
+ ResponseWriter responseWriter = _facesContext.getResponseWriter();
+ if (responseWriter == null)
+ {
+ // This case happens when getPartialResponseWriter() is called before
+ // render phase, like in ExternalContext.redirect(). We have to create a
+ // ResponseWriter from the RenderKit and then wrap if necessary.
+ try
+ {
+ responseWriter = _facesContext.getRenderKit()
+ .createResponseWriter(
+ _facesContext.getExternalContext()
+ .getResponseOutputWriter(),
+ "text/xml",
+ _facesContext.getExternalContext()
+ .getRequestCharacterEncoding());
+ }
+ catch (IOException e)
+ {
+ throw new IllegalStateException(
+ "Cannot create Partial Response Writer", e);
+ }
+ }
+ // It is possible that the RenderKit return a PartialResponseWriter instance when
+ // createResponseWriter, so we should cast here for it and prevent double wrapping.
+ if (responseWriter instanceof PartialResponseWriter)
+ {
+ _partialResponseWriter = (PartialResponseWriter) responseWriter;
+ }
+ else
+ {
+ _partialResponseWriter = new PartialResponseWriter(
+ responseWriter);
+ }
+ }
+ return _partialResponseWriter;
+ }
+
+ @Override
+ public void processPartial(PhaseId phaseId)
+ {
+
+ UIViewRoot viewRoot = _facesContext.getViewRoot();
+
+ VisitContext visitCtx = VisitContext.createVisitContext(_facesContext,
+ null, null);
+ viewRoot.visitTree(visitCtx, new MockVisitCallback());
+ }
+
+ @Override
+ public void release()
+ {
+ _executeClientIds = null;
+ _renderClientIds = null;
+ _ajaxRequest = null;
+ _partialRequest = null;
+ _renderAll = null;
+ _facesContext = null;
+ _evalScripts = new ArrayList<String>();
+ }
+
+ public List<String> getEvalScripts()
+ {
+ return _evalScripts;
+ }
+
+ private static String[] splitShortString(String str, char separator)
+ {
+ int len = str.length();
+
+ int lastTokenIndex = 0;
+
+ // Step 1: how many substrings?
+ // We exchange double scan time for less memory allocation
+ for (int pos = str.indexOf(separator); pos >= 0; pos = str.indexOf(
+ separator, pos + 1))
+ {
+ lastTokenIndex++;
+ }
+
+ // Step 2: allocate exact size array
+ String[] list = new String[lastTokenIndex + 1];
+
+ int oldPos = 0;
+
+ // Step 3: retrieve substrings
+ int pos = str.indexOf(separator);
+ int i = 0;
+
+ while (pos >= 0)
+ {
+ list[i++] = str.substring(oldPos, pos);
+ oldPos = (pos + 1);
+ pos = str.indexOf(separator, oldPos);
+ }
+
+ list[lastTokenIndex] = str.substring(oldPos, len);
+
+ return list;
+ }
+
+ private String _replaceTabOrEnterCharactersWithSpaces(String mode)
+ {
+ StringBuilder builder = new StringBuilder(mode.length());
+ for (int i = 0; i < mode.length(); i++)
+ {
+ if (mode.charAt(i) == '\t' || mode.charAt(i) == '\n')
+ {
+ builder.append(' ');
+ }
+ else
+ {
+ builder.append(mode.charAt(i));
+ }
+ }
+ return builder.toString();
+ }
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/mock/MockPartialViewContextFactory.java b/test30/src/main/java/org/apache/myfaces/test/mock/MockPartialViewContextFactory.java
new file mode 100644
index 0000000..35fcdc8
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/mock/MockPartialViewContextFactory.java
@@ -0,0 +1,41 @@
+/*
+ * 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.myfaces.test.mock;
+
+import javax.faces.context.FacesContext;
+import javax.faces.context.PartialViewContext;
+import javax.faces.context.PartialViewContextFactory;
+
+/**
+ * <p>Mock implementation of <code>PartialViewContextFactory</code>.</p>
+ * <p/>
+ * $Id$
+ *
+ * @since 1.0.0
+ */
+public class MockPartialViewContextFactory extends PartialViewContextFactory
+{
+
+ @Override
+ public PartialViewContext getPartialViewContext(FacesContext context)
+ {
+ return new MockPartialViewContext(context);
+ }
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/mock/MockPortletContext.java b/test30/src/main/java/org/apache/myfaces/test/mock/MockPortletContext.java
new file mode 100644
index 0000000..578b225
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/mock/MockPortletContext.java
@@ -0,0 +1,358 @@
+/*
+ * 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.myfaces.test.mock;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.Set;
+
+import javax.portlet.PortletContext;
+import javax.portlet.PortletRequestDispatcher;
+
+/**
+ * <p>Mock implementation of <code>PortletContext</code>.</p>
+ *
+ * $Id$
+ * @since 1.0.0
+ */
+public class MockPortletContext implements PortletContext
+{
+
+ // ----------------------------------------------------- Mock Object Methods
+
+ /**
+ * <p>Add a context initialization parameter to the set of parameters
+ * recognized by this instance.</p>
+ *
+ * @param name Parameter name
+ * @param value Parameter value
+ */
+ public void addInitParameter(String name, String value)
+ {
+
+ parameters.put(name, value);
+
+ }
+
+ /**
+ * <p>Add a new MIME type mapping to the set of mappings recognized by this
+ * instance.</p>
+ *
+ * @param extension Extension to check for (without the period)
+ * @param contentType Corresponding content type
+ */
+ public void addMimeType(String extension, String contentType)
+ {
+
+ mimeTypes.put(extension, contentType);
+
+ }
+
+ /**
+ * <p>Set the document root for <code>getRealPath()</code> resolution.
+ * This parameter <strong>MUST</strong> represent a directory.</p>
+ *
+ * @param documentRoot The new base directory
+ */
+ public void setDocumentRoot(File documentRoot)
+ {
+
+ this.documentRoot = documentRoot;
+
+ }
+
+ // ------------------------------------------------------ Instance Variables
+
+ private Hashtable attributes = new Hashtable();
+ private File documentRoot = null;
+ private Hashtable mimeTypes = new Hashtable();
+ private Hashtable parameters = new Hashtable();
+
+ // -------------------------------------------------- PortletContext Methods
+
+ /** {@inheritDoc} */
+ public Object getAttribute(String name)
+ {
+
+ return attributes.get(name);
+
+ }
+
+ /** {@inheritDoc} */
+ public Enumeration getAttributeNames()
+ {
+
+ return attributes.keys();
+
+ }
+
+ /** {@inheritDoc} */
+ public String getInitParameter(String name)
+ {
+
+ return (String) parameters.get(name);
+
+ }
+
+ /** {@inheritDoc} */
+ public Enumeration getInitParameterNames()
+ {
+
+ return parameters.keys();
+
+ }
+
+ /** {@inheritDoc} */
+ public int getMajorVersion()
+ {
+
+ return 1;
+
+ }
+
+ /** {@inheritDoc} */
+ public String getMimeType(String path)
+ {
+
+ int period = path.lastIndexOf('.');
+ if (period < 0)
+ {
+ return null;
+ }
+ String extension = path.substring(period + 1);
+ return (String) mimeTypes.get(extension);
+
+ }
+
+ public int getMinorVersion()
+ {
+
+ // TODO Auto-generated method stub
+ return 0;
+ }
+
+ public PortletRequestDispatcher getNamedDispatcher(String arg0)
+ {
+
+ throw new UnsupportedOperationException();
+
+ }
+
+ /** {@inheritDoc} */
+ public String getPortletContextName()
+ {
+
+ return "MockPortletContext";
+
+ }
+
+ /** {@inheritDoc} */
+ public String getRealPath(String path)
+ {
+
+ if (documentRoot != null)
+ {
+ if (!path.startsWith("/"))
+ {
+ throw new IllegalArgumentException("The specified path ('"
+ + path + "') does not start with a '/' character");
+ }
+ File resolved = new File(documentRoot, path.substring(1));
+ try
+ {
+ return resolved.getCanonicalPath();
+ }
+ catch (IOException e)
+ {
+ return resolved.getAbsolutePath();
+ }
+ }
+ else
+ {
+ return null;
+ }
+
+ }
+
+ /** {@inheritDoc} */
+ public PortletRequestDispatcher getRequestDispatcher(String arg0)
+ {
+
+ throw new UnsupportedOperationException();
+
+ }
+
+ /** {@inheritDoc} */
+ public URL getResource(String path) throws MalformedURLException
+ {
+
+ if (documentRoot != null)
+ {
+ if (!path.startsWith("/"))
+ {
+ throw new MalformedURLException("The specified path ('" + path
+ + "') does not start with a '/' character");
+ }
+ File resolved = new File(documentRoot, path.substring(1));
+ if (resolved.exists())
+ {
+ return resolved.toURL();
+ }
+ else
+ {
+ return null;
+ }
+ }
+ else
+ {
+ return null;
+ }
+
+ }
+
+ /** {@inheritDoc} */
+ public InputStream getResourceAsStream(String path)
+ {
+
+ try
+ {
+ URL url = getResource(path);
+ if (url != null)
+ {
+ return url.openStream();
+ }
+ }
+ catch (Exception e)
+ {
+ }
+ return null;
+
+ }
+
+ /** {@inheritDoc} */
+ public Set getResourcePaths(String path)
+ {
+
+ if (documentRoot == null)
+ {
+ return null;
+ }
+
+ // Enforce the leading slash restriction
+ if (!path.startsWith("/"))
+ {
+ throw new IllegalArgumentException("The specified path ('" + path
+ + "') does not start with a '/' character");
+ }
+
+ // Locate the File node for this path's directory (if it exists)
+ File node = new File(documentRoot, path.substring(1));
+ if (!node.exists())
+ {
+ return null;
+ }
+ if (!node.isDirectory())
+ {
+ return null;
+ }
+
+ // Construct a Set containing the paths to the contents of this
+ // directory
+ Set set = new HashSet();
+ String[] files = node.list();
+ if (files == null)
+ {
+ return null;
+ }
+ for (int i = 0; i < files.length; i++)
+ {
+ String subfile = path + files[i];
+ File subnode = new File(node, files[i]);
+ if (subnode.isDirectory())
+ {
+ subfile += "/";
+ }
+ set.add(subfile);
+ }
+
+ // Return the completed set
+ return set;
+
+ }
+
+ /** {@inheritDoc} */
+ public String getServerInfo()
+ {
+
+ return "MockPortletContext";
+ }
+
+ /** {@inheritDoc} */
+ public void log(String message)
+ {
+
+ System.out.println(message);
+
+ }
+
+ /** {@inheritDoc} */
+ public void log(String message, Throwable exception)
+ {
+
+ System.out.println(message);
+ exception.printStackTrace();
+
+ }
+
+ /** {@inheritDoc} */
+ public void removeAttribute(String name)
+ {
+
+ if (attributes.containsKey(name))
+ {
+ attributes.remove(name);
+ }
+
+ }
+
+ /** {@inheritDoc} */
+ public void setAttribute(String name, Object value)
+ {
+
+ if (name == null)
+ {
+ throw new IllegalArgumentException("Attribute name cannot be null");
+ }
+ if (value == null)
+ {
+ removeAttribute(name);
+ return;
+ }
+ attributes.put(name, value);
+
+ }
+
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/mock/MockPortletRequest.java b/test30/src/main/java/org/apache/myfaces/test/mock/MockPortletRequest.java
new file mode 100644
index 0000000..86815a9
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/mock/MockPortletRequest.java
@@ -0,0 +1,436 @@
+/*
+ * 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.myfaces.test.mock;
+
+import java.security.Principal;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+
+import javax.portlet.PortalContext;
+import javax.portlet.PortletMode;
+import javax.portlet.PortletPreferences;
+import javax.portlet.PortletRequest;
+import javax.portlet.PortletSession;
+import javax.portlet.WindowState;
+
+/**
+ * <p> Mock implementation of <code>PortletRequest</code>. </p>
+ *
+ * $Id$
+ * @since 1.0.0
+ */
+
+public class MockPortletRequest implements PortletRequest
+{
+
+ // ------------------------------------------------------------ Constructors
+
+ public MockPortletRequest()
+ {
+
+ super();
+
+ }
+
+ public MockPortletRequest(PortletSession session)
+ {
+
+ super();
+ this.session = session;
+
+ }
+
+ // ----------------------------------------------------- Mock Object Methods
+
+ /**
+ * <p> Add a request parameter for this request. </p>
+ *
+ * @param name Parameter name
+ * @param value Parameter value
+ */
+ public void addParameter(String name, String value)
+ {
+
+ String[] values = (String[]) parameters.get(name);
+ if (values == null)
+ {
+ String[] results = new String[] { value };
+ parameters.put(name, results);
+ return;
+ }
+ String[] results = new String[values.length + 1];
+ System.arraycopy(values, 0, results, 0, values.length);
+ results[values.length] = value;
+ parameters.put(name, results);
+
+ }
+
+ /**
+ * <p> Set the <code>PortletSession</code> associated with this request.
+ * </p>
+ *
+ * @param session The new session
+ */
+ public void setPortletSession(PortletSession session)
+ {
+
+ this.session = session;
+ }
+
+ /**
+ * <p> Set the <code>Locale</code> associated with this request. </p>
+ *
+ * @param locale The new locale
+ */
+ public void setLocale(Locale locale)
+ {
+
+ this.locale = locale;
+
+ }
+
+ /**
+ * <p> Set the <code>Principal</code> associated with this request. </p>
+ *
+ * @param principal The new Principal
+ */
+ public void setUserPrincipal(Principal principal)
+ {
+
+ this.principal = principal;
+
+ }
+
+ // ------------------------------------------------------ Instance Variables
+
+ private Map attributes = new HashMap();
+ private String contextPath = null;
+ private Locale locale = null;
+ private Map parameters = new HashMap();
+ private Principal principal = null;
+ private PortletSession session = null;
+
+ // -------------------------------------------------- PortletRequest Methods
+
+ /** {@inheritDoc} */
+ public String getAuthType()
+ {
+
+ throw new UnsupportedOperationException();
+
+ }
+
+ /** {@inheritDoc} */
+ public String getContextPath()
+ {
+
+ return contextPath;
+
+ }
+
+ /** {@inheritDoc} */
+ public Object getAttribute(String name)
+ {
+
+ return attributes.get(name);
+
+ }
+
+ /** {@inheritDoc} */
+ public Enumeration getAttributeNames()
+ {
+
+ return new MockEnumeration(attributes.keySet().iterator());
+
+ }
+
+ /** {@inheritDoc} */
+ public Locale getLocale()
+ {
+
+ return locale;
+ }
+
+ /** {@inheritDoc} */
+ public Enumeration getLocales()
+ {
+
+ throw new UnsupportedOperationException();
+
+ }
+
+ /** {@inheritDoc} */
+ public String getParameter(String name)
+ {
+
+ String[] values = (String[]) parameters.get(name);
+ if (values != null)
+ {
+ return values[0];
+ }
+ else
+ {
+ return null;
+ }
+
+ }
+
+ /** {@inheritDoc} */
+ public Map getParameterMap()
+ {
+
+ return parameters;
+
+ }
+
+ /** {@inheritDoc} */
+ public Enumeration getParameterNames()
+ {
+
+ return new MockEnumeration(parameters.keySet().iterator());
+
+ }
+
+ /** {@inheritDoc} */
+ public String[] getParameterValues(String name)
+ {
+
+ return (String[]) parameters.get(name);
+
+ }
+
+ /** {@inheritDoc} */
+ public PortalContext getPortalContext()
+ {
+
+ throw new UnsupportedOperationException();
+
+ }
+
+ /** {@inheritDoc} */
+ public PortletMode getPortletMode()
+ {
+
+ throw new UnsupportedOperationException();
+
+ }
+
+ /** {@inheritDoc} */
+ public PortletSession getPortletSession()
+ {
+
+ return getPortletSession(true);
+
+ }
+
+ /** {@inheritDoc} */
+ public PortletSession getPortletSession(boolean create)
+ {
+
+ if (create && (session == null))
+ {
+ throw new UnsupportedOperationException();
+ }
+ return session;
+
+ }
+
+ /** {@inheritDoc} */
+ public PortletPreferences getPreferences()
+ {
+
+ throw new UnsupportedOperationException();
+
+ }
+
+ /** {@inheritDoc} */
+ public Enumeration getProperties(String arg0)
+ {
+
+ throw new UnsupportedOperationException();
+
+ }
+
+ /** {@inheritDoc} */
+ public String getProperty(String arg0)
+ {
+
+ throw new UnsupportedOperationException();
+
+ }
+
+ /** {@inheritDoc} */
+ public Enumeration getPropertyNames()
+ {
+
+ throw new UnsupportedOperationException();
+
+ }
+
+ /** {@inheritDoc} */
+ public String getRemoteUser()
+ {
+
+ if (principal != null)
+ {
+ return principal.getName();
+ }
+ else
+ {
+ return null;
+ }
+
+ }
+
+ /** {@inheritDoc} */
+ public String getRequestedSessionId()
+ {
+
+ throw new UnsupportedOperationException();
+
+ }
+
+ /** {@inheritDoc} */
+ public String getResponseContentType()
+ {
+
+ throw new UnsupportedOperationException();
+
+ }
+
+ /** {@inheritDoc} */
+ public Enumeration getResponseContentTypes()
+ {
+
+ throw new UnsupportedOperationException();
+
+ }
+
+ /** {@inheritDoc} */
+ public String getScheme()
+ {
+
+ return ("http");
+
+ }
+
+ /** {@inheritDoc} */
+ public String getServerName()
+ {
+
+ return ("localhost");
+
+ }
+
+ /** {@inheritDoc} */
+ public int getServerPort()
+ {
+
+ return (8080);
+
+ }
+
+ /** {@inheritDoc} */
+ public Principal getUserPrincipal()
+ {
+
+ return principal;
+
+ }
+
+ /** {@inheritDoc} */
+ public WindowState getWindowState()
+ {
+
+ throw new UnsupportedOperationException();
+
+ }
+
+ /** {@inheritDoc} */
+ public boolean isPortletModeAllowed(PortletMode arg0)
+ {
+
+ throw new UnsupportedOperationException();
+
+ }
+
+ /** {@inheritDoc} */
+ public boolean isRequestedSessionIdValid()
+ {
+
+ throw new UnsupportedOperationException();
+
+ }
+
+ /** {@inheritDoc} */
+ public boolean isSecure()
+ {
+
+ return false;
+
+ }
+
+ /** {@inheritDoc} */
+ public boolean isUserInRole(String arg0)
+ {
+
+ throw new UnsupportedOperationException();
+
+ }
+
+ /** {@inheritDoc} */
+ public boolean isWindowStateAllowed(WindowState arg0)
+ {
+
+ throw new UnsupportedOperationException();
+
+ }
+
+ /** {@inheritDoc} */
+ public void removeAttribute(String name)
+ {
+
+ if (attributes.containsKey(name))
+ {
+ attributes.remove(name);
+ }
+
+ }
+
+ /** {@inheritDoc} */
+ public void setAttribute(String name, Object value)
+ {
+
+ if (name == null)
+ {
+ throw new IllegalArgumentException("Attribute name cannot be null");
+ }
+ if (value == null)
+ {
+ removeAttribute(name);
+ return;
+ }
+ attributes.put(name, value);
+
+ }
+
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/mock/MockPortletResponse.java b/test30/src/main/java/org/apache/myfaces/test/mock/MockPortletResponse.java
new file mode 100644
index 0000000..33fa0f0
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/mock/MockPortletResponse.java
@@ -0,0 +1,67 @@
+/*
+ * 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.myfaces.test.mock;
+
+import javax.portlet.PortletResponse;
+
+/**
+ * <p>Mock implementation of <code>PortletResponse</code>.</p>
+ *
+ * $Id$
+ * @since 1.0.0
+ */
+
+public class MockPortletResponse implements PortletResponse
+{
+
+ /**
+ * <p>Return a default instance.</p>
+ */
+ public MockPortletResponse()
+ {
+
+ }
+
+ // -------------------------------------------------- PortletContext Methods
+
+ /** {@inheritDoc} */
+ public void addProperty(String name, String value)
+ {
+
+ throw new UnsupportedOperationException();
+
+ }
+
+ /** {@inheritDoc} */
+ public String encodeURL(String url)
+ {
+
+ return url;
+ }
+
+ /** {@inheritDoc} */
+ public void setProperty(String name, String value)
+ {
+
+ throw new UnsupportedOperationException();
+
+ }
+
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/mock/MockPortletSession.java b/test30/src/main/java/org/apache/myfaces/test/mock/MockPortletSession.java
new file mode 100644
index 0000000..64069c0
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/mock/MockPortletSession.java
@@ -0,0 +1,291 @@
+/*
+ * 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.myfaces.test.mock;
+
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.portlet.PortletContext;
+import javax.portlet.PortletSession;
+
+/**
+ * <p> Mock implementation of <code>PortletSession</code>. </p>
+ *
+ * $Id$
+ * @since 1.0.0
+ */
+public class MockPortletSession implements PortletSession
+{
+
+ // ------------------------------------------------------------ Constructors
+
+ /**
+ * <p> Configure a default instance. </p>
+ */
+ public MockPortletSession()
+ {
+
+ super();
+
+ }
+
+ /**
+ * <p> Configure a session instance associated with the specified servlet
+ * context. </p>
+ *
+ * @param servletContext The associated servlet context
+ */
+ public MockPortletSession(PortletContext portletContext)
+ {
+
+ super();
+ this.portletContext = portletContext;
+
+ }
+
+ // ----------------------------------------------------- Mock Object Methods
+
+ /**
+ * <p> Set the <code>PortletContext</code> associated with this session.
+ * </p>
+ *
+ * @param servletContext The associated servlet context
+ */
+ public void setPortletContext(PortletContext portletContext)
+ {
+
+ this.portletContext = portletContext;
+
+ }
+
+ // ------------------------------------------------------ Instance Variables
+
+ private Map portletAttributes = new HashMap();
+ private Map applicationAttributes = new HashMap();
+ private String id = "123";
+ private PortletContext portletContext = null;
+
+ // ---------------------------------------------------------- Public Methods
+
+ /**
+ * <p> Set the session identifier of this session. </p>
+ *
+ * @param id The new session identifier
+ */
+ public void setId(String id)
+ {
+
+ this.id = id;
+
+ }
+
+ // -------------------------------------------------- PortletSession Methods
+
+ /** {@inheritDoc} */
+ public Object getAttribute(String name)
+ {
+
+ return getAttribute(name, PORTLET_SCOPE);
+
+ }
+
+ /** {@inheritDoc} */
+ public Object getAttribute(String name, int scope)
+ {
+
+ if (scope == PORTLET_SCOPE)
+ {
+ return portletAttributes.get(name);
+ }
+ else if (scope == APPLICATION_SCOPE)
+ {
+ return applicationAttributes.get(name);
+ }
+
+ throw new IllegalArgumentException("Scope constant " + scope
+ + " not recognized");
+
+ }
+
+ /** {@inheritDoc} */
+ public Enumeration getAttributeNames()
+ {
+
+ return getAttributeNames(PORTLET_SCOPE);
+
+ }
+
+ /** {@inheritDoc} */
+ public Enumeration getAttributeNames(int scope)
+ {
+
+ if (scope == PORTLET_SCOPE)
+ {
+ return new MockEnumeration(portletAttributes.keySet().iterator());
+ }
+ else if (scope == APPLICATION_SCOPE)
+ {
+ return new MockEnumeration(applicationAttributes.keySet()
+ .iterator());
+ }
+
+ throw new IllegalArgumentException("Scope constant " + scope
+ + " not recognized");
+
+ }
+
+ /** {@inheritDoc} */
+ public long getCreationTime()
+ {
+
+ throw new UnsupportedOperationException();
+
+ }
+
+ /** {@inheritDoc} */
+ public String getId()
+ {
+
+ return this.id;
+
+ }
+
+ /** {@inheritDoc} */
+ public long getLastAccessedTime()
+ {
+
+ throw new UnsupportedOperationException();
+
+ }
+
+ /** {@inheritDoc} */
+ public int getMaxInactiveInterval()
+ {
+
+ throw new UnsupportedOperationException();
+
+ }
+
+ /** {@inheritDoc} */
+ public PortletContext getPortletContext()
+ {
+
+ return portletContext;
+ }
+
+ /** {@inheritDoc} */
+ public void invalidate()
+ {
+
+ throw new UnsupportedOperationException();
+
+ }
+
+ /** {@inheritDoc} */
+ public boolean isNew()
+ {
+
+ throw new UnsupportedOperationException();
+
+ }
+
+ /** {@inheritDoc} */
+ public void removeAttribute(String name)
+ {
+
+ removeAttribute(name, PORTLET_SCOPE);
+
+ }
+
+ /** {@inheritDoc} */
+ public void removeAttribute(String name, int scope)
+ {
+
+ Map attributes;
+ if (scope == PORTLET_SCOPE)
+ {
+ attributes = portletAttributes;
+ }
+ else if (scope == APPLICATION_SCOPE)
+ {
+ attributes = applicationAttributes;
+ }
+ else
+ {
+ throw new IllegalArgumentException("Scope constant " + scope
+ + " not recognized");
+ }
+ if (attributes.containsKey(name))
+ {
+ attributes.remove(name);
+ }
+
+ }
+
+ /** {@inheritDoc} */
+ public void setAttribute(String name, Object value)
+ {
+
+ setAttribute(name, value, PORTLET_SCOPE);
+
+ }
+
+ /** {@inheritDoc} */
+ public void setAttribute(String name, Object value, int scope)
+ {
+
+ if (name == null)
+ {
+ throw new IllegalArgumentException("Attribute name cannot be null");
+ }
+ if (value == null)
+ {
+ removeAttribute(name, scope);
+ return;
+ }
+
+ Map attributes;
+ if (scope == PORTLET_SCOPE)
+ {
+ attributes = portletAttributes;
+ }
+ else if (scope == APPLICATION_SCOPE)
+ {
+ attributes = applicationAttributes;
+ }
+ else
+ {
+ throw new IllegalArgumentException("Scope constant " + scope
+ + " not recognized");
+ }
+ attributes.put(name, value);
+
+ }
+
+ /** {@inheritDoc} */
+ public void setMaxInactiveInterval(int arg0)
+ {
+
+ throw new UnsupportedOperationException();
+
+ }
+
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/mock/MockPrincipal.java b/test30/src/main/java/org/apache/myfaces/test/mock/MockPrincipal.java
new file mode 100644
index 0000000..2462815
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/mock/MockPrincipal.java
@@ -0,0 +1,79 @@
+/*
+ * 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.myfaces.test.mock;
+
+import java.security.Principal;
+
+/**
+ * <p>Mock implementation of <code>Principal</code>.</p>
+ *
+ * @since 1.0.0
+ */
+public class MockPrincipal implements Principal
+{
+
+ // ------------------------------------------------------------ Constructors
+
+ /**
+ * <p>Construct a default Principal instance.</p>
+ */
+ public MockPrincipal()
+ {
+ this(null);
+ }
+
+ /**
+ * <p>Construct a Principal with the specified name.</p>
+ *
+ * @param name Name for this Principal
+ */
+ public MockPrincipal(String name)
+ {
+ this.name = name;
+ }
+
+ // ------------------------------------------------------ Instance Variables
+
+ /**
+ * <p>The name for this Principal intance.</p>
+ */
+ private String name = null;
+
+ // ----------------------------------------------------- Mock Object Methods
+
+ /**
+ * <p>Set the name for this Principal.</p>
+ *
+ * @param name The new name
+ */
+ public void setName(String name)
+ {
+ this.name = name;
+ }
+
+ // ------------------------------------------------------- Principal Methods
+
+ /** {@inheritDoc} */
+ public String getName()
+ {
+ return this.name;
+ }
+
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/mock/MockPrintWriter.java b/test30/src/main/java/org/apache/myfaces/test/mock/MockPrintWriter.java
new file mode 100644
index 0000000..a98e170
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/mock/MockPrintWriter.java
@@ -0,0 +1,84 @@
+/*
+ * 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.myfaces.test.mock;
+
+import java.io.CharArrayWriter;
+import java.io.PrintWriter;
+
+/**
+ * <p>Mock implementation of <code>PrintWriter</code>.</p>
+ *
+ * $Id$
+ * @since 1.0.0
+ */
+
+public class MockPrintWriter extends PrintWriter
+{
+
+ // ------------------------------------------------------------ Constructors
+
+ /**
+ * <p>Return a default instance.</p>
+ *
+ * @param writer Temporary buffer storage for us to use
+ */
+ public MockPrintWriter(CharArrayWriter writer)
+ {
+ super(writer);
+ this.caw = writer;
+ }
+
+ // ----------------------------------------------------- Mock Object Methods
+
+ /**
+ * <p>Return the content that has been written to this writer.</p>
+ */
+ public char[] content()
+ {
+ return caw.toCharArray();
+ }
+
+ /**
+ * <p>Reset this output stream so that it appears no content has been
+ * written.</p>
+ */
+ public void reset()
+ {
+ caw.reset();
+ }
+
+ /**
+ * <p>Return the number of characters that have been written to this writer.</p>
+ */
+ public int size()
+ {
+ return caw.size();
+ }
+
+ // ------------------------------------------------------ Instance Variables
+
+ /**
+ * <p>The writer we will use for buffering.</p>
+ */
+ private CharArrayWriter caw = null;
+
+ // ----------------------------------------------------- PrintWriter Methods
+
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/mock/MockRenderKit.java b/test30/src/main/java/org/apache/myfaces/test/mock/MockRenderKit.java
new file mode 100644
index 0000000..3de55f8
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/mock/MockRenderKit.java
@@ -0,0 +1,145 @@
+/*
+ * 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.myfaces.test.mock;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.Writer;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.faces.context.ResponseStream;
+import javax.faces.context.ResponseWriter;
+import javax.faces.render.RenderKit;
+import javax.faces.render.Renderer;
+import javax.faces.render.ResponseStateManager;
+
+/**
+ * <p>Mock implementation of <code>RenderKit</code>.</p>
+ *
+ * $Id$
+ * @since 1.0.0
+ */
+
+public class MockRenderKit extends RenderKit
+{
+
+ // ------------------------------------------------------------ Constructors
+
+ /**
+ * <p>Return a default instance.</p>
+ */
+ public MockRenderKit()
+ {
+ }
+
+ // ----------------------------------------------------- Mock Object Methods
+
+ public void setResponseStateManager(ResponseStateManager rsm)
+ {
+ this.rsm = rsm;
+ }
+
+ // ------------------------------------------------------ Instance Variables
+
+ /**
+ * <p>The set of renderers registered here.</p>
+ */
+ private Map renderers = new HashMap();
+ private ResponseStateManager rsm = new MockResponseStateManager();
+
+ // ------------------------------------------------------- RenderKit Methods
+
+ /** {@inheritDoc} */
+ public void addRenderer(String family, String rendererType,
+ Renderer renderer)
+ {
+
+ if ((family == null) || (rendererType == null) || (renderer == null))
+ {
+ throw new NullPointerException();
+ }
+ renderers.put(family + "|" + rendererType, renderer);
+
+ }
+
+ /** {@inheritDoc} */
+ public Renderer getRenderer(String family, String rendererType)
+ {
+
+ if ((family == null) || (rendererType == null))
+ {
+ throw new NullPointerException();
+ }
+ return (Renderer) renderers.get(family + "|" + rendererType);
+
+ }
+
+ /** {@inheritDoc} */
+ public ResponseWriter createResponseWriter(Writer writer,
+ String contentTypeList, String characterEncoding)
+ {
+
+ return new MockResponseWriter(writer, contentTypeList,
+ characterEncoding);
+
+ }
+
+ /** {@inheritDoc} */
+ public ResponseStream createResponseStream(OutputStream out)
+ {
+
+ final OutputStream stream = out;
+ return new ResponseStream()
+ {
+
+ public void close() throws IOException
+ {
+ stream.close();
+ }
+
+ public void flush() throws IOException
+ {
+ stream.flush();
+ }
+
+ public void write(byte[] b) throws IOException
+ {
+ stream.write(b);
+ }
+
+ public void write(byte[] b, int off, int len) throws IOException
+ {
+ stream.write(b, off, len);
+ }
+
+ public void write(int b) throws IOException
+ {
+ stream.write(b);
+ }
+ };
+ }
+
+ /** {@inheritDoc} */
+ public ResponseStateManager getResponseStateManager()
+ {
+ return rsm;
+ }
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/mock/MockRenderKitFactory.java b/test30/src/main/java/org/apache/myfaces/test/mock/MockRenderKitFactory.java
new file mode 100644
index 0000000..367ecb6
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/mock/MockRenderKitFactory.java
@@ -0,0 +1,117 @@
+/*
+ * 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.myfaces.test.mock;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import javax.faces.context.FacesContext;
+import javax.faces.render.RenderKit;
+import javax.faces.render.RenderKitFactory;
+
+/**
+ * <p>Mock implementation of <code>RenderKitFactory</code>.</p>
+ *
+ * $Id$
+ * @since 1.0.0
+ */
+
+public class MockRenderKitFactory extends RenderKitFactory
+{
+
+ // ------------------------------------------------------------ Constructors
+
+ /**
+ * <p>Return a default instance.</p>
+ */
+ public MockRenderKitFactory()
+ {
+
+ renderKits = new HashMap();
+
+ }
+
+ // ----------------------------------------------------- Mock Object Methods
+
+ // ------------------------------------------------------ Instance Variables
+
+ /**
+ * <p>The set of render kits that have been registered here.</p>
+ */
+ private Map renderKits = new HashMap();
+
+ // ------------------------------------------------ RenderKitFactory Methods
+
+ /** {@inheritDoc} */
+ public void addRenderKit(String renderKitId, RenderKit renderKit)
+ {
+
+ if ((renderKitId == null) || (renderKit == null))
+ {
+ throw new NullPointerException();
+ }
+ if (renderKits.containsKey(renderKitId))
+ {
+ throw new IllegalArgumentException(renderKitId);
+ }
+ renderKits.put(renderKitId, renderKit);
+
+ }
+
+ /** {@inheritDoc} */
+ public RenderKit getRenderKit(FacesContext context, String renderKitId)
+ {
+
+ if (renderKitId == null)
+ {
+ throw new NullPointerException();
+ }
+ RenderKit renderKit = (RenderKit) renderKits.get(renderKitId);
+ if (renderKit == null)
+ {
+ // Issue 38294 -- We removed the automatic creation of the
+ // default renderkit in the constructor, allowing it to be
+ // added by AbstractJsfTestCase in the usual case. To preserve
+ // backwards compatibility, however, create one on the fly
+ // if the user asks for the default HTML renderkit and it has
+ // not been manually added yet
+ if (RenderKitFactory.HTML_BASIC_RENDER_KIT.equals(renderKitId))
+ {
+ renderKit = new MockRenderKit();
+ renderKits.put(RenderKitFactory.HTML_BASIC_RENDER_KIT,
+ renderKit);
+ return renderKit;
+ }
+ throw new IllegalArgumentException(renderKitId);
+ }
+ return renderKit;
+
+ }
+
+ /** {@inheritDoc} */
+ public Iterator getRenderKitIds()
+ {
+
+ return renderKits.keySet().iterator();
+
+ }
+
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/mock/MockRequestDispatcher.java b/test30/src/main/java/org/apache/myfaces/test/mock/MockRequestDispatcher.java
new file mode 100644
index 0000000..4fb22f5
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/mock/MockRequestDispatcher.java
@@ -0,0 +1,60 @@
+/*
+ * 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.myfaces.test.mock;
+
+import java.io.IOException;
+import java.net.URL;
+
+import javax.servlet.RequestDispatcher;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+
+/**
+ *
+ * @since 1.0.0
+ * @author Jacob Hookom
+ * @version $Id: MockRequestDispatcher.java 804043 2009-08-13 22:08:44Z lu4242 $
+ */
+public class MockRequestDispatcher implements RequestDispatcher
+{
+
+ protected final URL url;
+
+ public MockRequestDispatcher(URL url)
+ {
+ this.url = url;
+ }
+
+ public void forward(ServletRequest request, ServletResponse response)
+ throws ServletException, IOException
+ {
+ // TODO Auto-generated method stub
+
+ }
+
+ public void include(ServletRequest request, ServletResponse response)
+ throws ServletException, IOException
+ {
+ // TODO Auto-generated method stub
+
+ }
+
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/mock/MockResponseStateManager.java b/test30/src/main/java/org/apache/myfaces/test/mock/MockResponseStateManager.java
new file mode 100644
index 0000000..9688bf9
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/mock/MockResponseStateManager.java
@@ -0,0 +1,396 @@
+/*
+ * 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.myfaces.test.mock;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+
+import javax.faces.FacesException;
+import javax.faces.application.StateManager.SerializedView;
+import javax.faces.context.FacesContext;
+import javax.faces.context.ResponseWriter;
+import javax.faces.render.RenderKitFactory;
+import javax.faces.render.ResponseStateManager;
+
+/**
+ * Mock class that encode view state in hex format
+ *
+ * @author Leonardo Uribe
+ * @since 1.0.0
+ *
+ */
+public class MockResponseStateManager extends ResponseStateManager
+{
+ // ------------------------------------------------------------ Constructors
+
+ // ------------------------------------------------------ Instance Variables
+
+ private static final int TREE_PARAM = 0;
+ private static final int STATE_PARAM = 1;
+ private static final int VIEWID_PARAM = 2;
+ private static final String ZIP_CHARSET = "ISO-8859-1";
+
+ // ----------------------------------------------------- Mock Object Methods
+
+ // -------------------------------------------------- ResponseStateManager Methods
+
+ public Object getState(FacesContext facesContext, String viewId)
+ {
+ Object[] savedState = getSavedState(facesContext);
+ if (savedState == null)
+ {
+ return null;
+ }
+
+ return new Object[] { savedState[TREE_PARAM], savedState[STATE_PARAM] };
+ }
+
+ public Object getTreeStructureToRestore(FacesContext facesContext,
+ String viewId)
+ {
+ // Although this method won't be called anymore,
+ // it has been kept for backward compatibility.
+ Object[] savedState = getSavedState(facesContext);
+ if (savedState == null)
+ {
+ return null;
+ }
+
+ return savedState[TREE_PARAM];
+ }
+
+ public Object getComponentStateToRestore(FacesContext facesContext)
+ {
+ // Although this method won't be called anymore,
+ // it has been kept for backward compatibility.
+ Object[] savedState = getSavedState(facesContext);
+ if (savedState == null)
+ {
+ return null;
+ }
+
+ return savedState[STATE_PARAM];
+ }
+
+ public boolean isPostback(FacesContext context)
+ {
+ return context.getExternalContext().getRequestParameterMap()
+ .containsKey(ResponseStateManager.VIEW_STATE_PARAM);
+ }
+
+ public void writeState(FacesContext facescontext,
+ SerializedView serializedview) throws IOException
+ {
+ ResponseWriter responseWriter = facescontext.getResponseWriter();
+
+ Object[] savedState = new Object[3];
+
+ if (facescontext.getApplication().getStateManager()
+ .isSavingStateInClient(facescontext))
+ {
+ Object treeStruct = serializedview.getStructure();
+ Object compStates = serializedview.getState();
+
+ if (treeStruct != null)
+ {
+ savedState[TREE_PARAM] = treeStruct;
+ }
+
+ if (compStates != null)
+ {
+ savedState[STATE_PARAM] = compStates;
+ }
+ }
+ else
+ {
+ // write viewSequence
+ Object treeStruct = serializedview.getStructure();
+ if (treeStruct != null)
+ {
+ if (treeStruct instanceof String)
+ {
+ savedState[TREE_PARAM] = treeStruct;
+ }
+ }
+ }
+
+ savedState[VIEWID_PARAM] = facescontext.getViewRoot().getViewId();
+
+ // write the view state field
+ writeViewStateField(facescontext, responseWriter, savedState);
+
+ // renderKitId field
+ writeRenderKitIdField(facescontext, responseWriter);
+ }
+
+ private void writeViewStateField(FacesContext facesContext,
+ ResponseWriter responseWriter, Object savedState)
+ throws IOException
+ {
+
+ String serializedState = construct(facesContext, savedState);
+ responseWriter.startElement("input", null);
+ responseWriter.writeAttribute("type", "hidden", null);
+ responseWriter.writeAttribute("name", VIEW_STATE_PARAM, null);
+ responseWriter.writeAttribute("value", serializedState, null);
+ responseWriter.endElement("input");
+ }
+
+ private void writeRenderKitIdField(FacesContext facesContext,
+ ResponseWriter responseWriter) throws IOException
+ {
+
+ String defaultRenderKitId = facesContext.getApplication()
+ .getDefaultRenderKitId();
+ if (defaultRenderKitId != null
+ && !RenderKitFactory.HTML_BASIC_RENDER_KIT
+ .equals(defaultRenderKitId))
+ {
+ responseWriter.startElement("input", null);
+ responseWriter.writeAttribute("type", "hidden", null);
+ responseWriter.writeAttribute("name",
+ ResponseStateManager.RENDER_KIT_ID_PARAM, null);
+ responseWriter.writeAttribute("value", defaultRenderKitId, null);
+ responseWriter.endElement("input");
+ }
+ }
+
+ private String construct(FacesContext facesContext, Object savedState)
+ throws IOException
+ {
+ byte[] bytes = null;
+ ByteArrayOutputStream baos = null;
+ ObjectOutputStream oos = null;
+ try
+ {
+ baos = new ByteArrayOutputStream();
+ oos = new ObjectOutputStream(baos);
+ oos.writeObject(savedState);
+ bytes = baos.toByteArray();
+ }
+ finally
+ {
+ if (oos != null)
+ {
+ try
+ {
+ oos.close();
+ }
+ catch (IOException e)
+ {
+ }
+ finally
+ {
+ oos = null;
+ }
+ }
+ if (baos != null)
+ {
+ try
+ {
+ baos.close();
+ }
+ catch (IOException e)
+ {
+ }
+ finally
+ {
+ baos = null;
+ }
+ }
+ }
+ return new String(new _Hex().encode(bytes), ZIP_CHARSET);
+ }
+
+ private Object reconstruct(FacesContext facesContext, String encodedState)
+ throws IOException
+ {
+ byte[] bytes = encodedState.getBytes(ZIP_CHARSET);
+
+ try
+ {
+ bytes = new _Hex().decode(bytes);
+ }
+ catch(Exception e)
+ {
+ throw new IOException(e.getMessage());
+ }
+
+ ByteArrayInputStream input = null;
+ ObjectInputStream s = null;
+ Object object = null;
+
+ try
+ {
+ input = new ByteArrayInputStream(bytes);
+ s = new ObjectInputStream(input);
+ object = s.readObject();
+ }
+ catch (ClassNotFoundException e)
+ {
+ throw new IOException(e.getMessage());
+ }
+ finally
+ {
+ if (s != null)
+ {
+ try
+ {
+ s.close();
+ }
+ catch (IOException e)
+ {
+ }
+ finally
+ {
+ s = null;
+ }
+ }
+ if (input != null)
+ {
+ try
+ {
+ input.close();
+ }
+ catch (IOException e)
+ {
+ }
+ finally
+ {
+ input = null;
+ }
+ }
+ }
+ return object;
+ }
+
+ private Object[] getSavedState(FacesContext facesContext)
+ {
+ Object encodedState = facesContext.getExternalContext()
+ .getRequestParameterMap().get(VIEW_STATE_PARAM);
+ if (encodedState == null || (((String) encodedState).length() == 0))
+ {
+ return null;
+ }
+
+ Object[] savedState = null;
+
+ try
+ {
+ savedState = (Object[]) reconstruct(facesContext,
+ (String) encodedState);
+ }
+ catch (IOException e)
+ {
+ facesContext.getExternalContext().log(
+ "Cannot reconstruct view state", e);
+ }
+
+ if (savedState == null)
+ {
+ return null;
+ }
+
+ String restoredViewId = (String) savedState[VIEWID_PARAM];
+
+ if (restoredViewId == null)
+ {
+ return null;
+ }
+ return savedState;
+ }
+
+ public String getViewState(FacesContext facesContext, Object state)
+ {
+ if (state == null)
+ {
+ return null;
+ }
+
+ Object treeStruct = null;
+ Object compStates = null;
+
+ if (state instanceof SerializedView)
+ {
+ SerializedView view = (SerializedView) state;
+ treeStruct = view.getStructure();
+ compStates = view.getState();
+ }
+ else if (state instanceof Object[])
+ {
+ Object[] structureAndState = (Object[]) state;
+
+ if (structureAndState.length == 2)
+ {
+ treeStruct = structureAndState[0];
+ compStates = structureAndState[1];
+ }
+ else
+ {
+ throw new FacesException(
+ "The state should be an array of Object[] of lenght 2");
+ }
+ }
+ else
+ {
+ throw new FacesException(
+ "The state should be an array of Object[] of lenght 2, or a SerializedView instance");
+ }
+
+ Object[] savedState = new Object[3];
+
+ if (facesContext.getApplication().getStateManager()
+ .isSavingStateInClient(facesContext))
+ {
+ if (treeStruct != null)
+ {
+ savedState[TREE_PARAM] = treeStruct;
+ }
+
+ if (compStates != null)
+ {
+ savedState[STATE_PARAM] = compStates;
+ }
+ }
+ else
+ {
+ // write viewSequence
+ if (treeStruct != null)
+ {
+ if (treeStruct instanceof String)
+ {
+ savedState[TREE_PARAM] = treeStruct;
+ }
+ }
+ }
+ savedState[VIEWID_PARAM] = facesContext.getViewRoot().getViewId();
+
+ try
+ {
+ return construct(facesContext, savedState);
+ }
+ catch (IOException e)
+ {
+ throw new RuntimeException(e);
+ }
+ }
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/mock/MockResponseWriter.java b/test30/src/main/java/org/apache/myfaces/test/mock/MockResponseWriter.java
new file mode 100644
index 0000000..16f4033
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/mock/MockResponseWriter.java
@@ -0,0 +1,428 @@
+/*
+ * 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.myfaces.test.mock;
+
+import java.io.IOException;
+import java.io.Writer;
+import javax.faces.component.UIComponent;
+import javax.faces.context.ResponseWriter;
+
+/**
+ * <p>Mock implementation of <code>javax.faces.context.ResponseWriter</code>.</p>
+ *
+ * @since 1.0.0
+ */
+public class MockResponseWriter extends ResponseWriter
+{
+
+ // ------------------------------------------------------------ Constructors
+
+ public MockResponseWriter(Writer writer)
+ {
+ this.writer = writer;
+ this.contentType = "text/html";
+ this.characterEncoding = "UTF-8";
+ }
+
+ /**
+ * <p>Construct an instance wrapping the specified writer.</p>
+ *
+ * @param writer Writer we are wrapping
+ * @param contentType Content type to be created
+ * @param characterEncoding Character encoding of this response
+ */
+ public MockResponseWriter(Writer writer, String contentType,
+ String characterEncoding)
+ {
+ this.writer = writer;
+ this.contentType = contentType;
+ this.characterEncoding = characterEncoding;
+ }
+
+ // ------------------------------------------------------ Instance Variables
+
+ private String characterEncoding = null;
+ private String contentType = "text/html";
+ private boolean open = false; // Is an element currently open?
+ private UIComponent component;
+ private Writer writer = null;
+
+ // ----------------------------------------------------- Mock Object Methods
+
+ /**
+ * <p>Return the <code>Writer</code> that we are wrapping.</p>
+ */
+ public Writer getWriter()
+ {
+ return this.writer;
+ }
+
+ // -------------------------------------------------- ResponseWriter Methods
+
+ /** {@inheritDoc} */
+ public ResponseWriter cloneWithWriter(Writer writer)
+ {
+ return new MockResponseWriter(writer, contentType, characterEncoding);
+ }
+
+ /** {@inheritDoc} */
+ public void endDocument() throws IOException
+ {
+ finish();
+ writer.flush();
+ }
+
+ /** {@inheritDoc} */
+ public void endElement(String name) throws IOException
+ {
+ if (open)
+ {
+ writer.write("/");
+ finish();
+ }
+ else
+ {
+ writer.write("</");
+ writer.write(name);
+ writer.write(">");
+ }
+ component = null;
+ }
+
+ /** {@inheritDoc} */
+ public String getCharacterEncoding()
+ {
+ return this.characterEncoding;
+ }
+
+ /** {@inheritDoc} */
+ public String getContentType()
+ {
+ return this.contentType;
+ }
+
+ /** {@inheritDoc} */
+ public void flush() throws IOException
+ {
+ finish();
+ }
+
+ /** {@inheritDoc} */
+ public void startDocument() throws IOException
+ {
+ // Do nothing
+ }
+
+ /** {@inheritDoc} */
+ public void startElement(String name, UIComponent component)
+ throws IOException
+ {
+ if (name == null)
+ {
+ throw new NullPointerException();
+ }
+ finish();
+ writer.write('<');
+ writer.write(name);
+ open = true;
+ this.component = component;
+ }
+
+ /** {@inheritDoc} */
+ public void writeAttribute(String name, Object value, String property)
+ throws IOException
+ {
+ if (name == null)
+ {
+ throw new NullPointerException();
+ }
+ if (!open)
+ {
+ throw new IllegalStateException();
+ }
+ String attribute = findValue(value, property);
+ if (attribute != null)
+ {
+ writer.write(" ");
+ writer.write(name);
+ writer.write("=\"");
+ string(attribute);
+
+ writer.write("\"");
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void writeComment(Object comment) throws IOException
+ {
+ if (comment == null)
+ {
+ throw new NullPointerException();
+ }
+ finish();
+ writer.write("<!-- ");
+ if (comment instanceof String)
+ {
+ writer.write((String) comment);
+ }
+ else
+ {
+ writer.write(comment.toString());
+ }
+ writer.write(" -->");
+ }
+
+ /** {@inheritDoc} */
+ public void writeText(Object text, String property) throws IOException
+ {
+ if (text == null)
+ {
+ throw new NullPointerException();
+ }
+ finish();
+ String value = findValue(text, property);
+ if (value != null)
+ {
+ string(value);
+ }
+
+ }
+
+ /** {@inheritDoc} */
+ public void writeText(char[] text, int off, int len) throws IOException
+ {
+ if (text == null)
+ {
+ throw new NullPointerException();
+ }
+ if ((off < 0) || (off > text.length) || (len < 0)
+ || (len > text.length))
+ {
+ throw new IndexOutOfBoundsException();
+ }
+ finish();
+ string(text, off, len);
+ }
+
+ /** {@inheritDoc} */
+ public void writeURIAttribute(String name, Object value, String property)
+ throws IOException
+ {
+ if (name == null)
+ {
+ throw new NullPointerException();
+ }
+ if (!open)
+ {
+ throw new IllegalStateException();
+ }
+ String attribute = findValue(value, property);
+ if (attribute != null)
+ {
+ writer.write(" ");
+ writer.write(name);
+ writer.write("=\"");
+
+ string(attribute);
+ writer.write("\"");
+ }
+ }
+
+ // ---------------------------------------------------------- Writer Methods
+
+ /** {@inheritDoc} */
+ public void close() throws IOException
+ {
+ finish();
+ writer.close();
+ }
+
+ /** {@inheritDoc} */
+ public void write(char[] cbuf, int off, int len) throws IOException
+ {
+ finish();
+ writer.write(cbuf, off, len);
+ }
+
+ // --------------------------------------------------------- Support Methods
+
+ /**
+ * <p>Write the specified character, filtering if necessary.</p>
+ *
+ * @param ch Character to be written
+ *
+ * @exception IOException if an input/output error occurs
+ */
+ private void character(char ch) throws IOException
+ {
+
+ if (ch <= 0xff)
+ {
+ // In single byte characters, replace only the five
+ // characters for which well-known entities exist in XML
+ if (ch == 0x22)
+ {
+ writer.write(""");
+ }
+ else if (ch == 0x26)
+ {
+ writer.write("&");
+ }
+ else if (ch == 0x27)
+ {
+ writer.write("'");
+ }
+ else if (ch == 0x3C)
+ {
+ writer.write("<");
+ }
+ else if (ch == 0X3E)
+ {
+ writer.write(">");
+ }
+ else
+ {
+ writer.write(ch);
+ }
+ }
+ else
+ {
+ if (substitution())
+ {
+ numeric(writer, ch);
+ }
+ else
+ {
+ writer.write(ch);
+ }
+ }
+
+ }
+
+ /**
+ * <p>Close any element that is currently open.</p>
+ *
+ * @exception IOException if an input/output error occurs
+ */
+ private void finish() throws IOException
+ {
+
+ if (open)
+ {
+ writer.write(">");
+ open = false;
+ }
+
+ }
+
+ /**
+ * <p>Write a numeric character reference for specified character
+ * to the specfied writer.</p>
+ *
+ * @param writer Writer we are writing to
+ * @param ch Character to be translated and appended
+ *
+ * @exception IOException if an input/output error occurs
+ */
+ private void numeric(Writer writer, char ch) throws IOException
+ {
+
+ writer.write("&#");
+ writer.write(String.valueOf(ch));
+ writer.write(";");
+
+ }
+
+ /**
+ * <p>Write the specified characters (after performing suitable
+ * replacement of characters by corresponding entities).</p>
+ *
+ * @param text Character array containing text to be written
+ * @param off Starting offset (zero relative)
+ * @param len Number of characters to be written
+ *
+ * @exception IOException if an input/output error occurs
+ */
+ private void string(char[] text, int off, int len) throws IOException
+ {
+
+ // Process the specified characters
+ for (int i = off; i < (off + len); i++)
+ {
+ character(text[i]);
+ }
+
+ }
+
+ /**
+ * <p>Write the specified string (after performing suitable
+ * replacement of characters by corresponding entities).</p>
+ *
+ * @param s String to be filtered and written
+ *
+ * @exception IOException if an input/output error occurs
+ */
+ private void string(String s) throws IOException
+ {
+ for (int i = 0; i < s.length(); i++)
+ {
+ character(s.charAt(i));
+ }
+
+ }
+
+ /**
+ * <p>Return true if entity substitution should be performed on double
+ * byte character values.</p>
+ */
+ private boolean substitution()
+ {
+
+ return !("UTF-8".equals(characterEncoding) || "UTF-16"
+ .equals(characterEncoding));
+
+ }
+
+ private String findValue(final Object value, final String property)
+ {
+ if (value != null)
+ {
+ return value instanceof String ? (String) value : value.toString();
+ }
+ else if (property != null)
+ {
+ if (component != null)
+ {
+ final Object object = component.getAttributes().get(property);
+ if (object != null)
+ {
+ return object instanceof String ? (String) object : object
+ .toString();
+ }
+ else
+ {
+ return null;
+ }
+ }
+
+ }
+ return null;
+ }
+
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/mock/MockServlet.java b/test30/src/main/java/org/apache/myfaces/test/mock/MockServlet.java
new file mode 100644
index 0000000..bc0f966
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/mock/MockServlet.java
@@ -0,0 +1,119 @@
+/*
+ * 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.myfaces.test.mock;
+
+import java.io.IOException;
+
+import javax.servlet.Servlet;
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+
+/**
+ * <p>Mock implementation of <code>Servlet</code>.</p>
+ *
+ * $Id$
+ * @since 1.0.0
+ */
+
+public class MockServlet implements Servlet
+{
+
+ // ------------------------------------------------------------ Constructors
+
+ /**
+ * <p>Create a default Servlet instance.</p>
+ */
+ public MockServlet()
+ {
+ }
+
+ /**
+ * <p>Create a new Servlet with the specified ServletConfig.</p>
+ *
+ * @param config The new ServletConfig instance
+ */
+ public MockServlet(ServletConfig config) throws ServletException
+ {
+ init(config);
+ }
+
+ // ----------------------------------------------------- Mock Object Methods
+
+ /**
+ * <p>Set the <code>ServletConfig</code> instance for this servlet.</p>
+ *
+ * @param config The new ServletConfig instance
+ */
+ public void setServletConfig(ServletConfig config)
+ {
+
+ this.config = config;
+
+ }
+
+ // ------------------------------------------------------ Instance Variables
+
+ /**
+ * <p>The <code>ServletConfig</code> instance for this servlet.</p>
+ */
+ private ServletConfig config;
+
+ // --------------------------------------------------------- Servlet Methods
+
+ /** {@inheritDoc} */
+ public void destroy()
+ {
+ }
+
+ /** {@inheritDoc} */
+ public ServletConfig getServletConfig()
+ {
+
+ return this.config;
+
+ }
+
+ /** {@inheritDoc} */
+ public String getServletInfo()
+ {
+
+ return "MockServlet";
+
+ }
+
+ /** {@inheritDoc} */
+ public void init(ServletConfig config) throws ServletException
+ {
+
+ this.config = config;
+
+ }
+
+ /** {@inheritDoc} */
+ public void service(ServletRequest request, ServletResponse response)
+ throws IOException, ServletException
+ {
+
+ // Do nothing by default
+
+ }
+
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/mock/MockServletConfig.java b/test30/src/main/java/org/apache/myfaces/test/mock/MockServletConfig.java
new file mode 100644
index 0000000..57c26bd
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/mock/MockServletConfig.java
@@ -0,0 +1,124 @@
+/*
+ * 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.myfaces.test.mock;
+
+import java.util.Enumeration;
+import java.util.Hashtable;
+
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletContext;
+
+/**
+ * <p>Mock implementation of <code>ServletConfig</code>.</p>
+ *
+ * $Id$
+ * @since 1.0.0
+ */
+
+public class MockServletConfig implements ServletConfig
+{
+
+ // ------------------------------------------------------------ Constructors
+
+ /**
+ * <p>Construct a default instance.</p>
+ */
+ public MockServletConfig()
+ {
+ }
+
+ /**
+ * <p>Construct an instance associated with the specified
+ * servlet context.</p>
+ *
+ * @param context The associated ServletContext
+ */
+ public MockServletConfig(ServletContext context)
+ {
+ setServletContext(context);
+ }
+
+ // ----------------------------------------------------- Mock Object Methods
+
+ /**
+ * <p>Add a servlet initialization parameter.</p>
+ *
+ * @param name Parameter name
+ * @param value Parameter value
+ */
+ public void addInitParameter(String name, String value)
+ {
+
+ parameters.put(name, value);
+
+ }
+
+ /**
+ * <p>Set the servlet context for this application.</p>
+ *
+ * @param context The new servlet context
+ */
+ public void setServletContext(ServletContext context)
+ {
+
+ this.context = context;
+
+ }
+
+ // ------------------------------------------------------ Instance Variables
+
+ private ServletContext context;
+ private Hashtable parameters = new Hashtable();
+
+ // --------------------------------------------------- ServletConfig Methods
+
+ /** {@inheritDoc} */
+ public String getInitParameter(String name)
+ {
+
+ return (String) parameters.get(name);
+
+ }
+
+ /** {@inheritDoc} */
+ public Enumeration getInitParameterNames()
+ {
+
+ return parameters.keys();
+
+ }
+
+ /** {@inheritDoc} */
+ public ServletContext getServletContext()
+ {
+
+ return this.context;
+
+ }
+
+ /** {@inheritDoc} */
+ public String getServletName()
+ {
+
+ return "MockServlet";
+
+ }
+
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/mock/MockServletContext.java b/test30/src/main/java/org/apache/myfaces/test/mock/MockServletContext.java
new file mode 100644
index 0000000..9c3d199
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/mock/MockServletContext.java
@@ -0,0 +1,746 @@
+/*
+ * 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.myfaces.test.mock;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.EventListener;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import javax.servlet.Filter;
+import javax.servlet.FilterRegistration;
+
+import javax.servlet.RequestDispatcher;
+import javax.servlet.Servlet;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletContextAttributeEvent;
+import javax.servlet.ServletContextAttributeListener;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRegistration;
+import javax.servlet.SessionCookieConfig;
+import javax.servlet.SessionTrackingMode;
+import javax.servlet.descriptor.JspConfigDescriptor;
+
+/**
+ * <p>Mock implementation of <code>ServletContext</code>.</p>
+ *
+ * <p><strong>WARNING</strong> - Before you can get meaningful results from
+ * calls to the <code>getResource()</code>, <code>getResourceAsStream()</code>,
+ * <code>getResourcePaths()</code>, or <code>getRealPath()</code> methods,
+ * you must configure the <code>documentRoot</code> property, passing in a
+ * <code>File</code> object pointing at a directory that simulates a
+ * web application structure.</p>
+ *
+ * $Id$
+ * @since 1.0.0
+ */
+
+public class MockServletContext implements ServletContext
+{
+
+ // ----------------------------------------------------- Mock Object Methods
+
+ /**
+ * <p>Add a context initialization parameter to the set of
+ * parameters recognized by this instance.</p>
+ *
+ * @param name Parameter name
+ * @param value Parameter value
+ */
+ public void addInitParameter(String name, String value)
+ {
+ parameters.put(name, value);
+ }
+
+ /**
+ * <p>Add a new MIME type mapping to the set of mappings
+ * recognized by this instance.</p>
+ *
+ * @param extension Extension to check for (without the period)
+ * @param contentType Corresponding content type
+ */
+ public void addMimeType(String extension, String contentType)
+ {
+ mimeTypes.put(extension, contentType);
+ }
+
+ /**
+ * <p>Set the document root for <code>getRealPath()</code>
+ * resolution. This parameter <strong>MUST</strong> represent
+ * a directory.</p>
+ *
+ * @param documentRoot The new base directory
+ */
+ public void setDocumentRoot(File documentRoot)
+ {
+ this.documentRoot = documentRoot;
+ }
+
+ public void setDocumentRoot(URI base)
+ {
+ File f = new File(base);
+ if (!f.exists())
+ {
+ throw new IllegalArgumentException("File: " + base.getPath()
+ + " doesn't exist");
+ }
+ this.documentRoot = f;
+ }
+
+ /**
+ * <p>Add a new listener instance that should be notified about
+ * attribute changes.</p>
+ *
+ * @param listener Listener to be added
+ */
+ public void addAttributeListener(ServletContextAttributeListener listener)
+ {
+ MockWebContainer container = getWebContainer();
+ if (container == null)
+ {
+ attributeListeners.add(listener);
+ }
+ else
+ {
+ container.subscribeListener(listener);
+ }
+ }
+
+ public MockWebContainer getWebContainer()
+ {
+ return webContainer;
+ }
+
+ public void setWebContainer(MockWebContainer container)
+ {
+ webContainer = container;
+ }
+
+ // ------------------------------------------------------ Instance Variables
+
+ private Hashtable attributes = new Hashtable();
+ private File documentRoot = null;
+ private Hashtable mimeTypes = new Hashtable();
+ private Hashtable parameters = new Hashtable();
+ private List attributeListeners = new ArrayList();
+ private MockWebContainer webContainer;
+ private Map<String, ServletRegistration> servletRegistrations =
+ new HashMap<String, ServletRegistration>();
+
+ private ClassLoader classLoader;
+
+ // -------------------------------------------------- ServletContext Methods
+
+ /** {@inheritDoc} */
+ public Object getAttribute(String name)
+ {
+
+ return attributes.get(name);
+
+ }
+
+ /** {@inheritDoc} */
+ public Enumeration getAttributeNames()
+ {
+
+ return attributes.keys();
+
+ }
+
+ /** {@inheritDoc} */
+ public String getInitParameter(String name)
+ {
+
+ return (String) parameters.get(name);
+
+ }
+
+ /** {@inheritDoc} */
+ public Enumeration getInitParameterNames()
+ {
+
+ return parameters.keys();
+
+ }
+
+ /** {@inheritDoc} */
+ public int getMajorVersion()
+ {
+
+ return 2;
+
+ }
+
+ /** {@inheritDoc} */
+ public String getMimeType(String path)
+ {
+
+ int period = path.lastIndexOf('.');
+ if (period < 0)
+ {
+ return null;
+ }
+ String extension = path.substring(period + 1);
+ return (String) mimeTypes.get(extension);
+
+ }
+
+ /** {@inheritDoc} */
+ public int getMinorVersion()
+ {
+
+ return 4;
+
+ }
+
+ /** {@inheritDoc} */
+ public String getRealPath(String path)
+ {
+
+ if (documentRoot != null)
+ {
+ if (!path.startsWith("/"))
+ {
+ throw new IllegalArgumentException("The specified path ('"
+ + path + "') does not start with a '/' character");
+ }
+ File resolved = new File(documentRoot, path.substring(1));
+ try
+ {
+ return resolved.getCanonicalPath();
+ }
+ catch (IOException e)
+ {
+ return resolved.getAbsolutePath();
+ }
+ }
+ else
+ {
+ return null;
+ }
+
+ }
+
+ /** {@inheritDoc} */
+ public URL getResource(String path) throws MalformedURLException
+ {
+
+ if (documentRoot != null)
+ {
+ if (!path.startsWith("/"))
+ {
+ throw new MalformedURLException("The specified path ('" + path
+ + "') does not start with a '/' character");
+ }
+ File resolved = new File(documentRoot, path.substring(1));
+ if (resolved.exists())
+ {
+ return resolved.toURL();
+ }
+ else
+ {
+ return null;
+ }
+ }
+ else
+ {
+ return null;
+ }
+
+ }
+
+ /** {@inheritDoc} */
+ public InputStream getResourceAsStream(String path)
+ {
+ try
+ {
+ URL url = getResource(path);
+ if (url != null)
+ {
+ return url.openStream();
+ }
+ }
+ catch (Exception e)
+ {
+ //No op
+ }
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ public Set getResourcePaths(String path)
+ {
+
+ if (documentRoot == null)
+ {
+ return null;
+ }
+
+ // Enforce the leading slash restriction
+ if (!path.startsWith("/"))
+ {
+ throw new IllegalArgumentException("The specified path ('" + path
+ + "') does not start with a '/' character");
+ }
+
+ // Locate the File node for this path's directory (if it exists)
+ File node = new File(documentRoot, path.substring(1));
+ if (!node.exists())
+ {
+ return null;
+ }
+ if (!node.isDirectory())
+ {
+ return null;
+ }
+
+ // Construct a Set containing the paths to the contents of this directory
+ Set set = new HashSet();
+ String[] files = node.list();
+ if (files == null)
+ {
+ return null;
+ }
+ for (int i = 0; i < files.length; i++)
+ {
+ String subfile = path + files[i];
+ File subnode = new File(node, files[i]);
+ if (subnode.isDirectory())
+ {
+ subfile += "/";
+ }
+ set.add(subfile);
+ }
+
+ // Return the completed set
+ return set;
+ }
+
+ /** {@inheritDoc} */
+ public void log(String message)
+ {
+ System.out.println(message);
+ }
+
+ /** {@inheritDoc} */
+ public void log(Exception exception, String message)
+ {
+ System.out.println(message);
+ exception.printStackTrace();
+ }
+
+ /** {@inheritDoc} */
+ public void log(String message, Throwable exception)
+ {
+ System.out.println(message);
+ exception.printStackTrace();
+ }
+
+ /** {@inheritDoc} */
+ public void removeAttribute(String name)
+ {
+ if (attributes.containsKey(name))
+ {
+ Object value = attributes.remove(name);
+ fireAttributeRemoved(name, value);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void setAttribute(String name, Object value)
+ {
+ if (name == null)
+ {
+ throw new IllegalArgumentException("Attribute name cannot be null");
+ }
+ if (value == null)
+ {
+ removeAttribute(name);
+ return;
+ }
+ if (attributes.containsKey(name))
+ {
+ Object oldValue = attributes.get(name);
+ attributes.put(name, value);
+ fireAttributeReplaced(name, oldValue);
+ }
+ else
+ {
+ attributes.put(name, value);
+ fireAttributeAdded(name, value);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public ServletContext getContext(String uripath)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ /** {@inheritDoc} */
+ public String getContextPath()
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ /** {@inheritDoc} */
+ public Servlet getServlet(String name) throws ServletException
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ /** {@inheritDoc} */
+ public String getServletContextName()
+ {
+ return "MockServletContext";
+ }
+
+ /** {@inheritDoc} */
+ public String getServerInfo()
+ {
+ return "MockServletContext";
+ }
+
+ /** {@inheritDoc} */
+ public Enumeration getServlets()
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ /** {@inheritDoc} */
+ public Enumeration getServletNames()
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ /** {@inheritDoc} */
+ public RequestDispatcher getNamedDispatcher(String name)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ /** {@inheritDoc} */
+ public RequestDispatcher getRequestDispatcher(String path)
+ {
+ URI uri = this.resolve(path);
+ if (uri != null)
+ {
+ File f = new File(uri);
+ if (f.exists())
+ {
+ try
+ {
+ return new MockRequestDispatcher(uri.toURL());
+ }
+ catch (MalformedURLException e)
+ {
+ this.log(e.getMessage());
+ return null;
+ }
+ }
+
+ }
+ return null;
+ }
+
+ // --------------------------------------------------------- Private Methods
+
+ private final URI resolve(String path)
+ {
+ if (path == null)
+ {
+ throw new NullPointerException("Path cannot be null");
+ }
+ if (path.charAt(0) == '/')
+ {
+ if (path.length() > 1)
+ {
+ return documentRoot.toURI().resolve(path.substring(1));
+ }
+ return documentRoot.toURI();
+ }
+ return null;
+ }
+
+ /**
+ * <p>Fire an attribute added event to interested listeners.</p>
+ *
+ * @param key Attribute whose value has been added
+ * @param value The new value
+ */
+ void fireAttributeAdded(String key, Object value)
+ {
+ MockWebContainer container = getWebContainer();
+ if (container == null)
+ {
+ if (attributeListeners.size() < 1)
+ {
+ return;
+ }
+ ServletContextAttributeEvent event = new ServletContextAttributeEvent(
+ this, key, value);
+ Iterator listeners = attributeListeners.iterator();
+ while (listeners.hasNext())
+ {
+ ServletContextAttributeListener listener = (ServletContextAttributeListener) listeners
+ .next();
+ listener.attributeAdded(event);
+ }
+ }
+ else
+ {
+ ServletContextAttributeEvent event = new ServletContextAttributeEvent(
+ this, key, value);
+ container.attributeAdded(event);
+ }
+ }
+
+ /**
+ * <p>Fire an attribute removed event to interested listeners.</p>
+ *
+ * @param key Attribute whose value has been removed
+ * @param value The value that was removed
+ */
+ void fireAttributeRemoved(String key, Object value)
+ {
+ MockWebContainer container = getWebContainer();
+ if (container == null)
+ {
+ if (attributeListeners.size() < 1)
+ {
+ return;
+ }
+ ServletContextAttributeEvent event = new ServletContextAttributeEvent(
+ this, key, value);
+ Iterator listeners = attributeListeners.iterator();
+ while (listeners.hasNext())
+ {
+ ServletContextAttributeListener listener = (ServletContextAttributeListener) listeners
+ .next();
+ listener.attributeRemoved(event);
+ }
+ }
+ else
+ {
+ ServletContextAttributeEvent event = new ServletContextAttributeEvent(
+ this, key, value);
+ container.attributeRemoved(event);
+ }
+ }
+
+ /**
+ * <p>Fire an attribute replaced event to interested listeners.</p>
+ *
+ * @param key Attribute whose value has been replaced
+ * @param value The original value
+ */
+ void fireAttributeReplaced(String key, Object value)
+ {
+ MockWebContainer container = getWebContainer();
+ if (container == null)
+ {
+ if (attributeListeners.size() < 1)
+ {
+ return;
+ }
+ ServletContextAttributeEvent event = new ServletContextAttributeEvent(
+ this, key, value);
+ Iterator listeners = attributeListeners.iterator();
+ while (listeners.hasNext())
+ {
+ ServletContextAttributeListener listener = (ServletContextAttributeListener) listeners
+ .next();
+ listener.attributeReplaced(event);
+ }
+ }
+ else
+ {
+ ServletContextAttributeEvent event = new ServletContextAttributeEvent(
+ this, key, value);
+ container.attributeReplaced(event);
+ }
+ }
+
+ public boolean setInitParameter(String name, String value)
+ {
+ addInitParameter(name, value);
+ return true;
+ }
+
+ public ServletRegistration.Dynamic addServlet(String name, String string1)
+ throws IllegalArgumentException, IllegalStateException
+ {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ public ServletRegistration.Dynamic addServlet(String name, Servlet srvlt)
+ throws IllegalArgumentException, IllegalStateException
+ {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ public ServletRegistration.Dynamic addServlet(String name, Class<? extends Servlet> type)
+ throws IllegalArgumentException, IllegalStateException
+ {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ public <T extends Servlet> T createServlet(Class<T> type) throws ServletException
+ {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ public ServletRegistration getServletRegistration(String name)
+ {
+ return servletRegistrations.get(name);
+ }
+
+ public Map<String, ? extends ServletRegistration> getServletRegistrations()
+ {
+ return servletRegistrations;
+ }
+
+ public void addServletRegistration(String name, String servletClassName, String ... mappings)
+ {
+ ServletRegistration sr = new MockServletRegistration(name, servletClassName, mappings);
+ this.servletRegistrations.put(name, sr);
+ }
+
+ public FilterRegistration.Dynamic addFilter(String string, String string1)
+ throws IllegalArgumentException, IllegalStateException
+ {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ public FilterRegistration.Dynamic addFilter(String string, Filter filter)
+ throws IllegalArgumentException, IllegalStateException
+ {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ public FilterRegistration.Dynamic addFilter(String string, Class<? extends Filter> type)
+ throws IllegalArgumentException, IllegalStateException
+ {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ public <T extends Filter> T createFilter(Class<T> type) throws ServletException
+ {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ public FilterRegistration getFilterRegistration(String string)
+ {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ public Map<String, ? extends FilterRegistration> getFilterRegistrations()
+ {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ public void addListener(Class<? extends EventListener> type)
+ {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ public void addListener(String string)
+ {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ public <T extends EventListener> void addListener(T t)
+ {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ public <T extends EventListener> T createListener(Class<T> type) throws ServletException
+ {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ public void declareRoles(String... strings)
+ {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ public SessionCookieConfig getSessionCookieConfig()
+ {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ public void setSessionTrackingModes(Set<SessionTrackingMode> set)
+ {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ public Set<SessionTrackingMode> getDefaultSessionTrackingModes()
+ {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ public int getEffectiveMajorVersion() throws UnsupportedOperationException
+ {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ public int getEffectiveMinorVersion() throws UnsupportedOperationException
+ {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ public Set<SessionTrackingMode> getEffectiveSessionTrackingModes()
+ {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ public ClassLoader getClassLoader()
+ {
+ if (classLoader != null)
+ {
+ return classLoader;
+ }
+ else
+ {
+ return Thread.currentThread().getContextClassLoader();
+ }
+ }
+
+ public void setClassLoader(ClassLoader classLoader)
+ {
+ this.classLoader = classLoader;
+ }
+
+ public JspConfigDescriptor getJspConfigDescriptor()
+ {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/mock/MockServletInputStream.java b/test30/src/main/java/org/apache/myfaces/test/mock/MockServletInputStream.java
new file mode 100644
index 0000000..4a57bf0
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/mock/MockServletInputStream.java
@@ -0,0 +1,54 @@
+/*
+ * 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.myfaces.test.mock;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+import javax.servlet.ServletInputStream;
+
+/**
+ *
+ * @author Jacob Hookom
+ * @version $Id: MockServletInputStream.java 804043 2009-08-13 22:08:44Z lu4242 $
+ * @since 1.0.0
+ */
+public class MockServletInputStream extends ServletInputStream
+{
+
+ private final InputStream source;
+
+ public MockServletInputStream()
+ {
+ this.source = new ByteArrayInputStream(new byte[0]);
+ }
+
+ public MockServletInputStream(InputStream source)
+ {
+ this.source = source;
+ }
+
+ public int read() throws IOException
+ {
+ return this.source.read();
+ }
+
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/mock/MockServletOutputStream.java b/test30/src/main/java/org/apache/myfaces/test/mock/MockServletOutputStream.java
new file mode 100644
index 0000000..676bf00
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/mock/MockServletOutputStream.java
@@ -0,0 +1,93 @@
+/*
+ * 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.myfaces.test.mock;
+
+import java.io.ByteArrayOutputStream;
+import javax.servlet.ServletOutputStream;
+
+/**
+ * <p>Mock implementation of <code>ServletOutputStream</code>.</p>
+ *
+ * $Id$
+ * @since 1.0.0
+ */
+
+public class MockServletOutputStream extends ServletOutputStream
+{
+
+ // ------------------------------------------------------------ Constructors
+
+ /**
+ * <p>Return a default instance.</p>
+ *
+ * @param stream The stream we will use to buffer output
+ */
+ public MockServletOutputStream(ByteArrayOutputStream stream)
+ {
+ this.baos = stream;
+ }
+
+ // ----------------------------------------------------- Mock Object Methods
+
+ /**
+ * <p>Return the content that has been written to this output stream.</p>
+ */
+ public byte[] content()
+ {
+ return baos.toByteArray();
+ }
+
+ /**
+ * <p>Reset this output stream so that it appears no content has been
+ * written.</p>
+ */
+ public void reset()
+ {
+ baos.reset();
+ }
+
+ /**
+ * <p>Return the number of bytes that have been written to this output stream.</p>
+ */
+ public int size()
+ {
+ return baos.size();
+ }
+
+ // ------------------------------------------------------ Instance Variables
+
+ /**
+ * <p>The internal buffer we use to capture output.</p>
+ */
+ private ByteArrayOutputStream baos = null;
+
+ // --------------------------------------------- ServletOutputStream Methods
+
+ /**
+ * <p>Write the specified content to our internal cache.</p>
+ *
+ * @param content Content to be written
+ */
+ public void write(int content)
+ {
+ baos.write(content);
+ }
+
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/mock/MockServletRegistration.java b/test30/src/main/java/org/apache/myfaces/test/mock/MockServletRegistration.java
new file mode 100644
index 0000000..a639c69
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/mock/MockServletRegistration.java
@@ -0,0 +1,103 @@
+/*
+ * 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.myfaces.test.mock;
+
+import java.util.Collection;
+import java.util.LinkedHashSet;
+import java.util.Map;
+import java.util.Set;
+import javax.servlet.ServletRegistration;
+
+/**
+ *
+ */
+public class MockServletRegistration implements ServletRegistration
+{
+ public static String[] facesServletMappings = {"/faces/*", "*.jsf", "*.xhtml"};
+
+ private Set<String> mappings = new LinkedHashSet<String>();
+ private String name;
+ private String servletClassName;
+
+ public MockServletRegistration(String name, String servletClassName, String ... mappings)
+ {
+ this.name = name;
+ this.servletClassName = servletClassName;
+ }
+
+ public MockServletRegistration()
+ {
+ }
+
+ public Set<String> addMapping(String... mappingArray)
+ {
+ for (String s : mappingArray)
+ {
+ mappings.add(s);
+ }
+ return mappings;
+ }
+
+ public Collection<String> getMappings()
+ {
+ return mappings;
+ }
+
+ public String getRunAsRole()
+ {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ public String getClassName()
+ {
+ return servletClassName;
+ }
+
+ public String getInitParameter(String string)
+ {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ public Map<String, String> getInitParameters()
+ {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ public String getName()
+ {
+ return name;
+ }
+
+ public boolean setInitParameter(String string, String string1)
+ {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ public Set<String> setInitParameters(Map<String, String> map)
+ {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ public void setClassName(String className)
+ {
+ this.servletClassName = className;
+ }
+
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/mock/MockStateManager.java b/test30/src/main/java/org/apache/myfaces/test/mock/MockStateManager.java
new file mode 100644
index 0000000..07d1445
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/mock/MockStateManager.java
@@ -0,0 +1,112 @@
+/*
+ * 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.myfaces.test.mock;
+
+import java.io.IOException;
+
+import javax.faces.application.StateManager;
+import javax.faces.component.UIViewRoot;
+import javax.faces.context.FacesContext;
+
+/**
+ * <p>Mock implementation of <code>StateManager</code>.</p>
+ *
+ * $Id$
+ * @since 1.0.0
+ */
+public class MockStateManager extends StateManager
+{
+
+ // ------------------------------------------------------------ Constructors
+
+ /**
+ * <p>Construct a default instance.</p>
+ */
+ public MockStateManager()
+ {
+ }
+
+ // ----------------------------------------------------- Mock Object Methods
+
+ // ------------------------------------------------------ Instance Variables
+
+ // ---------------------------------------------------- StateManager Methods
+
+ /** {@inheritDoc} */
+ public SerializedView saveSerializedView(FacesContext context)
+ {
+
+ throw new UnsupportedOperationException();
+
+ }
+
+ /** {@inheritDoc} */
+ public Object getTreeStructureToSave(FacesContext context)
+ {
+
+ throw new UnsupportedOperationException();
+
+ }
+
+ /** {@inheritDoc} */
+ public Object getComponentStateToSave(FacesContext context)
+ {
+
+ throw new UnsupportedOperationException();
+
+ }
+
+ /** {@inheritDoc} */
+ public void writeState(FacesContext context, SerializedView view)
+ throws IOException
+ {
+
+ throw new UnsupportedOperationException();
+
+ }
+
+ /** {@inheritDoc} */
+ public UIViewRoot restoreView(FacesContext context, String viewId,
+ String renderKitId)
+ {
+
+ throw new UnsupportedOperationException();
+
+ }
+
+ /** {@inheritDoc} */
+ public UIViewRoot restoreTreeStructure(FacesContext context, String viewId,
+ String renderKitId)
+ {
+
+ throw new UnsupportedOperationException();
+
+ }
+
+ /** {@inheritDoc} */
+ public void restoreComponentState(FacesContext context, UIViewRoot view,
+ String renderKitId)
+ {
+
+ throw new UnsupportedOperationException();
+
+ }
+
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/mock/MockSubKeyMap.java b/test30/src/main/java/org/apache/myfaces/test/mock/MockSubKeyMap.java
new file mode 100644
index 0000000..5298b59
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/mock/MockSubKeyMap.java
@@ -0,0 +1,315 @@
+/*
+ * 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.myfaces.test.mock;
+
+import java.util.AbstractMap;
+import java.util.AbstractSet;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+
+/**
+ * NOTE: Class copied from trinidad to be used on MockFlash.
+ * <p/>
+ * Map that wraps another to provide an isolated namespace using
+ * a prefix. This is especially handy for storing properties on
+ * the session in a structured manner without putting them into
+ * a true "Map" - because storing in a Map breaks session failover.
+ * (Session failover won't trigger on mutations of contained objects.)
+ * <p/>
+ * Note that there is a potential design flaw; if you create a SubKeyMap
+ * for "mypackage.foo" and for "mypackage.foo.bar", all the keys in the
+ * latter will actually show up in the former (prefixed by ".bar"). This
+ * "flaw" is actually relied on by PageFlowScopeMap (since it provides
+ * a handy way to clear out all descendents), so don't "fix" it!
+ *
+ * @since 1.0.0
+ */
+final class MockSubKeyMap<V> extends AbstractMap<String, V>
+{
+ public MockSubKeyMap(Map<String, Object> base, String prefix)
+ {
+ if (base == null)
+ {
+ throw new NullPointerException();
+ }
+ if (prefix == null)
+ {
+ throw new NullPointerException();
+ }
+
+ // Optimize the scenario where we're wrapping another SubKeyMap
+ if (base instanceof MockSubKeyMap)
+ {
+ _base = ((MockSubKeyMap) base)._base;
+ _prefix = ((MockSubKeyMap) base)._prefix + prefix;
+ }
+ else
+ {
+ _base = base;
+ _prefix = prefix;
+ }
+ }
+
+ @Override
+ public boolean isEmpty()
+ {
+ return entrySet().isEmpty();
+ }
+
+ @Override
+ public V get(Object key)
+ {
+ key = _getBaseKey(key);
+ return (V) _base.get(key);
+ }
+
+ @Override
+ public V put(String key, V value)
+ {
+ key = _getBaseKey(key);
+ return (V) _base.put(key, value);
+ }
+
+ @Override
+ public V remove(Object key)
+ {
+ key = _getBaseKey(key);
+ return (V) _base.remove(key);
+ }
+
+ @Override
+ public boolean containsKey(Object key)
+ {
+ if (!(key instanceof String))
+ {
+ return false;
+ }
+
+ return _base.containsKey(_getBaseKey(key));
+ }
+
+ @Override
+ public Set<Map.Entry<String, V>> entrySet()
+ {
+ if (_entrySet == null)
+ {
+ _entrySet = new Entries<V>();
+ }
+ return _entrySet;
+ }
+
+ private String _getBaseKey(Object key)
+ {
+ if (key == null)
+ {
+ throw new NullPointerException();
+ }
+ // Yes, I want a ClassCastException if it's not a String
+ return _prefix + ((String) key);
+ }
+
+ private List<String> _gatherKeys()
+ {
+ List<String> list = new ArrayList<String>();
+ for (String key : _base.keySet())
+ {
+ if (key != null && key.startsWith(_prefix))
+ {
+ list.add(key);
+ }
+ }
+
+ return list;
+ }
+
+ //
+ // Set implementation for SubkeyMap.entrySet()
+ //
+
+ private class Entries<V> extends AbstractSet<Map.Entry<String, V>>
+ {
+ public Entries()
+ {
+ }
+
+ @Override
+ public Iterator<Map.Entry<String, V>> iterator()
+ {
+ // Sadly, if you just try to use a filtering approach
+ // on the iterator, you'll get concurrent modification
+ // exceptions. Consequently, gather the keys in a list
+ // and iterator over that.
+ List<String> keyList = _gatherKeys();
+ return new EntryIterator<V>(keyList.iterator());
+ }
+
+ @Override
+ public int size()
+ {
+ int size = 0;
+ for (String key : _base.keySet())
+ {
+ if (key != null && key.startsWith(_prefix))
+ {
+ size++;
+ }
+ }
+
+ return size;
+ }
+
+ @Override
+ public boolean isEmpty()
+ {
+ Iterator<String> keys = _base.keySet().iterator();
+ while (keys.hasNext())
+ {
+ String key = keys.next();
+ // Short-circuit: the default implementation would always
+ // need to iterate to find the total size.
+ if (key != null && key.startsWith(_prefix))
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ @Override
+ public void clear()
+ {
+ Iterator<String> keys = _base.keySet().iterator();
+ while (keys.hasNext())
+ {
+ String key = keys.next();
+ if (key != null && key.startsWith(_prefix))
+ {
+ keys.remove();
+ }
+ }
+ }
+ }
+
+ private class EntryIterator<V> implements Iterator<Map.Entry<String, V>>
+ {
+ public EntryIterator(Iterator<String> iterator)
+ {
+ _iterator = iterator;
+ }
+
+ public boolean hasNext()
+ {
+ return _iterator.hasNext();
+ }
+
+ public Map.Entry<String, V> next()
+ {
+ String baseKey = _iterator.next();
+ _currentKey = baseKey;
+ return new Entry<V>(baseKey);
+ }
+
+ public void remove()
+ {
+ if (_currentKey == null)
+ {
+ throw new IllegalStateException();
+ }
+
+ _base.remove(_currentKey);
+
+ _currentKey = null;
+ }
+
+ private Iterator<String> _iterator;
+ private String _currentKey;
+ }
+
+ private class Entry<V> implements Map.Entry<String, V>
+ {
+ public Entry(String baseKey)
+ {
+ _baseKey = baseKey;
+ }
+
+ public String getKey()
+ {
+ if (_key == null)
+ {
+ _key = _baseKey.substring(_prefix.length());
+ }
+ return _key;
+ }
+
+ public V getValue()
+ {
+ return (V) _base.get(_baseKey);
+ }
+
+ public V setValue(V value)
+ {
+ return (V) _base.put(_baseKey, value);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public boolean equals(Object o)
+ {
+ if (!(o instanceof Map.Entry))
+ {
+ return false;
+ }
+ Map.Entry<String, V> e = (Map.Entry<String, V>) o;
+ return _equals(getKey(), e.getKey())
+ && _equals(getValue(), e.getValue());
+ }
+
+ @Override
+ public int hashCode()
+ {
+ Object key = getKey();
+ Object value = getValue();
+ return ((key == null) ? 0 : key.hashCode())
+ ^ ((value == null) ? 0 : value.hashCode());
+ }
+
+ private String _baseKey;
+ private String _key;
+ }
+
+ static private boolean _equals(Object a, Object b)
+ {
+ if (a == null)
+ {
+ return b == null;
+ }
+ return a.equals(b);
+ }
+
+ private final Map<String, Object> _base;
+ private final String _prefix;
+ private Set<Map.Entry<String, V>> _entrySet;
+
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/mock/MockViewHandler.java b/test30/src/main/java/org/apache/myfaces/test/mock/MockViewHandler.java
new file mode 100644
index 0000000..5e3aa4e
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/mock/MockViewHandler.java
@@ -0,0 +1,169 @@
+/*
+ * 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.myfaces.test.mock;
+
+import java.util.Locale;
+
+import javax.faces.application.ViewHandler;
+import javax.faces.component.UIViewRoot;
+import javax.faces.context.FacesContext;
+import javax.faces.render.RenderKitFactory;
+
+/**
+ * <p>Mock implementation of <code>ViewHandler</code>.</p>
+ *
+ * $Id$
+ * @since 1.0.0
+ */
+
+public class MockViewHandler extends ViewHandler
+{
+
+ // ------------------------------------------------------------ Constructors
+
+ /**
+ * <p>Construct a default instance.</p>
+ */
+ public MockViewHandler()
+ {
+ }
+
+ // ----------------------------------------------------- Mock Object Methods
+
+ // ------------------------------------------------------ Instance Variables
+
+ // ----------------------------------------------------- ViewHandler Methods
+
+ /** {@inheritDoc} */
+ public Locale calculateLocale(FacesContext context)
+ {
+
+ Locale locale = context.getApplication().getDefaultLocale();
+ if (locale == null)
+ {
+ locale = Locale.getDefault();
+ }
+ return locale;
+
+ }
+
+ /** {@inheritDoc} */
+ public String calculateRenderKitId(FacesContext context)
+ {
+
+ String renderKitId = context.getApplication().getDefaultRenderKitId();
+ if (renderKitId == null)
+ {
+ renderKitId = RenderKitFactory.HTML_BASIC_RENDER_KIT;
+ }
+ return renderKitId;
+
+ }
+
+ /** {@inheritDoc} */
+ public UIViewRoot createView(FacesContext context, String viewId)
+ {
+
+ // Save locale and renderKitId from previous view (if any), per spec
+ Locale locale = null;
+ String renderKitId = null;
+ if (context.getViewRoot() != null)
+ {
+ locale = context.getViewRoot().getLocale();
+ renderKitId = context.getViewRoot().getRenderKitId();
+ }
+
+ // Configure a new UIViewRoot instance
+ UIViewRoot view = new UIViewRoot();
+ view.setViewId(viewId);
+ if (locale != null)
+ {
+ view.setLocale(locale);
+ }
+ else
+ {
+ view.setLocale(context.getApplication().getViewHandler()
+ .calculateLocale(context));
+ }
+ if (renderKitId != null)
+ {
+ view.setRenderKitId(renderKitId);
+ }
+ else
+ {
+ view.setRenderKitId(context.getApplication().getViewHandler()
+ .calculateRenderKitId(context));
+ }
+
+ // Return the configured instance
+ return view;
+
+ }
+
+ /** {@inheritDoc} */
+ public String getActionURL(FacesContext context, String viewId)
+ {
+
+ return FacesContext.getCurrentInstance().getExternalContext()
+ .getRequestContextPath()
+ + viewId;
+
+ }
+
+ /** {@inheritDoc} */
+ public String getResourceURL(FacesContext context, String path)
+ {
+
+ return FacesContext.getCurrentInstance().getExternalContext()
+ .getRequestContextPath()
+ + path;
+
+ }
+
+ /** {@inheritDoc} */
+ public void renderView(FacesContext context, UIViewRoot view)
+ {
+
+ throw new UnsupportedOperationException();
+
+ }
+
+ /** {@inheritDoc} */
+ public UIViewRoot restoreView(FacesContext context, String viewId)
+ {
+
+ throw new UnsupportedOperationException();
+
+ }
+
+ /** {@inheritDoc} */
+ public void writeState(FacesContext context)
+ {
+
+ }
+
+ public String getWebsocketURL(FacesContext context, String channelAndToken)
+ {
+ String url = context.getExternalContext().getRequestContextPath() +
+ "/javax.faces.push/"+channelAndToken;
+ return url;
+ }
+
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/mock/MockViewHandler20.java b/test30/src/main/java/org/apache/myfaces/test/mock/MockViewHandler20.java
new file mode 100644
index 0000000..3384f26
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/mock/MockViewHandler20.java
@@ -0,0 +1,52 @@
+/*
+ * 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.myfaces.test.mock;
+
+import java.util.List;
+import java.util.Map;
+
+import javax.faces.context.ExternalContext;
+import javax.faces.context.FacesContext;
+
+/**
+ * Mock for a JSF 2.0 ViewHandler.
+ * This ViewHandler is used by MockApplication20.
+ *
+ * @author Jakob Korherr (latest modification by $Author$)
+ * @version $Revision$ $Date$
+ * @since 1.0.0
+ */
+public class MockViewHandler20 extends MockViewHandler
+{
+
+ @Override
+ public String getBookmarkableURL(FacesContext context, String viewId,
+ Map<String, List<String>> parameters, boolean includeViewParams)
+ {
+ // the standard impl only calls getActionURL(context, viewId)
+ // but we want to include the parameters too
+
+ String actionEncodedViewId = getActionURL(context, viewId);
+ ExternalContext externalContext = context.getExternalContext();
+ String bookmarkEncodedURL = externalContext.encodeBookmarkableURL(
+ actionEncodedViewId, parameters);
+ return externalContext.encodeActionURL(bookmarkEncodedURL);
+ }
+
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/mock/MockWebContainer.java b/test30/src/main/java/org/apache/myfaces/test/mock/MockWebContainer.java
new file mode 100644
index 0000000..108bcea
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/mock/MockWebContainer.java
@@ -0,0 +1,299 @@
+/*
+ * 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.myfaces.test.mock;
+
+import java.util.ArrayList;
+import java.util.List;
+import javax.servlet.ServletContextAttributeEvent;
+import javax.servlet.ServletContextAttributeListener;
+import javax.servlet.ServletContextEvent;
+import javax.servlet.ServletContextListener;
+import javax.servlet.ServletRequestAttributeEvent;
+import javax.servlet.ServletRequestAttributeListener;
+import javax.servlet.ServletRequestEvent;
+import javax.servlet.ServletRequestListener;
+import javax.servlet.http.HttpSessionAttributeListener;
+import javax.servlet.http.HttpSessionBindingEvent;
+import javax.servlet.http.HttpSessionEvent;
+import javax.servlet.http.HttpSessionListener;
+
+/**
+ *
+ */
+public class MockWebContainer
+ implements ServletContextListener, ServletContextAttributeListener,
+ ServletRequestListener, ServletRequestAttributeListener,
+ HttpSessionListener, HttpSessionAttributeListener
+{
+
+ // context
+ private List<ServletContextListener> contextListeners;
+ private List<ServletContextAttributeListener> contextAttributeListeners;
+
+ // request
+ private List<ServletRequestListener> requestListeners;
+ private List<ServletRequestAttributeListener> requestAttributeListeners;
+
+ // session
+ private List<HttpSessionListener> sessionListeners;
+ private List<HttpSessionAttributeListener> sessionAttributeListeners;
+
+ public MockWebContainer()
+ {
+ }
+
+ /**
+ * Create an instance of the passes class and subscribe it as as servlet listener
+ *
+ * @param listenerClassName
+ * @throws ClassNotFoundException
+ * @throws InstantiationException
+ * @throws IllegalAccessException
+ */
+ public void subscribeListener(String listenerClassName)
+ throws ClassNotFoundException, InstantiationException, IllegalAccessException
+ {
+ Class clazz = this.getClass().getClassLoader().loadClass(listenerClassName);
+ Object instance = clazz.newInstance();
+ subscribeListener(instance);
+ }
+
+ /**
+ * Subscribe a servlet listener
+ *
+ * @param listener
+ */
+ public void subscribeListener(Object listener)
+ {
+ if (listener instanceof ServletContextListener)
+ {
+ if (contextListeners == null)
+ {
+ contextListeners = new ArrayList<ServletContextListener>();
+ }
+ contextListeners.add((ServletContextListener)listener);
+ }
+ if (listener instanceof ServletContextAttributeListener)
+ {
+ if (contextAttributeListeners == null)
+ {
+ contextAttributeListeners = new ArrayList<ServletContextAttributeListener>();
+ }
+ contextAttributeListeners.add((ServletContextAttributeListener)listener);
+ }
+ if (listener instanceof ServletRequestListener)
+ {
+ if (requestListeners == null)
+ {
+ requestListeners = new ArrayList<ServletRequestListener>();
+ }
+ requestListeners.add((ServletRequestListener)listener);
+ }
+ if (listener instanceof ServletRequestAttributeListener)
+ {
+ if (requestAttributeListeners == null)
+ {
+ requestAttributeListeners = new ArrayList<ServletRequestAttributeListener>();
+ }
+ requestAttributeListeners.add((ServletRequestAttributeListener)listener);
+ }
+ if (listener instanceof HttpSessionListener)
+ {
+ if (sessionListeners == null)
+ {
+ sessionListeners = new ArrayList<HttpSessionListener>();
+ }
+ sessionListeners.add((HttpSessionListener)listener);
+ }
+ if (listener instanceof HttpSessionAttributeListener)
+ {
+ if (sessionAttributeListeners == null)
+ {
+ sessionAttributeListeners = new ArrayList<HttpSessionAttributeListener>();
+ }
+ sessionAttributeListeners.add((HttpSessionAttributeListener)listener);
+ }
+ }
+
+ public void contextInitialized(ServletContextEvent sce)
+ {
+ if (contextListeners != null && !contextListeners.isEmpty())
+ {
+ for (ServletContextListener listener : contextListeners)
+ {
+ listener.contextInitialized(sce);
+ }
+ }
+ }
+
+ public void contextDestroyed(ServletContextEvent sce)
+ {
+ if (contextListeners != null && !contextListeners.isEmpty())
+ {
+ for (ServletContextListener listener : contextListeners)
+ {
+ listener.contextDestroyed(sce);
+ }
+ }
+ }
+
+ public void attributeAdded(ServletContextAttributeEvent scab)
+ {
+ if (contextAttributeListeners != null && !contextAttributeListeners.isEmpty())
+ {
+ for (ServletContextAttributeListener listener : contextAttributeListeners)
+ {
+ listener.attributeAdded(scab);
+ }
+ }
+ }
+
+ public void attributeRemoved(ServletContextAttributeEvent scab)
+ {
+ if (contextAttributeListeners != null && !contextAttributeListeners.isEmpty())
+ {
+ for (ServletContextAttributeListener listener : contextAttributeListeners)
+ {
+ listener.attributeRemoved(scab);
+ }
+ }
+ }
+
+ public void attributeReplaced(ServletContextAttributeEvent scab)
+ {
+ if (contextAttributeListeners != null && !contextAttributeListeners.isEmpty())
+ {
+ for (ServletContextAttributeListener listener : contextAttributeListeners)
+ {
+ listener.attributeReplaced(scab);
+ }
+ }
+ }
+
+ public void requestInitialized ( ServletRequestEvent sre )
+ {
+ if (requestListeners != null && !requestListeners.isEmpty())
+ {
+ for (ServletRequestListener listener : requestListeners)
+ {
+ listener.requestInitialized(sre);
+ }
+ }
+ }
+
+ public void requestDestroyed ( ServletRequestEvent sre )
+ {
+ if (requestListeners != null && !requestListeners.isEmpty())
+ {
+ for (ServletRequestListener listener : requestListeners)
+ {
+ listener.requestDestroyed(sre);
+ }
+ }
+ }
+
+ public void sessionCreated ( HttpSessionEvent se )
+ {
+ if (sessionListeners != null && !sessionListeners.isEmpty())
+ {
+ for (HttpSessionListener listener : sessionListeners)
+ {
+ listener.sessionCreated(se);
+ }
+ }
+ }
+
+ public void sessionDestroyed ( HttpSessionEvent se )
+ {
+ if (sessionListeners != null && !sessionListeners.isEmpty())
+ {
+ for (HttpSessionListener listener : sessionListeners)
+ {
+ listener.sessionDestroyed(se);
+ }
+ }
+ }
+
+ public void attributeAdded(ServletRequestAttributeEvent srae)
+ {
+ if (requestAttributeListeners != null && !requestAttributeListeners.isEmpty())
+ {
+ for (ServletRequestAttributeListener listener : requestAttributeListeners)
+ {
+ listener.attributeAdded(srae);
+ }
+ }
+ }
+
+ public void attributeRemoved(ServletRequestAttributeEvent srae)
+ {
+ if (requestAttributeListeners != null && !requestAttributeListeners.isEmpty())
+ {
+ for (ServletRequestAttributeListener listener : requestAttributeListeners)
+ {
+ listener.attributeRemoved(srae);
+ }
+ }
+ }
+
+ public void attributeReplaced(ServletRequestAttributeEvent srae)
+ {
+ if (requestAttributeListeners != null && !requestAttributeListeners.isEmpty())
+ {
+ for (ServletRequestAttributeListener listener : requestAttributeListeners)
+ {
+ listener.attributeReplaced(srae);
+ }
+ }
+ }
+
+ public void attributeAdded(HttpSessionBindingEvent se)
+ {
+ if (sessionAttributeListeners != null && !sessionAttributeListeners.isEmpty())
+ {
+ for (HttpSessionAttributeListener listener : sessionAttributeListeners)
+ {
+ listener.attributeAdded(se);
+ }
+ }
+ }
+
+ public void attributeRemoved(HttpSessionBindingEvent se)
+ {
+ if (sessionAttributeListeners != null && !sessionAttributeListeners.isEmpty())
+ {
+ for (HttpSessionAttributeListener listener : sessionAttributeListeners)
+ {
+ listener.attributeRemoved(se);
+ }
+ }
+ }
+
+ public void attributeReplaced(HttpSessionBindingEvent se)
+ {
+ if (sessionAttributeListeners != null && !sessionAttributeListeners.isEmpty())
+ {
+ for (HttpSessionAttributeListener listener : sessionAttributeListeners)
+ {
+ listener.attributeReplaced(se);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/test30/src/main/java/org/apache/myfaces/test/mock/MockedJsfTestContainer.java b/test30/src/main/java/org/apache/myfaces/test/mock/MockedJsfTestContainer.java
new file mode 100644
index 0000000..0a54621
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/mock/MockedJsfTestContainer.java
@@ -0,0 +1,415 @@
+/*
+ * 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.myfaces.test.mock;
+
+import java.util.Locale;
+import javax.faces.FactoryFinder;
+import javax.faces.application.ApplicationFactory;
+import javax.faces.component.UIViewRoot;
+import javax.faces.lifecycle.LifecycleFactory;
+import javax.faces.render.RenderKitFactory;
+import javax.servlet.ServletRequestEvent;
+import javax.servlet.http.HttpSessionEvent;
+import javax.servlet.http.HttpSessionListener;
+import org.apache.myfaces.test.config.ResourceBundleVarNames;
+import org.apache.myfaces.test.mock.lifecycle.MockLifecycle;
+import org.apache.myfaces.test.mock.lifecycle.MockLifecycleFactory;
+
+/**
+ *
+ */
+public class MockedJsfTestContainer implements HttpSessionListener
+{
+
+ // ------------------------------------------------------------ Constructors
+
+ /**
+ * <p>Construct a new instance of this test case.</p>
+ *
+ * @param name Name of this test case
+ */
+ public MockedJsfTestContainer()
+ {
+ }
+
+ // ---------------------------------------------------- Overall Test Methods
+
+ /**
+ * <p>Set up instance variables required by this test case.</p>
+ */
+ public void setUp()
+ {
+ // Set up Servlet API Objects
+ setUpServletContext();
+
+ // Set up JSF API Objects
+ FactoryFinder.releaseFactories();
+
+ setFactories();
+
+ setUpJSFObjects();
+ }
+
+ public void setUpAll()
+ {
+ setUp();
+ startRequest();
+ }
+
+ public void tearDownAll()
+ {
+ endRequest();
+ tearDownRequest();
+ }
+
+ /**
+ * <p>Setup JSF object used for the test. By default it calls to the following
+ * methods in this order:</p>
+ *
+ * <ul>
+ * <li><code>setUpLifecycle();</code></li>
+ * <li><code>setUpApplication();</code></li>
+ * <li><code>setUpRenderKit();</code></li>
+ * </ul>
+ *
+ * @throws Exception
+ */
+ protected void setUpJSFObjects()
+ {
+ setUpLifecycle();
+ setUpApplication();
+ setUpRenderKit();
+ }
+
+ /**
+ * <p>Setup servlet objects that will be used for the test:</p>
+ *
+ * <ul>
+ * <li><code>config</code> (<code>MockServletConfig</code>)</li>
+ * <li><code>servletContext</code> (<code>MockServletContext</code>)</li>
+ * </ul>
+ *
+ * @throws Exception
+ */
+ protected void setUpServletContext()
+ {
+ servletContext = new MockServletContext();
+ config = new MockServletConfig(servletContext);
+ webContainer = new MockWebContainer();
+ servletContext.setWebContainer(webContainer);
+ // Subscribe the container to receive session creation and destroy events.
+ webContainer.subscribeListener(this);
+ }
+
+ /**
+ * <p>Setup servlet objects that will be used for the test:</p>
+ *
+ * <ul>
+ * <li><code>request</code> (<code>MockHttpServletRequest</code></li>
+ * <li><code>response</code> (<code>MockHttpServletResponse</code>)</li>
+ * </ul>
+ *
+ * @throws Exception
+ */
+ protected void setUpRequest()
+ {
+ request = lastSession == null ?
+ new MockHttpServletRequest() : new MockHttpServletRequest(lastSession);
+ requestInitializedCalled = false;
+ request.setServletContext(servletContext);
+ response = new MockHttpServletResponse();
+ }
+
+ protected void doRequestInitialized()
+ {
+ if (!requestInitializedCalled)
+ {
+ webContainer.requestInitialized(new ServletRequestEvent(servletContext, request));
+ requestInitializedCalled = true;
+ }
+ }
+
+ public void startRequest()
+ {
+ setUpRequest();
+ doRequestInitialized();
+
+ setUpFacesContext();
+ setUpDefaultView();
+ }
+
+ public void startSession()
+ {
+ if (request != null)
+ {
+ //Create it indirectly through call to getSession(...)
+ request.getSession(true);
+ }
+ }
+
+ public void endSession()
+ {
+ MockHttpSession session = (MockHttpSession) request.getSession(false);
+ if (session != null)
+ {
+ session.invalidate();
+ }
+ }
+
+ public void sessionCreated(HttpSessionEvent se)
+ {
+ lastSession = (MockHttpSession) se.getSession();
+ //No op
+ }
+
+ public void sessionDestroyed(HttpSessionEvent se)
+ {
+ lastSession = null;
+ }
+
+ /**
+ * <p>Set JSF factories using FactoryFinder method setFactory.</p>
+ *
+ * @throws Exception
+ */
+ protected void setFactories()
+ {
+ FactoryFinder.setFactory(FactoryFinder.APPLICATION_FACTORY,
+ "org.apache.myfaces.test.mock.MockApplicationFactory");
+ FactoryFinder.setFactory(FactoryFinder.FACES_CONTEXT_FACTORY,
+ "org.apache.myfaces.test.mock.MockFacesContextFactory");
+ FactoryFinder.setFactory(FactoryFinder.LIFECYCLE_FACTORY,
+ "org.apache.myfaces.test.mock.lifecycle.MockLifecycleFactory");
+ FactoryFinder.setFactory(FactoryFinder.RENDER_KIT_FACTORY,
+ "org.apache.myfaces.test.mock.MockRenderKitFactory");
+ FactoryFinder.setFactory(FactoryFinder.EXCEPTION_HANDLER_FACTORY,
+ "org.apache.myfaces.test.mock.MockExceptionHandlerFactory");
+ FactoryFinder.setFactory(FactoryFinder.PARTIAL_VIEW_CONTEXT_FACTORY,
+ "org.apache.myfaces.test.mock.MockPartialViewContextFactory");
+ FactoryFinder.setFactory(FactoryFinder.VISIT_CONTEXT_FACTORY,
+ "org.apache.myfaces.test.mock.visit.MockVisitContextFactory");
+ FactoryFinder.setFactory(FactoryFinder.CLIENT_WINDOW_FACTORY,
+ "org.apache.myfaces.test.mock.MockClientWindowFactory");
+ }
+
+ /**
+ * Setup the <code>lifecycle</code> and <code>lifecycleFactory</code>
+ * variables.
+ *
+ * @throws Exception
+ */
+ protected void setUpLifecycle()
+ {
+ lifecycleFactory = (MockLifecycleFactory) FactoryFinder
+ .getFactory(FactoryFinder.LIFECYCLE_FACTORY);
+ lifecycle = (MockLifecycle) lifecycleFactory
+ .getLifecycle(LifecycleFactory.DEFAULT_LIFECYCLE);
+ }
+
+ /**
+ * Setup the <code>facesContextFactory</code> and <code>facesContext</code>
+ * variable. Before end, by default it override <code>externalContext</code>
+ * variable from the value retrieved from facesContext.getExternalContext(),
+ * because sometimes it is possible facesContext overrides externalContext
+ * internally.
+ *
+ * @throws Exception
+ */
+ protected void setUpFacesContext()
+ {
+ facesContextFactory = (MockFacesContextFactory) FactoryFinder
+ .getFactory(FactoryFinder.FACES_CONTEXT_FACTORY);
+ facesContext = (MockFacesContext) facesContextFactory.getFacesContext(
+ servletContext, request, response, lifecycle);
+ if (facesContext.getExternalContext() != null)
+ {
+ externalContext = (MockExternalContext) facesContext
+ .getExternalContext();
+ }
+ else
+ {
+ externalContext = new MockExternalContext(servletContext, request,
+ response);
+ facesContext.setExternalContext(externalContext);
+ }
+ facesContext.setApplication(application);
+ }
+
+ /**
+ * By default, create an instance of UIViewRoot, set its viewId as "/viewId"
+ * and assign it to the current facesContext.
+ *
+ * @throws Exception
+ */
+ protected void setUpDefaultView()
+ {
+ UIViewRoot root = new UIViewRoot();
+ root.setViewId("/viewId");
+ root.setLocale(getLocale());
+ root.setRenderKitId(RenderKitFactory.HTML_BASIC_RENDER_KIT);
+ facesContext.setViewRoot(root);
+ }
+
+ protected Locale getLocale()
+ {
+ return Locale.getDefault();
+ }
+
+ /**
+ * Setup the <code>application</code> variable and before
+ * the end by default it is assigned to the <code>facesContext</code>
+ * variable, calling <code>facesContext.setApplication(application)</code>
+ *
+ * @throws Exception
+ */
+ protected void setUpApplication()
+ {
+ ApplicationFactory applicationFactory = (ApplicationFactory) FactoryFinder
+ .getFactory(FactoryFinder.APPLICATION_FACTORY);
+ application = (MockApplication) applicationFactory.getApplication();
+ }
+
+ /**
+ * Setup the <code>renderKit</code> variable. This is a good place to use
+ * <code>ConfigParser</code> to register converters, validators, components
+ * or renderkits.
+ *
+ * @throws Exception
+ */
+ protected void setUpRenderKit()
+ {
+ RenderKitFactory renderKitFactory = (RenderKitFactory) FactoryFinder
+ .getFactory(FactoryFinder.RENDER_KIT_FACTORY);
+ renderKit = new MockRenderKit();
+ renderKitFactory.addRenderKit(RenderKitFactory.HTML_BASIC_RENDER_KIT,
+ renderKit);
+ }
+
+ public MockApplication getApplication()
+ {
+ return application;
+ }
+
+ public MockExternalContext getExternalContext()
+ {
+ return externalContext;
+ }
+
+ public MockFacesContext getFacesContext()
+ {
+ return facesContext;
+ }
+
+ public MockHttpServletRequest getRequest()
+ {
+ return request;
+ }
+
+ public MockHttpServletResponse getResponse()
+ {
+ return response;
+ }
+
+ public MockServletContext getServletContext()
+ {
+ return servletContext;
+ }
+
+ /**
+ * @return the webContainer
+ */
+ public MockWebContainer getWebContainer()
+ {
+ return webContainer;
+ }
+
+ /**
+ * This method call doRequestDestroyed() and then tearDownRequest().
+ */
+ public final void endRequest()
+ {
+
+ doRequestDestroyed();
+ tearDownRequest();
+ }
+
+ protected void doRequestDestroyed()
+ {
+ if (request != null)
+ {
+ webContainer.requestDestroyed(new ServletRequestEvent(servletContext, request));
+ }
+ }
+
+ protected void tearDownRequest()
+ {
+ if (facesContext != null)
+ {
+ facesContext.release();
+ }
+ externalContext = null;
+ facesContext = null;
+ request = null;
+ response = null;
+ }
+
+ /**
+ * <p>Tear down instance variables required by this test case.</p>
+ */
+ public void tearDown()
+ {
+ if (facesContext != null)
+ {
+ facesContext.release();
+ }
+ application = null;
+ config = null;
+ externalContext = null;
+ facesContext = null;
+ lifecycle = null;
+ lifecycleFactory = null;
+ renderKit = null;
+ request = null;
+ response = null;
+ servletContext = null;
+ lastSession = null;
+ webContainer = null;
+ FactoryFinder.releaseFactories();
+ ResourceBundleVarNames.resetNames();
+ }
+
+ // ------------------------------------------------------ Instance Variables
+
+ // Mock object instances for our tests
+ protected MockApplication application = null;
+ protected MockServletConfig config = null;
+ protected MockExternalContext externalContext = null;
+ protected MockFacesContext facesContext = null;
+ protected MockFacesContextFactory facesContextFactory = null;
+ protected MockLifecycle lifecycle = null;
+ protected MockLifecycleFactory lifecycleFactory = null;
+ protected MockRenderKit renderKit = null;
+ protected MockHttpServletRequest request = null;
+ protected boolean requestInitializedCalled = false;
+ protected MockHttpServletResponse response = null;
+ protected MockHttpSession lastSession = null;
+ protected MockServletContext servletContext = null;
+ private MockWebContainer webContainer = null;
+
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/mock/_AbstractAttributeMap.java b/test30/src/main/java/org/apache/myfaces/test/mock/_AbstractAttributeMap.java
new file mode 100644
index 0000000..ef5aecb
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/mock/_AbstractAttributeMap.java
@@ -0,0 +1,448 @@
+/*
+ * 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.myfaces.test.mock;
+
+import java.util.AbstractMap;
+import java.util.AbstractSet;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.Set;
+
+/**
+ * Helper Map implementation for use with different Attribute Maps.
+ *
+ * @author Anton Koinov (latest modification by $Author: jakobk $)
+ * @version $Revision: 979229 $ $Date: 2010-07-26 12:26:53 +0200 (Mo, 26 Jul 2010) $
+ * @since 1.0.0
+ */
+abstract class _AbstractAttributeMap<V> extends AbstractMap<String, V>
+{
+ private Set<String> _keySet;
+ private Collection<V> _values;
+ private Set<Entry<String, V>> _entrySet;
+
+ @Override
+ public void clear()
+ {
+ final List<String> names = Collections.list(getAttributeNames());
+
+ for (String name : names)
+ {
+ removeAttribute(name);
+ }
+ }
+
+ @Override
+ public final boolean containsKey(final Object key)
+ {
+ return getAttribute(key.toString()) != null;
+ }
+
+ @Override
+ public boolean containsValue(final Object findValue)
+ {
+ if (findValue == null)
+ {
+ return false;
+ }
+
+ for (final Enumeration<String> e = getAttributeNames(); e
+ .hasMoreElements();)
+ {
+ final Object value = getAttribute(e.nextElement());
+ if (findValue.equals(value))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ @Override
+ public Set<Entry<String, V>> entrySet()
+ {
+ if (_entrySet == null)
+ {
+ _entrySet = new EntrySet();
+ }
+ return _entrySet;
+ }
+
+ @Override
+ public V get(final Object key)
+ {
+ return getAttribute(key.toString());
+ }
+
+ @Override
+ public boolean isEmpty()
+ {
+ return !getAttributeNames().hasMoreElements();
+ }
+
+ @Override
+ public Set<String> keySet()
+ {
+ if (_keySet == null)
+ {
+ _keySet = new KeySet();
+ }
+ return _keySet;
+ }
+
+ @Override
+ public final V put(final String key, final V value)
+ {
+ final V retval = getAttribute(key);
+ setAttribute(key, value);
+ return retval;
+ }
+
+ @Override
+ public void putAll(final Map<? extends String, ? extends V> t)
+ {
+ for (final Entry<? extends String, ? extends V> entry : t.entrySet())
+ {
+ setAttribute(entry.getKey(), entry.getValue());
+ }
+ }
+
+ @Override
+ public final V remove(final Object key)
+ {
+ final String tempKey = key.toString();
+ final V retval = getAttribute(tempKey);
+ removeAttribute(tempKey);
+ return retval;
+ }
+
+ @Override
+ public int size()
+ {
+ int size = 0;
+ for (final Enumeration<String> e = getAttributeNames(); e
+ .hasMoreElements();)
+ {
+ size++;
+ e.nextElement();
+ }
+ return size;
+ }
+
+ @Override
+ public Collection<V> values()
+ {
+ if(_values == null)
+ {
+ _values = new Values();
+ }
+ return _values;
+ }
+
+ abstract protected V getAttribute(String key);
+
+ abstract protected void setAttribute(String key, V value);
+
+ abstract protected void removeAttribute(String key);
+
+ abstract protected Enumeration<String> getAttributeNames();
+
+ private abstract class AbstractAttributeSet<E> extends AbstractSet<E>
+ {
+ @Override
+ public boolean isEmpty()
+ {
+ return _AbstractAttributeMap.this.isEmpty();
+ }
+
+ @Override
+ public int size()
+ {
+ return _AbstractAttributeMap.this.size();
+ }
+
+ @Override
+ public void clear()
+ {
+ _AbstractAttributeMap.this.clear();
+ }
+ }
+
+ private final class KeySet extends AbstractAttributeSet<String>
+ {
+ @Override
+ public Iterator<String> iterator()
+ {
+ return new KeyIterator();
+ }
+
+ @Override
+ public boolean contains(final Object o)
+ {
+ return _AbstractAttributeMap.this.containsKey(o);
+ }
+
+ @Override
+ public boolean remove(final Object o)
+ {
+ return _AbstractAttributeMap.this.remove(o) != null;
+ }
+
+ }
+
+ private abstract class AbstractAttributeIterator<E> implements Iterator<E>
+ {
+ // We use a copied version of the Enumeration from getAttributeNames()
+ // here, because directly using it might cause a ConcurrentModificationException
+ // when performing remove(). Note that we can do this since the Enumeration
+ // from getAttributeNames() will contain exactly the attribute names from the time
+ // getAttributeNames() was called and it will not be updated if attributes are
+ // removed or added.
+ protected final Iterator<String> _i = Collections.list(
+ getAttributeNames()).iterator();
+ protected String _currentKey;
+
+ public void remove()
+ {
+ if (_currentKey == null)
+ {
+ throw new NoSuchElementException(
+ "You must call next() at least once");
+ }
+ _AbstractAttributeMap.this.remove(_currentKey);
+ }
+
+ public boolean hasNext()
+ {
+ return _i.hasNext();
+ }
+
+ public E next()
+ {
+ _currentKey = _i.next();
+ return getValue(_currentKey);
+ }
+
+ protected abstract E getValue(String attributeName);
+ }
+
+ private final class KeyIterator extends AbstractAttributeIterator<String>
+ {
+ @Override
+ protected String getValue(final String attributeName)
+ {
+ return attributeName;
+ }
+ }
+
+ private class Values extends AbstractAttributeSet<V>
+ {
+ @Override
+ public Iterator<V> iterator()
+ {
+ return new ValuesIterator();
+ }
+
+ @Override
+ public boolean contains(final Object o)
+ {
+ if (o == null)
+ {
+ return false;
+ }
+
+ for (final Iterator<V> it = iterator(); it.hasNext();)
+ {
+ if (o.equals(it.next()))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ @Override
+ public boolean remove(final Object o)
+ {
+ if (o == null)
+ {
+ return false;
+ }
+
+ for (final Iterator<V> it = iterator(); it.hasNext();)
+ {
+ if (o.equals(it.next()))
+ {
+ it.remove();
+ return true;
+ }
+ }
+
+ return false;
+ }
+ }
+
+ private class ValuesIterator extends AbstractAttributeIterator<V>
+ {
+ @Override
+ protected V getValue(final String attributeName)
+ {
+ return _AbstractAttributeMap.this.get(attributeName);
+ }
+ }
+
+ private final class EntrySet extends AbstractAttributeSet<Entry<String, V>>
+ {
+ @Override
+ public Iterator<Entry<String, V>> iterator()
+ {
+ return new EntryIterator();
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public boolean contains(final Object o)
+ {
+ if (!(o instanceof Entry))
+ {
+ return false;
+ }
+
+ final Entry<String, V> entry = (Entry<String, V>) o;
+ final Object key = entry.getKey();
+ final Object value = entry.getValue();
+ if (key == null || value == null)
+ {
+ return false;
+ }
+
+ return value.equals(_AbstractAttributeMap.this.get(key));
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public boolean remove(final Object o)
+ {
+ if (!(o instanceof Entry))
+ {
+ return false;
+ }
+
+ final Entry<String, V> entry = (Entry<String, V>) o;
+ final Object key = entry.getKey();
+ final Object value = entry.getValue();
+ if (key == null || value == null
+ || !value.equals(_AbstractAttributeMap.this.get(key)))
+ {
+ return false;
+ }
+
+ return _AbstractAttributeMap.this.remove(((Entry<String, V>) o)
+ .getKey()) != null;
+ }
+ }
+
+ /**
+ * Not very efficient since it generates a new instance of <code>Entry</code> for each element and still internaly
+ * uses the <code>KeyIterator</code>. It is more efficient to use the <code>KeyIterator</code> directly.
+ */
+ private final class EntryIterator extends
+ AbstractAttributeIterator<Entry<String, V>>
+ {
+ @Override
+ protected Entry<String, V> getValue(final String attributeName)
+ {
+ // Must create new Entry every time--value of the entry must stay
+ // linked to the same attribute name
+ return new EntrySetEntry(attributeName);
+ }
+ }
+
+ private final class EntrySetEntry implements Entry<String, V>
+ {
+ private final String _currentKey;
+
+ public EntrySetEntry(final String currentKey)
+ {
+ _currentKey = currentKey;
+ }
+
+ public String getKey()
+ {
+ return _currentKey;
+ }
+
+ public V getValue()
+ {
+ return _AbstractAttributeMap.this.get(_currentKey);
+ }
+
+ public V setValue(final V value)
+ {
+ return _AbstractAttributeMap.this.put(_currentKey, value);
+ }
+
+ @Override
+ public int hashCode()
+ {
+ int result = 1;
+ result = 31 * result
+ + ((_currentKey == null) ? 0 : _currentKey.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(final Object obj)
+ {
+ if (this == obj)
+ {
+ return true;
+ }
+ if (obj == null)
+ {
+ return false;
+ }
+ if (getClass() != obj.getClass())
+ {
+ return false;
+ }
+ final EntrySetEntry other = (EntrySetEntry) obj;
+ if (_currentKey == null)
+ {
+ if (other._currentKey != null)
+ {
+ return false;
+ }
+ }
+ else if (!_currentKey.equals(other._currentKey))
+ {
+ return false;
+ }
+ return true;
+ }
+
+ }
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/mock/_ApplicationMap.java b/test30/src/main/java/org/apache/myfaces/test/mock/_ApplicationMap.java
new file mode 100644
index 0000000..8f4088e
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/mock/_ApplicationMap.java
@@ -0,0 +1,78 @@
+/*
+ * 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.myfaces.test.mock;
+
+import java.util.Enumeration;
+import java.util.Map;
+
+import javax.servlet.ServletContext;
+
+/**
+ * ServletContext attributes as a Map.
+ *
+ * @author Anton Koinov (latest modification by $Author: slessard $)
+ * @version $Revision: 701829 $ $Date: 2008-10-05 19:06:02 +0200 (So, 05 Okt 2008) $
+ * @since 1.0.0
+ */
+final class _ApplicationMap extends _AbstractAttributeMap<Object>
+{
+ final ServletContext _servletContext;
+
+ _ApplicationMap(final ServletContext servletContext)
+ {
+ _servletContext = servletContext;
+ }
+
+ @Override
+ protected Object getAttribute(final String key)
+ {
+ return _servletContext.getAttribute(key);
+ }
+
+ @Override
+ protected void setAttribute(final String key, final Object value)
+ {
+ _servletContext.setAttribute(key, value);
+ }
+
+ @Override
+ protected void removeAttribute(final String key)
+ {
+ _servletContext.removeAttribute(key);
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ protected Enumeration<String> getAttributeNames()
+ {
+ return _servletContext.getAttributeNames();
+ }
+
+ @Override
+ public void putAll(final Map<? extends String, ? extends Object> t)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void clear()
+ {
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/mock/_CookieMap.java b/test30/src/main/java/org/apache/myfaces/test/mock/_CookieMap.java
new file mode 100644
index 0000000..9b46d2b
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/mock/_CookieMap.java
@@ -0,0 +1,167 @@
+/*
+ * 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.myfaces.test.mock;
+
+import java.util.Enumeration;
+import java.util.Map;
+import java.util.NoSuchElementException;
+
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+
+/**
+ * HttpServletRequest Cookies as Map.
+ *
+ * @author Dimitry D'hondt
+ * @author Anton Koinov
+ * @version $Revision: 701829 $ $Date: 2008-10-05 19:06:02 +0200 (So, 05 Okt 2008) $
+ * @since 1.0.0
+ */
+final class _CookieMap extends _AbstractAttributeMap<Object>
+{
+ private static final Cookie[] EMPTY_ARRAY = new Cookie[0];
+
+ private final HttpServletRequest _httpServletRequest;
+
+ _CookieMap(final HttpServletRequest httpServletRequest)
+ {
+ _httpServletRequest = httpServletRequest;
+ }
+
+ @Override
+ public void clear()
+ {
+ throw new UnsupportedOperationException(
+ "Cannot clear HttpRequest Cookies");
+ }
+
+ @Override
+ public boolean containsValue(final Object findValue)
+ {
+ if (findValue == null)
+ {
+ return false;
+ }
+
+ final Cookie[] cookies = _httpServletRequest.getCookies();
+ if (cookies == null)
+ {
+ return false;
+ }
+ for (int i = 0, len = cookies.length; i < len; i++)
+ {
+ if (findValue.equals(cookies[i]))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ @Override
+ public boolean isEmpty()
+ {
+ final Cookie[] cookies = _httpServletRequest.getCookies();
+ return cookies == null || cookies.length == 0;
+ }
+
+ @Override
+ public int size()
+ {
+ final Cookie[] cookies = _httpServletRequest.getCookies();
+ return cookies == null ? 0 : cookies.length;
+ }
+
+ @Override
+ public void putAll(final Map<? extends String, ? extends Object> t)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ protected Object getAttribute(final String key)
+ {
+ final Cookie[] cookies = _httpServletRequest.getCookies();
+ if (cookies == null)
+ {
+ return null;
+ }
+ for (int i = 0, len = cookies.length; i < len; i++)
+ {
+ if (cookies[i].getName().equals(key))
+ {
+ return cookies[i];
+ }
+ }
+
+ return null;
+ }
+
+ @Override
+ protected void setAttribute(final String key, final Object value)
+ {
+ throw new UnsupportedOperationException(
+ "Cannot set HttpRequest Cookies");
+ }
+
+ @Override
+ protected void removeAttribute(final String key)
+ {
+ throw new UnsupportedOperationException(
+ "Cannot remove HttpRequest Cookies");
+ }
+
+ @Override
+ protected Enumeration<String> getAttributeNames()
+ {
+ final Cookie[] cookies = _httpServletRequest.getCookies();
+
+ return cookies == null ? new CookieNameEnumeration(EMPTY_ARRAY)
+ : new CookieNameEnumeration(cookies);
+
+ }
+
+ private static class CookieNameEnumeration implements Enumeration<String>
+ {
+ private final Cookie[] _cookies;
+ private final int _length;
+ private int _index;
+
+ public CookieNameEnumeration(final Cookie[] cookies)
+ {
+ _cookies = cookies;
+ _length = cookies.length;
+ }
+
+ public boolean hasMoreElements()
+ {
+ return _index < _length;
+ }
+
+ public String nextElement()
+ {
+ if (!hasMoreElements())
+ {
+ throw new NoSuchElementException();
+ }
+ return _cookies[_index++].getName();
+ }
+ }
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/mock/_ELText.java b/test30/src/main/java/org/apache/myfaces/test/mock/_ELText.java
new file mode 100644
index 0000000..6371c13
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/mock/_ELText.java
@@ -0,0 +1,444 @@
+/*
+ * 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.myfaces.test.mock;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.el.ELContext;
+import javax.el.ELException;
+import javax.el.ExpressionFactory;
+import javax.el.ValueExpression;
+import javax.faces.context.ResponseWriter;
+
+/**
+ * Handles parsing EL Strings in accordance with the EL-API Specification. The parser accepts either <code>${..}</code>
+ * or <code>#{..}</code>.
+ *
+ * @author Jacob Hookom
+ * @version $Id: ELText.java,v 1.8 2008/07/13 19:01:42 rlubke Exp $
+ */
+class _ELText
+{
+
+ private static final class LiteralValueExpression extends ValueExpression
+ {
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = 1L;
+
+ private final String text;
+
+ public LiteralValueExpression(String text)
+ {
+ this.text = text;
+ }
+
+ public boolean isLiteralText()
+ {
+ return false;
+ }
+
+ public int hashCode()
+ {
+ return 0;
+ }
+
+ public String getExpressionString()
+ {
+ return this.text;
+ }
+
+ public boolean equals(Object obj)
+ {
+ return false;
+ }
+
+ public void setValue(ELContext context, Object value)
+ {
+ }
+
+ public boolean isReadOnly(ELContext context)
+ {
+ return false;
+ }
+
+ public Object getValue(ELContext context)
+ {
+ return null;
+ }
+
+ public Class<?> getType(ELContext context)
+ {
+ return null;
+ }
+
+ public Class<?> getExpectedType()
+ {
+ return null;
+ }
+
+ }
+
+ private static final class ELTextComposite extends _ELText
+ {
+ private final _ELText[] txt;
+
+ public ELTextComposite(_ELText[] txt)
+ {
+ super(null);
+ this.txt = txt;
+ }
+
+ public void write(Writer out, ELContext ctx) throws ELException, IOException
+ {
+ for (int i = 0; i < this.txt.length; i++)
+ {
+ this.txt[i].write(out, ctx);
+ }
+ }
+
+ public void writeText(ResponseWriter out, ELContext ctx) throws ELException, IOException
+ {
+ for (int i = 0; i < this.txt.length; i++)
+ {
+ this.txt[i].writeText(out, ctx);
+ }
+ }
+
+ public String toString(ELContext ctx)
+ {
+ StringBuffer sb = new StringBuffer();
+ for (int i = 0; i < this.txt.length; i++)
+ {
+ sb.append(this.txt[i].toString(ctx));
+ }
+ return sb.toString();
+ }
+
+ /*
+ * public String toString(ELContext ctx) { StringBuffer sb = new StringBuffer(); for (int i = 0; i <
+ * this.txt.length; i++) { sb.append(this.txt[i].toString(ctx)); } return sb.toString(); }
+ */
+
+ public String toString()
+ {
+ StringBuffer sb = new StringBuffer();
+ for (int i = 0; i < this.txt.length; i++)
+ {
+ sb.append(this.txt[i].toString());
+ }
+ return sb.toString();
+ }
+
+ public boolean isLiteral()
+ {
+ return false;
+ }
+
+ public _ELText apply(ExpressionFactory factory, ELContext ctx)
+ {
+ int len = this.txt.length;
+ _ELText[] nt = new _ELText[len];
+ for (int i = 0; i < len; i++)
+ {
+ nt[i] = this.txt[i].apply(factory, ctx);
+ }
+ return new ELTextComposite(nt);
+ }
+ }
+
+ private static final class ELTextVariable extends _ELText
+ {
+ private final ValueExpression ve;
+
+ public ELTextVariable(ValueExpression ve)
+ {
+ super(ve.getExpressionString());
+ this.ve = ve;
+ }
+
+ public boolean isLiteral()
+ {
+ return false;
+ }
+
+ public _ELText apply(ExpressionFactory factory, ELContext ctx)
+ {
+ return new ELTextVariable(factory.createValueExpression(ctx, this.ve.getExpressionString(), String.class));
+ }
+
+ public void write(Writer out, ELContext ctx) throws ELException, IOException
+ {
+ Object v = this.ve.getValue(ctx);
+ if (v != null)
+ {
+ out.write((String) v);
+ }
+ }
+
+ public String toString(ELContext ctx) throws ELException
+ {
+ Object v = this.ve.getValue(ctx);
+ if (v != null)
+ {
+ return v.toString();
+ }
+
+ return null;
+ }
+
+ public void writeText(ResponseWriter out, ELContext ctx) throws ELException, IOException
+ {
+ Object v = this.ve.getValue(ctx);
+ if (v != null)
+ {
+ out.writeText((String) v, null);
+ }
+ }
+ }
+
+ protected final String literal;
+
+ public _ELText(String literal)
+ {
+ this.literal = literal;
+ }
+
+ /**
+ * If it's literal text
+ *
+ * @return true if the String is literal (doesn't contain <code>#{..}</code> or <code>${..}</code>)
+ */
+ public boolean isLiteral()
+ {
+ return true;
+ }
+
+ /**
+ * Return an instance of <code>this</code> that is applicable given the ELContext and ExpressionFactory state.
+ *
+ * @param factory
+ * the ExpressionFactory to use
+ * @param ctx
+ * the ELContext to use
+ * @return an ELText instance
+ */
+ public _ELText apply(ExpressionFactory factory, ELContext ctx)
+ {
+ return this;
+ }
+
+ /**
+ * Allow this instance to write to the passed Writer, given the ELContext state
+ *
+ * @param out
+ * Writer to write to
+ * @param ctx
+ * current ELContext state
+ * @throws ELException
+ * @throws IOException
+ */
+ public void write(Writer out, ELContext ctx) throws ELException, IOException
+ {
+ out.write(this.literal);
+ }
+
+ public void writeText(ResponseWriter out, ELContext ctx) throws ELException, IOException
+ {
+ out.writeText(this.literal, null);
+ }
+
+ /**
+ * Evaluates the ELText to a String
+ *
+ * @param ctx
+ * current ELContext state
+ * @throws ELException
+ * @return the evaluated String
+ */
+ public String toString(ELContext ctx) throws ELException
+ {
+ return this.literal;
+ }
+
+ public String toString()
+ {
+ return this.literal;
+ }
+
+ /**
+ * Parses the passed string to determine if it's literal or not
+ *
+ * @param in
+ * input String
+ * @return true if the String is literal (doesn't contain <code>#{..}</code> or <code>${..}</code>)
+ */
+ public static boolean isLiteral(String in)
+ {
+ _ELText txt = parse(in);
+ return txt == null || txt.isLiteral();
+ }
+
+ /**
+ * Factory method for creating an unvalidated ELText instance. NOTE: All expressions in the passed String are
+ * treated as {@link org.apache.myfaces.view.facelets.el.LiteralValueExpression LiteralValueExpressions}.
+ *
+ * @param in
+ * String to parse
+ * @return ELText instance that knows if the String was literal or not
+ * @throws javax.el.ELException
+ */
+ public static _ELText parse(String in) throws ELException
+ {
+ return parse(null, null, in);
+ }
+
+ /**
+ * Factory method for creating a validated ELText instance. When an Expression is hit, it will use the
+ * ExpressionFactory to create a ValueExpression instance, resolving any functions at that time. <p/> Variables and
+ * properties will not be evaluated.
+ *
+ * @param fact
+ * ExpressionFactory to use
+ * @param ctx
+ * ELContext to validate against
+ * @param in
+ * String to parse
+ * @return ELText that can be re-applied later
+ * @throws javax.el.ELException
+ */
+ public static _ELText parse(ExpressionFactory fact, ELContext ctx, String in) throws ELException
+ {
+ char[] ca = in.toCharArray();
+ int i = 0;
+ char c = 0;
+ int len = ca.length;
+ int end = len - 1;
+ boolean esc = false;
+ int vlen = 0;
+
+ StringBuffer buff = new StringBuffer(128);
+ List<_ELText> text = new ArrayList<_ELText>();
+ _ELText t = null;
+ ValueExpression ve = null;
+
+ while (i < len)
+ {
+ c = ca[i];
+ if ('\\' == c)
+ {
+ esc = !esc;
+ if (esc && i < end && (ca[i + 1] == '$' || ca[i + 1] == '#'))
+ {
+ i++;
+ continue;
+ }
+ }
+ else if (!esc && ('$' == c || '#' == c))
+ {
+ if (i < end)
+ {
+ if ('{' == ca[i + 1])
+ {
+ if (buff.length() > 0)
+ {
+ text.add(new _ELText(buff.toString()));
+ buff.setLength(0);
+ }
+ vlen = findVarLength(ca, i);
+ if (ctx != null && fact != null)
+ {
+ ve = fact.createValueExpression(ctx, new String(ca, i, vlen), String.class);
+ t = new ELTextVariable(ve);
+ }
+ else
+ {
+ t = new ELTextVariable(new LiteralValueExpression(new String(ca, i, vlen)));
+ }
+ text.add(t);
+ i += vlen;
+ continue;
+ }
+ }
+ }
+ esc = false;
+ buff.append(c);
+ i++;
+ }
+
+ if (buff.length() > 0)
+ {
+ text.add(new _ELText(new String(buff.toString())));
+ buff.setLength(0);
+ }
+
+ if (text.size() == 0)
+ {
+ return null;
+ }
+ else if (text.size() == 1)
+ {
+ return (_ELText) text.get(0);
+ }
+ else
+ {
+ _ELText[] ta = (_ELText[]) text.toArray(new _ELText[text.size()]);
+ return new ELTextComposite(ta);
+ }
+ }
+
+ private static int findVarLength(char[] ca, int s) throws ELException
+ {
+ int i = s;
+ int len = ca.length;
+ char c = 0;
+ int str = 0;
+ while (i < len)
+ {
+ c = ca[i];
+ if ('\\' == c && i < len - 1)
+ {
+ i++;
+ }
+ else if ('\'' == c || '"' == c)
+ {
+ if (str == c)
+ {
+ str = 0;
+ }
+ else
+ {
+ str = c;
+ }
+ }
+ else if (str == 0 && ('}' == c))
+ {
+ return i - s + 1;
+ }
+ i++;
+ }
+ throw new ELException("EL Expression Unbalanced: ... " + new String(ca, s, i - s));
+ }
+
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/mock/_Hex.java b/test30/src/main/java/org/apache/myfaces/test/mock/_Hex.java
new file mode 100644
index 0000000..b22a80e
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/mock/_Hex.java
@@ -0,0 +1,341 @@
+/*
+ * 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.myfaces.test.mock;
+
+import java.io.UnsupportedEncodingException;
+
+/**
+ * Hex encoder and decoder. The charset used for certain operation can be set, the default is set in
+ * {@link #DEFAULT_CHARSET_NAME}
+ *
+ * @since 1.0.0
+ * @author Apache Software Foundation
+ * @version $Id: Hex.java 801639 2009-08-06 13:15:10Z niallp $
+ */
+class _Hex
+{
+
+ /**
+ * Default charset name is {@link CharEncoding#UTF_8}
+ */
+ public static final String DEFAULT_CHARSET_NAME = "UTF-8";
+
+ /**
+ * Used to build output as Hex
+ */
+ private static final char[] DIGITS_LOWER = { '0', '1', '2', '3', '4', '5',
+ '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
+
+ /**
+ * Used to build output as Hex
+ */
+ private static final char[] DIGITS_UPPER = { '0', '1', '2', '3', '4', '5',
+ '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
+
+ /**
+ * Converts an array of characters representing hexadecimal values into an array of bytes of those same values. The
+ * returned array will be half the length of the passed array, as it takes two characters to represent any given
+ * byte. An exception is thrown if the passed char array has an odd number of elements.
+ *
+ * @param data
+ * An array of characters containing hexadecimal digits
+ * @return A byte array containing binary data decoded from the supplied char array.
+ * @throws DecoderException
+ * Thrown if an odd number or illegal of characters is supplied
+ */
+ public static byte[] decodeHex(char[] data) throws Exception
+ {
+
+ int len = data.length;
+
+ if ((len & 0x01) != 0)
+ {
+ throw new Exception("Odd number of characters.");
+ }
+
+ byte[] out = new byte[len >> 1];
+
+ // two characters form the hex value.
+ for (int i = 0, j = 0; j < len; i++)
+ {
+ int f = toDigit(data[j], j) << 4;
+ j++;
+ f = f | toDigit(data[j], j);
+ j++;
+ out[i] = (byte) (f & 0xFF);
+ }
+
+ return out;
+ }
+
+ /**
+ * Converts an array of bytes into an array of characters representing the hexadecimal values of each byte in order.
+ * The returned array will be double the length of the passed array, as it takes two characters to represent any
+ * given byte.
+ *
+ * @param data
+ * a byte[] to convert to Hex characters
+ * @return A char[] containing hexadecimal characters
+ */
+ public static char[] encodeHex(byte[] data)
+ {
+ return encodeHex(data, true);
+ }
+
+ /**
+ * Converts an array of bytes into an array of characters representing the hexadecimal values of each byte in order.
+ * The returned array will be double the length of the passed array, as it takes two characters to represent any
+ * given byte.
+ *
+ * @param data
+ * a byte[] to convert to Hex characters
+ * @param toLowerCase
+ * <code>true</code> converts to lowercase, <code>false</code> to uppercase
+ * @return A char[] containing hexadecimal characters
+ */
+ public static char[] encodeHex(byte[] data, boolean toLowerCase)
+ {
+ return encodeHex(data, toLowerCase ? DIGITS_LOWER : DIGITS_UPPER);
+ }
+
+ /**
+ * Converts an array of bytes into an array of characters representing the hexadecimal values of each byte in order.
+ * The returned array will be double the length of the passed array, as it takes two characters to represent any
+ * given byte.
+ *
+ * @param data
+ * a byte[] to convert to Hex characters
+ * @param toDigits
+ * the output alphabet
+ * @return A char[] containing hexadecimal characters
+ */
+ protected static char[] encodeHex(byte[] data, char[] toDigits)
+ {
+ int l = data.length;
+ char[] out = new char[l << 1];
+ // two characters form the hex value.
+ for (int i = 0, j = 0; i < l; i++)
+ {
+ out[j++] = toDigits[(0xF0 & data[i]) >>> 4];
+ out[j++] = toDigits[0x0F & data[i]];
+ }
+ return out;
+ }
+
+ /**
+ * Converts an array of bytes into a String representing the hexadecimal values of each byte in order. The returned
+ * String will be double the length of the passed array, as it takes two characters to represent any given byte.
+ *
+ * @param data
+ * a byte[] to convert to Hex characters
+ * @return A String containing hexadecimal characters
+ */
+ public static String encodeHexString(byte[] data)
+ {
+ return new String(encodeHex(data));
+ }
+
+ /**
+ * Converts a hexadecimal character to an integer.
+ *
+ * @param ch
+ * A character to convert to an integer digit
+ * @param index
+ * The index of the character in the source
+ * @return An integer
+ * @throws DecoderException
+ * Thrown if ch is an illegal hex character
+ */
+ protected static int toDigit(char ch, int index) throws Exception
+ {
+ int digit = Character.digit(ch, 16);
+ if (digit == -1)
+ {
+ throw new Exception("Illegal hexadecimal charcter " + ch
+ + " at index " + index);
+ }
+ return digit;
+ }
+
+ private final String charsetName;
+
+ /**
+ * Creates a new codec with the default charset name {@link #DEFAULT_CHARSET_NAME}
+ */
+ public _Hex()
+ {
+ // use default encoding
+ this.charsetName = DEFAULT_CHARSET_NAME;
+ }
+
+ /**
+ * Creates a new codec with the given charset name.
+ *
+ * @param csName
+ * the charset name.
+ */
+ public _Hex(String csName)
+ {
+ this.charsetName = csName;
+ }
+
+ /**
+ * Converts an array of character bytes representing hexadecimal values into an array of bytes of those same values.
+ * The returned array will be half the length of the passed array, as it takes two characters to represent any given
+ * byte. An exception is thrown if the passed char array has an odd number of elements.
+ *
+ * @param array
+ * An array of character bytes containing hexadecimal digits
+ * @return A byte array containing binary data decoded from the supplied byte array (representing characters).
+ * @throws DecoderException
+ * Thrown if an odd number of characters is supplied to this function
+ * @see #decodeHex(char[])
+ */
+ public byte[] decode(byte[] array) throws Exception
+ {
+ try
+ {
+ return decodeHex(new String(array, getCharsetName()).toCharArray());
+ }
+ catch (UnsupportedEncodingException e)
+ {
+ throw new Exception(e.getMessage(), e);
+ }
+ }
+
+ /**
+ * Converts a String or an array of character bytes representing hexadecimal values into an array of bytes of those
+ * same values. The returned array will be half the length of the passed String or array, as it takes two characters
+ * to represent any given byte. An exception is thrown if the passed char array has an odd number of elements.
+ *
+ * @param object
+ * A String or, an array of character bytes containing hexadecimal digits
+ * @return A byte array containing binary data decoded from the supplied byte array (representing characters).
+ * @throws DecoderException
+ * Thrown if an odd number of characters is supplied to this function or the object is not a String or
+ * char[]
+ * @see #decodeHex(char[])
+ */
+ public Object decode(Object object) throws Exception
+ {
+ try
+ {
+ char[] charArray = object instanceof String ? ((String) object)
+ .toCharArray() : (char[]) object;
+ return decodeHex(charArray);
+ }
+ catch (ClassCastException e)
+ {
+ throw new Exception(e.getMessage(), e);
+ }
+ }
+
+ /**
+ * Converts an array of bytes into an array of bytes for the characters representing the hexadecimal values of each
+ * byte in order. The returned array will be double the length of the passed array, as it takes two characters to
+ * represent any given byte.
+ * <p>
+ * The conversion from hexadecimal characters to the returned bytes is performed with the charset named by
+ * {@link #getCharsetName()}.
+ * </p>
+ *
+ * @param array
+ * a byte[] to convert to Hex characters
+ * @return A byte[] containing the bytes of the hexadecimal characters
+ * @throws IllegalStateException
+ * if the charsetName is invalid. This API throws {@link IllegalStateException} instead of
+ * {@link UnsupportedEncodingException} for backward compatibility.
+ * @see #encodeHex(byte[])
+ */
+ public byte[] encode(byte[] array)
+ {
+ return getBytesUnchecked(encodeHexString(array), getCharsetName());
+ }
+
+ private static byte[] getBytesUnchecked(String string, String charsetName)
+ {
+ if (string == null)
+ {
+ return null;
+ }
+ try
+ {
+ return string.getBytes(charsetName);
+ }
+ catch (UnsupportedEncodingException e)
+ {
+ throw new IllegalStateException(charsetName, e);
+ }
+ }
+
+ /**
+ * Converts a String or an array of bytes into an array of characters representing the hexadecimal values of each
+ * byte in order. The returned array will be double the length of the passed String or array, as it takes two
+ * characters to represent any given byte.
+ * <p>
+ * The conversion from hexadecimal characters to bytes to be encoded to performed with the charset named by
+ * {@link #getCharsetName()}.
+ * </p>
+ *
+ * @param object
+ * a String, or byte[] to convert to Hex characters
+ * @return A char[] containing hexadecimal characters
+ * @throws EncoderException
+ * Thrown if the given object is not a String or byte[]
+ * @see #encodeHex(byte[])
+ */
+ public Object encode(Object object) throws Exception
+ {
+ try
+ {
+ byte[] byteArray = object instanceof String ? ((String) object)
+ .getBytes(getCharsetName()) : (byte[]) object;
+ return encodeHex(byteArray);
+ }
+ catch (ClassCastException e)
+ {
+ throw new Exception(e.getMessage(), e);
+ }
+ catch (UnsupportedEncodingException e)
+ {
+ throw new Exception(e.getMessage(), e);
+ }
+ }
+
+ /**
+ * Gets the charset name.
+ *
+ * @return the charset name.
+ */
+ public String getCharsetName()
+ {
+ return this.charsetName;
+ }
+
+ /**
+ * Returns a string representation of the object, which includes the charset name.
+ *
+ * @return a string representation of the object.
+ */
+ public String toString()
+ {
+ return super.toString() + "[charsetName=" + this.charsetName + "]";
+ }
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/mock/_InitParameterMap.java b/test30/src/main/java/org/apache/myfaces/test/mock/_InitParameterMap.java
new file mode 100644
index 0000000..5cd02c5
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/mock/_InitParameterMap.java
@@ -0,0 +1,80 @@
+/*
+ * 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.myfaces.test.mock;
+
+import java.util.Enumeration;
+import java.util.Map;
+
+import javax.servlet.ServletContext;
+
+/**
+ * ServletContext init parameters as Map.
+ *
+ * @author Anton Koinov (latest modification by $Author: slessard $)
+ * @version $Revision: 701829 $ $Date: 2008-10-05 19:06:02 +0200 (So, 05 Okt 2008) $
+ * @since 1.0.0
+ */
+final class _InitParameterMap extends _AbstractAttributeMap<String>
+{
+ private final ServletContext _servletContext;
+
+ _InitParameterMap(final ServletContext servletContext)
+ {
+ _servletContext = servletContext;
+ }
+
+ @Override
+ protected String getAttribute(final String key)
+ {
+ return _servletContext.getInitParameter(key);
+ }
+
+ @Override
+ protected void setAttribute(final String key, final String value)
+ {
+ throw new UnsupportedOperationException(
+ "Cannot set ServletContext InitParameter");
+ }
+
+ @Override
+ protected void removeAttribute(final String key)
+ {
+ throw new UnsupportedOperationException(
+ "Cannot remove ServletContext InitParameter");
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ protected Enumeration<String> getAttributeNames()
+ {
+ return _servletContext.getInitParameterNames();
+ }
+
+ @Override
+ public void putAll(final Map<? extends String, ? extends String> t)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void clear()
+ {
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/mock/_NullEnumeration.java b/test30/src/main/java/org/apache/myfaces/test/mock/_NullEnumeration.java
new file mode 100644
index 0000000..4ff90b5
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/mock/_NullEnumeration.java
@@ -0,0 +1,49 @@
+/*
+ * 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.myfaces.test.mock;
+
+import java.util.Enumeration;
+import java.util.NoSuchElementException;
+
+/**
+ * Enumeration without elements
+ *
+ * @author Anton Koinov (latest modification by $Author: matzew $)
+ * @version $Revision: 557350 $ $Date: 2007-07-18 13:19:50 -0500 (Mié, 18 Jul 2007) $
+ * @since 1.0.0
+ */
+final class _NullEnumeration implements Enumeration
+{
+ private static final _NullEnumeration NULL_ENUMERATION = new _NullEnumeration();
+
+ public static final _NullEnumeration instance()
+ {
+ return NULL_ENUMERATION;
+ }
+
+ public boolean hasMoreElements()
+ {
+ return false;
+ }
+
+ public Object nextElement()
+ {
+ throw new NoSuchElementException("NullEnumeration has no elements");
+ }
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/mock/_RequestHeaderMap.java b/test30/src/main/java/org/apache/myfaces/test/mock/_RequestHeaderMap.java
new file mode 100644
index 0000000..90466c1
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/mock/_RequestHeaderMap.java
@@ -0,0 +1,80 @@
+/*
+ * 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.myfaces.test.mock;
+
+import java.util.Enumeration;
+import java.util.Map;
+
+import javax.servlet.http.HttpServletRequest;
+
+/**
+ * HttpServletRequest headers as Map.
+ *
+ * @author Anton Koinov (latest modification by $Author: slessard $)
+ * @version $Revision: 698799 $ $Date: 2008-09-25 04:03:47 +0200 (Do, 25 Sep 2008) $
+ * @since 1.0.0
+ */
+final class _RequestHeaderMap extends _AbstractAttributeMap<String>
+{
+ private final HttpServletRequest _httpServletRequest;
+
+ _RequestHeaderMap(final HttpServletRequest httpServletRequest)
+ {
+ _httpServletRequest = httpServletRequest;
+ }
+
+ @Override
+ protected String getAttribute(final String key)
+ {
+ return _httpServletRequest.getHeader(key);
+ }
+
+ @Override
+ protected void setAttribute(final String key, final String value)
+ {
+ throw new UnsupportedOperationException(
+ "Cannot set HttpServletRequest Header");
+ }
+
+ @Override
+ protected void removeAttribute(final String key)
+ {
+ throw new UnsupportedOperationException(
+ "Cannot remove HttpServletRequest Header");
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ protected Enumeration<String> getAttributeNames()
+ {
+ return _httpServletRequest.getHeaderNames();
+ }
+
+ @Override
+ public void putAll(final Map<? extends String, ? extends String> t)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void clear()
+ {
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/mock/_RequestHeaderValuesMap.java b/test30/src/main/java/org/apache/myfaces/test/mock/_RequestHeaderValuesMap.java
new file mode 100644
index 0000000..2a60140
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/mock/_RequestHeaderValuesMap.java
@@ -0,0 +1,93 @@
+/*
+ * 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.myfaces.test.mock;
+
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.servlet.http.HttpServletRequest;
+
+/**
+ * HttpServletRequest header values (multi-value headers) as Map of String[].
+ *
+ * @author Anton Koinov (latest modification by $Author: slessard $)
+ * @version $Revision: 701829 $ $Date: 2008-10-05 19:06:02 +0200 (So, 05 Okt 2008) $
+ * @since 1.0.0
+ */
+final class _RequestHeaderValuesMap extends _AbstractAttributeMap<String[]>
+{
+ private final HttpServletRequest _httpServletRequest;
+ private final Map<String, String[]> _valueCache = new HashMap<String, String[]>();
+
+ _RequestHeaderValuesMap(final HttpServletRequest httpServletRequest)
+ {
+ _httpServletRequest = httpServletRequest;
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ protected String[] getAttribute(final String key)
+ {
+ String[] ret = _valueCache.get(key);
+ if (ret == null)
+ {
+ ret = toArray(_httpServletRequest
+ .getHeaders(key));
+ _valueCache.put(key, ret);
+ }
+
+ return ret;
+ }
+
+ @Override
+ protected void setAttribute(final String key, final String[] value)
+ {
+ throw new UnsupportedOperationException(
+ "Cannot set HttpServletRequest HeaderValues");
+ }
+
+ @Override
+ protected void removeAttribute(final String key)
+ {
+ throw new UnsupportedOperationException(
+ "Cannot remove HttpServletRequest HeaderValues");
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ protected Enumeration<String> getAttributeNames()
+ {
+ return _httpServletRequest.getHeaderNames();
+ }
+
+ private String[] toArray(Enumeration<String> e)
+ {
+ List<String> ret = new ArrayList<String>();
+
+ while (e.hasMoreElements())
+ {
+ ret.add(e.nextElement());
+ }
+
+ return ret.toArray(new String[ret.size()]);
+ }
+}
\ No newline at end of file
diff --git a/test30/src/main/java/org/apache/myfaces/test/mock/_RequestMap.java b/test30/src/main/java/org/apache/myfaces/test/mock/_RequestMap.java
new file mode 100644
index 0000000..366b689
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/mock/_RequestMap.java
@@ -0,0 +1,78 @@
+/*
+ * 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.myfaces.test.mock;
+
+import java.util.Enumeration;
+import java.util.Map;
+
+import javax.servlet.ServletRequest;
+
+/**
+ * ServletRequest attributes Map.
+ *
+ * @author Anton Koinov (latest modification by $Author: slessard $)
+ * @version $Revision: 698799 $ $Date: 2008-09-25 04:03:47 +0200 (Do, 25 Sep 2008) $
+ * @since 1.0.0
+ */
+final class _RequestMap extends _AbstractAttributeMap<Object>
+{
+ final ServletRequest _servletRequest;
+
+ _RequestMap(final ServletRequest servletRequest)
+ {
+ _servletRequest = servletRequest;
+ }
+
+ @Override
+ protected Object getAttribute(final String key)
+ {
+ return _servletRequest.getAttribute(key);
+ }
+
+ @Override
+ protected void setAttribute(final String key, final Object value)
+ {
+ _servletRequest.setAttribute(key, value);
+ }
+
+ @Override
+ protected void removeAttribute(final String key)
+ {
+ _servletRequest.removeAttribute(key);
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ protected Enumeration<String> getAttributeNames()
+ {
+ return _servletRequest.getAttributeNames();
+ }
+
+ @Override
+ public void putAll(final Map<? extends String, ? extends Object> t)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void clear()
+ {
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/mock/_RequestParameterMap.java b/test30/src/main/java/org/apache/myfaces/test/mock/_RequestParameterMap.java
new file mode 100644
index 0000000..2eecb47
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/mock/_RequestParameterMap.java
@@ -0,0 +1,67 @@
+/*
+ * 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.myfaces.test.mock;
+
+import java.util.Enumeration;
+
+import javax.servlet.ServletRequest;
+
+/**
+ * ServletRequest parameters as Map.
+ *
+ * @author Anton Koinov (latest modification by $Author: lu4242 $)
+ * @version $Revision: 695059 $ $Date: 2008-09-14 01:10:53 +0200 (So, 14 Sep 2008) $
+ * @since 1.0.0
+ */
+final class _RequestParameterMap extends _AbstractAttributeMap<String>
+{
+ private final ServletRequest _servletRequest;
+
+ _RequestParameterMap(final ServletRequest servletRequest)
+ {
+ _servletRequest = servletRequest;
+ }
+
+ @Override
+ protected String getAttribute(final String key)
+ {
+ return _servletRequest.getParameter(key);
+ }
+
+ @Override
+ protected void setAttribute(final String key, final String value)
+ {
+ throw new UnsupportedOperationException(
+ "Cannot set ServletRequest Parameter");
+ }
+
+ @Override
+ protected void removeAttribute(final String key)
+ {
+ throw new UnsupportedOperationException(
+ "Cannot remove ServletRequest Parameter");
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ protected Enumeration<String> getAttributeNames()
+ {
+ return _servletRequest.getParameterNames();
+ }
+}
\ No newline at end of file
diff --git a/test30/src/main/java/org/apache/myfaces/test/mock/_RequestParameterValuesMap.java b/test30/src/main/java/org/apache/myfaces/test/mock/_RequestParameterValuesMap.java
new file mode 100644
index 0000000..fa34704
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/mock/_RequestParameterValuesMap.java
@@ -0,0 +1,67 @@
+/*
+ * 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.myfaces.test.mock;
+
+import java.util.Enumeration;
+
+import javax.servlet.ServletRequest;
+
+/**
+ * ServletRequest multi-value parameters as Map.
+ *
+ * @author Anton Koinov (latest modification by $Author: lu4242 $)
+ * @version $Revision: 695059 $ $Date: 2008-09-14 01:10:53 +0200 (So, 14 Sep 2008) $
+ * @since 1.0.0
+ */
+final class _RequestParameterValuesMap extends _AbstractAttributeMap<String[]>
+{
+ private final ServletRequest _servletRequest;
+
+ _RequestParameterValuesMap(final ServletRequest servletRequest)
+ {
+ _servletRequest = servletRequest;
+ }
+
+ @Override
+ protected String[] getAttribute(final String key)
+ {
+ return _servletRequest.getParameterValues(key);
+ }
+
+ @Override
+ protected void setAttribute(final String key, final String[] value)
+ {
+ throw new UnsupportedOperationException(
+ "Cannot set ServletRequest ParameterValues");
+ }
+
+ @Override
+ protected void removeAttribute(final String key)
+ {
+ throw new UnsupportedOperationException(
+ "Cannot remove ServletRequest ParameterValues");
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ protected Enumeration<String> getAttributeNames()
+ {
+ return _servletRequest.getParameterNames();
+ }
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/mock/_SessionMap.java b/test30/src/main/java/org/apache/myfaces/test/mock/_SessionMap.java
new file mode 100644
index 0000000..64f2f47
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/mock/_SessionMap.java
@@ -0,0 +1,88 @@
+/*
+ * 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.myfaces.test.mock;
+
+import java.util.Enumeration;
+import java.util.Map;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpSession;
+
+/**
+ * HttpSession attibutes as Map.
+ *
+ * @author Anton Koinov (latest modification by $Author: jakobk $)
+ * @version $Revision: 979229 $ $Date: 2010-07-26 12:26:53 +0200 (Mo, 26 Jul 2010) $
+ * @since 1.0.0
+ */
+final class _SessionMap extends _AbstractAttributeMap<Object>
+{
+ private final HttpServletRequest _httpRequest;
+
+ _SessionMap(final HttpServletRequest httpRequest)
+ {
+ _httpRequest = httpRequest;
+ }
+
+ @Override
+ protected Object getAttribute(final String key)
+ {
+ final HttpSession httpSession = _getSession();
+ return (httpSession == null) ? null : httpSession.getAttribute(key);
+ }
+
+ @Override
+ protected void setAttribute(final String key, final Object value)
+ {
+ _httpRequest.getSession(true).setAttribute(key, value);
+ }
+
+ @Override
+ protected void removeAttribute(final String key)
+ {
+ final HttpSession httpSession = _getSession();
+ if (httpSession != null)
+ {
+ httpSession.removeAttribute(key);
+ }
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ protected Enumeration<String> getAttributeNames()
+ {
+ final HttpSession httpSession = _getSession();
+ return (httpSession == null) ? _NullEnumeration.instance()
+ : httpSession.getAttributeNames();
+ }
+
+ @Override
+ public void putAll(final Map<? extends String, ? extends Object> t)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ // we can use public void clear() from super-class
+
+ private HttpSession _getSession()
+ {
+ return _httpRequest.getSession(false);
+ }
+
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/mock/lifecycle/ApplyRequestValuesExecutor.java b/test30/src/main/java/org/apache/myfaces/test/mock/lifecycle/ApplyRequestValuesExecutor.java
new file mode 100644
index 0000000..ae803ea
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/mock/lifecycle/ApplyRequestValuesExecutor.java
@@ -0,0 +1,45 @@
+/*
+ * 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.myfaces.test.mock.lifecycle;
+
+import javax.faces.context.FacesContext;
+import javax.faces.event.PhaseId;
+
+/**
+ * Implements the lifecycle as described in Spec. 1.0 PFD Chapter 2
+ *
+ * Apply request values phase (JSF Spec 2.2.2)
+ *
+ * @author Nikolay Petrov
+ * @since 1.0.0
+ */
+class ApplyRequestValuesExecutor implements PhaseExecutor
+{
+ public boolean execute(FacesContext facesContext)
+ {
+ facesContext.getViewRoot().processDecodes(facesContext);
+ return false;
+ }
+
+ public PhaseId getPhase()
+ {
+ return PhaseId.APPLY_REQUEST_VALUES;
+ }
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/mock/lifecycle/DefaultRestoreViewSupport.java b/test30/src/main/java/org/apache/myfaces/test/mock/lifecycle/DefaultRestoreViewSupport.java
new file mode 100644
index 0000000..5158189
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/mock/lifecycle/DefaultRestoreViewSupport.java
@@ -0,0 +1,124 @@
+/*
+ * 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.myfaces.test.mock.lifecycle;
+
+import java.util.Iterator;
+import java.util.Map;
+
+import javax.el.ValueExpression;
+import javax.faces.FacesException;
+import javax.faces.component.UIComponent;
+import javax.faces.context.ExternalContext;
+import javax.faces.context.FacesContext;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * @author Mathias Broekelmann (latest modification by $Author: mbr $)
+ * @version $Revision: 517403 $ $Date: 2007-03-12 22:17:00 +0100 (Mo, 12 Mrz 2007) $
+ * @since 1.0.0
+ */
+public class DefaultRestoreViewSupport implements RestoreViewSupport
+{
+ private static final String JAVAX_SERVLET_INCLUDE_SERVLET_PATH = "javax.servlet.include.servlet_path";
+
+ private static final String JAVAX_SERVLET_INCLUDE_PATH_INFO = "javax.servlet.include.path_info";
+
+ private final Log log = LogFactory.getLog(DefaultRestoreViewSupport.class);
+
+ public void processComponentBinding(FacesContext facesContext,
+ UIComponent component)
+ {
+ ValueExpression binding = component.getValueExpression("binding");
+ if (binding != null)
+ {
+ binding.setValue(facesContext.getELContext(), component);
+ }
+
+ for (Iterator iter = component.getFacetsAndChildren(); iter.hasNext();)
+ {
+ processComponentBinding(facesContext, (UIComponent) iter.next());
+ }
+ }
+
+ public String calculateViewId(FacesContext facesContext)
+ {
+ //Assert.notNull(facesContext);
+ ExternalContext externalContext = facesContext.getExternalContext();
+ Map requestMap = externalContext.getRequestMap();
+
+ String viewId = (String) requestMap
+ .get(JAVAX_SERVLET_INCLUDE_PATH_INFO);
+ boolean traceEnabled = log.isTraceEnabled();
+ if (viewId != null)
+ {
+ if (traceEnabled)
+ {
+ log.trace("Calculated viewId '" + viewId
+ + "' from request param '"
+ + JAVAX_SERVLET_INCLUDE_PATH_INFO + "'");
+ }
+ }
+ else
+ {
+ viewId = externalContext.getRequestPathInfo();
+ if (viewId != null && traceEnabled)
+ {
+ log.trace("Calculated viewId '" + viewId
+ + "' from request path info");
+ }
+ }
+
+ if (viewId == null)
+ {
+ viewId = (String) requestMap
+ .get(JAVAX_SERVLET_INCLUDE_SERVLET_PATH);
+ if (viewId != null && traceEnabled)
+ {
+ log.trace("Calculated viewId '" + viewId
+ + "' from request param '"
+ + JAVAX_SERVLET_INCLUDE_SERVLET_PATH + "'");
+ }
+ }
+
+ if (viewId == null)
+ {
+ viewId = externalContext.getRequestServletPath();
+ if (viewId != null && traceEnabled)
+ {
+ log.trace("Calculated viewId '" + viewId
+ + "' from request servlet path");
+ }
+ }
+
+ if (viewId == null)
+ {
+ throw new FacesException("Could not determine view id.");
+ }
+
+ return viewId;
+ }
+
+ public boolean isPostback(FacesContext facesContext)
+ {
+ return true;
+ }
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/mock/lifecycle/InvokeApplicationExecutor.java b/test30/src/main/java/org/apache/myfaces/test/mock/lifecycle/InvokeApplicationExecutor.java
new file mode 100644
index 0000000..d0d7cfd
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/mock/lifecycle/InvokeApplicationExecutor.java
@@ -0,0 +1,45 @@
+/*
+ * 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.myfaces.test.mock.lifecycle;
+
+import javax.faces.context.FacesContext;
+import javax.faces.event.PhaseId;
+
+/**
+ * Implements the lifecycle as described in Spec. 1.0 PFD Chapter 2
+ *
+ * Invoke application phase (JSF Spec 2.2.5)
+ *
+ * @author Nikolay Petrov
+ * @since 1.0.0
+ */
+class InvokeApplicationExecutor implements PhaseExecutor
+{
+ public boolean execute(FacesContext facesContext)
+ {
+ facesContext.getViewRoot().processApplication(facesContext);
+ return false;
+ }
+
+ public PhaseId getPhase()
+ {
+ return PhaseId.INVOKE_APPLICATION;
+ }
+}
\ No newline at end of file
diff --git a/test30/src/main/java/org/apache/myfaces/test/mock/lifecycle/MockLifecycle.java b/test30/src/main/java/org/apache/myfaces/test/mock/lifecycle/MockLifecycle.java
new file mode 100644
index 0000000..de199c8
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/mock/lifecycle/MockLifecycle.java
@@ -0,0 +1,201 @@
+/*
+ * 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.myfaces.test.mock.lifecycle;
+
+import java.util.ArrayList;
+import java.util.List;
+import javax.faces.FacesException;
+import javax.faces.context.FacesContext;
+import javax.faces.event.PhaseId;
+import javax.faces.event.PhaseListener;
+import javax.faces.lifecycle.Lifecycle;
+
+/**
+ * <p>Mock implementation of <code>Lifecycle</code>.</p>
+ *
+ * $Id$
+ * @since 1.0.0
+ */
+
+public class MockLifecycle extends Lifecycle
+{
+
+ // ----------------------------------------------------- Mock Object Methods
+
+ // ------------------------------------------------------ Instance Variables
+
+ /**
+ * <p>List of event listeners for this instance.</p>
+ */
+ private List phaseListenerList = new ArrayList();
+ private PhaseExecutor[] lifecycleExecutors;
+ private PhaseExecutor renderExecutor;
+
+ public MockLifecycle()
+ {
+ lifecycleExecutors = new PhaseExecutor[] { new RestoreViewExecutor(),
+ new ApplyRequestValuesExecutor(),
+ new ProcessValidationsExecutor(),
+ new UpdateModelValuesExecutor(),
+ new InvokeApplicationExecutor() };
+
+ renderExecutor = new RenderResponseExecutor();
+ }
+
+ // ------------------------------------------------------- Lifecycle Methods
+
+ /** {@inheritDoc} */
+ public void addPhaseListener(PhaseListener listener)
+ {
+
+ phaseListenerList.add(listener);
+
+ }
+
+ /** {@inheritDoc} */
+ public void execute(FacesContext context) throws FacesException
+ {
+
+ PhaseListenerManager phaseListenerMgr = new PhaseListenerManager(this,
+ context, getPhaseListeners());
+ for (int executorIndex = 0; executorIndex < lifecycleExecutors.length; executorIndex++)
+ {
+ if (executePhase(context, lifecycleExecutors[executorIndex],
+ phaseListenerMgr))
+ {
+ return;
+ }
+ }
+ }
+
+ private boolean executePhase(FacesContext facesContext,
+ PhaseExecutor executor, PhaseListenerManager phaseListenerMgr)
+ throws FacesException
+ {
+ boolean skipFurtherProcessing = false;
+
+ try
+ {
+ phaseListenerMgr.informPhaseListenersBefore(executor.getPhase());
+
+ if (isResponseComplete(facesContext, executor.getPhase(), true))
+ {
+ // have to return right away
+ return true;
+ }
+ if (shouldRenderResponse(facesContext, executor.getPhase(), true))
+ {
+ skipFurtherProcessing = true;
+ }
+
+ if (executor.execute(facesContext))
+ {
+ return true;
+ }
+ }
+ finally
+ {
+ phaseListenerMgr.informPhaseListenersAfter(executor.getPhase());
+ }
+
+ if (isResponseComplete(facesContext, executor.getPhase(), false)
+ || shouldRenderResponse(facesContext, executor.getPhase(),
+ false))
+ {
+ // since this phase is completed we don't need to return right away even if the response is completed
+ skipFurtherProcessing = true;
+ }
+
+ return skipFurtherProcessing;
+ }
+
+ /** {@inheritDoc} */
+ public PhaseListener[] getPhaseListeners()
+ {
+
+ return (PhaseListener[]) phaseListenerList
+ .toArray(new PhaseListener[phaseListenerList.size()]);
+
+ }
+
+ /** {@inheritDoc} */
+ public void removePhaseListener(PhaseListener listener)
+ {
+
+ phaseListenerList.remove(listener);
+
+ }
+
+ /** {@inheritDoc} */
+ public void render(FacesContext context) throws FacesException
+ {
+
+ // if the response is complete we should not be invoking the phase listeners
+ if (isResponseComplete(context, renderExecutor.getPhase(), true))
+ {
+ return;
+ }
+
+ PhaseListenerManager phaseListenerMgr = new PhaseListenerManager(this,
+ context, getPhaseListeners());
+
+ try
+ {
+ phaseListenerMgr.informPhaseListenersBefore(renderExecutor
+ .getPhase());
+ // also possible that one of the listeners completed the response
+ if (isResponseComplete(context, renderExecutor.getPhase(), true))
+ {
+ return;
+ }
+
+ renderExecutor.execute(context);
+ }
+ finally
+ {
+ phaseListenerMgr.informPhaseListenersAfter(renderExecutor
+ .getPhase());
+ }
+
+ }
+
+ private boolean isResponseComplete(FacesContext facesContext,
+ PhaseId phase, boolean before)
+ {
+ boolean flag = false;
+ if (facesContext.getResponseComplete())
+ {
+ flag = true;
+ }
+ return flag;
+ }
+
+ private boolean shouldRenderResponse(FacesContext facesContext,
+ PhaseId phase, boolean before)
+ {
+ boolean flag = false;
+ if (facesContext.getRenderResponse())
+ {
+ flag = true;
+ }
+ return flag;
+ }
+
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/mock/lifecycle/MockLifecycleFactory.java b/test30/src/main/java/org/apache/myfaces/test/mock/lifecycle/MockLifecycleFactory.java
new file mode 100644
index 0000000..24ca920
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/mock/lifecycle/MockLifecycleFactory.java
@@ -0,0 +1,87 @@
+/*
+ * 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.myfaces.test.mock.lifecycle;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import javax.faces.lifecycle.Lifecycle;
+import javax.faces.lifecycle.LifecycleFactory;
+
+/**
+ * <p>Mock implementation of <code>LifecycleFactory</code>.</p>
+ *
+ * $Id$
+ * @since 1.0.0
+ */
+
+public class MockLifecycleFactory extends LifecycleFactory
+{
+
+ // ------------------------------------------------------------ Constructors
+
+ /**
+ * <p>Return a default instance.</p>
+ */
+ public MockLifecycleFactory()
+ {
+
+ lifecycles = new HashMap();
+ lifecycles.put(LifecycleFactory.DEFAULT_LIFECYCLE, new MockLifecycle());
+
+ }
+
+ // ----------------------------------------------------- Mock Object Methods
+
+ // ------------------------------------------------------ Instance Variables
+
+ /**
+ * <p>The set of Lifecycle instances registered with us.</p>
+ */
+ private Map lifecycles = null;
+
+ // ------------------------------------------------ LifecycleFactory Methods
+
+ /** {@inheritDoc} */
+ public void addLifecycle(String lifecycleId, Lifecycle lifecycle)
+ {
+
+ lifecycles.put(lifecycleId, lifecycle);
+
+ }
+
+ /** {@inheritDoc} */
+ public Lifecycle getLifecycle(String lifecycleId)
+ {
+
+ return (Lifecycle) lifecycles.get(lifecycleId);
+
+ }
+
+ /** {@inheritDoc} */
+ public Iterator getLifecycleIds()
+ {
+
+ return lifecycles.keySet().iterator();
+
+ }
+
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/mock/lifecycle/PhaseExecutor.java b/test30/src/main/java/org/apache/myfaces/test/mock/lifecycle/PhaseExecutor.java
new file mode 100644
index 0000000..54b7402
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/mock/lifecycle/PhaseExecutor.java
@@ -0,0 +1,49 @@
+/*
+ * 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.myfaces.test.mock.lifecycle;
+
+import javax.faces.context.FacesContext;
+import javax.faces.event.PhaseId;
+
+/**
+ * Implements the PhaseExecutor for a lifecycle
+ *
+ * @author Nikolay Petrov
+ * @since 1.0.0
+ *
+ */
+interface PhaseExecutor
+{
+
+ /**
+ * Executes a phase of the JavaServer(tm) Faces lifecycle, like UpdateModelValues.
+ * The <code>execute</code> method is called by the lifecylce implementation's private
+ * <code>executePhase</code>.
+ * @param facesContext The <code>FacesContext</code> for the current request we are processing
+ * @return <code>true</code> if execution should be stopped
+ */
+ boolean execute(FacesContext facesContext);
+
+ /**
+ * Returns the <code>PhaseId</code> for which the implemented executor is invoked
+ * @return
+ */
+ PhaseId getPhase();
+}
\ No newline at end of file
diff --git a/test30/src/main/java/org/apache/myfaces/test/mock/lifecycle/PhaseListenerManager.java b/test30/src/main/java/org/apache/myfaces/test/mock/lifecycle/PhaseListenerManager.java
new file mode 100644
index 0000000..ca3a2b0
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/mock/lifecycle/PhaseListenerManager.java
@@ -0,0 +1,126 @@
+/*
+ * 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.myfaces.test.mock.lifecycle;
+
+import java.util.HashMap;
+import java.util.Map;
+import javax.faces.context.FacesContext;
+import javax.faces.event.PhaseEvent;
+import javax.faces.event.PhaseId;
+import javax.faces.event.PhaseListener;
+import javax.faces.lifecycle.Lifecycle;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * This class encapsulates the logic used to call PhaseListeners. It was
+ * needed because of issue 9 of the JSF 1.2 spec. See section 11.3 for more
+ * details.
+ *
+ * @author Stan Silvert
+ * @since 1.0.0
+ */
+class PhaseListenerManager
+{
+
+ private static final Log log = LogFactory
+ .getLog(PhaseListenerManager.class);
+
+ private Lifecycle lifecycle;
+ private FacesContext facesContext;
+ private PhaseListener[] phaseListeners;
+
+ // Tracks success in the beforePhase. Listeners that throw an exception
+ // in beforePhase or were never called because a previous listener threw
+ // an exception should not have its afterPhase called
+ //private Map<PhaseId, boolean[]> listenerSuccessMap = new HashMap<PhaseId, boolean[]>();
+ private Map listenerSuccessMap = new HashMap();
+
+ /** Creates a new instance of PhaseListenerManager */
+ PhaseListenerManager(Lifecycle lifecycle, FacesContext facesContext,
+ PhaseListener[] phaseListeners)
+ {
+ this.lifecycle = lifecycle;
+ this.facesContext = facesContext;
+ this.phaseListeners = phaseListeners;
+ }
+
+ private boolean isListenerForThisPhase(PhaseListener phaseListener,
+ PhaseId phaseId)
+ {
+ int listenerPhaseId = phaseListener.getPhaseId().getOrdinal();
+ return (listenerPhaseId == PhaseId.ANY_PHASE.getOrdinal() || listenerPhaseId == phaseId
+ .getOrdinal());
+ }
+
+ void informPhaseListenersBefore(PhaseId phaseId)
+ {
+ boolean[] beforePhaseSuccess = new boolean[phaseListeners.length];
+ listenerSuccessMap.put(phaseId, beforePhaseSuccess);
+
+ for (int i = 0; i < phaseListeners.length; i++)
+ {
+ PhaseListener phaseListener = phaseListeners[i];
+ if (isListenerForThisPhase(phaseListener, phaseId))
+ {
+ try
+ {
+ phaseListener.beforePhase(new PhaseEvent(facesContext,
+ phaseId, lifecycle));
+ beforePhaseSuccess[i] = true;
+ }
+ catch (Exception e)
+ {
+ beforePhaseSuccess[i] = false; // redundant - for clarity
+ log.error("Exception in PhaseListener "
+ + phaseId.toString() + " beforePhase.", e);
+ return;
+ }
+ }
+ }
+ }
+
+ void informPhaseListenersAfter(PhaseId phaseId)
+ {
+ //boolean[] beforePhaseSuccess = listenerSuccessMap.get(phaseId);
+ boolean[] beforePhaseSuccess = (boolean[]) listenerSuccessMap
+ .get(phaseId);
+
+ for (int i = phaseListeners.length - 1; i >= 0; i--)
+ {
+ PhaseListener phaseListener = phaseListeners[i];
+ if (isListenerForThisPhase(phaseListener, phaseId)
+ && beforePhaseSuccess[i])
+ {
+ try
+ {
+ phaseListener.afterPhase(new PhaseEvent(facesContext,
+ phaseId, lifecycle));
+ }
+ catch (Exception e)
+ {
+ log.error("Exception in PhaseListener "
+ + phaseId.toString() + " afterPhase", e);
+ }
+ }
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/test30/src/main/java/org/apache/myfaces/test/mock/lifecycle/ProcessValidationsExecutor.java b/test30/src/main/java/org/apache/myfaces/test/mock/lifecycle/ProcessValidationsExecutor.java
new file mode 100644
index 0000000..8f5ab67
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/mock/lifecycle/ProcessValidationsExecutor.java
@@ -0,0 +1,43 @@
+/*
+ * 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.myfaces.test.mock.lifecycle;
+
+import javax.faces.context.FacesContext;
+import javax.faces.event.PhaseId;
+
+/**
+ * Implements the lifecycle as described in Spec. 1.0 PFD Chapter 2
+ *
+ * Process validations phase (JSF Spec 2.2.3)
+ * @author Nikolay Petrov
+ * @since 1.0.0
+ */
+class ProcessValidationsExecutor implements PhaseExecutor
+{
+ public boolean execute(FacesContext facesContext)
+ {
+ facesContext.getViewRoot().processValidators(facesContext);
+ return false;
+ }
+
+ public PhaseId getPhase()
+ {
+ return PhaseId.PROCESS_VALIDATIONS;
+ }
+}
\ No newline at end of file
diff --git a/test30/src/main/java/org/apache/myfaces/test/mock/lifecycle/RenderResponseExecutor.java b/test30/src/main/java/org/apache/myfaces/test/mock/lifecycle/RenderResponseExecutor.java
new file mode 100644
index 0000000..6d6ed33
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/mock/lifecycle/RenderResponseExecutor.java
@@ -0,0 +1,60 @@
+/*
+ * 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.myfaces.test.mock.lifecycle;
+
+import java.io.IOException;
+
+import javax.faces.FacesException;
+import javax.faces.application.Application;
+import javax.faces.application.ViewHandler;
+import javax.faces.context.FacesContext;
+import javax.faces.event.PhaseId;
+
+/**
+ * Implements the lifecycle as described in Spec. 1.0 PFD Chapter 2
+ *
+ * render response phase (JSF Spec 2.2.6)
+ *
+ * @author Nikolay Petrov
+ * @since 1.0.0
+ */
+class RenderResponseExecutor implements PhaseExecutor
+{
+ public boolean execute(FacesContext facesContext)
+ {
+ Application application = facesContext.getApplication();
+ ViewHandler viewHandler = application.getViewHandler();
+
+ try
+ {
+ viewHandler.renderView(facesContext, facesContext.getViewRoot());
+ }
+ catch (IOException e)
+ {
+ throw new FacesException(e.getMessage(), e);
+ }
+ return false;
+ }
+
+ public PhaseId getPhase()
+ {
+ return PhaseId.RENDER_RESPONSE;
+ }
+}
\ No newline at end of file
diff --git a/test30/src/main/java/org/apache/myfaces/test/mock/lifecycle/RestoreViewExecutor.java b/test30/src/main/java/org/apache/myfaces/test/mock/lifecycle/RestoreViewExecutor.java
new file mode 100644
index 0000000..4e92def
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/mock/lifecycle/RestoreViewExecutor.java
@@ -0,0 +1,199 @@
+/*
+ * 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.myfaces.test.mock.lifecycle;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.myfaces.test.util.Jsf11Utils;
+import org.apache.myfaces.test.util.Jsf12Utils;
+import org.apache.myfaces.test.util.JsfVersion;
+
+import javax.faces.FacesException;
+import javax.faces.application.Application;
+import javax.faces.application.ViewHandler;
+import javax.faces.component.UIViewRoot;
+import javax.faces.context.ExternalContext;
+import javax.faces.context.FacesContext;
+import javax.faces.event.PhaseId;
+
+/**
+ * Implements the Restore View Phase (JSF Spec 2.2.1)
+ *
+ * @author Nikolay Petrov
+ * @author Bruno Aranda (JSF 1.2)
+ * @version $Revision: 517403 $ $Date: 2007-03-12 22:17:00 +0100 (Mo, 12 Mrz 2007) $
+ * @since 1.0.0
+ */
+class RestoreViewExecutor implements PhaseExecutor
+{
+
+ private static final Log log = LogFactory.getLog(RestoreViewExecutor.class);
+ private RestoreViewSupport _restoreViewSupport;
+
+ public boolean execute(FacesContext facesContext)
+ {
+ if (facesContext == null)
+ {
+ throw new FacesException("FacesContext is null");
+ }
+
+ // init the View
+ Application application = facesContext.getApplication();
+ ViewHandler viewHandler = application.getViewHandler();
+ if (JsfVersion.supports12())
+ {
+ Jsf12Utils.initView(facesContext, viewHandler);
+ }
+ else
+ {
+ // nothing to do
+ }
+
+ UIViewRoot viewRoot = facesContext.getViewRoot();
+
+ RestoreViewSupport restoreViewSupport = getRestoreViewSupport();
+
+ if (viewRoot != null)
+ {
+ if (log.isTraceEnabled())
+ {
+ log.trace("View already exists in the FacesContext");
+ }
+
+ viewRoot.setLocale(facesContext.getExternalContext()
+ .getRequestLocale());
+ restoreViewSupport.processComponentBinding(facesContext, viewRoot);
+ return false;
+ }
+
+ String viewId = restoreViewSupport.calculateViewId(facesContext);
+
+ // Determine if this request is a postback or initial request
+ if (restoreViewSupport.isPostback(facesContext))
+ {
+ if (log.isTraceEnabled())
+ {
+ log.trace("Request is a postback");
+ }
+
+ viewRoot = viewHandler.restoreView(facesContext, viewId);
+ if (viewRoot == null)
+ {
+ if (JsfVersion.supports12())
+ {
+ Jsf12Utils.throwViewExpiredException(viewId);
+ }
+ else
+ {
+ Jsf11Utils.throwViewExpiredException(viewId);
+ }
+ }
+ restoreViewSupport.processComponentBinding(facesContext, viewRoot);
+ }
+ else
+ {
+ if (log.isTraceEnabled())
+ {
+ log
+ .trace("Request is not a postback. New UIViewRoot will be created");
+ }
+
+ viewRoot = viewHandler.createView(facesContext, viewId);
+ facesContext.renderResponse();
+ }
+
+ facesContext.setViewRoot(viewRoot);
+
+ return false;
+ }
+
+ protected RestoreViewSupport getRestoreViewSupport()
+ {
+ if (_restoreViewSupport == null)
+ {
+ _restoreViewSupport = new DefaultRestoreViewSupport();
+ }
+ return _restoreViewSupport;
+ }
+
+ /**
+ * @param restoreViewSupport
+ * the restoreViewSupport to set
+ */
+ public void setRestoreViewSupport(RestoreViewSupport restoreViewSupport)
+ {
+ _restoreViewSupport = restoreViewSupport;
+ }
+
+ public PhaseId getPhase()
+ {
+ return PhaseId.RESTORE_VIEW;
+ }
+
+ /**
+ * TODO place that stuff into the default view handler implementation.
+ */
+ private static String deriveViewId(FacesContext facesContext)
+ {
+ ExternalContext externalContext = facesContext.getExternalContext();
+
+ // if (PortletUtil.isPortletRequest(facesContext))
+ // {
+ // PortletRequest request = (PortletRequest) externalContext.getRequest();
+ // return request.getParameter(MyFacesGenericPortlet.VIEW_ID);
+ // }
+ //
+ String viewId = externalContext.getRequestPathInfo(); // getPathInfo
+ if (viewId == null)
+ {
+ // No extra path info found, so it is propably extension mapping
+ viewId = externalContext.getRequestServletPath(); // getServletPath
+ // DebugUtils.assertError(viewId != null, log,
+ // "RequestServletPath is null, cannot determine viewId of current page.");
+ if (viewId == null)
+ {
+ return null;
+ }
+
+ // TODO: JSF Spec 2.2.1 - what do they mean by "if the default
+ // ViewHandler implementation is used..." ?
+ String defaultSuffix = externalContext
+ .getInitParameter(ViewHandler.DEFAULT_SUFFIX_PARAM_NAME);
+ String suffix = defaultSuffix != null ? defaultSuffix
+ : ViewHandler.DEFAULT_SUFFIX;
+ // DebugUtils.assertError(suffix.charAt(0) == '.', log, "Default suffix must start with a dot!");
+
+ int dot = viewId.lastIndexOf('.');
+ if (dot == -1)
+ {
+ log
+ .error("Assumed extension mapping, but there is no extension in "
+ + viewId);
+ viewId = null;
+ }
+ else
+ {
+ viewId = viewId.substring(0, dot) + suffix;
+ }
+ }
+
+ return viewId;
+ }
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/mock/lifecycle/RestoreViewSupport.java b/test30/src/main/java/org/apache/myfaces/test/mock/lifecycle/RestoreViewSupport.java
new file mode 100644
index 0000000..d750a33
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/mock/lifecycle/RestoreViewSupport.java
@@ -0,0 +1,74 @@
+/*
+ * 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.myfaces.test.mock.lifecycle;
+
+import javax.faces.component.UIComponent;
+import javax.faces.context.FacesContext;
+
+/**
+ * Support class for restore view phase
+ *
+ * @author Mathias Broekelmann (latest modification by $Author: mbr $)
+ * @version $Revision: 517403 $ $Date: 2007-03-12 22:17:00 +0100 (Mo, 12 Mrz 2007) $
+ * @since 1.0.0
+ */
+public interface RestoreViewSupport
+{
+ /**
+ * <p>
+ * Calculates the view id from the given faces context by the following algorithm
+ * </p>
+ * <ul>
+ * <li>lookup the viewid from the request attribute "javax.servlet.include.path_info"
+ * <li>if null lookup the value for viewid by {@link ExternalContext#getRequestPathInfo()}
+ * <li>if null lookup the value for viewid from the request attribute "javax.servlet.include.servlet_path"
+ * <li>if null lookup the value for viewid by {@link ExternalContext#getRequestServletPath()}
+ * <li>if null throw a {@link FacesException}
+ * </ul>
+ */
+ String calculateViewId(FacesContext facesContext);
+
+ /**
+ * Processes the component tree. For each component (including the given one) in the tree determine if a value
+ * expression for the attribute "binding" is defined. If the expression is not null set the component instance to
+ * the value of this expression
+ *
+ * @param facesContext
+ * @param component
+ * the root component
+ */
+ void processComponentBinding(FacesContext facesContext,
+ UIComponent component);
+
+ /**
+ * <p>
+ * Determine if the current request is a post back by the following algorithm.
+ * </p>
+ * <p>
+ * Find the render-kit-id for the current request by calling calculateRenderKitId() on the Application’s
+ * ViewHandler. Get that RenderKit’s ResponseStateManager and call its isPostback() method, passing the given
+ * FacesContext.
+ * </p>
+ *
+ * @param facesContext
+ * @return
+ */
+ boolean isPostback(FacesContext facesContext);
+}
\ No newline at end of file
diff --git a/test30/src/main/java/org/apache/myfaces/test/mock/lifecycle/UpdateModelValuesExecutor.java b/test30/src/main/java/org/apache/myfaces/test/mock/lifecycle/UpdateModelValuesExecutor.java
new file mode 100644
index 0000000..f1e7832
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/mock/lifecycle/UpdateModelValuesExecutor.java
@@ -0,0 +1,45 @@
+/*
+ * 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.myfaces.test.mock.lifecycle;
+
+import javax.faces.context.FacesContext;
+import javax.faces.event.PhaseId;
+
+/**
+ * Implements the lifecycle as described in Spec. 1.0 PFD Chapter 2
+ *
+ * Update model values phase (JSF Spec 2.2.4)
+ *
+ * @author Nikolay Petrov
+ * @since 1.0.0
+ */
+class UpdateModelValuesExecutor implements PhaseExecutor
+{
+ public boolean execute(FacesContext facesContext)
+ {
+ facesContext.getViewRoot().processUpdates(facesContext);
+ return false;
+ }
+
+ public PhaseId getPhase()
+ {
+ return PhaseId.UPDATE_MODEL_VALUES;
+ }
+}
\ No newline at end of file
diff --git a/test30/src/main/java/org/apache/myfaces/test/mock/resource/MockClassLoaderResourceLoader.java b/test30/src/main/java/org/apache/myfaces/test/mock/resource/MockClassLoaderResourceLoader.java
new file mode 100644
index 0000000..bc38e07
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/mock/resource/MockClassLoaderResourceLoader.java
@@ -0,0 +1,130 @@
+/*
+ * 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.myfaces.test.mock.resource;
+
+import java.io.InputStream;
+import java.net.URL;
+
+/**
+ * A resource loader implementation which loads resources from the thread ClassLoader.
+ *
+ * @author Leonardo Uribe (latest modification by $Author: lu4242 $)
+ * @version $Revision: 882702 $ $Date: 2009-11-20 15:16:07 -0500 (Vie, 20 Nov 2009) $
+ * @since 1.0.0
+ */
+public class MockClassLoaderResourceLoader extends MockResourceLoader
+{
+ private ClassLoader _classLoader;
+
+ public MockClassLoaderResourceLoader(ClassLoader loader, String prefix)
+ {
+ super(prefix);
+ _classLoader = loader;
+ }
+
+ @Override
+ public String getLibraryVersion(String path)
+ {
+ return null;
+ }
+
+ @Override
+ public InputStream getResourceInputStream(MockResourceMeta resourceMeta)
+ {
+ if (getPrefix() != null && !"".equals(getPrefix()))
+ {
+ return getClassLoader().getResourceAsStream(
+ getPrefix() + '/' + resourceMeta.getResourceIdentifier());
+ }
+ else
+ {
+ return getClassLoader().getResourceAsStream(
+ resourceMeta.getResourceIdentifier());
+ }
+ }
+
+ @Override
+ public URL getResourceURL(MockResourceMeta resourceMeta)
+ {
+ if (getPrefix() != null && !"".equals(getPrefix()))
+ {
+ return getClassLoader().getResource(
+ getPrefix() + '/' + resourceMeta.getResourceIdentifier());
+ }
+ else
+ {
+ return getClassLoader().getResource(
+ resourceMeta.getResourceIdentifier());
+ }
+ }
+
+ @Override
+ public String getResourceVersion(String path)
+ {
+ return null;
+ }
+
+ @Override
+ public MockResourceMeta createResourceMeta(String prefix,
+ String libraryName, String libraryVersion, String resourceName,
+ String resourceVersion)
+ {
+ return new MockResourceMeta(prefix, libraryName, libraryVersion,
+ resourceName, resourceVersion);
+ }
+
+ /**
+ * Returns the ClassLoader to use when looking up resources under the top level package. By default, this is the
+ * context class loader.
+ *
+ * @return the ClassLoader used to lookup resources
+ */
+ public ClassLoader getClassLoader()
+ {
+ return _classLoader;
+ }
+
+ public void setClassLoader(ClassLoader loader)
+ {
+ _classLoader = loader;
+ }
+
+ @Override
+ public boolean libraryExists(String libraryName)
+ {
+ if (getPrefix() != null && !"".equals(getPrefix()))
+ {
+ URL url = getClassLoader().getResource(
+ getPrefix() + '/' + libraryName);
+ if (url != null)
+ {
+ return true;
+ }
+ }
+ else
+ {
+ URL url = getClassLoader().getResource(libraryName);
+ if (url != null)
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/mock/resource/MockExternalContextResourceLoader.java b/test30/src/main/java/org/apache/myfaces/test/mock/resource/MockExternalContextResourceLoader.java
new file mode 100644
index 0000000..388d617
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/mock/resource/MockExternalContextResourceLoader.java
@@ -0,0 +1,223 @@
+/*
+ * 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.myfaces.test.mock.resource;
+
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.regex.Pattern;
+
+import javax.faces.context.FacesContext;
+
+/**
+ * A resource loader implementation which loads resources from the webapp root.
+ * It uses the methods on ExternalContext for handle resources.
+ *
+ * @author Leonardo Uribe (latest modification by $Author: lu4242 $)
+ * @version $Revision: 882702 $ $Date: 2009-11-20 15:16:07 -0500 (Vie, 20 Nov 2009) $
+ * @since 1.0.0
+ */
+public class MockExternalContextResourceLoader extends MockResourceLoader
+{
+ /**
+ * It checks version like this: /1/, /1_0/, /1_0_0/, /100_100/
+ *
+ * Used on getLibraryVersion to filter resource directories
+ **/
+ protected static final Pattern VERSION_CHECKER = Pattern
+ .compile("/\\p{Digit}+(_\\p{Digit}*)*/");
+
+ /**
+ * It checks version like this: /1.js, /1_0.js, /1_0_0.js, /100_100.js
+ *
+ * Used on getResourceVersion to filter resources
+ **/
+ protected static final Pattern RESOURCE_VERSION_CHECKER = Pattern
+ .compile("/\\p{Digit}+(_\\p{Digit}*)*\\..*");
+
+ public MockExternalContextResourceLoader(String prefix)
+ {
+ super(prefix);
+ }
+
+ protected Set<String> getResourcePaths(String path)
+ {
+ return FacesContext.getCurrentInstance().getExternalContext()
+ .getResourcePaths(getPrefix() + '/' + path);
+ }
+
+ @Override
+ public String getResourceVersion(String path)
+ {
+ String resourceVersion = null;
+ Set<String> resourcePaths = this.getResourcePaths(path);
+ if (getPrefix() != null)
+ {
+ path = getPrefix() + '/' + path;
+ }
+
+ if (null != resourcePaths && !resourcePaths.isEmpty())
+ {
+ // resourceVersion = // execute the comment
+ // Look in the resourcePaths for versioned resources.
+ // If one or more versioned resources are found, take
+ // the one with the "highest" version number as the value
+ // of resourceVersion. If no versioned libraries
+ // are found, let resourceVersion remain null.
+ for (String resourcePath : resourcePaths)
+ {
+ String version = resourcePath.substring(path.length());
+
+ if (RESOURCE_VERSION_CHECKER.matcher(version).matches())
+ {
+ version = version.substring(1, version.lastIndexOf('.'));
+ if (resourceVersion == null)
+ {
+ resourceVersion = version;
+ }
+ else if (getVersionComparator().compare(resourceVersion,
+ version) < 0)
+ {
+ resourceVersion = version;
+ }
+ }
+ }
+ //Since it is a directory and no version was found, set as invalid
+ if (resourceVersion == null)
+ {
+ resourceVersion = VERSION_INVALID;
+ }
+ }
+ return resourceVersion;
+ }
+
+ @Override
+ public String getLibraryVersion(String path)
+ {
+ String libraryVersion = null;
+ Set<String> libraryPaths = this.getResourcePaths(path);
+ path = getPrefix() + '/' + path;
+ if (null != libraryPaths && !libraryPaths.isEmpty())
+ {
+ // Look in the libraryPaths for versioned libraries.
+ // If one or more versioned libraries are found, take
+ // the one with the "highest" version number as the value
+ // of libraryVersion. If no versioned libraries
+ // are found, let libraryVersion remain null.
+
+ for (Iterator<String> it = libraryPaths.iterator(); it.hasNext();)
+ {
+ String libraryPath = it.next();
+ String version = libraryPath.substring(path.length());
+
+ if (VERSION_CHECKER.matcher(version).matches())
+ {
+ version = version.substring(1, version.length() - 1);
+ if (libraryVersion == null)
+ {
+ libraryVersion = version;
+ }
+ else if (getVersionComparator().compare(libraryVersion,
+ version) < 0)
+ {
+ libraryVersion = version;
+ }
+ }
+ }
+ }
+ return libraryVersion;
+ }
+
+ @Override
+ public URL getResourceURL(MockResourceMeta resourceMeta)
+ {
+ try
+ {
+ return FacesContext.getCurrentInstance().getExternalContext()
+ .getResource(
+ getPrefix() + '/'
+ + resourceMeta.getResourceIdentifier());
+ }
+ catch (MalformedURLException e)
+ {
+ return null;
+ }
+ }
+
+ @Override
+ public InputStream getResourceInputStream(MockResourceMeta resourceMeta)
+ {
+ return FacesContext.getCurrentInstance().getExternalContext()
+ .getResourceAsStream(
+ getPrefix() + '/'
+ + resourceMeta.getResourceIdentifier());
+ }
+
+ @Override
+ public MockResourceMeta createResourceMeta(String prefix,
+ String libraryName, String libraryVersion, String resourceName,
+ String resourceVersion)
+ {
+ return new MockResourceMeta(prefix, libraryName, libraryVersion,
+ resourceName, resourceVersion);
+ }
+
+ @Override
+ public boolean libraryExists(String libraryName)
+ {
+ if (getPrefix() != null && !"".equals(getPrefix()))
+ {
+ try
+ {
+ URL url = FacesContext.getCurrentInstance()
+ .getExternalContext().getResource(
+ getPrefix() + '/' + libraryName);
+ if (url != null)
+ {
+ return true;
+ }
+ }
+ catch (MalformedURLException e)
+ {
+ return false;
+ }
+ }
+ else
+ {
+ try
+ {
+
+ URL url = FacesContext.getCurrentInstance()
+ .getExternalContext().getResource(libraryName);
+
+ if (url != null)
+ {
+ return true;
+ }
+ }
+ catch (MalformedURLException e)
+ {
+ return false;
+ }
+ }
+ return false;
+ }
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/mock/resource/MockResource.java b/test30/src/main/java/org/apache/myfaces/test/mock/resource/MockResource.java
new file mode 100644
index 0000000..436e3ce
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/mock/resource/MockResource.java
@@ -0,0 +1,105 @@
+/*
+ * 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.myfaces.test.mock.resource;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.Collections;
+import java.util.Map;
+
+import javax.faces.application.Resource;
+import javax.faces.application.ResourceHandler;
+import javax.faces.context.FacesContext;
+
+/**
+ * <p>Mock implementation of <code>Resource</code>.</p>
+ * <p/>
+ * @author Leonardo Uribe (latest modification by $Author$)
+ * @version $Revision$ $Date$
+ * @since 1.0.0
+ */
+public class MockResource extends Resource
+{
+ private MockResourceMeta _resourceMeta;
+ private MockResourceLoader _resourceLoader;
+ private MockResourceHandlerSupport _resourceHandlerSupport;
+
+ public MockResource(MockResourceMeta resourceMeta,
+ MockResourceLoader resourceLoader,
+ MockResourceHandlerSupport support, String contentType)
+ {
+ _resourceMeta = resourceMeta;
+ _resourceLoader = resourceLoader;
+ _resourceHandlerSupport = support;
+ setLibraryName(resourceMeta.getLibraryName());
+ setResourceName(resourceMeta.getResourceName());
+ setContentType(contentType);
+ }
+
+ public MockResourceLoader getResourceLoader()
+ {
+ return _resourceLoader;
+ }
+
+ @Override
+ public InputStream getInputStream() throws IOException
+ {
+ return getResourceLoader().getResourceInputStream(_resourceMeta);
+ }
+
+ @Override
+ public String getRequestPath()
+ {
+ String path;
+ if (_resourceHandlerSupport.isExtensionMapping())
+ {
+ path = ResourceHandler.RESOURCE_IDENTIFIER + '/'
+ + getResourceName() + _resourceHandlerSupport.getMapping();
+ }
+ else
+ {
+ String mapping = _resourceHandlerSupport.getMapping();
+ path = ResourceHandler.RESOURCE_IDENTIFIER + '/'
+ + getResourceName();
+ path = (mapping == null) ? path : mapping + path;
+ }
+
+ return path;
+ }
+
+ @Override
+ public Map<String, String> getResponseHeaders()
+ {
+ return Collections.emptyMap();
+ }
+
+ @Override
+ public URL getURL()
+ {
+ return getResourceLoader().getResourceURL(_resourceMeta);
+ }
+
+ @Override
+ public boolean userAgentNeedsUpdate(FacesContext context)
+ {
+ return true;
+ }
+}
\ No newline at end of file
diff --git a/test30/src/main/java/org/apache/myfaces/test/mock/resource/MockResourceHandler.java b/test30/src/main/java/org/apache/myfaces/test/mock/resource/MockResourceHandler.java
new file mode 100644
index 0000000..a12cf48
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/mock/resource/MockResourceHandler.java
@@ -0,0 +1,401 @@
+/*
+ * 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.myfaces.test.mock.resource;
+
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.security.AccessController;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.util.Locale;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+
+import javax.faces.FacesException;
+import javax.faces.application.Resource;
+import javax.faces.application.ResourceHandler;
+import javax.faces.context.FacesContext;
+
+/**
+ * <p>Mock implementation of <code>ResourceHandler</code>.</p>
+ * <p>This ResourceHandler implementation try to follow the default algorithm
+ * defined by the spec, so it try to load resources using the current
+ * ExternalContext and the specified ClassLoader, in the same locations
+ * it is expected ("resources" and "META-INF/resources").</p>
+ *
+ * @author Leonardo Uribe (latest modification by $Author$)
+ * @version $Revision$ $Date$
+ * @since 1.0.0
+ */
+public class MockResourceHandler extends ResourceHandler
+{
+
+ private boolean _resourceRequest;
+
+ private MockResourceHandlerSupport resourceHandlerSupport;
+
+ private ClassLoader _classLoader;
+
+ public MockResourceHandler()
+ {
+ _classLoader = getContextClassLoader();
+ resourceHandlerSupport = new MockResourceHandlerSupport(true, ".jsf",
+ _classLoader);
+ }
+
+ public MockResourceHandler(ClassLoader classLoader)
+ {
+ if (classLoader == null)
+ {
+ _classLoader = getContextClassLoader();
+ }
+ else
+ {
+ _classLoader = classLoader;
+ }
+
+ resourceHandlerSupport = new MockResourceHandlerSupport(true, ".jsf",
+ _classLoader);
+ }
+
+ public MockResourceHandler(boolean extensionMapping, String mapping,
+ ClassLoader classLoader)
+ {
+ if (classLoader == null)
+ {
+ _classLoader = getContextClassLoader();
+ }
+ else
+ {
+ _classLoader = classLoader;
+ }
+
+ resourceHandlerSupport = new MockResourceHandlerSupport(
+ extensionMapping, mapping, _classLoader);
+ }
+
+ @Override
+ public Resource createResource(String resourceName)
+ {
+ return createResource(resourceName, null);
+ }
+
+ @Override
+ public Resource createResource(String resourceName, String libraryName)
+ {
+ return createResource(resourceName, libraryName, null);
+ }
+
+ @Override
+ public Resource createResource(String resourceName, String libraryName,
+ String contentType)
+ {
+ Resource resource = null;
+
+ if (contentType == null)
+ {
+ //Resolve contentType using ExternalContext.getMimeType
+ contentType = FacesContext.getCurrentInstance()
+ .getExternalContext().getMimeType(resourceName);
+ }
+
+ for (MockResourceLoader loader : getResourceHandlerSupport()
+ .getResourceLoaders())
+ {
+ MockResourceMeta resourceMeta = deriveResourceMeta(loader,
+ resourceName, libraryName);
+
+ if (resourceMeta != null)
+ {
+ resource = new MockResource(resourceMeta, loader,
+ getResourceHandlerSupport(), contentType);
+ break;
+ }
+ }
+ return resource;
+ }
+
+ /**
+ * This method try to create a ResourceMeta for a specific resource
+ * loader. If no library, or resource is found, just return null,
+ * so the algorithm in createResource can continue checking with the
+ * next registered ResourceLoader.
+ */
+ protected MockResourceMeta deriveResourceMeta(
+ MockResourceLoader resourceLoader, String resourceName,
+ String libraryName)
+ {
+ String localePrefix = getLocalePrefixForLocateResource();
+ String resourceVersion = null;
+ String libraryVersion = null;
+ MockResourceMeta resourceId = null;
+
+ //1. Try to locate resource in a localized path
+ if (localePrefix != null)
+ {
+ if (null != libraryName)
+ {
+ String pathToLib = localePrefix + '/' + libraryName;
+ libraryVersion = resourceLoader.getLibraryVersion(pathToLib);
+
+ if (null != libraryVersion)
+ {
+ String pathToResource = localePrefix + '/' + libraryName
+ + '/' + libraryVersion + '/' + resourceName;
+ resourceVersion = resourceLoader
+ .getResourceVersion(pathToResource);
+ }
+ else
+ {
+ String pathToResource = localePrefix + '/' + libraryName
+ + '/' + resourceName;
+ resourceVersion = resourceLoader
+ .getResourceVersion(pathToResource);
+ }
+
+ if (!(resourceVersion != null && MockResourceLoader.VERSION_INVALID
+ .equals(resourceVersion)))
+ {
+ resourceId = resourceLoader.createResourceMeta(
+ localePrefix, libraryName, libraryVersion,
+ resourceName, resourceVersion);
+ }
+ }
+ else
+ {
+ resourceVersion = resourceLoader
+ .getResourceVersion(localePrefix + '/' + resourceName);
+ if (!(resourceVersion != null && MockResourceLoader.VERSION_INVALID
+ .equals(resourceVersion)))
+ {
+ resourceId = resourceLoader.createResourceMeta(
+ localePrefix, null, null, resourceName,
+ resourceVersion);
+ }
+ }
+
+ if (resourceId != null)
+ {
+ URL url = resourceLoader.getResourceURL(resourceId);
+ if (url == null)
+ {
+ resourceId = null;
+ }
+ }
+ }
+
+ //2. Try to localize resource in a non localized path
+ if (resourceId == null)
+ {
+ if (null != libraryName)
+ {
+ libraryVersion = resourceLoader.getLibraryVersion(libraryName);
+
+ if (null != libraryVersion)
+ {
+ String pathToResource = (libraryName + '/' + libraryVersion
+ + '/' + resourceName);
+ resourceVersion = resourceLoader
+ .getResourceVersion(pathToResource);
+ }
+ else
+ {
+ String pathToResource = (libraryName + '/' + resourceName);
+ resourceVersion = resourceLoader
+ .getResourceVersion(pathToResource);
+ }
+
+ if (!(resourceVersion != null && MockResourceLoader.VERSION_INVALID
+ .equals(resourceVersion)))
+ {
+ resourceId = resourceLoader.createResourceMeta(null,
+ libraryName, libraryVersion, resourceName,
+ resourceVersion);
+ }
+ }
+ else
+ {
+ resourceVersion = resourceLoader
+ .getResourceVersion(resourceName);
+
+ if (!(resourceVersion != null && MockResourceLoader.VERSION_INVALID
+ .equals(resourceVersion)))
+ {
+ resourceId = resourceLoader.createResourceMeta(null, null,
+ null, resourceName, resourceVersion);
+ }
+ }
+
+ if (resourceId != null)
+ {
+ URL url = resourceLoader.getResourceURL(resourceId);
+ if (url == null)
+ {
+ resourceId = null;
+ }
+ }
+ }
+
+ return resourceId;
+ }
+
+ @Override
+ public String getRendererTypeForResourceName(String resourceName)
+ {
+ if (resourceName.endsWith(".js"))
+ {
+ return "javax.faces.resource.Script";
+ }
+ else if (resourceName.endsWith(".css"))
+ {
+ return "javax.faces.resource.Stylesheet";
+ }
+ return null;
+ }
+
+ @Override
+ public void handleResourceRequest(FacesContext context) throws IOException
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean isResourceRequest(FacesContext facesContext)
+ {
+ return _resourceRequest;
+ }
+
+ @Override
+ public boolean libraryExists(String libraryName)
+ {
+ String localePrefix = getLocalePrefixForLocateResource();
+
+ String pathToLib;
+
+ if (localePrefix != null)
+ {
+ //Check with locale
+ pathToLib = localePrefix + '/' + libraryName;
+ }
+ else
+ {
+ pathToLib = libraryName;
+ }
+
+ try
+ {
+ URL url = FacesContext.getCurrentInstance().getExternalContext()
+ .getResource("/" + pathToLib);
+ return (url != null);
+ }
+ catch (MalformedURLException e)
+ {
+ return false;
+ }
+ }
+
+ protected String getLocalePrefixForLocateResource()
+ {
+ String localePrefix = null;
+ FacesContext context = FacesContext.getCurrentInstance();
+
+ String bundleName = context.getApplication().getMessageBundle();
+
+ if (null != bundleName)
+ {
+ Locale locale = context.getApplication().getViewHandler()
+ .calculateLocale(context);
+
+ ResourceBundle bundle = ResourceBundle.getBundle(bundleName,
+ locale, getContextClassLoader());
+
+ if (bundle != null)
+ {
+ try
+ {
+ localePrefix = bundle
+ .getString(ResourceHandler.LOCALE_PREFIX);
+ }
+ catch (MissingResourceException e)
+ {
+ // Ignore it and return null
+ }
+ }
+ }
+ return localePrefix;
+ }
+
+ /**
+ * Gets the ClassLoader associated with the current thread. Includes a check for priviledges
+ * against java2 security to ensure no security related exceptions are encountered.
+ *
+ * @return ClassLoader
+ */
+ static ClassLoader getContextClassLoader()
+ {
+ if (System.getSecurityManager() != null)
+ {
+ try
+ {
+ ClassLoader cl = AccessController
+ .doPrivileged(new PrivilegedExceptionAction<ClassLoader>()
+ {
+ public ClassLoader run()
+ throws PrivilegedActionException
+ {
+ return Thread.currentThread()
+ .getContextClassLoader();
+ }
+ });
+ return cl;
+ }
+ catch (PrivilegedActionException pae)
+ {
+ throw new FacesException(pae);
+ }
+ }
+ else
+ {
+ return Thread.currentThread().getContextClassLoader();
+ }
+ }
+
+ public MockResourceHandlerSupport getResourceHandlerSupport()
+ {
+ return resourceHandlerSupport;
+ }
+
+ public void setResourceHandlerSupport(
+ MockResourceHandlerSupport resourceHandlerSupport)
+ {
+ this.resourceHandlerSupport = resourceHandlerSupport;
+ }
+
+ public void setResourceRequest(boolean resourceRequest)
+ {
+ this._resourceRequest = resourceRequest;
+ }
+
+ public boolean isResourceRequest()
+ {
+ return _resourceRequest;
+ }
+}
\ No newline at end of file
diff --git a/test30/src/main/java/org/apache/myfaces/test/mock/resource/MockResourceHandlerSupport.java b/test30/src/main/java/org/apache/myfaces/test/mock/resource/MockResourceHandlerSupport.java
new file mode 100644
index 0000000..5f423e5
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/mock/resource/MockResourceHandlerSupport.java
@@ -0,0 +1,124 @@
+/*
+ * 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.myfaces.test.mock.resource;
+
+/**
+ * Store additional info used by MockResource and MockResourceHandler
+ *
+ * @author Leonardo Uribe (latest modification by $Author: jakobk $)
+ * @version $Revision: 960906 $ $Date: 2010-07-06 09:45:40 -0500 (Mar, 06 Jul 2010) $
+ * @since 1.0.0
+ */
+public class MockResourceHandlerSupport
+{
+
+ private boolean _extensionMapping;
+
+ private String _mapping;
+
+ private MockResourceLoader[] _resourceLoaders;
+
+ public MockResourceHandlerSupport()
+ {
+ _extensionMapping = true;
+ _mapping = ".jsf";
+ _resourceLoaders = new MockResourceLoader[] {
+ new MockExternalContextResourceLoader("/resources"),
+ new MockClassLoaderResourceLoader(MockResourceHandler
+ .getContextClassLoader(), "META-INF/resources") };
+ }
+
+ public MockResourceHandlerSupport(boolean extensionMapping, String mapping)
+ {
+ super();
+ _extensionMapping = extensionMapping;
+ _mapping = mapping;
+ _resourceLoaders = new MockResourceLoader[] {
+ new MockExternalContextResourceLoader("/resources"),
+ new MockClassLoaderResourceLoader(MockResourceHandler
+ .getContextClassLoader(), "META-INF/resources") };
+ }
+
+ public MockResourceHandlerSupport(boolean extensionMapping, String mapping,
+ ClassLoader classLoader)
+ {
+ _extensionMapping = extensionMapping;
+ _mapping = mapping;
+ _resourceLoaders = new MockResourceLoader[] {
+ new MockExternalContextResourceLoader("/resources"),
+ new MockClassLoaderResourceLoader(classLoader,
+ "META-INF/resources") };
+ }
+
+ public MockResourceHandlerSupport(boolean extensionMapping, String mapping,
+ MockResourceLoader[] resourceLoaders)
+ {
+ super();
+ _extensionMapping = extensionMapping;
+ _mapping = mapping;
+ _resourceLoaders = resourceLoaders;
+ }
+
+ /**
+ * Check if the mapping used is done using extensions (.xhtml, .jsf)
+ * or if it is not (/faces/*)
+ * @return
+ */
+ public boolean isExtensionMapping()
+ {
+ return _extensionMapping;
+ }
+
+ public void setExtensionMapping(boolean extensionMapping)
+ {
+ _extensionMapping = extensionMapping;
+ }
+
+ /**
+ * Get the mapping used as prefix(/faces) or sufix(.jsf)
+ *
+ * @return
+ */
+ public String getMapping()
+ {
+ return _mapping;
+ }
+
+ public void setMapping(String prefix)
+ {
+ _mapping = prefix;
+ }
+
+ /**
+ * Return an array of resource loaders used to find resources
+ * using the standard. The order of ResourceLoaders define
+ * its precedence.
+ *
+ * @return
+ */
+ public MockResourceLoader[] getResourceLoaders()
+ {
+ return _resourceLoaders;
+ }
+
+ public void setResourceLoaders(MockResourceLoader[] resourceLoaders)
+ {
+ _resourceLoaders = resourceLoaders;
+ }
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/mock/resource/MockResourceLoader.java b/test30/src/main/java/org/apache/myfaces/test/mock/resource/MockResourceLoader.java
new file mode 100644
index 0000000..33ba348
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/mock/resource/MockResourceLoader.java
@@ -0,0 +1,158 @@
+/*
+ * 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.myfaces.test.mock.resource;
+
+import java.io.InputStream;
+import java.net.URL;
+import java.util.Comparator;
+
+/**
+ * Base class for resource loaders. Resource loaders can lookup resources
+ * as URLs from arbitrary locations, including JAR files.
+ *
+ * @author Leonardo Uribe (latest modification by $Author: lu4242 $)
+ * @version $Revision: 882702 $ $Date: 2009-11-20 15:16:07 -0500 (Vie, 20 Nov 2009) $
+ * @since 1.0.0
+ */
+public abstract class MockResourceLoader
+{
+
+ public static final String VERSION_INVALID = "INVALID";
+
+ private String _prefix;
+
+ public MockResourceLoader(String prefix)
+ {
+ _prefix = prefix;
+ }
+
+ public abstract String getResourceVersion(String path);
+
+ /**
+ * Return the max available version found (if exists) or
+ * return null if no version available.
+ */
+ public abstract String getLibraryVersion(String path);
+
+ /**
+ * Return the max available version found (if exists) or
+ * return null if no version available.
+ */
+ public abstract URL getResourceURL(MockResourceMeta resourceMeta);
+
+ public abstract InputStream getResourceInputStream(
+ MockResourceMeta resourceMeta);
+
+ public abstract MockResourceMeta createResourceMeta(String prefix,
+ String libraryName, String libraryVersion, String resourceName,
+ String resourceVersion);
+
+ public abstract boolean libraryExists(String libraryName);
+
+ private Comparator<String> _versionComparator = null;
+
+ protected Comparator<String> getVersionComparator()
+ {
+ if (_versionComparator == null)
+ {
+ _versionComparator = new VersionComparator();
+ }
+ return _versionComparator;
+ }
+
+ protected void setVersionComparator(Comparator<String> versionComparator)
+ {
+ _versionComparator = versionComparator;
+ }
+
+ public class VersionComparator implements Comparator<String>
+ {
+
+ public int compare(String s1, String s2)
+ {
+ int n1 = 0;
+ int n2 = 0;
+ String o1 = s1;
+ String o2 = s2;
+
+ boolean p1 = true;
+ boolean p2 = true;
+
+ while (n1 == n2 && (p1 || p2))
+ {
+ int i1 = o1.indexOf('_');
+ int i2 = o2.indexOf('_');
+ if (i1 < 0)
+ {
+ if (o1.length() > 0)
+ {
+ p1 = false;
+ n1 = Integer.valueOf(o1);
+ o1 = "";
+ }
+ else
+ {
+ p1 = false;
+ n1 = 0;
+ }
+ }
+ else
+ {
+ n1 = Integer.valueOf(o1.substring(0, i1));
+ o1 = o1.substring(i1 + 1);
+ }
+ if (i2 < 0)
+ {
+ if (o2.length() > 0)
+ {
+ p2 = false;
+ n2 = Integer.valueOf(o2);
+ o2 = "";
+ }
+ else
+ {
+ p2 = false;
+ n2 = 0;
+ }
+ }
+ else
+ {
+ n2 = Integer.valueOf(o2.substring(0, i2));
+ o2 = o2.substring(i2 + 1);
+ }
+ }
+
+ if (n1 == n2)
+ {
+ return s1.length() - s2.length();
+ }
+ return n1 - n2;
+ }
+ }
+
+ public String getPrefix()
+ {
+ return _prefix;
+ }
+
+ public void setPrefix(String prefix)
+ {
+ _prefix = prefix;
+ }
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/mock/resource/MockResourceMeta.java b/test30/src/main/java/org/apache/myfaces/test/mock/resource/MockResourceMeta.java
new file mode 100644
index 0000000..dd8fc1d
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/mock/resource/MockResourceMeta.java
@@ -0,0 +1,123 @@
+/*
+ * 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.myfaces.test.mock.resource;
+
+/**
+ * Contains the metadata information to reference a resource
+ *
+ * @author Leonardo Uribe (latest modification by $Author: lu4242 $)
+ * @version $Revision: 946779 $ $Date: 2010-05-20 15:31:42 -0500 (Jue, 20 May 2010) $
+ * @since 1.0.0
+ */
+public class MockResourceMeta
+{
+
+ private final String _prefix;
+ private final String _libraryName;
+ private final String _libraryVersion;
+ private final String _resourceName;
+ private final String _resourceVersion;
+
+ public MockResourceMeta(String prefix, String libraryName,
+ String libraryVersion, String resourceName, String resourceVersion)
+ {
+ _prefix = prefix;
+ _libraryName = libraryName;
+ _libraryVersion = libraryVersion;
+ _resourceName = resourceName;
+ _resourceVersion = resourceVersion;
+ }
+
+ public String getLibraryName()
+ {
+ return _libraryName;
+ }
+
+ public String getResourceName()
+ {
+ return _resourceName;
+ }
+
+ public String getLocalePrefix()
+ {
+ return _prefix;
+ }
+
+ public String getLibraryVersion()
+ {
+ return _libraryVersion;
+ }
+
+ public String getResourceVersion()
+ {
+ return _resourceVersion;
+ }
+
+ public String getResourceIdentifier()
+ {
+ StringBuilder builder = new StringBuilder();
+ boolean firstSlashAdded = false;
+ if (_prefix != null && _prefix.length() > 0)
+ {
+ builder.append(_prefix);
+ firstSlashAdded = true;
+ }
+ if (_libraryName != null)
+ {
+ if (firstSlashAdded)
+ {
+ builder.append('/');
+ }
+ builder.append(_libraryName);
+ firstSlashAdded = true;
+ }
+ if (_libraryVersion != null)
+ {
+ if (firstSlashAdded)
+ {
+ builder.append('/');
+ }
+ builder.append(_libraryVersion);
+ firstSlashAdded = true;
+ }
+ if (_resourceName != null)
+ {
+ if (firstSlashAdded)
+ {
+ builder.append('/');
+ }
+ builder.append(_resourceName);
+ firstSlashAdded = true;
+ }
+ if (_resourceVersion != null)
+ {
+ if (firstSlashAdded)
+ {
+ builder.append('/');
+ }
+ builder.append(_resourceVersion);
+ builder.append(_resourceName.substring(_resourceName
+ .lastIndexOf('.')));
+ firstSlashAdded = true;
+ }
+
+ return builder.toString();
+ }
+
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/mock/resource/MockSimpleResource.java b/test30/src/main/java/org/apache/myfaces/test/mock/resource/MockSimpleResource.java
new file mode 100644
index 0000000..8629f5d
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/mock/resource/MockSimpleResource.java
@@ -0,0 +1,200 @@
+/*
+ * 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.myfaces.test.mock.resource;
+
+import org.apache.myfaces.test.mock.MockServletContext;
+
+import javax.faces.application.Resource;
+import javax.faces.context.FacesContext;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Map;
+
+/**
+ * <p>Mock simple implementation of <code>Resource</code>.</p>
+ *
+ * <p>
+ * It is used by MockSimpleResourceHandler to wrap resource instances.
+ * </p>
+ *
+ * @author Jakob Korherr (latest modification by $Author: lu4242 $)
+ * @version $Revision: 882702 $ $Date: 2009-11-20 15:16:07 -0500 (Vie, 20 Nov 2009) $
+ * @since 1.0.0
+ */
+public class MockSimpleResource extends Resource
+{
+
+ private String _prefix;
+ private String _libraryName;
+ private String _libraryVersion;
+ private String _resourceName;
+ private String _resourceVersion;
+ private File _documentRoot;
+
+ /**
+ * Creates new resource object
+ *
+ * @param prefix locale prefix if any
+ * @param libraryName resource library name
+ * @param libraryVersion resource library version if any
+ * @param resourceName resource file name
+ * @param resourceVersion resource version if any
+ * @param documentRoot parent folder of resource directories. Must not be <code>null</code>
+ */
+ public MockSimpleResource(String prefix, String libraryName,
+ String libraryVersion, String resourceName, String resourceVersion,
+ File documentRoot)
+ {
+ _prefix = prefix;
+ _libraryName = libraryName;
+ _libraryVersion = libraryVersion;
+ _resourceName = resourceName;
+ _resourceVersion = resourceVersion;
+ _documentRoot = documentRoot;
+
+ if (_documentRoot == null)
+ {
+ throw new IllegalArgumentException("documentRoot must not be null");
+ }
+ }
+
+ @Override
+ public String getResourceName()
+ {
+ return _resourceName;
+ }
+
+ @Override
+ public void setResourceName(String resourceName)
+ {
+ _resourceName = resourceName;
+ }
+
+ @Override
+ public String getLibraryName()
+ {
+ return _libraryName;
+ }
+
+ @Override
+ public void setLibraryName(String libraryName)
+ {
+ _libraryName = libraryName;
+ }
+
+ @Override
+ public InputStream getInputStream() throws IOException
+ {
+ MockServletContext servletContext = (MockServletContext) FacesContext
+ .getCurrentInstance().getExternalContext().getContext();
+ servletContext.setDocumentRoot(_documentRoot);
+ return servletContext.getResourceAsStream(buildResourcePath());
+ }
+
+ @Override
+ public String getRequestPath()
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Map<String, String> getResponseHeaders()
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public URL getURL()
+ {
+ MockServletContext servletContext = (MockServletContext) FacesContext
+ .getCurrentInstance().getExternalContext().getContext();
+ servletContext.setDocumentRoot(_documentRoot);
+
+ try
+ {
+ return servletContext.getResource(buildResourcePath());
+ }
+ catch (MalformedURLException e)
+ {
+ return null;
+ }
+ }
+
+ @Override
+ public boolean userAgentNeedsUpdate(FacesContext context)
+ {
+ return true;
+ }
+
+ private String buildResourcePath()
+ {
+ StringBuilder builder = new StringBuilder();
+ builder.append('/');
+ boolean firstSlashAdded = false;
+ if (_prefix != null && _prefix.length() > 0)
+ {
+ builder.append(_prefix);
+ firstSlashAdded = true;
+ }
+ if (_libraryName != null)
+ {
+ if (firstSlashAdded)
+ {
+ builder.append('/');
+ }
+ builder.append(_libraryName);
+ firstSlashAdded = true;
+ }
+ if (_libraryVersion != null)
+ {
+ if (firstSlashAdded)
+ {
+ builder.append('/');
+ }
+ builder.append(_libraryVersion);
+ firstSlashAdded = true;
+ }
+ if (_resourceName != null)
+ {
+ if (firstSlashAdded)
+ {
+ builder.append('/');
+ }
+ builder.append(_resourceName);
+ firstSlashAdded = true;
+ }
+ if (_resourceVersion != null)
+ {
+ if (firstSlashAdded)
+ {
+ builder.append('/');
+ }
+ builder.append(_resourceVersion);
+ builder.append(_resourceName.substring(_resourceName
+ .lastIndexOf('.')));
+ }
+
+ return builder.toString();
+ }
+
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/mock/resource/MockSimpleResourceHandler.java b/test30/src/main/java/org/apache/myfaces/test/mock/resource/MockSimpleResourceHandler.java
new file mode 100644
index 0000000..b189e3a
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/mock/resource/MockSimpleResourceHandler.java
@@ -0,0 +1,410 @@
+/*
+ * 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.myfaces.test.mock.resource;
+
+import org.apache.myfaces.test.mock.MockServletContext;
+
+import javax.faces.FacesException;
+import javax.faces.application.Resource;
+import javax.faces.application.ResourceHandler;
+import javax.faces.context.ExternalContext;
+import javax.faces.context.FacesContext;
+import java.io.File;
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.security.AccessController;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.util.Locale;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+import java.util.Set;
+import java.util.regex.Pattern;
+
+/**
+ * <p>Mock implementation of <code>ResourceHandler</code>.</p>
+ * <p>This Mock could be used on situations where all resources
+ * are on a specific path.</p>
+ *
+ * @author Jakob Korherr (latest modification by $Author: lu4242 $)
+ * @version $Revision: 882702 $ $Date: 2009-11-20 15:16:07 -0500 (Vie, 20 Nov 2009) $
+ * @since 1.0.0
+ */
+public class MockSimpleResourceHandler extends ResourceHandler
+{
+
+ private static final String IS_RESOURCE_REQUEST = "org.apache.myfaces.IS_RESOURCE_REQUEST";
+
+ /**
+ * It checks version like this: /1/, /1_0/, /1_0_0/, /100_100/
+ * <p/>
+ * Used on getLibraryVersion to filter resource directories
+ */
+ protected static final Pattern VERSION_CHECKER = Pattern
+ .compile("/\\p{Digit}+(_\\p{Digit}*)*/");
+
+ /**
+ * It checks version like this: /1.js, /1_0.js, /1_0_0.js, /100_100.js
+ * <p/>
+ * Used on getResourceVersion to filter resources
+ */
+ protected static final Pattern RESOURCE_VERSION_CHECKER = Pattern
+ .compile("/\\p{Digit}+(_\\p{Digit}*)*\\..*");
+
+ private File _documentRoot;
+
+ /**
+ * @param documentRoot parent folder of resource directories. Must not be <code>null</code>
+ */
+ public MockSimpleResourceHandler(File documentRoot)
+ {
+ if (documentRoot == null)
+ {
+ throw new NullPointerException("documentRoot must not be null");
+ }
+
+ _documentRoot = documentRoot;
+
+ ((MockServletContext) FacesContext.getCurrentInstance()
+ .getExternalContext().getContext())
+ .setDocumentRoot(_documentRoot);
+ }
+
+ @Override
+ public Resource createResource(String resourceName)
+ {
+ return createResource(resourceName, null);
+ }
+
+ @Override
+ public Resource createResource(String resourceName, String libraryName)
+ {
+ return createResource(resourceName, libraryName, null);
+ }
+
+ @Override
+ public Resource createResource(String resourceName, String libraryName,
+ String contentType)
+ {
+ String prefix = getLocalePrefixForLocateResource();
+ String libraryVersion = getLibraryVersion(prefix + "/" + libraryName);
+
+ String pathToResource;
+ if (null != libraryVersion)
+ {
+ pathToResource = prefix + '/' + libraryName + '/' + libraryVersion
+ + '/' + resourceName;
+ }
+ else
+ {
+ pathToResource = prefix + '/' + libraryName + '/' + resourceName;
+ }
+
+ return new MockSimpleResource(prefix, libraryName, libraryVersion,
+ resourceName, getResourceVersion(pathToResource), _documentRoot);
+ }
+
+ @Override
+ public String getRendererTypeForResourceName(String resourceName)
+ {
+ if (resourceName.endsWith(".js"))
+ {
+ return "javax.faces.resource.Script";
+ }
+ else if (resourceName.endsWith(".css"))
+ {
+ return "javax.faces.resource.Stylesheet";
+ }
+ return null;
+ }
+
+ @Override
+ public void handleResourceRequest(FacesContext context) throws IOException
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean isResourceRequest(FacesContext facesContext)
+ {
+ // Since this method could be called many times we save it
+ //on request map so the first time is calculated it remains
+ //alive until the end of the request
+ Boolean value = (Boolean) facesContext.getExternalContext()
+ .getRequestMap().get(IS_RESOURCE_REQUEST);
+
+ if (value != null && value.booleanValue())
+ {
+ //return the saved value
+ return value.booleanValue();
+ }
+ else
+ {
+ // assuming that we don't have servlet mapping
+ String resourceBasePath = facesContext.getExternalContext()
+ .getRequestPathInfo();
+
+ if (resourceBasePath != null
+ && resourceBasePath
+ .startsWith(ResourceHandler.RESOURCE_IDENTIFIER))
+ {
+ facesContext.getExternalContext().getRequestMap().put(
+ IS_RESOURCE_REQUEST, Boolean.TRUE);
+ return true;
+ }
+ else
+ {
+ facesContext.getExternalContext().getRequestMap().put(
+ IS_RESOURCE_REQUEST, Boolean.FALSE);
+ return false;
+ }
+ }
+ }
+
+ @Override
+ public boolean libraryExists(String libraryName)
+ {
+ String localePrefix = getLocalePrefixForLocateResource();
+
+ String pathToLib;
+
+ if (localePrefix != null)
+ {
+ //Check with locale
+ pathToLib = localePrefix + '/' + libraryName;
+ }
+ else
+ {
+ pathToLib = libraryName;
+ }
+
+ try
+ {
+ URL url = FacesContext.getCurrentInstance().getExternalContext()
+ .getResource("/" + pathToLib);
+ return (url != null);
+ }
+ catch (MalformedURLException e)
+ {
+ return false;
+ }
+ }
+
+ protected String getLocalePrefixForLocateResource()
+ {
+ String localePrefix = null;
+ FacesContext context = FacesContext.getCurrentInstance();
+
+ String bundleName = context.getApplication().getMessageBundle();
+
+ if (null != bundleName)
+ {
+ Locale locale = context.getApplication().getViewHandler()
+ .calculateLocale(context);
+
+ ResourceBundle bundle = ResourceBundle.getBundle(bundleName,
+ locale, getContextClassLoader());
+
+ if (bundle != null)
+ {
+ try
+ {
+ localePrefix = bundle
+ .getString(ResourceHandler.LOCALE_PREFIX);
+ }
+ catch (MissingResourceException e)
+ {
+ // Ignore it and return null
+ }
+ }
+ }
+ return localePrefix;
+ }
+
+ /**
+ * Gets the ClassLoader associated with the current thread. Includes a check for priviledges
+ * against java2 security to ensure no security related exceptions are encountered.
+ *
+ * @return ClassLoader
+ * @since 3.0.6
+ */
+ private static ClassLoader getContextClassLoader()
+ {
+ if (System.getSecurityManager() != null)
+ {
+ try
+ {
+ ClassLoader cl = AccessController
+ .doPrivileged(new PrivilegedExceptionAction<ClassLoader>()
+ {
+ public ClassLoader run()
+ throws PrivilegedActionException
+ {
+ return Thread.currentThread()
+ .getContextClassLoader();
+ }
+ });
+ return cl;
+ }
+ catch (PrivilegedActionException pae)
+ {
+ throw new FacesException(pae);
+ }
+ }
+ else
+ {
+ return Thread.currentThread().getContextClassLoader();
+ }
+ }
+
+ private String getLibraryVersion(String path)
+ {
+ ExternalContext context = FacesContext.getCurrentInstance()
+ .getExternalContext();
+
+ String libraryVersion = null;
+ Set<String> libraryPaths = context.getResourcePaths("/" + path);
+ if (null != libraryPaths && !libraryPaths.isEmpty())
+ {
+ // Look in the libraryPaths for versioned libraries.
+ // If one or more versioned libraries are found, take
+ // the one with the "highest" version number as the value
+ // of libraryVersion. If no versioned libraries
+ // are found, let libraryVersion remain null.
+
+ for (String libraryPath : libraryPaths)
+ {
+ String version = libraryPath.substring(path.length());
+
+ if (VERSION_CHECKER.matcher(version).matches())
+ {
+ version = version.substring(1, version.length() - 1);
+ if (libraryVersion == null)
+ {
+ libraryVersion = version;
+ }
+ else if (compareVersion(libraryVersion, version) < 0)
+ {
+ libraryVersion = version;
+ }
+ }
+ }
+ }
+ return libraryVersion;
+ }
+
+ private int compareVersion(String s1, String s2)
+ {
+ int n1 = 0;
+ int n2 = 0;
+ String o1 = s1;
+ String o2 = s2;
+
+ boolean p1 = true;
+ boolean p2 = true;
+
+ while (n1 == n2 && (p1 || p2))
+ {
+ int i1 = o1.indexOf('_');
+ int i2 = o2.indexOf('_');
+ if (i1 < 0)
+ {
+ if (o1.length() > 0)
+ {
+ p1 = false;
+ n1 = Integer.valueOf(o1);
+ o1 = "";
+ }
+ else
+ {
+ p1 = false;
+ n1 = 0;
+ }
+ }
+ else
+ {
+ n1 = Integer.valueOf(o1.substring(0, i1));
+ o1 = o1.substring(i1 + 1);
+ }
+ if (i2 < 0)
+ {
+ if (o2.length() > 0)
+ {
+ p2 = false;
+ n2 = Integer.valueOf(o2);
+ o2 = "";
+ }
+ else
+ {
+ p2 = false;
+ n2 = 0;
+ }
+ }
+ else
+ {
+ n2 = Integer.valueOf(o2.substring(0, i2));
+ o2 = o2.substring(i2 + 1);
+ }
+ }
+
+ if (n1 == n2)
+ {
+ return s1.length() - s2.length();
+ }
+ return n1 - n2;
+ }
+
+ private String getResourceVersion(String path)
+ {
+ ExternalContext context = FacesContext.getCurrentInstance()
+ .getExternalContext();
+ String resourceVersion = null;
+ Set<String> resourcePaths = context.getResourcePaths("/" + path);
+
+ if (null != resourcePaths && !resourcePaths.isEmpty())
+ {
+ // resourceVersion = // execute the comment
+ // Look in the resourcePaths for versioned resources.
+ // If one or more versioned resources are found, take
+ // the one with the "highest" version number as the value
+ // of resourceVersion. If no versioned libraries
+ // are found, let resourceVersion remain null.
+ for (String resourcePath : resourcePaths)
+ {
+ String version = resourcePath.substring(path.length());
+
+ if (RESOURCE_VERSION_CHECKER.matcher(version).matches())
+ {
+ version = version.substring(1, version.lastIndexOf('.'));
+ if (resourceVersion == null)
+ {
+ resourceVersion = version;
+ }
+ else if (compareVersion(resourceVersion, version) < 0)
+ {
+ resourceVersion = version;
+ }
+ }
+ }
+ }
+ return resourceVersion;
+ }
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/mock/visit/FullVisitContext.java b/test30/src/main/java/org/apache/myfaces/test/mock/visit/FullVisitContext.java
new file mode 100644
index 0000000..c092381
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/mock/visit/FullVisitContext.java
@@ -0,0 +1,148 @@
+/*
+ * 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.myfaces.test.mock.visit;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.Set;
+
+import javax.faces.component.NamingContainer;
+import javax.faces.component.UIComponent;
+import javax.faces.component.visit.VisitCallback;
+import javax.faces.component.visit.VisitContext;
+import javax.faces.component.visit.VisitHint;
+import javax.faces.component.visit.VisitResult;
+import javax.faces.context.FacesContext;
+
+/**
+ * <p>A VisitContext implementation that is
+ * used when performing a full component tree visit.</p>
+ *
+ * @author Werner Punz, Blake Sullivan (latest modification by $Author: matzew $)
+ * @version $Rev: 885739 $ $Date: 2009-12-01 06:27:24 -0500 (Mar, 01 Dic 2009) $
+ * @since 1.0.0
+ */
+public class FullVisitContext extends VisitContext
+{
+
+ /**
+ * Creates a FullVisitorContext instance.
+ * @param facesContext the FacesContext for the current request
+ * @throws NullPointerException if {@code facesContext}
+ * is {@code null}
+ */
+ public FullVisitContext(FacesContext facesContext)
+ {
+ this(facesContext, null);
+ }
+
+ /**
+ * Creates a FullVisitorContext instance with the specified
+ * hints.
+ *
+ * @param facesContext the FacesContext for the current request
+ * @param hints a the VisitHints for this visit
+ * @param phaseId PhaseId, if any that visit is ocurring under
+ * @throws NullPointerException if {@code facesContext}
+ * is {@code null}
+ * @throws IllegalArgumentException if the phaseId is specified and
+ * hints does not contain VisitHint.EXECUTE_LIFECYCLE
+ */
+ public FullVisitContext(FacesContext facesContext, Set<VisitHint> hints)
+ {
+ if (facesContext == null)
+ {
+ throw new NullPointerException();
+ }
+
+ _facesContext = facesContext;
+
+ // Copy and store hints - ensure unmodifiable and non-empty
+ EnumSet<VisitHint> hintsEnumSet = ((hints == null) || (hints.isEmpty())) ? EnumSet
+ .noneOf(VisitHint.class)
+ : EnumSet.copyOf(hints);
+
+ _hints = Collections.unmodifiableSet(hintsEnumSet);
+ }
+
+ /**
+ * @see VisitContext#getFacesContext VisitContext.getFacesContext()
+ */
+ @Override
+ public FacesContext getFacesContext()
+ {
+ return _facesContext;
+ }
+
+ /**
+ * @see VisitContext#getIdsToVisit VisitContext.getIdsToVisit()
+ */
+ @Override
+ public Collection<String> getIdsToVisit()
+ {
+ // We always visits all ids
+ return ALL_IDS;
+ }
+
+ /**
+ * @see VisitContext#getSubtreeIdsToVisit VisitContext.getSubtreeIdsToVisit()
+ */
+ @Override
+ public Collection<String> getSubtreeIdsToVisit(UIComponent component)
+ {
+ // Make sure component is a NamingContainer
+ if (!(component instanceof NamingContainer))
+ {
+ throw new IllegalArgumentException(
+ "Component is not a NamingContainer: " + component);
+ }
+
+ // We always visits all ids
+ return ALL_IDS;
+ }
+
+ /**
+ * @see VisitContext#getHints VisitContext.getHints
+ */
+ @Override
+ public Set<VisitHint> getHints()
+ {
+ return _hints;
+ }
+
+ /**
+ * @see VisitContext#invokeVisitCallback VisitContext.invokeVisitCallback()
+ */
+ @Override
+ public VisitResult invokeVisitCallback(UIComponent component,
+ VisitCallback callback)
+ {
+ // Nothing interesting here - just invoke the callback.
+ // (PartialVisitContext.invokeVisitCallback() does all of the
+ // interesting work.)
+ return callback.visit(this, component);
+ }
+
+ // The FacesContext for this request
+ private final FacesContext _facesContext;
+
+ // Our visit hints
+ private final Set<VisitHint> _hints;
+}
\ No newline at end of file
diff --git a/test30/src/main/java/org/apache/myfaces/test/mock/visit/MockVisitCallback.java b/test30/src/main/java/org/apache/myfaces/test/mock/visit/MockVisitCallback.java
new file mode 100644
index 0000000..052e38a
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/mock/visit/MockVisitCallback.java
@@ -0,0 +1,41 @@
+/*
+ * 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.myfaces.test.mock.visit;
+
+import javax.faces.component.UIComponent;
+import javax.faces.component.visit.VisitCallback;
+import javax.faces.component.visit.VisitContext;
+import javax.faces.component.visit.VisitResult;
+
+/**
+ * <p>Mock implementation of <code>VisitCallback</code>.</p>
+ * <p/>
+ * $Id$
+ *
+ * @since 1.0.0
+ */
+public class MockVisitCallback implements VisitCallback
+{
+
+ public VisitResult visit(VisitContext context, UIComponent target)
+ {
+ return VisitResult.ACCEPT;
+ }
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/mock/visit/MockVisitContext.java b/test30/src/main/java/org/apache/myfaces/test/mock/visit/MockVisitContext.java
new file mode 100644
index 0000000..7e5b43c
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/mock/visit/MockVisitContext.java
@@ -0,0 +1,106 @@
+/*
+ * 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.myfaces.test.mock.visit;
+
+import javax.faces.component.NamingContainer;
+import javax.faces.component.UIComponent;
+import javax.faces.component.visit.VisitCallback;
+import javax.faces.component.visit.VisitContext;
+import javax.faces.component.visit.VisitHint;
+import javax.faces.component.visit.VisitResult;
+import javax.faces.context.FacesContext;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.Set;
+
+/**
+ * <p>Mock implementation of <code>VisitContext</code>.</p>
+ * <p/>
+ * $Id$
+ *
+ * @since 1.0.0
+ */
+public class MockVisitContext extends VisitContext
+{
+
+ private final FacesContext _facesContext;
+ private final Set<VisitHint> _hints;
+
+ public MockVisitContext(FacesContext facesContext)
+ {
+ this(facesContext, null);
+ }
+
+ public MockVisitContext(FacesContext facesContext, Set<VisitHint> hints)
+ {
+ if (facesContext == null)
+ {
+ throw new NullPointerException();
+ }
+
+ _facesContext = facesContext;
+
+ // Copy and store hints - ensure unmodifiable and non-empty
+ EnumSet<VisitHint> hintsEnumSet = ((hints == null) || (hints.isEmpty())) ? EnumSet
+ .noneOf(VisitHint.class)
+ : EnumSet.copyOf(hints);
+
+ _hints = Collections.unmodifiableSet(hintsEnumSet);
+ }
+
+ @Override
+ public FacesContext getFacesContext()
+ {
+ return _facesContext;
+ }
+
+ @Override
+ public Set<VisitHint> getHints()
+ {
+ return _hints;
+ }
+
+ @Override
+ public Collection<String> getIdsToVisit()
+ {
+ return ALL_IDS;
+ }
+
+ @Override
+ public Collection<String> getSubtreeIdsToVisit(UIComponent component)
+ {
+ // Make sure component is a NamingContainer
+ if (!(component instanceof NamingContainer))
+ {
+ throw new IllegalArgumentException(
+ "Component is not a NamingContainer: " + component);
+ }
+
+ return ALL_IDS;
+ }
+
+ @Override
+ public VisitResult invokeVisitCallback(UIComponent component,
+ VisitCallback callback)
+ {
+ return callback.visit(this, component);
+ }
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/mock/visit/MockVisitContextFactory.java b/test30/src/main/java/org/apache/myfaces/test/mock/visit/MockVisitContextFactory.java
new file mode 100644
index 0000000..8d48013
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/mock/visit/MockVisitContextFactory.java
@@ -0,0 +1,53 @@
+/*
+ * 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.myfaces.test.mock.visit;
+
+import java.util.Collection;
+import java.util.Set;
+
+import javax.faces.component.visit.VisitContext;
+import javax.faces.component.visit.VisitContextFactory;
+import javax.faces.component.visit.VisitHint;
+import javax.faces.context.FacesContext;
+
+/**
+ * <p>Mock implementation of <code>VisitContextFactory</code>.</p>
+ * <p/>
+ * $Id$
+ *
+ * @since 1.0.0
+ */
+public class MockVisitContextFactory extends VisitContextFactory
+{
+
+ @Override
+ public VisitContext getVisitContext(FacesContext context,
+ Collection<String> ids, Set<VisitHint> hints)
+ {
+ if (ids == null || ids.isEmpty())
+ {
+ return new FullVisitContext(context, hints);
+ }
+ else
+ {
+ return new PartialVisitContext(context, ids, hints);
+ }
+ }
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/mock/visit/PartialVisitContext.java b/test30/src/main/java/org/apache/myfaces/test/mock/visit/PartialVisitContext.java
new file mode 100644
index 0000000..54e04c0
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/mock/visit/PartialVisitContext.java
@@ -0,0 +1,469 @@
+/*
+ * 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.myfaces.test.mock.visit;
+
+import java.util.AbstractCollection;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+import javax.faces.component.NamingContainer;
+import javax.faces.component.UIComponent;
+import javax.faces.component.UINamingContainer;
+import javax.faces.component.visit.VisitCallback;
+import javax.faces.component.visit.VisitContext;
+import javax.faces.component.visit.VisitHint;
+import javax.faces.component.visit.VisitResult;
+import javax.faces.context.FacesContext;
+
+/**
+ * <p>A VisitContext implementation that is
+ * used when performing a partial component tree visit.</p>
+ *
+ * @author Werner Punz, Blake Sullivan (latest modification by $Author: lu4242 $)
+ * @version $Rev: 949094 $ $Date: 2010-05-27 22:59:10 -0500 (Jue, 27 May 2010) $
+ * @since 1.0.0
+ */
+public class PartialVisitContext extends VisitContext
+{
+
+ /**
+ * Creates a PartialVisitorContext instance.
+ * @param facesContext the FacesContext for the current request
+ * @param clientIds the client ids of the components to visit
+ * @throws NullPointerException if {@code facesContext}
+ * is {@code null}
+ */
+ public PartialVisitContext(FacesContext facesContext,
+ Collection<String> clientIds)
+ {
+ this(facesContext, clientIds, null);
+ }
+
+ /**
+ * Creates a PartialVisitorContext instance with the specified hints.
+ * @param facesContext the FacesContext for the current request
+ * @param clientIds the client ids of the components to visit
+ * @param hints a the VisitHints for this visit
+ * @throws NullPointerException if {@code facesContext}
+ * is {@code null}
+ * @throws IllegalArgumentException if the phaseId is specified and
+ * hints does not contain VisitHint.EXECUTE_LIFECYCLE
+ */
+ public PartialVisitContext(FacesContext facesContext,
+ Collection<String> clientIds, Set<VisitHint> hints)
+ {
+ if (facesContext == null)
+ {
+ throw new NullPointerException();
+ }
+
+ _facesContext = facesContext;
+
+ // Copy the client ids into a HashSet to allow for quick lookups.
+ Set<String> clientIdSet = (clientIds == null) ? new HashSet<String>()
+ : new HashSet<String>(clientIds);
+
+ // Initialize our various collections
+ // We maintain 4 collections:
+ //
+ // 1. clientIds: contains all of the client ids to visit
+ // 2. ids: contains just ids (not client ids) to visit.
+ // We use this to optimize our check to see whether a
+ // particular component is in the visit set (ie. to
+ // avoid having to compute the client id).
+ // 3. subtreeClientIds: contains client ids to visit broken
+ // out by naming container subtree. (Needed by
+ // getSubtreeIdsToVisit()).
+ // 4. unvisitedClientIds: contains the client ids to visit that
+ // have not yet been visited.
+ //
+ // We populate these now.
+ //
+ // Note that we use default HashSet/Map initial capacities, though
+ // perhaps we could pick more intelligent defaults.
+
+ // Initialize unvisitedClientIds collection
+ _unvisitedClientIds = new HashSet<String>();
+
+ // Initialize ids collection
+ _ids = new HashSet<String>();
+
+ // Intialize subtreeClientIds collection
+ _subtreeClientIds = new HashMap<String, Collection<String>>();
+
+ // Initialize the clientIds collection. Note that we proxy
+ // this collection so that we can trap adds/removes and sync
+ // up all of the other collections.
+ _clientIds = new CollectionProxy<String>(new HashSet<String>());
+
+ // Finally, populate the clientIds collection. This has the
+ // side effect of populating all of the other collections.
+ _clientIds.addAll(clientIdSet);
+
+ // Copy and store hints - ensure unmodifiable and non-empty
+ EnumSet<VisitHint> hintsEnumSet = ((hints == null) || (hints.isEmpty())) ? EnumSet
+ .noneOf(VisitHint.class)
+ : EnumSet.copyOf(hints);
+
+ _hints = Collections.unmodifiableSet(hintsEnumSet);
+ }
+
+ /**
+ * @see VisitContext#getFacesContext VisitContext.getFacesContext()
+ */
+ @Override
+ public FacesContext getFacesContext()
+ {
+ return _facesContext;
+ }
+
+ /**
+ * @see VisitContext#getHints VisitContext.getHints
+ */
+ @Override
+ public Set<VisitHint> getHints()
+ {
+ return _hints;
+ }
+
+ /**
+ * @see VisitContext#getIdsToVisit VisitContext.getIdsToVisit()
+ */
+ @Override
+ public Collection<String> getIdsToVisit()
+ {
+ // We just return our clientIds collection. This is
+ // the modifiable (but proxied) collection of all of
+ // the client ids to visit.
+ return _clientIds;
+ }
+
+ /**
+ * @see VisitContext#getSubtreeIdsToVisit VisitContext.getSubtreeIdsToVisit()
+ */
+ @Override
+ public Collection<String> getSubtreeIdsToVisit(UIComponent component)
+ {
+ // Make sure component is a NamingContainer
+ if (!(component instanceof NamingContainer))
+ {
+ throw new IllegalArgumentException(
+ "Component is not a NamingContainer: " + component);
+ }
+
+ String clientId = component.getClientId(getFacesContext());
+ Collection<String> ids = _subtreeClientIds.get(clientId);
+
+ if (ids == null)
+ {
+ return Collections.emptyList();
+ }
+ else
+ {
+ return Collections.unmodifiableCollection(ids);
+ }
+ }
+
+ /**
+ * @see VisitContext#invokeVisitCallback VisitContext.invokeVisitCallback()
+ */
+ @Override
+ public VisitResult invokeVisitCallback(UIComponent component,
+ VisitCallback callback)
+ {
+ // First sure that we should visit this component - ie.
+ // that this component is represented in our id set.
+ String clientId = _getVisitId(component);
+
+ if (clientId == null)
+ {
+ // Not visiting this component, but allow visit to
+ // continue into this subtree in case we've got
+ // visit targets there.
+ return VisitResult.ACCEPT;
+ }
+
+ // If we made it this far, the component matches one of
+ // client ids, so perform the visit.
+ VisitResult result = callback.visit(this, component);
+
+ // Remove the component from our "unvisited" collection
+ _unvisitedClientIds.remove(clientId);
+
+ // If the unvisited collection is now empty, we are done.
+ // Return VisitResult.COMPLETE to terminate the visit.
+ if (_unvisitedClientIds.isEmpty())
+ {
+ return VisitResult.COMPLETE;
+ }
+ else
+ {
+ // Otherwise, just return the callback's result
+ return result;
+ }
+ }
+
+ // Called by CollectionProxy to notify PartialVisitContext that
+ // an new id has been added.
+ private void _idAdded(String clientId)
+ {
+ // An id to visit has been added, update our other
+ // collections to reflect this.
+
+ // Update the ids collection
+ _ids.add(_getIdFromClientId(clientId));
+
+ // Update the unvisited ids collection
+ _unvisitedClientIds.add(clientId);
+
+ // Update the subtree ids collection
+ _addSubtreeClientId(clientId);
+ }
+
+ // Called by CollectionProxy to notify PartialVisitContext that
+ // an id has been removed
+ private void _idRemoved(String clientId)
+ {
+ // An id to visit has been removed, update our other
+ // collections to reflect this. Note that we don't
+ // update the ids collection, since we ids (non-client ids)
+ // may not be unique.
+
+ // Update the unvisited ids collection
+ _unvisitedClientIds.remove(clientId);
+
+ // Update the subtree ids collection
+ _removeSubtreeClientId(clientId);
+ }
+
+ // Tests whether the specified component should be visited.
+ // If so, returns its client id. If not, returns null.
+ private String _getVisitId(UIComponent component)
+ {
+ // We first check to see whether the component's id
+ // is in our id collection. We do this before checking
+ // for the full client id because getting the full client id
+ // is more expensive than just getting the local id.
+ String id = component.getId();
+
+ if ((id != null) && !_ids.contains(id))
+ {
+ return null;
+ }
+
+ // The id was a match - now check the client id.
+ // note that client id should never be null (should be
+ // generated even if id is null, so asserting this.)
+ String clientId = component.getClientId(getFacesContext());
+ assert (clientId != null);
+
+ return _clientIds.contains(clientId) ? clientId : null;
+ }
+
+ // Converts an client id into a plain old id by ripping
+ // out the trailing id segmetn.
+ private String _getIdFromClientId(String clientId)
+ {
+ final char separator = UINamingContainer
+ .getSeparatorChar(_facesContext);
+ int lastIndex = clientId.lastIndexOf(separator);
+
+ String id = null;
+
+ if (lastIndex < 0)
+ {
+ id = clientId;
+ }
+ else if (lastIndex < (clientId.length() - 1))
+ {
+ id = clientId.substring(lastIndex + 1);
+ }
+ else
+ {
+ // TODO log warning for trailing colon case
+ }
+
+ return id;
+ }
+
+ // Given a single client id, populate the subtree map with all possible
+ // subtree client ids
+ private void _addSubtreeClientId(String clientId)
+ {
+ // Loop over the client id and find the substring corresponding to
+ // each ancestor NamingContainer client id. For each ancestor
+ // NamingContainer, add an entry into the map for the full client
+ // id.
+ final char separator = UINamingContainer
+ .getSeparatorChar(_facesContext);
+
+ int length = clientId.length();
+
+ for (int i = 0; i < length; i++)
+ {
+ if (clientId.charAt(i) == separator)
+ {
+ // We found an ancestor NamingContainer client id - add
+ // an entry to the map.
+ String namingContainerClientId = clientId.substring(0, i);
+
+ // Check to see whether we've already ids under this
+ // NamingContainer client id. If not, create the
+ // Collection for this NamingContainer client id and
+ // stash it away in our map
+ Collection<String> c = _subtreeClientIds
+ .get(namingContainerClientId);
+
+ if (c == null)
+ {
+ // TODO: smarter initial size?
+ c = new ArrayList<String>();
+ _subtreeClientIds.put(namingContainerClientId, c);
+ }
+
+ // Stash away the client id
+ c.add(clientId);
+ }
+ }
+ }
+
+ // Given a single client id, remove any entries corresponding
+ // entries from our subtree collections
+ private void _removeSubtreeClientId(String clientId)
+ {
+ // Loop through each entry in the map and check to see whether
+ // the client id to remove should be contained in the corresponding
+ // collection - ie. whether the key (the NamingContainer client id)
+ // is present at the start of the client id to remove.
+ for (String key : _subtreeClientIds.keySet())
+ {
+ if (clientId.startsWith(key))
+ {
+ // If the clientId starts with the key, we should
+ // have an entry for this clientId in the corresponding
+ // collection. Remove it.
+ Collection<String> ids = _subtreeClientIds.get(key);
+ ids.remove(clientId);
+ }
+ }
+ }
+
+ // Little proxy collection implementation. We proxy the id
+ // collection so that we can detect modifications and update
+ // our internal state when ids to visit are added or removed.
+ private class CollectionProxy<E extends String> extends
+ AbstractCollection<E>
+ {
+ private CollectionProxy(Collection<E> wrapped)
+ {
+ _wrapped = wrapped;
+ }
+
+ @Override
+ public int size()
+ {
+ return _wrapped.size();
+ }
+
+ @Override
+ public Iterator<E> iterator()
+ {
+ return new IteratorProxy<E>(_wrapped.iterator());
+ }
+
+ @Override
+ public boolean add(E o)
+ {
+ boolean added = _wrapped.add(o);
+
+ if (added)
+ {
+ _idAdded(o);
+ }
+
+ return added;
+ }
+
+ private final Collection<E> _wrapped;
+ }
+
+ // Little proxy iterator implementation used by CollectionProxy
+ // so that we can catch removes.
+ private class IteratorProxy<E extends String> implements Iterator<E>
+ {
+ private IteratorProxy(Iterator<E> wrapped)
+ {
+ _wrapped = wrapped;
+ }
+
+ public boolean hasNext()
+ {
+ return _wrapped.hasNext();
+ }
+
+ public E next()
+ {
+ _current = _wrapped.next();
+
+ return _current;
+ }
+
+ public void remove()
+ {
+ if (_current != null)
+ {
+ _idRemoved(_current);
+ }
+
+ _wrapped.remove();
+ }
+
+ private final Iterator<E> _wrapped;
+
+ private E _current = null;
+ }
+
+ // The client ids to visit
+ private final Collection<String> _clientIds;
+
+ // The ids to visit
+ private final Collection<String> _ids;
+
+ // The client ids that have yet to be visited
+ private final Collection<String> _unvisitedClientIds;
+
+ // This map contains the information needed by getIdsToVisit().
+ // The keys in this map are NamingContainer client ids. The values
+ // are collections containing all of the client ids to visit within
+ // corresponding naming container.
+ private final Map<String, Collection<String>> _subtreeClientIds;
+
+ // The FacesContext for this request
+ private final FacesContext _facesContext;
+
+ // Our visit hints
+ private final Set<VisitHint> _hints;
+}
\ No newline at end of file
diff --git a/test30/src/main/java/org/apache/myfaces/test/runners/NamedRunner.java b/test30/src/main/java/org/apache/myfaces/test/runners/NamedRunner.java
new file mode 100644
index 0000000..4f9502e
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/runners/NamedRunner.java
@@ -0,0 +1,126 @@
+/*
+ * 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.myfaces.test.runners;
+
+import org.junit.runner.notification.RunNotifier;
+import org.junit.runners.BlockJUnit4ClassRunner;
+import org.junit.runners.model.FrameworkMethod;
+import org.junit.runners.model.InitializationError;
+
+/**
+ * A Junit 4 runner that stores the name of the method that runs. The methods marked with @After and @Before
+ * (but also the Test method itself) can request this name for which it is running.
+ *
+ * @author Rudy De Busscher
+ * @since 1.0.0
+ */
+public class NamedRunner extends BlockJUnit4ClassRunner
+{
+
+ /** The Constant PREFIX_KEY. */
+ private static final String PREFIX_KEY = "ClassLoaderRunner_TestMethodName_";
+
+ /** The Constant NO_NAME. */
+ private static final String NO_NAME = "null";
+
+ /**
+ * Instantiates a new named runner.
+ *
+ * @param klass the klass
+ *
+ * @throws InitializationError the initialization error
+ */
+ public NamedRunner(Class<?> klass) throws InitializationError
+ {
+ super(klass);
+ }
+
+ @Override
+ protected void runChild(FrameworkMethod method, RunNotifier notifier)
+
+ {
+ storeTestMethodName(method.getName());
+ super.runChild(method, notifier);
+ removeTestMethodName();
+ }
+
+ /**
+ * Gets the test method name.
+ *
+ * @return the test method name
+ */
+ public static String getTestMethodName()
+ {
+ return retrieveTestMethodName();
+ }
+
+ /**
+ * Retrieve test method name.
+ *
+ * @return the string
+ */
+ private static String retrieveTestMethodName()
+ {
+ // We can't use a ThreadLocal variable in the case the TestPerClassLoader runner is used. Then this
+ // Method is accessed from another classloader and thus reinitialized variables.
+ String result = null;
+ String storedName = System.getProperty(getKey());
+ if (!NO_NAME.equals(storedName))
+ {
+ result = storedName;
+ }
+ return result;
+ }
+
+ /**
+ * Removes the test method name.
+ */
+ private static void removeTestMethodName()
+ {
+ // We can't use a ThreadLocal variable in the case the TestPerClassLoader runner is used. Then this
+ // Method is accessed from another classloader and thus reinitialized variables.
+ System.setProperty(getKey(), NO_NAME);
+
+ }
+
+ /**
+ * Store test method name.
+ *
+ * @param name the name
+ */
+ private static void storeTestMethodName(String name)
+ {
+
+ // We can't use a ThreadLocal variable in the case the TestPerClassLoader runner is used. Then this
+ // Method is accessed from another classloader and thus reinitialized variables.
+ System.setProperty(getKey(), name);
+ }
+
+ /**
+ * Gets the key.
+ *
+ * @return the key
+ */
+ private static String getKey()
+ {
+ StringBuffer buffer = new StringBuffer();
+ buffer.append(PREFIX_KEY).append(Thread.currentThread().getName());
+ return buffer.toString();
+ }
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/runners/TestClassLoader.java b/test30/src/main/java/org/apache/myfaces/test/runners/TestClassLoader.java
new file mode 100644
index 0000000..86ee8fe
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/runners/TestClassLoader.java
@@ -0,0 +1,359 @@
+/*
+ * 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.myfaces.test.runners;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.Enumeration;
+import java.util.Properties;
+import java.util.StringTokenizer;
+import java.util.Vector;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+
+/**
+ * A Classloader that delegates the loading of the classes to the parent if it is in the
+ * excluded list or does it itself otherwise.
+ * Useful for a system that sets up a classloader per Test system.
+ *
+ * @author Rudy De Busscher
+ * @since 1.0.0
+ */
+public class TestClassLoader extends ClassLoader
+{
+ /** scanned class path */
+ private Vector<String> fPathItems;
+ /** default excluded paths */
+ private String[] defaultExclusions = { "junit.framework.",
+ "junit.extensions.", "junit.runner." };
+ /** name of excluded properties file */
+ static final String EXCLUDED_FILE = "excluded.properties";
+ /** excluded paths */
+ private Vector<String> fExcluded;
+
+ /**
+ * Constructs a TestCaseLoader. It scans the class path
+ * and the excluded package paths
+ */
+ public TestClassLoader()
+ {
+ this(System.getProperty("java.class.path"));
+ }
+
+ /**
+ * Constructs a TestCaseLoader. It scans the class path
+ * and the excluded package paths
+ */
+ public TestClassLoader(String classPath)
+ {
+ scanPath(classPath);
+ readExcludedPackages();
+ }
+
+ private void scanPath(String classPath)
+ {
+ String separator = System.getProperty("path.separator");
+ fPathItems = new Vector<String>(10);
+ StringTokenizer st = new StringTokenizer(classPath, separator);
+ while (st.hasMoreTokens())
+ {
+ fPathItems.addElement(st.nextToken());
+ }
+ }
+
+ @Override
+ public URL getResource(String name)
+ {
+ return ClassLoader.getSystemResource(name);
+ }
+
+ @Override
+ public InputStream getResourceAsStream(String name)
+ {
+ return ClassLoader.getSystemResourceAsStream(name);
+ }
+
+ /**
+ * Checks if path is excluded.
+ *
+ * @param name the name
+ *
+ * @return true, if is excluded
+ */
+ public boolean isExcluded(String name)
+ {
+ for (int i = 0; i < fExcluded.size(); i++)
+ {
+ if (name.startsWith((String) fExcluded.elementAt(i)))
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public synchronized Class loadClass(String name, boolean resolve)
+ throws ClassNotFoundException
+ {
+
+ Class c = findLoadedClass(name);
+ if (c != null)
+ {
+ return c;
+ }
+ //
+ // Delegate the loading of excluded classes to the
+ // standard class loader.
+ //
+ if (isExcluded(name))
+ {
+ try
+ {
+ c = findSystemClass(name);
+ return c;
+ }
+ catch (ClassNotFoundException e)
+ {
+ // keep searching
+ }
+ }
+ if (c == null)
+ {
+ byte[] data = lookupClassData(name);
+ if (data == null)
+ {
+ throw new ClassNotFoundException();
+ }
+ c = defineClass(name, data, 0, data.length);
+ }
+ if (resolve)
+ {
+ resolveClass(c);
+ }
+ return c;
+ }
+
+ /**
+ * Lookup class data in one of the classpath elements configured.
+ *
+ * @param className the class name
+ *
+ * @return the bytes making up the class
+ *
+ * @throws ClassNotFoundException when class can't be found on the classpath elements.
+ */
+ private byte[] lookupClassData(String className)
+ throws ClassNotFoundException
+ {
+ byte[] data = null;
+ for (int i = 0; i < fPathItems.size(); i++)
+ {
+ String path = (String) fPathItems.elementAt(i);
+ String fileName = className.replace('.', '/') + ".class";
+ if (isJar(path))
+ {
+ data = loadJarData(path, fileName);
+ }
+ else
+ {
+ data = loadFileData(path, fileName);
+ }
+ if (data != null)
+ {
+ return data;
+ }
+ }
+ throw new ClassNotFoundException(className);
+ }
+
+ /**
+ * Checks if is jar.
+ *
+ * @param pathEntry the path entry
+ *
+ * @return true, if is jar
+ */
+ boolean isJar(String pathEntry)
+ {
+ return pathEntry.endsWith(".jar") || pathEntry.endsWith(".zip");
+ }
+
+ /**
+ * Load class bytes from file.
+ *
+ * @param path the path
+ * @param fileName the file name
+ *
+ * @return the bytes making up the class
+ */
+ private byte[] loadFileData(String path, String fileName)
+ {
+ File file = new File(path, fileName);
+ if (file.exists())
+ {
+ return getClassData(file);
+ }
+ return null;
+ }
+
+ private byte[] getClassData(File f)
+ {
+ try
+ {
+ FileInputStream stream = new FileInputStream(f);
+ ByteArrayOutputStream out = new ByteArrayOutputStream(1000);
+ byte[] b = new byte[1000];
+ int n;
+ while ((n = stream.read(b)) != -1)
+ {
+ out.write(b, 0, n);
+ }
+ stream.close();
+ out.close();
+ return out.toByteArray();
+
+ }
+ catch (IOException e)
+ {
+ }
+ return null;
+ }
+
+ /**
+ * Load class bytes from jar.
+ *
+ * @param path the path
+ * @param fileName the file name
+ *
+ * @return the bytes making up the class
+ */
+ private byte[] loadJarData(String path, String fileName)
+ {
+ ZipFile zipFile = null;
+ InputStream stream = null;
+ File archive = new File(path);
+ if (!archive.exists())
+ {
+ return null;
+ }
+ try
+ {
+ zipFile = new ZipFile(archive);
+ }
+ catch (IOException io)
+ {
+ return null;
+ }
+ ZipEntry entry = zipFile.getEntry(fileName);
+ if (entry == null)
+ {
+ return null;
+ }
+ int size = (int) entry.getSize();
+ try
+ {
+ stream = zipFile.getInputStream(entry);
+ byte[] data = new byte[size];
+ int pos = 0;
+ while (pos < size)
+ {
+ int n = stream.read(data, pos, data.length - pos);
+ pos += n;
+ }
+ zipFile.close();
+ return data;
+ }
+ catch (IOException e)
+ {
+ }
+ finally
+ {
+ try
+ {
+ if (stream != null)
+ {
+ stream.close();
+ }
+ }
+ catch (IOException e)
+ {
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Read excluded packages for which classes are read by the parent class loader.
+ */
+ private void readExcludedPackages()
+ {
+ fExcluded = new Vector<String>(10);
+ for (int i = 0; i < defaultExclusions.length; i++)
+ {
+ fExcluded.addElement(defaultExclusions[i]);
+ }
+
+ InputStream is = getClass().getResourceAsStream(EXCLUDED_FILE);
+ if (is == null)
+ {
+ return;
+ }
+ Properties p = new Properties();
+ try
+ {
+ p.load(is);
+ }
+ catch (IOException e)
+ {
+ return;
+ }
+ finally
+ {
+ try
+ {
+ is.close();
+ }
+ catch (IOException e)
+ {
+ }
+ }
+ for (Enumeration e = p.propertyNames(); e.hasMoreElements();)
+ {
+ String key = (String) e.nextElement();
+ if (key.startsWith("excluded."))
+ {
+ String path = p.getProperty(key);
+ path = path.trim();
+ if (path.endsWith("*"))
+ {
+ path = path.substring(0, path.length() - 1);
+ }
+ if (path.length() > 0)
+ {
+ fExcluded.addElement(path);
+ }
+ }
+ }
+ }
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/runners/TestPerClassLoaderRunner.java b/test30/src/main/java/org/apache/myfaces/test/runners/TestPerClassLoaderRunner.java
new file mode 100644
index 0000000..8f2d047
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/runners/TestPerClassLoaderRunner.java
@@ -0,0 +1,299 @@
+/*
+ * 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.myfaces.test.runners;
+
+import java.io.File;
+import java.io.IOException;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+import java.net.MalformedURLException;
+import java.util.List;
+import java.util.StringTokenizer;
+import java.util.Vector;
+import java.util.jar.Attributes;
+import java.util.jar.Manifest;
+import java.util.logging.Logger;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.internal.runners.statements.Fail;
+import org.junit.internal.runners.statements.RunAfters;
+import org.junit.internal.runners.statements.RunBefores;
+import org.junit.runners.model.FrameworkMethod;
+import org.junit.runners.model.InitializationError;
+import org.junit.runners.model.Statement;
+import org.junit.runners.model.TestClass;
+
+/**
+ * A Junit 4 runner that executes each Test method with a new Custom classloader so
+ * that all variables,
+ * also the final ones, are reinitialized.
+ *
+ * @author Rudy De Busscher
+ * @since 1.0.0
+ */
+public class TestPerClassLoaderRunner extends NamedRunner
+{
+ private static final Logger LOGGER = Logger
+ .getLogger(TestPerClassLoaderRunner.class.getName());
+
+ // The classpath is needed because the custom class loader looks there to find the classes.
+ private static String classPath;
+ private static boolean classPathDetermined = false;
+
+ // Some data related to the class from the custom class loader.
+ private TestClass testClassFromClassLoader;
+ private Object beforeFromClassLoader;
+ private Object afterFromClassLoader;
+
+ /**
+ * Instantiates a new test per class loader runner.
+ *
+ * @param klass the klass
+ *
+ * @throws InitializationError the initialization error
+ */
+ public TestPerClassLoaderRunner(Class<?> klass) throws InitializationError
+ {
+ super(klass);
+ }
+
+ @Override
+ protected Object createTest() throws Exception
+ {
+ // Need an instance now from the class loaded by the custom loader.
+ return testClassFromClassLoader.getJavaClass().newInstance();
+ }
+
+ /**
+ * Load classes (TestCase, @Before and @After with custom class loader.
+ *
+ * @throws ClassNotFoundException the class not found exception
+ */
+ private void loadClassesWithCustomClassLoader()
+ throws ClassNotFoundException
+ {
+ ClassLoader classLoader;
+
+ // We need the classpath so that our custom loader can search for the requested classes.
+ String testPath = getClassPath();
+ if (testPath != null)
+ {
+ classLoader = new TestClassLoader(testPath);
+ }
+
+ else
+ {
+ classLoader = new TestClassLoader();
+
+ }
+
+ Thread.currentThread().setContextClassLoader(classLoader);
+ testClassFromClassLoader = new TestClass(classLoader
+ .loadClass(getTestClass().getJavaClass().getName()));
+ // See withAfters and withBefores for the reason.
+ beforeFromClassLoader = classLoader.loadClass(Before.class.getName());
+ afterFromClassLoader = classLoader.loadClass(After.class.getName());
+ }
+
+ @Override
+ protected Statement methodBlock(FrameworkMethod method)
+ {
+ FrameworkMethod newMethod = null;
+ try
+ {
+ // Need the class frmo the custom loader now, so lets load the class.
+ loadClassesWithCustomClassLoader();
+ // The method as parameter is frmo the original class and thus not found in our
+ // class loaded by the custom name (reflection is class loader sensitive)
+ // So find the same method but now in the class frmo the class Loader.
+ Method methodFromNewlyLoadedClass = testClassFromClassLoader
+ .getJavaClass().getMethod(method.getName());
+ newMethod = new FrameworkMethod(methodFromNewlyLoadedClass);
+ }
+ catch (ClassNotFoundException e)
+ {
+ // Show any problem nicely as a JUnit Test failure.
+ return new Fail(e);
+ }
+ catch (SecurityException e)
+ {
+ return new Fail(e);
+ }
+ catch (NoSuchMethodException e)
+ {
+ return new Fail(e);
+ }
+
+ // We can carry out the normal JUnit functionality with our newly discoverd method now.
+ return super.methodBlock(newMethod);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ protected Statement withAfters(FrameworkMethod method, Object target,
+ Statement statement)
+ {
+ // We now to need to search in the class from the custom loader.
+ //We also need to search with the annotation loaded by the custom class loader or otherwise
+ // we don't find any method.
+ List<FrameworkMethod> afters = testClassFromClassLoader
+ .getAnnotatedMethods((Class<? extends Annotation>) afterFromClassLoader);
+ return new RunAfters(statement, afters, target);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ protected Statement withBefores(FrameworkMethod method, Object target,
+ Statement statement)
+ {
+ // We now to need to search in the class from the custom loader.
+ //We also need to search with the annotation loaded by the custom class loader or otherwise
+ // we don't find any method.
+ List<FrameworkMethod> befores = testClassFromClassLoader
+ .getAnnotatedMethods((Class<? extends Annotation>) beforeFromClassLoader);
+ return new RunBefores(statement, befores, target);
+ }
+
+ /**
+ * Gets the class path. This value is cached in a static variable for performance reasons.
+ *
+ * @return the class path
+ */
+ private static String getClassPath()
+ {
+ if (classPathDetermined)
+ {
+ return classPath;
+ }
+
+ classPathDetermined = true;
+ // running from maven, we have the classpath in this property.
+ classPath = System.getProperty("surefire.test.class.path");
+ if (classPath != null)
+ {
+ return classPath;
+ }
+
+ // For a multi module project, running it from the top we have to find it using another way.
+ // We also need to set useSystemClassLoader=true in the POM so that we gets a jar with the classpath in it.
+ String booterClassPath = System.getProperty("java.class.path");
+ Vector<String> pathItems = null;
+ if (booterClassPath != null)
+ {
+ pathItems = scanPath(booterClassPath);
+ }
+ // Do we have just 1 entry as classpath which is a jar?
+ if (pathItems != null && pathItems.size() == 1
+ && isJar((String) pathItems.get(0)))
+ {
+ classPath = loadJarManifestClassPath((String) pathItems.get(0),
+ "META-INF/MANIFEST.MF");
+ }
+ return classPath;
+
+ }
+
+ /**
+ * Load jar manifest class path.
+ *
+ * @param path the path
+ * @param fileName the file name
+ *
+ * @return the string
+ */
+ private static String loadJarManifestClassPath(String path, String fileName)
+ {
+ File archive = new File(path);
+ if (!archive.exists())
+ {
+ return null;
+ }
+ ZipFile zipFile = null;
+
+ try
+ {
+ zipFile = new ZipFile(archive);
+ }
+ catch (IOException io)
+ {
+ return null;
+ }
+
+ ZipEntry entry = zipFile.getEntry(fileName);
+ if (entry == null)
+ {
+ return null;
+ }
+ try
+ {
+ Manifest mf = new Manifest();
+ mf.read(zipFile.getInputStream(entry));
+
+ return mf.getMainAttributes().getValue(Attributes.Name.CLASS_PATH)
+ .replaceAll(" ", System.getProperty("path.separator"))
+ .replaceAll("file:/", "");
+ }
+ catch (MalformedURLException e)
+ {
+ LOGGER.throwing("ClassLoaderTestSuite", "loadJarManifestClassPath",
+ e);
+ }
+ catch (IOException e)
+ {
+ LOGGER.throwing("ClassLoaderTestSuite", "loadJarManifestClassPath",
+ e);
+ }
+ return null;
+ }
+
+ /**
+ * Checks if is jar.
+ *
+ * @param pathEntry the path entry
+ *
+ * @return true, if is jar
+ */
+ private static boolean isJar(String pathEntry)
+ {
+ return pathEntry.endsWith(".jar") || pathEntry.endsWith(".zip");
+ }
+
+ /**
+ * Scan path for all directories.
+ *
+ * @param classPath the class path
+ *
+ * @return the vector< string>
+ */
+ private static Vector<String> scanPath(String classPath)
+ {
+ String separator = System.getProperty("path.separator");
+ Vector<String> pathItems = new Vector<String>(10);
+ StringTokenizer st = new StringTokenizer(classPath, separator);
+ while (st.hasMoreTokens())
+ {
+ pathItems.addElement(st.nextToken());
+ }
+ return pathItems;
+ }
+
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/util/Jsf11Utils.java b/test30/src/main/java/org/apache/myfaces/test/util/Jsf11Utils.java
new file mode 100644
index 0000000..cb3c6ff
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/util/Jsf11Utils.java
@@ -0,0 +1,30 @@
+/*
+ * 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.myfaces.test.util;
+
+public class Jsf11Utils
+{
+
+ public static void throwViewExpiredException(String viewId)
+ {
+ throw new RuntimeException(
+ "The expected view was not returned for the view identifier: " + viewId);
+ }
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/util/Jsf12Utils.java b/test30/src/main/java/org/apache/myfaces/test/util/Jsf12Utils.java
new file mode 100644
index 0000000..17d1803
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/util/Jsf12Utils.java
@@ -0,0 +1,39 @@
+/*
+ * 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.myfaces.test.util;
+
+import javax.faces.application.ViewExpiredException;
+import javax.faces.application.ViewHandler;
+import javax.faces.context.FacesContext;
+
+public class Jsf12Utils
+{
+
+ public static void initView(FacesContext facesContext, ViewHandler viewHandler)
+ {
+ viewHandler.initView(facesContext);
+ }
+
+ public static void throwViewExpiredException(String viewId)
+ {
+ throw new ViewExpiredException(
+ "The expected view was not returned for the view identifier: " + viewId, viewId);
+ }
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/util/JsfVersion.java b/test30/src/main/java/org/apache/myfaces/test/util/JsfVersion.java
new file mode 100644
index 0000000..82fee9f
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/util/JsfVersion.java
@@ -0,0 +1,78 @@
+/*
+ * 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.myfaces.test.util;
+
+import javax.faces.application.Application;
+
+public class JsfVersion
+{
+
+ private static boolean supports12;
+ private static boolean supports20;
+
+ static
+ {
+ try
+ {
+ Application.class.getMethod("getExpressionFactory");
+ supports12 = true;
+
+ try
+ {
+ Application.class.getMethod("getExceptionHandler");
+ supports20 = true;
+
+ }
+ catch (NoSuchMethodException e)
+ {
+ // ignore
+ }
+
+ }
+ catch (NoSuchMethodException e)
+ {
+ // ignore
+ }
+ }
+
+ private JsfVersion()
+ {
+ // avoid instantiation
+ }
+
+ /**
+ * Does the JSF is version 1.2 or higher
+ *
+ * @return Supports 1.2 or higher
+ */
+ public static boolean supports12()
+ {
+ return supports12;
+ }
+
+ /**
+ * Does the JSF is version 2.0 or higher
+ *
+ * @return Supports 2.0 or higher
+ */
+ public static boolean supports20()
+ {
+ return supports20;
+ }
+}