Home » tapestry-src-5.0.19 » org.apache.tapestry5.corelib.components » [javadoc | source]

    1   // Copyright 2007, 2008 The Apache Software Foundation
    2   //
    3   // Licensed under the Apache License, Version 2.0 (the "License");
    4   // you may not use this file except in compliance with the License.
    5   // You may obtain a copy of the License at
    6   //
    7   //     http://www.apache.org/licenses/LICENSE-2.0
    8   //
    9   // Unless required by applicable law or agreed to in writing, software
   10   // distributed under the License is distributed on an "AS IS" BASIS,
   11   // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   12   // See the License for the specific language governing permissions and
   13   // limitations under the License.
   14   
   15   package org.apache.tapestry5.corelib.components;
   16   
   17   import org.apache.tapestry5;
   18   import org.apache.tapestry5.annotations.BeforeRenderTemplate;
   19   import org.apache.tapestry5.annotations.Environmental;
   20   import org.apache.tapestry5.annotations.Mixin;
   21   import org.apache.tapestry5.annotations.Parameter;
   22   import org.apache.tapestry5.corelib.base.AbstractField;
   23   import org.apache.tapestry5.corelib.data.BlankOption;
   24   import org.apache.tapestry5.corelib.mixins.RenderDisabled;
   25   import org.apache.tapestry5.internal.TapestryInternalUtils;
   26   import org.apache.tapestry5.internal.util.SelectModelRenderer;
   27   import org.apache.tapestry5.ioc.Messages;
   28   import org.apache.tapestry5.ioc.annotations.Inject;
   29   import org.apache.tapestry5.ioc.internal.util.InternalUtils;
   30   import org.apache.tapestry5.services;
   31   import org.apache.tapestry5.util.EnumSelectModel;
   32   
   33   import java.util.Locale;
   34   
   35   /**
   36    * Select an item from a list of values, using an [X]HTML <select> element on the client side. An validation
   37    * decorations will go around the entire <select> element.
   38    * <p/>
   39    * A core part of this component is the {@link ValueEncoder} (the encoder parameter) that is used to convert between
   40    * server-side values and client-side strings. In many cases, a {@link ValueEncoder} can be generated automatically from
   41    * the type of the value parameter. The {@link ValueEncoderSource} service provides an encoder in these situations; it
   42    * can be overriden by binding the encoder parameter, or extended by contributing a {@link ValueEncoderFactory} into the
   43    * service's configuration.
   44    */
   45   public class Select extends AbstractField
   46   {
   47       private class Renderer extends SelectModelRenderer
   48       {
   49   
   50           public Renderer(MarkupWriter writer)
   51           {
   52               super(writer, encoder);
   53           }
   54   
   55           @Override
   56           protected boolean isOptionSelected(OptionModel optionModel, String clientValue)
   57           {
   58               return isSelected(clientValue);
   59           }
   60       }
   61   
   62       /**
   63        * Allows a specific implementation of {@link ValueEncoder} to be supplied. This is used to create client-side
   64        * string values for the different options.
   65        *
   66        * @see ValueEncoderSource
   67        */
   68       @Parameter
   69       private ValueEncoder encoder;
   70   
   71       @Inject
   72       private ComponentDefaultProvider defaultProvider;
   73   
   74       @Inject
   75       private Locale locale;
   76   
   77       // Maybe this should default to property "<componentId>Model"?
   78       /**
   79        * The model used to identify the option groups and options to be presented to the user. This can be generated
   80        * automatically for Enum types.
   81        */
   82       @Parameter(required = true, allowNull = false)
   83       private SelectModel model;
   84   
   85       /**
   86        * Controls whether an additional blank option is provided. The blank option precedes all other options and is never
   87        * selected.  The value for the blank option is always the empty string, the label may be the blank string; the
   88        * label is from the blankLabel parameter (and is often also the empty string).
   89        */
   90       @Parameter(value = "auto", defaultPrefix = BindingConstants.LITERAL)
   91       private BlankOption blankOption;
   92   
   93       /**
   94        * The label to use for the blank option, if rendered.  If not specified, the container's message catalog is
   95        * searched for a key, <code><em>id</em>-blanklabel</code>.
   96        */
   97       @Parameter(defaultPrefix = BindingConstants.LITERAL)
   98       private String blankLabel;
   99   
  100       @Inject
  101       private Request request;
  102   
  103       @Inject
  104       private ComponentResources resources;
  105   
  106       @Environmental
  107       private ValidationTracker tracker;
  108   
  109       /**
  110        * Performs input validation on the value supplied by the user in the form submission.
  111        */
  112       @Parameter(defaultPrefix = BindingConstants.VALIDATE)
  113       @SuppressWarnings("unchecked")
  114       private FieldValidator<Object> validate;
  115   
  116       /**
  117        * The value to read or update.
  118        */
  119       @Parameter(required = true, principal = true, autoconnect = true)
  120       private Object value;
  121   
  122       @Inject
  123       private FieldValidationSupport fieldValidationSupport;
  124   
  125       @SuppressWarnings("unused")
  126       @Mixin
  127       private RenderDisabled renderDisabled;
  128   
  129       private String selectedClientValue;
  130   
  131       private boolean isSelected(String clientValue)
  132       {
  133           return TapestryInternalUtils.isEqual(clientValue, selectedClientValue);
  134       }
  135   
  136       @SuppressWarnings({"unchecked"})
  137       @Override
  138       protected void processSubmission(String elementName)
  139       {
  140           String submittedValue = request.getParameter(elementName);
  141   
  142           tracker.recordInput(this, submittedValue);
  143   
  144           Object selectedValue = InternalUtils.isBlank(submittedValue)
  145                                  ? null :
  146                                  encoder.toValue(submittedValue);
  147   
  148           try
  149           {
  150               fieldValidationSupport.validate(selectedValue, resources, validate);
  151   
  152               value = selectedValue;
  153           }
  154           catch (ValidationException ex)
  155           {
  156               tracker.recordError(this, ex.getMessage());
  157           }
  158       }
  159   
  160       void afterRender(MarkupWriter writer)
  161       {
  162           writer.end();
  163       }
  164   
  165       void beginRender(MarkupWriter writer)
  166       {
  167           writer.element("select", "name", getControlName(), "id", getClientId());
  168   
  169           validate.render(writer);
  170   
  171           resources.renderInformalParameters(writer);
  172   
  173           // Disabled is via a mixin
  174       }
  175   
  176       @SuppressWarnings("unchecked")
  177       ValueEncoder defaultEncoder()
  178       {
  179           return defaultProvider.defaultValueEncoder("value", resources);
  180       }
  181   
  182       @SuppressWarnings("unchecked")
  183       SelectModel defaultModel()
  184       {
  185           Class valueType = resources.getBoundType("value");
  186   
  187           if (valueType == null) return null;
  188   
  189           if (Enum.class.isAssignableFrom(valueType))
  190               return new EnumSelectModel(valueType, resources.getContainerMessages());
  191   
  192           return null;
  193       }
  194   
  195       /**
  196        * Computes a default value for the "validate" parameter using {@link FieldValidatorDefaultSource}.
  197        */
  198       Binding defaultValidate()
  199       {
  200           return defaultProvider.defaultValidatorBinding("value", resources);
  201       }
  202   
  203       Object defaultBlankLabel()
  204       {
  205           Messages containerMessages = resources.getContainerMessages();
  206   
  207           String key = resources.getId() + "-blanklabel";
  208   
  209           if (containerMessages.contains(key)) return containerMessages.get(key);
  210   
  211           return null;
  212       }
  213   
  214       /**
  215        * Renders the options, including the blank option.
  216        */
  217       @BeforeRenderTemplate
  218       void options(MarkupWriter writer)
  219       {
  220           selectedClientValue = tracker.getInput(this);
  221   
  222           // Use the value passed up in the form submission, if available.
  223           // Failing that, see if there is a current value (via the value parameter), and
  224           // convert that to a client value for later comparison.
  225   
  226           if (selectedClientValue == null) selectedClientValue = value == null ? null : encoder.toClient(value);
  227   
  228           if (showBlankOption())
  229           {
  230               writer.element("option", "value", "");
  231               writer.write(blankLabel);
  232               writer.end();
  233           }
  234   
  235   
  236           SelectModelVisitor renderer = new Renderer(writer);
  237   
  238           model.visit(renderer);
  239       }
  240   
  241       @Override
  242       public boolean isRequired()
  243       {
  244           return validate.isRequired();
  245       }
  246   
  247       private boolean showBlankOption()
  248       {
  249           switch (blankOption)
  250           {
  251               case ALWAYS:
  252                   return true;
  253   
  254               case NEVER:
  255                   return false;
  256   
  257               default:
  258                   return !isRequired();
  259           }
  260       }
  261   
  262       // For testing.
  263   
  264       void setModel(SelectModel model)
  265       {
  266           this.model = model;
  267           blankOption = BlankOption.NEVER;
  268       }
  269   
  270       void setValue(Object value)
  271       {
  272           this.value = value;
  273       }
  274   
  275       void setValueEncoder(ValueEncoder encoder)
  276       {
  277           this.encoder = encoder;
  278       }
  279   
  280       void setValidationTracker(ValidationTracker tracker)
  281       {
  282           this.tracker = tracker;
  283       }
  284   
  285       void setBlankOption(BlankOption option, String label)
  286       {
  287           blankOption = option;
  288           blankLabel = label;
  289       }
  290   }

Home » tapestry-src-5.0.19 » org.apache.tapestry5.corelib.components » [javadoc | source]