ClassNameTempletLoader.java
/*
* $Source$
* $Revision$
*
* Copyright (C) 2000 William Chesters
*
* Part of Melati (http://melati.org), a framework for the rapid
* development of clean, maintainable web applications.
*
* Melati is free software; Permission is granted to copy, distribute
* and/or modify this software under the terms either:
*
* a) the GNU General Public License as published by the Free Software
* Foundation; either version 2 of the License, or (at your option)
* any later version,
*
* or
*
* b) any version of the Melati Software License, as published
* at http://melati.org
*
* You should have received a copy of the GNU General Public License and
* the Melati Software License along with this program;
* if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA to obtain the
* GNU General Public License and visit http://melati.org to obtain the
* Melati Software License.
*
* Feel free to contact the Developers of Melati (http://melati.org),
* if you would like to work out a different arrangement than the options
* outlined here. It is our intention to allow Melati to be used by as
* wide an audience as possible.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* Contact details for copyright holder:
*
* William Chesters <williamc At paneris.org>
* http://paneris.org/~williamc
* Obrechtstraat 114, 2517VX Den Haag, The Netherlands
*/
package org.melati.template;
import org.melati.poem.FieldAttributes;
import org.melati.util.MelatiBugMelatiException;
import java.util.Hashtable;
/**
* Load a template to render an object based upon the object's class.
*/
public final class ClassNameTempletLoader implements TempletLoader {
/** The instance. */
private static ClassNameTempletLoader it = null;
// NOTE It is not expected that templates will be added at runtime.
private static Hashtable<String,Template> templetForClassCache = new Hashtable<String,Template>();
private static final Integer FOUND = new Integer(1);
private static final Integer NOT_FOUND = new Integer(0);
private static Hashtable<String,Integer> lookedupTemplateNames = new Hashtable<String,Integer>();
/** Disable instantiation. */
private ClassNameTempletLoader() {}
/**
* @return the instance
*/
public static ClassNameTempletLoader getInstance() {
if (it == null)
it = new ClassNameTempletLoader();
return it;
}
protected static String templetsPath(TemplateEngine templateEngine,
MarkupLanguage markupLanguage) {
/*
// Fails to find templates in jars on Windows!!
return "org" + File.separatorChar +
"melati" + File.separatorChar +
"template" + File.separatorChar +
templateEngine.getName() + File.separatorChar +
"templets" + File.separatorChar +
markupLanguage.getName() + File.separatorChar;
*/
return "org/melati/templets/" +
markupLanguage.getName() + "/";
}
/**
* @return the path in the templets directory
*/
protected static String templetsTempletPath(TemplateEngine templateEngine,
MarkupLanguage markupLanguage,
String purpose, String name) {
if (purpose == null)
return
templetsPath(templateEngine, markupLanguage) +
name +
templateEngine.templateExtension();
return
templetsPath(templateEngine, markupLanguage) +
purpose + "/" +
name +
templateEngine.templateExtension();
}
protected static String classpathTempletPath(Class<?> clazz, String purpose, TemplateEngine templateEngine) {
if (purpose == null) {
return clazz.getName().replace('.', '/') + templateEngine.templateExtension();
} else {
return clazz.getPackage().getName().replace('.', '/')
+ "/" + purpose
+ "/" + clazz.getSimpleName()
+ templateEngine.templateExtension();
}
}
/**
* Get a templet by name, with optional purpose.
*
* @see TempletLoader#templet
*/
public Template templet(TemplateEngine templateEngine,
MarkupLanguage markupLanguage, String purpose,
String name) throws NotFoundException {
return templateEngine.template(templetsTempletPath(templateEngine, markupLanguage,
purpose, name));
}
/**
* Get a templet by its name, looking only in the templets directory.
*
* {@inheritDoc}
* @see TempletLoader#templet(TemplateEngine, MarkupLanguage, String)
*/
public Template templet(TemplateEngine templateEngine,
MarkupLanguage markupLanguage, String name)
throws NotFoundException {
return templet(templateEngine, markupLanguage, null, name);
}
/**
* Get a templet based upon class name and optional purpose,
* looking in the templets directory and also the classpath.
*
* {@inheritDoc}
* @see TempletLoader#templet(TemplateEngine, MarkupLanguage,
* String, Class)
*/
public Template templet(TemplateEngine templateEngine,
MarkupLanguage markupLanguage, String purpose,
Class<?> clazz)
throws TemplateEngineException {
Class<?> lookupClass = clazz;
Template templet = null;
Template fromCache = null;
String originalCacheKey = cacheKey(templateEngine, markupLanguage, purpose, lookupClass);
String lookupCacheKey = originalCacheKey;
String lookupPurpose = purpose;
while (true) {
fromCache = (Template)templetForClassCache.get(lookupCacheKey);
if (fromCache != null) {
templet = fromCache;
break;
}
/*
// FIXME currently we only have specialised templets for fields
templet = getSpecialTemplate(lookupClass, lookupPurpose, markupLanguage, templateEngine);
if (templet != null) {
break;
}
*/
// Try to find one in the templets directory
String templetPath = templetsTempletPath(templateEngine, markupLanguage,
lookupPurpose, lookupClass.getName());
templet = getTemplate(templateEngine, templetPath);
if (templet != null)
break;
// Try to find one on classpath
templetPath = classpathTempletPath(lookupClass, purpose, templateEngine);
templet = getTemplate(templateEngine, templetPath);
if (templet != null)
break;
if (lookupPurpose != null)
lookupPurpose = null;
else {
lookupClass = lookupClass.getSuperclass();
lookupPurpose = purpose;
}
lookupCacheKey = cacheKey(templateEngine, markupLanguage, lookupPurpose, lookupClass);
}
// We should have at last found Object template
//if (templet == null)
// throw new MelatiBugMelatiException("Cannot even find template for Object");
if (fromCache == null)
templetForClassCache.put(originalCacheKey, templet);
if (!lookupCacheKey.equals(originalCacheKey)) {
if (templetForClassCache.get(lookupCacheKey) == null)
templetForClassCache.put(lookupCacheKey, templet);
}
return templet;
}
private String cacheKey(TemplateEngine templateEngine,
MarkupLanguage markupLanguage,
String purpose,
Class<?> lookupClass) {
return purpose == null ? cacheKey(templateEngine, markupLanguage, lookupClass)
: lookupClass + "/" +
purpose + "/" +
markupLanguage + "/" +
templateEngine.getName();
}
private String cacheKey(TemplateEngine templateEngine,
MarkupLanguage markupLanguage,
Class<?> lookupClass) {
return lookupClass +
"/" + markupLanguage +
"/" + templateEngine.getName();
}
private Template getTemplate(TemplateEngine templateEngine, String templetPath) {
Template templet = null;
try {
Object triedAlready = lookedupTemplateNames.get(templetPath);
if (triedAlready != NOT_FOUND) {
templet = templateEngine.template(templetPath);
lookedupTemplateNames.put(templetPath, FOUND);
}
} catch (NotFoundException e) {
lookedupTemplateNames.put(templetPath, NOT_FOUND);
}
return templet;
}
/**
* Get a templet for a class.
*
* {@inheritDoc}
* @see TempletLoader#templet(TemplateEngine, MarkupLanguage, Class)
*/
public Template templet(TemplateEngine templateEngine,
MarkupLanguage markupLanguage, Class<?> clazz) {
return templet(templateEngine, markupLanguage, null, clazz);
}
/**
* Get a templet either from the classname concatenated with
* FieldAttributes.RenederInfo or the class name.
*
* {@inheritDoc}
* @see TempletLoader#templet(TemplateEngine,MarkupLanguage,FieldAttributes)
*/
public Template templet(TemplateEngine templateEngine,
MarkupLanguage markupLanguage,
FieldAttributes<?> attributes) {
if (attributes.getRenderInfo() != null) {
String templetName = attributes.getType().getClass().getName()
+ "-"
+ attributes.getRenderInfo();
try {
return templet(templateEngine, markupLanguage,
templetName);
} catch (NotFoundException e) {
throw new MelatiBugMelatiException(
"Templet " + templetName + " not found", e);
}
} else {
return templet(templateEngine, markupLanguage,
attributes.getType().getClass());
}
}
}