<commit_before>


package org.cipres.treebase.web.controllers;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.log4j.Logger;
import org.cipres.treebase.TreebaseIDString;
import org.cipres.treebase.TreebaseUtil;
import org.cipres.treebase.TreebaseIDString.MalformedTreebaseIDString;
import org.cipres.treebase.domain.TBPersistable;
import org.cipres.treebase.domain.search.SearchResults;
import org.cipres.treebase.domain.search.SearchResultsFrozen;
import org.cipres.treebase.domain.search.SearchResultsType;
import org.cipres.treebase.domain.search.SearchService;
import org.cipres.treebase.domain.search.StudySearchResults;
import org.cipres.treebase.domain.taxon.TaxonLabelService;
import org.cipres.treebase.service.AbstractService;
import org.cipres.treebase.web.Constants;
import org.cipres.treebase.web.model.SearchCommand;
import org.cipres.treebase.web.util.SearchMessageSetter;
import org.springframework.validation.BindException;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.view.RedirectView;
import org.z3950.zing.cql.CQLBooleanNode;
import org.z3950.zing.cql.CQLNode;
import org.z3950.zing.cql.CQLParseException;
import org.z3950.zing.cql.CQLRelation;
import org.z3950.zing.cql.CQLTermNode;


/**
 * @author mjd 20080701
 */
public abstract class SearchController extends BaseFormController {
	/**
	 * Logger for this class
	 */
	private static final Logger LOGGER = Logger.getLogger(SearchController.class);	
	protected SearchService searchService;
	protected String formView;
	private TaxonLabelService mTaxonLabelService;
	
	protected abstract ModelAndView handleQueryRequest(HttpServletRequest request,HttpServletResponse response,BindException errors,String query) throws CQLParseException, IOException, InstantiationException;
	
	/**
	 * Recursively traverses a CQL parse tree (presumably starting from its
	 * root node), replacing the observed indices with ones suggested by the map
	 * returned by getPredicateMapping(). The function of this is to allow concrete
	 * subclasses to create specific mappings from more general ones, e.g. when a
	 * study search query refers to the index "dcterms.identifier", the StudySearchController
	 * can specify that within its context this index is understood to mean "tb.identifier.study",
	 * i.e. the study ID.
	 * @param node
	 * @return
	 */
	protected CQLNode normalizeParseTree(CQLNode node) {
		if ( node instanceof CQLBooleanNode ) {
			((CQLBooleanNode)node).left = normalizeParseTree(((CQLBooleanNode)node).left);
			((CQLBooleanNode)node).right = normalizeParseTree(((CQLBooleanNode)node).right);
			return node;
		}
		else if ( node instanceof CQLTermNode ) {
			String index = ((CQLTermNode)node).getIndex();
			String term = ((CQLTermNode)node).getTerm();
			CQLRelation relation = ((CQLTermNode)node).getRelation();
			Map<String,String> mapping = getPredicateMapping();
			for ( String key : mapping.keySet() ) {
				index = index.replaceAll(key, mapping.get(key));
			}
			return new CQLTermNode(index,relation,term);
		}
		logger.debug(node);
		return node;
	}	
	
	/**
	 * Returns a mapping between more general search indices (e.g. "dcterms.identifier")
	 * and the specific indices (e.g. "tb.identifier.study") that are understood to be used 
	 * by the concrete subclass
	 * @return
	 */
	protected abstract Map<String,String> getPredicateMapping();

	protected ModelAndView onSubmit(
			HttpServletRequest request,
			HttpServletResponse response,
			Object command,
			BindException errors) throws Exception {

		LOGGER.info("in generic SearchController.onSubmit");

		clearMessages(request);
		String formName = request.getParameter("formName");

		LOGGER.info("formName is '" + formName + "'");

		if (formName == null) {
			throw new Error("No form name supplied");
		} else if (formName.equals("resultsAction")) {
			if (request.getParameterMap().containsKey("refineSearch")) {
				// TODO
			} else if (request.getParameterMap().containsKey("discardResults")) {
				onSubmitDiscardResults(request, response, command, errors);
			} else if (request.getParameterMap().containsKey("action")) {
				String action = request.getParameter("action");
				if (action.equals("discardResults")) {
					onSubmitDiscardResults(request, response, command, errors);
				} else if ( action.equals("refineSearch") ){
					onSubmitRefineSearch(request,response,command,errors);					
				} else {
					throw new Error("Unknown action '" + action + "'");
				}
			} else {
				throw new Error("Couldn't find button selection");	
			}
		} else if (formName.equals("searchResultsConversion")) {			
			onSubmitConversion(request, response, command, errors);
		} else if (formName.equals("discardResults")) {
			onSubmitDiscardResults(request, response, command, errors);
		} else {
			throw new Error("Unknown form name '" + formName + "'");
		}
		
		LOGGER.debug("using selectResultsView to determine view");
		return selectResultsView(request);
	}
	
	protected ModelAndView searchResultsAsRDF (SearchResults<?> searchResults,HttpServletRequest request,CQLNode root, String schema, String original) throws UnsupportedEncodingException {
		//${baseURL}
		//${phyloWSPath}
		//${normalizedCQLQuery}
		//${searchResults}
		//${domainAddress}
		String phyloWSPath = "find/" + original;
		request.getSession().setAttribute("searchResultsThawed", searchResults);		
		StringBuffer domainAddress = new StringBuffer("http://");
		domainAddress
			.append(request.getServerName())
			.append(':')
			.append(request.getServerPort());
		StringBuffer baseURL = new StringBuffer(domainAddress);
		baseURL.append("/treebase-web/phylows");
		request.getSession().setAttribute("recordSchema", schema);
		request.getSession().setAttribute("format", request.getParameter("format"));
		request.getSession().setAttribute("baseURL", baseURL.toString());
		request.getSession().setAttribute("domainAddress", domainAddress.toString());
		request.getSession().setAttribute("phyloWSPath", phyloWSPath);
		request.getSession().setAttribute("originalSection", original);
		if ( null != root ) {			
			request.getSession().setAttribute("normalizedCQLQuery", URLEncoder.encode(root.toCQL(),"UTF-8"));
		}
		return new ModelAndView("searchResultsAsRDF");
	}
	
	protected <E extends TBPersistable> SearchResults<E> intersectSearchResults(
			SearchResults<E> oldRes,
			SearchResults<E> newRes, 
			SearchMessageSetter searchMessages,
			String noResultsMessage) {
		if (noResultsMessage == null) noResultsMessage = "Your search produced no results";
		
		if (! newRes.isAll()) 
			LOGGER.info("Search returned " + newRes.nItems());
		
		if (oldRes.isAll() || oldRes.isEmpty()) 
			return newRes;
		else if (newRes.isAll()) 
			return oldRes;
		else {
			@SuppressWarnings("unchecked")
			SearchResults<E> intersection = oldRes.clone();
			intersection.intersect(newRes);
			
			if (intersection.isEmpty()) {<commit_msg>The baseURL session attribute is now set to the PURL domain (which is obtained from the JNDI configuration) instead of trying (and failing) to compute it from the local domain address.<commit_after>


package org.cipres.treebase.web.controllers;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.log4j.Logger;
import org.cipres.treebase.TreebaseIDString;
import org.cipres.treebase.TreebaseUtil;
import org.cipres.treebase.TreebaseIDString.MalformedTreebaseIDString;
import org.cipres.treebase.domain.TBPersistable;
import org.cipres.treebase.domain.search.SearchResults;
import org.cipres.treebase.domain.search.SearchResultsFrozen;
import org.cipres.treebase.domain.search.SearchResultsType;
import org.cipres.treebase.domain.search.SearchService;
import org.cipres.treebase.domain.search.StudySearchResults;
import org.cipres.treebase.domain.taxon.TaxonLabelService;
import org.cipres.treebase.service.AbstractService;
import org.cipres.treebase.web.Constants;
import org.cipres.treebase.web.model.SearchCommand;
import org.cipres.treebase.web.util.SearchMessageSetter;
import org.springframework.validation.BindException;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.view.RedirectView;
import org.z3950.zing.cql.CQLBooleanNode;
import org.z3950.zing.cql.CQLNode;
import org.z3950.zing.cql.CQLParseException;
import org.z3950.zing.cql.CQLRelation;
import org.z3950.zing.cql.CQLTermNode;


/**
 * @author mjd 20080701
 */
public abstract class SearchController extends BaseFormController {
	/**
	 * Logger for this class
	 */
	private static final Logger LOGGER = Logger.getLogger(SearchController.class);	
	protected SearchService searchService;
	protected String formView;
	private TaxonLabelService mTaxonLabelService;
	
	protected abstract ModelAndView handleQueryRequest(HttpServletRequest request,HttpServletResponse response,BindException errors,String query) throws CQLParseException, IOException, InstantiationException;
	
	/**
	 * Recursively traverses a CQL parse tree (presumably starting from its
	 * root node), replacing the observed indices with ones suggested by the map
	 * returned by getPredicateMapping(). The function of this is to allow concrete
	 * subclasses to create specific mappings from more general ones, e.g. when a
	 * study search query refers to the index "dcterms.identifier", the StudySearchController
	 * can specify that within its context this index is understood to mean "tb.identifier.study",
	 * i.e. the study ID.
	 * @param node
	 * @return
	 */
	protected CQLNode normalizeParseTree(CQLNode node) {
		if ( node instanceof CQLBooleanNode ) {
			((CQLBooleanNode)node).left = normalizeParseTree(((CQLBooleanNode)node).left);
			((CQLBooleanNode)node).right = normalizeParseTree(((CQLBooleanNode)node).right);
			return node;
		}
		else if ( node instanceof CQLTermNode ) {
			String index = ((CQLTermNode)node).getIndex();
			String term = ((CQLTermNode)node).getTerm();
			CQLRelation relation = ((CQLTermNode)node).getRelation();
			Map<String,String> mapping = getPredicateMapping();
			for ( String key : mapping.keySet() ) {
				index = index.replaceAll(key, mapping.get(key));
			}
			return new CQLTermNode(index,relation,term);
		}
		logger.debug(node);
		return node;
	}	
	
	/**
	 * Returns a mapping between more general search indices (e.g. "dcterms.identifier")
	 * and the specific indices (e.g. "tb.identifier.study") that are understood to be used 
	 * by the concrete subclass
	 * @return
	 */
	protected abstract Map<String,String> getPredicateMapping();

	protected ModelAndView onSubmit(
			HttpServletRequest request,
			HttpServletResponse response,
			Object command,
			BindException errors) throws Exception {

		LOGGER.info("in generic SearchController.onSubmit");

		clearMessages(request);
		String formName = request.getParameter("formName");

		LOGGER.info("formName is '" + formName + "'");

		if (formName == null) {
			throw new Error("No form name supplied");
		} else if (formName.equals("resultsAction")) {
			if (request.getParameterMap().containsKey("refineSearch")) {
				// TODO
			} else if (request.getParameterMap().containsKey("discardResults")) {
				onSubmitDiscardResults(request, response, command, errors);
			} else if (request.getParameterMap().containsKey("action")) {
				String action = request.getParameter("action");
				if (action.equals("discardResults")) {
					onSubmitDiscardResults(request, response, command, errors);
				} else if ( action.equals("refineSearch") ){
					onSubmitRefineSearch(request,response,command,errors);					
				} else {
					throw new Error("Unknown action '" + action + "'");
				}
			} else {
				throw new Error("Couldn't find button selection");	
			}
		} else if (formName.equals("searchResultsConversion")) {			
			onSubmitConversion(request, response, command, errors);
		} else if (formName.equals("discardResults")) {
			onSubmitDiscardResults(request, response, command, errors);
		} else {
			throw new Error("Unknown form name '" + formName + "'");
		}
		
		LOGGER.debug("using selectResultsView to determine view");
		return selectResultsView(request);
	}
	
	protected ModelAndView searchResultsAsRDF (SearchResults<?> searchResults,HttpServletRequest request,CQLNode root, String schema, String original) throws UnsupportedEncodingException {
		//${baseURL}
		//${phyloWSPath}
		//${normalizedCQLQuery}
		//${searchResults}
		//${domainAddress}
		String phyloWSPath = "find/" + original;
		request.getSession().setAttribute("searchResultsThawed", searchResults);		
		StringBuffer domainAddress = new StringBuffer("http://");
		domainAddress
			.append(request.getServerName())
			.append(':')
			.append(request.getServerPort());
		StringBuffer baseURL = new StringBuffer(domainAddress);
		baseURL.append("/treebase-web/phylows");
		request.getSession().setAttribute("recordSchema", schema);
		request.getSession().setAttribute("format", request.getParameter("format"));
		request.getSession().setAttribute("baseURL", TreebaseUtil.getPurlBase());
		request.getSession().setAttribute("domainAddress", domainAddress.toString());
		request.getSession().setAttribute("phyloWSPath", phyloWSPath);
		request.getSession().setAttribute("originalSection", original);
		if ( null != root ) {			
			request.getSession().setAttribute("normalizedCQLQuery", URLEncoder.encode(root.toCQL(),"UTF-8"));
		}
		return new ModelAndView("searchResultsAsRDF");
	}
	
	protected <E extends TBPersistable> SearchResults<E> intersectSearchResults(
			SearchResults<E> oldRes,
			SearchResults<E> newRes, 
			SearchMessageSetter searchMessages,
			String noResultsMessage) {
		if (noResultsMessage == null) noResultsMessage = "Your search produced no results";
		
		if (! newRes.isAll()) 
			LOGGER.info("Search returned " + newRes.nItems());
		
		if (oldRes.isAll() || oldRes.isEmpty()) 
			return newRes;
		else if (newRes.isAll()) 
			return oldRes;
		else {
			@SuppressWarnings("unchecked")
			SearchResults<E> intersection = oldRes.clone();
			intersection.intersect(newRes);
			
			if (intersection.isEmpty()) {
{'fraction_non_alphanumeric': 0.11599428532553235, 'fraction_numerical': 0.004081910334036329, 'mean_word_length': 5.181665264928512, 'pattern_counts': {'":': 0, '<': 19, '<?xml version=': 0, '>': 19, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6964943', 'n_tokens_mistral': 4677, 'n_tokens_neox': 4238, 'n_words': 966}