updated controller and related dtos to,
[cdmlib.git] / cdmlib-remote / src / main / java / eu / etaxonomy / cdm / remote / controller / NameCatalogueController.java
1 package eu.etaxonomy.cdm.remote.controller;
2
3 import java.io.IOException;
4 import java.util.ArrayList;
5 import java.util.Arrays;
6 import java.util.LinkedHashMap;
7 import java.util.List;
8 import java.util.Map;
9 import java.util.Set;
10 import java.util.UUID;
11
12 import javax.servlet.http.HttpServletRequest;
13 import javax.servlet.http.HttpServletResponse;
14
15 import java.util.Hashtable;
16 import org.springframework.beans.factory.annotation.Autowired;
17 import org.springframework.stereotype.Controller;
18 import org.springframework.web.bind.annotation.RequestMapping;
19 import org.springframework.web.bind.annotation.RequestMethod;
20 import org.springframework.web.bind.annotation.RequestParam;
21 import org.springframework.web.servlet.ModelAndView;
22
23 import eu.etaxonomy.cdm.api.service.IClassificationService;
24 import eu.etaxonomy.cdm.api.service.INameService;
25 import eu.etaxonomy.cdm.api.service.ITaxonService;
26
27 import eu.etaxonomy.cdm.remote.dto.common.ErrorResponse;
28 import eu.etaxonomy.cdm.remote.dto.common.RemoteResponse;
29 import eu.etaxonomy.cdm.remote.dto.namecatalogue.NameInformation;
30 import eu.etaxonomy.cdm.remote.dto.namecatalogue.NameSearch;
31 import eu.etaxonomy.cdm.remote.dto.namecatalogue.TaxonInformation;
32 import eu.etaxonomy.cdm.model.common.Language;
33 import eu.etaxonomy.cdm.model.name.NonViralName;
34 import eu.etaxonomy.cdm.model.name.TaxonNameBase;
35 import eu.etaxonomy.cdm.model.name.TypeDesignationBase;
36 import eu.etaxonomy.cdm.model.reference.Reference;
37 import eu.etaxonomy.cdm.model.taxon.Synonym;
38 import eu.etaxonomy.cdm.model.taxon.SynonymRelationship;
39 import eu.etaxonomy.cdm.model.taxon.Taxon;
40 import eu.etaxonomy.cdm.model.taxon.TaxonBase;
41 import eu.etaxonomy.cdm.model.taxon.TaxonNode;
42 import eu.etaxonomy.cdm.model.taxon.TaxonRelationship;
43 import eu.etaxonomy.cdm.persistence.query.MatchMode;
44
45 /**
46 * The controller class for the namespace 'name_catalogue'.
47 * This controller provides search mechanisims for searching by taxon name as well as taxon.
48 *
49 * @author c.mathew
50 * @version 1.0
51 * @created 15-Apr-2012
52 */
53
54 @Controller
55 @RequestMapping(value = {"/name_catalogue"})
56 public class NameCatalogueController extends BaseController<TaxonNameBase, INameService> {
57
58 /** Taxon status strings*/
59 public static final String ACCEPTED_NAME_STATUS = "accepted";
60 public static final String SYNONYM_STATUS = "synonym";
61
62 /** Flag strings*/
63 public static final String DOUBTFUL_FLAG = "doubtful";
64
65 /** Query strings */
66 public static final String NAME_SEARCH = "name";
67 public static final String TITLE_SEARCH = "title";
68
69 @Autowired
70 private ITaxonService taxonService;
71
72 @Autowired
73 private IClassificationService classificationService;
74
75 private static final List<String> NAME_SEARCH_INIT_STRATEGY = Arrays.asList(new String []{
76 "combinationAuthorTeam.$",
77 "exCombinationAuthorTeam.$",
78 "basionymAuthorTeam.$",
79 "exBasionymAuthorTeam.$",
80 "nameCache",
81 "taxonBases"
82 });
83
84 private static final List<String> NAME_INFORMATION_INIT_STRATEGY = Arrays.asList(new String []{
85 "taxonBases",
86 "status",
87 "nomenclaturalReference.$",
88 "combinationAuthorTeam.$",
89 "exCombinationAuthorTeam.$",
90 "basionymAuthorTeam.$",
91 "exBasionymAuthorTeam.$",
92 "relationsToThisName.$",
93 "relationsFromThisName.$"
94 });
95
96 private static final List<String> TAXON_INFORMATION_INIT_STRATEGY = Arrays.asList(new String []{
97 "synonymRelations.type.$",
98 "relationsFromThisTaxon.type.$",
99 "relationsToThisTaxon.type.$",
100 "taxonNodes",
101 "taxonNodes.classification"
102 });
103
104 private static final List<String> TAXON_NODE_INIT_STRATEGY = Arrays.asList(new String[]{
105 "taxon.sec",
106 "taxon.name",
107 "classification",
108 "classification.reference.$",
109 "classification.reference.authorTeam.$"
110 });
111
112 public NameCatalogueController(){
113 super();
114 setInitializationStrategy(Arrays.asList(new String[]{"$"})); //TODO still needed????
115 }
116
117 /* (non-Javadoc)
118 * @see eu.etaxonomy.cdm.remote.controller.GenericController#setService(eu.etaxonomy.cdm.api.service.IService)
119 */
120 @Autowired
121 @Override
122 public void setService(INameService service) {
123 this.service = service;
124 }
125
126 /**
127 * Returns a list of taxon names matching the <code>{query}</code> string pattern.
128 * Each of these taxon names is accompanied by a list of name uuids and
129 * a list of taxon uuids.
130 * <p>
131 * URI: <b>&#x002F;{datasource-name}&#x002F;name_catalogue</b>
132 *
133 * @param query
134 * The taxon name pattern(s) to query for. The query can contain wildcard characters ('*').
135 * The query can be performed with no wildcard or with the wildcard at the begin and / or end
136 * depending on the search pattern.
137 * @param type
138 * The type of name to query. This could any of,
139 * - name : scientific name corresponding to 'name cache' in CDM
140 * - title : complete name corresponding to 'title cache' in CDM
141 * @param request
142 * @param response
143 * @return a List of {@link NameSearch} objects each corresponding to a single query. These are built from
144 * {@TaxonNameBase} entities which are in turn initialized using the {@link #NAME_SEARCH_INIT_STRATEGY}
145 * @throws IOException
146 */
147 @RequestMapping(value = {""},
148 method = RequestMethod.GET)
149 public ModelAndView doGetNameSearch(@RequestParam(value = "query", required = true) String[] queries,
150 @RequestParam(value = "type", required = false, defaultValue=NAME_SEARCH) String searchType,
151 HttpServletRequest request,
152 HttpServletResponse response) throws IOException {
153 ModelAndView mv = new ModelAndView();
154 List <RemoteResponse> nsList = new ArrayList<RemoteResponse>();
155
156 if(!searchType.equals(NAME_SEARCH) && !searchType.equals(TITLE_SEARCH)) {
157 ErrorResponse er = new ErrorResponse();
158 er.setErrorMessage("searchType parameter can only be set as" + NAME_SEARCH + " or " + TITLE_SEARCH);
159 mv.addObject(er);
160 return mv;
161 }
162
163 for(String query : queries ) {
164
165 String queryWOWildcards = getQueryWithoutWildCards(query);
166 MatchMode mm = getMatchModeFromQuery(query);
167 logger.info("doGetNameSearch()" + request.getServletPath() + " for query \"" + query + "\" without wild cards : " + queryWOWildcards + " and match mode : " + mm);
168 List<NonViralName> nameList = new ArrayList<NonViralName>();
169 if(searchType.equals(NAME_SEARCH)) {
170 nameList = (List<NonViralName>)service.findNamesByNameCache(queryWOWildcards, mm, NAME_SEARCH_INIT_STRATEGY);
171 }
172
173 if(searchType.equals(TITLE_SEARCH)) {
174 nameList = (List<NonViralName>)service.findNamesByTitleCache(queryWOWildcards, mm, NAME_SEARCH_INIT_STRATEGY);
175 }
176 if(nameList == null || !nameList.isEmpty()) {
177 NameSearch ns = new NameSearch();
178 ns.setRequest(query);
179
180 for (NonViralName nvn : nameList)
181 {
182 ns.addToResponseList(nvn.getTitleCache(), nvn.getNameCache(), nvn.getUuid().toString(), nvn.getTaxonBases());
183 }
184 nsList.add(ns);
185
186 } else {
187 ErrorResponse er = new ErrorResponse();
188 er.setErrorMessage("No Taxon Name for given query : " + query);
189 nsList.add(er);
190 }
191 }
192 mv.addObject(nsList);
193 return mv;
194 }
195
196 /**
197 * Returns information related to the taxon name matching the given <code>{nameUuid}</code>.
198 * The information includes the name string, relationships, rank, list of related lsids / taxon uuids, etc.
199 * <p>
200 * URI: <b>&#x002F;{datasource-name}&#x002F;name_catalogue</b>
201 *
202 * @param query
203 * The taxon name pattern(s) to query for. The query can contain wildcard characters ('*').
204 * The query can be performed with no wildcard or with the wildcard at the begin and / or end
205 * depending on the search pattern.
206 * @param request
207 * @param response
208 * @return a List of {@link NameSearch} objects each corresponding to a single query. These are built from
209 * {@TaxonNameBase} entities which are in turn initialized using the {@link #NAME_SEARCH_INIT_STRATEGY}
210 * @throws IOException
211 */
212 @RequestMapping(value = {"name"},
213 method = RequestMethod.GET)
214 public ModelAndView doGetNameInformation(@RequestParam(value = "nameUuid", required = true) String[] nameUuids,
215 HttpServletRequest request,
216 HttpServletResponse response) throws IOException {
217 ModelAndView mv = new ModelAndView();
218 List <RemoteResponse> niList = new ArrayList<RemoteResponse>();
219 for(String nameUuid : nameUuids ) {
220 logger.info("doGetNameInformation()" + request.getServletPath() + " for name uuid \"" + nameUuid + "\"");
221 NonViralName nvn = (NonViralName)service.findNameByUuid(UUID.fromString(nameUuid), NAME_INFORMATION_INIT_STRATEGY);
222 if(nvn != null) {
223 NameInformation ni = new NameInformation();
224 ni.setRequest(nameUuid);
225 Reference ref = (Reference) nvn.getNomenclaturalReference();
226 String citation = "";
227 String citation_details = "";
228 if(ref != null) {
229 citation = ref.getTitleCache();
230 }
231 ni.setResponse(nvn.getTitleCache(),
232 nvn.getNameCache(),
233 nvn.getRank().getTitleCache(),
234 nvn.getStatus(),
235 citation,
236 nvn.getRelationsFromThisName(),
237 nvn.getRelationsToThisName(),
238 nvn.getTaxonBases());
239 niList.add(ni);
240 } else {
241 ErrorResponse re = new ErrorResponse();
242 re.setErrorMessage("No Taxon Name for given UUID : " + nameUuid);
243 niList.add(re);
244 }
245 }
246 mv.addObject(niList);
247 return mv;
248 }
249
250 @RequestMapping(value = {"taxon"},
251 method = RequestMethod.GET)
252 public ModelAndView doGetTaxonInformation(@RequestParam(value = "taxonUuid", required = true) String[] taxonUuids,
253 HttpServletRequest request,
254 HttpServletResponse response) throws IOException {
255 ModelAndView mv = new ModelAndView();
256 List <RemoteResponse> tiList = new ArrayList<RemoteResponse>();
257 for(String taxonUuid : taxonUuids ) {
258 logger.info("doGetTaxonInformation()" + request.getServletPath() + " for taxon uuid \"" + taxonUuid);
259 TaxonBase tb= taxonService.findTaxonByUuid(UUID.fromString(taxonUuid), TAXON_INFORMATION_INIT_STRATEGY);
260
261 if(tb != null) {
262 TaxonInformation ti = new TaxonInformation();
263 ti.setRequest(taxonUuid);
264
265 if(tb.isInstanceOf(Taxon.class)) {
266 Taxon taxon = (Taxon)tb;
267 ti.setResponseTaxon(tb.getTitleCache(),
268 ACCEPTED_NAME_STATUS,
269 buildFlagMap(tb),
270 buildClassificationMap(taxon));
271 Set<SynonymRelationship> synRelationships = taxon.getSynonymRelations();
272 for(SynonymRelationship sr: synRelationships) {
273 Synonym syn = sr.getSynonym();
274 String uuid = syn.getUuid().toString();
275 String title = syn.getTitleCache();
276 String status = SYNONYM_STATUS;
277 String relLabel = sr.getType().getInverseRepresentation(Language.DEFAULT()).getLabel();
278 ti.addToResponseRelatedTaxa(uuid, title, status, "", relLabel);
279 }
280
281 Set<TaxonRelationship> trFromSet = taxon.getRelationsFromThisTaxon();
282
283 for(TaxonRelationship tr : trFromSet) {
284 String titleFrom = tr.getRelatedFrom().getTitleCache();
285
286 String titleTo = tr.getRelatedTo().getTitleCache();
287 String uuid = tr.getRelatedTo().getUuid().toString();
288 String status = ACCEPTED_NAME_STATUS;
289 String relLabel = tr.getType().getRepresentation(Language.DEFAULT()).getLabel();
290 //System.out.println("From : " + titleFrom + ", To : " + titleTo + "type " + tr.getType().getTitleCache());
291 ti.addToResponseRelatedTaxa(uuid, titleTo, status, "", relLabel);
292 }
293
294 Set<TaxonRelationship> trToSet = taxon.getRelationsToThisTaxon();
295 for(TaxonRelationship tr : trToSet) {
296 String titleTo = tr.getRelatedTo().getTitleCache();
297
298 String titleFrom = tr.getRelatedFrom().getTitleCache();
299 String uuid = tr.getRelatedFrom().getUuid().toString();
300 String status = ACCEPTED_NAME_STATUS;
301 String relLabel = tr.getType().getInverseRepresentation(Language.DEFAULT()).getLabel();
302 //System.out.println("From : " + titleFrom + ", To : " + titleFrom + "type " + tr.getType().getTitleCache());
303 ti.addToResponseRelatedTaxa(uuid, titleFrom, status, "", relLabel);
304 }
305 } else if(tb instanceof Synonym) {
306 Synonym synonym = (Synonym)tb;
307 ti.setResponseTaxon(synonym.getTitleCache(),
308 SYNONYM_STATUS,
309 buildFlagMap(synonym),
310 null);
311 Set<SynonymRelationship> synRelationships = synonym.getSynonymRelations();
312 for(SynonymRelationship sr: synRelationships) {
313 Taxon accTaxon = sr.getAcceptedTaxon();
314 String uuid = accTaxon.getUuid().toString();
315 String title = accTaxon.getTitleCache();
316 String status = ACCEPTED_NAME_STATUS;
317 String relLabel = sr.getType().getRepresentation(Language.DEFAULT()).getLabel();
318 ti.addToResponseRelatedTaxa(uuid, title, status, "", relLabel);
319 }
320 }
321 tiList.add(ti);
322 } else {
323 ErrorResponse re = new ErrorResponse();
324 re.setErrorMessage("No Taxon for given UUID : " + taxonUuid);
325 tiList.add(re);
326 }
327 }
328 mv.addObject(tiList);
329 return mv;
330 }
331
332 private MatchMode getMatchModeFromQuery(String query) {
333 if(query.startsWith("*") && query.endsWith("*")) {
334 return MatchMode.ANYWHERE;
335 } else if(query.startsWith("*")) {
336 return MatchMode.END;
337 } else if(query.endsWith("*")) {
338 return MatchMode.BEGINNING;
339 } else {
340 return MatchMode.EXACT;
341 }
342 }
343
344 private String getQueryWithoutWildCards(String query) {
345
346 String newQuery = query;
347
348 if(query.startsWith("*")) {
349 newQuery = newQuery.substring(1, newQuery.length());
350 }
351
352 if(query.endsWith("*")) {
353 newQuery = newQuery.substring(0, newQuery.length()-1);
354 }
355
356 return newQuery.trim();
357 }
358
359 private Map<String, String> buildFlagMap(TaxonBase tb) {
360 Map<String, String> flags = new Hashtable<String, String>();
361 flags.put(DOUBTFUL_FLAG, Boolean.toString(tb.isDoubtful()));
362 return flags;
363 }
364
365 private Map<String, Map> buildClassificationMap(Taxon taxon) {
366 Map<String, Map> classificationMap = new Hashtable<String, Map>();
367 Set<TaxonNode> taxonNodes = taxon.getTaxonNodes();
368
369 for(TaxonNode tn : taxonNodes) {
370 Map<String, String> classification = new LinkedHashMap<String, String>();
371 List<TaxonNode> tnList = classificationService.loadTreeBranchToTaxon(taxon, tn.getClassification(), null, TAXON_NODE_INIT_STRATEGY);
372 for(TaxonNode classificationtn : tnList) {
373
374 classification.put(classificationtn.getTaxon().getName().getRank().getTitleCache(),
375 classificationtn.getTaxon().getName().getTitleCache());
376 }
377
378 String cname = tn.getClassification().getTitleCache();
379 String [] words = cname.split("\\s+");
380 //"\\s+" in regular expression language meaning one or more spaces
381 StringBuilder builder = new StringBuilder();
382 for (String word : words){
383 builder.append(word);
384 }
385 cname = builder.toString();
386 classificationMap.put(cname, classification);
387 }
388 return classificationMap;
389 }
390
391
392 }