Merge branch 'master' into abcd
[cdmlib.git] / cdmlib-remote / src / main / java / eu / etaxonomy / cdm / remote / controller / DescriptionPortalController.java
index e3fcbf996d5dd0800d0b6cf7fba5b81b32a98de5..a6979d58e00a58405b0f1ae90bc0334dea692fe4 100644 (file)
 \r
 package eu.etaxonomy.cdm.remote.controller;\r
 \r
+import java.awt.Color;\r
 import java.io.IOException;\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.Map;\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.ObjectUtils;\r
+import org.codehaus.jackson.JsonParseException;\r
+import org.codehaus.jackson.map.JsonMappingException;\r
 import org.springframework.beans.factory.annotation.Autowired;\r
 import org.springframework.stereotype.Controller;\r
+import org.springframework.transaction.annotation.Transactional;\r
 import org.springframework.web.bind.WebDataBinder;\r
 import org.springframework.web.bind.annotation.InitBinder;\r
-import org.springframework.web.bind.annotation.ModelAttribute;\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
+import org.springframework.web.servlet.ModelAndView;\r
+\r
+import com.wordnik.swagger.annotations.Api;\r
 \r
-import eu.etaxonomy.cdm.api.service.AnnotatableServiceBase;\r
-import eu.etaxonomy.cdm.api.service.DescriptionServiceImpl;\r
 import eu.etaxonomy.cdm.api.service.DistributionTree;\r
 import eu.etaxonomy.cdm.api.service.IDescriptionService;\r
-import eu.etaxonomy.cdm.api.service.IFeatureTreeService;\r
-import eu.etaxonomy.cdm.api.service.NamedAreaTree;\r
+import eu.etaxonomy.cdm.api.service.ITermService;\r
+import eu.etaxonomy.cdm.api.service.dto.DistributionInfoDTO;\r
+import eu.etaxonomy.cdm.api.service.dto.DistributionInfoDTO.InfoPart;\r
 import eu.etaxonomy.cdm.api.service.pager.Pager;\r
+import eu.etaxonomy.cdm.api.utility.DescriptionUtility;\r
+import eu.etaxonomy.cdm.ext.geo.CondensedDistributionRecipe;\r
+import eu.etaxonomy.cdm.ext.geo.EditGeoServiceUtilities;\r
+import eu.etaxonomy.cdm.ext.geo.IEditGeoService;\r
 import eu.etaxonomy.cdm.model.common.Annotation;\r
+import eu.etaxonomy.cdm.model.common.Marker;\r
+import eu.etaxonomy.cdm.model.common.MarkerType;\r
 import eu.etaxonomy.cdm.model.description.DescriptionBase;\r
 import eu.etaxonomy.cdm.model.description.DescriptionElementBase;\r
-import eu.etaxonomy.cdm.model.description.FeatureTree;\r
+import eu.etaxonomy.cdm.model.description.PresenceAbsenceTerm;\r
 import eu.etaxonomy.cdm.model.description.TaxonDescription;\r
 import eu.etaxonomy.cdm.model.location.NamedAreaLevel;\r
+import eu.etaxonomy.cdm.remote.editor.DefinedTermBaseList;\r
 import eu.etaxonomy.cdm.remote.editor.NamedAreaLevelPropertyEditor;\r
-\r
+import eu.etaxonomy.cdm.remote.editor.TermBaseListPropertyEditor;\r
 import eu.etaxonomy.cdm.remote.editor.UUIDListPropertyEditor;\r
-import eu.etaxonomy.cdm.remote.editor.UUIDPropertyEditor;\r
 import eu.etaxonomy.cdm.remote.editor.UuidList;\r
+import eu.etaxonomy.cdm.remote.l10n.LocaleContext;\r
 \r
 /**\r
- * TODO write controller documentation\r
+ * IMPORTANT:\r
+ *\r
+ * This controller is mostly a 1:1 copy of the DescriptionController\r
+ * and this provides identical end points which only differ in the depth of the\r
+ * object graphs returned.\r
  *\r
  * @author a.kohlbecker\r
- * @date 24.03.2009\r
+ * @date Jun 25, 2013\r
+ *\r
  */\r
-\r
 @Controller\r
-@RequestMapping(value = {"/portal/description/{uuid}", "/portal/description/{uuid_list}", "/portal/descriptionElement/{descriptionelement_uuid}", "/portal/featureTree/{featuretree_uuid}"})\r
+@Api("portal_description")\r
+@Transactional(readOnly=true)\r
+@RequestMapping(value = {\r
+            "/portal/description/{uuid}",\r
+            "/portal/description/{uuid_list}",\r
+            "/portal/descriptionElement/{descriptionelement_uuid}"})\r
 public class DescriptionPortalController extends BaseController<DescriptionBase, IDescriptionService>\r
 {\r
-    @Autowired\r
-    private IFeatureTreeService featureTreeService;\r
-\r
-    private static final List<String> FEATURETREE_INIT_STRATEGY = Arrays.asList(\r
-            new String[]{\r
-                "representations",\r
-                "root.feature.representations",\r
-                "root.children.feature.representations",\r
-            });\r
-    private static final List<String> DESCRIPTIONS_DISTRIBUTION_INIT_STRATEGY = Arrays.asList(new String []{\r
-            "elements.sources.citation.$",\r
-            "elements.area.$",\r
-            });\r
-    protected static final List<String> TAXONDESCRIPTION_INIT_STRATEGY = Arrays.asList(new String []{\r
+\r
+    protected static final List<String> DESCRIPTION_INIT_STRATEGY = Arrays.asList(new String []{\r
             "$",\r
             "elements.$",\r
-            "elements.sources.citation.authorTeam.$",\r
-            "elements.sources.nameUsedInSource.originalNameString",\r
+            "elements.multilanguageText.*",\r
+            "elements.annotations",\r
+            "elements.sources.citation.authorship.$",\r
+            "elements.sources.nameUsedInSource",\r
             "elements.area.level",\r
             "elements.modifyingText",\r
+            "elements.stateData.*",\r
+            "elements.statisticalValues.*",\r
+            "elements.unit",\r
+            "elements.media",\r
+            "elements.kindOfUnit"\r
+\r
     });\r
 \r
+    protected static final List<String> ORDERED_DISTRIBUTION_INIT_STRATEGY = Arrays.asList(new String []{\r
+            "elements.$",\r
+            "elements.annotations",\r
+            "elements.markers",\r
+            "elements.sources.citation.authorship.$",\r
+            "elements.sources.nameUsedInSource",\r
+            "elements.area.level",\r
+    });\r
+\r
+    protected static final List<String> DISTRIBUTION_INFO_INIT_STRATEGY = Arrays.asList(new String []{\r
+            "sources.citation.authorship.$",\r
+            "sources.nameUsedInSource",\r
+            "annotations"\r
+    });\r
+\r
+    @Autowired\r
+    private ITermService termService;\r
+\r
+    @Autowired\r
+    private IEditGeoService geoService;\r
+\r
 \r
     public DescriptionPortalController() {\r
         super();\r
-        setInitializationStrategy(TAXONDESCRIPTION_INIT_STRATEGY);\r
+        setInitializationStrategy(DESCRIPTION_INIT_STRATEGY);\r
     }\r
 \r
     @InitBinder\r
@@ -95,6 +135,7 @@ public class DescriptionPortalController extends BaseController<DescriptionBase,
         super.initBinder(binder);\r
         binder.registerCustomEditor(UuidList.class, new UUIDListPropertyEditor());\r
         binder.registerCustomEditor(NamedAreaLevel.class, new NamedAreaLevelPropertyEditor());\r
+        binder.registerCustomEditor(DefinedTermBaseList.class, new TermBaseListPropertyEditor<MarkerType>(termService));\r
     }\r
 \r
     /* (non-Javadoc)\r
@@ -106,53 +147,122 @@ public class DescriptionPortalController extends BaseController<DescriptionBase,
         this.service = service;\r
     }\r
 \r
-    /**\r
-     * TODO write controller method documentation\r
-     *\r
-     * @param request\r
-     * @param response\r
-     * @return\r
-     * @throws IOException\r
-     */\r
-\r
-    @RequestMapping(value = {"/portal/featureTree/{featuretree_uuid}"}, method = RequestMethod.GET)\r
-    public FeatureTree doGetFeatureTree(\r
-            @PathVariable("featuretree_uuid") UUID featureUuid,\r
-            HttpServletRequest request,\r
-            HttpServletResponse response)throws IOException {\r
-        //UUID featureTreeUuid = readValueUuid(request, null);\r
-        FeatureTree featureTree = featureTreeService.load(featureUuid, FEATURETREE_INIT_STRATEGY);\r
-        if(featureTree == null){\r
-            HttpStatusMessage.UUID_NOT_FOUND.send(response);\r
-        }\r
-        return featureTree;\r
-    }\r
-\r
-    @RequestMapping(value = "/portal/descriptionElement/{descriptionelement_uuid}/annotation", method = RequestMethod.GET)\r
+    @RequestMapping(value = "//portal/descriptionElement/{descriptionelement_uuid}/annotation", method = RequestMethod.GET) // mapped as absolute path, see CdmAntPathMatcher\r
     public Pager<Annotation> getAnnotations(\r
             @PathVariable("descriptionelement_uuid") UUID uuid,\r
             HttpServletRequest request,\r
             HttpServletResponse response) throws IOException {\r
-        logger.info("getAnnotations() - " + request.getServletPath());\r
+        logger.info("getAnnotations() - " + requestPathAndQuery(request) );\r
         DescriptionElementBase annotatableEntity = service.getDescriptionElementByUuid(uuid);\r
-        Pager<Annotation> annotations = service.getDescriptionElementAnnotations(annotatableEntity, null, null, 0, null, DEFAULT_INIT_STRATEGY);\r
+        Pager<Annotation> annotations = service.getDescriptionElementAnnotations(annotatableEntity, null, null, 0, null, getInitializationStrategy());\r
         return annotations;\r
     }\r
 \r
-    @RequestMapping(value = "/portal/description/{uuid_list}/DistributionTree", method = RequestMethod.GET)\r
+    /**\r
+     * NOTICE: required to have a TreeNodeBeanProcessor configured which suppresses the\r
+     * redundant output of distribution.area\r
+     *\r
+     * @param descriptionUuidList\r
+     * @param subAreaPreference\r
+     *            enables the <b>Sub area preference rule</b> if set to true,\r
+     *            see {@link DescriptionUtility#filterDistributions(Collection, boolean, boolean}\r
+     * @param statusOrderPreference\r
+     *            enables the <b>Status order preference rule</b> if set to true,\r
+     *            see {@link DescriptionUtility#filterDistributions(Collection, boolean, boolean}\r
+     * @param hideMarkedAreas\r
+     *            distributions where the area has a {@link Marker} with one of\r
+     *            the specified {@link MarkerType}s will be skipped, see\r
+     *            {@link DescriptionUtility#filterDistributions(Collection, boolean, boolean, Set)}\r
+     * @param omitLevels\r
+     * @param request\r
+     * @param response\r
+     * @return\r
+     */\r
+    @RequestMapping(value = "//portal/description/{uuid_list}/DistributionTree", method = RequestMethod.GET) // mapped as absolute path, see CdmAntPathMatcher\r
     public DistributionTree doGetOrderedDistributionsB(\r
             @PathVariable("uuid_list") UuidList descriptionUuidList,\r
-            @RequestParam(value = "omitLevels", required = false) Set<NamedAreaLevel> levels,\r
-            HttpServletRequest request, HttpServletResponse response) {\r
-        logger.info("getOrderedDistributionsB(" + ObjectUtils.toString(levels) + ") - " + request.getServletPath());\r
+            @RequestParam(value = "subAreaPreference", required = false) boolean subAreaPreference,\r
+            @RequestParam(value = "statusOrderPreference", required = false) boolean statusOrderPreference,\r
+            @RequestParam(value = "hideMarkedAreas", required = false) DefinedTermBaseList<MarkerType> hideMarkedAreasList,\r
+            @RequestParam(value = "omitLevels", required = false) Set<NamedAreaLevel> omitLevels,\r
+            HttpServletRequest request,\r
+            HttpServletResponse response) {\r
+\r
+        logger.info("getOrderedDistributionsB() - " + requestPathAndQuery(request) );\r
+\r
         Set<TaxonDescription> taxonDescriptions = new HashSet<TaxonDescription>();\r
         TaxonDescription description;\r
         for (UUID descriptionUuid : descriptionUuidList) {\r
-            description = (TaxonDescription) service.load(descriptionUuid, TAXONDESCRIPTION_INIT_STRATEGY);\r
+            logger.debug("  loading description " + descriptionUuid.toString() );\r
+            description = (TaxonDescription) service.load(descriptionUuid, null);\r
             taxonDescriptions.add(description);\r
         }\r
-        DistributionTree distTree = service.getOrderedDistributions(taxonDescriptions, levels, TAXONDESCRIPTION_INIT_STRATEGY);\r
+\r
+        Set<MarkerType> hideMarkedAreas = null;\r
+        if(hideMarkedAreasList != null){\r
+            hideMarkedAreas = hideMarkedAreasList.asSet();\r
+        }\r
+\r
+        logger.debug("  get ordered distributions ");\r
+        DistributionTree distTree = service.getOrderedDistributions(taxonDescriptions, subAreaPreference, statusOrderPreference,\r
+                hideMarkedAreas, omitLevels, ORDERED_DISTRIBUTION_INIT_STRATEGY);\r
+        if (logger.isDebugEnabled()){ logger.debug("done");}\r
         return distTree;\r
     }\r
 \r
+    /**\r
+     * @param taxonUuid\r
+     * @param parts\r
+     *            possible values: condensedStatus, tree, mapUriParams,\r
+     *            elements,\r
+     * @param subAreaPreference\r
+     * @param statusOrderPreference\r
+     * @param hideMarkedAreasList\r
+     * @param recipe\r
+     *  The recipe for creating the condensed distribution status\r
+     * @param omitLevels\r
+     * @param request\r
+     * @param response\r
+     * @return\r
+     * @throws IOException\r
+     * @throws JsonMappingException\r
+     * @throws JsonParseException\r
+     */\r
+    @RequestMapping(value = "//portal/description/distributionInfoFor/{uuid}", method = RequestMethod.GET) // mapped as absolute path, see CdmAntPathMatcher\r
+    public ModelAndView doGetDistributionInfo(\r
+            @PathVariable("uuid") UUID taxonUuid,\r
+            @RequestParam("part") Set<InfoPart> partSet,\r
+            @RequestParam(value = "subAreaPreference", required = false) boolean subAreaPreference,\r
+            @RequestParam(value = "statusOrderPreference", required = false) boolean statusOrderPreference,\r
+            @RequestParam(value = "hideMarkedAreas", required = false) DefinedTermBaseList<MarkerType> hideMarkedAreasList,\r
+            @RequestParam(value = "fallbackAreaMarkerType", required = false) MarkerType fallbackAreaMarkerType,\r
+            @RequestParam(value = "omitLevels", required = false) Set<NamedAreaLevel> omitLevels,\r
+            @RequestParam(value = "statusColors", required = false) String statusColorsString,\r
+            @RequestParam(value = "recipe", required = false, defaultValue="EuroPlusMed") CondensedDistributionRecipe recipe,\r
+\r
+            HttpServletRequest request,\r
+            HttpServletResponse response) throws JsonParseException, JsonMappingException, IOException {\r
+\r
+            logger.info("doGetDistributionInfo() - " + requestPathAndQuery(request));\r
+\r
+            ModelAndView mv = new ModelAndView();\r
+\r
+            Set<MarkerType> hideMarkedAreas = null;\r
+            if(hideMarkedAreasList != null){\r
+                hideMarkedAreas = hideMarkedAreasList.asSet();\r
+            }\r
+\r
+            EnumSet<InfoPart> parts = EnumSet.copyOf(partSet);\r
+\r
+            Map<PresenceAbsenceTerm, Color> presenceAbsenceTermColors = EditGeoServiceUtilities.buildStatusColorMap(statusColorsString, termService);\r
+\r
+            DistributionInfoDTO dto = geoService.composeDistributionInfoFor(parts, taxonUuid, subAreaPreference, statusOrderPreference,\r
+                    hideMarkedAreas, fallbackAreaMarkerType, omitLevels, presenceAbsenceTermColors, LocaleContext.getLanguages(), DISTRIBUTION_INFO_INIT_STRATEGY, recipe);\r
+\r
+            mv.addObject(dto);\r
+\r
+            return mv;\r
+    }\r
+\r
+\r
 }\r