/**
*
*/
package org.ajax4jsf.webapp;
import java.io.IOException;
import java.io.InputStream;
import java.util.EmptyStackException;
import java.util.Properties;
import java.util.regex.PatternSyntaxException;
import javax.servlet.FilterConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import org.ajax4jsf.Messages;
import org.ajax4jsf.io.parser.FastHtmlParser;
import org.ajax4jsf.resource.util.URLToStreamHelper;
import org.ajax4jsf.webapp.nekko.NekkoParser;
import org.ajax4jsf.webapp.tidy.TidyParser;
import org.apache.commons.collections.ArrayStack;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* @author asmirnov
*
*/
public class ConfigurableXMLFilter extends BaseXMLFilter {
public static final String NONE = "NONE";
public static final String NEKO = "NEKO";
public static final String TIDY = "TIDY";
private static final Log log = LogFactory
.getLog(ConfigurableXMLFilter.class);
private static final String PARSERS_LIST_PARAMETER = "org.ajax4jsf.xmlparser.ORDER";
private static final String VIEW_ID_PATTERN_PARAMETER = "org.ajax4jsf.xmlparser.";
/**
* map of the parsers names and viewId patterns for all parser type.
*/
private ParserConfig parsers = new TidyParserConfig();
// private ParserConfig passParserConfig = new PassParserConfig();
public void init(FilterConfig config) throws ServletException {
super.init(config);
ServletContext servletContext = config.getServletContext();
String parsersParameter = servletContext
.getInitParameter(PARSERS_LIST_PARAMETER);
if (null != parsersParameter) {
configureParsers(servletContext, parsersParameter);
}
}
/**
* @param servletContext
* @param parsersParameter
* @throws ServletException
*/
public void configureParsers(ServletContext servletContext,
String parsersParameter) throws ServletException {
String[] parsersNames = parsersParameter.split("\\s*,\\s*");
for (int i = parsersNames.length - 1; i >= 0; i--) {
String parserName = parsersNames[i];
ParserConfig parserConfig;
if (TIDY.equals(parserName)) {
parserConfig = new TidyParserConfig();
} else if (NEKO.equals(parserName)) {
parserConfig = new NekoParserConfig();
} else if (NONE.equals(parserName)) {
parserConfig = new PassParserConfig();
} else {
throw new ServletException(
"Unknown XML parser type in config parameter "
+ parserName);
}
parserConfig.setNext(parsers);
if (null != servletContext) {
try {
String parserViewPattern = servletContext
.getInitParameter(VIEW_ID_PATTERN_PARAMETER
+ parserName);
parserConfig.setPatterns(parserViewPattern);
} catch (PatternSyntaxException e) {
throw new ServletException("Invalid pattern for a parser "
+ parserName + " :" + e.getMessage());
}
}
parsers = parserConfig;
}
}
/*
* (non-Javadoc)
*
* @see org.ajax4jsf.webapp.BaseXMLFilter#getParser(java.lang.String,
* boolean, java.lang.String)
*/
protected HtmlParser getParser(String mimetype, boolean isAjax,
String viewId) {
HtmlParser parser = null;
if (isAjax || mimetype.startsWith(TEXT_HTML) || mimetype.startsWith(APPLICATION_XHTML_XML)) {
if (isAjax || isForcexml()) {
parser = parsers.getParser(viewId, mimetype);
} else {
parser = new FastHtmlParser();
}
}
return parser;
}
/*
* (non-Javadoc)
*
* @see org.ajax4jsf.webapp.BaseXMLFilter#reuseParser(org.ajax4jsf.webapp.HtmlParser)
*/
protected void reuseParser(HtmlParser parser) {
parsers.reuseParser(parser);
}
private static final int STACK_SIZE = 100;
private ArrayStack _xhtmlParsersPool = new ArrayStack(STACK_SIZE);
private class NekoParserConfig extends ParserConfig {
protected HtmlParser createParser(String mime) {
NekkoParser parser;
try {
synchronized (_xhtmlParsersPool) {
parser = (NekkoParser) _xhtmlParsersPool.pop();
}
} catch (EmptyStackException e) {
parser = new NekkoParser();
parser.setPublicId(getPublicid());
parser.setSystemid(getSystemid());
parser.setNamespace(getNamespace());
// If tidy not handle all requests, disable reorganising
// of html
// parser.setMoveElements(isForcexml());
parser.init();
}
parser.setMime(mime);
// TODO - set header scripts/styles filter.
return parser;
}
boolean storeParser(HtmlParser parser) {
if (null != parser && parser instanceof NekkoParser) {
synchronized (_xhtmlParsersPool) {
if (_xhtmlParsersPool.size() < STACK_SIZE) {
((NekkoParser) parser).reset();
_xhtmlParsersPool.push(parser);
}
}
return true;
}
return false;
}
}
private class TidyParserConfig extends ParserConfig {
protected HtmlParser createParser(String mime) {
// TODO Auto-generated method stub
TidyParser tidyParser = new TidyParser(getTidyProperties());
tidyParser.setMoveElements(isForcexml());
tidyParser.setMime(mime);
return tidyParser;
}
}
private class PassParserConfig extends ParserConfig {
protected HtmlParser createParser(String mime) {
return new FastHtmlParser();
}
}
private Properties _tidyProperties;
private Properties getTidyProperties() {
if (null == _tidyProperties) {
_tidyProperties = new Properties();
InputStream defaultprops = null;
InputStream props = null;
try {
defaultprops = URLToStreamHelper.urlToStreamSafe(TidyParser.class
.getResource("tidy.properties"));
if (null != defaultprops) {
_tidyProperties.load(defaultprops);
if (log.isDebugEnabled()) {
log.debug("default tidy parser properties loaded");
}
} else if (log.isDebugEnabled()) {
log.debug("No default tidy parser properties found");
}
// Second part - user-defined properties.
props = URLToStreamHelper.urlToStreamSafe(Thread.currentThread().getContextClassLoader()
.getResource("tidy.properties"));
if (null != props) {
_tidyProperties.load(props);
if (log.isDebugEnabled()) {
log.debug("application-specific tidy parser properties loaded");
}
}
} catch (IOException e) {
// TODO Auto-generated catch block
log.warn(Messages
.getMessage(Messages.READING_TIDY_PROPERTIES_ERROR), e);
} finally {
if (null != props) {
try {
props.close();
} catch (IOException e) {
// can be ignored
}
}
if (null != defaultprops) {
try {
defaultprops.close();
} catch (IOException e) {
// can be ignored
}
}
}
}
return _tidyProperties;
}
public ParserConfig getParsers() {
return parsers;
}
}