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 org.apache.hivemind.ApplicationRuntimeException; 018 import org.apache.hivemind.HiveMind; 019 import org.apache.tapestry.IMarkupWriter; 020 import org.apache.tapestry.IRequestCycle; 021 import org.apache.tapestry.Tapestry; 022 import org.apache.tapestry.components.ILinkComponent; 023 import org.apache.tapestry.engine.ILink; 024 025 /** 026 * Default implementation of {@link org.apache.tapestry.link.ILinkRenderer}, which does nothing 027 * special. Can be used as a base class to provide additional handling. 028 * 029 * @author Howard Lewis Ship, David Solis 030 * @since 3.0 031 */ 032 033 public class DefaultLinkRenderer implements ILinkRenderer 034 { 035 /** 036 * A shared instance used as a default for any link that doesn't explicitly override. 037 */ 038 039 public static final ILinkRenderer SHARED_INSTANCE = new DefaultLinkRenderer(); 040 041 public void renderLink(IMarkupWriter writer, IRequestCycle cycle, ILinkComponent linkComponent) 042 { 043 IMarkupWriter wrappedWriter = null; 044 045 if (cycle.getAttribute(Tapestry.LINK_COMPONENT_ATTRIBUTE_NAME) != null) 046 throw new ApplicationRuntimeException(LinkMessages.noNesting(), linkComponent, null, 047 null); 048 049 cycle.setAttribute(Tapestry.LINK_COMPONENT_ATTRIBUTE_NAME, linkComponent); 050 051 boolean hasBody = getHasBody(); 052 053 boolean disabled = linkComponent.isDisabled() || cycle.isRewinding(); 054 055 if (!disabled) 056 { 057 if (hasBody) 058 writer.begin(getElement()); 059 else 060 writer.beginEmpty(getElement()); 061 062 writer.attribute(getUrlAttribute(), constructURL(linkComponent, cycle)); 063 064 String target = linkComponent.getTarget(); 065 066 if (HiveMind.isNonBlank(target)) 067 writer.attribute(getTargetAttribute(), target); 068 069 beforeBodyRender(writer, cycle, linkComponent); 070 071 // Allow the wrapped components a chance to render. 072 // Along the way, they may interact with this component 073 // and cause the name variable to get set. 074 075 wrappedWriter = writer.getNestedWriter(); 076 } 077 else 078 wrappedWriter = writer; 079 080 if (hasBody) 081 linkComponent.renderBody(wrappedWriter, cycle); 082 083 if (!disabled) 084 { 085 afterBodyRender(writer, cycle, linkComponent); 086 087 linkComponent.renderAdditionalAttributes(writer, cycle); 088 089 if (hasBody) 090 { 091 wrappedWriter.close(); 092 093 // Close the <element> tag 094 095 writer.end(); 096 } 097 else 098 writer.closeTag(); 099 } 100 101 cycle.removeAttribute(Tapestry.LINK_COMPONENT_ATTRIBUTE_NAME); 102 } 103 104 /** 105 * Converts the EngineServiceLink into a URI or URL. This implementation gets the scheme and 106 * anchor from the component (both of which may be null), and invokes 107 * {@link ILink#getURL(String, String, int, String, boolean)}. 108 */ 109 110 protected String constructURL(ILinkComponent component, IRequestCycle cycle) 111 { 112 ILink link = component.getLink(cycle); 113 114 String scheme = component.getScheme(); 115 Integer port = component.getPort(); 116 int portI = (port == null) ? 0 : port.intValue(); 117 String anchor = component.getAnchor(); 118 119 return link.getURL(scheme, null, portI, anchor, true); 120 } 121 122 /** 123 * Invoked after the href attribute has been written but before the body of the link is rendered 124 * (but only if the link is not disabled). 125 * <p> 126 * This implementation does nothing. 127 */ 128 129 protected void beforeBodyRender(IMarkupWriter writer, IRequestCycle cycle, ILinkComponent link) 130 { 131 } 132 133 /** 134 * Invoked after the body of the link is rendered, but before 135 * {@link ILinkComponent#renderAdditionalAttributes(IMarkupWriter, IRequestCycle)}is invoked 136 * (but only if the link is not disabled). 137 * <p> 138 * This implementation does nothing. 139 */ 140 141 protected void afterBodyRender(IMarkupWriter writer, IRequestCycle cycle, ILinkComponent link) 142 { 143 } 144 145 /** @since 3.0 * */ 146 147 protected String getElement() 148 { 149 return "a"; 150 } 151 152 protected String getUrlAttribute() 153 { 154 return "href"; 155 } 156 157 protected String getTargetAttribute() 158 { 159 return "target"; 160 } 161 162 protected boolean getHasBody() 163 { 164 return true; 165 } 166 }