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