// Copyright 2009, 2010, 2011 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.corelib.components;

import org.apache.tapestry5.*;
import org.apache.tapestry5.dom.Element;
import org.apache.tapestry5.annotations.Environmental;
import org.apache.tapestry5.annotations.Parameter;
import org.apache.tapestry5.annotations.SupportsInformalParameters;
import org.apache.tapestry5.ioc.annotations.Inject;
import org.apache.tapestry5.services.ComponentDefaultProvider;
import org.apache.tapestry5.services.FormSupport;
import org.apache.tapestry5.services.Request;
import org.apache.tapestry5.services.javascript.JavaScriptSupport;

/**
 * Used to record a page property as a value into the form. The value is
 * {@linkplain org.apache.tapestry5.ValueEncoder#toClient(Object) encoded} when rendered, then decoded when the form is
 * submitted,
 * and the value parameter updated.
 * 
 * @since 5.1.0.2
 * @tapestrydoc
 */
@SupportsInformalParameters
public class Hidden implements ClientElement
{
    /**
     * The value to read (when rendering) or update (when the form is submitted).
     */
    @Parameter(required = true, autoconnect = true, principal = true)
    private Object value;

    /**
     * Value encoder for the value, usually determined automatically from the type of the property bound to the value
     * parameter.
     */
    @Parameter(required = true)
    private ValueEncoder encoder;

    private String clientId;

    private String controlName;

    private Element hiddenInputElement;

    @Environmental(false)
    private FormSupport formSupport;

    @Environmental
    private JavaScriptSupport jsSupport;

    @Inject
    private ComponentResources resources;

    @Inject
    private ComponentDefaultProvider defaultProvider;

    @Inject
    private Request request;

    ValueEncoder defaultEncoder()
    {
        return defaultProvider.defaultValueEncoder("value", resources);
    }

    static class ProcessSubmission implements ComponentAction<Hidden>
    {
        private final String controlName;

        public ProcessSubmission(String controlName)
        {
            this.controlName = controlName;
        }

        public void execute(Hidden component)
        {
            component.processSubmission(controlName);
        }
    }

    boolean beginRender(MarkupWriter writer)
    {
        if (formSupport == null)
            throw new RuntimeException("The Hidden component must be enclosed by a Form component.");

        controlName = formSupport.allocateControlName(resources.getId());

        clientId = null;

        formSupport.store(this, new ProcessSubmission(controlName));

        String encoded = encoder.toClient(value);

        hiddenInputElement = writer.element("input", "type", "hidden", "name", controlName, "value", encoded);

        resources.renderInformalParameters(writer);

        writer.end();

        return false;
    }

    private void processSubmission(String controlName)
    {
        String encoded = request.getParameter(controlName);

        Object decoded = encoder.toValue(encoded);

        value = decoded;
    }

    public String getClientId()
    {
        if (clientId == null)
        {
            clientId = jsSupport.allocateClientId(resources);
            hiddenInputElement.forceAttributes("id", clientId);
        }

        return clientId;
    }

    public String getControlName()
    {
        return controlName;
    }
}

@@ -694,49 +694,8 @@
 .*;%0A
-import org.apache.tapestry5.dom.Element;%0A
 impo
@@ -864,16 +864,57 @@
 meters;%0A
+import org.apache.tapestry5.dom.Element;%0A
 import o
@@ -957,16 +957,77 @@
 Inject;%0A
+import org.apache.tapestry5.ioc.internal.util.InternalUtils;%0A
 import o
@@ -1494,17 +1494,32 @@
 ated.%0A *
- 
+%0A * @tapestrydoc
 %0A * @sin
@@ -1529,32 +1529,16 @@
 5.1.0.2%0A
- * @tapestrydoc%0A
  */%0A@Sup
@@ -1799,24 +1799,441 @@
 ect value;%0A%0A
+    /**%0A     * Defines how nulls on the server side, or sent from the client side, are treated. The selected strategy may%0A     * replace the nulls with some other value. The default strategy leaves nulls alone.  Another built-in strategy,%0A     * zero, replaces nulls with the value 0.%0A     */%0A    @Parameter(defaultPrefix = BindingConstants.NULLFIELDSTRATEGY, value = %22default%22)%0A    private NullFieldStrategy nulls;%0A%0A
     /**%0A    
@@ -3391,32 +3391,42 @@
 upport == null)%0A
+        %7B%0A
             thro
@@ -3510,16 +3510,26 @@
 nent.%22);
+%0A        %7D
 %0A%0A      
@@ -3701,32 +3701,132 @@
         
-String encoded =
+Object toEncode = value == null ? nulls.replaceToClient() : value;%0A%0A        String encoded = toEncode == null ? %22%22 :
  encoder
@@ -3835,20 +3835,23 @@
 oClient(
-valu
+toEncod
 e);%0A%0A   
@@ -4198,24 +4198,147 @@
     
-Object decoded =
+String toDecode = InternalUtils.isBlank(encoded) ? nulls.replaceFromClient() : encoded;%0A%0A        Object decoded = toDecode == null ? null :
  enc
@@ -4350,23 +4350,24 @@
 toValue(
+toD
 e
-n
 code
-d
 );%0A%0A    

Support a nulls parameter on Hidden to correctly handle null values sent to or received from the client inside the field