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    }