You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tapestry.apache.org by hl...@apache.org on 2009/02/05 02:40:30 UTC
svn commit: r740971 [1/2] - in /tapestry/tapestry5/trunk: src/site/apt/
tapestry-core/src/main/java/org/apache/tapestry5/
tapestry-core/src/main/java/org/apache/tapestry5/corelib/base/
tapestry-core/src/main/java/org/apache/tapestry5/corelib/components...
Author: hlship
Date: Thu Feb 5 01:40:29 2009
New Revision: 740971
URL: http://svn.apache.org/viewvc?rev=740971&view=rev
Log:
TAP5-499: Cleanup and simplfy PageTester to remove ComponentInvocation, InvocationTarget, etc.
Added:
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/dom/Visitor.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ArrayEventContext.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/LinkImpl.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/test/CaptureRenderedDocument.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/test/EndOfRequestCleanupFilter.java
Removed:
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ComponentEventTarget.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ComponentInvocation.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ComponentInvocationImpl.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ComponentInvocationMap.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/InvocationTarget.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/NoOpComponentInvocationMap.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/OpaqueConstantTarget.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/PageRenderTarget.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/test/ComponentEventInvoker.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/test/ComponentInvoker.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/test/PageRenderInvoker.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/test/PageTesterComponentInvocationMap.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/test/TestableMarkupWriterFactory.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/test/TestableMarkupWriterFactoryImpl.java
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/ComponentInvocationImplTest.java
Modified:
tapestry/tapestry5/trunk/src/site/apt/index.apt
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/ComponentResourcesCommon.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/base/AbstractLink.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Form.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/dom/Document.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/dom/Element.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/TapestryInternalUtils.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/InternalModule.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/LinkFactory.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/LinkFactoryImpl.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/LinkSourceImpl.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/test/PageTesterModule.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/test/TestableRequest.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/test/TestableRequestImpl.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/test/TestableResponse.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/test/TestableResponseImpl.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/ComponentEventRequestParameters.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/PageRenderRequestParameters.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/test/PageTester.java
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/corelib/base/AbstractLinkTest.java
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/dom/DOMTest.java
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/pagelevel/LocaleTest.java
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/pagelevel/SubmitTest.java
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/LinkImplTest.java
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/LinkSourceImplTest.java
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/test/InternalBaseTestCase.java
Modified: tapestry/tapestry5/trunk/src/site/apt/index.apt
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/src/site/apt/index.apt?rev=740971&r1=740970&r2=740971&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/src/site/apt/index.apt (original)
+++ tapestry/tapestry5/trunk/src/site/apt/index.apt Thu Feb 5 01:40:29 2009
@@ -36,7 +36,7 @@
Now that that 5.0 release is <finally> out and available, work is starting on a 5.1 release.
- The goal is to produce such releases on a regular schedule, ever 4 - 6 months.
+ The goal is to produce such releases on a regular schedule, every 4 - 6 months.
High priorities for 5.1 include Spring Web Flow integration, and support for developing Tapestry applications as Portlets.
@@ -79,7 +79,14 @@
New And Of Note
- * Tapestry now {{{guide/compress.html}compresses}} responses for clients that support it. Context and classpath assets
+ * Tapestry IoC services can now be easily <{{{tapestry-ioc/advice.html}advised}}> as well as
+ <{{{tapestry-ioc/decorator.html}decorated}}> (both of these refer to Aspect Oriented Techniques applied to
+ Tapestry IoC services).
+
+ * Tapestry Services can now be injected into Spring Beans, when using the Tapestry/Spring integration library.
+
+ * Tapestry now {{{guide/compress.html}compresses}} responses for clients that support compression.
+ Context and classpath assets
are now handled uniformly: versioned URLs, far-future expiration headers, and GZIP compression where applicable.
* Ordered and mapped configurations can now have overrides.
@@ -89,80 +96,7 @@
* IoC Service contributions may now be made in terms of classes (that are automatically instantiated) as well as
instances.
- * At long last, an official {{{tapestry-core/ref/org/apache/tapestry5/corelib/components/LinkSubmit.html}LinkSubmit}} component.
-
- * A {{{tapestry-ioc/injection.html}detailed guide to Injection}} has been added.
-
- * You can now configure Tapestry to move \<script\> links to the top of the page.
-
- * Event handler methods for Ajax requests may now return a page name, page class or page instance to force
- the browser to redirect to the page.
-
- * The Inject and InjectService annotations may now be used on fields of service implementations or other
- objects constructed by the IoC container. In the past, injections only occured through method or
- constructor parameters.
-
- * Tapestry now bundles Prototype version 1.6.0.2.
-
- * New methods have been added to
- {{{apidocs/org/apache/tapestry5/dom/Node.html}Node}} to allow nodes to be moved about or otherwise
- manipulated.
-
- * Application State Objects are now automatically saved back to the session at the end of the request,
- which ensures that ASO data is properly replicated across at cluster.
-
- * The new {{{apidocs/org/apache/tapestry5/ioc/annotations/Local.html}@Local}}
- annotation makes it easier to reference services within the same module when injecting.
-
- * Most general documentation has been moved from the tapestry-core module up to the project level.
-
- * Work has started on a {{{cookbook}Tapestry Cookbook}}, showing how to tackle common scenarios.
-
- * Component methods may be marked with the @Log annotation, to enable debug logging of
- method entry (with parameters) and exit (with return value, or thrown exception).
-
- * It is now possible to provide method invocation advice to component methods. This opens up
- a very powerful Aspect Oriented Programming approach to Tapestry components.
-
- * The Exception Report page now identifies the version of the Tapestry framework, and lists
- out System properties (including the Java classpath).
-
- * The {{{tapestry-core/ref/org/apache/tapestry5/corelib/components/Grid.html}Grid}} component can now update itself in place,
- using Ajax, when paging or sorting links are clicked.
-
- * Added a zone parameter to the {{{tapestry-core/ref/org/apache/tapestry5/corelib/components/BeanEditForm.html}BeanEditForm}}
- component, to support Ajax updates.
-
- * The @Cached annotation has been added to allowing the caching of method results.
-
- []
-
-
-What's changed since Tapestry 4?
-
- Tapestry 5 is an all new code base, written from the ground up to take Java web
- application development to new levels of productivity.
-
- This new release removes many limitations of Tapestry 4:
-
- * Components no longer extend from base classes.
-
- * {{{tapestry-core/guide/component-classes.html}Components classes are no longer <abstract>}}.
- Components are pure, simple POJOs (<plain old Java objects>).
-
- * Tapestry no longer uses XML page and component specification files. Information that used to
- be supplied in such files is now supplied directly in the Java class, using Java annotations and naming conventions.
-
- * {{{tapestry-core/guide/reload.html}Changes to Tapestry component templates <and classes> are now picked up <immediately>}},
- without any kind
- of restart. This will even work properly in <production>, not just during development.
-
- * <<Blazing Speed>>. The new code base operates considerably faster than Tapestry 4. Critical
- code paths have been simplified, and the use of reflection has been virtually eliminated.
- Tapestry 4 was as fast as an equivalent Servlet/JSP application, Tapestry 5 is much faster.
-
[]
-
Adaptive API
@@ -271,7 +205,7 @@
directory, including a POM that links to the Apache snapshots repository.
<<Documentation on this site usually refers to the latest snapshot ... that is, it is usually ahead of the last official release. In some cases,
- it is written as if the snapshot release is stable; if documentation refers to version 5.0.x and that doesn't work, try 5.0.x-SNAPSHOT.>>
+ it is written as if the snapshot release is stable; if documentation refers to version 5.1.x.x and that doesn't work, try 5.1.x.x-SNAPSHOT.>>
Principle 1 -- Static Structure, Dynamic Behavior
@@ -347,3 +281,7 @@
Because of this, Tapestry will be able to change internally to a great degree without it affecting any
of the application code <you> write. This should finally crack the backwards compatibility nut, allowing you to have
great assurance that you can upgrade to future releases of Tapestry without breaking your existing applications.
+
+ This is already evident in Tapestry 5.1, where many new features and improvements have occurred, but is still
+ 100% backwards compatible to Tapestry 5.0, as long as you've avoided the temptation to make use of
+ internal APIs.
\ No newline at end of file
Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/ComponentResourcesCommon.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/ComponentResourcesCommon.java?rev=740971&r1=740970&r2=740971&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/ComponentResourcesCommon.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/ComponentResourcesCommon.java Thu Feb 5 01:40:29 2009
@@ -1,4 +1,4 @@
-// Copyright 2006, 2007, 2008 The Apache Software Foundation
+// Copyright 2006, 2007, 2008, 2009 The Apache Software Foundation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -23,14 +23,12 @@
* Operations shared by the public {@link org.apache.tapestry5.ComponentResources} interface and {@link
* org.apache.tapestry5.internal.structure.ComponentPageElement} interface (on the internal side).
*/
-@SuppressWarnings({"JavaDoc"})
+@SuppressWarnings({ "JavaDoc" })
public interface ComponentResourcesCommon extends Locatable
{
/**
* Returns the simple (or local) id of the component. The id will be unique within the component's immediate
* container. For a page's root component, the value null is returned.
- * <p/>
- * \
*/
String getId();
Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/base/AbstractLink.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/base/AbstractLink.java?rev=740971&r1=740970&r2=740971&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/base/AbstractLink.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/base/AbstractLink.java Thu Feb 5 01:40:29 2009
@@ -1,4 +1,4 @@
-// Copyright 2007, 2008 The Apache Software Foundation
+// Copyright 2007, 2008, 2009 The Apache Software Foundation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -19,7 +19,6 @@
import org.apache.tapestry5.annotations.SetupRender;
import org.apache.tapestry5.annotations.SupportsInformalParameters;
import org.apache.tapestry5.dom.Element;
-import org.apache.tapestry5.internal.services.ComponentInvocationMap;
import org.apache.tapestry5.ioc.annotations.Inject;
/**
@@ -28,9 +27,6 @@
@SupportsInformalParameters
public abstract class AbstractLink implements ClientElement
{
- @Inject
- private ComponentInvocationMap componentInvocationMap;
-
/**
* An anchor value to append to the generated URL (the hash separator will be added automatically).
*/
@@ -90,8 +86,6 @@
resources.renderInformalParameters(writer);
- componentInvocationMap.store(element, link);
-
this.link = link;
}
@@ -140,10 +134,9 @@
/**
* Used for testing.
*/
- final void inject(String anchor, ComponentInvocationMap map, ComponentResources resources)
+ final void inject(String anchor, ComponentResources resources)
{
this.anchor = anchor;
- componentInvocationMap = map;
this.resources = resources;
}
}
Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Form.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Form.java?rev=740971&r1=740970&r2=740971&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Form.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Form.java Thu Feb 5 01:40:29 2009
@@ -1,4 +1,4 @@
-// Copyright 2006, 2007, 2008 The Apache Software Foundation
+// Copyright 2006, 2007, 2008, 2009 The Apache Software Foundation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -22,7 +22,6 @@
import org.apache.tapestry5.corelib.mixins.RenderInformals;
import org.apache.tapestry5.dom.Element;
import org.apache.tapestry5.internal.services.ClientBehaviorSupport;
-import org.apache.tapestry5.internal.services.ComponentInvocationMap;
import org.apache.tapestry5.internal.services.ComponentResultProcessorWrapper;
import org.apache.tapestry5.internal.services.HeartbeatImpl;
import org.apache.tapestry5.internal.util.AutofocusValidationDecorator;
@@ -183,9 +182,6 @@
@Persist(PersistenceConstants.FLASH)
private ValidationTracker defaultTracker;
- @Inject
- private ComponentInvocationMap componentInvocationMap;
-
private InternalFormSupport formSupport;
private Element form;
@@ -276,8 +272,6 @@
"method", "post",
"action", link);
- componentInvocationMap.store(form, link);
-
resources.renderInformalParameters(writer);
div = writer.element("div", "class", CSSClassConstants.INVISIBLE);
Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/dom/Document.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/dom/Document.java?rev=740971&r1=740970&r2=740971&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/dom/Document.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/dom/Document.java Thu Feb 5 01:40:29 2009
@@ -192,4 +192,15 @@
return rootElement.getNamespaceURIToPrefix();
}
+
+ /**
+ * Visits the root element of the document.
+ *
+ * @param visitor callback
+ * @since 5.1.0.0
+ */
+ void visit(Visitor visitor)
+ {
+ rootElement.visit(visitor);
+ }
}
Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/dom/Element.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/dom/Element.java?rev=740971&r1=740970&r2=740971&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/dom/Element.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/dom/Element.java Thu Feb 5 01:40:29 2009
@@ -18,6 +18,7 @@
import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
import org.apache.tapestry5.ioc.internal.util.Defense;
import org.apache.tapestry5.ioc.internal.util.InternalUtils;
+import org.apache.tapestry5.ioc.util.Stack;
import java.io.PrintWriter;
import java.util.*;
@@ -691,4 +692,48 @@
return true;
}
+
+ /**
+ * Depth-first visitor traversal of this Element and its Element children. The traversal order is the same as render
+ * order.
+ *
+ * @param visitor callback
+ * @since 5.1.0.0
+ */
+ public void visit(Visitor visitor)
+ {
+ Stack<Element> queue = CollectionFactory.newStack();
+
+ queue.push(this);
+
+ while (!queue.isEmpty())
+ {
+ Element e = queue.pop();
+
+ visitor.visit(e);
+
+ e.queueChildren(queue);
+ }
+ }
+
+
+ private void queueChildren(Stack<Element> queue)
+ {
+ List<Node> children = getChildren();
+
+ int count = children.size();
+
+ // Push them in reverse order to get the correct
+ // traversal: the lowest index child is traversed first.
+
+ for (int i = count - 1; i >= 0; i--)
+ {
+ Node n = children.get(i);
+
+ if (n instanceof Element)
+ {
+ queue.push((Element) n);
+ }
+ }
+ }
}
Added: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/dom/Visitor.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/dom/Visitor.java?rev=740971&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/dom/Visitor.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/dom/Visitor.java Thu Feb 5 01:40:29 2009
@@ -0,0 +1,30 @@
+// Copyright 2009 The Apache Software Foundation
+//
+// Licensed 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.tapestry5.dom;
+
+/**
+ * A callback interface used navigate the {@link org.apache.tapestry5.dom.Element}s of a document.
+ *
+ * @since 5.1.0.0
+ */
+public interface Visitor
+{
+ /**
+ * Called for each Element being visited.
+ *
+ * @param element visited
+ */
+ void visit(Element element);
+}
Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/TapestryInternalUtils.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/TapestryInternalUtils.java?rev=740971&r1=740970&r2=740971&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/TapestryInternalUtils.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/TapestryInternalUtils.java Thu Feb 5 01:40:29 2009
@@ -17,6 +17,7 @@
import org.apache.commons.codec.binary.Base64;
import org.apache.tapestry5.OptionModel;
import org.apache.tapestry5.SelectModel;
+import org.apache.tapestry5.EventContext;
import org.apache.tapestry5.ioc.Messages;
import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
import org.apache.tapestry5.ioc.internal.util.Defense;
@@ -437,4 +438,21 @@
return lastx > 0 ? asString.substring(0, lastx) : asString;
}
+
+ public static boolean isEqual(EventContext left, EventContext right)
+ {
+ if (left == right) return true;
+
+ int count = left.getCount();
+
+ if (count != right.getCount()) return false;
+
+ for (int i = 0; i < count; i++)
+ {
+ if (!left.get(Object.class, i).equals(right.get(Object.class, i)))
+ return false;
+ }
+
+ return true;
+ }
}
Added: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ArrayEventContext.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ArrayEventContext.java?rev=740971&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ArrayEventContext.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ArrayEventContext.java Thu Feb 5 01:40:29 2009
@@ -0,0 +1,46 @@
+// Copyright 2009 The Apache Software Foundation
+//
+// Licensed 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.tapestry5.internal.services;
+
+import org.apache.tapestry5.EventContext;
+import org.apache.tapestry5.ioc.services.TypeCoercer;
+
+/**
+ * Simple implementation of {@link org.apache.tapestry5.EventContext}.
+ *
+ * @since 5.1.0.0
+ */
+public class ArrayEventContext implements EventContext
+{
+ private final TypeCoercer typeCoercer;
+
+ private final Object[] values;
+
+ public ArrayEventContext(TypeCoercer typeCoercer, Object... values)
+ {
+ this.typeCoercer = typeCoercer;
+ this.values = values;
+ }
+
+ public <T> T get(Class<T> desiredType, int index)
+ {
+ return typeCoercer.coerce(values[index], desiredType);
+ }
+
+ public int getCount()
+ {
+ return values.length;
+ }
+}
Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/InternalModule.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/InternalModule.java?rev=740971&r1=740970&r2=740971&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/InternalModule.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/InternalModule.java Thu Feb 5 01:40:29 2009
@@ -59,7 +59,6 @@
binder.bind(TemplateParser.class, TemplateParserImpl.class);
binder.bind(PageResponseRenderer.class, PageResponseRendererImpl.class);
binder.bind(PageMarkupRenderer.class, PageMarkupRendererImpl.class);
- binder.bind(ComponentInvocationMap.class, NoOpComponentInvocationMap.class);
binder.bind(LinkSource.class, LinkSourceImpl.class);
binder.bind(LocalizationSetter.class, LocalizationSetterImpl.class);
binder.bind(PageElementFactory.class, PageElementFactoryImpl.class);
Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/LinkFactory.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/LinkFactory.java?rev=740971&r1=740970&r2=740971&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/LinkFactory.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/LinkFactory.java Thu Feb 5 01:40:29 2009
@@ -15,7 +15,8 @@
package org.apache.tapestry5.internal.services;
import org.apache.tapestry5.Link;
-import org.apache.tapestry5.internal.structure.Page;
+import org.apache.tapestry5.services.ComponentEventRequestParameters;
+import org.apache.tapestry5.services.PageRenderRequestParameters;
/**
* Used by {@link org.apache.tapestry5.internal.services.LinkSource} service to create {@link org.apache.tapestry5.Link}
@@ -25,12 +26,23 @@
*/
public interface LinkFactory
{
+
+ /**
+ * Creates a component event link (possibly for a Form).
+ *
+ * @param parameters defines the data need to create the link
+ * @param forForm if true, the link is for a form (which handles query parameters differently than normal)
+ * @return the component event link
+ * @see org.apache.tapestry5.services.ComponentEventRequestHandler
+ */
+ Link createComponentEventLink(ComponentEventRequestParameters parameters, boolean forForm);
+
/**
- * Creates a new link; the page is used to determine if the request needs to shift between HTTP and HTTPS.
+ * Creates a page render event link.
*
- * @param page
- * @param invocation
- * @return link that calls the invocation
+ * @param parameters defines the page and page activation context
+ * @return the link
+ * @see org.apache.tapestry5.services.PageRenderRequestHandler
*/
- Link create(Page page, ComponentInvocation invocation);
+ Link createPageRenderLink(PageRenderRequestParameters parameters);
}
Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/LinkFactoryImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/LinkFactoryImpl.java?rev=740971&r1=740970&r2=740971&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/LinkFactoryImpl.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/LinkFactoryImpl.java Thu Feb 5 01:40:29 2009
@@ -15,10 +15,14 @@
package org.apache.tapestry5.internal.services;
import org.apache.tapestry5.Link;
+import org.apache.tapestry5.EventConstants;
+import org.apache.tapestry5.EventContext;
+import org.apache.tapestry5.ioc.internal.util.InternalUtils;
import org.apache.tapestry5.internal.structure.Page;
-import org.apache.tapestry5.services.PersistentLocale;
-import org.apache.tapestry5.services.Request;
-import org.apache.tapestry5.services.Response;
+import org.apache.tapestry5.internal.InternalConstants;
+import org.apache.tapestry5.services.*;
+
+import java.util.Locale;
public class LinkFactoryImpl implements LinkFactory
{
@@ -30,34 +34,166 @@
private final RequestPathOptimizer optimizer;
- private final ComponentInvocationMap componentInvocationMap;
-
private final PersistentLocale persistentLocale;
+ private final RequestPageCache requestPageCache;
+
+ private final ContextValueEncoder valueEncoder;
+
+ private final URLEncoder urlEncoder;
+
+ private static final int BUFFER_SIZE = 100;
+
+ private static final char SLASH = '/';
+
public LinkFactoryImpl(Request request, Response response, RequestSecurityManager requestSecurityManager,
- RequestPathOptimizer optimizer, ComponentInvocationMap componentInvocationMap,
- PersistentLocale persistentLocale)
+ RequestPathOptimizer optimizer, PersistentLocale persistentLocale,
+ RequestPageCache requestPageCache,
+ ContextValueEncoder valueEncoder, URLEncoder urlEncoder)
{
this.request = request;
this.response = response;
this.requestSecurityManager = requestSecurityManager;
this.optimizer = optimizer;
- this.componentInvocationMap = componentInvocationMap;
this.persistentLocale = persistentLocale;
+ this.requestPageCache = requestPageCache;
+ this.valueEncoder = valueEncoder;
+ this.urlEncoder = urlEncoder;
+ }
+
+ public Link createComponentEventLink(ComponentEventRequestParameters parameters, boolean forForm)
+ {
+ StringBuilder builder = new StringBuilder(BUFFER_SIZE);
+
+ // Build up the absolute URI.
+
+ String activePageName = parameters.getActivePageName();
+ String containingPageName = parameters.getContainingPageName();
+ String eventType = parameters.getEventType();
+
+ Page activePage = requestPageCache.get(activePageName);
+
+ String nestedComponentId = parameters.getNestedComponentId();
+ boolean hasComponentId = InternalUtils.isNonBlank(nestedComponentId);
+
+ String baseURL = requestSecurityManager.getBaseURL(activePage);
+
+ if (baseURL != null)
+ builder.append(baseURL);
+
+ builder.append(request.getContextPath());
+
+ Locale locale = persistentLocale.get();
+
+ if (locale != null)
+ {
+ builder.append(SLASH);
+ builder.append(locale.toString());
+ }
+
+ builder.append(SLASH);
+ builder.append(activePageName.toLowerCase());
+
+ if (hasComponentId)
+ {
+ builder.append('.');
+ builder.append(nestedComponentId);
+ }
+
+ if (!hasComponentId || !eventType.equals(EventConstants.ACTION))
+ {
+ builder.append(":");
+ builder.append(eventType.toLowerCase());
+ }
+
+ appendContext(parameters.getEventContext(), builder);
+
+ Link result = new LinkImpl(builder.toString(), baseURL == null, forForm, response, optimizer);
+
+ EventContext pageActivationContext = parameters.getPageActivationContext();
+
+ if (pageActivationContext.getCount() != 0)
+ {
+ // Reuse the builder
+ builder.setLength(0);
+ appendContext(pageActivationContext, builder);
+
+ // Omit that first slash
+ result.addParameter(InternalConstants.PAGE_CONTEXT_NAME, builder.substring(1));
+ }
+
+ // TAPESTRY-2044: Sometimes the active page drags in components from another page and we
+ // need to differentiate that.
+
+ if (!containingPageName.equalsIgnoreCase(activePageName))
+ result.addParameter(InternalConstants.CONTAINER_PAGE_NAME, containingPageName.toLowerCase());
+
+ return result;
}
- public Link create(Page page, ComponentInvocation invocation)
+
+ public Link createPageRenderLink(PageRenderRequestParameters parameters)
{
- String baseURL = requestSecurityManager.getBaseURL(page);
+ StringBuilder builder = new StringBuilder(BUFFER_SIZE);
+
+ // Build up the absolute URI.
+
+ String activePageName = parameters.getLogicalPageName();
+
+ Page activePage = requestPageCache.get(activePageName);
+
+
+ String baseURL = requestSecurityManager.getBaseURL(activePage);
+
+ if (baseURL != null)
+ builder.append(baseURL);
+
+ builder.append(request.getContextPath());
- Link link = new LinkImpl(response, optimizer, baseURL, request.getContextPath(), persistentLocale.get(),
- invocation);
+ Locale locale = persistentLocale.get();
- // This is a hook used for testing; we can relate the link to an invocation so that we can simulate
- // the clicking of the link (or submitting of the form).
+ if (locale != null)
+ {
+ builder.append(SLASH);
+ builder.append(locale.toString());
+ }
+
+ builder.append(SLASH);
+ builder.append(activePageName.toLowerCase());
+
+ appendContext(parameters.getActivationContext(), builder);
+
+ return new LinkImpl(builder.toString(), baseURL == null, false, response, optimizer);
+ }
+
+ public void appendContext(EventContext context, StringBuilder builder)
+ {
+ for (int i = 0; i < context.getCount(); i++)
+ {
+ Object raw = context.get(Object.class, i);
+
+ String valueEncoded = raw == null ? null : valueEncoder.toClient(raw);
+ String urlEncoded = urlEncoder.encode(valueEncoded);
+
+ builder.append(SLASH);
+
+ builder.append(urlEncoded);
+ }
+ }
+
+ private String trimIndex(String pageName)
+ {
+ int lastSlash = pageName.lastIndexOf('/');
- componentInvocationMap.store(link, invocation);
+ String name = lastSlash > 0
+ ? pageName.substring(lastSlash + 1)
+ : pageName;
+
+ if (name.equalsIgnoreCase("index"))
+ return lastSlash > 0
+ ? pageName.substring(0, lastSlash)
+ : "";
- return link;
+ return pageName;
}
}
Added: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/LinkImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/LinkImpl.java?rev=740971&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/LinkImpl.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/LinkImpl.java Thu Feb 5 01:40:29 2009
@@ -0,0 +1,154 @@
+// Copyright 2009 The Apache Software Foundation
+//
+// Licensed 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.tapestry5.internal.services;
+
+import org.apache.tapestry5.Link;
+import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
+import org.apache.tapestry5.ioc.internal.util.Defense;
+import org.apache.tapestry5.ioc.internal.util.InternalUtils;
+import org.apache.tapestry5.services.Response;
+
+import java.util.List;
+import java.util.Map;
+
+public class LinkImpl implements Link
+{
+ private Map<String, String> parameters;
+
+ private final String absoluteURI;
+
+ private final boolean optimizable;
+
+ private final boolean forForm;
+
+ private final Response response;
+
+ private final RequestPathOptimizer optimizer;
+
+ private String anchor;
+
+ public LinkImpl(String absoluteURI, boolean optimizable, boolean forForm, Response response,
+ RequestPathOptimizer optimizer)
+ {
+ this.absoluteURI = absoluteURI;
+ this.optimizable = optimizable;
+ this.forForm = forForm;
+ this.response = response;
+ this.optimizer = optimizer;
+ }
+
+ public void addParameter(String parameterName, String value)
+ {
+ Defense.notBlank(parameterName, "parameterName");
+ Defense.notBlank(value, "value");
+
+ if (parameters == null)
+ parameters = CollectionFactory.newMap();
+
+ parameters.put(parameterName, value);
+ }
+
+ public String getAnchor()
+ {
+ return anchor;
+ }
+
+ public List<String> getParameterNames()
+ {
+ return InternalUtils.sortedKeys(parameters);
+ }
+
+ public String getParameterValue(String name)
+ {
+ return InternalUtils.get(parameters, name);
+ }
+
+ public void setAnchor(String anchor)
+ {
+ this.anchor = anchor;
+ }
+
+ public String toAbsoluteURI()
+ {
+ return appendAnchor(response.encodeURL(buildURI()));
+ }
+
+ public String toRedirectURI()
+ {
+ return appendAnchor(response.encodeRedirectURL(buildURI()));
+ }
+
+ public String toURI()
+ {
+ String path = buildURI();
+
+ if (optimizable)
+ path = optimizer.optimizePath(path);
+
+ return appendAnchor(response.encodeURL(path));
+ }
+
+ private String appendAnchor(String path)
+ {
+ return InternalUtils.isBlank(anchor)
+ ? path
+ : path + "#" + anchor;
+ }
+
+ /**
+ * Returns the value from {@link #toURI()}
+ */
+ @Override
+ public String toString()
+ {
+ return toURI();
+ }
+
+
+ /**
+ * Extends the absolute path with any query parameters. Query parameters are never added to a forForm link.
+ *
+ * @return absoluteURI appended with query parameters
+ */
+ private String buildURI()
+ {
+ if (forForm || parameters == null)
+ return absoluteURI;
+
+ StringBuilder builder = new StringBuilder(absoluteURI.length() * 2);
+
+ builder.append(absoluteURI);
+
+ String sep = "?";
+
+ for (String name : getParameterNames())
+ {
+ String value = parameters.get(name);
+
+ builder.append(sep);
+
+ // We assume that the name is URL safe and that the value will already have been URL
+ // encoded if it is not known to be URL safe.
+
+ builder.append(name);
+ builder.append("=");
+ builder.append(value);
+
+ sep = "&";
+ }
+
+ return builder.toString();
+ }
+}
Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/LinkSourceImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/LinkSourceImpl.java?rev=740971&r1=740970&r2=740971&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/LinkSourceImpl.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/LinkSourceImpl.java Thu Feb 5 01:40:29 2009
@@ -15,13 +15,11 @@
package org.apache.tapestry5.internal.services;
import org.apache.tapestry5.Link;
-import org.apache.tapestry5.internal.InternalConstants;
import org.apache.tapestry5.internal.structure.Page;
import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
import org.apache.tapestry5.ioc.internal.util.Defense;
-import org.apache.tapestry5.services.ContextPathEncoder;
-import org.apache.tapestry5.services.LinkCreationHub;
-import org.apache.tapestry5.services.LinkCreationListener;
+import org.apache.tapestry5.ioc.services.TypeCoercer;
+import org.apache.tapestry5.services.*;
import java.util.List;
@@ -31,26 +29,25 @@
private final PageRenderQueue pageRenderQueue;
- private final ContextPathEncoder contextPathEncoder;
-
private final PageActivationContextCollector contextCollector;
private final LinkFactory linkFactory;
private final List<LinkCreationListener> listeners = CollectionFactory.newThreadSafeList();
+ private final TypeCoercer typeCoercer;
+
public LinkSourceImpl(
RequestPageCache pageCache,
PageRenderQueue pageRenderQueue,
- ContextPathEncoder contextPathEncoder,
PageActivationContextCollector contextCollector,
- LinkFactory linkFactory)
+ LinkFactory linkFactory, TypeCoercer typeCoercer)
{
this.pageCache = pageCache;
this.pageRenderQueue = pageRenderQueue;
- this.contextPathEncoder = contextPathEncoder;
this.contextCollector = contextCollector;
this.linkFactory = linkFactory;
+ this.typeCoercer = typeCoercer;
}
public Link createComponentEventLink(Page page, String nestedId, String eventType, boolean forForm,
@@ -65,20 +62,19 @@
if (activePage == null)
activePage = page;
- ComponentEventTarget target = new ComponentEventTarget(eventType, activePage.getLogicalName(), nestedId);
-
Object[] pageActivationContext = contextCollector.collectPageActivationContext(activePage);
- ComponentInvocation invocation = new ComponentInvocationImpl(contextPathEncoder, target, eventContext,
- pageActivationContext, forForm);
-
- Link link = linkFactory.create(activePage, invocation);
+ ComponentEventRequestParameters parameters
+ = new ComponentEventRequestParameters(
+ activePage.getLogicalName(),
+ page.getLogicalName(),
+ toBlank(nestedId),
+ eventType,
+ new ArrayEventContext(typeCoercer, pageActivationContext),
+ new ArrayEventContext(typeCoercer, eventContext));
- // TAPESTRY-2044: Sometimes the active page drags in components from another page and we
- // need to differentiate that.
- if (activePage != page)
- link.addParameter(InternalConstants.CONTAINER_PAGE_NAME, page.getLogicalName().toLowerCase());
+ Link link = linkFactory.createComponentEventLink(parameters, forForm);
for (LinkCreationListener listener : listeners)
listener.createdComponentEventLink(link);
@@ -86,6 +82,10 @@
return link;
}
+ private String toBlank(String input)
+ {
+ return input == null ? "" : input;
+ }
public Link createPageRenderLink(Page page, boolean override, Object... pageActivationContext)
{
@@ -99,11 +99,11 @@
? pageActivationContext
: contextCollector.collectPageActivationContext(page);
- PageRenderTarget target = new PageRenderTarget(logicalPageName);
-
- ComponentInvocation invocation = new ComponentInvocationImpl(contextPathEncoder, target, null, context, false);
+ PageRenderRequestParameters parameters =
+ new PageRenderRequestParameters(logicalPageName,
+ new ArrayEventContext(typeCoercer, context));
- Link link = linkFactory.create(page, invocation);
+ Link link = linkFactory.createPageRenderLink(parameters);
for (LinkCreationListener listener : listeners)
listener.createdPageRenderLink(link);
Added: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/test/CaptureRenderedDocument.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/test/CaptureRenderedDocument.java?rev=740971&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/test/CaptureRenderedDocument.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/test/CaptureRenderedDocument.java Thu Feb 5 01:40:29 2009
@@ -0,0 +1,43 @@
+// Copyright 2009 The Apache Software Foundation
+//
+// Licensed 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.tapestry5.internal.test;
+
+import org.apache.tapestry5.services.MarkupRendererFilter;
+import org.apache.tapestry5.services.MarkupRenderer;
+import org.apache.tapestry5.MarkupWriter;
+
+/**
+ * Used to capture the rendered document from a traditional page render. Invokes {@link
+ * org.apache.tapestry5.internal.test.TestableResponse#setRenderedDocument(org.apache.tapestry5.dom.Document)}.
+ *
+ * @since 5.1.0.0
+ */
+public class CaptureRenderedDocument implements MarkupRendererFilter
+{
+ private final TestableResponse testableResponse;
+
+ public CaptureRenderedDocument(TestableResponse testableResponse)
+ {
+ this.testableResponse = testableResponse;
+ }
+
+
+ public void renderMarkup(MarkupWriter writer, MarkupRenderer renderer)
+ {
+ renderer.renderMarkup(writer);
+
+ testableResponse.setRenderedDocument(writer.getDocument());
+ }
+}
Added: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/test/EndOfRequestCleanupFilter.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/test/EndOfRequestCleanupFilter.java?rev=740971&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/test/EndOfRequestCleanupFilter.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/test/EndOfRequestCleanupFilter.java Thu Feb 5 01:40:29 2009
@@ -0,0 +1,49 @@
+// Copyright 2009 The Apache Software Foundation
+//
+// Licensed 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.tapestry5.internal.test;
+
+import org.apache.tapestry5.services.RequestHandler;
+import org.apache.tapestry5.services.Request;
+import org.apache.tapestry5.services.Response;
+import org.apache.tapestry5.services.RequestFilter;
+import org.apache.tapestry5.ioc.services.PerthreadManager;
+
+import java.io.IOException;
+
+/**
+ * Makes sure that {@link org.apache.tapestry5.ioc.services.PerthreadManager#cleanup()} is invoked at the end of each
+ * request (normally handled by {@link org.apache.tapestry5.TapestryFilter}).
+ */
+public class EndOfRequestCleanupFilter implements RequestFilter
+{
+ private final PerthreadManager perThreadManager;
+
+ public EndOfRequestCleanupFilter(PerthreadManager perThreadManager)
+ {
+ this.perThreadManager = perThreadManager;
+ }
+
+ public boolean service(Request request, Response response, RequestHandler handler) throws IOException
+ {
+ try
+ {
+ return handler.service(request, response);
+ }
+ finally
+ {
+ perThreadManager.cleanup();
+ }
+ }
+}
Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/test/PageTesterModule.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/test/PageTesterModule.java?rev=740971&r1=740970&r2=740971&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/test/PageTesterModule.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/test/PageTesterModule.java Thu Feb 5 01:40:29 2009
@@ -1,4 +1,4 @@
-// Copyright 2007, 2008 The Apache Software Foundation
+// Copyright 2007, 2008, 2009 The Apache Software Foundation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -15,17 +15,10 @@
package org.apache.tapestry5.internal.test;
import org.apache.tapestry5.SymbolConstants;
-import org.apache.tapestry5.internal.services.ComponentInvocationMap;
import org.apache.tapestry5.internal.services.CookieSink;
import org.apache.tapestry5.internal.services.CookieSource;
-import org.apache.tapestry5.ioc.Configuration;
-import org.apache.tapestry5.ioc.MappedConfiguration;
-import org.apache.tapestry5.ioc.ObjectLocator;
-import org.apache.tapestry5.ioc.ServiceBinder;
-import org.apache.tapestry5.services.AliasContribution;
-import org.apache.tapestry5.services.MarkupWriterFactory;
-import org.apache.tapestry5.services.Request;
-import org.apache.tapestry5.services.Response;
+import org.apache.tapestry5.ioc.*;
+import org.apache.tapestry5.services.*;
import org.apache.tapestry5.test.PageTester;
/**
@@ -40,32 +33,28 @@
{
binder.bind(TestableRequest.class, TestableRequestImpl.class);
binder.bind(TestableResponse.class, TestableResponseImpl.class);
- binder.bind(TestableMarkupWriterFactory.class, TestableMarkupWriterFactoryImpl.class);
}
public static void contributeAlias(Configuration<AliasContribution> configuration, ObjectLocator locator)
{
- add(configuration, ComponentInvocationMap.class, new PageTesterComponentInvocationMap());
-
- add(configuration, locator, Request.class, "TestableRequest");
- add(configuration, locator, Response.class, "TestableResponse");
- add(configuration, locator, MarkupWriterFactory.class, "TestableMarkupWriterFactory");
+ alias(configuration, locator, Request.class, "TestableRequest");
+ alias(configuration, locator, Response.class, "TestableResponse");
TestableCookieSinkSource cookies = new TestableCookieSinkSource();
- add(configuration, CookieSink.class, cookies);
- add(configuration, CookieSource.class, cookies);
+ alias(configuration, CookieSink.class, cookies);
+ alias(configuration, CookieSource.class, cookies);
}
- private static <T> void add(Configuration<AliasContribution> configuration, ObjectLocator locator,
- Class<T> serviceClass, String serviceId)
+ private static <T> void alias(Configuration<AliasContribution> configuration, ObjectLocator locator,
+ Class<T> serviceClass, String serviceId)
{
T service = locator.getService(serviceId, serviceClass);
- add(configuration, serviceClass, service);
+ alias(configuration, serviceClass, service);
}
- private static <T> void add(Configuration<AliasContribution> configuration, Class<T> serviceClass, T service)
+ private static <T> void alias(Configuration<AliasContribution> configuration, Class<T> serviceClass, T service)
{
AliasContribution<T> contribution = AliasContribution.create(serviceClass, TEST_MODE, service);
@@ -76,4 +65,14 @@
{
configuration.add(SymbolConstants.FORCE_ABSOLUTE_URIS, "true");
}
+
+ public static void contributeRequestHandler(OrderedConfiguration<RequestFilter> configuration)
+ {
+ configuration.addInstance("EndOfRequestCleanup", EndOfRequestCleanupFilter.class, "before:StaticFiles");
+ }
+
+ public static void contributeMarkupRenderer(OrderedConfiguration<MarkupRendererFilter> configuration)
+ {
+ configuration.addInstance("CaptureRenderedDocument", CaptureRenderedDocument.class, "before:DocumentLinker");
+ }
}
Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/test/TestableRequest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/test/TestableRequest.java?rev=740971&r1=740970&r2=740971&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/test/TestableRequest.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/test/TestableRequest.java Thu Feb 5 01:40:29 2009
@@ -1,4 +1,4 @@
-// Copyright 2007 The Apache Software Foundation
+// Copyright 2007, 2009 The Apache Software Foundation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -17,7 +17,7 @@
import org.apache.tapestry5.services.Request;
import org.apache.tapestry5.test.PageTester;
-import java.util.Map;
+import java.util.Locale;
/**
* An extended version of {@link Request} that allows the {@link PageTester} to control and override behavior,
@@ -27,16 +27,36 @@
{
/**
* Clears the internal parameters map.
+ *
+ * @return the request for further configuration
*/
- void clear();
+ TestableRequest clear();
/**
- * Loads new parameter/value pairs into the map.
+ * Sets the path; the path should begin with a "/" character and contain everything from there to the start of query
+ * parameters (if any).
+ *
+ * @param path
+ * @return the request for further configuration
*/
- void loadParameters(Map<String, String> parameterValues);
+ TestableRequest setPath(String path);
/**
- * Loads a single parameter/value pair.
+ * Sets the locale requested by "the browser".
+ *
+ * @returns the request for further configuration
*/
- void loadParameter(String parameterName, String parameterValue);
+ TestableRequest setLocale(Locale locale);
+
+ /**
+ * Loads a single parameter/value pair. This may define a new parameter, or add a value to a list of parameters.
+ *
+ * @return the request for further configuration
+ */
+ TestableRequest loadParameter(String parameterName, String parameterValue);
+
+ /**
+ * Overrides a parameter to the specific value, regardless of how the parameter was previously set.
+ */
+ TestableRequest overrideParameter(String parameterName, String parameterValue);
}
Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/test/TestableRequestImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/test/TestableRequestImpl.java?rev=740971&r1=740970&r2=740971&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/test/TestableRequestImpl.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/test/TestableRequestImpl.java Thu Feb 5 01:40:29 2009
@@ -1,4 +1,4 @@
-// Copyright 2007, 2008 The Apache Software Foundation
+// Copyright 2007, 2008, 2009 The Apache Software Foundation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -22,17 +22,22 @@
import java.util.List;
import java.util.Locale;
import java.util.Map;
+import java.util.ArrayList;
public class TestableRequestImpl implements TestableRequest
{
private final String contextPath;
- private final Map<String, String> parameters = CollectionFactory.newMap();
+ private final Map<String, Object> parameters = CollectionFactory.newMap();
private final Map<String, Object> attributes = CollectionFactory.newMap();
private Session session;
+ private String path = "/";
+
+ private Locale locale = Locale.getDefault();
+
@Inject
public TestableRequestImpl()
{
@@ -50,24 +55,65 @@
String.format("Request: method %s() not yet implemented by TestableRequestImpl.", methodName));
}
- public void clear()
+ public TestableRequest clear()
{
parameters.clear();
+
+ return this;
}
- public void loadParameter(String parameterName, String parameterValue)
+ public TestableRequest setPath(String path)
{
- parameters.put(parameterName, parameterValue);
+ this.path = path;
+
+ return this;
}
- public void loadParameters(Map<String, String> parameterValues)
+ public TestableRequest setLocale(Locale locale)
{
- parameters.putAll(parameterValues);
+ this.locale = locale;
+
+ return this;
+ }
+
+ public TestableRequest loadParameter(String parameterName, String parameterValue)
+ {
+ Object existing = parameters.get(parameterName);
+
+ if (existing == null)
+ {
+ parameters.put(parameterName, parameterValue);
+ return this;
+ }
+
+ if (existing instanceof List)
+ {
+ ((List) existing).add(parameterValue);
+ return this;
+ }
+
+ // Convert from a single String to a List of Strings.
+
+ List list = new ArrayList();
+ list.add(existing);
+ list.add(parameterValue);
+
+ parameters.put(parameterName, list);
+
+ return this;
+ }
+
+ public TestableRequest overrideParameter(String parameterName, String parameterValue)
+ {
+ parameters.put(parameterName, parameterValue);
+
+ return this;
}
public long getDateHeader(String name)
{
nyi("getDateHeader");
+
return 0;
}
@@ -83,7 +129,7 @@
public Locale getLocale()
{
- return nyi("getLocale");
+ return locale;
}
public List<String> getParameterNames()
@@ -93,14 +139,21 @@
public String[] getParameters(String name)
{
- String value = getParameter(name);
+ Object value = parameters.get(name);
- return value == null ? null : new String[] {value};
+ if (value == null) return null;
+
+ if (value instanceof String)
+ return new String[] { (String) value };
+
+ List list = (List) value;
+
+ return (String[]) list.toArray(new String[list.size()]);
}
public String getPath()
{
- return nyi("getPath");
+ return path;
}
public String getContextPath()
@@ -110,7 +163,13 @@
public String getParameter(String name)
{
- return parameters.get(name);
+ Object value = parameters.get(name);
+
+ if (value == null || value instanceof String) return (String) value;
+
+ List<String> list = (List<String>) value;
+
+ return list.get(0);
}
public Session getSession(boolean create)
Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/test/TestableResponse.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/test/TestableResponse.java?rev=740971&r1=740970&r2=740971&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/test/TestableResponse.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/test/TestableResponse.java Thu Feb 5 01:40:29 2009
@@ -1,4 +1,4 @@
-// Copyright 2007 The Apache Software Foundation
+// Copyright 2007, 2009 The Apache Software Foundation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -15,11 +15,22 @@
package org.apache.tapestry5.internal.test;
import org.apache.tapestry5.Link;
+import org.apache.tapestry5.dom.Document;
import org.apache.tapestry5.services.Response;
public interface TestableResponse extends Response
{
/**
+ * Invoked as part of the rendering pipeline to store the final rendered Document object.
+ */
+ void setRenderedDocument(Document document);
+
+ /**
+ * Allows access to the rendered document.
+ */
+ Document getRenderedDocument();
+
+ /**
* Returns the link redirected to via {@link org.apache.tapestry5.services.Response#sendRedirect(org.apache.tapestry5.Link)}.
*/
Link getRedirectLink();
Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/test/TestableResponseImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/test/TestableResponseImpl.java?rev=740971&r1=740970&r2=740971&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/test/TestableResponseImpl.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/test/TestableResponseImpl.java Thu Feb 5 01:40:29 2009
@@ -1,4 +1,4 @@
-// Copyright 2007, 2008 The Apache Software Foundation
+// Copyright 2007, 2008, 2009 The Apache Software Foundation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -15,6 +15,7 @@
package org.apache.tapestry5.internal.test;
import org.apache.tapestry5.Link;
+import org.apache.tapestry5.dom.Document;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
@@ -27,6 +28,8 @@
private boolean committed;
+ private Document renderedDocument;
+
private void nyi(String methodName)
{
throw new RuntimeException(String.format("TestableResponse: Method %s() not yet implemented.", methodName));
@@ -60,22 +63,18 @@
public void setContentLength(int length)
{
- nyi("setContentLength");
}
public void setDateHeader(String name, long date)
{
- nyi("setDateHeader");
}
public void setHeader(String name, String value)
{
- nyi("setHeader");
}
public void setIntHeader(String name, int value)
{
- nyi("setIntHeader");
}
public void sendRedirect(Link link) throws IOException
@@ -113,5 +112,17 @@
{
committed = false;
link = null;
+
+ renderedDocument = null;
+ }
+
+ public Document getRenderedDocument()
+ {
+ return renderedDocument;
+ }
+
+ public void setRenderedDocument(Document document)
+ {
+ renderedDocument = document;
}
}
Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/ComponentEventRequestParameters.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/ComponentEventRequestParameters.java?rev=740971&r1=740970&r2=740971&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/ComponentEventRequestParameters.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/ComponentEventRequestParameters.java Thu Feb 5 01:40:29 2009
@@ -15,6 +15,7 @@
package org.apache.tapestry5.services;
import org.apache.tapestry5.EventContext;
+import org.apache.tapestry5.internal.TapestryInternalUtils;
import org.apache.tapestry5.ioc.internal.util.Defense;
/**
@@ -67,26 +68,9 @@
if (!eventType.equals(that.eventType)) return false;
if (!nestedComponentId.equals(that.nestedComponentId)) return false;
- if (!isEqual(eventContext, that.eventContext)) return false;
+ if (!TapestryInternalUtils.isEqual(eventContext, that.eventContext)) return false;
- return isEqual(pageActivationContext, that.pageActivationContext);
- }
-
- private boolean isEqual(EventContext left, EventContext right)
- {
- if (left == right) return true;
-
- int count = left.getCount();
-
- if (count != right.getCount()) return false;
-
- for (int i = 0; i < count; i++)
- {
- if (!left.get(Object.class, i).equals(right.get(Object.class, i)))
- return false;
- }
-
- return true;
+ return TapestryInternalUtils.isEqual(pageActivationContext, that.pageActivationContext);
}
Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/PageRenderRequestParameters.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/PageRenderRequestParameters.java?rev=740971&r1=740970&r2=740971&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/PageRenderRequestParameters.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/PageRenderRequestParameters.java Thu Feb 5 01:40:29 2009
@@ -1,4 +1,4 @@
-// Copyright 2008 The Apache Software Foundation
+// Copyright 2008, 2009 The Apache Software Foundation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -15,6 +15,7 @@
package org.apache.tapestry5.services;
import org.apache.tapestry5.EventContext;
+import org.apache.tapestry5.internal.TapestryInternalUtils;
import org.apache.tapestry5.ioc.internal.util.Defense;
/**
@@ -46,4 +47,24 @@
{
return activationContext;
}
+
+ @Override
+ public boolean equals(Object obj)
+ {
+ if (this == obj) return true;
+
+ if (obj == null || getClass() != obj.getClass()) return false;
+
+ PageRenderRequestParameters other = (PageRenderRequestParameters) obj;
+
+ return logicalPageName.equals(other.logicalPageName) &&
+ TapestryInternalUtils.isEqual(activationContext, other.activationContext);
+ }
+
+
+ @Override
+ public String toString()
+ {
+ return String.format("PageRenderRequestParameters[%s]", logicalPageName);
+ }
}
Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/test/PageTester.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/test/PageTester.java?rev=740971&r1=740970&r2=740971&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/test/PageTester.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/test/PageTester.java Thu Feb 5 01:40:29 2009
@@ -14,52 +14,51 @@
package org.apache.tapestry5.test;
+import org.apache.tapestry5.Link;
import org.apache.tapestry5.dom.Document;
import org.apache.tapestry5.dom.Element;
-import org.apache.tapestry5.dom.Node;
+import org.apache.tapestry5.dom.Visitor;
import org.apache.tapestry5.internal.InternalConstants;
import org.apache.tapestry5.internal.SingleKeySymbolProvider;
import org.apache.tapestry5.internal.TapestryAppInitializer;
-import org.apache.tapestry5.internal.services.*;
-import org.apache.tapestry5.internal.test.*;
+import org.apache.tapestry5.internal.test.PageTesterContext;
+import org.apache.tapestry5.internal.test.PageTesterModule;
+import org.apache.tapestry5.internal.test.TestableRequest;
+import org.apache.tapestry5.internal.test.TestableResponse;
import org.apache.tapestry5.ioc.Registry;
import org.apache.tapestry5.ioc.def.ModuleDef;
-import static org.apache.tapestry5.ioc.internal.util.CollectionFactory.newMap;
-import static org.apache.tapestry5.ioc.internal.util.Defense.notNull;
+import org.apache.tapestry5.ioc.internal.util.Defense;
+import org.apache.tapestry5.ioc.internal.util.InternalUtils;
import org.apache.tapestry5.ioc.services.SymbolProvider;
-import org.apache.tapestry5.ioc.services.ThreadLocale;
-import org.apache.tapestry5.ioc.util.StrategyRegistry;
import org.apache.tapestry5.services.ApplicationGlobals;
-import org.apache.tapestry5.services.ContextPathEncoder;
+import org.apache.tapestry5.services.RequestHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import java.io.IOException;
import java.util.Locale;
import java.util.Map;
/**
- * This class is used to run a Tapestry app in an in-process testing environment. You can ask it to render a certain
- * page and check the DOM object created. You can also ask it to click on a link element in the DOM object to get the
- * next page. Because no servlet container is required, it is very fast and you can directly debug into your code in
- * your IDE.
+ * This class is used to run a Tapestry app in a single-threaded, in-process testing environment. You can ask it to
+ * render a certain page and check the DOM object created. You can also ask it to click on a link element in the DOM
+ * object to get the next page. Because no servlet container is required, it is very fast and you can directly debug
+ * into your code in your IDE.
*/
-public class PageTester implements ComponentInvoker
+public class PageTester
{
+ @SuppressWarnings({ "FieldCanBeLocal" })
private final Logger logger = LoggerFactory.getLogger(PageTester.class);
private final Registry registry;
- private final ComponentInvocationMap invocationMap;
-
private final TestableRequest request;
- private final StrategyRegistry<ComponentInvoker> invokerRegistry;
-
- private final ThreadLocale threadLocale;
+ private final TestableResponse response;
- private final ContextPathEncoder contextPathEncoder;
+ // private final StrategyRegistry<ComponentInvoker> invokerRegistry;
- private Locale preferedLanguage;
+ private final RequestHandler requestHandler;
public static final String DEFAULT_CONTEXT_PATH = "src/main/webapp";
@@ -89,8 +88,6 @@
*/
public PageTester(String appPackage, String appName, String contextPath, Class... moduleClasses)
{
- preferedLanguage = Locale.ENGLISH;
-
SymbolProvider provider = new SingleKeySymbolProvider(InternalConstants.TAPESTRY_APP_PACKAGE_PARAM, appPackage);
TapestryAppInitializer initializer = new TapestryAppInitializer(logger, provider, appName,
@@ -102,25 +99,16 @@
registry = initializer.createRegistry();
- request = registry.getObject(TestableRequest.class, null);
-
- threadLocale = registry.getService(ThreadLocale.class);
-
- invocationMap = registry.getObject(ComponentInvocationMap.class, null);
+ request = registry.getService(TestableRequest.class);
+ response = registry.getService(TestableResponse.class);
ApplicationGlobals globals = registry.getObject(ApplicationGlobals.class, null);
globals.storeContext(new PageTesterContext(contextPath));
- Map<Class, ComponentInvoker> map = newMap();
-
- map.put(PageRenderTarget.class, new PageRenderInvoker(registry, this, invocationMap));
-
- map.put(ComponentEventTarget.class, new ComponentEventInvoker(registry, this, invocationMap));
-
- invokerRegistry = StrategyRegistry.newInstance(ComponentInvoker.class, map);
+ requestHandler = registry.getService("RequestHandler", RequestHandler.class);
- contextPathEncoder = registry.getService(ContextPathEncoder.class);
+ request.setLocale(Locale.ENGLISH);
}
/**
@@ -134,7 +122,8 @@
/**
- * You should call it after use
+ * Invoke this method when done using the PageTester; it shuts down the internal {@link
+ * org.apache.tapestry5.ioc.Registry} used by the tester.
*/
public void shutdown()
{
@@ -169,50 +158,170 @@
*/
public Document renderPage(String pageName)
{
- return invoke(
- new ComponentInvocationImpl(contextPathEncoder, new PageRenderTarget(pageName), null, null, false));
+ request.clear().setPath("/" + pageName);
+
+ while (true)
+ {
+ try
+ {
+ response.clear();
+
+ boolean handled = requestHandler.service(request, response);
+
+ if (!handled)
+ {
+ throw new RuntimeException(
+ String.format("Request was not handled: '%s' may not be a valid page name.", pageName));
+ }
+
+ Link link = response.getRedirectLink();
+
+ if (link != null)
+ {
+ setupRequestFromLink(link);
+ continue;
+ }
+
+ Document result = response.getRenderedDocument();
+
+ if (result == null)
+ throw new RuntimeException(
+ String.format("Render of page '%s' did not result in a Document.", pageName));
+
+
+ return result;
+ }
+ catch (IOException ex)
+ {
+ throw new RuntimeException(ex);
+ }
+
+ }
+
}
/**
* Simulates a click on a link.
*
- * @param link The Link object to be "clicked" on.
+ * @param linkElement The Link object to be "clicked" on.
* @return The DOM created. Typically you will assert against it.
*/
- public Document clickLink(Element link)
+
+ public Document clickLink(Element linkElement)
{
- notNull(link, "link");
+ Defense.notNull(linkElement, "link");
+
+ validateElementName(linkElement, "a");
+
+ String href = extractNonBlank(linkElement, "href");
- ComponentInvocation invocation = getInvocation(link);
+ setupRequestFromURI(href);
- return invoke(invocation);
+ return runComponentEventRequest();
}
- private ComponentInvocation getInvocation(Element element)
+ private String extractNonBlank(Element element, String attributeName)
{
- ComponentInvocation invocation = invocationMap.get(element);
+ String result = element.getAttribute(attributeName);
- if (invocation == null)
- throw new IllegalArgumentException("No component invocation object is associated with the Element.");
+ if (InternalUtils.isBlank(result))
+ throw new RuntimeException(
+ String.format("The %s attribute of the <%s> element was blank or missing.",
+ element.getName(), attributeName));
- return invocation;
+ return result;
}
- public Document invoke(ComponentInvocation invocation)
+ private void validateElementName(Element element, String expectedElementName)
{
- // It is critical to clear the map before invoking an invocation (render a page or click a
- // link).
- invocationMap.clear();
+ if (!element.getName().equalsIgnoreCase(expectedElementName))
+ throw new RuntimeException(
+ String.format("The element must be type '%s', not '%s'.", expectedElementName, element.getName()));
+ }
+
+ private Document runComponentEventRequest()
+ {
+ while (true)
+ {
+ response.clear();
+
+ try
+ {
+ boolean handled = requestHandler.service(request, response);
+
+ if (!handled)
+ throw new RuntimeException(String.format("Request for path '%s' was not handled by Tapestry.",
+ request.getPath()));
+
+ Link link = response.getRedirectLink();
+
+ if (link != null)
+ {
+ setupRequestFromLink(link);
+ continue;
+ }
+
+ Document result = response.getRenderedDocument();
+
+ if (result == null)
+ throw new RuntimeException(
+ String.format("Render request '%s' did not result in a Document.", request.getPath()));
+
+ return result;
+ }
+ catch (IOException ex)
+ {
+ throw new RuntimeException(ex);
+ }
+
+ }
+
+
+ }
- threadLocale.setLocale(preferedLanguage);
+ private void setupRequestFromLink(Link link)
+ {
+ setupRequestFromURI(link.toRedirectURI());
+ }
+
+ private void setupRequestFromURI(String URI)
+ {
+ String linkPath = stripContextFromPath(URI);
- ComponentInvoker invoker = invokerRegistry.getByInstance(invocation.getTarget());
+ int comma = linkPath.indexOf('?');
- return invoker.invoke(invocation);
+ String path = comma < 0
+ ? linkPath
+ : linkPath.substring(0, comma);
+
+ request.clear().setPath(path);
+
+ if (comma > 0)
+ decodeParametersIntoRequest(path.substring(comma + 1));
+ }
+
+ private void decodeParametersIntoRequest(String queryString)
+ {
+ if (InternalUtils.isNonBlank(queryString))
+ throw new RuntimeException("Have not yet implemented this method.");
+ }
+
+ private String stripContextFromPath(String path)
+ {
+ String contextPath = request.getContextPath();
+
+ if (contextPath.equals("")) return path;
+
+ if (!path.startsWith(contextPath))
+ throw new RuntimeException(String.format("Path '%s' does not start with context path '%s'.",
+ path, contextPath));
+
+ return path.substring(contextPath.length());
}
/**
- * Simulates a submission of the form specified. The caller can specify values for the form fields.
+ * Simulates a submission of the form specified. The caller can specify values for the form fields, which act as
+ * overrides on the values stored inside the elements.
*
* @param form the form to be submitted.
* @param parameters the query parameter name/value pairs
@@ -220,17 +329,96 @@
*/
public Document submitForm(Element form, Map<String, String> parameters)
{
- notNull(form, "form");
+ Defense.notNull(form, "form");
+
+ validateElementName(form, "form");
+
+ request.clear().setPath(stripContextFromPath(extractNonBlank(form, "action")));
+
+ pushFieldValuesIntoRequest(form);
+
+ overrideParameters(parameters);
+
+ // addHiddenFormFields(form);
+
+ // ComponentInvocation invocation = getInvocation(form);
+
+ return runComponentEventRequest();
+ }
+
+ private void overrideParameters(Map<String, String> fieldValues)
+ {
+ for (Map.Entry<String, String> e : fieldValues.entrySet())
+ {
+ request.overrideParameter(e.getKey(), e.getValue());
+ }
+ }
+
+ private void pushFieldValuesIntoRequest(Element form)
+ {
+ Visitor visitor = new Visitor()
+ {
+ public void visit(Element element)
+ {
+ if (InternalUtils.isNonBlank(element.getAttribute("disabled")))
+ return;
+
+ String name = element.getName();
+
+ if (name.equals("input"))
+ {
+ String type = extractNonBlank(element, "type");
+
+ if (type.equals("radio") || type.equals("checkbox"))
+ {
+ if (InternalUtils.isBlank(element.getAttribute("checked")))
+ return;
+ }
- request.clear();
+ // Assume that, if the element is a button/submit, it wasn't clicked,
+ // and therefore, is not part of the submission.
- request.loadParameters(parameters);
+ if (type.equals("button") || type.equals("submit"))
+ return;
- addHiddenFormFields(form);
+ // Handle radio, checkbox, text, radio, hidden
+ String value = element.getAttribute("value");
- ComponentInvocation invocation = getInvocation(form);
+ if (InternalUtils.isNonBlank(value))
+ request.loadParameter(extractNonBlank(element, "name"), value);
- return invoke(invocation);
+ return;
+ }
+
+ if (name.equals("option"))
+ {
+ String value = element.getAttribute("value");
+
+ // TODO: If value is blank do we use the content, or is the content only the label?
+
+ if (InternalUtils.isNonBlank(element.getAttribute("selected")))
+ {
+ String selectName = extractNonBlank(findAncestor(element, "select"), "name");
+
+ request.loadParameter(selectName, value);
+ }
+
+ return;
+ }
+
+ if (name.equals("textarea"))
+ {
+ String content = element.getChildMarkup();
+
+ if (InternalUtils.isNonBlank(content))
+ request.loadParameter(extractNonBlank(element, "name"), content);
+
+ return;
+ }
+ }
+ };
+
+ form.visit(visitor);
}
/**
@@ -243,18 +431,26 @@
*/
public Document clickSubmit(Element submitButton, Map<String, String> fieldValues)
{
- notNull(submitButton, "submitButton");
+ Defense.notNull(submitButton, "submitButton");
assertIsSubmit(submitButton);
Element form = getFormAncestor(submitButton);
+
+ request.clear().setPath(stripContextFromPath(extractNonBlank(form, "action")));
+
+ pushFieldValuesIntoRequest(form);
+
+ overrideParameters(fieldValues);
+
String value = submitButton.getAttribute("value");
- if (value == null) value = DEFAULT_SUBMIT_VALUE_ATTRIBUTE;
+ if (value == null)
+ value = DEFAULT_SUBMIT_VALUE_ATTRIBUTE;
- fieldValues.put(submitButton.getAttribute("name"), value);
+ request.overrideParameter(extractNonBlank(submitButton, "name"), value);
- return submitForm(form, fieldValues);
+ return runComponentEventRequest();
}
private void assertIsSubmit(Element element)
@@ -271,37 +467,34 @@
private Element getFormAncestor(Element element)
{
- while (true)
- {
- if (element == null) throw new IllegalArgumentException("The given element is not contained by a form.");
-
- if (element.getName().equalsIgnoreCase("form")) return element;
-
- element = element.getParent();
- }
+ return findAncestor(element, "form");
}
- private void addHiddenFormFields(Element element)
+ private Element findAncestor(Element element, String ancestorName)
{
- if (isHiddenFormField(element))
- request.loadParameter(element.getAttribute("name"), element.getAttribute("value"));
+ Element e = element;
- for (Node child : element.getChildren())
+ while (e != null)
{
- if (child instanceof Element)
- {
- addHiddenFormFields((Element) child);
- }
+ if (e.getName().equalsIgnoreCase(ancestorName))
+ return e;
+
+ e = e.getParent();
}
- }
- private boolean isHiddenFormField(Element element)
- {
- return element.getName().equalsIgnoreCase("input") && "hidden".equalsIgnoreCase(element.getAttribute("type"));
+ throw new RuntimeException(
+ String.format("Could not locate an ancestor element of type '%s'.", ancestorName));
+
}
+ /**
+ * Sets the simulated browser's preferred language, i.e., the value returned from {@link
+ * org.apache.tapestry5.services.Request#getLocale()}.
+ *
+ * @param preferedLanguage preferred language setting
+ */
public void setPreferedLanguage(Locale preferedLanguage)
{
- this.preferedLanguage = preferedLanguage;
+ request.setLocale(preferedLanguage);
}
}