/* 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/>.
*
* SearchTab.java
*
* Created on 20 April 2006, 07:55
*
*/
package no.sesat.search.view.config;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import no.sesat.search.site.config.AbstractDocumentFactory;
import no.sesat.search.view.navigation.NavigationConfig;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
/**
* Immutable POJO holding the view configuration for a given tab.
*
*
* @version $Id$
*/
public final class SearchTab implements Serializable{
public enum Scope{REQUEST, SESSION};
// Constants -----------------------------------------------------
public static final String PARAMETER_KEY = "c";
//private static final Logger LOG = Logger.getLogger(SearchTab.class);
// Attributes ----------------------------------------------------
private final String id;
private final String adCommand;
private final int adLimit;
private final int adOnTop;
private final SearchTab inherit;
private final String key;
private final String parentKey;
private final String rssResultName;
private final boolean rssHidden;
private final boolean displayCss;
private final boolean executeOnBlank;
private final Collection<EnrichmentPlacementHint> placements = new ArrayList<EnrichmentPlacementHint>();
private final Collection<EnrichmentHint> enrichments = new ArrayList<EnrichmentHint>();
private final String mode;
private final List<String> css = new ArrayList<String>();
private final List<String> javascript = new ArrayList<String>();
private final Layout defaultLayout;
private final Map<String,Layout> layouts = new HashMap<String,Layout>();
private final NavigationConfig navigationConfig;
private final Scope scope;
// Static --------------------------------------------------------
// Constructors --------------------------------------------------
/** Creates a new instance of SearchTab
* @param inherit
* @param id
* @param mode
* @param key
* @param parentKey
* @param rssResultName
* @param rssHidden
* @param navConf
* @param placements
* @param enrichments
* @param adCommand
* @param adLimit
* @param adOnTop
* @param css
* @param javascript
* @param defaultLayout
* @param displayCss
* @param executeOnBlank
* @param layouts
* @param scope
*/
public SearchTab(
final SearchTab inherit,
final String id,
final String mode,
final String key,
final String parentKey,
final String rssResultName,
final boolean rssHidden,
final NavigationConfig navConf,
final Collection<EnrichmentPlacementHint> placements,
final Collection<EnrichmentHint> enrichments,
final String adCommand,
final int adLimit,
final int adOnTop,
final List<String> css,
final List<String> javascript,
final boolean displayCss,
final boolean executeOnBlank,
final Layout defaultLayout,
final Map<String,Layout> layouts,
final Scope scope){
this.inherit = inherit;
this.id = id;
// rather compact code. simply assigns the property to that pass in, or that from the inherit object, or null/-1
this.mode = mode != null && mode.trim().length() >0 ? mode : inherit != null ? inherit.mode : null;
this.key = key != null && key.trim().length() >0 ? key : inherit != null ? inherit.key : null;
this.parentKey = parentKey != null && parentKey.trim().length() >0
? parentKey
: inherit != null ? inherit.parentKey : null;
this.navigationConfig = navConf;
this.placements.addAll(placements);
this.enrichments.addAll(enrichments);
this.adCommand = adCommand != null && adCommand.trim().length() >0
? adCommand
: inherit != null ? inherit.adCommand : null;
this.adLimit = adLimit >=0 || inherit == null ? adLimit : inherit.adLimit;
this.adOnTop = adOnTop >=0 || inherit == null ? adOnTop : inherit.adOnTop;
this.displayCss = displayCss;
if(inherit != null){
// we cannot inherit navigators because there require a live reference to the applicable SearchTabFactory
// but we do inherit enrichments and css
this.enrichments.addAll(inherit.enrichments);
this.placements.addAll(inherit.placements);
this.css.addAll(inherit.css);
this.layouts.putAll(inherit.layouts);
}
this.rssResultName = rssResultName;
this.css.addAll(css);
this.javascript.addAll(javascript);
this.executeOnBlank = executeOnBlank;
this.rssHidden = rssHidden;
this.defaultLayout = defaultLayout;
this.layouts.putAll(layouts);
this.scope = null != scope ? scope : inherit.scope;
}
// Getters --------------------------------------------------------
/**
* Getter for property id.
* @return Value of property id.
*/
public String getId() {
return this.id;
}
/**
* Getter for property adCommand.
* @return Value of property adCommand.
*/
public String getAdCommand() {
return this.adCommand;
}
/**
* Getter for property adsLimit.
* @return Value of property adsLimit.
*/
public int getAdLimit() {
return this.adLimit;
}
/**
* Getter for property adOnTop.
* @return Value of property adOnTop.
*/
public int getAdOnTop() {
return this.adOnTop;
}
/**
* Getter for property inherit.
* @return Value of property inherit.
*/
public SearchTab getInherit() {
return this.inherit;
}
/**
* Getter for property key.
* Implicitly favours parent-key if it exists.
* @return Value of property key.
*/
public String getKey() {
if (parentKey != null) {
return parentKey;
} else {
return key;
}
}
/**
* Getter for the property key.
* Does not give prefence to parent-key like "getKey()" does.
* @return Valaue of property key.
*/
public String getRealKey(){
return key;
}
/**
* Getter for property parentKey.
* @return Value of property parentKey.
*/
public String getParentKey() {
return this.parentKey;
}
/**
* Getter for property rssResultName.
* If it is an (comma seperated) array return the first element.
* @return Value of property rssResultName.
*/
public String getRssResultName() {
return rssResultName.contains(",")
? rssResultName.split(",")[0]
: rssResultName;
}
/**
* Getter for property rssResultName.
* Returns the whole array, indicated the list of commands to run.
* @return Value of property rssResultName.
*/
public String[] getRssCommands() {
return rssResultName.split(",");
}
/**
* Returns true if there should be no visible links to the rss version of this tab.
* @deprecated Not JavaBean compatable. Use isRssHidden() instead.
*
* @return true if hidden.
*/
public boolean getRssHidden() {
return rssHidden;
}
/**
* Returns true if there should be no visible links to the rss version of this tab.
*
* @return true if hidden.
*/
public boolean isRssHidden() {
return rssHidden;
}
/**
* Getter for property showRss
* @return
* @deprecated Not JavaBean compatable. Use isShowRss() instead.
*/
public boolean getShowRss() {
return !"".equals(rssResultName) && !getRssHidden();
}
/**
* Getter for property showRss
* @return
*/
public boolean isShowRss() {
return !"".equals(rssResultName) && !isRssHidden();
}
/**
* Getter for property executeOnFront
* @return
*/
public boolean isExecuteOnBlank() {
return executeOnBlank;
}
/**
* Getter for property displayCss
* @return
*
* @deprecated css definitions belong against the layout.
*/
public boolean isDisplayCss() {
return displayCss;
}
public EnrichmentPlacementHint getEnrichmentPlacement(final String id){
for(EnrichmentPlacementHint p : placements){
if(p.getId().equals(id)){
return p;
}
}
return null;
}
/**
* Getter for property enrichments.
* @return Value of property enrichments.
*/
public Collection<EnrichmentHint> getEnrichments() {
return Collections.unmodifiableCollection(enrichments);
}
/**
*
* @param command
* @return
*/
public EnrichmentHint getEnrichmentByCommand(final String command){
for(EnrichmentHint e : enrichments){
if(e.getCommand().equals(command)){
return e;
}
}
return null;
}
/**
* Getter for property mode.
* @return Value of property mode.
*/
public String getMode() {
return this.mode;
}
@Override
public String toString(){
return id + (inherit != null ? " --> " + inherit.toString() : "");
}
/**
*
* @return
*/
public List<SearchTab> getAncestry(){
// XXX cache result
final List<SearchTab> ancestry = new ArrayList<SearchTab>();
for(SearchTab t = this; t != null; t = t.getInherit()){
if (t.displayCss) {
ancestry.add(t);
}
}
Collections.reverse(ancestry);
return Collections.unmodifiableList(ancestry);
}
/**
* Getter for property css.
* @return Value of property css.
*
* @deprecated css definitions belong against the layout.
*/
public List<String> getCss() {
return Collections.unmodifiableList(css);
}
/**
* Getter for property javascript.
* @return Value of property javascript.
*
* @deprecated javascript definitions belong against the layout.
*/
public List<String> getJavascript() {
return Collections.unmodifiableList(javascript);
}
/**
* Getter for property defaultLayout.
* @return Value of property defaultLayout.
*/
public Layout getDefaultLayout() {
return defaultLayout;
}
public Map<String,Layout> getLayouts(){
return Collections.unmodifiableMap(layouts);
}
public NavigationConfig getNavigationConfiguration(){
return navigationConfig;
}
public Scope getScope(){
return scope;
}
// Inner classes -------------------------------------------------
/** Immutable POJO holding Enrichment properties from a given tab. **/
public static final class EnrichmentHint implements Serializable {
public static final String NAME_KEY = "enrichmentName";
public static final String SCORE_KEY = "enrichmentScore";
public static final String HINT_KEY = "enrichmentHint";
/**
*
* @param rule
* @param baseScore
* @param threshold
* @param weight
* @param command
* @param properties
*/
public EnrichmentHint(
final String rule,
final int baseScore,
final int threshold,
final float weight,
final String command,
final Map<String,String> properties){
this.rule = rule;
this.baseScore = baseScore;
this.threshold = threshold;
this .weight = weight;
this.command = command;
this.properties.putAll(properties);
}
private final String rule;
private final int baseScore;
private final int threshold;
private final String command;
private final float weight;
private final Map<String,String> properties = new HashMap<String,String>();
/**
* Getter for property rule.
* @return Value of property rule. Returns null if value equals empty
* String("").
*/
public String getRule() {
return "".equals(rule) ? null : rule;
}
/**
* Getter for property baseScore.
* @return Value of property baseScore.
*/
public int getBaseScore() {
return this.baseScore;
}
/**
* Getter for property threshold.
* @return Value of property threshold.
*/
public int getThreshold() {
return this.threshold;
}
/**
* Getter for property command.
* @return Value of property command.
*/
public String getCommand() {
return this.command;
}
/**
* Getter for property weight.
* @return Value of property weight.
*/
public float getWeight() {
return this.weight;
}
@Override
public String toString() {
return rule + '[' + command + ']';
}
public Map<String,String> getProperties(){
return Collections.unmodifiableMap(properties);
}
public String getProperty(final String key){
return properties.get(key);
}
}
/** Immutable POJO holdng Enrichment Placement properties for a given placement on a given tab. **/
public static final class EnrichmentPlacementHint implements Serializable {
private final String id;
private final int threshold;
private final int max;
private final Map<String,String> properties = new HashMap<String,String>();
public EnrichmentPlacementHint(
final String id,
final int threshold,
final int max,
final Map<String,String> properties){
this.id = id;
this.threshold = threshold;
this.max = max;
this.properties.putAll(properties);
}
public String getId(){
return id;
}
public int getThreshold(){
return threshold;
}
public int getMax(){
return max;
}
public String getProperty(final String key){
return properties.get(key);
}
}
/** POJO holding defaultLayout information for the given tab.
* readLayout(Element) is the only way to mutate the bean and can only be called once.
**/
public static final class Layout implements Serializable {
private String id;
private String origin;
private String main;
private String front;
private Map<String,String> includes;
private Map<String,String> properties;
private String contentType;
private int expires = -1;
private boolean displayCss;
private final List<String> css = new ArrayList<String>();
private final List<String> javascript = new ArrayList<String>();
private Layout(){}
/** Copy constructor. Used when inheriting from another Layout.
*
* Two fields cannot be inherited, id and origin.
* These must be explicitly set on each instance.
*
* @param inherit
*/
public Layout(final Layout inherit){
if( null != inherit ){
// id cannot be inherited!
// origin cannot be inherited!
main = inherit.main;
front = inherit.front;
includes = inherit.includes;
properties = inherit.properties;
contentType = inherit.contentType;
expires = inherit.expires;
displayCss = inherit.displayCss;
css.addAll(inherit.css);
javascript.addAll(inherit.javascript);
}
}
public String getId(){
return id;
}
public Map<String,String> getIncludes(){
return includes;
}
/** Get the include with the given key.
*
* @param key
* @return
*/
public String getInclude(final String key){
return includes.get(key);
}
public Map<String,String> getProperties(){
return properties;
}
/** Get the property with the given key
*
* @param key
* @return
*/
public String getProperty(final String key){
return properties.get(key);
}
/**
* @return
* @deprecated no need to use this anymore.
**/
public String getOrigin(){
return origin;
}
public String getMain(){
return main;
}
public String getFront(){
return front;
}
/**
* Get the content type that is specified in views.xml.
*
* @return content type
*/
public String getContentType(){
return contentType;
}
/**
* Number of seconds until this layout (page) should expire.
*
* @return number of seconds until page expires.
*/
public int getExpiresInSeconds() {
return expires;
}
/**
* Getter for property displayCss
* @return
*/
public boolean isDisplayCss() {
return displayCss;
}
/**
* Getter for property css.
* @return Value of property css.
*/
public List<String> getCss() {
return Collections.unmodifiableList(css);
}
/**
* Getter for property javascript.
* @return Value of property javascript.
*/
public List<String> getJavascript() {
return Collections.unmodifiableList(javascript);
}
/** Will return null when the element argument is null.
* Otherwise returns the Layout object deserialised from the contents of the Element.
** @param element
*
* @return
*/
public Layout readLayout(final Element element){
if( null != origin ){
throw new IllegalStateException("Not allowed to call readLayout(element) twice");
}
if( null != element ){
id = element.getAttribute("id");
origin = element.getAttribute("origin");
if(0 < element.getAttribute("main").length()){
main = element.getAttribute("main");
}
if(0 < element.getAttribute("front").length()){
front = element.getAttribute("front");
}
if(0 < element.getAttribute("content-type").length()){
contentType = element.getAttribute("content-type");
}
if(0 < element.getAttribute("expires-in-seconds").length()){
expires = Integer.parseInt(element.getAttribute("expires-in-seconds"));
}
if(0 < element.getAttribute("display-css").length()){
displayCss = Boolean.parseBoolean(element.getAttribute("display-css"));
}
final String allCss = AbstractDocumentFactory.parseString(element.getAttribute("css"), null);
final String[] cssArr = allCss != null ? allCss.split(",") : new String[]{};
css.addAll(Arrays.asList(cssArr));
final String allJavascript
= AbstractDocumentFactory.parseString(element.getAttribute("javascript"), null);
final String[] jsArr = allJavascript != null ? allJavascript.split(",") : new String[]{};
javascript.addAll(Arrays.asList(jsArr));
includes = readMap(includes, element.getElementsByTagName("include"), "key", "template");
properties = readMap(properties, element.getElementsByTagName("property"), "key", "value");
}
return null == element ? null : this;
}
private Map<String,String> readMap(
final Map<String,String> inherited,
final NodeList list,
final String keyElementName,
final String valueElementName){
final Map<String,String> map = new HashMap<String,String>(null != inherited
? inherited
: Collections.<String, String>emptyMap());
for(int i = 0; i< list.getLength(); ++i){
final Element include = (Element) list.item(i);
final String key = include.getAttribute(keyElementName);
map.put(key, include.hasAttribute(valueElementName) ? include.getAttribute(valueElementName) : "");
}
return Collections.unmodifiableMap(map);
}
}
}