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    }