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 org.apache.hivemind.ApplicationRuntimeException;
018import org.apache.hivemind.HiveMind;
019import org.apache.tapestry.IMarkupWriter;
020import org.apache.tapestry.IRequestCycle;
021import org.apache.tapestry.Tapestry;
022import org.apache.tapestry.components.ILinkComponent;
023import 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
033public 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}