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 * <head> portions of the page. [ <a 034 * href="../../../../../ComponentReference/Shell.html">Component Reference </a>] 035 * <p> 036 * Specifically does <em>not</em> provide a <body> tag, that is usually accomplished using a 037 * {@link Body} 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, cycle, 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, cycle, 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, IRequestCycle cycle, 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(), "&", "&")); 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 }