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