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.html;
016    
017    import org.apache.commons.lang.StringUtils;
018    import org.apache.hivemind.HiveMind;
019    import org.apache.tapestry.*;
020    import org.apache.tapestry.coerce.ValueConverter;
021    import org.apache.tapestry.engine.IEngineService;
022    import org.apache.tapestry.engine.ILink;
023    import org.apache.tapestry.services.ResponseBuilder;
024    import org.apache.tapestry.spec.IApplicationSpecification;
025    
026    import java.util.ArrayList;
027    import java.util.Date;
028    import java.util.Iterator;
029    import java.util.List;
030    
031    /**
032     * Component for creating a standard 'shell' for a page, which comprises the <html> and
033     * &lt;head&gt; portions of the page. [ <a
034     * href="../../../../../ComponentReference/Shell.html">Component Reference </a>]
035     * <p>
036     * Specifically does <em>not</em> provide a &lt;body&gt; tag, that is usually accomplished using a
037     * {@link Body}&nbsp; component.
038     * 
039     * @author Howard Lewis Ship
040     */
041    
042    public abstract class Shell extends AbstractComponent
043    {
044        public static final String SHELL_ATTRIBUTE = "org.apache.tapestry.html.Shell";
045        
046        private static final String GENERATOR_CONTENT = "Tapestry Application Framework, version " + Tapestry.VERSION;
047    
048        protected void renderComponent(IMarkupWriter writer, IRequestCycle cycle)
049        {
050            TapestryUtils.storeUniqueAttribute(cycle, SHELL_ATTRIBUTE, this);
051            
052            long startTime = System.currentTimeMillis();
053            boolean rewinding = cycle.isRewinding();
054            boolean dynamic = getBuilder().isDynamic();
055    
056            if (!rewinding && !dynamic)
057            {
058                writeDocType(writer, cycle);
059    
060                IPage page = getPage();
061    
062                if (!isDisableTapestryMeta()) {
063    
064                    writer.comment("Application: " + getApplicationSpecification().getName());
065    
066                    writer.comment("Page: " + page.getPageName());
067                    writer.comment("Generated: " + new Date());
068                }
069    
070                writer.begin("html");
071                writer.println();
072                writer.begin("head");
073                writer.println();
074    
075                if (!isDisableTapestryMeta())
076                    writeMetaTag(writer, "name", "generator", GENERATOR_CONTENT);
077    
078                if (isDisableCaching())
079                    writeMetaTag(writer, "http-equiv", "content", "no-cache");
080    
081                if (getRenderContentType())
082                    writeMetaTag(writer, "http-equiv", "Content-Type", writer.getContentType());
083    
084                writeRefresh(writer, cycle);
085    
086                if (getRenderBaseTag())
087                    getBaseTagWriter().render(writer, cycle);
088    
089                writer.begin("title");
090    
091                writer.print(getTitle(), getRaw());
092                writer.end(); // title
093                writer.println();
094    
095                IRender delegate = getDelegate();
096    
097                if (delegate != null)
098                    delegate.render(writer, cycle);
099    
100                IRender ajaxDelegate = getAjaxDelegate();
101    
102                if (ajaxDelegate != null)
103                    ajaxDelegate.render(writer, cycle);
104                
105                IAsset stylesheet = getStylesheet();
106    
107                if (stylesheet != null)
108                    writeStylesheetLink(writer, stylesheet);
109    
110                Iterator i = (Iterator) getValueConverter().coerceValue(getStylesheets(), Iterator.class);
111    
112                while (i.hasNext())
113                {
114                    stylesheet = (IAsset) i.next();
115    
116                    writeStylesheetLink(writer, stylesheet);
117                }
118            }
119    
120            // Render the body, the actual page content
121    
122            IMarkupWriter nested = !dynamic ? writer.getNestedWriter() : writer;
123    
124            renderBody(nested, cycle);
125    
126            if (!rewinding)
127            {
128                List relations = getRelations();
129                if (relations != null)
130                    writeRelations(writer, relations); 
131                
132                StringBuffer additionalContent = getContentBuffer();
133                if (additionalContent != null)
134                    writer.printRaw(additionalContent.toString());
135                
136                writer.end(); // head
137            }
138            
139            if (!dynamic)
140                nested.close();
141            
142            if (!rewinding && !dynamic)
143            {
144                writer.end(); // html
145                writer.println();
146    
147                if (!isDisableTapestryMeta()) {
148                    
149                    long endTime = System.currentTimeMillis();
150    
151                    writer.comment("Render time: ~ " + (endTime - startTime) + " ms");     
152                }
153            }
154    
155        }
156    
157        protected void cleanupAfterRender(IRequestCycle cycle)
158        {
159            super.cleanupAfterRender(cycle);
160    
161            cycle.removeAttribute(SHELL_ATTRIBUTE);
162        }    
163        
164        private void writeDocType(IMarkupWriter writer, IRequestCycle cycle)
165        {
166            // This is the real code
167            String doctype = getDoctype();
168            if (HiveMind.isNonBlank(doctype))
169            {
170                writer.printRaw("<!DOCTYPE " + doctype + ">");
171                writer.println();
172            }
173        }
174    
175        private void writeStylesheetLink(IMarkupWriter writer, IAsset stylesheet)
176        {
177            writer.beginEmpty("link");
178            writer.attribute("rel", "stylesheet");
179            writer.attribute("type", "text/css");
180            writer.attribute("href", stylesheet.buildURL());
181            writer.println();
182        }
183        
184        private void writeRefresh(IMarkupWriter writer, IRequestCycle cycle)
185        {
186            int refresh = getRefresh();
187    
188            if (refresh <= 0)
189                return;
190    
191            // Here comes the tricky part ... have to assemble a complete URL
192            // for the current page.
193    
194            IEngineService pageService = getPageService();
195            String pageName = getPage().getPageName();
196    
197            ILink link = pageService.getLink(false, pageName);
198    
199            StringBuffer buffer = new StringBuffer();
200            buffer.append(refresh);
201            buffer.append("; URL=");
202            buffer.append(StringUtils.replace(link.getAbsoluteURL(), "&amp;", "&"));
203    
204            writeMetaTag(writer, "http-equiv", "Refresh", buffer.toString());
205        }
206        
207        private void writeMetaTag(IMarkupWriter writer, String key, String value, String content)
208        {
209            writer.beginEmpty("meta");
210            writer.attribute(key, value);
211            writer.attribute("content", content);
212            writer.println();
213        }
214        
215        private void writeRelations(IMarkupWriter writer, List relations)
216        {
217            Iterator i = relations.iterator();
218            while (i.hasNext())
219            {
220                RelationBean relationBean = (RelationBean) i.next();
221                if (relationBean != null)
222                    writeRelation(writer, relationBean);
223            }
224        }
225        
226        private void writeRelation(IMarkupWriter writer, RelationBean relationBean)
227        {
228                writer.beginEmpty("link");
229                writeAttributeIfNotNull(writer, "rel", relationBean.getRel());
230                writeAttributeIfNotNull(writer, "rev", relationBean.getRev());            
231                writeAttributeIfNotNull(writer, "type", relationBean.getType());
232                writeAttributeIfNotNull(writer, "media", relationBean.getMedia());
233                writeAttributeIfNotNull(writer, "title", relationBean.getTitle());
234                writeAttributeIfNotNull(writer, "href", relationBean.getHref());
235                writer.println();
236        }    
237        
238        private void writeAttributeIfNotNull(IMarkupWriter writer, String name, String value)
239        {
240            if (value != null)
241                writer.attribute(name, value);
242        }
243        
244        /**
245         * Retrieves the {@link Shell} that was stored into the request
246         * cycle. This allows components wrapped by the {@link Shell} to
247         * locate it and access the services it provides.
248         * 
249         * @since 4.1.1
250         */
251        public static Shell get(IRequestCycle cycle)
252        {
253            return (Shell) cycle.getAttribute(SHELL_ATTRIBUTE);
254        }    
255        
256        /**
257         * Adds a relation (stylesheets, favicon, e.t.c.) to the page.
258         *
259         * @since 4.1.1
260         */
261        public void addRelation(RelationBean relation)
262        {
263            List relations = getRelations();
264            if (relations == null)
265                relations = new ArrayList();
266            
267            if (!relations.contains(relation))
268                relations.add(relation);
269            
270            setRelations(relations);             
271        }
272    
273        /**
274         * Include additional content in the header of a page.
275         * 
276         * @param content 
277         *
278         * @since 4.1.1
279         */
280        public void includeAdditionalContent(String content)
281        {
282            if (HiveMind.isBlank(content))
283                return;
284            
285            StringBuffer buffer = getContentBuffer();
286            
287            if (buffer == null)
288                buffer = new StringBuffer();
289            
290            buffer.append(content);
291            
292            setContentBuffer(buffer);
293        }
294        
295        public abstract boolean isDisableCaching();
296        
297        public abstract IRender getAjaxDelegate();
298        
299        public abstract IRender getDelegate();
300        
301        public abstract int getRefresh();
302    
303        public abstract IAsset getStylesheet();
304    
305        public abstract Object getStylesheets();
306    
307        public abstract String getTitle();
308    
309        public abstract String getDoctype();
310    
311        public abstract boolean getRenderContentType();
312    
313        public abstract boolean isDisableTapestryMeta();
314    
315        public abstract ResponseBuilder getBuilder();
316    
317        /** @since 4.0 */
318        public abstract ValueConverter getValueConverter();
319    
320        /** @since 4.0 */
321    
322        public abstract IEngineService getPageService();
323    
324        /** @since 4.0 */
325    
326        public abstract IApplicationSpecification getApplicationSpecification();
327    
328        /** @since 4.0 */
329    
330        public abstract IRender getBaseTagWriter();
331        
332        /** @since 4.0.1 */
333        
334        public abstract boolean getRenderBaseTag();
335        
336        /** @since 4.0.3 */
337        
338        public abstract boolean getRaw();
339        
340        /** @since 4.1.1 */
341        
342        public abstract List getRelations();
343        
344        /** @since 4.1.1 */
345        
346        public abstract void setRelations(List relations);
347        
348        /** @since 4.1.1 */
349        
350        public abstract StringBuffer getContentBuffer();
351        
352        /** @since 4.1.1 */
353        
354        public abstract void setContentBuffer(StringBuffer buffer);    
355    
356    }