001: /* Copyright (c) 2001 - 2007 TOPP - www.openplans.org. All rights reserved.
002: * This code is licensed under the GPL 2.0 license, availible at the root
003: * application directory.
004: */
005: package org.geoserver.template;
006:
007: import freemarker.cache.ClassTemplateLoader;
008: import freemarker.cache.FileTemplateLoader;
009: import freemarker.cache.TemplateLoader;
010: import freemarker.template.Configuration;
011:
012: import org.geotools.feature.FeatureType;
013: import org.vfny.geoserver.global.GeoserverDataDirectory;
014: import java.io.File;
015: import java.io.IOException;
016: import java.io.Reader;
017: import java.util.NoSuchElementException;
018: import java.util.logging.Logger;
019:
020: /**
021: * A freemarker template loader which can load templates from locations under
022: * a GeoServer data directory.
023: * <p>
024: * To use this template loader, use the {@link Configuration#setTemplateLoader(TemplateLoader)}
025: * method:
026: * <pre>
027: * <code>
028: * Configuration cfg = new Configuration();
029: * cfg.setTemplateLoader( new GeoServerTemplateLoader() );
030: * ...
031: * Template template = cfg.getTemplate( "foo.ftl" );
032: * ...
033: * </code>
034: * </pre>
035: * </p>
036: * <p>
037: * In {@link #findTemplateSource(String)}, the following lookup heuristic is
038: * applied to locate a file based on the given path.
039: * <ol>
040: * <li>The path relative to '<data_dir>/featureTypes/[featureType]'
041: * given that a feature ( {@link #setFeatureType(String)} ) has been set
042: * <li>The path relative to '<data_dir>/featureTypes'
043: * <li>The path relative to '<data_dir>/templates'
044: * <li>The path relative to the calling class with {@link Class#getResource(String)}.
045: * </ol>
046: * <b>Note:</b> If method 5 succeeds, the resulting template will be copied to
047: * the 'templates' directory of the data directory.
048: * </p>
049: *
050: * @author Justin Deoliveira, The Open Planning Project, jdeolive@openplans.org
051: *
052: */
053: public class GeoServerTemplateLoader implements TemplateLoader {
054: /** logger */
055: static Logger LOGGER = org.geotools.util.logging.Logging
056: .getLogger("org.geoserver.template");
057:
058: /**
059: * Delegate file based template loader
060: */
061: FileTemplateLoader fileTemplateLoader;
062:
063: /**
064: * Delegate class based template loader, may be null depending on how
065: */
066: ClassTemplateLoader classTemplateLoader;
067:
068: /**
069: * Feature type directory to load template against
070: */
071: FeatureType featureType;
072:
073: /**
074: * Constructs the template loader.
075: *
076: * @param caller The "calling" class, used to look up templates based with
077: * {@link Class#getResource(String)}, may be <code>null</code>
078: *
079: * @throws IOException
080: */
081: public GeoServerTemplateLoader(Class caller) throws IOException {
082: //create a file template loader to delegate to
083: fileTemplateLoader = new FileTemplateLoader(
084: GeoserverDataDirectory.getGeoserverDataDirectory());
085:
086: //create a class template loader to delegate to
087: if (caller != null) {
088: classTemplateLoader = new ClassTemplateLoader(caller, "");
089: }
090: }
091:
092: /**
093: * Sets the feature type in which templates are loaded against.
094: * <p>
095: *
096: * </p>
097: * @param featureType
098: */
099: public void setFeatureType(FeatureType featureType) {
100: this .featureType = featureType;
101: }
102:
103: public Object findTemplateSource(String path) throws IOException {
104: File template = null;
105:
106: //first check relative to set feature type
107: try {
108: if (featureType != null) {
109: String dirName = GeoserverDataDirectory
110: .findFeatureTypeDirName(featureType);
111: template = (File) fileTemplateLoader
112: .findTemplateSource("featureTypes"
113: + File.separator + dirName
114: + File.separator + path);
115:
116: if (template != null) {
117: return template;
118: }
119: }
120: } catch (NoSuchElementException e) {
121: // this one is thrown if the feature type is not found, and happens whenever
122: // the feature type is a remote one
123: // No problem, we just go on, there won't be any specific template for it
124: }
125:
126: // next, try relative to feature types
127: template = (File) fileTemplateLoader
128: .findTemplateSource("featureTypes" + File.separator
129: + path);
130:
131: if (template != null) {
132: return template;
133: }
134:
135: //next, check the templates directory
136: template = (File) fileTemplateLoader
137: .findTemplateSource("templates" + File.separator + path);
138:
139: if (template != null) {
140: return template;
141: }
142:
143: //final effort to use a class resource
144: if (classTemplateLoader != null) {
145: Object source = classTemplateLoader
146: .findTemplateSource(path);
147:
148: //wrap the source in a source that maintains the orignial path
149: if (source != null) {
150: return new ClassTemplateSource(path, source);
151: }
152: }
153:
154: return null;
155: }
156:
157: public long getLastModified(Object source) {
158: if (source instanceof File) {
159: //loaded from file
160: return fileTemplateLoader.getLastModified(source);
161: } else {
162: //loaded from class
163: ClassTemplateSource wrapper = (ClassTemplateSource) source;
164:
165: return classTemplateLoader.getLastModified(wrapper.source);
166: }
167: }
168:
169: public Reader getReader(Object source, String encoding)
170: throws IOException {
171: if (source instanceof File) {
172: // loaded from file
173: return fileTemplateLoader.getReader(source, encoding);
174: } else {
175: // get teh resource for the raw source as use it right away
176: ClassTemplateSource wrapper = (ClassTemplateSource) source;
177:
178: return classTemplateLoader.getReader(wrapper.source,
179: encoding);
180: }
181: }
182:
183: public void closeTemplateSource(Object source) throws IOException {
184: if (source instanceof File) {
185: fileTemplateLoader.closeTemplateSource(source);
186: } else {
187: ClassTemplateSource wrapper = (ClassTemplateSource) source;
188:
189: //close the raw source
190: classTemplateLoader.closeTemplateSource(wrapper.source);
191:
192: //cleanup
193: wrapper.path = null;
194: wrapper.source = null;
195: }
196: }
197:
198: /**
199: * Template source for use when a template is loaded from a class.
200: * <p>
201: * Used to store the intial path so the template can be copied to the data
202: * directory.
203: * </p>
204: * @author Justin Deoliveira, The Open Planning Project, jdeolive@openplans.org
205: *
206: */
207: static class ClassTemplateSource {
208: /**
209: * The path used to load the template.
210: */
211: String path;
212:
213: /**
214: * The raw source from the class template loader
215: */
216: Object source;
217:
218: public ClassTemplateSource(String path, Object source) {
219: this.path = path;
220: this.source = source;
221: }
222: }
223: }
|