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