//=== Copyright (C) 2001-2005 Food and Agriculture Organization of the
//=== United Nations (FAO-UN), United Nations World Food Programme (WFP)
//=== and United Nations Environment Programme (UNEP)
//===
//=== This program is free software; you can redistribute it and/or modify
//=== it under the terms of the GNU General Public License as published by
//=== the Free Software Foundation; either version 2 of the License, or (at
//=== your option) any later version.
//===
//=== This program 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
//=== General Public License for more details.
//===
//=== You should have received a copy of the GNU General Public License
//=== along with this program; if not, write to the Free Software
//=== Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//===
//=== Contact: Jeroen Ticheler - FAO - Viale delle Terme di Caracalla 2,
//=== Rome - Italy. email: GeoNetwork@fao.org
//==============================================================================
package org.fao.geonet.kernel.search;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import jeeves.server.context.ServiceContext;
import org.fao.geonet.Util;
import org.fao.geonet.constants.Geonet;
import org.fao.geonet.kernel.KeywordBean;
import org.fao.geonet.kernel.Thesaurus;
import org.fao.geonet.kernel.ThesaurusFinder;
import org.fao.geonet.kernel.rdf.Query;
import org.fao.geonet.kernel.rdf.QueryBuilder;
import org.fao.geonet.kernel.rdf.Selectors;
import org.fao.geonet.kernel.rdf.Wheres;
import org.fao.geonet.kernel.search.keyword.KeywordRelation;
import org.fao.geonet.kernel.search.keyword.KeywordSearchParams;
import org.fao.geonet.kernel.search.keyword.KeywordSearchParamsBuilder;
import org.fao.geonet.languages.IsoLanguagesMapper;
import org.jdom.Element;
/**
*
* Select entries from SKOS thesauri.
*
*/
public class KeywordsSearcher {
private final ThesaurusFinder _thesaurusFinder;
private final IsoLanguagesMapper _isoLanguageMapper;
private List<KeywordBean> _results = new ArrayList<KeywordBean>();
/**
* Create a new searcher
*
* @param mapper the language mapper to use
* @param thesaurusFinder the object for looking up thesauri
*/
public KeywordsSearcher(IsoLanguagesMapper mapper, ThesaurusFinder thesaurusFinder) {
_thesaurusFinder = thesaurusFinder;
this._isoLanguageMapper = mapper;
}
public KeywordsSearcher(ServiceContext context, ThesaurusFinder thesaurusFinder) {
this(context.getBean(IsoLanguagesMapper.class), thesaurusFinder);
}
/**
* Based on the id (code) and the thesaurus name/key a keyword will be read from the thesaurus
* The translations read will be those provided in the lang params.
*
* All the translation will be in the Keyword even if not present in the thesaurus. If a language is not in the
* thesaurus then an empty string will be in the KeywordBean
*
* @param id id of the keyword to read. The id is a URI with the format: ns#id. For example: http://somesite.com#4
* @param sThesaurusName the key that identifies a thesaurus. usually of form type.category.thesaurus. It is obtained by calling {@link Thesaurus#getKey()}
* @param languages translations to load into the keyword bean
*
* @return keywordbean with the requested translations
*
* @throws Exception
*/
public KeywordBean searchById(String id, String sThesaurusName, String... languages) throws Exception {
Query<KeywordBean> query = QueryBuilder.keywordQueryBuilder(_isoLanguageMapper, languages).where(Wheres.ID(id)).build();
Thesaurus thesaurus = _thesaurusFinder.getThesaurusByName(sThesaurusName);
List<KeywordBean> kws = query.execute(thesaurus);
if(kws.isEmpty()) {
return null;
} else {
return kws.get(0);
}
}
public void search(KeywordSearchParams params) throws Exception {
this._results = params.search(_thesaurusFinder);
}
/**
* TODO javadoc.
*
* @param srvContext servicecontext
* @param params params
* @throws Exception hmm
*/
public void search(String contextLanguage, Element params) throws Exception {
if(contextLanguage == null) {
contextLanguage = Geonet.DEFAULT_LANGUAGE;
}
KeywordSearchParamsBuilder paramsBuilder = KeywordSearchParamsBuilder.createFromElement(_isoLanguageMapper, params);
if(paramsBuilder.getLangs().isEmpty()) {
paramsBuilder.addLang(contextLanguage);
}
search(paramsBuilder.build());
}
/**
* Find keywords that are related to the keyword with the provided ID.
*
* the parameters have to have 2 children.
* <ul>
* <li>id - the id/uri of the keyword to find</li>
* <li>thesaurus - the id/key of the thesaurus to search</li>
* <ul>
*
* @param params parameters
* @param request request
* @param extraLangs the languages to load
* @throws Exception hmm
*/
public void searchForRelated(Element params, KeywordRelation request, String... languages) throws Exception {
//System.out.println("KeywordsSearcher searchBN");
// TODO : Add geonetinfo elements.
String id = Util.getParam(params, "id");
String sThesaurusName = Util.getParam(params, "thesaurus");
searchForRelated(id, sThesaurusName, request, languages);
}
/**
* Find keywords that are related to the keyword with the provided ID
*
* @param id id
* @param sThesaurusName thesaurus name
* @param request request
* @param languages the languages to load
* @throws Exception hmm
*/
public void searchForRelated(String id, String sThesaurusName, KeywordRelation request, String... languages) throws Exception {
_results.clear();
Thesaurus thesaurus = _thesaurusFinder.getThesaurusByName(sThesaurusName);
Query<KeywordBean> query = QueryBuilder.keywordQueryBuilder(_isoLanguageMapper, languages).select(Selectors.related(id,request), true).build();
for (KeywordBean keywordBean : query.execute(thesaurus)) {
_results.add(keywordBean);
}
}
/**
* Return the number of results
*
* @return number of results
*/
public int getNbResults() {
return _results.size();
}
/**
* Sort the results according to the comparator. Build in comparators can be found in the {@link org.fao.geonet.kernel.search.keyword.KeywordSort} class
*
* @param comparator the comparator to use for sorting
*/
public void sortResults(Comparator<KeywordBean> comparator) {
Collections.sort(_results, comparator);
}
public List<KeywordBean> getResults() {
return _results;
}
/**
* Formats the keywords as XML and returns root (descKeys) Element
*
* General structure of Xml is defined in the {@link #toRawElement(Element, KeywordBean)} method. The root element is descKeys
*
* @return element
* @throws Exception hmm
*/
public Element getXmlResults() throws Exception {
Element elDescKeys = new Element("descKeys");
int nbResults = this.getNbResults();
//for (int i = from; i <= to; i++) {
for (int i = 0; i <= nbResults - 1; i++) {
KeywordBean kb = _results.get(i);
toRawElement(elDescKeys, kb);
}
return elDescKeys;
}
/**
* Convert the keyword to xml in the following format:
*
* <descKeys>
* <keyword>
* <selected>true/false</selected>
* <id>id valid only for this search</id>
* <uri>general uri id that is always valid</uri>
* <value>label in default lang (first lang that was defined in search params</value>
* <definition>definition in default lang (first lang that was defined in search params</definition>
* <defaultLang>the default language of the bean. It is the 3 letter language code</defaulLang>
* <thesaurus>Thesaurus that the keyword belongs to</thesaurus>
* <values>
* <value language="3 letter language code">value for this translation</value>
* ...
* </values>
* <definitions>
* <definition language="3 letter language code">value for this translation</definition>
* </definitions>
* <descKeys>
* @param rootEl the element to add the xml data to.
* @param kb the keyword to convert
* @return rootEl with the new keyword data attached
*/
public static Element toRawElement(Element rootEl, KeywordBean kb) {
Element elKeyword = new Element("keyword");
Element elSelected = new Element("selected");
// TODO : Add Thesaurus name
if (kb.isSelected()) {
elSelected.addContent("true");
} else {
elSelected.addContent("false");
}
String defaultLang = kb.getDefaultLang();
elKeyword.addContent(elSelected);
elKeyword.addContent(new Element("id").addContent(Integer.toString(kb.getId())));
elKeyword.addContent(new Element("value").addContent(kb.getDefaultValue()).setAttribute("language", defaultLang));
elKeyword.addContent(new Element("definition").addContent(kb.getDefaultDefinition()).setAttribute("language", defaultLang));
elKeyword.addContent(new Element("defaultLang").addContent(defaultLang));
Element thesaurusElement = new Element("thesaurus");
thesaurusElement.addContent(new Element("key").setText(kb.getThesaurusKey()));
thesaurusElement.addContent(new Element("title").setText(kb.getThesaurusTitle()));
thesaurusElement.addContent(new Element("date").setText(kb.getThesaurusDate()));
thesaurusElement.addContent(new Element("type").setText(kb.getType()));
thesaurusElement.addContent(new Element("url").setText(kb.getDownloadUrl()));
elKeyword.addContent(thesaurusElement);
elKeyword.addContent(new Element("uri").addContent(kb.getUriCode()));
addBbox(kb, elKeyword);
rootEl.addContent(elKeyword);
elKeyword.addContent(addAllTranslations(kb, kb.getValues(), "values", "value"));
elKeyword.addContent(addAllTranslations(kb, kb.getDefinitions(), "definitions", "definition"));
return rootEl;
}
private static Element addAllTranslations(KeywordBean kb, Map<String, String> map, String rootElemName, String leafElemName) {
Element values = new Element(rootElemName);
for (Map.Entry<String, String> entry : map.entrySet()) {
values.addContent(new Element(leafElemName).addContent(entry.getValue()).setAttribute("language", entry.getKey()));
}
return values;
}
/**
* Gets all children with node name pIdKeyword and toggles the selection flag on the keywords with matching ids. Id can be either UI or int id
*
* @param params parameters with pIdKeyword children
*/
public void selectUnselectKeywords(Element params) {
@SuppressWarnings("unchecked")
List<Element> listIdKeywordsSelected = params.getChildren("pIdKeyword");
Set<String> ids = new HashSet<String>();
for (Element el : listIdKeywordsSelected) {
ids.add(el.getTextTrim());
}
selectUnselectKeywords(ids);
}
/**
* Toggles Selection on all keywords with either the uri or int id that matches one of the ids provided
*
* @param idSet to toggleSelection
*/
public void selectUnselectKeywords(Set<String> idSet) {
for (KeywordBean keyword : _results) {
if(idSet.contains(""+keyword.getId()) || idSet.contains(keyword.getUriCode())) {
keyword.setSelected(!keyword.isSelected());
}
}
}
public void clearSelection() {
for (KeywordBean keyword : _results) {
keyword.setSelected(false);
}
}
/**
* Format selected keywords and return them as XML
*
* @return an element describing the list of selected keywords
*/
public Element getSelectedKeywordsAsXml() {
Element elDescKeys = new Element("descKeys");
int nbSelectedKeywords = 0;
for (int i = 0; i < this.getNbResults(); i++) {
KeywordBean kb = _results.get(i);
if (kb.isSelected()) {
toRawElement(elDescKeys, kb);
nbSelectedKeywords++;
}
}
Element elNbTot = new Element("nbtot");
elNbTot.addContent(Integer.toString(nbSelectedKeywords));
elDescKeys.addContent(elNbTot);
return elDescKeys;
}
/**
* Adds bounding box of keyword if one available.
*
* @param kb The keyword to analyze.
* @param elKeyword The XML fragment to update.
*/
private static void addBbox(KeywordBean kb, Element elKeyword) {
if (kb.getCoordEast() != null && kb.getCoordWest() != null
&& kb.getCoordSouth() != null
&& kb.getCoordNorth() != null && !kb.getCoordEast().equals("")
&& !kb.getCoordWest().equals("")
&& !kb.getCoordSouth().equals("")
&& !kb.getCoordNorth().equals("")) {
Element elBbox = new Element("geo");
Element elEast = new Element("east");
elEast.addContent(kb.getCoordEast());
Element elWest = new Element("west");
elWest.addContent(kb.getCoordWest());
Element elSouth = new Element("south");
elSouth.addContent(kb.getCoordSouth());
Element elNorth = new Element("north");
elNorth.addContent(kb.getCoordNorth());
elBbox.addContent(elEast);
elBbox.addContent(elWest);
elBbox.addContent(elSouth);
elBbox.addContent(elNorth);
elKeyword.addContent(elBbox);
}
}
/**
* Traverse the list of results and create a new list with all the
*
* @return list of keywordbeans
*/
public List<KeywordBean> getSelectedKeywordsInList() {
List<KeywordBean> keywords = new ArrayList<KeywordBean>();
for (int i = 0; i < this.getNbResults(); i++) {
KeywordBean kb = _results.get(i);
if (kb.isSelected()) {
keywords.add(kb);
}
}
return keywords;
}
/**
* find the keyword with provided ID
*
* @param id integer id (not URI) of keyword
*
* @return keywordbean
*/
public KeywordBean getKeywordFromResultsById(int id) {
for (KeywordBean kb : _results) {
if (kb.getId() == id) {
return kb;
}
}
return null;
}
/**
* find the keyword with provided ID
*
* @param id integer id (not URI) of keyword
*
* @return keywordbean
*/
public KeywordBean getKeywordFromResultsById(String id) {
return getKeywordFromResultsById(Integer.parseInt(id));
}
/**
* find the keyword with provided code/uri
*
* @param code uri of the keyword
*
* @return keywordbean
*/
public KeywordBean getKeywordFromResultsByUriCode(String code) {
for (KeywordBean kb : _results) {
if (kb.getUriCode().equals(code)) {
return kb;
}
}
return null;
}
}