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                renderInformalParameters(writer, cycle);
072                writer.println();
073                writer.begin("head");
074                writer.println();
075    
076                if (!isDisableTapestryMeta())
077                    writeMetaTag(writer, "name", "generator", GENERATOR_CONTENT);
078    
079                if (isDisableCaching())
080                    writeMetaTag(writer, "http-equiv", "content", "no-cache");
081    
082                if (getRenderContentType())
083                    writeMetaTag(writer, "http-equiv", "Content-Type", writer.getContentType());
084    
085                writeRefresh(writer, cycle);
086    
087                if (getRenderBaseTag())
088                    getBaseTagWriter().render(writer, cycle);
089    
090                writer.begin("title");
091    
092                writer.print(getTitle(), getRaw());
093                writer.end(); // title
094                writer.println();
095    
096                IRender delegate = getDelegate();
097    
098                if (delegate != null)
099                    delegate.render(writer, cycle);
100    
101                IRender ajaxDelegate = getAjaxDelegate();
102    
103                if (ajaxDelegate != null)
104                    ajaxDelegate.render(writer, cycle);
105    
106                IAsset stylesheet = getStylesheet();
107    
108                if (stylesheet != null)
109                    writeStylesheetLink(writer, stylesheet);
110    
111                Iterator i = (Iterator) getValueConverter().coerceValue(getStylesheets(), Iterator.class);
112    
113                while (i.hasNext())
114                {
115                    stylesheet = (IAsset) i.next();
116    
117                    writeStylesheetLink(writer, stylesheet);
118                }
119            }
120    
121            // Render the body, the actual page content
122    
123            IMarkupWriter nested = !dynamic ? writer.getNestedWriter() : writer;
124    
125            renderBody(nested, cycle);
126    
127            if (!rewinding)
128            {
129                List relations = getRelations();
130                if (relations != null)
131                    writeRelations(writer, relations);
132    
133                StringBuffer additionalContent = getContentBuffer();
134                if (additionalContent != null)
135                    writer.printRaw(additionalContent.toString());
136    
137                writer.end(); // head
138            }
139    
140            if (!dynamic)
141                nested.close();
142    
143            if (!rewinding && !dynamic)
144            {
145                writer.end(); // html
146                writer.println();
147    
148                if (!isDisableTapestryMeta())
149                {
150                    long endTime = System.currentTimeMillis();
151    
152                    writer.comment("Render time: ~ " + (endTime - startTime) + " ms");
153                }
154            }
155    
156        }
157    
158        protected void cleanupAfterRender(IRequestCycle cycle)
159        {
160            super.cleanupAfterRender(cycle);
161    
162            cycle.removeAttribute(SHELL_ATTRIBUTE);
163        }
164    
165        private void writeDocType(IMarkupWriter writer, IRequestCycle cycle)
166        {
167            // This is the real code
168            String doctype = getDoctype();
169            if (HiveMind.isNonBlank(doctype))
170            {
171                writer.printRaw("<!DOCTYPE " + doctype + ">");
172                writer.println();
173            }
174        }
175    
176        private void writeStylesheetLink(IMarkupWriter writer, IAsset stylesheet)
177        {
178            writer.beginEmpty("link");
179            writer.attribute("rel", "stylesheet");
180            writer.attribute("type", "text/css");
181            writer.attribute("href", stylesheet.buildURL());
182            writer.println();
183        }
184    
185        private void writeRefresh(IMarkupWriter writer, IRequestCycle cycle)
186        {
187            int refresh = getRefresh();
188    
189            if (refresh <= 0)
190                return;
191    
192            // Here comes the tricky part ... have to assemble a complete URL
193            // for the current page.
194    
195            IEngineService pageService = getPageService();
196            String pageName = getPage().getPageName();
197    
198            ILink link = pageService.getLink(false, pageName);
199    
200            StringBuffer buffer = new StringBuffer();
201            buffer.append(refresh);
202            buffer.append("; URL=");
203            buffer.append(StringUtils.replace(link.getAbsoluteURL(), "&amp;", "&"));
204    
205            writeMetaTag(writer, "http-equiv", "Refresh", buffer.toString());
206        }
207    
208        private void writeMetaTag(IMarkupWriter writer, String key, String value, String content)
209        {
210            writer.beginEmpty("meta");
211            writer.attribute(key, value);
212            writer.attribute("content", content);
213            writer.println();
214        }
215    
216        private void writeRelations(IMarkupWriter writer, List relations)
217        {
218            Iterator i = relations.iterator();
219            
220            while (i.hasNext())
221            {
222                RelationBean relationBean = (RelationBean) i.next();
223                if (relationBean != null)
224                    writeRelation(writer, relationBean);
225            }
226        }
227    
228        private void writeRelation(IMarkupWriter writer, RelationBean relationBean)
229        {
230            writer.beginEmpty("link");
231    
232            writeAttributeIfNotNull(writer, "rel", relationBean.getRel());
233            writeAttributeIfNotNull(writer, "rev", relationBean.getRev());
234            writeAttributeIfNotNull(writer, "type", relationBean.getType());
235            writeAttributeIfNotNull(writer, "media", relationBean.getMedia());
236            writeAttributeIfNotNull(writer, "title", relationBean.getTitle());
237            writeAttributeIfNotNull(writer, "href", relationBean.getHref());
238            
239            writer.println();
240        }
241    
242        private void writeAttributeIfNotNull(IMarkupWriter writer, String name, String value)
243        {
244            if (value != null)
245                writer.attribute(name, value);
246        }
247    
248        /**
249         * Retrieves the {@link Shell} that was stored into the request
250         * cycle. This allows components wrapped by the {@link Shell} to
251         * locate it and access the services it provides.
252         *
253         * @since 4.1.1
254         */
255        public static Shell get(IRequestCycle cycle)
256        {
257            return (Shell) cycle.getAttribute(SHELL_ATTRIBUTE);
258        }
259    
260        /**
261         * Adds a relation (stylesheets, favicon, e.t.c.) to the page.
262         *
263         * @since 4.1.1
264         */
265        public void addRelation(RelationBean relation)
266        {
267            List relations = getRelations();
268            if (relations == null)
269                relations = new ArrayList();
270    
271            if (!relations.contains(relation))
272                relations.add(relation);
273    
274            setRelations(relations);
275        }
276    
277        /**
278         * Include additional content in the header of a page.
279         *
280         * @param content
281         *
282         * @since 4.1.1
283         */
284        public void includeAdditionalContent(String content)
285        {
286            if (HiveMind.isBlank(content))
287                return;
288    
289            StringBuffer buffer = getContentBuffer();
290    
291            if (buffer == null)
292                buffer = new StringBuffer();
293    
294            buffer.append(content);
295    
296            setContentBuffer(buffer);
297        }
298    
299        public abstract boolean isDisableCaching();
300    
301        public abstract IRender getAjaxDelegate();
302    
303        public abstract IRender getDelegate();
304    
305        public abstract int getRefresh();
306    
307        public abstract IAsset getStylesheet();
308    
309        public abstract Object getStylesheets();
310    
311        public abstract String getTitle();
312    
313        public abstract String getDoctype();
314    
315        public abstract boolean getRenderContentType();
316    
317        public abstract boolean isDisableTapestryMeta();
318    
319        public abstract ResponseBuilder getBuilder();
320    
321        /** @since 4.0 */
322        public abstract ValueConverter getValueConverter();
323    
324        /** @since 4.0 */
325    
326        public abstract IEngineService getPageService();
327    
328        /** @since 4.0 */
329    
330        public abstract IApplicationSpecification getApplicationSpecification();
331    
332        /** @since 4.0 */
333    
334        public abstract IRender getBaseTagWriter();
335    
336        /** @since 4.0.1 */
337    
338        public abstract boolean getRenderBaseTag();
339    
340        /** @since 4.0.3 */
341    
342        public abstract boolean getRaw();
343    
344        /** @since 4.1.1 */
345    
346        public abstract List getRelations();
347    
348        /** @since 4.1.1 */
349    
350        public abstract void setRelations(List relations);
351    
352        /** @since 4.1.1 */
353    
354        public abstract StringBuffer getContentBuffer();
355    
356        /** @since 4.1.1 */
357    
358        public abstract void setContentBuffer(StringBuffer buffer);
359    
360        /** @since 4.1.4 */
361        public abstract String getSearchIds();
362    
363        /** @since 4.1.4 */
364        public abstract void setSearchIds(String ids);
365    
366    }