/* Copyright (2007-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.run.handler;
import java.util.Map;
import no.sesat.search.datamodel.DataModel;
import no.sesat.search.datamodel.generic.StringDataObject;
import no.sesat.search.datamodel.navigation.NavigationDataObject;
import no.sesat.search.view.NavigationControllerSpiFactory;
import no.sesat.search.result.NavigationItem;
import no.sesat.search.result.BasicNavigationItem;
import no.sesat.search.site.Site;
import no.sesat.search.site.SiteContext;
import java.util.List;
import java.util.Properties;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import javax.xml.parsers.DocumentBuilder;
import no.sesat.search.site.config.BytecodeLoader;
import no.sesat.search.site.config.DocumentLoader;
import no.sesat.search.site.config.PropertiesLoader;
import no.sesat.search.site.config.SiteClassLoaderFactory;
import no.sesat.search.site.config.Spi;
import no.sesat.search.view.navigation.NavigationConfig;
import no.sesat.search.view.navigation.NavigationController;
import no.sesat.search.view.navigation.NavigationControllerFactory;
import no.sesat.search.view.navigation.NavigationManager;
import no.sesat.search.view.navigation.NavigationState;
import no.sesat.search.view.navigation.UrlGenerator;
/**
* To help generating navigation urls in the view. I got tired of all
* the URL handling velocity code. Some of the effects from this is virtually impossible to
* code in velocity.
* <p/>
* As a bonus from using this, you don't need to data-model the commands that only are
* there for navigation.
*
*
*
* @version $Id: NavigationRunHandler.java 5846 2007-10-28 13:15:35Z ssmaeklu $
*/
public final class NavigationRunHandler implements RunHandler{
private NavigationControllerSpiFactory controllerFactoryFactory;
private NavigationManager navigationManager;
private static final String PARAM_LAYOUT = "layout";
public NavigationRunHandler(final RunHandlerConfig rhc) {}
private boolean isRss(final DataModel datamodel) {
final Map<String,StringDataObject> parameters = datamodel.getParameters().getValues();
return parameters.get(PARAM_LAYOUT) != null && parameters.get(PARAM_LAYOUT).getString().equals("rss");
}
public void handleRunningQuery(final Context context) {
if (isRss(context.getDataModel())) {
return;
}
final NavigationControllerSpiFactory.Context cxt = new NavigationControllerSpiFactory.Context() {
public Site getSite() {
return context.getSite();
}
public BytecodeLoader newBytecodeLoader(SiteContext siteContext, String className, String jarFileName) {
return context.newBytecodeLoader(siteContext, className, jarFileName);
}
};
this.controllerFactoryFactory = new NavigationControllerSpiFactory(cxt);
this.navigationManager = new NavigationManager(context.getDataModel());
final SiteClassLoaderFactory.Context classLoadingContext = createClassLoadingContext(context);
// Update the datamodel
final NavigationDataObject navDO = context.getDataModel().getNavigation();
if (navDO.getConfiguration() != null) {
for (final NavigationConfig.Navigation n : navDO.getConfiguration().getNavigationList()) {
final UrlGenerator urlGenerator = getUrlGeneratorInstance(classLoadingContext, n, context);
final NavigationController.Context navCxt = createNavigationControllerContext(urlGenerator, context);
processNavs(n.getNavList(), context.getDataModel(), navCxt, urlGenerator);
}
}
}
/**
* Process the navs in a top-down fashion so that children can use the result of their parents.
*
* @param navs
* @param dataModel
* @param navCxt
* @param urlGenerator
*/
private void processNavs(
final List<NavigationConfig.Nav> navs,
final DataModel dataModel,
final NavigationController.Context navCxt,
final UrlGenerator urlGenerator) {
final NavigationDataObject navDO = dataModel.getNavigation();
for (final NavigationConfig.Nav nav : navs) {
final NavigationItem items = getNavigators(nav, navCxt);
// Navs with null id are considered anonymous. These navs typically just modify the result of their
// parent and will not be found in the navmap.
if (items != null && nav.getId() != null) {
navDO.setNavigation(nav.getId(), items);
// Create a "back" navigation item.
final NavigationItem reset
= new BasicNavigationItem("reset_" + nav.getId(), urlGenerator.getURL("", nav), 0);
navDO.setNavigation("reset_" + nav.getId(), reset);
}
processNavs(nav.getChildNavs(), dataModel, navCxt, urlGenerator);
}
}
private NavigationItem getNavigators(
final NavigationConfig.Nav navEntry,
final NavigationController.Context navCxt) {
final NavigationControllerFactory<NavigationConfig.Nav> factory
= controllerFactoryFactory.getController(navEntry);
return factory.get(navEntry).getNavigationItems(navCxt);
}
private NavigationController.Context createNavigationControllerContext(
final UrlGenerator urlGenerator,
final Context context) {
return new NavigationController.Context() {
public DataModel getDataModel() {
return context.getDataModel();
}
public DocumentLoader newDocumentLoader(SiteContext siteCxt, String resource, DocumentBuilder builder) {
return context.newDocumentLoader(siteCxt, resource, builder);
}
public PropertiesLoader newPropertiesLoader(SiteContext siteCxt, String resource, Properties properties) {
return context.newPropertiesLoader(siteCxt, resource, properties);
}
public BytecodeLoader newBytecodeLoader(SiteContext siteContext, String className, String jarFileName) {
return context.newBytecodeLoader(siteContext, className, jarFileName);
}
public Site getSite() {
return context.getSite();
}
public UrlGenerator getUrlGenerator() {
return urlGenerator;
}
};
}
private UrlGenerator getUrlGeneratorInstance(
final SiteClassLoaderFactory.Context classLoadingContext,
final NavigationConfig.Navigation navigation,
final Context context) {
try {
final SiteClassLoaderFactory f = SiteClassLoaderFactory.instanceOf(classLoadingContext);
final Class clazz = f.getClassLoader().loadClass(navigation.getUrlGenerator());
@SuppressWarnings("unchecked")
final Constructor<? extends UrlGenerator> s
= clazz.getConstructor(DataModel.class, NavigationConfig.Navigation.class, NavigationState.class);
return s.newInstance(context.getDataModel(), navigation, navigationManager.getNavigationState());
}catch (InstantiationException ex) {
throw new IllegalArgumentException(
"Unable to load desired url generator: " + navigation.getUrlGenerator(),
ex);
}catch (IllegalAccessException ex) {
throw new IllegalArgumentException(
"Unable to load desired url generator: " + navigation.getUrlGenerator(),
ex);
}catch (IllegalArgumentException ex) {
throw new IllegalArgumentException(
"Unable to load desired url generator: " + navigation.getUrlGenerator(),
ex);
}catch (InvocationTargetException ex) {
throw new IllegalArgumentException(
"Unable to load desired url generator: " + navigation.getUrlGenerator(),
ex);
}catch (NoSuchMethodException ex) {
throw new IllegalArgumentException(
"Unable to load desired url generator: " + navigation.getUrlGenerator(),
ex);
}catch (SecurityException ex) {
throw new IllegalArgumentException(
"Unable to load desired url generator: " + navigation.getUrlGenerator(),
ex);
}catch(ClassNotFoundException ex){
throw new IllegalArgumentException(
"Unable to load desired url generator: " + navigation.getUrlGenerator(),
ex);
}
}
private SiteClassLoaderFactory.Context createClassLoadingContext(final Context context) {
return new SiteClassLoaderFactory.Context() {
public BytecodeLoader newBytecodeLoader(SiteContext siteContext, String className, String jarFileName) {
return context.newBytecodeLoader(siteContext, className, jarFileName);
}
public Site getSite() {
return context.getSite();
}
public Spi getSpi() {
return Spi.VIEW_CONTROL;
}
};
}
}