refactoring taxon controllers, reducing code duplication
[cdmlib.git] / cdmlib-remote / src / main / java / eu / etaxonomy / cdm / remote / controller / TaxonListController.java
index 2ea0bb98534cf924e8c46d4ea5cd76edbb9c5f9f..6b8679a91d0c2eb189898bd42f66799e5aab435f 100644 (file)
 /**\r
  * Copyright (C) 2009 EDIT European Distributed Institute of Taxonomy\r
  * http://www.e-taxonomy.eu\r
- * \r
+ *\r
  * The contents of this file are subject to the Mozilla Public License Version\r
  * 1.1 See LICENSE.TXT at the top of this package for the full license terms.\r
  */\r
 \r
 package eu.etaxonomy.cdm.remote.controller;\r
 \r
+import java.io.IOException;\r
+import java.util.ArrayList;\r
+import java.util.Arrays;\r
+import java.util.Collection;\r
+import java.util.EnumSet;\r
+import java.util.HashSet;\r
+import java.util.List;\r
+import java.util.Set;\r
+import java.util.UUID;\r
+\r
+import javax.servlet.http.HttpServletRequest;\r
+import javax.servlet.http.HttpServletResponse;\r
+\r
+import org.apache.commons.lang.BooleanUtils;\r
+import org.apache.lucene.queryParser.ParseException;\r
 import org.springframework.beans.factory.annotation.Autowired;\r
 import org.springframework.stereotype.Controller;\r
+import org.springframework.web.bind.WebDataBinder;\r
+import org.springframework.web.bind.annotation.InitBinder;\r
+import org.springframework.web.bind.annotation.PathVariable;\r
 import org.springframework.web.bind.annotation.RequestMapping;\r
+import org.springframework.web.bind.annotation.RequestMethod;\r
+import org.springframework.web.bind.annotation.RequestParam;\r
 \r
+import eu.etaxonomy.cdm.api.service.IClassificationService;\r
 import eu.etaxonomy.cdm.api.service.ITaxonService;\r
+import eu.etaxonomy.cdm.api.service.ITermService;\r
+import eu.etaxonomy.cdm.api.service.TaxaAndNamesSearchMode;\r
+import eu.etaxonomy.cdm.api.service.config.FindTaxaAndNamesConfiguratorImpl;\r
+import eu.etaxonomy.cdm.api.service.config.IFindTaxaAndNamesConfigurator;\r
+import eu.etaxonomy.cdm.api.service.pager.Pager;\r
+import eu.etaxonomy.cdm.api.service.search.LuceneMultiSearchException;\r
+import eu.etaxonomy.cdm.api.service.search.SearchResult;\r
+import eu.etaxonomy.cdm.model.common.IdentifiableEntity;\r
+import eu.etaxonomy.cdm.model.common.Language;\r
+import eu.etaxonomy.cdm.model.description.DescriptionElementBase;\r
+import eu.etaxonomy.cdm.model.description.Feature;\r
+import eu.etaxonomy.cdm.model.description.PresenceAbsenceTermBase;\r
+import eu.etaxonomy.cdm.model.location.NamedArea;\r
+import eu.etaxonomy.cdm.model.taxon.Classification;\r
+import eu.etaxonomy.cdm.model.taxon.Synonym;\r
+import eu.etaxonomy.cdm.model.taxon.Taxon;\r
 import eu.etaxonomy.cdm.model.taxon.TaxonBase;\r
+import eu.etaxonomy.cdm.persistence.query.MatchMode;\r
+import eu.etaxonomy.cdm.persistence.query.OrderHint;\r
+import eu.etaxonomy.cdm.remote.controller.util.PagerParameters;\r
+import eu.etaxonomy.cdm.remote.editor.DefinedTermBaseList;\r
+import eu.etaxonomy.cdm.remote.editor.TermBaseListPropertyEditor;\r
+import eu.etaxonomy.cdm.remote.editor.UuidList;\r
 \r
 /**\r
+ * TODO write controller documentation\r
+ *\r
  * @author a.kohlbecker\r
  * @date 20.03.2009\r
  */\r
 @Controller\r
-@RequestMapping(value = {"/*/taxon.*", "/*/taxon"})\r
-public class TaxonListController extends BaseListController<TaxonBase, ITaxonService> {\r
-\r
-       /* (non-Javadoc)\r
-        * @see eu.etaxonomy.cdm.remote.controller.BaseListController#setService(eu.etaxonomy.cdm.api.service.IService)\r
-        */\r
-       @Override\r
-       @Autowired\r
-       public void setService(ITaxonService service) {\r
-               this.service = service; \r
-       }       \r
+@RequestMapping(value = {"/taxon"})\r
+public class TaxonListController extends IdentifiableListController<TaxonBase, ITaxonService> {\r
+\r
+\r
+    private static final List<String> SIMPLE_TAXON_INIT_STRATEGY = DEFAULT_INIT_STRATEGY;\r
+    protected List<String> getSimpleTaxonInitStrategy() {\r
+        // TODO Auto-generated method stub\r
+        return SIMPLE_TAXON_INIT_STRATEGY;\r
+    }\r
+\r
+    /**\r
+     *\r
+     */\r
+    public TaxonListController(){\r
+        super();\r
+        setInitializationStrategy(Arrays.asList(new String[]{"$","name.nomenclaturalReference"}));\r
+    }\r
+\r
+    /* (non-Javadoc)\r
+     * @see eu.etaxonomy.cdm.remote.controller.BaseListController#setService(eu.etaxonomy.cdm.api.service.IService)\r
+     */\r
+    @Override\r
+    @Autowired\r
+    public void setService(ITaxonService service) {\r
+        this.service = service;\r
+    }\r
+\r
+    @Autowired\r
+    private IClassificationService classificationService;\r
+\r
+    @Autowired\r
+    private ITermService termService;\r
+\r
+    @InitBinder\r
+    @Override\r
+    public void initBinder(WebDataBinder binder) {\r
+        super.initBinder(binder);\r
+        binder.registerCustomEditor(DefinedTermBaseList.class, new TermBaseListPropertyEditor<NamedArea>(termService));\r
+    }\r
+\r
+    /**\r
+     * Find Taxa, Synonyms, Common Names by name, either globally or in a specific geographic area.\r
+     * <p>\r
+     * URI: <b>&#x002F;{datasource-name}&#x002F;portal&#x002F;taxon&#x002F;find</b>\r
+     *\r
+     * @param query\r
+     *            the string to query for. Since the wildcard character '*'\r
+     *            internally always is appended to the query string, a search\r
+     *            always compares the query string with the beginning of a name.\r
+     *            - <i>required parameter</i>\r
+     * @param treeUuid\r
+     *            the {@link UUID} of a {@link Classification} to which the\r
+     *            search is to be restricted. - <i>optional parameter</i>\r
+     * @param areas\r
+     *            restrict the search to a set of geographic {@link NamedArea}s.\r
+     *            The parameter currently takes a list of TDWG area labels.\r
+     *            - <i>optional parameter</i>\r
+     * @param pageNumber\r
+     *            the number of the page to be returned, the first page has the\r
+     *            pageNumber = 1 - <i>optional parameter</i>\r
+     * @param pageSize\r
+     *            the maximum number of entities returned per page (can be -1\r
+     *            to return all entities in a single page) - <i>optional parameter</i>\r
+     * @param doTaxa\r
+     *            weather to search for instances of {@link Taxon} - <i>optional parameter</i>\r
+     * @param doSynonyms\r
+     *            weather to search for instances of {@link Synonym} - <i>optional parameter</i>\r
+     * @param doTaxaByCommonNames\r
+     *            for instances of {@link Taxon} by a common name used - <i>optional parameter</i>\r
+     * @return a Pager on a list of {@link IdentifiableEntity}s initialized by\r
+     *         the following strategy {@link #SIMPLE_TAXON_INIT_STRATEGY}\r
+     * @throws IOException\r
+     * @throws LuceneMultiSearchException\r
+     * @throws ParseException\r
+     */\r
+    @RequestMapping(method = RequestMethod.GET, value={"search"})\r
+    public Pager<SearchResult<TaxonBase>> doSearch(\r
+            @RequestParam(value = "query", required = true) String query,\r
+            @RequestParam(value = "tree", required = false) UUID treeUuid,\r
+            @RequestParam(value = "area", required = false) DefinedTermBaseList<NamedArea> areaList,\r
+            @RequestParam(value = "status", required = false) Set<PresenceAbsenceTermBase<?>> status,\r
+            @RequestParam(value = "pageNumber", required = false) Integer pageNumber,\r
+            @RequestParam(value = "pageSize", required = false) Integer pageSize,\r
+            @RequestParam(value = "doTaxa", required = false) Boolean doTaxa,\r
+            @RequestParam(value = "doSynonyms", required = false) Boolean doSynonyms,\r
+            @RequestParam(value = "doMisappliedNames", required = false) Boolean doMisappliedNames,\r
+            @RequestParam(value = "doTaxaByCommonNames", required = false) Boolean doTaxaByCommonNames,\r
+            HttpServletRequest request,\r
+            HttpServletResponse response\r
+            )\r
+             throws IOException, ParseException, LuceneMultiSearchException {\r
+\r
+\r
+        logger.info("search : " + requestPathAndQuery(request) );\r
+\r
+        Set<NamedArea> areaSet = null;\r
+        if(areaList != null){\r
+            areaSet = new HashSet<NamedArea>(areaList.size());\r
+            areaSet.addAll(areaList);\r
+            TaxonListController.includeAllSubAreas(areaSet, termService);\r
+        }\r
+\r
+        PagerParameters pagerParams = new PagerParameters(pageSize, pageNumber);\r
+        pagerParams.normalizeAndValidate(response);\r
+\r
+        // TODO change type of do* parameters  to TaxaAndNamesSearchMode\r
+        EnumSet<TaxaAndNamesSearchMode> searchModes = EnumSet.noneOf(TaxaAndNamesSearchMode.class);\r
+        if(BooleanUtils.toBoolean(doTaxa)) {\r
+            searchModes.add(TaxaAndNamesSearchMode.doTaxa);\r
+        }\r
+        if(BooleanUtils.toBoolean(doSynonyms)) {\r
+            searchModes.add(TaxaAndNamesSearchMode.doSynonyms);\r
+        }\r
+        if(BooleanUtils.toBoolean(doMisappliedNames)) {\r
+            searchModes.add(TaxaAndNamesSearchMode.doMisappliedNames);\r
+        }\r
+        if(BooleanUtils.toBoolean(doTaxaByCommonNames)) {\r
+            searchModes.add(TaxaAndNamesSearchMode.doTaxaByCommonNames);\r
+        }\r
+\r
+        Classification classification = null;\r
+        if(treeUuid != null){\r
+            classification = classificationService.find(treeUuid);\r
+        }\r
+\r
+        return service.findTaxaAndNamesByFullText(searchModes, query,\r
+                classification, areaSet, status, null,\r
+                false, pagerParams.getPageSize(), pagerParams.getPageIndex(),\r
+                OrderHint.NOMENCLATURAL_SORT_ORDER, getSimpleTaxonInitStrategy());\r
+    }\r
+\r
+    /**\r
+     * Find Taxa, Synonyms, Common Names by name, either globally or in a specific geographic area.\r
+     * <p>\r
+     * URI: <b>&#x002F;{datasource-name}&#x002F;portal&#x002F;taxon&#x002F;find</b>\r
+     *\r
+     * @param query\r
+     *            the string to query for. Since the wildcard character '*'\r
+     *            internally always is appended to the query string, a search\r
+     *            always compares the query string with the beginning of a name.\r
+     *            - <i>required parameter</i>\r
+     * @param treeUuid\r
+     *            the {@link UUID} of a {@link Classification} to which the\r
+     *            search is to be restricted. - <i>optional parameter</i>\r
+     * @param areas\r
+     *            restrict the search to a set of geographic {@link NamedArea}s.\r
+     *            The parameter currently takes a list of TDWG area labels.\r
+     *            - <i>optional parameter</i>\r
+     * @param pageNumber\r
+     *            the number of the page to be returned, the first page has the\r
+     *            pageNumber = 1 - <i>optional parameter</i>\r
+     * @param pageSize\r
+     *            the maximum number of entities returned per page (can be -1\r
+     *            to return all entities in a single page) - <i>optional parameter</i>\r
+     * @param doTaxa\r
+     *            weather to search for instances of {@link Taxon} - <i>optional parameter</i>\r
+     * @param doSynonyms\r
+     *            weather to search for instances of {@link Synonym} - <i>optional parameter</i>\r
+     * @param doTaxaByCommonNames\r
+     *            for instances of {@link Taxon} by a common name used - <i>optional parameter</i>\r
+     * @param matchMode\r
+     *           valid values are "EXACT", "BEGINNING", "ANYWHERE", "END" (case sensitive !!!)\r
+     * @return a Pager on a list of {@link IdentifiableEntity}s initialized by\r
+     *         the following strategy {@link #SIMPLE_TAXON_INIT_STRATEGY}\r
+     * @throws IOException\r
+     */\r
+    @RequestMapping(method = RequestMethod.GET, value={"find"})\r
+    public Pager<IdentifiableEntity> doFind(\r
+            @RequestParam(value = "query", required = true) String query,\r
+            @RequestParam(value = "tree", required = false) UUID treeUuid,\r
+            @RequestParam(value = "area", required = false) Set<NamedArea> areas,\r
+            @RequestParam(value = "pageNumber", required = false) Integer pageNumber,\r
+            @RequestParam(value = "pageSize", required = false) Integer pageSize,\r
+            @RequestParam(value = "doTaxa", required = false) Boolean doTaxa,\r
+            @RequestParam(value = "doSynonyms", required = false) Boolean doSynonyms,\r
+            @RequestParam(value = "doMisappliedNames", required = false) Boolean doMisappliedNames,\r
+            @RequestParam(value = "doTaxaByCommonNames", required = false) Boolean doTaxaByCommonNames,\r
+            @RequestParam(value = "matchMode", required = false) MatchMode matchMode,\r
+            HttpServletRequest request,\r
+            HttpServletResponse response\r
+            )\r
+             throws IOException {\r
+\r
+\r
+        logger.info("find : " + request.getRequestURI() + "?" + request.getQueryString() );\r
+\r
+        PagerParameters pagerParams = new PagerParameters(pageSize, pageNumber);\r
+        pagerParams.normalizeAndValidate(response);\r
+\r
+        IFindTaxaAndNamesConfigurator config = new FindTaxaAndNamesConfiguratorImpl();\r
+        config.setPageNumber(pagerParams.getPageIndex());\r
+        config.setPageSize(pagerParams.getPageSize());\r
+        config.setTitleSearchString(query);\r
+        config.setDoTaxa(doTaxa!= null ? doTaxa : Boolean.FALSE );\r
+        config.setDoSynonyms(doSynonyms != null ? doSynonyms : Boolean.FALSE );\r
+        config.setDoMisappliedNames(doMisappliedNames != null ? doMisappliedNames : Boolean.FALSE);\r
+        config.setDoTaxaByCommonNames(doTaxaByCommonNames != null ? doTaxaByCommonNames : Boolean.FALSE );\r
+        config.setMatchMode(matchMode != null ? matchMode : MatchMode.BEGINNING);\r
+        config.setTaxonPropertyPath(getSimpleTaxonInitStrategy());\r
+        config.setNamedAreas(areas);\r
+        if(treeUuid != null){\r
+            Classification classification = classificationService.find(treeUuid);\r
+            config.setClassification(classification);\r
+        }\r
+\r
+        return service.findTaxaAndNames(config);\r
+\r
+    }\r
+\r
+\r
+    /**\r
+     * @param clazz\r
+     * @param queryString\r
+     * @param treeUuid TODO unimplemented in TaxonServiceImpl !!!!\r
+     * @param languages\r
+     * @param pageNumber\r
+     * @param pageSize\r
+     * @param request\r
+     * @param response\r
+     * @return\r
+     * @throws IOException\r
+     * @throws ParseException\r
+     */\r
+    @RequestMapping(method = RequestMethod.GET, value={"findByDescriptionElementFullText"})\r
+    public Pager<SearchResult<TaxonBase>> dofindByDescriptionElementFullText(\r
+            @RequestParam(value = "clazz", required = false) Class<? extends DescriptionElementBase> clazz,\r
+            @RequestParam(value = "query", required = true) String queryString,\r
+            @RequestParam(value = "tree", required = false) UUID treeUuid,\r
+            @RequestParam(value = "features", required = false) UuidList featureUuids,\r
+            @RequestParam(value = "languages", required = false) List<Language> languages,\r
+            @RequestParam(value = "hl", required = false) Boolean highlighting,\r
+            @RequestParam(value = "pageNumber", required = false) Integer pageNumber,\r
+            @RequestParam(value = "pageSize", required = false) Integer pageSize,\r
+            HttpServletRequest request,\r
+            HttpServletResponse response\r
+            )\r
+             throws IOException, ParseException {\r
+\r
+         logger.info("findByDescriptionElementFullText : " + requestPathAndQuery(request) );\r
+\r
+         PagerParameters pagerParams = new PagerParameters(pageSize, pageNumber);\r
+         pagerParams.normalizeAndValidate(response);\r
+\r
+         if(highlighting == null){\r
+             highlighting = false;\r
+         }\r
+\r
+         Classification classification = null;\r
+        if(treeUuid != null){\r
+            classification = classificationService.find(treeUuid);\r
+        }\r
+\r
+        List<Feature> features = null;\r
+        if(featureUuids != null){\r
+            features = new ArrayList<Feature>(featureUuids.size());\r
+            for(UUID uuid : featureUuids){\r
+                features.add((Feature) termService.find(uuid));\r
+            }\r
+        }\r
+\r
+        Pager<SearchResult<TaxonBase>> pager = service.findByDescriptionElementFullText(\r
+                clazz, queryString, classification, features, languages, highlighting,\r
+                pagerParams.getPageSize(), pagerParams.getPageIndex(),\r
+                ((List<OrderHint>)null), getSimpleTaxonInitStrategy());\r
+        return pager;\r
+    }\r
+\r
+    @RequestMapping(method = RequestMethod.GET, value={"findByFullText"})\r
+    public Pager<SearchResult<TaxonBase>> dofindByFullText(\r
+            @RequestParam(value = "clazz", required = false) Class<? extends TaxonBase> clazz,\r
+            @RequestParam(value = "query", required = true) String queryString,\r
+            @RequestParam(value = "tree", required = false) UUID treeUuid,\r
+            @RequestParam(value = "languages", required = false) List<Language> languages,\r
+            @RequestParam(value = "hl", required = false) Boolean highlighting,\r
+            @RequestParam(value = "pageNumber", required = false) Integer pageNumber,\r
+            @RequestParam(value = "pageSize", required = false) Integer pageSize,\r
+            HttpServletRequest request,\r
+            HttpServletResponse response\r
+            )\r
+             throws IOException, ParseException {\r
+\r
+         logger.info("findByFullText : " + requestPathAndQuery(request)  );\r
+\r
+         PagerParameters pagerParams = new PagerParameters(pageSize, pageNumber);\r
+         pagerParams.normalizeAndValidate(response);\r
+\r
+         if(highlighting == null){\r
+             highlighting = false;\r
+         }\r
+\r
+         Classification classification = null;\r
+        if(treeUuid != null){\r
+            classification = classificationService.find(treeUuid);\r
+        }\r
+\r
+        Pager<SearchResult<TaxonBase>> pager = service.findByFullText(clazz, queryString, classification, languages,\r
+                highlighting, pagerParams.getPageSize(), pagerParams.getPageIndex(), ((List<OrderHint>)  null),\r
+                initializationStrategy);\r
+        return pager;\r
+    }\r
+\r
+    @RequestMapping(method = RequestMethod.GET, value={"findByEverythingFullText"})\r
+    public Pager<SearchResult<TaxonBase>> dofindByEverythingFullText(\r
+            @RequestParam(value = "clazz", required = false) Class<? extends TaxonBase> clazz,\r
+            @RequestParam(value = "query", required = true) String queryString,\r
+            @RequestParam(value = "tree", required = false) UUID treeUuid,\r
+            @RequestParam(value = "languages", required = false) List<Language> languages,\r
+            @RequestParam(value = "hl", required = false) Boolean highlighting,\r
+            @RequestParam(value = "pageNumber", required = false) Integer pageNumber,\r
+            @RequestParam(value = "pageSize", required = false) Integer pageSize,\r
+            HttpServletRequest request,\r
+            HttpServletResponse response\r
+            )\r
+             throws IOException, ParseException, LuceneMultiSearchException {\r
+\r
+         logger.info("findByEverythingFullText : " + requestPathAndQuery(request) );\r
+\r
+         PagerParameters pagerParams = new PagerParameters(pageSize, pageNumber);\r
+         pagerParams.normalizeAndValidate(response);\r
+\r
+         if(highlighting == null){\r
+             highlighting = false;\r
+         }\r
+\r
+         Classification classification = null;\r
+        if(treeUuid != null){\r
+            classification = classificationService.find(treeUuid);\r
+        }\r
+\r
+        Pager<SearchResult<TaxonBase>> pager = service.findByEverythingFullText(\r
+                queryString, classification, languages, highlighting,\r
+                pagerParams.getPageSize(), pagerParams.getPageIndex(),\r
+                ((List<OrderHint>)null), initializationStrategy);\r
+        return pager;\r
+    }\r
+\r
+    /**\r
+     * @param areaSet\r
+     */\r
+    static public void includeAllSubAreas(Set<NamedArea> areaSet, ITermService termService) {\r
+        Collection<NamedArea> tmpAreas = new HashSet<NamedArea>(areaSet);\r
+        // expand all areas to include also the sub areas\r
+        Pager<NamedArea> pager = null;\r
+        while(true){\r
+            pager = termService.getIncludes(tmpAreas, 1000, null, null);\r
+            if(pager.getCount() == 0){\r
+                break;\r
+            }\r
+            tmpAreas = pager.getRecords();\r
+            tmpAreas.removeAll(areaSet);\r
+            areaSet.addAll(tmpAreas);\r
+        }\r
+    }\r
+\r
+    @RequestMapping(value = "bestMatchingTaxon/{taxonName}", method = RequestMethod.GET)\r
+    public TaxonBase doFindBestMatchingTaxon(\r
+             @PathVariable("taxonName") String taxonName,\r
+            HttpServletRequest request,\r
+            HttpServletResponse response)throws IOException {\r
+\r
+        Taxon bestMatchingTaxon =  service.findBestMatchingTaxon(taxonName);\r
+\r
+        return bestMatchingTaxon;\r
+    }\r
+\r
 }
\ No newline at end of file