/* Copyright (2006-2012) Schibsted ASA
* This file is part of Possom.
*
* Possom is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Possom 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Possom. If not, see <http://www.gnu.org/licenses/>.
*/
package no.sesat.search.mode;
import no.sesat.Interpreter;
import no.sesat.search.run.transform.RunTransformerConfig;
import no.sesat.search.site.config.AbstractConfigFactory;
import no.sesat.commons.ioc.ContextWrapper;
import no.sesat.search.mode.config.CommandConfig;
import no.sesat.search.mode.config.SearchConfiguration;
import no.sesat.search.query.transform.QueryTransformerConfig;
import no.sesat.search.result.handler.ResultHandlerConfig;
import no.sesat.search.site.Site;
import no.sesat.search.site.SiteKeyedFactory;
import no.sesat.search.site.config.Spi;
import no.sesat.search.site.config.AbstractDocumentFactory;
import no.sesat.search.site.config.ResourceContext;
import no.sesat.search.site.config.DocumentLoader;
import org.apache.log4j.Logger;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import no.sesat.search.mode.config.BaseSearchConfiguration;
import no.sesat.search.mode.config.querybuilder.QueryBuilderConfig;
import no.sesat.search.run.handler.RunHandlerConfig;
/**<p>
* Controlling class around the deserialisation of modes.xml into all the SearchModes.
* This in turn deserialises all SearchConfigurations, QueryTransformers, QueryBuilders, and ResultHandlers.</p><p>
*
* Deserialisation of xml elements to configuration classes works through each implementation of AbstractDocumentFactory
* These being: <br/>
* mode --> SearchModeFactory<br/>
* *-commands --> SearchModeFactory.SearchCommandFactory</br>
* query-builder --> QueryBuilderFactory<br/>
* query-transformers --> QueryTransformerFactory<br/>
* result-handlers --> ResultHandlerFactory<br/>
* run-handlers --> RunHandlerFactory<br/></p><p>
*
* The lookup of configuration classname from the xml element name generally follows the pattern provided by the
* @see #xmlToBeanName(java.lang.String) method.
* But each AbstractDocumentFactory.findClass(..) provides the final implementations with details of which package to
* look in and any static Prefix and Suffixes to the classname.</p><p>
* For search configuration classes the implementation is such:<br/>
* 1) to be found in the no.sesat.search.mode.config package<br/>
* 2) the first character always becomes uppercase (to suit java standards for naming classes)<br/>
* 3) the suffix "Config" is added.<br/>
* For example <solr-command id="abc" .../> becomes "SolrCommandConfig".
* </p><p>
* The lookup from configuration class to control class happens via the value of the Controller annotation.
* (There's discussion in moving this annotation to a normal string attribute in the configuration bean).
* </p>
* @version <tt>$Id$</tt>
*/
public final class SearchModeFactory extends AbstractDocumentFactory implements SiteKeyedFactory {
/**
* The context any SearchModeFactory must work against. *
*/
public interface Context extends ResourceContext, AbstractConfigFactory.Context {}
// Constants -----------------------------------------------------
private static final Map<Site, SearchModeFactory> INSTANCES = new HashMap<Site, SearchModeFactory>();
private static final ReentrantReadWriteLock INSTANCES_LOCK = new ReentrantReadWriteLock();
private static final SearchCommandFactory SEARCH_CONFIGURATION_FACTORY = new SearchCommandFactory();
private static final QueryTransformerFactory QUERY_TRANSFORMER_FACTORY = new QueryTransformerFactory();
private static final ResultHandlerFactory RESULT_HANDLER_FACTORY = new ResultHandlerFactory();
private static final RunHandlerConfigFactory RUN_HANDLER_FACTORY = new RunHandlerConfigFactory();
private static final RunTransformerConfigFactory RUN_TRANSFORMER_FACTORY = new RunTransformerConfigFactory();
private static final QueryBuilderFactory QUERY_BUILDER_FACTORY = new QueryBuilderFactory();
/**
* The name of the modes configuration file.
*/
public static final String MODES_XMLFILE = "modes.xml";
private static final String RUN_HANDLERS = "run-handlers";
private static final String RUN_TRANSFORMERS = "run-transformers";
private static final Map<SearchMode, Map<String, SearchConfiguration>> COMMANDS
= new HashMap<SearchMode, Map<String, SearchConfiguration>>();
private static final ReentrantReadWriteLock COMMANDS_LOCK = new ReentrantReadWriteLock();
private static final Logger LOG = Logger.getLogger(SearchModeFactory.class);
private static final String ERR_DOC_BUILDER_CREATION
= "Failed to DocumentBuilderFactory.newInstance().newDocumentBuilder()";
private static final String INFO_PARSING_MODE = "Parsing mode ";
private static final String INFO_PARSING_CONFIGURATION = " Parsing configuration ";
private static final String ERR_PARENT_COMMAND_NOT_FOUND = "Parent command {0} not found for {1} in mode {2}";
// Attributes ----------------------------------------------------
private final Map<String, SearchMode> modes = new HashMap<String, SearchMode>();
private final ReentrantReadWriteLock modesLock = new ReentrantReadWriteLock();
private final DocumentLoader loader;
private final Context context;
// Static --------------------------------------------------------
static {
Interpreter.addFunction("commands", new Interpreter.Function() {
public String execute(Interpreter.Context ctx) {
String res = "";
COMMANDS_LOCK.readLock().lock();
try {
for (SearchMode c : COMMANDS.keySet()) {
res += "Mode: " + c + "\n";
for (String s : COMMANDS.get(c).keySet()) {
res += " " + COMMANDS.get(c).get(s).toString() + "\n";
}
res += "\n";
}
}
finally {
COMMANDS_LOCK.readLock().unlock();
}
return res;
}
@Override
public String describe() {
return "Print out the SearchModes in COMMANDS.";
}
});
}
/**
* Returns the factory for the site in the context.
*
* @param cxt The context.
* @return the factory for the context.
*/
public static SearchModeFactory instanceOf(final Context cxt) {
final Site site = cxt.getSite();
SearchModeFactory instance;
try {
INSTANCES_LOCK.readLock().lock();
instance = INSTANCES.get(site);
} finally {
INSTANCES_LOCK.readLock().unlock();
}
if (instance == null) {
try {
instance = new SearchModeFactory(cxt);
} catch (ParserConfigurationException ex) {
LOG.error(ERR_DOC_BUILDER_CREATION, ex);
}
}
return instance;
}
/**
* Removes mode factory associated with site causing configuration to be reloaded.
*
* @param site The site to remove.
* @return true if the site existed.
*/
public boolean remove(final Site site) {
try {
INSTANCES_LOCK.writeLock().lock();
return null != INSTANCES.remove(site);
} finally {
INSTANCES_LOCK.writeLock().unlock();
}
}
// Constructors --------------------------------------------------
/**
* Creates a new instance of ModeFactoryImpl
*/
private SearchModeFactory(final Context cxt)
throws ParserConfigurationException {
LOG.trace("ModeFactory(cxt)");
try {
INSTANCES_LOCK.writeLock().lock();
context = cxt;
// configuration files
final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setValidating(false);
final DocumentBuilder builder = factory.newDocumentBuilder();
loader = context.newDocumentLoader(cxt, MODES_XMLFILE, builder);
// update the store of factories
INSTANCES.put(context.getSite(), this);
// start initialisation
init();
} finally {
INSTANCES_LOCK.writeLock().unlock();
}
}
// Public --------------------------------------------------------
/**
* Returns the mode with given identifier.
*
* @param id The mode identifier.
* @return The mode.
*/
public SearchMode getMode(final String id) {
LOG.trace("getMode(" + id + ")");
SearchMode mode = getModeImpl(id);
if (mode == null && id != null && id.length() > 0 && context.getSite().getParent() != null) {
// not found in this site's modes.xml. look in parent's site.
final SearchModeFactory factory = instanceOf(ContextWrapper.wrap(
Context.class,
context.getSite().getParent().getSiteContext(),
context
));
mode = factory.getMode(id);
}
return mode;
}
// Package protected ---------------------------------------------
/**
* Return the modes created by the factory.
*
* @return a map of the modes. Mode id is the key.
*/
Map<String, SearchMode> getModes() {
return Collections.unmodifiableMap(modes);
}
// Protected -----------------------------------------------------
// Private -------------------------------------------------------
private void init() throws ParserConfigurationException {
loader.abut();
LOG.debug("Parsing " + MODES_XMLFILE + " started");
final Document doc = loader.getDocument();
final Element root = doc.getDocumentElement();
if (null != root) {
// loop through modes.
final NodeList modeList = root.getElementsByTagName("mode");
for (int i = 0; i < modeList.getLength(); ++i) {
final Element modeE = (Element) modeList.item(i);
final String id = modeE.getAttribute("id");
LOG.info(INFO_PARSING_MODE + modeE.getLocalName() + " " + id);
final SearchMode inherit = getMode(modeE.getAttribute("inherit"));
final SearchMode mode = new SearchMode(inherit);
mode.setId(id);
mode.setExecutor(parseExecutor(
modeE.getAttribute("executor"),
inherit != null ? inherit.getExecutor() : SearchMode.SearchCommandExecutorConfig.SEQUENTIAL));
fillBeanProperty(mode, inherit, "evaluation", ParseType.Boolean, modeE, "true");
fillBeanProperty(mode, inherit, "analysis", ParseType.Boolean, modeE, "false");
fillBeanProperty(mode, inherit, "autoBroadening", ParseType.Boolean, modeE, "true");
mode.setRunTransformers(getRunTransformers(modeE, inherit));
mode.setRunHandlers(getRunHandlers(modeE, inherit));
// setup new commands list for this mode
final Map<String, SearchConfiguration> modesCommands = new HashMap<String, SearchConfiguration>();
try {
COMMANDS_LOCK.writeLock().lock();
COMMANDS.put(mode, modesCommands);
} finally {
COMMANDS_LOCK.writeLock().unlock();
}
final NodeList childNodes = modeE.getChildNodes();
final Collection<SearchConfiguration> searchConfigurations = new ArrayList<SearchConfiguration>();
for (int j = 0; j < childNodes.getLength(); ++j) {
final Node childNode = childNodes.item(j);
if (!(childNode instanceof Element)) {
continue;
}
final Element childElement = (Element) childNode;
if(SEARCH_CONFIGURATION_FACTORY.supported(childElement.getTagName(), context)){
// commands
final SearchConfiguration sc
= SEARCH_CONFIGURATION_FACTORY.parseSearchConfiguration(context, childElement, mode);
modesCommands.put(sc.getId(), sc);
searchConfigurations.add(sc);
// }else if("navigation".equals(childElement.getTagName())){
// // navigation
// assert null == mode.getNavigationConfiguration() : "NavigationConfiguration already set!";
//
// final NodeList navigationElements = childElement.getElementsByTagName("navigation");
//
// mode.setNavigationConfiguration(parseNavigation(id, navigationElements));
}
}
mode.setSearchConfigurations(searchConfigurations);
// add mode
try {
modesLock.writeLock().lock();
modes.put(id, mode);
} finally {
modesLock.writeLock().unlock();
}
}
}
// finished
LOG.debug("Parsing " + MODES_XMLFILE + " finished");
}
private final List<RunHandlerConfig> getRunHandlers(final Element e, final SearchMode inherit) {
final List<RunHandlerConfig> runHandlers = new ArrayList<RunHandlerConfig>();
final NodeList rhNodes = e.getElementsByTagName(RUN_HANDLERS);
if (rhNodes.getLength() > 0) {
final NodeList nodes = rhNodes.item(0).getChildNodes();
for (int i = 0; i < nodes.getLength(); i++) {
if (nodes.item(i) instanceof Element) {
final Element node = (Element) nodes.item(i);
if (RUN_HANDLER_FACTORY.supported(node.getTagName(), context)) {
RunHandlerConfig rhc = RUN_HANDLER_FACTORY.parseRunHandlerConfiguration(context, node);
runHandlers.add(rhc);
}
}
}
} else if (inherit != null && inherit.getRunHandlers().size() > 0) {
runHandlers.addAll(inherit.getRunHandlers());
}
return runHandlers;
}
private final List<RunTransformerConfig> getRunTransformers(final Element e, final SearchMode inherit) {
final List<RunTransformerConfig> runTransformers = new ArrayList<RunTransformerConfig>();
final NodeList rtNodes = e.getElementsByTagName(RUN_TRANSFORMERS);
if (rtNodes.getLength() > 0) {
final NodeList nodes = rtNodes.item(0).getChildNodes();
for (int i = 0; i < nodes.getLength(); i++) {
if (nodes.item(i) instanceof Element) {
final Element node = (Element) nodes.item(i);
if (RUN_TRANSFORMER_FACTORY.supported(node.getTagName(), context)) {
RunTransformerConfig rtc = RUN_TRANSFORMER_FACTORY.parseRunTransformerConfiguration(context, node);
runTransformers.add(rtc);
}
}
}
} else if (inherit != null && inherit.getRunTransformers().size() > 0) {
runTransformers.addAll(inherit.getRunTransformers());
}
return runTransformers;
}
private static SearchMode.SearchCommandExecutorConfig parseExecutor(
final String name,
final SearchMode.SearchCommandExecutorConfig def) {
try {
if (0 < name.length()) {
return SearchMode.SearchCommandExecutorConfig.valueOf(name.toUpperCase());
}
} catch (IllegalArgumentException iae) {
LOG.error("Unparsable executor " + name, iae);
}
return def;
}
private SearchMode getModeImpl(final String id) {
try {
modesLock.readLock().lock();
return modes.get(id);
} finally {
modesLock.readLock().unlock();
}
}
// Inner classes -------------------------------------------------
private static final class RunHandlerConfigFactory extends AbstractConfigFactory<RunHandlerConfig> {
RunHandlerConfigFactory() {}
@Override
protected Class<RunHandlerConfig> findClass(final String xmlName, final Context context)
throws ClassNotFoundException {
final String bName = xmlToBeanName(xmlName);
final String className = Character.toUpperCase(bName.charAt(0)) + bName.substring(1, bName.length());
final String classNameFQ = "no.sesat.search.run.handler."+ className+ "RunHandlerConfig";
final Class<RunHandlerConfig> clazz = loadClass(context, classNameFQ, Spi.RUN_HANDLER_CONFIG);
LOG.debug("run-handler: Found class " + clazz.getName());
return clazz;
}
protected final RunHandlerConfig parseRunHandlerConfiguration(final Context context, final Element e) {
return construct(e, context).readRunHandler(e);
};
}
private static final class RunTransformerConfigFactory extends AbstractConfigFactory<RunTransformerConfig> {
RunTransformerConfigFactory() {}
@Override
protected Class<RunTransformerConfig> findClass(final String xmlName, final Context context)
throws ClassNotFoundException {
final String bName = xmlToBeanName(xmlName);
final String className = Character.toUpperCase(bName.charAt(0)) + bName.substring(1, bName.length());
final String classNameFQ = "no.sesat.search.run.transform."+ className+ "RunTransformerConfig";
final Class<RunTransformerConfig> clazz = loadClass(context, classNameFQ, Spi.RUN_TRANSFORM_CONFIG);
LOG.debug("run-transformer: Found class " + clazz.getName());
return clazz;
}
protected final RunTransformerConfig parseRunTransformerConfiguration(final Context context, final Element e) {
return construct(e, context).readRunTransformer(e);
};
}
private static final class SearchCommandFactory extends AbstractConfigFactory<CommandConfig> {
SearchCommandFactory() {}
SearchConfiguration parseSearchConfiguration(
final SearchModeFactory.Context cxt,
final Element commandE,
final SearchMode mode) throws ParserConfigurationException {
final String parentName = commandE.getAttribute("inherit");
final String id = commandE.getAttribute("id");
final SearchConfiguration inherit = findParent(parentName, mode);
if (!"".equals(parentName) && inherit == null) {
throw new IllegalArgumentException(
MessageFormat.format(ERR_PARENT_COMMAND_NOT_FOUND, parentName, id, mode.getId()));
}
LOG.info(INFO_PARSING_CONFIGURATION + commandE.getLocalName() + " " + id);
try {
final SearchConfiguration sc = parseSearchConfigurationImpl(commandE, inherit, cxt);
if(sc instanceof BaseSearchConfiguration){
// TODO move this into CommandConfig
final BaseSearchConfiguration bsc = (BaseSearchConfiguration)sc;
// initial query transformer (fills out the transformedTerms map the first time)
NodeList iqtNodeList = commandE.getElementsByTagName("initial-query-transformer");
final Element iqtRootElement = (Element) iqtNodeList.item(0);
if (iqtRootElement != null) {
iqtNodeList = iqtRootElement.getChildNodes();
for (int i = 0; i < iqtNodeList.getLength(); i++) {
final Node node = iqtNodeList.item(i);
if (!(node instanceof Element)) {
continue;
}
final Element iqtE = (Element) node;
if (QUERY_TRANSFORMER_FACTORY.supported(iqtE.getTagName(), cxt)) {
bsc.setInitialQueryTransformer(
QUERY_TRANSFORMER_FACTORY.parseQueryTransformer(iqtE, cxt));
// only one can be defined so we're finished
break;
}
}
} else if (null != inherit && inherit instanceof BaseSearchConfiguration) {
// inherit it
bsc.setInitialQueryTransformer(((BaseSearchConfiguration)inherit).getInitialQueryTransformer());
}
// query transformers
NodeList qtNodeList = commandE.getElementsByTagName("query-transformers");
final Element qtRootElement = (Element) qtNodeList.item(0);
if (qtRootElement != null) {
qtNodeList = qtRootElement.getChildNodes();
// clear all inherited query-transformers
bsc.clearQueryTransformers();
for (int i = 0; i < qtNodeList.getLength(); i++) {
final Node node = qtNodeList.item(i);
if (!(node instanceof Element)) {
continue;
}
final Element qt = (Element) node;
if (QUERY_TRANSFORMER_FACTORY.supported(qt.getTagName(), cxt)) {
bsc.addQueryTransformer(QUERY_TRANSFORMER_FACTORY.parseQueryTransformer(qt, cxt));
}
}
}else if (null != inherit && inherit instanceof BaseSearchConfiguration) {
// inherit all
for (QueryTransformerConfig qtc : ((BaseSearchConfiguration)inherit).getQueryTransformers()) {
bsc.addQueryTransformer(qtc);
}
}
// query builder
NodeList qbNodeList = commandE.getElementsByTagName("query-builder");
final Element qbRootElement = (Element) qbNodeList.item(0);
if (qbRootElement != null) {
qbNodeList = qbRootElement.getChildNodes();
for (int i = 0; i < qbNodeList.getLength(); i++) {
final Node node = qbNodeList.item(i);
if (!(node instanceof Element)) {
continue;
}
final Element qb = (Element) node;
if (QUERY_BUILDER_FACTORY.supported(qb.getTagName(), cxt)) {
bsc.setQueryBuilder(QUERY_BUILDER_FACTORY.parseQueryBuilder(qb, cxt));
// only one can be defined so we're finished
break;
}
}
}else if (null != inherit && inherit instanceof BaseSearchConfiguration) {
// inherit it
bsc.setQueryBuilder(((BaseSearchConfiguration)inherit).getQueryBuilder());
}
// result handlers
NodeList rhNodeList = commandE.getElementsByTagName("result-handlers");
final Element rhRootElement = (Element) rhNodeList.item(0);
if (rhRootElement != null) {
rhNodeList = rhRootElement.getChildNodes();
// clear all inherited result handlers
bsc.clearResultHandlers();
for (int i = 0; i < rhNodeList.getLength(); i++) {
final Node node = rhNodeList.item(i);
if (!(node instanceof Element)) {
continue;
}
final Element rh = (Element) node;
if (RESULT_HANDLER_FACTORY.supported(rh.getTagName(), cxt)) {
bsc.addResultHandler(RESULT_HANDLER_FACTORY.parseResultHandler(rh, cxt));
}
}
} else if (null != inherit && inherit instanceof BaseSearchConfiguration) {
// inherit all
for (ResultHandlerConfig rhc : ((BaseSearchConfiguration)inherit).getResultHandlers()) {
bsc.addResultHandler(rhc);
}
}
}
return sc;
} catch (SecurityException ex) {
throw new ParserConfigurationException(ex.getMessage());
} catch (IllegalArgumentException ex) {
throw new ParserConfigurationException(ex.getMessage());
}
}
private SearchConfiguration findParent(
final String id,
final SearchMode mode) {
SearchMode m = mode;
SearchConfiguration config;
do {
final Map<String, SearchConfiguration> configs;
try {
COMMANDS_LOCK.readLock().lock();
configs = COMMANDS.get(m);
} finally {
COMMANDS_LOCK.readLock().unlock();
}
config = configs.get(id);
m = m.getParentSearchMode();
} while (config == null && m != null);
return config;
}
private SearchConfiguration parseSearchConfigurationImpl(
final Element element,
final SearchConfiguration inherit,
final SearchModeFactory.Context context) {
CommandConfig sc = construct(element, context);
assert null == inherit || inherit.getClass().isAssignableFrom(sc.getClass())
: "Can only inherit from same or superclass configuration. "
+ element.getAttribute("id") + '(' + sc.getClass().getSimpleName() + ')'
+ " trying to inherit from " + inherit.getId() + '(' + inherit.getClass().getSimpleName() + ')';
sc.readSearchConfiguration(element, inherit, context);
LOG.info("ParsedSearchConfiguration: " + sc);
return sc;
}
protected Class<CommandConfig> findClass(final String xmlName, final Context context)
throws ClassNotFoundException {
final String bName = xmlToBeanName(xmlName);
final String className = Character.toUpperCase(bName.charAt(0)) + bName.substring(1, bName.length());
LOG.debug("findClass (SearchConfiguration) " + className);
final String classNameFQ = "no.sesat.search.mode.config."+ className+ "Config";
final Class<CommandConfig> clazz = loadClass(context, classNameFQ, Spi.SEARCH_COMMAND_CONFIG);
LOG.debug("Found class " + clazz.getName());
return clazz;
}
}
private static final class QueryTransformerFactory extends AbstractConfigFactory<QueryTransformerConfig> {
QueryTransformerFactory() {
}
QueryTransformerConfig parseQueryTransformer(final Element qt, final SearchModeFactory.Context context) {
return construct(qt, context).readQueryTransformer(qt);
}
protected Class<QueryTransformerConfig> findClass(final String xmlName, final Context context)
throws ClassNotFoundException {
final String bName = xmlToBeanName(xmlName);
final String className = Character.toUpperCase(bName.charAt(0)) + bName.substring(1, bName.length());
LOG.debug("findClass (QueryTransformerConfig) " + className);
final String classNameFQ = "no.sesat.search.query.transform."
+ className
+ "QueryTransformerConfig";
final Class<QueryTransformerConfig> clazz = loadClass(context, classNameFQ, Spi.QUERY_TRANSFORM_CONFIG);
LOG.debug("Found class " + clazz.getName());
return clazz;
}
}
private static final class ResultHandlerFactory extends AbstractConfigFactory<ResultHandlerConfig> {
ResultHandlerFactory() {
}
ResultHandlerConfig parseResultHandler(final Element rh, final SearchModeFactory.Context context) {
return construct(rh, context).readResultHandler(rh);
}
protected Class<ResultHandlerConfig> findClass(final String xmlName, final Context context)
throws ClassNotFoundException {
final String bName = xmlToBeanName(xmlName);
final String className = Character.toUpperCase(bName.charAt(0)) + bName.substring(1, bName.length());
LOG.debug("findClass (ResultHandlerConfig) " + className);
final String classNameFQ = "no.sesat.search.result.handler."
+ className
+ "ResultHandlerConfig";
final Class<ResultHandlerConfig> clazz = loadClass(context, classNameFQ, Spi.RESULT_HANDLER_CONFIG);
LOG.info("Found class " + clazz.getName());
return clazz;
}
}
private static final class QueryBuilderFactory extends AbstractConfigFactory<QueryBuilderConfig> {
QueryBuilderFactory() {}
QueryBuilderConfig parseQueryBuilder(final Element qt, final SearchModeFactory.Context context) {
final QueryBuilderConfig config = construct(qt, context);
if( config instanceof QueryBuilderConfig.ModesW3cDomDeserialiser ){
return ((QueryBuilderConfig.ModesW3cDomDeserialiser)config).readQueryBuilder(qt);
}
throw new UnsupportedOperationException("Unknown deserialisation method");
}
protected Class<QueryBuilderConfig> findClass(final String xmlName, final Context context)
throws ClassNotFoundException {
final String bName = xmlToBeanName(xmlName);
final String className = Character.toUpperCase(bName.charAt(0)) + bName.substring(1, bName.length());
LOG.debug("findClass (QueryBuilderConfig) " + className);
final String classNameFQ = "no.sesat.search.mode.config.querybuilder."
+ className
+ "QueryBuilderConfig";
final Class<QueryBuilderConfig> clazz = loadClass(context, classNameFQ, Spi.SEARCH_COMMAND_CONFIG);
LOG.debug("Found class " + clazz.getName());
return clazz;
}
}
}