001// Copyright 2008-2014 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
015package org.apache.tapestry5.corelib.internal;
016
017import java.io.IOException;
018import java.io.ObjectOutputStream;
019
020import org.apache.tapestry5.ComponentAction;
021import org.apache.tapestry5.runtime.Component;
022import org.apache.tapestry5.services.ClientDataEncoder;
023import org.apache.tapestry5.services.ClientDataSink;
024import org.slf4j.Logger;
025
026/**
027 * Used to collection component actions, with the ultimate goal being the creation of a MIME-encoded string of the
028 * serialization of those actions.
029 */
030public class ComponentActionSink
031{
032    private final Logger logger;
033
034    private final ObjectOutputStream stream;
035
036    private final ClientDataSink sink;
037
038    private boolean empty = true;
039
040    public ComponentActionSink(Logger logger, ClientDataEncoder encoder)
041    {
042        this.logger = logger;
043
044        sink = encoder.createSink();
045
046        stream = sink.getObjectOutputStream();
047    }
048
049    public <T> void store(T component, ComponentAction<T> action)
050    {
051        streamAction(component, false, action);
052    }
053
054    public <T> void storeCancel(T component, ComponentAction<T> action)
055    {
056        streamAction(component, true, action);
057    }
058
059    private <T> void streamAction(T component, boolean cancel, ComponentAction<T> action)
060    {
061        Component castComponent = (Component) component;
062        assert action != null;
063
064        String completeId = castComponent.getComponentResources().getCompleteId();
065
066        logger.debug("Storing {}: {} {}", cancel ? "cancel action": "action", completeId, action);
067
068        try
069        {
070            // Writing the complete id is not very efficient, but the GZip filter
071            // should help out there.
072            stream.writeUTF(completeId);
073            stream.writeBoolean(cancel);
074            stream.writeObject(action);
075        }
076        catch (IOException ex)
077        {
078            throw new RuntimeException(String.format("Error serializing component action for component %s: %s", completeId, ex), ex);
079        }
080
081        empty = false;
082    }
083
084    /** @since 5.2.0 */
085    public boolean isEmpty()
086    {
087        return empty;
088    }
089
090    public String getClientData()
091    {
092        return sink.getClientData();
093    }
094}