You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tapestry.apache.org by hl...@apache.org on 2010/03/03 02:42:37 UTC

svn commit: r918301 - in /tapestry/tapestry5/trunk/tapestry-core/src: main/java/org/apache/tapestry5/annotations/ main/java/org/apache/tapestry5/corelib/components/ main/java/org/apache/tapestry5/corelib/mixins/ main/java/org/apache/tapestry5/internal/...

Author: hlship
Date: Wed Mar  3 01:42:37 2010
New Revision: 918301

URL: http://svn.apache.org/viewvc?rev=918301&view=rev
Log:
TAP5-156: Add a @QueryParameter annotation for parameters to event handler method

Added:
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/annotations/QueryParameter.java   (with props)
    tapestry/tapestry5/trunk/tapestry-core/src/test/app1/QueryParameterDemo.tml
    tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/QueryParameterTests.java   (with props)
    tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/QueryParameterDemo.java   (with props)
Modified:
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/DateField.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/FormFragment.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/FormInjector.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Select.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/mixins/Autocomplete.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/InternalClassTransformationImpl.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/OnEventWorker.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/TapestryModule.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/TransformMethod.java
    tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/Index.java

Added: 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=918301&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/annotations/QueryParameter.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/annotations/QueryParameter.java Wed Mar  3 01:42:37 2010
@@ -0,0 +1,57 @@
+// 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.ElementType.PARAMETER;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+import static org.apache.tapestry5.ioc.annotations.AnnotationUseContext.COMPONENT;
+import static org.apache.tapestry5.ioc.annotations.AnnotationUseContext.MIXIN;
+import static org.apache.tapestry5.ioc.annotations.AnnotationUseContext.PAGE;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import org.apache.tapestry5.ioc.annotations.UseWith;
+import org.apache.tapestry5.ioc.services.TypeCoercer;
+import org.apache.tapestry5.services.Request;
+
+/**
+ * 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
+ * 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.
+ * 
+ * @since 5.2.0
+ */
+@Target(
+{ PARAMETER })
+@Retention(RUNTIME)
+@Documented
+@UseWith(
+{ COMPONENT, MIXIN, PAGE })
+public @interface QueryParameter
+{
+    /** The name of the query parameter to extract from the request. */
+    String value();
+
+    /**
+     * If false (the default), then an exception is thrown when the query parameter is read, if it is blank (null or an
+     * empty string). If true, then blank values are allowed.
+     */
+    boolean allowBlank() default false;
+}

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

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/DateField.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/DateField.java?rev=918301&r1=918300&r2=918301&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/DateField.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/DateField.java Wed Mar  3 01:42:37 2010
@@ -1,10 +1,10 @@
-// Copyright 2007, 2008, 2009 The Apache Software Foundation
+// Copyright 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.
 // You may obtain a copy of the License at
 //
-//     http://www.apache.org/licenses/LICENSE-2.0
+// 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,
@@ -14,8 +14,21 @@
 
 package org.apache.tapestry5.corelib.components;
 
+import java.text.DateFormat;
+import java.text.DateFormatSymbols;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.Locale;
+
 import org.apache.tapestry5.*;
-import org.apache.tapestry5.annotations.*;
+import org.apache.tapestry5.annotations.Environmental;
+import org.apache.tapestry5.annotations.Events;
+import org.apache.tapestry5.annotations.IncludeJavaScriptLibrary;
+import org.apache.tapestry5.annotations.IncludeStylesheet;
+import org.apache.tapestry5.annotations.Parameter;
+import org.apache.tapestry5.annotations.QueryParameter;
 import org.apache.tapestry5.corelib.base.AbstractField;
 import org.apache.tapestry5.ioc.Messages;
 import org.apache.tapestry5.ioc.annotations.Inject;
@@ -25,30 +38,21 @@
 import org.apache.tapestry5.services.ComponentDefaultProvider;
 import org.apache.tapestry5.services.Request;
 
-import java.text.DateFormat;
-import java.text.DateFormatSymbols;
-import java.text.ParseException;
-import java.text.SimpleDateFormat;
-import java.util.Calendar;
-import java.util.Date;
-import java.util.Locale;
-
 /**
  * A component used to collect a provided date from the user using a client-side JavaScript calendar. Non-JavaScript
  * clients can simply type into a text field.
  * <p/>
  * One wierd aspect here is that, because client-side JavaScript formatting and parsing is so limited, we (currently)
  * use Ajax to send the user's input to the server for parsing (before raising the popup) and formatting (after closing
- * the popup).  Wierd and inefficient, but easier than writing client-side JavaScript for that purpose.
+ * the popup). Wierd and inefficient, but easier than writing client-side JavaScript for that purpose.
  * <p/>
- * Tapestry's DateField component is a wrapper around <a href="http://webfx.eae.net/dhtml/datepicker/datepicker.html">WebFX
- * DatePicker</a>.
+ * Tapestry's DateField component is a wrapper around <a
+ * href="http://webfx.eae.net/dhtml/datepicker/datepicker.html">WebFX DatePicker</a>.
  */
 // TODO: More testing; see https://issues.apache.org/jira/browse/TAPESTRY-1844
 @IncludeStylesheet("${tapestry.datepicker}/css/datepicker.css")
-@IncludeJavaScriptLibrary({ "${tapestry.datepicker}/js/datepicker.js",
-        "datefield.js"
-})
+@IncludeJavaScriptLibrary(
+{ "${tapestry.datepicker}/js/datepicker.js", "datefield.js" })
 @Events(EventConstants.VALIDATE)
 public class DateField extends AbstractField
 {
@@ -59,7 +63,7 @@
     private Date value;
 
     /**
-     * Request attribute set to true if localization for the client-side DatePicker has been configured.  Used to ensure
+     * Request attribute set to true if localization for the client-side DatePicker has been configured. Used to ensure
      * that this only occurs once, regardless of how many DateFields are on the page.
      */
     static final String LOCALIZATION_CONFIGURED_FLAG = "tapestry.DateField.localization-configured";
@@ -73,7 +77,7 @@
     private DateFormat format;
 
     /**
-     * If true, then  the text field will be hidden, and only the icon for the date picker will be visible. The default
+     * If true, then the text field will be hidden, and only the icon for the date picker will be visible. The default
      * is false.
      */
     @Parameter
@@ -124,7 +128,6 @@
     private static final String ERROR = "error";
     private static final String INPUT_PARAMETER = "input";
 
-
     DateFormat defaultFormat()
     {
         DateFormat shortDateFormat = DateFormat.getDateInstance(DateFormat.SHORT, locale);
@@ -154,12 +157,12 @@
     /**
      * Ajax event handler, used when initiating the popup. The client sends the input value form the field to the server
      * to parse it according to the server-side format. The response contains a "result" key of the formatted date in a
-     * format acceptable to the JavaScript Date() constructor.  Alternately, an "error" key indicates the the input was
+     * format acceptable to the JavaScript Date() constructor. Alternately, an "error" key indicates the the input was
      * not formatted correct.
      */
-    JSONObject onParse()
+    JSONObject onParse(@QueryParameter(INPUT_PARAMETER)
+    String input)
     {
-        String input = request.getParameter(INPUT_PARAMETER);
         JSONObject response = new JSONObject();
 
         try
@@ -181,10 +184,9 @@
      * milliseconds since the epoch, to the server, which reformats it according to the server side format and returns
      * the result.
      */
-    JSONObject onFormat()
+    JSONObject onFormat(@QueryParameter(INPUT_PARAMETER)
+    String input)
     {
-        String input = request.getParameter(INPUT_PARAMETER);
-
         JSONObject response = new JSONObject();
 
         try
@@ -207,27 +209,28 @@
     {
         String value = tracker.getInput(this);
 
-        if (value == null) value = formatCurrentValue();
+        if (value == null)
+            value = formatCurrentValue();
 
         String clientId = getClientId();
         String triggerId = clientId + "-trigger";
 
         writer.element("input",
 
-                       "type", hideTextField ? "hidden" : "text",
+        "type", hideTextField ? "hidden" : "text",
 
-                       "name", getControlName(),
+        "name", getControlName(),
 
-                       "id", clientId,
+        "id", clientId,
 
-                       "value", value);
+        "value", value);
 
         writeDisabled(writer);
-        
+
         putPropertyNameIntoBeanValidationContext("value");
 
         validate.render(writer);
-        
+
         removePropertyNameFromBeanValidationContext();
 
         resources.renderInformalParameters(writer);
@@ -240,13 +243,13 @@
 
         writer.element("img",
 
-                       "id", triggerId,
+        "id", triggerId,
 
-                       "class", "t-calendar-trigger",
+        "class", "t-calendar-trigger",
 
-                       "src", icon.toClientURL(),
+        "src", icon.toClientURL(),
 
-                       "alt", "[Show]");
+        "alt", "[Show]");
         writer.end(); // img
 
         JSONObject setup = new JSONObject();
@@ -298,13 +301,14 @@
 
     private void writeDisabled(MarkupWriter writer)
     {
-        if (isDisabled()) writer.attributes("disabled", "disabled");
+        if (isDisabled())
+            writer.attributes("disabled", "disabled");
     }
 
-
     private String formatCurrentValue()
     {
-        if (value == null) return "";
+        if (value == null)
+            return "";
 
         return format.format(value);
     }
@@ -340,7 +344,7 @@
         {
             tracker.recordError(this, ex.getMessage());
         }
-        
+
         removePropertyNameFromBeanValidationContext();
     }
 

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/FormFragment.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/FormFragment.java?rev=918301&r1=918300&r2=918301&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/FormFragment.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/FormFragment.java Wed Mar  3 01:42:37 2010
@@ -1,10 +1,10 @@
-// Copyright 2008, 2009 The Apache Software Foundation
+// Copyright 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.
 // You may obtain a copy of the License at
 //
-//     http://www.apache.org/licenses/LICENSE-2.0
+// 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,
@@ -14,7 +14,13 @@
 
 package org.apache.tapestry5.corelib.components;
 
-import org.apache.tapestry5.*;
+import org.apache.tapestry5.BindingConstants;
+import org.apache.tapestry5.CSSClassConstants;
+import org.apache.tapestry5.ClientElement;
+import org.apache.tapestry5.ComponentAction;
+import org.apache.tapestry5.ComponentResources;
+import org.apache.tapestry5.MarkupWriter;
+import org.apache.tapestry5.RenderSupport;
 import org.apache.tapestry5.annotations.Environmental;
 import org.apache.tapestry5.annotations.Parameter;
 import org.apache.tapestry5.annotations.SupportsInformalParameters;
@@ -23,31 +29,44 @@
 import org.apache.tapestry5.corelib.internal.HiddenFieldPositioner;
 import org.apache.tapestry5.dom.Element;
 import org.apache.tapestry5.ioc.annotations.Inject;
-import org.apache.tapestry5.services.*;
+import org.apache.tapestry5.services.ClientBehaviorSupport;
+import org.apache.tapestry5.services.ClientDataEncoder;
+import org.apache.tapestry5.services.Environment;
+import org.apache.tapestry5.services.FormSupport;
+import org.apache.tapestry5.services.HiddenFieldLocationRules;
 import org.slf4j.Logger;
 
 /**
- * A FormFragment is a portion of a Form that may be selectively displayed.  Form elements inside a FormFragment will
- * automatically bypass validation when the fragment is invisible.  The trick is to also bypass server-side form
- * processing for such fields when the form is submitted; client-side logic "removes" the {@link
- * org.apache.tapestry5.corelib.components.Form#FORM_DATA form data} for the fragment if it is invisible when the form
+ * A FormFragment is a portion of a Form that may be selectively displayed. Form elements inside a FormFragment will
+ * automatically bypass validation when the fragment is invisible. The trick is to also bypass server-side form
+ * processing for such fields when the form is submitted; client-side logic "removes" the
+ * {@link org.apache.tapestry5.corelib.components.Form#FORM_DATA form data} for the fragment if it is invisible when the
+ * form
  * is submitted; alternately, client-side logic can simply remove the form fragment element (including its visible and
  * hidden fields) to prevent server-side processing.
  * <p/>
- * The client-side element has a new property, formFragment, added to it.  The formFragment object has new methods to
- * control the client-side behavior of the fragment: <dl> <dt>hide()</dt> <dd>Hides the element, using the configured
- * client-side animation effect.</dd> <dt>hideAndRemove()</dt> <dd>As with hide(), but the element is removed from the
- * DOM after being hidden.</dd> <dt>show()</dt> <dd>Makes the element visible, using the configured client-side
- * animation effect.</dd> <dt>toggle()</dt> <dd>Invokes hide() or show() as necessary.</dd> <dt>setVisible()</dt>
- * <dd>Passed a boolean parameter, invokes hide() or show() as necessary.</dd> </dl>
- *
+ * The client-side element has a new property, formFragment, added to it. The formFragment object has new methods to
+ * control the client-side behavior of the fragment:
+ * <dl>
+ * <dt>hide()</dt>
+ * <dd>Hides the element, using the configured client-side animation effect.</dd>
+ * <dt>hideAndRemove()</dt>
+ * <dd>As with hide(), but the element is removed from the DOM after being hidden.</dd>
+ * <dt>show()</dt>
+ * <dd>Makes the element visible, using the configured client-side animation effect.</dd>
+ * <dt>toggle()</dt>
+ * <dd>Invokes hide() or show() as necessary.</dd>
+ * <dt>setVisible()</dt>
+ * <dd>Passed a boolean parameter, invokes hide() or show() as necessary.</dd>
+ * </dl>
+ * 
  * @see org.apache.tapestry5.corelib.mixins.TriggerFragment
  */
 @SupportsInformalParameters
 public class FormFragment implements ClientElement
 {
     /**
-     * Determines if the fragment is intially visible or initially invisible (the default). This is only used when
+     * Determines if the fragment is initially visible or initially invisible (the default). This is only used when
      * rendering; when the form is submitted, the hidden field value is used to determine whether the elements within
      * the fragment should be processed (or ignored if still invisible).
      */
@@ -82,7 +101,6 @@
     @Parameter(name = "id", defaultPrefix = BindingConstants.LITERAL)
     private String idParameter;
 
-
     @Inject
     private Environment environment;
 
@@ -90,9 +108,6 @@
     private RenderSupport renderSupport;
 
     @Inject
-    private ComponentSource componentSource;
-
-    @Inject
     private ComponentResources resources;
 
     @Environmental
@@ -103,9 +118,6 @@
     private ComponentActionSink componentActions;
 
     @Inject
-    private Request request;
-
-    @Inject
     private Logger logger;
 
     @Inject
@@ -166,7 +178,7 @@
             }
         };
 
-        // Tada!  Now all the enclosed components will use our override of FormSupport,
+        // Tada! Now all the enclosed components will use our override of FormSupport,
         // until we pop it off.
 
         environment.push(FormSupport.class, override);
@@ -176,20 +188,18 @@
     /**
      * Closes the &lt;div&gt; tag and pops off the {@link org.apache.tapestry5.services.FormSupport} environmental
      * override.
-     *
+     * 
      * @param writer
      */
     void afterRender(MarkupWriter writer)
     {
-        hiddenFieldPositioner.getElement().attributes(
-                "type", "hidden",
+        hiddenFieldPositioner.getElement().attributes("type", "hidden",
 
-                "name", Form.FORM_DATA,
+        "name", Form.FORM_DATA,
 
-                "id", clientId + "-hidden",
+        "id", clientId + "-hidden",
 
-                "value", componentActions.getClientData()
-        );
+        "value", componentActions.getClientData());
 
         writer.end(); // div
 

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/FormInjector.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/FormInjector.java?rev=918301&r1=918300&r2=918301&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/FormInjector.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/FormInjector.java Wed Mar  3 01:42:37 2010
@@ -1,4 +1,4 @@
-// Copyright 2008, 2009 The Apache Software Foundation
+// Copyright 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.
@@ -14,27 +14,34 @@
 
 package org.apache.tapestry5.corelib.components;
 
+import java.io.IOException;
+
 import org.apache.tapestry5.*;
 import org.apache.tapestry5.annotations.Environmental;
 import org.apache.tapestry5.annotations.Events;
 import org.apache.tapestry5.annotations.Parameter;
+import org.apache.tapestry5.annotations.QueryParameter;
 import org.apache.tapestry5.annotations.SupportsInformalParameters;
 import org.apache.tapestry5.corelib.data.InsertPosition;
 import org.apache.tapestry5.corelib.internal.ComponentActionSink;
 import org.apache.tapestry5.corelib.internal.HiddenFieldPositioner;
 import org.apache.tapestry5.corelib.internal.InternalFormSupport;
 import org.apache.tapestry5.dom.Element;
-import org.apache.tapestry5.internal.services.ComponentResultProcessorWrapper;
 import org.apache.tapestry5.internal.services.PageRenderQueue;
 import org.apache.tapestry5.ioc.annotations.Inject;
 import org.apache.tapestry5.ioc.internal.util.IdAllocator;
-import org.apache.tapestry5.ioc.internal.util.InternalUtils;
 import org.apache.tapestry5.json.JSONObject;
-import org.apache.tapestry5.services.*;
+import org.apache.tapestry5.services.ClientBehaviorSupport;
+import org.apache.tapestry5.services.ClientDataEncoder;
+import org.apache.tapestry5.services.ComponentSource;
+import org.apache.tapestry5.services.Environment;
+import org.apache.tapestry5.services.FormSupport;
+import org.apache.tapestry5.services.Heartbeat;
+import org.apache.tapestry5.services.HiddenFieldLocationRules;
+import org.apache.tapestry5.services.PartialMarkupRenderer;
+import org.apache.tapestry5.services.PartialMarkupRendererFilter;
 import org.slf4j.Logger;
 
-import java.io.IOException;
-
 /**
  * A way to add new content to an existing Form. The FormInjector emulates its tag from the template (or uses a
  * &lt;div&gt;). When triggered, new content is obtained from the application and is injected before or after the
@@ -104,9 +111,6 @@
     private ComponentResources resources;
 
     @Inject
-    private Request request;
-
-    @Inject
     private Environment environment;
 
     @Inject
@@ -175,7 +179,13 @@
      * event notification is what will ultimately render (typically, its a Block). However, we do a <em>lot</em> of
      * tricks to provide the desired FormSupport around the what renders.
      */
-    void onInject(EventContext context) throws IOException
+    void onInject(EventContext context,
+
+    @QueryParameter(FORM_CLIENTID_PARAMETER)
+    final String formClientId,
+
+    @QueryParameter(FORM_COMPONENTID_PARAMETER)
+    String formComponentId) throws IOException
     {
         resources.triggerContextEvent(EventConstants.ACTION, context, eventCallback);
 
@@ -184,10 +194,6 @@
 
         // Here's where it gets very, very tricky.
 
-        final String formClientId = readParameterValue(FORM_CLIENTID_PARAMETER);
-
-        String formComponentId = request.getParameter(FORM_COMPONENTID_PARAMETER);
-
         final Form form = (Form) componentSource.getComponent(formComponentId);
 
         final ComponentActionSink actionSink = new ComponentActionSink(logger, clientDataEncoder);
@@ -238,15 +244,4 @@
 
         pageRenderQueue.addPartialMarkupRendererFilter(filter);
     }
-
-    private String readParameterValue(String parameterName)
-    {
-        String value = request.getParameter(parameterName);
-
-        if (InternalUtils.isBlank(value))
-            throw new RuntimeException(String.format(
-                    "Query parameter '%s' was blank, but should have been specified in the request.", parameterName));
-
-        return value;
-    }
 }

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Select.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Select.java?rev=918301&r1=918300&r2=918301&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Select.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Select.java Wed Mar  3 01:42:37 2010
@@ -243,12 +243,14 @@
         }
     }
 
-    Object onChange()
-    {
-        final String formId = this.request.getParameter(FORM_COMPONENTID_PARAMETER);
+    Object onChange(
 
-        final String changedValue = this.request.getParameter("t:selectvalue");
+    @QueryParameter(FORM_COMPONENTID_PARAMETER)
+    final String formId,
 
+    @QueryParameter(value = "t:selectvalue", allowBlank = true)
+    final String changedValue)
+    {
         final Object newValue = toValue(changedValue);
 
         final Holder<Object> holder = Holder.create();

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/mixins/Autocomplete.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/mixins/Autocomplete.java?rev=918301&r1=918300&r2=918301&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/mixins/Autocomplete.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/mixins/Autocomplete.java Wed Mar  3 01:42:37 2010
@@ -4,7 +4,7 @@
 // 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
+// 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,
@@ -41,10 +41,11 @@
  * Multiple selection on the client is enabled by binding the tokens parameter (however, the mixin doesn't help split
  * multiple selections up on the server, that is still your code's responsibility).
  * <p/>
- * The container is responsible for providing an event handler for event "providecompletions".  The context will be the
- * partial input string sent from the client.  The return value should be an array or list of completions, in
- * presentation order.  I.e.
+ * The container is responsible for providing an event handler for event "providecompletions". The context will be the
+ * partial input string sent from the client. The return value should be an array or list of completions, in
+ * presentation order. I.e.
  * <p/>
+ * 
  * <pre>
  * String[] onProvideCompletionsFromMyField(String input)
  * {
@@ -52,7 +53,8 @@
  * }
  * </pre>
  */
-@IncludeJavaScriptLibrary({ "${tapestry.scriptaculous}/controls.js", "autocomplete.js" })
+@IncludeJavaScriptLibrary(
+{ "${tapestry.scriptaculous}/controls.js", "autocomplete.js" })
 @Events(EventConstants.PROVIDE_COMPLETIONS)
 public class Autocomplete
 {
@@ -73,9 +75,6 @@
     private RenderSupport renderSupport;
 
     @Inject
-    private Request request;
-
-    @Inject
     private TypeCoercer coercer;
 
     @Inject
@@ -94,7 +93,6 @@
     @Inject
     private ResponseRenderer responseRenderer;
 
-
     /**
      * Overrides the default check frequency for determining whether to send a server request. The default is .4
      * seconds.
@@ -112,7 +110,7 @@
     /**
      * Mixin afterRender phrase occurs after the component itself. This is where we write the &lt;div&gt; element and
      * the JavaScript.
-     *
+     * 
      * @param writer
      */
     void afterRender(MarkupWriter writer)
@@ -127,32 +125,33 @@
 
         writer.element("img",
 
-                       "src", spacerImage.toClientURL(),
+        "src", spacerImage.toClientURL(),
 
-                       "class", "t-autoloader-icon " + CSSClassConstants.INVISIBLE,
+        "class", "t-autoloader-icon " + CSSClassConstants.INVISIBLE,
 
-                       "alt", "",
+        "alt", "",
 
-                       "id", loaderId);
+        "id", loaderId);
         writer.end();
 
         writer.element("div",
 
-                       "id", menuId,
+        "id", menuId,
 
-                       "class", "t-autocomplete-menu");
+        "class", "t-autocomplete-menu");
         writer.end();
 
         Link link = resources.createEventLink(EVENT_NAME);
 
-
         JSONObject config = new JSONObject();
         config.put("paramName", PARAM_NAME);
         config.put("indicator", loaderId);
 
-        if (resources.isBound("minChars")) config.put("minChars", minChars);
+        if (resources.isBound("minChars"))
+            config.put("minChars", minChars);
 
-        if (resources.isBound("frequency")) config.put("frequency", frequency);
+        if (resources.isBound("frequency"))
+            config.put("frequency", frequency);
 
         if (resources.isBound("tokens"))
         {
@@ -168,10 +167,9 @@
         renderSupport.addInit("autocompleter", new JSONArray(id, menuId, link.toAbsoluteURI(), config));
     }
 
-    Object onAutocomplete()
+    Object onAutocomplete(@QueryParameter(PARAM_NAME)
+    String input)
     {
-        String input = request.getParameter(PARAM_NAME);
-
         final Holder<List> matchesHolder = Holder.create();
 
         // Default it to an empty list.
@@ -190,7 +188,8 @@
             }
         };
 
-        resources.triggerEvent(EventConstants.PROVIDE_COMPLETIONS, new Object[] { input }, callback);
+        resources.triggerEvent(EventConstants.PROVIDE_COMPLETIONS, new Object[]
+        { input }, callback);
 
         ContentType contentType = responseRenderer.findContentType(this);
 
@@ -208,8 +207,9 @@
      * <p/>
      * <p/>
      * This implementation does nothing.
-     *
-     * @param config parameters object
+     * 
+     * @param config
+     *            parameters object
      */
     protected void configure(JSONObject config)
     {
@@ -219,9 +219,11 @@
      * Generates the markup response that will be returned to the client; this should be an &lt;ul&gt; element with
      * nested &lt;li&gt; elements. Subclasses may override this to produce more involved markup (including images and
      * CSS class attributes).
-     *
-     * @param writer  to write the list to
-     * @param matches list of matching objects, each should be converted to a string
+     * 
+     * @param writer
+     *            to write the list to
+     * @param matches
+     *            list of matching objects, each should be converted to a string
      */
     protected void generateResponseMarkup(MarkupWriter writer, List matches)
     {

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/InternalClassTransformationImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/InternalClassTransformationImpl.java?rev=918301&r1=918300&r2=918301&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/InternalClassTransformationImpl.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/InternalClassTransformationImpl.java Wed Mar  3 01:42:37 2010
@@ -98,6 +98,8 @@
 
         private Boolean override;
 
+        private List<List<Annotation>> parameterAnnotations;
+
         TransformMethodImpl(CtMethod method, boolean added)
         {
             this.method = method;
@@ -125,6 +127,39 @@
             return findAnnotationInList(annotationClass, annotations);
         }
 
+        public <A extends Annotation> A getParameterAnnotation(int index, Class<A> annotationType)
+        {
+            if (parameterAnnotations == null)
+                extractParameterAnnotations();
+
+            return findAnnotationInList(annotationType, parameterAnnotations.get(index));
+        }
+
+        private void extractParameterAnnotations()
+        {
+            int count = sig.getParameterTypes().length;
+
+            parameterAnnotations = CollectionFactory.newList();
+
+            for (int parameterIndex = 0; parameterIndex < count; parameterIndex++)
+            {
+                List<Annotation> annotations = extractAnnotationsForParameter(parameterIndex);
+
+                parameterAnnotations.add(annotations);
+            }
+        }
+
+        private List<Annotation> extractAnnotationsForParameter(int parameterIndex)
+        {
+            List<Annotation> result = CollectionFactory.newList();
+
+            Object[] parameterAnnotations = method.getAvailableParameterAnnotations()[parameterIndex];
+
+            addAnnotationsToList(result, parameterAnnotations, false);
+
+            return result;
+        }
+
         public TransformMethodSignature getSignature()
         {
             return sig;
@@ -317,7 +352,6 @@
             return methodName;
         }
 
-
         public String getMethodIdentifier()
         {
             if (identifier == null)

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/OnEventWorker.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/OnEventWorker.java?rev=918301&r1=918300&r2=918301&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/OnEventWorker.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/OnEventWorker.java Wed Mar  3 01:42:37 2010
@@ -20,16 +20,22 @@
 
 import org.apache.tapestry5.EventContext;
 import org.apache.tapestry5.annotations.OnEvent;
+import org.apache.tapestry5.annotations.QueryParameter;
+import org.apache.tapestry5.internal.services.ComponentClassCache;
 import org.apache.tapestry5.ioc.Predicate;
 import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
+import org.apache.tapestry5.ioc.internal.util.InternalUtils;
+import org.apache.tapestry5.ioc.services.TypeCoercer;
 import org.apache.tapestry5.model.MutableComponentModel;
 import org.apache.tapestry5.runtime.ComponentEvent;
 import org.apache.tapestry5.services.ClassTransformation;
 import org.apache.tapestry5.services.ComponentClassTransformWorker;
 import org.apache.tapestry5.services.ComponentMethodAdvice;
 import org.apache.tapestry5.services.ComponentMethodInvocation;
+import org.apache.tapestry5.services.Request;
 import org.apache.tapestry5.services.TransformConstants;
 import org.apache.tapestry5.services.TransformMethod;
+import org.apache.tapestry5.services.TransformMethodSignature;
 
 /**
  * Provides implementations of the
@@ -38,6 +44,13 @@
  */
 public class OnEventWorker implements ComponentClassTransformWorker
 {
+
+    private final Request request;
+
+    private final TypeCoercer typeCoercer;
+
+    private final ComponentClassCache classCache;
+
     /**
      * Stores a couple of special parameter type mappings that are used when matching the entire event context
      * (either as Object[] or EventContext).
@@ -77,6 +90,13 @@
         });
     }
 
+    public OnEventWorker(Request request, TypeCoercer typeCoercer, ComponentClassCache classCache)
+    {
+        this.request = request;
+        this.typeCoercer = typeCoercer;
+        this.classCache = classCache;
+    }
+
     public void transform(ClassTransformation transformation, MutableComponentModel model)
     {
         List<TransformMethod> methods = matchEventHandlerMethods(transformation);
@@ -84,7 +104,7 @@
         if (methods.isEmpty())
             return;
 
-        List<EventHandlerMethodInvoker> invokers = toInvokers(methods);
+        List<EventHandlerMethodInvoker> invokers = toInvokers(transformation.getClassName(), methods);
 
         updateModelWithHandledEvents(model, invokers);
 
@@ -175,19 +195,19 @@
         });
     }
 
-    private List<EventHandlerMethodInvoker> toInvokers(List<TransformMethod> methods)
+    private List<EventHandlerMethodInvoker> toInvokers(String componentClassName, List<TransformMethod> methods)
     {
         List<EventHandlerMethodInvoker> result = CollectionFactory.newList();
 
         for (TransformMethod method : methods)
         {
-            result.add(toInvoker(method));
+            result.add(toInvoker(componentClassName, method));
         }
 
         return result;
     }
 
-    private EventHandlerMethodInvoker toInvoker(TransformMethod method)
+    private EventHandlerMethodInvoker toInvoker(final String componentClassName, TransformMethod method)
     {
         OnEvent annotation = method.getAnnotation(OnEvent.class);
 
@@ -196,7 +216,9 @@
         String eventType = extractEventType(methodName, annotation);
         String componentId = extractComponentId(methodName, annotation);
 
-        String[] parameterTypes = method.getSignature().getParameterTypes();
+        final TransformMethodSignature signature = method.getSignature();
+
+        String[] parameterTypes = signature.getParameterTypes();
 
         if (parameterTypes.length == 0)
             return new BaseEventHandlerMethodInvoker(method, eventType, componentId);
@@ -206,16 +228,27 @@
         // I'd refactor a bit more of this if Java had covariant return types.
 
         int contextIndex = 0;
-        boolean catchAll = false;
 
-        for (final String type : parameterTypes)
+        for (int i = 0; i < parameterTypes.length; i++)
         {
+            String type = parameterTypes[i];
+
             EventHandlerMethodParameterSource source = parameterTypeToSource.get(type);
 
             if (source != null)
             {
                 sources.add(source);
-                catchAll = true;
+                continue;
+            }
+
+            QueryParameter parameterAnnotation = method.getParameterAnnotation(i, QueryParameter.class);
+
+            if (parameterAnnotation != null)
+            {
+                String parameterName = parameterAnnotation.value();
+
+                sources.add(createQueryParameterSource(componentClassName, signature, i, parameterName, type,
+                        parameterAnnotation.allowBlank()));
                 continue;
             }
 
@@ -224,12 +257,54 @@
 
             final int parameterIndex = contextIndex++;
 
-            sources.add(createParameterSource(type, parameterIndex));
+            sources.add(createEventContextSource(type, parameterIndex));
         }
 
-        int minContextCount = catchAll ? 0 : contextIndex;
+        return createInvoker(method, eventType, componentId, contextIndex, sources);
+    }
+
+    private EventHandlerMethodParameterSource createQueryParameterSource(final String componentClassName,
+            final TransformMethodSignature signature, final int parameterIndex, final String parameterName,
+            final String parameterTypeName, final boolean allowBlank)
+    {
+        return new EventHandlerMethodParameterSource()
+        {
+            @SuppressWarnings("unchecked")
+            public Object valueForEventHandlerMethodParameter(ComponentEvent event)
+            {
+                try
+                {
+                    String parameterValue = request.getParameter(parameterName);
+
+                    if (!allowBlank && InternalUtils.isBlank(parameterValue))
+                        throw new RuntimeException(String.format(
+                                "The value for query parameter '%s' was blank, but a non-blank value is needed.",
+                                parameterName));
 
-        return createInvoker(method, eventType, componentId, minContextCount, sources);
+                    Class parameterType = classCache.forName(parameterTypeName);
+
+                    Object coerced = typeCoercer.coerce(parameterValue, parameterType);
+
+                    if (parameterType.isPrimitive() && coerced == null)
+                        throw new RuntimeException(
+                                String
+                                        .format(
+                                                "Query parameter '%s' evaluates to null, but the event method parameter is type %s, a primitive.",
+                                                parameterName, parameterType.getName()));
+
+                    return coerced;
+                }
+                catch (Exception ex)
+                {
+                    throw new RuntimeException(
+                            String
+                                    .format(
+                                            "Unable process query parameter '%s' as parameter #%d of event handler method %s (in class %s): %s",
+                                            parameterName, parameterIndex + 1, signature, componentClassName,
+                                            InternalUtils.toMessage(ex)), ex);
+                }
+            }
+        };
     }
 
     private EventHandlerMethodInvoker createInvoker(TransformMethod method, String eventType, String componentId,
@@ -260,7 +335,7 @@
         };
     }
 
-    private EventHandlerMethodParameterSource createParameterSource(final String type, final int parameterIndex)
+    private EventHandlerMethodParameterSource createEventContextSource(final String type, final int parameterIndex)
     {
         return new EventHandlerMethodParameterSource()
         {

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=918301&r1=918300&r2=918301&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 Wed Mar  3 01:42:37 2010
@@ -555,7 +555,7 @@
         configuration.add("MixinAfter", new MixinAfterWorker());
         configuration.add("Component", new ComponentWorker(resolver));
         configuration.add("Mixin", new MixinWorker(resolver));
-        configuration.add("OnEvent", new OnEventWorker());
+        configuration.addInstance("OnEvent", OnEventWorker.class);
         configuration.add("SupportsInformalParameters", new SupportsInformalParametersWorker());
         configuration.addInstance("InjectPage", InjectPageWorker.class);
         configuration.addInstance("InjectContainer", InjectContainerWorker.class);

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/TransformMethod.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/TransformMethod.java?rev=918301&r1=918300&r2=918301&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/TransformMethod.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/TransformMethod.java Wed Mar  3 01:42:37 2010
@@ -14,6 +14,8 @@
 
 package org.apache.tapestry5.services;
 
+import java.lang.annotation.Annotation;
+
 import org.apache.tapestry5.ioc.AnnotationProvider;
 
 /**
@@ -71,4 +73,14 @@
      */
     boolean isOverride();
 
+    /**
+     * Gets an annotation on a parameter of the method.
+     * 
+     * @param index
+     *            index of parameter
+     * @param annotationType
+     *            type of annotation to check for
+     * @return the annotation, if found, or null
+     */
+    <A extends Annotation> A getParameterAnnotation(int index, Class<A> annotationType);
 }

Added: tapestry/tapestry5/trunk/tapestry-core/src/test/app1/QueryParameterDemo.tml
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/app1/QueryParameterDemo.tml?rev=918301&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/app1/QueryParameterDemo.tml (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/app1/QueryParameterDemo.tml Wed Mar  3 01:42:37 2010
@@ -0,0 +1,25 @@
+<html t:type="Border" xmlns:t="http://tapestry.apache.org/schema/tapestry_5_0_0.xsd">
+
+  <h1>QueryParameter Annotation Demo</h1>
+
+  <p>
+    The current value is:
+    <span id="current">${value}</span>
+    .
+  </p>
+
+  <ul>
+    <li>
+      <a href="${workingLink}">Working Link</a>
+    </li>
+    <li>
+      <a href="${brokenLink}">Broken Link</a>
+    </li>
+    <li>
+      <a href="${nullLink}">Null Link</a>
+    </li>
+    <li>
+      <a href="${nullAllowedLink}">Null Allowed Link</a>
+    </li>
+  </ul>
+</html>
\ No newline at end of file

Added: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/QueryParameterTests.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/QueryParameterTests.java?rev=918301&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/QueryParameterTests.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/QueryParameterTests.java Wed Mar  3 01:42:37 2010
@@ -0,0 +1,59 @@
+// 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;
+
+public class QueryParameterTests extends TapestryCoreTestCase
+{
+    @Test
+    public void successful_use_of_query_parameter_annotation()
+    {
+        clickThru("QueryParameter Annotation Demo", "Working Link");
+
+        assertText("id=current", "97");
+    }
+
+    @Test
+    public void null_value_when_not_allowed()
+    {
+        clickThru("QueryParameter Annotation Demo", "Null Link");
+
+        assertTextPresent(
+                "Unable process query parameter 'gnip' as parameter #1 of event handler method void onFrob(int) (in class org.apache.tapestry5.integration.app1.pages.QueryParameterDemo)",
+                "The value for query parameter 'gnip' was blank, but a non-blank value is needed.");
+    }
+
+    @Test
+    public void null_for_primitive_when_allowed()
+    {
+        clickThru("QueryParameter Annotation Demo", "Null Allowed Link");
+
+        assertTextPresent(
+                "Unable process query parameter 'gnip' as parameter #1 of event handler method void onFrobNullAllowed(int) (in class org.apache.tapestry5.integration.app1.pages.QueryParameterDemo)",
+                "Query parameter 'gnip' evaluates to null, but the event method parameter is type int, a primitive.");
+    }
+
+    @Test
+    public void type_mismatch_for_method_parameter()
+    {
+        clickThru("QueryParameter Annotation Demo", "Broken Link");
+
+        assertTextPresent(
+                "Unable process query parameter 'gnip' as parameter #1 of event handler method void onFrob(int) (in class org.apache.tapestry5.integration.app1.pages.QueryParameterDemo)",
+                "Coercion of frodo to type java.lang.Integer");
+    }
+}

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

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=918301&r1=918300&r2=918301&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 Wed Mar  3 01:42:37 2010
@@ -4,7 +4,7 @@
 // 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
+// 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,
@@ -66,7 +66,10 @@
 
     private static final List<Item> ITEMS = CollectionFactory
             .newList(
-                    
+
+                    new Item("QueryParameterDemo", "QueryParameter Annotation Demo",
+                            "Use of @QueryParameter annotation on event handler method parameters"),
+
                     new Item("CancelDemo", "Cancel Demo", "Use of the cancel option with Submit"),
 
                     new Item("PageResetDemo", "PageReset Annotation Demo",
@@ -85,11 +88,9 @@
 
                     new Item("FormZoneDemo", "Form Zone Demo", "Use a form to update a zone."),
 
-                    new Item("ZoneUpdateNamespace", "Zone/Namespace Interaction",
-                            "Prove that TAP5-573 is fixed"),
+                    new Item("ZoneUpdateNamespace", "Zone/Namespace Interaction", "Prove that TAP5-573 is fixed"),
 
-                    new Item("AbstractComponentDemo", "Abstract Component Demo",
-                            "Error when a component is abstract"),
+                    new Item("AbstractComponentDemo", "Abstract Component Demo", "Error when a component is abstract"),
 
                     new Item("TemplateOverrideDemo", "Template Override Demo",
                             "Child component extends and overrides parent template."),
@@ -103,8 +104,7 @@
                     new Item("SlowAjaxDemo", "Slow Ajax Demo",
                             "Handling of client-side Ajax before the page is fully loaded"),
 
-                    new Item("ProgressiveDemo", "ProgressiveDisplay Demo",
-                            "Progressive Enhancement via a component"),
+                    new Item("ProgressiveDemo", "ProgressiveDisplay Demo", "Progressive Enhancement via a component"),
 
                     new Item("ClientNumericValidationDemo", "Client-Side Numeric Validation",
                             "Client-side locale-specific validation"),
@@ -114,16 +114,13 @@
 
                     new Item("LinkSubmitDemo", "LinkSubmit Demo", "JavaScript LinkSubmit component"),
 
-                    new Item(
-                            "LinkSubmitWithoutValidatorDemo",
-                            "LinkSubmit Without Validator Demo",
+                    new Item("LinkSubmitWithoutValidatorDemo", "LinkSubmit Without Validator Demo",
                             "Demonstrates that the LinkSubmit component is working without a validator on any of fields in the form"),
 
                     new Item("PerFormValidationMessageDemo", "Per-Form Validation Messages",
                             "Per-form configuration of validation messages and constraints."),
 
-                    new Item("EmptyLoopDemo", "Empty Loop Demo",
-                            "Use of empty parameter with the Loop component."),
+                    new Item("EmptyLoopDemo", "Empty Loop Demo", "Use of empty parameter with the Loop component."),
 
                     new Item("BlankPasswordDemo", "Blank Password Demo",
                             "Show that a blank value in a PasswordField does not update the server side value."),
@@ -134,8 +131,7 @@
                     new Item("DateFieldAjaxFormLoop", "DateField inside AjaxFormLoop",
                             "Show that DateField component works correctly inside AjaxFormLoop"),
 
-                    new Item("NestedForm", "Nested Form Demo",
-                            "Error when a Form is nested inside another Form."),
+                    new Item("NestedForm", "Nested Form Demo", "Error when a Form is nested inside another Form."),
 
                     new Item("UnhandledEventDemo", "Unhandled Event Demo",
                             "Events that don't have matching event handlers cause exceptions"),
@@ -143,52 +139,42 @@
                     new Item("PrimitiveDefaultDemo", "Primitive Default Demo",
                             "Primitive value returned from parameter default method"),
 
-                    new Item("ValidateFormValidationExceptionDemo",
-                            "ValidationForm ValidationException Demo",
+                    new Item("ValidateFormValidationExceptionDemo", "ValidationForm ValidationException Demo",
                             "Throwing a ValidationException from the validateForm event handler."),
 
-                    new Item("ClientFormatDemo", "Client Format Validation",
-                            "Client-side input format validation"),
+                    new Item("ClientFormatDemo", "Client Format Validation", "Client-side input format validation"),
 
                     new Item("ShortGrid", "Short Grid",
                             "Grid where the number of claimed rows is less than the number of actual rows"),
 
-                    new Item("NullParameterDemo", "Null Parameter Demo",
-                            "Binding a not-null parameter to null."),
+                    new Item("NullParameterDemo", "Null Parameter Demo", "Binding a not-null parameter to null."),
 
                     new Item("nestedbeaneditor", "Nested BeanEditor",
                             "BeanEditor as override for property editor in BeanEditForm"),
 
                     new Item("actionpage", "Action Page", "tests fixture for ActionLink component"),
 
-                    new Item("cleancachedemo", "Clean Cache Demo",
-                            "cache cleared properly during Ajax calls"),
+                    new Item("cleancachedemo", "Clean Cache Demo", "cache cleared properly during Ajax calls"),
 
                     new Item("numberbeaneditordemo", "Number BeanEditor Demo",
                             "use of nulls and wrapper types with BeanEditor"),
 
-                    new Item("forminjectordemo", "FormInjector Demo",
-                            "extending a form dynamically via Ajax"),
+                    new Item("forminjectordemo", "FormInjector Demo", "extending a form dynamically via Ajax"),
 
                     new Item("music", "Music Page", "demo handling of edge cases of page naming"),
 
-                    new Item("PersistentDemo", "Persistent Demo",
-                            "storing and clearing persistent properties"),
+                    new Item("PersistentDemo", "Persistent Demo", "storing and clearing persistent properties"),
 
-                    new Item("ActionViaLinkDemo", "Action via Link Demo",
-                            "tests creating an action link explicitly"),
+                    new Item("ActionViaLinkDemo", "Action via Link Demo", "tests creating an action link explicitly"),
 
-                    new Item("FormFragmentDemo", "Form Fragment Demo",
-                            "page with dynamic form sections"),
+                    new Item("FormFragmentDemo", "Form Fragment Demo", "page with dynamic form sections"),
 
                     new Item("BooleanDemo", "Boolean Property Demo",
                             "demo boolean properties using both is and get prefixes"),
 
-                    new Item("DeleteFromGridDemo", "Delete From Grid",
-                            "demo deleting items form a Grid"),
+                    new Item("DeleteFromGridDemo", "Delete From Grid", "demo deleting items form a Grid"),
 
-                    new Item("RenderErrorDemo", "Render Error Demo",
-                            "reporting of errors while rendering"),
+                    new Item("RenderErrorDemo", "Render Error Demo", "reporting of errors while rendering"),
 
                     new Item("nested/AssetDemo", "AssetDemo", "declaring an image using Assets"),
 
@@ -197,13 +183,11 @@
 
                     new Item("blockdemo", "BlockDemo", "use of blocks to control rendering"),
 
-                    new Item("countdown", "Countdown Page",
-                            "defining component using @Component annotation"),
+                    new Item("countdown", "Countdown Page", "defining component using @Component annotation"),
 
                     new Item("injectdemo", "Inject Demo", "use of various kinds of injection"),
 
-                    new Item("instancemixin", "InstanceMixin",
-                            "mixin added to a particular component instance"),
+                    new Item("instancemixin", "InstanceMixin", "mixin added to a particular component instance"),
 
                     new Item("TextFieldWrapperTypeDemo", "TextField Wrapper Types",
                             "use of TextField to edit numeric wrapper types (not primitives) "),
@@ -216,19 +200,16 @@
                     new Item("ExpansionSubclass", "ExpansionSubclass",
                             "components can inherit templates from base classes"),
 
-                    new Item("Localization", "Localization",
-                            "access localized messages from the component catalog"),
+                    new Item("Localization", "Localization", "access localized messages from the component catalog"),
 
                     new Item("NumberSelect", "NumberSelect", "passivate/activate page context demo"),
 
                     new Item("ParameterConflict", "Template Overridden by Class Page",
                             "Parameters in the class override those in the template"),
 
-                    new Item("ParameterDefault", "ParameterDefault",
-                            "defaulter methods for component parameters"),
+                    new Item("ParameterDefault", "ParameterDefault", "defaulter methods for component parameters"),
 
-                    new Item("passwordfielddemo", "PasswordFieldDemo",
-                            "test for the PasswordField component"),
+                    new Item("passwordfielddemo", "PasswordFieldDemo", "test for the PasswordField component"),
 
                     new Item("rendercomponentdemo", "RenderComponentDemo",
                             "components that \"nominate\" other components to render"),
@@ -236,29 +217,24 @@
                     new Item("renderphaseorder", "RenderPhaseOrder",
                             "order of operations when invoking render phase methods"),
 
-                    new Item("simpleform", "SimpleForm",
-                            "first pass at writing Form and TextField components"),
+                    new Item("simpleform", "SimpleForm", "first pass at writing Form and TextField components"),
 
                     new Item("validform", "ValidForm", "server-side input validation"),
 
-                    new Item("ToDoListVolatile", "ToDo List (Volatile)",
-                            "Loops and Submit inside Form, volatile mode"),
+                    new Item("ToDoListVolatile", "ToDo List (Volatile)", "Loops and Submit inside Form, volatile mode"),
 
                     new Item("MissingTemplate", "Missing Template Demo",
                             "Demo for what happens when a template is not found for a page"),
 
                     new Item("nested/zonedemo", "Zone Demo", "dynamic updates within a page"),
 
-                    new Item("todolist", "ToDo List",
-                            "Loops and Submit inside Form using primary key encoder"),
+                    new Item("todolist", "ToDo List", "Loops and Submit inside Form using primary key encoder"),
 
                     new Item("flashdemo", "FlashDemo", "demonstrate 'flash' persistence"),
 
-                    new Item("beaneditordemo", "BeanEditor Demo",
-                            "demonstrate the BeanEditor mega-component"),
+                    new Item("beaneditordemo", "BeanEditor Demo", "demonstrate the BeanEditor mega-component"),
 
-                    new Item("pageloadeddemo", "PageLoaded Demo",
-                            "shows that page lifecycle methods are invoked"),
+                    new Item("pageloadeddemo", "PageLoaded Demo", "shows that page lifecycle methods are invoked"),
 
                     new Item("griddemo", "Grid Demo", "default Grid component"),
 
@@ -274,8 +250,7 @@
                     new Item("protected", "Protected Page",
                             "Demonstrate result of non-void return from a page's activate method"),
 
-                    new Item("Kicker", "Kicker",
-                            "demos complex page and component context in links"),
+                    new Item("Kicker", "Kicker", "demos complex page and component context in links"),
 
                     new Item("simpletrackgriddemo", "SimpleTrack Grid Demo",
                             "customizing the model for a Grid around an interface"),
@@ -283,14 +258,11 @@
                     new Item("pagelinkcontext", "PageLink Context Demo",
                             "passing explicit context in a page render link"),
 
-                    new Item("pagecontextinform", "Page Context in Form",
-                            "passivate/activate page context in Form"),
+                    new Item("pagecontextinform", "Page Context in Form", "passivate/activate page context in Form"),
 
-                    new Item("ValidBeanEditorDemo", "Client Validation Demo",
-                            "BeanEditor with validation enabled"),
+                    new Item("ValidBeanEditorDemo", "Client Validation Demo", "BeanEditor with validation enabled"),
 
-                    new Item("Unreachable", "Unreachable Page",
-                            "page not reachable due to IgnoredPathsFilter"),
+                    new Item("Unreachable", "Unreachable Page", "page not reachable due to IgnoredPathsFilter"),
 
                     new Item("renderabledemo", "Renderable Demo",
                             "shows that render phase methods can return a Renderable"),
@@ -306,8 +278,7 @@
 
                     new Item("PaletteDemo", "Palette Demo", "multiple selection component"),
 
-                    new Item("ReturnTypes", "Return Types",
-                            "tests various event handler return types"),
+                    new Item("ReturnTypes", "Return Types", "tests various event handler return types"),
 
                     new Item("FormEncodingType", "Form Encoding Type",
                             "Test ability to set an encoding type for a Form"),
@@ -324,8 +295,7 @@
 
                     new Item("GridFormDemo", "Grid Form Demo", "Grid operating inside a Form"),
 
-                    new Item("DateFieldDemo", "DateField Demo",
-                            "using DateField by itself on a page"),
+                    new Item("DateFieldDemo", "DateField Demo", "using DateField by itself on a page"),
 
                     new Item("BeanEditDateDemo", "BeanEditor / Date Demo",
                             "Use of date properties inside BeanEditor and BeanDisplay"),
@@ -342,21 +312,15 @@
                     new Item("inheritinformalsdemo", "Inherit Informal Parameters Demo",
                             "Demo a component which inherits informal parameters from its container"),
 
-                    new Item(
-                            "disabledfields",
-                            "Disabled Fields",
+                    new Item("disabledfields", "Disabled Fields",
                             "Demonstrate a bunch of disabled fields, to verify that the RenderDisabled mixin works and is being used properly"),
 
-                    new Item(
-                            "BeanEditorOverride",
-                            "BeanEditor Override",
+                    new Item("BeanEditorOverride", "BeanEditor Override",
                             "Property editor overrides work for the BeanEditor component itself (not just the BeanEditForm component)"),
 
                     new Item("varbindingdemo", "Var Binding Demo", "use of the var: binding prefix"),
 
-                    new Item(
-                            "leangriddemo",
-                            "Lean Grid Demo",
+                    new Item("leangriddemo", "Lean Grid Demo",
                             "Grid component with lean parameter turned on, to eliminate CSS class attributes in TD and TH elements"),
 
                     new Item("blockcaller", "Action Links off of Active Page",
@@ -367,23 +331,19 @@
                     new Item("MagicValueEncoder", "Magic ValueEncoder Demo",
                             "Automatic creation of ValueEncoder using the TypeCoercer"),
 
-                    new Item("NullStrategyDemo", "Null Field Strategy Demo",
-                            "use of the nulls parameter of TextField"),
+                    new Item("NullStrategyDemo", "Null Field Strategy Demo", "use of the nulls parameter of TextField"),
 
                     new Item("OverrideValidationDecorator", "Override Validation Decorator",
                             "override the default validation decorator"),
 
-                    new Item("ExceptionEventDemo", "Exception Event Demo",
-                            "handling component event exceptions"),
+                    new Item("ExceptionEventDemo", "Exception Event Demo", "handling component event exceptions"),
 
-                    new Item("AddedGridColumnsDemo", "Added Grid Columns Demo",
-                            "programatically adding grid columns"),
+                    new Item("AddedGridColumnsDemo", "Added Grid Columns Demo", "programatically adding grid columns"),
 
                     new Item("PrimitiveArrayParameterDemo", "Primitive Array Parameter Demo",
                             "use primitive array as parameter type"),
 
-                    new Item("RenderPhaseMethodExceptionDemo",
-                            "Render Phase Method Exception Demo",
+                    new Item("RenderPhaseMethodExceptionDemo", "Render Phase Method Exception Demo",
                             "render phase methods may throw checked exceptions"),
 
                     new Item("TrackEditor", "Generic Page Class Demo",
@@ -392,22 +352,17 @@
                     new Item("IndirectProtectedFields", "Protected Fields Demo",
                             "demo exception when component class contains protected fields"),
 
-                    new Item("injectcomponentdemo", "Inject Component Demo",
-                            "inject component defined in template"),
+                    new Item("injectcomponentdemo", "Inject Component Demo", "inject component defined in template"),
 
                     new Item("cachedpage", "Cached Annotation", "Caching method return values"),
 
-                    new Item("cachedpage2", "Cached Annotation2",
-                            "Caching method return values w/ inheritence"),
+                    new Item("cachedpage2", "Cached Annotation2", "Caching method return values w/ inheritence"),
 
-                    new Item("inplacegriddemo", "In-Place Grid Demo",
-                            "Grid that updates in-place using Ajax"),
+                    new Item("inplacegriddemo", "In-Place Grid Demo", "Grid that updates in-place using Ajax"),
 
-                    new Item("methodadvicedemo", "Method Advice Demo",
-                            "Advising component methods."),
+                    new Item("methodadvicedemo", "Method Advice Demo", "Advising component methods."),
 
-                    new Item("HasBodyDemo", "Has Body Demo",
-                            "Verify the hasBody() method of ComponentResources"),
+                    new Item("HasBodyDemo", "Has Body Demo", "Verify the hasBody() method of ComponentResources"),
 
                     new Item("BeanEditorBeanEditContext", "BeanEditor BeanEditContext",
                             "BeanEditContext is pushed into enviroment by BeanEditor."),
@@ -418,11 +373,9 @@
                     new Item("FormFieldOutsideForm", "Form Field Outside Form",
                             "Nice exception message for common problem of form fields outside forms"),
 
-                    new Item("SubmitWithContext", "Submit With Context",
-                            "Providing a context for Submit component"),
+                    new Item("SubmitWithContext", "Submit With Context", "Providing a context for Submit component"),
 
-                    new Item("MessageConstraintGeneratorDemo",
-                            "Validation Constraints From Messages",
+                    new Item("MessageConstraintGeneratorDemo", "Validation Constraints From Messages",
                             "Providing validators to apply from a properties file"),
 
                     new Item("RenderClientIdDemo", "RenderClientId Mixin",
@@ -437,8 +390,7 @@
                     new Item("BindParameterOnComponent", "BindParameter on component",
                             "Verify that BindParameter can only be used on mixin fields"),
 
-                    new Item("MixinOrderingDemo", "Mixin Ordering Demo",
-                            "Various mixin-ordering scenarios"),
+                    new Item("MixinOrderingDemo", "Mixin Ordering Demo", "Various mixin-ordering scenarios"),
 
                     new Item(
                             "MissingComponentClassException",
@@ -456,12 +408,11 @@
                     new Item("ImageSubmitDemo", "Submit with an Image Demo",
                             "Make sure that submit with the image parameter set triggers the 'selected' event."),
 
-                    new Item("SelectZoneDemo", "Select Zone Demo",
-                            "Use a Select component to update a zone."),
+                    new Item("SelectZoneDemo", "Select Zone Demo", "Use a Select component to update a zone."),
 
                     new Item("AssetProtectionDemo", "Asset Protection Demo",
                             "AssetProtectionDispatcher is properly contributed and functioning"),
-                            
+
                     new Item("BeanDisplayEnumDemo", "BeanDisplay Enum Demo",
                             "User represenation of enum values is correctly read from messages")
 

Added: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/QueryParameterDemo.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/QueryParameterDemo.java?rev=918301&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/QueryParameterDemo.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/QueryParameterDemo.java Wed Mar  3 01:42:37 2010
@@ -0,0 +1,74 @@
+// 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.ComponentResources;
+import org.apache.tapestry5.Link;
+import org.apache.tapestry5.annotations.Persist;
+import org.apache.tapestry5.annotations.Property;
+import org.apache.tapestry5.annotations.QueryParameter;
+import org.apache.tapestry5.ioc.annotations.Inject;
+
+public class QueryParameterDemo
+{
+    private static final String PARAMETER_NAME = "gnip";
+
+    private static final String EVENT_NAME = "frob";
+
+    @Property
+    @Persist
+    private int value;
+
+    @Inject
+    private ComponentResources resources;
+
+    public Link getWorkingLink()
+    {
+        Link link = resources.createEventLink(EVENT_NAME);
+        link.addParameter(PARAMETER_NAME, "97");
+
+        return link;
+    }
+
+    public Link getBrokenLink()
+    {
+        Link link = resources.createEventLink(EVENT_NAME);
+        link.addParameter(PARAMETER_NAME, "frodo");
+
+        return link;
+    }
+
+    public Link getNullLink()
+    {
+        return resources.createEventLink(EVENT_NAME);
+    }
+
+    public Link getNullAllowedLink()
+    {
+        return resources.createEventLink("frobNullAllowed");
+    }
+
+    void onFrob(@QueryParameter(PARAMETER_NAME)
+    int value)
+    {
+        this.value = value;
+    }
+
+    void onFrobNullAllowed(@QueryParameter(value = PARAMETER_NAME, allowBlank = true)
+    int value)
+    {
+        this.value = value;
+    }
+}

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