001// Copyright 2004, 2005 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.tapestry.link;
016
017import java.util.ArrayList;
018import java.util.HashMap;
019import java.util.Iterator;
020import java.util.List;
021import java.util.Map;
022
023import org.apache.tapestry.AbstractComponent;
024import org.apache.tapestry.IMarkupWriter;
025import org.apache.tapestry.IRequestCycle;
026import org.apache.tapestry.PageRenderSupport;
027import org.apache.tapestry.TapestryUtils;
028import org.apache.tapestry.components.ILinkComponent;
029import org.apache.tapestry.components.LinkEventType;
030import org.apache.tapestry.engine.IEngineService;
031import org.apache.tapestry.engine.ILink;
032
033/**
034 * Base class for implementations of {@link ILinkComponent}. Includes a disabled attribute (that
035 * should be bound to a disabled parameter), an anchor attribute, and a renderer attribute (that
036 * should be bound to a renderer parameter). A default, shared instance of
037 * {@link org.apache.tapestry.link.DefaultLinkRenderer} is used when no specific renderer is
038 * provided.
039 * 
040 * @author Howard Lewis Ship
041 */
042
043public abstract class AbstractLinkComponent extends AbstractComponent implements ILinkComponent
044{
045    private Map _eventHandlers;
046
047    public abstract boolean isDisabled();
048
049    /**
050     * Adds an event handler (typically, from a wrapped component such as a
051     * {@link org.apache.tapestry.html.Rollover}).
052     */
053
054    public void addEventHandler(LinkEventType eventType, String functionName)
055    {
056        Object currentValue;
057
058        if (_eventHandlers == null)
059            _eventHandlers = new HashMap();
060
061        currentValue = _eventHandlers.get(eventType);
062
063        // The first value is added as a String
064
065        if (currentValue == null)
066        {
067            _eventHandlers.put(eventType, functionName);
068            return;
069        }
070
071        // When adding the second value, convert to a List
072
073        if (currentValue instanceof String)
074        {
075            List list = new ArrayList();
076            list.add(currentValue);
077            list.add(functionName);
078
079            _eventHandlers.put(eventType, list);
080            return;
081        }
082
083        // For the third and up, add the new function to the List
084
085        List list = (List) currentValue;
086        list.add(functionName);
087    }
088
089    /**
090     * Renders the link by delegating to an instance of {@link ILinkRenderer}.
091     */
092
093    protected void renderComponent(IMarkupWriter writer, IRequestCycle cycle)
094    {
095        getRenderer().renderLink(writer, cycle, this);
096    }
097
098    protected void cleanupAfterRender(IRequestCycle cycle)
099    {
100        _eventHandlers = null;
101
102        super.cleanupAfterRender(cycle);
103    }
104
105    protected void writeEventHandlers(IMarkupWriter writer, IRequestCycle cycle)
106    {
107        String name = null;
108
109        if (_eventHandlers == null)
110            return;
111
112        PageRenderSupport pageRenderSupport = TapestryUtils.getPageRenderSupport(cycle, this);
113
114        Iterator i = _eventHandlers.entrySet().iterator();
115
116        while (i.hasNext())
117        {
118            Map.Entry entry = (Map.Entry) i.next();
119            LinkEventType type = (LinkEventType) entry.getKey();
120
121            name = writeEventHandler(
122                    writer,
123                    pageRenderSupport,
124                    name,
125                    type.getAttributeName(),
126                    entry.getValue());
127        }
128
129    }
130
131    protected String writeEventHandler(IMarkupWriter writer, PageRenderSupport pageRenderSupport,
132            String name, String attributeName, Object value)
133    {
134        String wrapperFunctionName;
135
136        if (value instanceof String)
137        {
138            wrapperFunctionName = (String) value;
139        }
140        else
141        {
142            String finalName = name == null ? pageRenderSupport.getUniqueString("Link") : name;
143
144            wrapperFunctionName = attributeName + "_" + finalName;
145
146            StringBuffer buffer = new StringBuffer();
147
148            buffer.append("function ");
149            buffer.append(wrapperFunctionName);
150            buffer.append(" ()\n{\n");
151
152            Iterator i = ((List) value).iterator();
153            while (i.hasNext())
154            {
155                String functionName = (String) i.next();
156                buffer.append("  ");
157                buffer.append(functionName);
158                buffer.append("();\n");
159            }
160
161            buffer.append("}\n\n");
162
163            pageRenderSupport.addBodyScript(buffer.toString());
164        }
165
166        writer.attribute(attributeName, "javascript:" + wrapperFunctionName + "();");
167
168        return name;
169    }
170
171    /** @since 3.0 * */
172
173    public abstract ILinkRenderer getRenderer();
174
175    public abstract void setRenderer(ILinkRenderer renderer);
176
177    public void renderAdditionalAttributes(IMarkupWriter writer, IRequestCycle cycle)
178    {
179        writeEventHandlers(writer, cycle);
180
181        // Generate additional attributes from informal parameters.
182
183        renderInformalParameters(writer, cycle);
184    }
185
186    /**
187     * Utility method for subclasses; Gets the named service from the engine and invokes
188     * {@link IEngineService#getLink(org.apache.tapestry.IComponent, Object[])}on
189     * it.
190     * 
191     * @since 3.0
192     * @deprecated To be removed in 4.1; links may now have the necessary engine service injected.
193     */
194
195    protected ILink getLink(IRequestCycle cycle, String serviceName, Object parameter)
196    {
197        IEngineService service = cycle.getEngine().getService(serviceName);
198
199        return service.getLink(false, parameter);
200    }
201
202    public abstract String getAnchor();
203
204    public ILink getLink(IRequestCycle cycle)
205    {
206        return null;
207    }
208
209    /**
210     * Sets the renderer parameter property to its default value
211     * {@link DefaultLinkRenderer#SHARED_INSTANCE}.
212     * 
213     * @since 3.0
214     */
215    protected void finishLoad()
216    {
217        setRenderer(DefaultLinkRenderer.SHARED_INSTANCE);
218    }
219
220}