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(Tapestry
047                        .getMessage("AbstractLinkComponent.no-nesting"), linkComponent, null, null);
048    
049            cycle.setAttribute(Tapestry.LINK_COMPONENT_ATTRIBUTE_NAME, linkComponent);
050    
051            boolean hasBody = getHasBody();
052    
053            boolean disabled = linkComponent.isDisabled();
054    
055            if (!disabled)
056            {
057                ILink l = linkComponent.getLink(cycle);
058    
059                if (hasBody)
060                    writer.begin(getElement());
061                else
062                    writer.beginEmpty(getElement());
063    
064                writer.attribute(getUrlAttribute(), constructURL(l, linkComponent.getAnchor(), cycle));
065    
066                String target = linkComponent.getTarget();
067    
068                if (HiveMind.isNonBlank(target))
069                    writer.attribute(getTargetAttribute(), target);
070    
071                beforeBodyRender(writer, cycle, linkComponent);
072    
073                // Allow the wrapped components a chance to render.
074                // Along the way, they may interact with this component
075                // and cause the name variable to get set.
076    
077                wrappedWriter = writer.getNestedWriter();
078            }
079            else
080                wrappedWriter = writer;
081    
082            if (hasBody)
083                linkComponent.renderBody(wrappedWriter, cycle);
084    
085            if (!disabled)
086            {
087                afterBodyRender(writer, cycle, linkComponent);
088    
089                linkComponent.renderAdditionalAttributes(writer, cycle);
090    
091                if (hasBody)
092                {
093                    wrappedWriter.close();
094    
095                    // Close the <element> tag
096    
097                    writer.end();
098                }
099                else
100                    writer.closeTag();
101            }
102    
103            cycle.removeAttribute(Tapestry.LINK_COMPONENT_ATTRIBUTE_NAME);
104        }
105    
106        /**
107         * Converts the EngineServiceLink into a URI or URL. This implementation simply invokes
108         * {@link ILink#getURL(String, boolean)}.
109         */
110    
111        protected String constructURL(ILink link, String anchor, IRequestCycle cycle)
112        {
113            return link.getURL(anchor, true);
114        }
115    
116        /**
117         * Invoked after the href attribute has been written but before the body of the link is rendered
118         * (but only if the link is not disabled).
119         * <p>
120         * This implementation does nothing.
121         */
122    
123        protected void beforeBodyRender(IMarkupWriter writer, IRequestCycle cycle, ILinkComponent link)
124        {
125        }
126    
127        /**
128         * Invoked after the body of the link is rendered, but before
129         * {@link ILinkComponent#renderAdditionalAttributes(IMarkupWriter, IRequestCycle)}is invoked
130         * (but only if the link is not disabled).
131         * <p>
132         * This implementation does nothing.
133         */
134    
135        protected void afterBodyRender(IMarkupWriter writer, IRequestCycle cycle, ILinkComponent link)
136        {
137        }
138    
139        /** @since 3.0 * */
140    
141        protected String getElement()
142        {
143            return "a";
144        }
145    
146        protected String getUrlAttribute()
147        {
148            return "href";
149        }
150    
151        protected String getTargetAttribute()
152        {
153            return "target";
154        }
155    
156        protected boolean getHasBody()
157        {
158            return true;
159        }
160    }