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}