View Javadoc
1   /*
2    * $Source$
3    * $Revision$
4    *
5    * Copyright (C) 2000 William Chesters
6    *
7    * Part of Melati (http://melati.org), a framework for the rapid
8    * development of clean, maintainable web applications.
9    *
10   * Melati is free software; Permission is granted to copy, distribute
11   * and/or modify this software under the terms either:
12   *
13   * a) the GNU General Public License as published by the Free Software
14   *    Foundation; either version 2 of the License, or (at your option)
15   *    any later version,
16   *
17   *    or
18   *
19   * b) any version of the Melati Software License, as published
20   *    at http://melati.org
21   *
22   * You should have received a copy of the GNU General Public License and
23   * the Melati Software License along with this program;
24   * if not, write to the Free Software Foundation, Inc.,
25   * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA to obtain the
26   * GNU General Public License and visit http://melati.org to obtain the
27   * Melati Software License.
28   *
29   * Feel free to contact the Developers of Melati (http://melati.org),
30   * if you would like to work out a different arrangement than the options
31   * outlined here.  It is our intention to allow Melati to be used by as
32   * wide an audience as possible.
33   *
34   * This program is distributed in the hope that it will be useful,
35   * but WITHOUT ANY WARRANTY; without even the implied warranty of
36   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
37   * GNU General Public License for more details.
38   *
39   * Contact details for copyright holder:
40   *
41   *     William Chesters <williamc At paneris.org>
42   *     http://paneris.org/~williamc
43   *     Obrechtstraat 114, 2517VX Den Haag, The Netherlands
44   */
45  
46  package org.melati.template;
47  
48  import org.melati.poem.FieldAttributes;
49  import org.melati.util.MelatiBugMelatiException;
50  
51  import java.util.Hashtable;
52  
53  /**
54   * Load a template to render an object based upon the object's class.
55   */
56  public final class ClassNameTempletLoader implements TempletLoader {
57  
58    /** The instance. */
59    private static ClassNameTempletLoader it = null;
60  
61    // NOTE It is not expected that templates will be added at runtime.
62    private static Hashtable<String,Template> templetForClassCache = new Hashtable<String,Template>();
63    
64    private static final Integer FOUND = new Integer(1);
65    private static final Integer NOT_FOUND = new Integer(0);
66    private static Hashtable<String,Integer> lookedupTemplateNames = new Hashtable<String,Integer>();
67  
68    /** Disable instantiation. */
69    private ClassNameTempletLoader() {}
70  
71    /**
72     * @return the instance
73     */
74    public static ClassNameTempletLoader getInstance() {
75      if (it == null)
76        it = new ClassNameTempletLoader();
77      return it;
78    }
79    protected static String templetsPath(TemplateEngine templateEngine, 
80                                  MarkupLanguage markupLanguage) {
81      /*
82      // Fails to find templates in jars on Windows!!
83      return "org" + File.separatorChar +
84             "melati" + File.separatorChar +
85             "template" + File.separatorChar +
86              templateEngine.getName() + File.separatorChar +
87             "templets" + File.separatorChar +
88              markupLanguage.getName() + File.separatorChar;
89      */
90      return "org/melati/templets/" + 
91             markupLanguage.getName() + "/";
92      
93      }
94  
95    /**
96     * @return the path in the templets directory
97     */
98    protected static String templetsTempletPath(TemplateEngine templateEngine,
99                                 MarkupLanguage markupLanguage,
100                                String purpose, String name) {
101     if (purpose == null)
102       return 
103         templetsPath(templateEngine, markupLanguage) + 
104         name +
105         templateEngine.templateExtension();
106     return 
107         templetsPath(templateEngine, markupLanguage) + 
108         purpose + "/" + 
109         name +
110         templateEngine.templateExtension();
111   }
112 
113   protected static String classpathTempletPath(Class<?> clazz, String purpose, TemplateEngine templateEngine) {
114     if (purpose == null) {
115       return clazz.getName().replace('.', '/') + templateEngine.templateExtension();
116     } else {
117       return clazz.getPackage().getName().replace('.', '/')
118           + "/" + purpose
119           + "/" + clazz.getSimpleName()
120           + templateEngine.templateExtension();
121     }
122   }
123 
124   /**
125    * Get a templet by name, with optional purpose. 
126    * 
127    * @see TempletLoader#templet
128    */
129   public Template templet(TemplateEngine templateEngine,
130                           MarkupLanguage markupLanguage, String purpose,
131                           String name) throws NotFoundException {
132     return templateEngine.template(templetsTempletPath(templateEngine, markupLanguage,
133         purpose, name));
134   }
135 
136   /**
137    * Get a templet by its name, looking only in the templets directory.
138    * 
139    * {@inheritDoc}
140    * @see TempletLoader#templet(TemplateEngine, MarkupLanguage, String)
141    */
142   public Template templet(TemplateEngine templateEngine,
143                           MarkupLanguage markupLanguage, String name) 
144       throws NotFoundException {
145     return templet(templateEngine, markupLanguage, null, name);
146   }
147 
148   /**
149    * Get a templet based upon class name and optional purpose, 
150    * looking in the templets directory and also the classpath.
151    * 
152    * {@inheritDoc}
153    * @see TempletLoader#templet(TemplateEngine, MarkupLanguage, 
154    *                            String, Class)
155    */
156   public Template templet(TemplateEngine templateEngine,
157                           MarkupLanguage markupLanguage, String purpose,
158                           Class<?> clazz)
159       throws TemplateEngineException {
160     Class<?> lookupClass = clazz;
161     Template templet = null;
162     Template fromCache = null;
163     String originalCacheKey = cacheKey(templateEngine, markupLanguage, purpose, lookupClass);
164     String lookupCacheKey = originalCacheKey;
165     String lookupPurpose = purpose;
166     while (true) {
167       fromCache = (Template)templetForClassCache.get(lookupCacheKey);
168       if (fromCache != null) {
169         templet = fromCache;
170         break;
171       }
172       /*
173       // FIXME currently we only have specialised templets for fields
174       templet = getSpecialTemplate(lookupClass, lookupPurpose, markupLanguage, templateEngine);
175       if (templet != null) {
176         break;
177       }
178       */
179       // Try to find one in the templets directory
180       String templetPath = templetsTempletPath(templateEngine, markupLanguage,
181               lookupPurpose, lookupClass.getName());
182       templet = getTemplate(templateEngine, templetPath);
183       if (templet != null)
184         break;
185       // Try to find one on classpath
186       templetPath = classpathTempletPath(lookupClass, purpose, templateEngine);
187       templet = getTemplate(templateEngine, templetPath);
188       if (templet != null)
189         break;
190       
191       if (lookupPurpose != null)
192         lookupPurpose = null;
193       else { 
194         lookupClass = lookupClass.getSuperclass();
195         lookupPurpose = purpose;
196       }
197       lookupCacheKey = cacheKey(templateEngine, markupLanguage, lookupPurpose, lookupClass);
198     }
199     // We should have at last found Object template    
200     //if (templet == null)
201     //  throw new MelatiBugMelatiException("Cannot even find template for Object");
202     if (fromCache == null)
203       templetForClassCache.put(originalCacheKey, templet);
204     if (!lookupCacheKey.equals(originalCacheKey)) { 
205       if (templetForClassCache.get(lookupCacheKey) == null) 
206         templetForClassCache.put(lookupCacheKey, templet);
207     } 
208     return templet;
209   }
210 
211   private String cacheKey(TemplateEngine templateEngine, 
212       MarkupLanguage markupLanguage, 
213       String purpose, 
214       Class<?> lookupClass) {
215     return  purpose == null ? cacheKey(templateEngine, markupLanguage, lookupClass) 
216                             : lookupClass + "/" + 
217                                purpose + "/" + 
218                                markupLanguage + "/" + 
219                                templateEngine.getName();
220   }
221   
222   private String cacheKey(TemplateEngine templateEngine, 
223       MarkupLanguage markupLanguage, 
224       Class<?> lookupClass) {
225     return lookupClass + 
226            "/" + markupLanguage + 
227            "/" + templateEngine.getName();
228   }
229 
230   private Template getTemplate(TemplateEngine templateEngine, String templetPath)  { 
231     Template templet = null;
232     try {
233       Object triedAlready = lookedupTemplateNames.get(templetPath);
234       if (triedAlready != NOT_FOUND) {
235         templet = templateEngine.template(templetPath);
236         lookedupTemplateNames.put(templetPath, FOUND);
237       } 
238     } catch (NotFoundException e) {
239       lookedupTemplateNames.put(templetPath, NOT_FOUND);
240     }
241     return templet;
242   }
243 
244   /**
245    * Get a templet for a class.
246    * 
247    * {@inheritDoc}
248    * @see TempletLoader#templet(TemplateEngine, MarkupLanguage, Class)
249    */
250   public Template templet(TemplateEngine templateEngine,
251                           MarkupLanguage markupLanguage, Class<?> clazz) {
252     return templet(templateEngine, markupLanguage, null, clazz);
253   }
254 
255   /**
256    * Get a templet either from the classname concatenated with 
257    * FieldAttributes.RenederInfo or the class name.
258    * 
259    * {@inheritDoc}
260    * @see TempletLoader#templet(TemplateEngine,MarkupLanguage,FieldAttributes)
261    */
262   public Template templet(TemplateEngine templateEngine,
263                           MarkupLanguage markupLanguage,
264                           FieldAttributes<?> attributes) {
265     if (attributes.getRenderInfo() != null) {
266       String templetName = attributes.getType().getClass().getName() 
267           + "-"
268           + attributes.getRenderInfo();
269       try {
270         return templet(templateEngine, markupLanguage, 
271                 templetName);
272       } catch (NotFoundException e) {
273         throw new MelatiBugMelatiException(
274                 "Templet " + templetName  + " not found", e);
275       }
276     } else {
277         return templet(templateEngine, markupLanguage,
278                 attributes.getType().getClass());
279     }
280   }
281 }
282 
283 
284 
285 
286 
287 
288 
289 
290 
291