1 /*
2 * Title: AgentDecoratorMapper
3 * Description:
4 *
5 * This software is published under the terms of the OpenSymphony Software
6 * License version 1.1, of which a copy has been included with this
7 * distribution in the LICENSE.txt file.
8 */
9
10 package com.opensymphony.module.sitemesh.mapper;
11
12 import com.opensymphony.module.sitemesh.Config;
13 import com.opensymphony.module.sitemesh.Decorator;
14 import com.opensymphony.module.sitemesh.DecoratorMapper;
15 import com.opensymphony.module.sitemesh.Page;
16
17 import javax.servlet.http.HttpServletRequest;
18 import java.io.File;
19 import java.util.HashMap;
20 import java.util.Iterator;
21 import java.util.Map;
22 import java.util.Properties;
23
24 /**
25 * The AgentDecoratorMapper can determine the user-agent (i.e. web-browser)
26 * requesting a page, and map to a suitable Decorator.
27 *
28 * <p>This can be useful for supplying different versions of the same content
29 * for different browsers (e.g. vanilla HTML for Lynx, complex tables and frames
30 * for Netscape, extra stuff for IE5, etc).</p>
31 *
32 * <p>This can also be used to enhance search-engine ratings by using a 'bait and
33 * switch' system - this involves showing a search-engine friendly of the content
34 * to spiders only.</p>
35 *
36 * <p>When AgentDecoratorMapper is in the chain, it will request the appropriate Decorator
37 * from its parent. It will then add an extention to the filename of the Decorator, and
38 * if that file exists it shall be used as the Decorator instead. For example, if the
39 * Decorator path is <code>/blah.jsp</code> and the detected user-agent is <code>ie</code>,
40 * the path <code>/blah-ie.jsp</code> shall be used.</p>
41 *
42 * <p>The agent mappings are configured by passing properties with <code>match.</code> as a prefix.
43 * For example: 'match.MSIE'=ie , 'match.Lynx'=plain .</p>
44 *
45 * @author <a href="mailto:joe@truemesh.com">Joe Walnes</a>
46 * @version $Revision: 1.2 $
47 *
48 * @see com.opensymphony.module.sitemesh.DecoratorMapper
49 */
50 public final class AgentDecoratorMapper extends AbstractDecoratorMapper {
51 private Map map = null;
52
53 public void init(Config config, Properties properties, DecoratorMapper parent) throws InstantiationException {
54 super.init(config, properties, parent);
55 map = new HashMap();
56 initMap(properties);
57 }
58
59 public Decorator getDecorator(HttpServletRequest request, Page page) {
60 try {
61 Decorator result = null;
62 final Decorator d = super.getDecorator(request, page);
63 String path = modifyPath(d.getPage(), getExt(request.getHeader("User-Agent")));
64
65 File decFile = new File(config.getServletContext().getRealPath(path));
66
67 if (decFile.isFile()) {
68 result = new DefaultDecorator(d.getName(), path, null) {
69 public String getInitParameter(String paramName) {
70 return d.getInitParameter(paramName);
71 }
72 };
73 }
74 return result == null ? super.getDecorator(request, page) : result;
75 }
76 catch (NullPointerException e) {
77 return super.getDecorator(request, page);
78 }
79 }
80
81 /** Get extention for user-agent. */
82 private String getExt(String userAgent) {
83 Iterator i = map.entrySet().iterator();
84 while (i.hasNext()) {
85 Map.Entry entry = (Map.Entry) i.next();
86 String curr = (String) entry.getKey();
87 if (userAgent.indexOf(curr) > -1) return (String) entry.getValue();
88 }
89 return null;
90 }
91
92 /** Change /abc/def.jsp into /abc/def-XYZ.jsp */
93 private static String modifyPath(String path, String ext) {
94 int dot = path.indexOf('.');
95 if (dot > -1) {
96 return path.substring(0, dot) + '-' + ext + path.substring(dot);
97 }
98 else {
99 return path + '-' + ext;
100 }
101 }
102
103 /** Initialize user-agent mappings. */
104 private void initMap(Properties props) {
105 Iterator i = props.entrySet().iterator();
106 while (i.hasNext()) {
107 Map.Entry entry = (Map.Entry) i.next();
108 String key = (String) entry.getKey();
109 if (key.startsWith("match.")) {
110 String match = key.substring(6);
111 String ext = (String) entry.getValue();
112 map.put(match, ext);
113 }
114 }
115 }
116 }