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    
015    package org.apache.tapestry.link;
016    
017    import java.util.ArrayList;
018    import java.util.HashMap;
019    import java.util.Iterator;
020    import java.util.List;
021    import java.util.Map;
022    
023    import org.apache.tapestry.AbstractComponent;
024    import org.apache.tapestry.IMarkupWriter;
025    import org.apache.tapestry.IRequestCycle;
026    import org.apache.tapestry.PageRenderSupport;
027    import org.apache.tapestry.TapestryUtils;
028    import org.apache.tapestry.components.ILinkComponent;
029    import org.apache.tapestry.components.LinkEventType;
030    import org.apache.tapestry.engine.IEngineService;
031    import 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    
043    public 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    }