/* 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.view.velocity; import org.apache.velocity.context.InternalContextAdapter; import org.apache.velocity.runtime.parser.node.Node; import org.apache.velocity.exception.ResourceNotFoundException; import org.apache.velocity.exception.ParseErrorException; import org.apache.velocity.exception.MethodInvocationException; import org.apache.log4j.Logger; import java.io.Writer; import java.io.IOException; import java.util.Comparator; import java.util.Set; import java.util.TreeSet; import no.sesat.search.datamodel.DataModel; import no.sesat.search.datamodel.search.SearchDataObject; import no.sesat.search.result.ResultList; import no.sesat.search.site.Site; import no.sesat.search.view.config.SearchTab; import org.apache.velocity.Template; import static no.sesat.search.view.config.SearchTab.EnrichmentHint.*; import org.apache.velocity.app.VelocityEngine; /** * Handles presenting the enrichments * * The first argument allow inclusion/exclusion of each enrichment according to the subclasses implementation of * placementCorrect(tab, placement, i, e) <br/><br/> * * The second argument specifies the a string to use in beginning wrapping around each enrichment. * a third argument is expected for end wrapping. * If no argument is specified no div is written around each enrichment.) <br/><br/> * * The enrichments that are rendered are those named as the results field for EnrichmentHint.NAME_KEY within the * "templates/enrichments/${placement}/" directory. If this is not found then the same named template * within the "templates/enrichments/" directory is used. * * * @version $Id$ */ public abstract class AbstractEnrichmentDirective extends AbstractDirective { private static final Logger LOG = Logger.getLogger(AbstractEnrichmentDirective.class); private static final Logger PRODUCT_LOG = Logger.getLogger("no.sesat.Product"); // subclasses have to copy this private static final String NAME = "enrichments"; public String getName() { return NAME; } public int getType() { return LINE; } /** * @param cxt The cxt. * @param writer The writer. * @param node The node. * * @return return true on success. * * @throws IOException * @throws ResourceNotFoundException * @throws ParseErrorException * @throws MethodInvocationException */ public boolean render(final InternalContextAdapter cxt, final Writer writer, final Node node) throws IOException, ResourceNotFoundException, ParseErrorException, MethodInvocationException { if (1 > node.jjtGetNumChildren() && 3 < node.jjtGetNumChildren()) { LOG.error("#" + getName() + " - wrong count of arguments"); return false; } final String placement = getArgument(cxt, node, 0); if(null != getDataModel(cxt).getPage() && null != getDataModel(cxt).getPage().getCurrentTab()){ final SearchTab tab = getDataModel(cxt).getPage().getCurrentTab(); final Site site = getDataModel(cxt).getSite().getSite(); final VelocityEngine engine = VelocityEngineFactory .valueOf(getDataModel(cxt).getSite().getSite()) .getEngine(); final boolean enrichmentsExisting = null != cxt.get("enrichments"); @SuppressWarnings("unchecked") final Set<ResultList> enrichments = enrichmentsExisting ? (Set<ResultList>)cxt.get("enrichments") : new TreeSet<ResultList>(new Comparator<ResultList>(){ public int compare(ResultList o1, ResultList o2) { // highest scores first, ie descending order. final int result = (int)((Float)o2.getObjectField(SCORE_KEY) - (Float)o1.getObjectField(SCORE_KEY)); // never return zero. in a treeset it means overriding the other enrichment. return 0 != result ? result : String.CASE_INSENSITIVE_ORDER.compare(o1.getField(NAME_KEY), o2.getField(NAME_KEY)); } }); if(!enrichmentsExisting){ for(SearchDataObject search : getDataModel(cxt).getSearches().values()){ if(null != search.getResults().getObjectField(HINT_KEY)){ enrichments.add(search.getResults()); } } cxt.put("enrichments", enrichments); } // we check later if this has already been written for this request final StringBuilder log = enrichmentsExisting ? null : new StringBuilder( "<enrichments mode=\"" + tab.getKey() + "\" size=\"" + enrichments.size() + "\">" + "<query>" + getDataModel(cxt).getQuery().getXmlEscaped() + "</query>"); int i = 0; for(ResultList e : enrichments){ // product log if(!enrichmentsExisting){ log.append("<enrichment name=\"" + e.getField(NAME_KEY) + "\" score=\"" + e.getObjectField(SCORE_KEY) + "\"/>"); } // enrichments if (placementCorrect(getDataModel(cxt), placement, i, e)){ if(3 == node.jjtGetNumChildren()){ writer.append(getArgument(cxt, node, 1)); } cxt.put("commandName", e.getField(NAME_KEY)); Template template = null; try{ template = VelocityEngineFactory .getTemplate(engine, site, "/enrichments/" + placement + '/' + e.getField(NAME_KEY)); }catch(ResourceNotFoundException rnfe){ LOG.debug(rnfe.getMessage(), rnfe); // not important template = VelocityEngineFactory .getTemplate(engine, site, "/enrichments/" + e.getField(NAME_KEY)); } template.merge(cxt, writer); if(3 == node.jjtGetNumChildren()){ writer.append(getArgument(cxt, node, 2)); } } ++i; } if(!enrichmentsExisting){ PRODUCT_LOG.info(log.toString() + "</enrichments>"); } return true; } return false; } protected abstract boolean placementCorrect(DataModel datamodel, String placement, int i, ResultList e); }