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 2010/06/25 02:06:05 UTC

svn commit: r957763 - in /tapestry/tapestry5/trunk/tapestry-core/src: main/java/org/apache/tapestry5/annotations/ main/java/org/apache/tapestry5/internal/transform/ main/java/org/apache/tapestry5/services/ test/app1/ test/conf/ test/groovy/org/apache/t...

Author: hlship
Date: Fri Jun 25 00:06:04 2010
New Revision: 957763

URL: http://svn.apache.org/viewvc?rev=957763&view=rev
Log:
TAP5-1190: Add @QueryParameterMapped annotation and supporting logic

Added:
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/annotations/QueryParameterMapped.java   (with props)
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/QueryParameterMappedWorker.java   (with props)
    tapestry/tapestry5/trunk/tapestry-core/src/test/app1/QueryParameterMappedDemo.tml
    tapestry/tapestry5/trunk/tapestry-core/src/test/groovy/org/apache/tapestry5/integration/
    tapestry/tapestry5/trunk/tapestry-core/src/test/groovy/org/apache/tapestry5/integration/app1/
    tapestry/tapestry5/trunk/tapestry-core/src/test/groovy/org/apache/tapestry5/integration/app1/QueryParameterMappedTests.groovy
    tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/QueryParameterMappedDemo.java   (with props)
Modified:
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/annotations/Persist.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/annotations/QueryParameter.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/TapestryModule.java
    tapestry/tapestry5/trunk/tapestry-core/src/test/conf/testng-limited.xml
    tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/Index.java

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/annotations/Persist.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/annotations/Persist.java?rev=957763&r1=957762&r2=957763&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/annotations/Persist.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/annotations/Persist.java Fri Jun 25 00:06:04 2010
@@ -1,4 +1,4 @@
-// Copyright 2006, 2007, 2008, 2009 The Apache Software Foundation
+// Copyright 2006, 2007, 2008, 2009, 2010 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.
@@ -40,6 +40,7 @@ import org.apache.tapestry5.ioc.annotati
  *
  * @see org.apache.tapestry5.services.MetaDataLocator
  * @see org.apache.tapestry5.PersistenceConstants
+ * @see QueryParameterMapped
  */
 @Target(FIELD)
 @Documented

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/annotations/QueryParameter.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/annotations/QueryParameter.java?rev=957763&r1=957762&r2=957763&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/annotations/QueryParameter.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/annotations/QueryParameter.java Fri Jun 25 00:06:04 2010
@@ -24,6 +24,7 @@ import java.lang.annotation.Documented;
 import java.lang.annotation.Retention;
 import java.lang.annotation.Target;
 
+import org.apache.tapestry5.internal.transform.OnEventWorker;
 import org.apache.tapestry5.ioc.annotations.UseWith;
 import org.apache.tapestry5.ioc.services.TypeCoercer;
 import org.apache.tapestry5.services.Request;
@@ -31,12 +32,15 @@ import org.apache.tapestry5.services.Req
 /**
  * Annotation that may be placed on parameters of event handler methods.
  * Annotated parameters will be {@linkplain Request#getParameter(String) extracted from the request},
- * then {@linkplain TypeCoercer coerced} to the type of the parameter. Such parameters are seperate
+ * then {@linkplain TypeCoercer coerced} to the type of the parameter. Such parameters are separate
  * from ordinary context parameters (extracted from the Request path). Typically, this is used when
  * client-side JavaScript adds a query parameter to a request to communicate some information from the client
  * side to the server side.
+ * <p>
+ * Individual fields may also be directly mapped to query parameters using the {@link QueryParameterMapped} annotation.
  * 
  * @since 5.2.0
+ * @see OnEventWorker
  */
 @Target(
 { PARAMETER })

Added: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/annotations/QueryParameterMapped.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/annotations/QueryParameterMapped.java?rev=957763&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/annotations/QueryParameterMapped.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/annotations/QueryParameterMapped.java Fri Jun 25 00:06:04 2010
@@ -0,0 +1,67 @@
+// Copyright 2010 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.annotations;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+import static org.apache.tapestry5.ioc.annotations.AnnotationUseContext.PAGE;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import org.apache.tapestry5.ComponentResources;
+import org.apache.tapestry5.EventConstants;
+import org.apache.tapestry5.Link;
+import org.apache.tapestry5.ValueEncoder;
+import org.apache.tapestry5.ioc.annotations.UseWith;
+import org.apache.tapestry5.services.ValueEncoderSource;
+
+/**
+ * Marks a field of a page (not a component) as persistent within the URL. The field is mapped
+ * to a query parameter. When component event or page render links are generated for the page,
+ * additional values will be added to the {@link Link} (via the {@link EventConstants#DECORATE_COMPONENT_EVENT_LINK} or
+ * {@link EventConstants#DECORATE_PAGE_RENDER_LINK} events).
+ * <p>
+ * Care must be taken that the query parameter name does not clash with a query parameter name generated for a form
+ * field.
+ * <p>
+ * The field may be of any type; a {@link ValueEncoder} (from the {@link ValueEncoderSource}) will be used to convert
+ * between client-side and server-side representations. Null values are not added as query parameters (just non-null).
+ * <p>
+ * When a page is activated, the mapped fields will receive their values before an {@linkplain EventConstants#ACTIVATE
+ * activate} event handler method is invoked.
+ * <p>
+ * This annotation is an alternative to {@link Persist}.
+ * <p>
+ * Fields annotated with QueryParameterMapped are <em>not</em> considered persistent (its a process parallel to the one
+ * related to the {@link Persist} annotation). Invoking {@link ComponentResources#discardPersistentFieldChanges()} will
+ * <em>not</em> affect annotated fields, only assigning them back to null will.
+ * 
+ * @see QueryParameter
+ */
+@Target(
+{ ElementType.FIELD })
+@Retention(RUNTIME)
+@Documented
+@UseWith(
+{ PAGE })
+public @interface QueryParameterMapped
+{
+    /** The name of the query parameter, which defaults to the name of the field. */
+    String value() default "";
+
+    // TODO: Attributes to limit it to just render links, or just component event links?
+}

Propchange: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/annotations/QueryParameterMapped.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/QueryParameterMappedWorker.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/QueryParameterMappedWorker.java?rev=957763&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/QueryParameterMappedWorker.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/QueryParameterMappedWorker.java Fri Jun 25 00:06:04 2010
@@ -0,0 +1,163 @@
+// Copyright 2010 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.transform;
+
+import org.apache.tapestry5.EventConstants;
+import org.apache.tapestry5.Link;
+import org.apache.tapestry5.ValueEncoder;
+import org.apache.tapestry5.annotations.QueryParameterMapped;
+import org.apache.tapestry5.internal.services.ComponentClassCache;
+import org.apache.tapestry5.model.MutableComponentModel;
+import org.apache.tapestry5.runtime.Component;
+import org.apache.tapestry5.runtime.ComponentEvent;
+import org.apache.tapestry5.services.*;
+
+/**
+ * Hooks the activate event handler on the component (presumably, a page) to
+ * extract query parameters, and hooks the link decoration events to extract values
+ * and add them to the {@link Link}.
+ * 
+ * @see QueryParameterMapped
+ * @since 5.2.0
+ */
+public class QueryParameterMappedWorker implements ComponentClassTransformWorker
+{
+    private final Request request;
+
+    private final ComponentClassCache classCache;
+
+    private final ValueEncoderSource valueEncoderSource;
+
+    interface EventHandler
+    {
+        void invoke(Component component, ComponentEvent event);
+    }
+
+    public QueryParameterMappedWorker(Request request, ComponentClassCache classCache,
+            ValueEncoderSource valueEncoderSource)
+    {
+        this.request = request;
+        this.classCache = classCache;
+        this.valueEncoderSource = valueEncoderSource;
+    }
+
+    public void transform(ClassTransformation transformation, MutableComponentModel model)
+    {
+        for (TransformField field : transformation.matchFieldsWithAnnotation(QueryParameterMapped.class))
+        {
+            mapFieldToQueryParameter(field, transformation, model);
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    private void mapFieldToQueryParameter(TransformField field, ClassTransformation transformation,
+            MutableComponentModel model)
+    {
+        QueryParameterMapped annotation = field.getAnnotation(QueryParameterMapped.class);
+
+        String parameterName = getParameterName(field, annotation);
+
+        TransformMethod dispatchMethod = transformation.getOrCreateMethod(TransformConstants.DISPATCH_COMPONENT_EVENT);
+
+        // Assumption: the field type is not one that's loaded by the component class loader, so it's safe
+        // to convert to a hard type during class transformation.
+
+        Class fieldType = classCache.forName(field.getType());
+
+        ValueEncoder encoder = valueEncoderSource.getValueEncoder(fieldType);
+
+        FieldAccess access = field.getAccess();
+
+        setValueFromInitializeEventHandler(access, parameterName, encoder, dispatchMethod, model);
+        decorateLinks(access, parameterName, encoder, dispatchMethod, model);
+    }
+
+    @SuppressWarnings("unchecked")
+    private void setValueFromInitializeEventHandler(final FieldAccess access, final String parameterName,
+            final ValueEncoder encoder, TransformMethod dispatchMethod, MutableComponentModel model)
+    {
+        EventHandler handler = new EventHandler()
+        {
+            public void invoke(Component component, ComponentEvent event)
+            {
+                String clientValue = request.getParameter(parameterName);
+
+                if (clientValue == null)
+                    return;
+
+                Object value = encoder.toValue(clientValue);
+
+                access.write(component, value);
+            }
+        };
+
+        add(dispatchMethod, model, EventConstants.ACTIVATE, handler);
+    }
+
+    @SuppressWarnings("unchecked")
+    private void decorateLinks(final FieldAccess access, final String parameterName, final ValueEncoder encoder,
+            TransformMethod dispatchMethod, MutableComponentModel model)
+    {
+        EventHandler handler = new EventHandler()
+        {
+            public void invoke(Component component, ComponentEvent event)
+            {
+                Object value = access.read(component);
+
+                if (value == null)
+                    return;
+
+                Link link = event.getEventContext().get(Link.class, 0);
+
+                String clientValue = encoder.toClient(value);
+
+                link.addParameter(parameterName, clientValue);
+            }
+        };
+
+        add(dispatchMethod, model, EventConstants.DECORATE_COMPONENT_EVENT_LINK, handler);
+        add(dispatchMethod, model, EventConstants.DECORATE_PAGE_RENDER_LINK, handler);
+    }
+
+    private void add(TransformMethod dispatchMethod, MutableComponentModel model, final String eventType,
+            final EventHandler handler)
+    {
+        dispatchMethod.addAdvice(new ComponentMethodAdvice()
+        {
+            public void advise(ComponentMethodInvocation invocation)
+            {
+                ComponentEvent event = (ComponentEvent) invocation.getParameter(0);
+
+                if (event.matches(eventType, "", 0))
+                {
+                    handler.invoke(invocation.getInstance(), event);
+                }
+                
+                invocation.proceed();
+            }
+        });
+
+        model.addEventHandler(eventType);
+    }
+
+    private String getParameterName(TransformField field, QueryParameterMapped annotation)
+    {
+        if (annotation.value().equals(""))
+            return field.getName();
+
+        return annotation.value();
+    }
+
+}

Propchange: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/QueryParameterMappedWorker.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/TapestryModule.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/TapestryModule.java?rev=957763&r1=957762&r2=957763&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/TapestryModule.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/TapestryModule.java Fri Jun 25 00:06:04 2010
@@ -588,6 +588,8 @@ public final class TapestryModule
      * <dd>Checks for the {@link PageReset} annotation
      * <dt>HeartbeatDeferred
      * <dd>Support for the {@link HeartbeatDeferred} annotation
+     * <dt>QueryParameterMapped
+     * <dd>Support for the {@link QueryParameterMapped} annotation
      * </dl>
      */
     public static void contributeComponentClassTransformWorker(
@@ -607,6 +609,7 @@ public final class TapestryModule
         configuration.add("MixinAfter", new MixinAfterWorker());
         configuration.add("Component", new ComponentWorker(resolver));
         configuration.add("Mixin", new MixinWorker(resolver));
+        configuration.addInstance("QueryParameterMapped", QueryParameterMappedWorker.class, "before:OnEvent");
         configuration.addInstance("OnEvent", OnEventWorker.class);
         configuration.add("SupportsInformalParameters", new SupportsInformalParametersWorker());
         configuration.addInstance("InjectPage", InjectPageWorker.class);

Added: tapestry/tapestry5/trunk/tapestry-core/src/test/app1/QueryParameterMappedDemo.tml
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/app1/QueryParameterMappedDemo.tml?rev=957763&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/app1/QueryParameterMappedDemo.tml (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/app1/QueryParameterMappedDemo.tml Fri Jun 25 00:06:04 2010
@@ -0,0 +1,25 @@
+<html t:type="Border" xmlns:t="http://tapestry.apache.org/schema/tapestry_5_0_0.xsd">
+
+  <h1>@QueryParameterMapped Demo</h1>
+
+  <dl>
+    <dt>Click count:</dt>
+    <dd id="clickCount">${clickCount}</dd>
+    <dt>Click count set:</dt>
+    <dd id="clickCountSet">${clickCountSet}</dd>
+    <dt>Message:</dt>
+    <dd id="message">${message}</dd>
+  </dl>
+
+  <ul>
+    <li>
+      <t:actionlink t:id="increment">increment count</t:actionlink>
+    </li>
+    <li>
+      <t:actionlink t:id="setMessage">set message</t:actionlink>
+    </li>
+    <li>
+      <t:actionlink t:id="reset">reset</t:actionlink>
+    </li>
+  </ul>
+</html>
\ No newline at end of file

Modified: tapestry/tapestry5/trunk/tapestry-core/src/test/conf/testng-limited.xml
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/conf/testng-limited.xml?rev=957763&r1=957762&r2=957763&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/conf/testng-limited.xml (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/conf/testng-limited.xml Fri Jun 25 00:06:04 2010
@@ -11,7 +11,7 @@
       <class name="org.apache.tapestry5.test.SeleniumLauncher"/>
 
       <!--  Modify classes below as needed. -->
-      <class name="org.apache.tapestry5.integration.app1.AjaxTests"/>
+      <class name="org.apache.tapestry5.integration.app1.QueryParameterMappedTests"/>
 
     </classes>
   </test>

Added: tapestry/tapestry5/trunk/tapestry-core/src/test/groovy/org/apache/tapestry5/integration/app1/QueryParameterMappedTests.groovy
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/groovy/org/apache/tapestry5/integration/app1/QueryParameterMappedTests.groovy?rev=957763&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/groovy/org/apache/tapestry5/integration/app1/QueryParameterMappedTests.groovy (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/groovy/org/apache/tapestry5/integration/app1/QueryParameterMappedTests.groovy Fri Jun 25 00:06:04 2010
@@ -0,0 +1,42 @@
+// Copyright 2010 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.integration.app1;
+
+import org.apache.tapestry5.integration.TapestryCoreTestCase 
+import org.testng.annotations.Test 
+
+class QueryParameterMappedTests extends TapestryCoreTestCase
+{
+    @Test
+    void basic_links()
+    {
+        clickThru "@QueryParameterMapped Demo"
+        
+        assertText("clickCount", "")
+        assertText("clickCountSet", "false")
+        assertText("message", "")
+        
+        clickAndWait "link=increment count"
+        
+        assertText("clickCount", "1")
+        assertText("clickCountSet", "true")
+        
+        clickAndWait "link=set message"
+        
+        assertText("clickCount", "1")
+        assertText("clickCountSet", "true")
+        assertText("message", "Link clicked!")        
+    }
+}

Modified: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/Index.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/Index.java?rev=957763&r1=957762&r2=957763&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/Index.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/Index.java Fri Jun 25 00:06:04 2010
@@ -67,6 +67,9 @@ public class Index
     private static final List<Item> ITEMS = CollectionFactory
             .newList(
 
+                    new Item("QueryParameterMappedDemo", "@QueryParameterMapped Demo",
+                            "Use of @QueryParameterMapped to encode page state into query parameters"),
+                            
                     new Item("LibraryMessagesDemo", "Library Messages Demo",
                             "Demo ability to contribute additional message catalog resources to the application global catalog."),
 

Added: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/QueryParameterMappedDemo.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/QueryParameterMappedDemo.java?rev=957763&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/QueryParameterMappedDemo.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/QueryParameterMappedDemo.java Fri Jun 25 00:06:04 2010
@@ -0,0 +1,54 @@
+// Copyright 2010 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.integration.app1.pages;
+
+import org.apache.tapestry5.annotations.Property;
+import org.apache.tapestry5.annotations.QueryParameterMapped;
+
+public class QueryParameterMappedDemo
+{
+    
+    @Property
+    private boolean clickCountSet;
+    
+    @Property
+    @QueryParameterMapped
+    private Integer clickCount;
+
+    @Property
+    @QueryParameterMapped
+    private String message;
+
+    void onActivate()
+    {
+        clickCountSet = clickCount != null;
+    }
+    
+    void onActionFromIncrement()
+    {
+        clickCount = clickCount == null ? 1 : clickCount + 1;
+    }
+
+    void onActionFromSetMessage()
+    {
+        message = "Link clicked!";
+    }
+
+    void onActionFromReset()
+    {
+        clickCount = null;
+        message = null;
+    }
+}

Propchange: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/QueryParameterMappedDemo.java
------------------------------------------------------------------------------
    svn:eol-style = native