    001    // Copyright 2010 The Apache Software Foundation
    002    //
    003    // Licensed under the Apache License, Version 2.0 (the "License");
    004    // you may not use this file except in compliance with the License.
    005    // You may obtain a copy of the License at
    006    //
    007    // http://www.apache.org/licenses/LICENSE-2.0
    008    //
    009    // Unless required by applicable law or agreed to in writing, software
    010    // distributed under the License is distributed on an "AS IS" BASIS,
    011    // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    012    // See the License for the specific language governing permissions and
    013    // limitations under the License.
    014
    015    package org.apache.tapestry5.internal.services.ajax;
    016
    017    import org.apache.tapestry5.MarkupWriter;
    018    import org.apache.tapestry5.ValidationTracker;
    019    import org.apache.tapestry5.ValidationTrackerImpl;
    020    import org.apache.tapestry5.corelib.components.Form;
    021    import org.apache.tapestry5.corelib.internal.ComponentActionSink;
    022    import org.apache.tapestry5.corelib.internal.HiddenFieldPositioner;
    023    import org.apache.tapestry5.corelib.internal.InternalFormSupport;
    024    import org.apache.tapestry5.internal.util.CaptureResultCallback;
    025    import org.apache.tapestry5.ioc.ScopeConstants;
    026    import org.apache.tapestry5.ioc.annotations.Scope;
    027    import org.apache.tapestry5.ioc.util.IdAllocator;
    028    import org.apache.tapestry5.runtime.Component;
    029    import org.apache.tapestry5.services.ClientDataEncoder;
    030    import org.apache.tapestry5.services.ComponentSource;
    031    import org.apache.tapestry5.services.Environment;
    032    import org.apache.tapestry5.services.FormSupport;
    033    import org.apache.tapestry5.services.Heartbeat;
    034    import org.apache.tapestry5.services.HiddenFieldLocationRules;
    035    import org.slf4j.Logger;
    036
    037    @Scope(ScopeConstants.PERTHREAD)
    038    public class AjaxFormUpdateControllerImpl implements AjaxFormUpdateController
    039    {
    040        private final ComponentSource componentSource;
    041
    042        private final HiddenFieldLocationRules rules;
    043
    044        private final Environment environment;
    045
    046        private final Heartbeat heartbeat;
    047
    048        private final ClientDataEncoder clientDataEncoder;
    049
    050        private final Logger logger;
    051
    052        private String formComponentId;
    053
    054        private String formClientId;
    055
    056        private HiddenFieldPositioner hiddenFieldPositioner;
    057
    058        private ComponentActionSink actionSink;
    059
    060        private InternalFormSupport formSupport;
    061
    062        public AjaxFormUpdateControllerImpl(ComponentSource componentSource, HiddenFieldLocationRules rules,
    063                Environment environment, Heartbeat heartbeat, ClientDataEncoder clientDataEncoder, Logger logger)
    064        {
    065            this.componentSource = componentSource;
    066            this.rules = rules;
    067            this.environment = environment;
    068            this.heartbeat = heartbeat;
    069            this.clientDataEncoder = clientDataEncoder;
    070            this.logger = logger;
    071        }
    072
    073        public void initializeForForm(String formComponentId, String formClientId)
    074        {
    075            this.formComponentId = formComponentId;
    076            this.formClientId = formClientId;
    077        }
    078
    079        public void setupBeforePartialZoneRender(MarkupWriter writer)
    080        {
    081            if (formComponentId == null)
    082                return;
    083
    084            hiddenFieldPositioner = new HiddenFieldPositioner(writer, rules);
    085
    086            actionSink = new ComponentActionSink(logger, clientDataEncoder);
    087
    088            formSupport = createInternalFormSupport(formClientId, formComponentId, actionSink);
    089
    090            environment.push(FormSupport.class, formSupport);
    091            environment.push(ValidationTracker.class, new ValidationTrackerImpl());
    092
    093            heartbeat.begin();
    094        }
    095
    096        public void cleanupAfterPartialZoneRender()
    097        {
    098            if (formComponentId == null)
    099                return;
    100
    101            heartbeat.end();
    102
    103            formSupport.executeDeferred();
    104
    105            environment.pop(ValidationTracker.class);
    106            environment.pop(FormSupport.class);
    107
    108            // If the Zone didn't actually contain any form control elements, then
    109            // nothing will have been written to the action sink. In that case,
    110            // get rid of the hidden field, if one was even added.
    111
    112            if (actionSink.isEmpty())
    113            {
    114                hiddenFieldPositioner.discard();
    115
    116                return;
    117            }
    118
    119            // We've collected some hidden data that needs to be placed inside the Zone.
    120            // This will raise an exception if the content of the zone didn't provide such a position.
    121
    122            hiddenFieldPositioner.getElement().attributes("type", "hidden",
    123
    124            "name", Form.FORM_DATA,
    125
    126            "value", actionSink.getClientData());
    127        }
    128
    129        private InternalFormSupport createInternalFormSupport(String formClientId, String formComponentId,
    130                ComponentActionSink actionSink)
    131        {
    132            // Kind of ugly, but the only way to ensure we don't have name collisions on the
    133            // client side is to force a unique id into each name (as well as each id, but that's
    134            // JavaScriptSupport's job). It would be nice if we could agree on the uid, but
    135            // not essential.
    136
    137            String uid = Long.toHexString(System.nanoTime());
    138
    139            IdAllocator idAllocator = new IdAllocator("_" + uid);
    140
    141            Component formComponent = componentSource.getComponent(formComponentId);
    142
    143            CaptureResultCallback<InternalFormSupport> callback = CaptureResultCallback.create();
    144
    145            // This is a bit of a back-door to access a non-public method!
    146
    147            formComponent.getComponentResources().triggerEvent("internalCreateRenderTimeFormSupport", new Object[]
    148            { formClientId, actionSink, idAllocator }, callback);
    149
    150            return callback.getResult();
    151        }
    152    }