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 * <head> portions of the page. [ <a
039 * href="../../../../../ComponentReference/Shell.html">Component Reference </a>]
040 * <p>
041 * Specifically does <em>not</em> provide a <body> tag, that is usually accomplished using a
042 * {@link Body} 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 }