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 }