/* $$ Clover has instrumented this file $$ */// Copyright 2004 The Apache Software Foundation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package org.apache.tapestry.services.impl;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.Collections;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.logging.Log;
import org.apache.hivemind.ApplicationRuntimeException;
import org.apache.hivemind.Resource;
import org.apache.tapestry.IAsset;
import org.apache.tapestry.IComponent;
import org.apache.tapestry.IRequestCycle;
import org.apache.tapestry.Tapestry;
import org.apache.tapestry.engine.DefaultComponentPropertySource;
import org.apache.tapestry.engine.IPropertySource;
import org.apache.tapestry.engine.ITemplateSourceDelegate;
import org.apache.tapestry.event.ResetEventListener;
import org.apache.tapestry.parse.ComponentTemplate;
import org.apache.tapestry.parse.ITemplateParserDelegate;
import org.apache.tapestry.parse.TemplateParseException;
import org.apache.tapestry.parse.ITemplateParser;
import org.apache.tapestry.parse.TemplateToken;
import org.apache.tapestry.resolver.ComponentSpecificationResolver;
import org.apache.tapestry.resource.ContextResource;
import org.apache.tapestry.services.TemplateSource;
import org.apache.tapestry.spec.IComponentSpecification;
import org.apache.tapestry.util.MultiKey;
/**
* Implementation of {@link org.apache.tapestry.services.TemplateSource}.
* Templates, once parsed,
* stay in memory until explicitly cleared.
*
*
* @author Howard Lewis Ship
*/
public class TemplateSourceImpl implements TemplateSource, ResetEventListener
{public static com.cortexeb.tools.clover.d __CLOVER_338_0 = com.cortexeb.tools.clover.aq.getRecorder(new char[] {67,58,92,119,111,114,107,115,112,97,99,101,92,106,97,107,97,114,116,97,45,116,97,112,101,115,116,114,121,92,102,114,97,109,101,119,111,114,107,92,116,97,114,103,101,116,92,99,108,111,118,101,114,45,100,98},1096998272901L);
private Log _log;
// The name of the component/application/etc property that will be used to
// determine the encoding to use when loading the template
public static final String TEMPLATE_ENCODING_PROPERTY_NAME =
"org.apache.tapestry.template-encoding";
// Cache of previously retrieved templates. Key is a multi-key of
// specification resource path and locale (local may be null), value
// is the ComponentTemplate.
private Map _cache = Collections.synchronizedMap(new HashMap());
// Previously read templates; key is the Resource, value
// is the ComponentTemplate.
private Map _templates = Collections.synchronizedMap(new HashMap());
private static final int BUFFER_SIZE = 2000;
private ITemplateParser _parser;
/** @since 2.2 */
private Resource _applicationRoot;
/** @since 3.0 */
private ITemplateSourceDelegate _delegate;
/** @since 3.1 */
private IPropertySource _applicationPropertySource;
/** @since 3.1 */
private ServletContext _context;
/** @since 3.1 */
private HttpServletRequest _request;
/** @since 3.1 */
private ComponentSpecificationResolver _componentSpecificationResolver;
public void initializeService()
{try { __CLOVER_338_0.M[1642]++;
__CLOVER_338_0.S[7563]++;String servletPath = _request.getServletPath();
__CLOVER_338_0.S[7564]++;_applicationRoot = new ContextResource(_context, servletPath);
} finally { }}
/**
* Clears the template cache. This is used during debugging.
*
*/
public void resetEventDidOccur()
{try { __CLOVER_338_0.M[1643]++;
__CLOVER_338_0.S[7565]++;_cache.clear();
__CLOVER_338_0.S[7566]++;_templates.clear();
} finally { }}
/**
* Reads the template for the component.
*
*
*/
public ComponentTemplate getTemplate(IRequestCycle cycle, IComponent component)
{try { __CLOVER_338_0.M[1644]++;
__CLOVER_338_0.S[7567]++;IComponentSpecification specification = component.getSpecification();
__CLOVER_338_0.S[7568]++;Resource resource = specification.getSpecificationLocation();
__CLOVER_338_0.S[7569]++;Locale locale = component.getPage().getLocale();
__CLOVER_338_0.S[7570]++;Object key = new MultiKey(new Object[] { resource, locale }, false);
__CLOVER_338_0.S[7571]++;ComponentTemplate result = searchCache(key);
__CLOVER_338_0.S[7572]++;if ((((result != null) && (++__CLOVER_338_0.CT[1304] != 0)) || (++__CLOVER_338_0.CF[1304] == 0))){
__CLOVER_338_0.S[7573]++;return result;}
__CLOVER_338_0.S[7574]++;result = findTemplate(cycle, resource, component, locale);
__CLOVER_338_0.S[7575]++;if ((((result == null) && (++__CLOVER_338_0.CT[1305] != 0)) || (++__CLOVER_338_0.CF[1305] == 0))){
{
__CLOVER_338_0.S[7576]++;result = _delegate.findTemplate(cycle, component, locale);
__CLOVER_338_0.S[7577]++;if ((((result != null) && (++__CLOVER_338_0.CT[1306] != 0)) || (++__CLOVER_338_0.CF[1306] == 0))){
__CLOVER_338_0.S[7578]++;return result;}
__CLOVER_338_0.S[7579]++;String message =
(((component.getSpecification().isPageSpecification()
) && (++__CLOVER_338_0.CT[1307] != 0)) || (++__CLOVER_338_0.CF[1307] == 0))? ImplMessages.noTemplateForPage(component.getExtendedId(), locale)
: ImplMessages.noTemplateForComponent(component.getExtendedId(), locale);
__CLOVER_338_0.S[7580]++;throw new ApplicationRuntimeException(
message,
component,
component.getLocation(),
null);
}}
__CLOVER_338_0.S[7581]++;saveToCache(key, result);
__CLOVER_338_0.S[7582]++;return result;
} finally { }}
private ComponentTemplate searchCache(Object key)
{try { __CLOVER_338_0.M[1645]++;
__CLOVER_338_0.S[7583]++;return (ComponentTemplate) _cache.get(key);
} finally { }}
private void saveToCache(Object key, ComponentTemplate template)
{try { __CLOVER_338_0.M[1646]++;
__CLOVER_338_0.S[7584]++;_cache.put(key, template);
} finally { }}
/**
* Finds the template for the given component, using the following rules:
* <ul>
* <li>If the component has a $template asset, use that
* <li>Look for a template in the same folder as the component
* <li>If a page in the application namespace, search in the application root
* <li>Fail!
* </ul>
*
* @return the template, or null if not found
*
*/
private ComponentTemplate findTemplate(
IRequestCycle cycle,
Resource resource,
IComponent component,
Locale locale)
{try { __CLOVER_338_0.M[1647]++;
__CLOVER_338_0.S[7585]++;IAsset templateAsset = component.getAsset(TEMPLATE_ASSET_NAME);
__CLOVER_338_0.S[7586]++;if ((((templateAsset != null) && (++__CLOVER_338_0.CT[1308] != 0)) || (++__CLOVER_338_0.CF[1308] == 0))){
__CLOVER_338_0.S[7587]++;return readTemplateFromAsset(cycle, component, templateAsset);}
__CLOVER_338_0.S[7588]++;String name = resource.getName();
__CLOVER_338_0.S[7589]++;int dotx = name.lastIndexOf('.');
__CLOVER_338_0.S[7590]++;String templateBaseName = name.substring(0, dotx + 1) + getTemplateExtension(component);
__CLOVER_338_0.S[7591]++;ComponentTemplate result =
findStandardTemplate(cycle, resource, component, templateBaseName, locale);
__CLOVER_338_0.S[7592]++;if ((((result == null
&& component.getSpecification().isPageSpecification()
&& component.getNamespace().isApplicationNamespace()) && (++__CLOVER_338_0.CT[1309] != 0)) || (++__CLOVER_338_0.CF[1309] == 0))){
__CLOVER_338_0.S[7593]++;result = findPageTemplateInApplicationRoot(cycle, component, templateBaseName, locale);}
__CLOVER_338_0.S[7594]++;return result;
} finally { }}
private ComponentTemplate findPageTemplateInApplicationRoot(
IRequestCycle cycle,
IComponent component,
String templateBaseName,
Locale locale)
{try { __CLOVER_338_0.M[1648]++;
__CLOVER_338_0.S[7595]++;if ((((_log.isDebugEnabled()) && (++__CLOVER_338_0.CT[1310] != 0)) || (++__CLOVER_338_0.CF[1310] == 0))){
__CLOVER_338_0.S[7596]++;_log.debug("Checking for " + templateBaseName + " in application root");}
__CLOVER_338_0.S[7597]++;Resource baseLocation = _applicationRoot.getRelativeResource(templateBaseName);
__CLOVER_338_0.S[7598]++;Resource localizedLocation = baseLocation.getLocalization(locale);
__CLOVER_338_0.S[7599]++;if ((((localizedLocation == null) && (++__CLOVER_338_0.CT[1311] != 0)) || (++__CLOVER_338_0.CF[1311] == 0))){
__CLOVER_338_0.S[7600]++;return null;}
__CLOVER_338_0.S[7601]++;return getOrParseTemplate(cycle, localizedLocation, component);
} finally { }}
/**
* Reads an asset to get the template.
*
*/
private ComponentTemplate readTemplateFromAsset(
IRequestCycle cycle,
IComponent component,
IAsset asset)
{try { __CLOVER_338_0.M[1649]++;
__CLOVER_338_0.S[7602]++;InputStream stream = asset.getResourceAsStream(cycle);
__CLOVER_338_0.S[7603]++;char[] templateData = null;
__CLOVER_338_0.S[7604]++;try
{
__CLOVER_338_0.S[7605]++;String encoding = getTemplateEncoding(component, null);
__CLOVER_338_0.S[7606]++;templateData = readTemplateStream(stream, encoding);
__CLOVER_338_0.S[7607]++;stream.close();
}
catch (IOException ex)
{
__CLOVER_338_0.S[7608]++;throw new ApplicationRuntimeException(ImplMessages.unableToReadTemplate(asset), ex);
}
__CLOVER_338_0.S[7609]++;Resource resourceLocation = asset.getResourceLocation();
__CLOVER_338_0.S[7610]++;return constructTemplateInstance(cycle, templateData, resourceLocation, component);
} finally { }}
/**
* Search for the template corresponding to the resource and the locale.
* This may be in the template map already, or may involve reading and
* parsing the template.
*
* @return the template, or null if not found.
*
*/
private ComponentTemplate findStandardTemplate(
IRequestCycle cycle,
Resource resource,
IComponent component,
String templateBaseName,
Locale locale)
{try { __CLOVER_338_0.M[1650]++;
__CLOVER_338_0.S[7611]++;if ((((_log.isDebugEnabled()) && (++__CLOVER_338_0.CT[1312] != 0)) || (++__CLOVER_338_0.CF[1312] == 0))){
__CLOVER_338_0.S[7612]++;_log.debug(
"Searching for localized version of template for "
+ resource
+ " in locale "
+ locale.getDisplayName());}
__CLOVER_338_0.S[7613]++;Resource baseTemplateLocation = resource.getRelativeResource(templateBaseName);
__CLOVER_338_0.S[7614]++;Resource localizedTemplateLocation = baseTemplateLocation.getLocalization(locale);
__CLOVER_338_0.S[7615]++;if ((((localizedTemplateLocation == null) && (++__CLOVER_338_0.CT[1313] != 0)) || (++__CLOVER_338_0.CF[1313] == 0))){
__CLOVER_338_0.S[7616]++;return null;}
__CLOVER_338_0.S[7617]++;return getOrParseTemplate(cycle, localizedTemplateLocation, component);
} finally { }}
/**
* Returns a previously parsed template at the specified location (which must already
* be localized). If not already in the template Map, then the
* location is parsed and stored into the templates Map, then returned.
*
*/
private ComponentTemplate getOrParseTemplate(
IRequestCycle cycle,
Resource resource,
IComponent component)
{try { __CLOVER_338_0.M[1651]++;
__CLOVER_338_0.S[7618]++;ComponentTemplate result = (ComponentTemplate) _templates.get(resource);
__CLOVER_338_0.S[7619]++;if ((((result != null) && (++__CLOVER_338_0.CT[1314] != 0)) || (++__CLOVER_338_0.CF[1314] == 0))){
__CLOVER_338_0.S[7620]++;return result;}
// Ok, see if it exists.
__CLOVER_338_0.S[7621]++;result = parseTemplate(cycle, resource, component);
__CLOVER_338_0.S[7622]++;if ((((result != null) && (++__CLOVER_338_0.CT[1315] != 0)) || (++__CLOVER_338_0.CF[1315] == 0))){
__CLOVER_338_0.S[7623]++;_templates.put(resource, result);}
__CLOVER_338_0.S[7624]++;return result;
} finally { }}
/**
* Reads the template for the given resource; returns null if the
* resource doesn't exist. Note that this method is only invoked
* from a synchronized block, so there shouldn't be threading
* issues here.
*
*/
private ComponentTemplate parseTemplate(
IRequestCycle cycle,
Resource resource,
IComponent component)
{try { __CLOVER_338_0.M[1652]++;
__CLOVER_338_0.S[7625]++;String encoding = getTemplateEncoding(component, resource.getLocale());
__CLOVER_338_0.S[7626]++;char[] templateData = readTemplate(resource, encoding);
__CLOVER_338_0.S[7627]++;if ((((templateData == null) && (++__CLOVER_338_0.CT[1316] != 0)) || (++__CLOVER_338_0.CF[1316] == 0))){
__CLOVER_338_0.S[7628]++;return null;}
__CLOVER_338_0.S[7629]++;return constructTemplateInstance(cycle, templateData, resource, component);
} finally { }}
/**
* This method is currently synchronized, because
* {@link TemplateParser} is not threadsafe. Another good candidate
* for a pooling mechanism, especially because parsing a template
* may take a while.
*
*/
private synchronized ComponentTemplate constructTemplateInstance(
IRequestCycle cycle,
char[] templateData,
Resource resource,
IComponent component)
{try { __CLOVER_338_0.M[1653]++;
__CLOVER_338_0.S[7630]++;ITemplateParserDelegate delegate =
new DefaultParserDelegate(component, cycle, _componentSpecificationResolver);
__CLOVER_338_0.S[7631]++;TemplateToken[] tokens;
__CLOVER_338_0.S[7632]++;try
{
__CLOVER_338_0.S[7633]++;tokens = _parser.parse(templateData, delegate, resource);
}
catch (TemplateParseException ex)
{
__CLOVER_338_0.S[7634]++;throw new ApplicationRuntimeException(ImplMessages.unableToParseTemplate(resource), ex);
}
__CLOVER_338_0.S[7635]++;if ((((_log.isDebugEnabled()) && (++__CLOVER_338_0.CT[1317] != 0)) || (++__CLOVER_338_0.CF[1317] == 0))){
__CLOVER_338_0.S[7636]++;_log.debug("Parsed " + tokens.length + " tokens from template");}
__CLOVER_338_0.S[7637]++;return new ComponentTemplate(templateData, tokens);
} finally { }}
/**
* Reads the template, given the complete path to the
* resource. Returns null if the resource doesn't exist.
*
*/
private char[] readTemplate(Resource resource, String encoding)
{try { __CLOVER_338_0.M[1654]++;
__CLOVER_338_0.S[7638]++;if ((((_log.isDebugEnabled()) && (++__CLOVER_338_0.CT[1318] != 0)) || (++__CLOVER_338_0.CF[1318] == 0))){
__CLOVER_338_0.S[7639]++;_log.debug("Reading template " + resource);}
__CLOVER_338_0.S[7640]++;URL url = resource.getResourceURL();
__CLOVER_338_0.S[7641]++;if ((((url == null) && (++__CLOVER_338_0.CT[1319] != 0)) || (++__CLOVER_338_0.CF[1319] == 0))){
{
__CLOVER_338_0.S[7642]++;if ((((_log.isDebugEnabled()) && (++__CLOVER_338_0.CT[1320] != 0)) || (++__CLOVER_338_0.CF[1320] == 0))){
__CLOVER_338_0.S[7643]++;_log.debug("Template does not exist.");}
__CLOVER_338_0.S[7644]++;return null;
}}
__CLOVER_338_0.S[7645]++;if ((((_log.isDebugEnabled()) && (++__CLOVER_338_0.CT[1321] != 0)) || (++__CLOVER_338_0.CF[1321] == 0))){
__CLOVER_338_0.S[7646]++;_log.debug("Reading template from URL " + url);}
__CLOVER_338_0.S[7647]++;InputStream stream = null;
__CLOVER_338_0.S[7648]++;try
{
__CLOVER_338_0.S[7649]++;stream = url.openStream();
__CLOVER_338_0.S[7650]++;return readTemplateStream(stream, encoding);
}
catch (IOException ex)
{
__CLOVER_338_0.S[7651]++;throw new ApplicationRuntimeException(ImplMessages.unableToReadTemplate(resource), ex);
}
finally
{
__CLOVER_338_0.S[7652]++;Tapestry.close(stream);
}
} finally { }}
/**
* Reads a Stream into memory as an array of characters.
*
*/
private char[] readTemplateStream(InputStream stream, String encoding) throws IOException
{try { __CLOVER_338_0.M[1655]++;
__CLOVER_338_0.S[7653]++;char[] charBuffer = new char[BUFFER_SIZE];
__CLOVER_338_0.S[7654]++;StringBuffer buffer = new StringBuffer();
__CLOVER_338_0.S[7655]++;InputStreamReader reader;
__CLOVER_338_0.S[7656]++;if ((((encoding != null) && (++__CLOVER_338_0.CT[1322] != 0)) || (++__CLOVER_338_0.CF[1322] == 0))){
__CLOVER_338_0.S[7657]++;reader = new InputStreamReader(new BufferedInputStream(stream), encoding);}
else{
__CLOVER_338_0.S[7658]++;reader = new InputStreamReader(new BufferedInputStream(stream));}
__CLOVER_338_0.S[7659]++;try
{
__CLOVER_338_0.S[7660]++;while (true){
{
__CLOVER_338_0.S[7661]++;int charsRead = reader.read(charBuffer, 0, BUFFER_SIZE);
__CLOVER_338_0.S[7662]++;if ((((charsRead <= 0) && (++__CLOVER_338_0.CT[1323] != 0)) || (++__CLOVER_338_0.CF[1323] == 0))){
__CLOVER_338_0.S[7663]++;break;}
__CLOVER_338_0.S[7664]++;buffer.append(charBuffer, 0, charsRead);
}}
}
finally
{
__CLOVER_338_0.S[7665]++;reader.close();
}
// OK, now reuse the charBuffer variable to
// produce the final result.
__CLOVER_338_0.S[7666]++;int length = buffer.length();
__CLOVER_338_0.S[7667]++;charBuffer = new char[length];
// Copy the character out of the StringBuffer and into the
// array.
__CLOVER_338_0.S[7668]++;buffer.getChars(0, length, charBuffer, 0);
__CLOVER_338_0.S[7669]++;return charBuffer;
} finally { }}
/**
* Checks for the {@link Tapestry#TEMPLATE_EXTENSION_PROPERTY} in the component's
* specification, then in the component's namespace's specification. Returns
* {@link Tapestry#DEFAULT_TEMPLATE_EXTENSION} if not otherwise overriden.
*
*/
private String getTemplateExtension(IComponent component)
{try { __CLOVER_338_0.M[1656]++;
__CLOVER_338_0.S[7670]++;String extension =
component.getSpecification().getProperty(Tapestry.TEMPLATE_EXTENSION_PROPERTY);
__CLOVER_338_0.S[7671]++;if ((((extension != null) && (++__CLOVER_338_0.CT[1324] != 0)) || (++__CLOVER_338_0.CF[1324] == 0))){
__CLOVER_338_0.S[7672]++;return extension;}
__CLOVER_338_0.S[7673]++;extension =
component.getNamespace().getSpecification().getProperty(
Tapestry.TEMPLATE_EXTENSION_PROPERTY);
__CLOVER_338_0.S[7674]++;if ((((extension != null) && (++__CLOVER_338_0.CT[1325] != 0)) || (++__CLOVER_338_0.CF[1325] == 0))){
__CLOVER_338_0.S[7675]++;return extension;}
__CLOVER_338_0.S[7676]++;return Tapestry.DEFAULT_TEMPLATE_EXTENSION;
} finally { }}
private String getTemplateEncoding(IComponent component, Locale locale)
{try { __CLOVER_338_0.M[1657]++;
__CLOVER_338_0.S[7677]++;IPropertySource source =
new DefaultComponentPropertySource(component, _applicationPropertySource, locale);
__CLOVER_338_0.S[7678]++;return source.getPropertyValue(TEMPLATE_ENCODING_PROPERTY_NAME);
} finally { }}
/** @since 3.1 */
public void setParser(ITemplateParser parser)
{try { __CLOVER_338_0.M[1658]++;
__CLOVER_338_0.S[7679]++;_parser = parser;
} finally { }}
/** @since 3.1 */
public void setApplicationPropertySource(IPropertySource source)
{try { __CLOVER_338_0.M[1659]++;
__CLOVER_338_0.S[7680]++;_applicationPropertySource = source;
} finally { }}
/** @since 3.1 */
public void setLog(Log log)
{try { __CLOVER_338_0.M[1660]++;
__CLOVER_338_0.S[7681]++;_log = log;
} finally { }}
/** @since 3.1 */
public void setDelegate(ITemplateSourceDelegate delegate)
{try { __CLOVER_338_0.M[1661]++;
__CLOVER_338_0.S[7682]++;_delegate = delegate;
} finally { }}
/** @since 3.1 */
public void setContext(ServletContext context)
{try { __CLOVER_338_0.M[1662]++;
__CLOVER_338_0.S[7683]++;_context = context;
} finally { }}
/** @since 3.1 */
public void setRequest(HttpServletRequest request)
{try { __CLOVER_338_0.M[1663]++;
__CLOVER_338_0.S[7684]++;_request = request;
} finally { }}
/** @since 3.1 */
public void setComponentSpecificationResolver(ComponentSpecificationResolver resolver)
{try { __CLOVER_338_0.M[1664]++;
__CLOVER_338_0.S[7685]++;_componentSpecificationResolver = resolver;
} finally { }}
}