KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > riotfamily > website > css > CssTemplateController


1 /* ***** BEGIN LICENSE BLOCK *****
2  * Version: MPL 1.1
3  * The contents of this file are subject to the Mozilla Public License Version
4  * 1.1 (the "License"); you may not use this file except in compliance with
5  * the License. You may obtain a copy of the License at
6  * http://www.mozilla.org/MPL/
7  *
8  * Software distributed under the License is distributed on an "AS IS" basis,
9  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
10  * for the specific language governing rights and limitations under the
11  * License.
12  *
13  * The Original Code is Riot.
14  *
15  * The Initial Developer of the Original Code is
16  * Neteye GmbH.
17  * Portions created by the Initial Developer are Copyright (C) 2006
18  * the Initial Developer. All Rights Reserved.
19  *
20  * Contributor(s):
21  * Felix Gnass [fgnass at neteye dot de]
22  *
23  * ***** END LICENSE BLOCK ***** */

24 package org.riotfamily.website.css;
25
26 import java.io.File JavaDoc;
27 import java.io.IOException JavaDoc;
28 import java.io.StringWriter JavaDoc;
29 import java.util.HashMap JavaDoc;
30 import java.util.Map JavaDoc;
31 import java.util.regex.Matcher JavaDoc;
32 import java.util.regex.Pattern JavaDoc;
33
34 import javax.servlet.ServletContext JavaDoc;
35 import javax.servlet.http.HttpServletRequest JavaDoc;
36 import javax.servlet.http.HttpServletResponse JavaDoc;
37
38 import org.riotfamily.cachius.spring.AbstractCacheableController;
39 import org.riotfamily.common.web.filter.ResourceStamper;
40 import org.springframework.beans.factory.InitializingBean;
41 import org.springframework.core.io.Resource;
42 import org.springframework.web.context.ServletContextAware;
43 import org.springframework.web.servlet.ModelAndView;
44 import org.springframework.web.servlet.mvc.LastModified;
45 import org.springframework.web.util.UrlPathHelper;
46
47 import freemarker.template.Configuration;
48 import freemarker.template.Template;
49 import freemarker.template.TemplateException;
50
51 /**
52  * Controller that serves dynamic CSS files.
53  * <p>
54  * It's sometimes desirable to use constants in CSS files or to perform
55  * arithmetic operations. This controller allows you to do so, by using
56  * FreeMarker to process the stylesheets.
57  * </p>
58  * <p>
59  * You can place a <code>css.ini</code> file in the same directory where your
60  * stylesheets are located. All properties defined in that file will be
61  * available within the FreeMarker template.
62  * </p>
63  * <p>
64  * Additionally the controller allows you to create styles that look different
65  * in various contexts. A good example would be a website that uses multiple
66  * color schemes. Therefore you can add named sections to your your ini file
67  * and request <code>&lt;file>_&lt;section-name>.css</code> instead of
68  * <code>&lt;file>.css</code>. This will cause the controller to expose the
69  * properties of the requested section, possibly overriding any default
70  * values with the same name.
71  * </p>
72  * <p>
73  * You can access the properties from all sections at any time by using
74  * <code>&lt;section_name>.&lt;property_name></code> in the FreeMarker template.
75  * If a default value has been overridden by a section value you can still
76  * access the original default, by using <code>global.&lt;property_name></code>.
77  * </p>
78  */

79 public class CssTemplateController extends AbstractCacheableController
80         implements LastModified, ServletContextAware, InitializingBean {
81
82     public static final String JavaDoc KEY_PROPERTY = "key";
83
84     public static final String JavaDoc CONTEXT_PATH_PROPERTY = "contextPath";
85
86     private static final String JavaDoc DEFAULT_INI_FILE_NAME = "css.ini";
87
88     private static final String JavaDoc CSS_SUFFIX = ".css";
89
90     private UrlPathHelper urlPathHelper = new UrlPathHelper();
91
92     private ServletContext JavaDoc servletContext;
93
94     private Pattern JavaDoc keyPattern = Pattern.compile("(/[^/]+?)_(.*?)(\\.css)");
95
96     private String JavaDoc contentType = "text/css";
97
98     private Configuration freeMarkerConfig;
99
100     private IniFile iniFile;
101
102     private Pattern JavaDoc urlPattern = Pattern.compile(
103             "(url\\s*\\(\\s*[\"']?)(.*?)(['\"]?\\s*\\))");
104
105     private ResourceStamper stamper;
106
107     private ColorTool colorTool = new ColorTool();
108     
109     private boolean addContextPathToUrls = false;
110
111     public void setServletContext(ServletContext JavaDoc servletContext) {
112         this.servletContext = servletContext;
113     }
114
115     public void setFreeMarkerConfig(Configuration configuration) {
116         this.freeMarkerConfig = configuration;
117     }
118
119     /**
120      * Sets the ResourceStamper that should be used to add timestamps to
121      * URLs specified within the template.
122      *
123      * @see ResourceStamper
124      * @since 6.4
125      */

126     public void setStamper(ResourceStamper stamper) {
127         this.stamper = stamper;
128     }
129
130     /**
131      * Sets whether the contextPath should be added to absolute URLs
132      * specified within the template. Defaults to <code>false</code>.
133      *
134      * @since 6.4
135      */

136     public void setAddContextPathToUrls(boolean addContextPathToUrls) {
137         this.addContextPathToUrls = addContextPathToUrls;
138     }
139
140     public void setIniFileLocation(Resource resource) throws IOException JavaDoc {
141         iniFile = new IniFile(resource.getFile());
142     }
143
144     public void afterPropertiesSet() throws Exception JavaDoc {
145         if (freeMarkerConfig == null) {
146             freeMarkerConfig = new Configuration();
147         }
148         freeMarkerConfig.setDirectoryForTemplateLoading(
149                 new File JavaDoc(servletContext.getRealPath("/")));
150     }
151
152     public long getLastModified(HttpServletRequest JavaDoc request) {
153         DynamicStylesheet stylesheet = lookup(request);
154         return stylesheet.lastModified();
155     }
156
157     public ModelAndView handleRequest(HttpServletRequest JavaDoc request,
158             HttpServletResponse JavaDoc response) throws Exception JavaDoc {
159
160         DynamicStylesheet stylesheet = lookup(request);
161         stylesheet.serve(request, response);
162         return null;
163     }
164
165     protected DynamicStylesheet lookup(HttpServletRequest JavaDoc request) {
166         String JavaDoc path = urlPathHelper.getPathWithinApplication(request);
167         String JavaDoc key = null;
168         File JavaDoc file = new File JavaDoc(servletContext.getRealPath(path));
169         if (!file.exists()) {
170             Matcher JavaDoc matcher = keyPattern.matcher(path);
171             if (matcher.find()) {
172                 key = matcher.group(2);
173                 path = matcher.replaceFirst("$1$3");
174                 file = new File JavaDoc(servletContext.getRealPath(path));
175             }
176         }
177         return new DynamicStylesheet(file, path, key);
178     }
179
180     protected class DynamicStylesheet {
181
182         private File JavaDoc file;
183
184         private String JavaDoc path;
185
186         private String JavaDoc key;
187
188         public DynamicStylesheet(File JavaDoc file, String JavaDoc path, String JavaDoc key) {
189             this.file = file;
190             this.path = path;
191             this.key = key;
192         }
193
194         public long lastModified() {
195             long lastModified = -1;
196             if (file != null) {
197                 lastModified = file.lastModified();
198             }
199             if (iniFile != null) {
200                 lastModified = Math.max(lastModified, iniFile.lastModified());
201             }
202             return lastModified;
203         }
204
205         public void serve(HttpServletRequest JavaDoc request,
206                 HttpServletResponse JavaDoc response)
207                 throws IOException JavaDoc, TemplateException {
208
209             if (file == null) {
210                 response.sendError(HttpServletResponse.SC_NOT_FOUND);
211                 return;
212             }
213             if (!file.getName().endsWith(CSS_SUFFIX)) {
214                 response.sendError(HttpServletResponse.SC_FORBIDDEN);
215             }
216             if (!file.canRead()) {
217                 response.sendError(HttpServletResponse.SC_NOT_FOUND,
218                         "Can't read file " + file.getAbsolutePath());
219
220                 return;
221             }
222
223             if (iniFile == null) {
224                 File JavaDoc f = new File JavaDoc(file.getParentFile(), DEFAULT_INI_FILE_NAME);
225                 if (f.canRead()) {
226                     iniFile = new IniFile(f);
227                 }
228             }
229
230             response.setContentType(contentType);
231
232             Map JavaDoc model = buildModel();
233             model.put(KEY_PROPERTY, key);
234             model.put(CONTEXT_PATH_PROPERTY, request.getContextPath());
235
236             Template template = freeMarkerConfig.getTemplate(path);
237             StringWriter JavaDoc sw = new StringWriter JavaDoc();
238             template.process(model, sw);
239             response.getWriter().print(
240                     processUrls(sw.toString(), request.getContextPath()));
241         }
242
243         private Map JavaDoc buildModel() {
244             HashMap JavaDoc model = new HashMap JavaDoc();
245             model.put("color", colorTool);
246             if (iniFile != null) {
247                 Map JavaDoc sections = iniFile.getSections();
248                 model.putAll(sections);
249                 model.putAll((Map JavaDoc) sections.get(IniFile.GLOBAL_SECTION));
250                 if (key != null) {
251                     Map JavaDoc current = (Map JavaDoc) sections.get(key);
252                     if (current != null) {
253                         model.putAll(current);
254                     }
255                 }
256             }
257             return model;
258         }
259
260         private String JavaDoc processUrls(String JavaDoc css, String JavaDoc contextPath) {
261             if (stamper == null && !addContextPathToUrls) {
262                 return css;
263             }
264             StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
265             Matcher JavaDoc matcher = urlPattern.matcher(css);
266             while (matcher.find()) {
267                 String JavaDoc url = matcher.group(2);
268                 if (stamper != null) {
269                     url = stamper.stamp(url);
270                 }
271                 if (addContextPathToUrls && url.startsWith("/")) {
272                     url = contextPath + url;
273                 }
274                 matcher.appendReplacement(sb, "$1" + url + "$3");
275             }
276             matcher.appendTail(sb);
277             return sb.toString();
278         }
279
280     }
281 }
282
Popular Tags