1 |
236ab161
|
Andreas Müller
|
/**
|
2 |
|
|
* Copyright (C) 2009 EDIT
|
3 |
|
|
* European Distributed Institute of Taxonomy
|
4 |
|
|
* http://www.e-taxonomy.eu
|
5 |
|
|
*
|
6 |
|
|
* The contents of this file are subject to the Mozilla Public License Version 1.1
|
7 |
|
|
* See LICENSE.TXT at the top of this package for the full license terms.
|
8 |
|
|
*/
|
9 |
|
|
package eu.etaxonomy.cdm.remote.controller.dto;
|
10 |
|
|
|
11 |
|
|
import java.io.IOException;
|
12 |
|
|
import java.io.InputStream;
|
13 |
|
|
import java.util.ArrayList;
|
14 |
|
|
import java.util.Arrays;
|
15 |
|
|
import java.util.HashMap;
|
16 |
|
|
import java.util.HashSet;
|
17 |
|
|
import java.util.Hashtable;
|
18 |
|
|
import java.util.Iterator;
|
19 |
|
|
import java.util.LinkedHashMap;
|
20 |
|
|
import java.util.List;
|
21 |
|
|
import java.util.Map;
|
22 |
|
|
import java.util.Set;
|
23 |
|
|
import java.util.TreeMap;
|
24 |
|
|
import java.util.UUID;
|
25 |
|
|
|
26 |
|
|
import javax.servlet.http.HttpServletRequest;
|
27 |
|
|
import javax.servlet.http.HttpServletResponse;
|
28 |
|
|
|
29 |
|
|
import org.apache.lucene.document.Document;
|
30 |
|
|
import org.apache.lucene.queryParser.ParseException;
|
31 |
|
|
import org.joda.time.DateTime;
|
32 |
|
|
import org.joda.time.format.DateTimeFormat;
|
33 |
|
|
import org.joda.time.format.DateTimeFormatter;
|
34 |
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
35 |
|
|
import org.springframework.context.ResourceLoaderAware;
|
36 |
|
|
import org.springframework.core.io.Resource;
|
37 |
|
|
import org.springframework.core.io.ResourceLoader;
|
38 |
|
|
import org.springframework.stereotype.Controller;
|
39 |
|
|
import org.springframework.web.bind.annotation.RequestMapping;
|
40 |
|
|
import org.springframework.web.bind.annotation.RequestMethod;
|
41 |
|
|
import org.springframework.web.bind.annotation.RequestParam;
|
42 |
|
|
import org.springframework.web.servlet.ModelAndView;
|
43 |
|
|
|
44 |
|
|
import com.wordnik.swagger.annotations.Api;
|
45 |
|
|
|
46 |
|
|
import eu.etaxonomy.cdm.api.service.IClassificationService;
|
47 |
|
|
import eu.etaxonomy.cdm.api.service.ICommonService;
|
48 |
|
|
import eu.etaxonomy.cdm.api.service.INameService;
|
49 |
|
|
import eu.etaxonomy.cdm.api.service.ITaxonService;
|
50 |
|
|
import eu.etaxonomy.cdm.api.service.search.DocumentSearchResult;
|
51 |
|
|
import eu.etaxonomy.cdm.common.DocUtils;
|
52 |
|
|
import eu.etaxonomy.cdm.hibernate.search.AcceptedTaxonBridge;
|
53 |
|
|
import eu.etaxonomy.cdm.model.common.CdmBase;
|
54 |
|
|
import eu.etaxonomy.cdm.model.common.IdentifiableSource;
|
55 |
|
|
import eu.etaxonomy.cdm.model.common.Language;
|
56 |
|
|
import eu.etaxonomy.cdm.model.name.NonViralName;
|
57 |
|
|
import eu.etaxonomy.cdm.model.name.TaxonNameBase;
|
58 |
|
|
import eu.etaxonomy.cdm.model.reference.Reference;
|
59 |
|
|
import eu.etaxonomy.cdm.model.taxon.Classification;
|
60 |
|
|
import eu.etaxonomy.cdm.model.taxon.Synonym;
|
61 |
|
|
import eu.etaxonomy.cdm.model.taxon.SynonymRelationship;
|
62 |
|
|
import eu.etaxonomy.cdm.model.taxon.Taxon;
|
63 |
|
|
import eu.etaxonomy.cdm.model.taxon.TaxonBase;
|
64 |
|
|
import eu.etaxonomy.cdm.model.taxon.TaxonNode;
|
65 |
|
|
import eu.etaxonomy.cdm.model.taxon.TaxonRelationship;
|
66 |
|
|
import eu.etaxonomy.cdm.persistence.query.MatchMode;
|
67 |
|
|
import eu.etaxonomy.cdm.persistence.query.OrderHint;
|
68 |
|
|
import eu.etaxonomy.cdm.persistence.query.OrderHint.SortOrder;
|
69 |
|
|
import eu.etaxonomy.cdm.remote.controller.AbstractController;
|
70 |
|
|
import eu.etaxonomy.cdm.remote.dto.common.ErrorResponse;
|
71 |
|
|
import eu.etaxonomy.cdm.remote.dto.common.RemoteResponse;
|
72 |
|
|
import eu.etaxonomy.cdm.remote.dto.namecatalogue.AcceptedNameSearch;
|
73 |
|
|
import eu.etaxonomy.cdm.remote.dto.namecatalogue.NameInformation;
|
74 |
|
|
import eu.etaxonomy.cdm.remote.dto.namecatalogue.NameSearch;
|
75 |
|
|
import eu.etaxonomy.cdm.remote.dto.namecatalogue.TaxonInformation;
|
76 |
|
|
import eu.etaxonomy.cdm.remote.view.HtmlView;
|
77 |
|
|
|
78 |
|
|
/**
|
79 |
|
|
* The controller class for the namespace 'name_catalogue'. This web service namespace
|
80 |
|
|
* is an add-on to the already existing CDM REST API and provides information relating
|
81 |
|
|
* to scientific names as well as taxa present in the underlying datasource.
|
82 |
|
|
*
|
83 |
|
|
* @author c.mathew
|
84 |
|
|
* @version 1.1.0
|
85 |
|
|
* @created 15-Apr-2012
|
86 |
|
|
*/
|
87 |
|
|
|
88 |
|
|
@Controller
|
89 |
|
|
@Api("name_catalogue")
|
90 |
|
|
@RequestMapping(value = { "/name_catalogue" })
|
91 |
|
|
public class NameCatalogueController extends AbstractController<TaxonNameBase, INameService> implements ResourceLoaderAware {
|
92 |
|
|
|
93 |
|
|
private ResourceLoader resourceLoader;
|
94 |
|
|
|
95 |
|
|
/** Taxonomic status 'accepted' string */
|
96 |
|
|
public static final String ACCEPTED_NAME_STATUS = "accepted";
|
97 |
|
|
|
98 |
|
|
/** Taxonpmic status 'synonym' string */
|
99 |
|
|
public static final String SYNONYM_STATUS = "synonym";
|
100 |
|
|
|
101 |
|
|
/** Flag 'doubtful' strings */
|
102 |
|
|
public static final String DOUBTFUL_FLAG = "doubtful";
|
103 |
|
|
|
104 |
|
|
/** Base scientific name search type */
|
105 |
|
|
public static final String NAME_SEARCH = "name";
|
106 |
|
|
|
107 |
|
|
/** Complete scientific name search type */
|
108 |
|
|
public static final String TITLE_SEARCH = "title";
|
109 |
|
|
|
110 |
|
|
/** Default name search type */
|
111 |
|
|
public static final String DEFAULT_SEARCH_TYPE = NAME_SEARCH;
|
112 |
|
|
|
113 |
|
|
/** Default max number of hits for the exact name search */
|
114 |
|
|
public static final String DEFAULT_MAX_NB_FOR_EXACT_SEARCH = "100";
|
115 |
|
|
|
116 |
|
|
/** Classifcation 'default' key */
|
117 |
|
|
public static final String CLASSIFICATION_DEFAULT = "default";
|
118 |
|
|
|
119 |
|
|
/** Classifcation 'all' key */
|
120 |
|
|
public static final String CLASSIFICATION_ALL = "all";
|
121 |
|
|
|
122 |
|
|
/** Classification to include uuids key */
|
123 |
|
|
public static final String INCLUDE_CLUUIDS = "cluuids";
|
124 |
|
|
|
125 |
|
|
/** Fuzzy Name Cache search */
|
126 |
|
|
public static final String FUZZY_NAME_CACHE = "name";
|
127 |
|
|
|
128 |
|
|
/** Fuzzy Atomised Name search */
|
129 |
|
|
public static final String FUZZY_ATOMISED = "atomised";
|
130 |
|
|
|
131 |
|
|
private static final String DWC_DATASET_ID = "http://rs.tdwg.org/dwc/terms/datasetID";
|
132 |
|
|
|
133 |
|
|
private static final DateTimeFormatter fmt = DateTimeFormat.forPattern("dd-MM-yyyy");
|
134 |
|
|
|
135 |
|
|
@Autowired
|
136 |
|
|
private ITaxonService taxonService;
|
137 |
|
|
|
138 |
|
|
|
139 |
|
|
@Autowired
|
140 |
|
|
private IClassificationService classificationService;
|
141 |
|
|
|
142 |
|
|
@Autowired
|
143 |
|
|
private ICommonService commonService;
|
144 |
|
|
|
145 |
|
|
/** Hibernate name search initialisation strategy */
|
146 |
|
|
private static final List<String> NAME_SEARCH_INIT_STRATEGY = Arrays.asList(new String[] {
|
147 |
|
|
"combinationAuthorship.$",
|
148 |
|
|
"exCombinationAuthorship.$",
|
149 |
|
|
"basionymAuthorship.$",
|
150 |
|
|
"exBasionymAuthorship.$",
|
151 |
|
|
"nameCache",
|
152 |
|
|
"taxonBases",
|
153 |
|
|
"taxonBases.synonymRelations.type.$"});
|
154 |
|
|
|
155 |
|
|
/** Hibernate accepted name search initialisation strategy */
|
156 |
|
|
private static final List<String> ACC_NAME_SEARCH_INIT_STRATEGY = Arrays.asList(new String[] {
|
157 |
|
|
"nameCache",
|
158 |
|
|
"taxonBases",
|
159 |
|
|
"taxonBases.synonymRelations.acceptedTaxon.name.nameCache",
|
160 |
|
|
"taxonBases.synonymRelations.acceptedTaxon.name.rank.titleCache",
|
161 |
|
|
"taxonBases.synonymRelations.acceptedTaxon.taxonNodes.classification",
|
162 |
|
|
"taxonBases.taxonNodes.classification",
|
163 |
|
|
"taxonBases.relationsFromThisTaxon.type.$"});
|
164 |
|
|
|
165 |
|
|
/** Hibernate name information initialisation strategy */
|
166 |
|
|
private static final List<String> NAME_INFORMATION_INIT_STRATEGY = Arrays.asList(new String[] {
|
167 |
|
|
"taxonBases",
|
168 |
|
|
"status",
|
169 |
|
|
"nomenclaturalReference.$",
|
170 |
|
|
"combinationAuthorship.$",
|
171 |
|
|
"exCombinationAuthorship.$",
|
172 |
|
|
"basionymAuthorship.$",
|
173 |
|
|
"exBasionymAuthorship.$",
|
174 |
|
|
"relationsToThisName.fromName.$",
|
175 |
|
|
"relationsToThisName.nomenclaturalReference.$",
|
176 |
|
|
"relationsToThisName.type.$",
|
177 |
|
|
"relationsFromThisName.toName.$",
|
178 |
|
|
"relationsFromThisName.nomenclaturalReference.$",
|
179 |
|
|
"relationsFromThisName.type.$"});
|
180 |
|
|
|
181 |
|
|
/** Hibernate taxon information initialisation strategy */
|
182 |
|
|
private static final List<String> TAXON_INFORMATION_INIT_STRATEGY = Arrays.asList(new String[] {
|
183 |
|
|
"name.titleCache",
|
184 |
|
|
"name.rank.titleCache",
|
185 |
|
|
|
186 |
|
|
"sec.updated",
|
187 |
|
|
"sec.titleCache",
|
188 |
|
|
"sources.citation.sources.idNamespace",
|
189 |
|
|
"sources.citation.sources.idInSource",
|
190 |
|
|
|
191 |
|
|
"synonymRelations.synonym.name.rank.titleCache",
|
192 |
|
|
"synonymRelations.synonym.sec.updated",
|
193 |
|
|
"synonymRelations.synonym.sec.titleCache",
|
194 |
|
|
"synonymRelations.synonym.sources.citation.sources.idNamespace",
|
195 |
|
|
"synonymRelations.synonym.sources.citation.sources.idInSource",
|
196 |
|
|
"synonymRelations.acceptedTaxon.name.rank.titleCache",
|
197 |
|
|
"synonymRelations.acceptedTaxon.sec.titleCache",
|
198 |
|
|
"synonymRelations.acceptedTaxon.sources.citation.sources.idNamespace",
|
199 |
|
|
"synonymRelations.acceptedTaxon.sources.citation.sources.idInSource",
|
200 |
|
|
"synonymRelations.type.inverseRepresentations",
|
201 |
|
|
|
202 |
|
|
"relationsFromThisTaxon.type.inverseRepresentations",
|
203 |
|
|
"relationsFromThisTaxon.toTaxon.name.rank.titleCache",
|
204 |
|
|
"relationsFromThisTaxon.toTaxon.sec.updated",
|
205 |
|
|
"relationsFromThisTaxon.toTaxon.sec.titleCache",
|
206 |
|
|
"relationsFromThisTaxon.toTaxon.sources.citation.sources.idNamespace",
|
207 |
|
|
"relationsFromThisTaxon.toTaxon.sources.citation.sources.idInSource",
|
208 |
|
|
|
209 |
|
|
"relationsToThisTaxon.type.inverseRepresentations",
|
210 |
|
|
"relationsToThisTaxon.fromTaxon.name.rank.titleCache",
|
211 |
|
|
"relationsToThisTaxon.fromTaxon.sec.updated",
|
212 |
|
|
"relationsToThisTaxon.fromTaxon.sec.titleCache",
|
213 |
|
|
"relationsToThisTaxon.fromTaxon.sources.citation.sources.idNamespace",
|
214 |
|
|
"relationsToThisTaxon.fromTaxon.sources.citation.sources.idInSource",
|
215 |
|
|
|
216 |
|
|
"taxonNodes",
|
217 |
|
|
"taxonNodes.classification" });
|
218 |
|
|
|
219 |
|
|
/** Hibernate taxon node initialisation strategy */
|
220 |
|
|
private static final List<String> TAXON_NODE_INIT_STRATEGY = Arrays.asList(new String[] {
|
221 |
|
|
"taxon.sec",
|
222 |
|
|
"taxon.name",
|
223 |
|
|
"classification",
|
224 |
|
|
"classification.reference.$",
|
225 |
|
|
"classification.reference.authorship.$" });
|
226 |
|
|
|
227 |
|
|
/** Hibernate classification vocabulary initialisation strategy */
|
228 |
|
|
private static final List<String> VOC_CLASSIFICATION_INIT_STRATEGY = Arrays.asList(new String[] {
|
229 |
|
|
"classification",
|
230 |
|
|
"classification.reference.$",
|
231 |
|
|
"classification.reference.authorship.$" });
|
232 |
|
|
|
233 |
|
|
/** Hibernate classification vocabulary initialisation strategy */
|
234 |
|
|
private static final List<String> COMMON_INIT_STRATEGY = Arrays.asList(new String[] {});
|
235 |
|
|
|
236 |
|
|
public NameCatalogueController() {
|
237 |
|
|
super();
|
238 |
|
|
setInitializationStrategy(Arrays.asList(new String[] { "$" }));
|
239 |
|
|
}
|
240 |
|
|
|
241 |
|
|
/*
|
242 |
|
|
* (non-Javadoc)
|
243 |
|
|
*
|
244 |
|
|
* @see
|
245 |
|
|
* eu.etaxonomy.cdm.remote.controller.GenericController#setService(eu
|
246 |
|
|
* .etaxonomy.cdm.api.service.IService)
|
247 |
|
|
*/
|
248 |
|
|
@Autowired
|
249 |
|
|
@Override
|
250 |
|
|
public void setService(INameService service) {
|
251 |
|
|
this.service = service;
|
252 |
|
|
}
|
253 |
|
|
|
254 |
|
|
/**
|
255 |
|
|
* Returns a documentation page for the Name Search API.
|
256 |
|
|
* <p>
|
257 |
|
|
* URI: <b>/{datasource-name}/name_catalogue</b>
|
258 |
|
|
*
|
259 |
|
|
* @param request
|
260 |
|
|
* @param response
|
261 |
|
|
* @return Html page describing the Name Search API
|
262 |
|
|
* @throws IOException
|
263 |
|
|
*/
|
264 |
|
|
@RequestMapping(value = { "" }, method = RequestMethod.GET, params = {})
|
265 |
|
|
public ModelAndView doGetNameSearchDocumentation(
|
266 |
|
|
HttpServletRequest request, HttpServletResponse response)
|
267 |
|
|
throws IOException {
|
268 |
|
|
ModelAndView mv = new ModelAndView();
|
269 |
|
|
// Read apt documentation file.
|
270 |
|
|
Resource resource = resourceLoader.getResource("classpath:eu/etaxonomy/cdm/doc/remote/apt/name-catalogue-default.apt");
|
271 |
|
|
// using input stream as this works for both files in the classes directory
|
272 |
|
|
// as well as files inside jars
|
273 |
|
|
InputStream aptInputStream = resource.getInputStream();
|
274 |
|
|
// Build Html View
|
275 |
|
|
Map<String, String> modelMap = new HashMap<String, String>();
|
276 |
|
|
// Convert Apt to Html
|
277 |
|
|
modelMap.put("html", DocUtils.convertAptToHtml(aptInputStream));
|
278 |
|
|
mv.addAllObjects(modelMap);
|
279 |
|
|
|
280 |
|
|
HtmlView hv = new HtmlView();
|
281 |
|
|
mv.setView(hv);
|
282 |
|
|
return mv;
|
283 |
|
|
}
|
284 |
|
|
|
285 |
|
|
/**
|
286 |
|
|
* Returns a list of scientific names matching the <code>{query}</code>
|
287 |
|
|
* string pattern. Each of these scientific names is accompanied by a list of
|
288 |
|
|
* name uuids, a list of taxon uuids, a list of accepted taxon uuids, etc.
|
289 |
|
|
* <p>
|
290 |
|
|
* Endpoint documentation can be found <a href="{@docRoot}/../remote/name-catalogue-default.html">here</a>
|
291 |
|
|
* <p>
|
292 |
|
|
* URI: <b>/{datasource-name}/name_catalogue</b>
|
293 |
|
|
*
|
294 |
|
|
* @param queries
|
295 |
|
|
* The base scientific name pattern(s) to query for. The query can
|
296 |
|
|
* contain wildcard characters ('*'). The query can be
|
297 |
|
|
* performed with no wildcard or with the wildcard at the
|
298 |
|
|
* begin and / or end depending on the search pattern.
|
299 |
|
|
* @param request Http servlet request.
|
300 |
|
|
* @param response Http servlet response.
|
301 |
|
|
* @return a list of {@link NameSearch} objects each corresponding to a
|
302 |
|
|
* single query. These are built from {@link TaxonNameBase}
|
303 |
|
|
* entities which are in turn initialized using
|
304 |
|
|
* the {@link #NAME_SEARCH_INIT_STRATEGY}
|
305 |
|
|
* @throws IOException
|
306 |
|
|
*/
|
307 |
|
|
@RequestMapping(value = { "" }, method = RequestMethod.GET, params = {"query"})
|
308 |
|
|
public ModelAndView doGetNameSearch(@RequestParam(value = "query", required = true) String[] queries,
|
309 |
|
|
HttpServletRequest request, HttpServletResponse response) throws IOException {
|
310 |
|
|
return doGetNameSearch(queries, DEFAULT_SEARCH_TYPE, DEFAULT_MAX_NB_FOR_EXACT_SEARCH, request, response);
|
311 |
|
|
}
|
312 |
|
|
|
313 |
|
|
/**
|
314 |
|
|
* Returns a list of scientific names matching the <code>{query}</code>
|
315 |
|
|
* string pattern. Each of these scientific names is accompanied by a list of
|
316 |
|
|
* name uuids, a list of taxon uuids and a list of accepted taxon uuids.
|
317 |
|
|
* <p>
|
318 |
|
|
* Endpoint documentation can be found <a href="{@docRoot}/../remote/name-catalogue-default.html">here</a>
|
319 |
|
|
* <p>
|
320 |
|
|
* URI: <b>/{datasource-name}/name_catalogue</b>
|
321 |
|
|
*
|
322 |
|
|
* @param query
|
323 |
|
|
* The scientific name pattern(s) to query for. The query can
|
324 |
|
|
* contain wildcard characters ('*'). The query can be
|
325 |
|
|
* performed with no wildcard or with the wildcard at the
|
326 |
|
|
* begin and / or end depending on the search pattern.
|
327 |
|
|
* @param type
|
328 |
|
|
* The type of name to query. This be either
|
329 |
|
|
* "name" : scientific name corresponding to 'name cache' in CDM or
|
330 |
|
|
* "title" : complete name corresponding to 'title cache' in CDM
|
331 |
|
|
* @param hits
|
332 |
|
|
* Maximum number of responses to be returned.
|
333 |
|
|
* @param request Http servlet request.
|
334 |
|
|
* @param response Http servlet response.
|
335 |
|
|
* @return a List of {@link NameSearch} objects each corresponding to a
|
336 |
|
|
* single query. These are built from {@link TaxonNameBase} entities
|
337 |
|
|
* which are in turn initialized using the {@link #NAME_SEARCH_INIT_STRATEGY}
|
338 |
|
|
* @throws IOException
|
339 |
|
|
*/
|
340 |
|
|
@RequestMapping(value = { "" }, method = RequestMethod.GET, params = {"query", "type"})
|
341 |
|
|
public ModelAndView doGetNameSearch(@RequestParam(value = "query", required = true) String[] queries,
|
342 |
|
|
@RequestParam(value = "type", required = false, defaultValue = DEFAULT_SEARCH_TYPE) String searchType,
|
343 |
|
|
@RequestParam(value = "hits", required = false, defaultValue = DEFAULT_MAX_NB_FOR_EXACT_SEARCH) String hits,
|
344 |
|
|
HttpServletRequest request, HttpServletResponse response) throws IOException {
|
345 |
|
|
ModelAndView mv = new ModelAndView();
|
346 |
|
|
List<RemoteResponse> nsList = new ArrayList<RemoteResponse>();
|
347 |
|
|
|
348 |
|
|
int h = 100;
|
349 |
|
|
try {
|
350 |
|
|
h = Integer.parseInt(hits);
|
351 |
|
|
} catch(NumberFormatException nfe) {
|
352 |
|
|
ErrorResponse er = new ErrorResponse();
|
353 |
|
|
er.setErrorMessage("hits parameter is not a number");
|
354 |
|
|
mv.addObject(er);
|
355 |
|
|
return mv;
|
356 |
|
|
}
|
357 |
|
|
|
358 |
|
|
// search through each query
|
359 |
|
|
for (String query : queries) {
|
360 |
|
|
if(query.equals("")) {
|
361 |
|
|
ErrorResponse er = new ErrorResponse();
|
362 |
|
|
er.setErrorMessage("Empty query field");
|
363 |
|
|
nsList.add(er);
|
364 |
|
|
continue;
|
365 |
|
|
}
|
366 |
|
|
// remove wildcards if any
|
367 |
|
|
String queryWOWildcards = getQueryWithoutWildCards(query);
|
368 |
|
|
// convert first char to upper case
|
369 |
|
|
char[] stringArray = queryWOWildcards.toCharArray();
|
370 |
|
|
stringArray[0] = Character.toUpperCase(stringArray[0]);
|
371 |
|
|
queryWOWildcards = new String(stringArray);
|
372 |
|
|
|
373 |
|
|
boolean wc = false;
|
374 |
|
|
|
375 |
|
|
if(getMatchModeFromQuery(query) == MatchMode.BEGINNING) {
|
376 |
|
|
wc = true;
|
377 |
|
|
}
|
378 |
|
|
logger.info("doGetNameSearch()" + request.getRequestURI() + " for query \"" + query);
|
379 |
|
|
|
380 |
|
|
List<DocumentSearchResult> nameSearchList = new ArrayList<DocumentSearchResult>();
|
381 |
|
|
try {
|
382 |
|
|
nameSearchList = service.findByNameExactSearch(
|
383 |
|
|
queryWOWildcards,
|
384 |
|
|
wc,
|
385 |
|
|
null,
|
386 |
|
|
false,
|
387 |
|
|
h);
|
388 |
|
|
} catch (ParseException e) {
|
389 |
|
|
// TODO Auto-generated catch block
|
390 |
|
|
//e.printStackTrace();
|
391 |
|
|
ErrorResponse er = new ErrorResponse();
|
392 |
|
|
er.setErrorMessage("Could not parse name : " + query);
|
393 |
|
|
nsList.add(er);
|
394 |
|
|
continue;
|
395 |
|
|
}
|
396 |
|
|
|
397 |
|
|
|
398 |
|
|
// if search is successful then get related information , else return error
|
399 |
|
|
if (nameSearchList == null || !nameSearchList.isEmpty()) {
|
400 |
|
|
NameSearch ns = new NameSearch();
|
401 |
|
|
ns.setRequest(query);
|
402 |
|
|
|
403 |
|
|
for (DocumentSearchResult searchResult : nameSearchList) {
|
404 |
|
|
for(Document doc : searchResult.getDocs()) {
|
405 |
|
|
|
406 |
|
|
// we need to retrieve both taxon uuid of name queried and
|
407 |
|
|
// the corresponding accepted taxa.
|
408 |
|
|
// reason to return accepted taxa also, is to be able to get from
|
409 |
|
|
// scientific name to taxon concept in two web service calls.
|
410 |
|
|
List<String> tbUuidList = new ArrayList<String>();//nvn.getTaxonBases();
|
411 |
|
|
List<String> accTbUuidList = new ArrayList<String>();
|
412 |
|
|
String[] tbUuids = doc.getValues("taxonBases.uuid");
|
413 |
|
|
String[] tbClassNames = doc.getValues("taxonBases.classInfo.name");
|
414 |
|
|
for(int i=0;i<tbUuids.length;i++) {
|
415 |
|
|
if(tbClassNames[i].equals("eu.etaxonomy.cdm.model.taxon.Taxon")) {
|
416 |
|
|
accTbUuidList.add(tbUuids[i]);
|
417 |
|
|
}
|
418 |
|
|
}
|
419 |
|
|
// update name search object
|
420 |
|
|
ns.addToResponseList(doc.getValues("titleCache")[0],
|
421 |
|
|
doc.getValues("nameCache")[0],
|
422 |
|
|
searchResult.getMaxScore(),
|
423 |
|
|
doc.getValues("uuid")[0].toString(),
|
424 |
|
|
doc.getValues("taxonBases.uuid"),
|
425 |
|
|
mergeSynAccTaxonUuids(doc.getValues("taxonBases.accTaxon.uuids")));
|
426 |
|
|
}
|
427 |
|
|
}
|
428 |
|
|
nsList.add(ns);
|
429 |
|
|
|
430 |
|
|
} else {
|
431 |
|
|
ErrorResponse er = new ErrorResponse();
|
432 |
|
|
er.setErrorMessage("No Taxon Name matches : " + query);
|
433 |
|
|
nsList.add(er);
|
434 |
|
|
}
|
435 |
|
|
}
|
436 |
|
|
|
437 |
|
|
mv.addObject(nsList);
|
438 |
|
|
return mv;
|
439 |
|
|
}
|
440 |
|
|
|
441 |
|
|
/**
|
442 |
|
|
* Returns a documentation page for the Fuzzy Name Search API.
|
443 |
|
|
* <p>
|
444 |
|
|
* URI: <b>/{datasource-name}/name_catalogue/accepted</b>
|
445 |
|
|
*
|
446 |
|
|
* @param request Http servlet request.
|
447 |
|
|
* @param response Http servlet response.
|
448 |
|
|
* @return Html page describing the Fuzzy Name Search API
|
449 |
|
|
* @throws IOException
|
450 |
|
|
*/
|
451 |
|
|
@RequestMapping(value = { "fuzzy" }, method = RequestMethod.GET, params = {})
|
452 |
|
|
public ModelAndView doGetNameFuzzySearchDocumentation(
|
453 |
|
|
HttpServletRequest request, HttpServletResponse response)
|
454 |
|
|
throws IOException {
|
455 |
|
|
ModelAndView mv = new ModelAndView();
|
456 |
|
|
// Read apt documentation file.
|
457 |
|
|
Resource resource = resourceLoader.getResource("classpath:eu/etaxonomy/cdm/doc/remote/apt/name-catalogue-fuzzy.apt");
|
458 |
|
|
// using input stream as this works for both files in the classes directory
|
459 |
|
|
// as well as files inside jars
|
460 |
|
|
InputStream aptInputStream = resource.getInputStream();
|
461 |
|
|
// Build Html View
|
462 |
|
|
Map<String, String> modelMap = new HashMap<String, String>();
|
463 |
|
|
// Convert Apt to Html
|
464 |
|
|
modelMap.put("html", DocUtils.convertAptToHtml(aptInputStream));
|
465 |
|
|
mv.addAllObjects(modelMap);
|
466 |
|
|
|
467 |
|
|
HtmlView hv = new HtmlView();
|
468 |
|
|
mv.setView(hv);
|
469 |
|
|
return mv;
|
470 |
|
|
}
|
471 |
|
|
/**
|
472 |
|
|
* Returns a list of scientific names similar to the <code>{query}</code>
|
473 |
|
|
* string pattern. Each of these scientific names is accompanied by a list of
|
474 |
|
|
* name uuids, a list of taxon uuids and a list of accepted taxon uuids.
|
475 |
|
|
* The underlying (Lucene FuzzyQuery) string distance metric used is based on a
|
476 |
|
|
* fail-fast Levenshtein distance algorithm (is aborted if it is discovered that
|
477 |
|
|
* the mimimum distance between the words is greater than some threshold)
|
478 |
|
|
* <p>
|
479 |
|
|
* Endpoint documentation can be found <a href="{@docRoot}/../remote/name-catalogue-fuzzy.html">here</a>
|
480 |
|
|
* <p>
|
481 |
|
|
* URI: <b>/{datasource-name}/name_catalogue/fuzzy</b>
|
482 |
|
|
*
|
483 |
|
|
* @param query
|
484 |
|
|
* The scientific name pattern(s) to query for. Any wildcard characters in the
|
485 |
|
|
* query are removed.
|
486 |
|
|
* @param accuracy
|
487 |
|
|
* Similarity measure (between 0 and 1) to impose on the matching algorithm.
|
488 |
|
|
* Briefly described, this is equivalent to the edit distance between the two words, divided by
|
489 |
|
|
* the length of the shorter of the compared terms.
|
490 |
|
|
* @param hits
|
491 |
|
|
* Maximum number of responses to be returned.
|
492 |
|
|
* @param type
|
493 |
|
|
* The type of fuzzy search to call. This can be either
|
494 |
|
|
* "name" : fuzzy searches scientific names corresponding to 'name cache' in CDM or
|
495 |
|
|
* "atomised" : parses the query into atomised elements and fuzzy searches the individual elements in the CDM
|
496 |
|
|
* @param request Http servlet request.
|
497 |
|
|
* @param response Http servlet response.
|
498 |
|
|
* @return a List of {@link NameSearch} objects each corresponding to a
|
499 |
|
|
* single query. These are built from {@link TaxonNameBase} entities
|
500 |
|
|
* which are in turn initialized using the {@link #NAME_SEARCH_INIT_STRATEGY}
|
501 |
|
|
* @throws IOException
|
502 |
|
|
*/
|
503 |
|
|
@RequestMapping(value = { "fuzzy" }, method = RequestMethod.GET, params = {"query"})
|
504 |
|
|
public ModelAndView doGetNameFuzzySearch(@RequestParam(value = "query", required = true) String[] queries,
|
505 |
|
|
@RequestParam(value = "accuracy", required = false, defaultValue = "0.6") String accuracy,
|
506 |
|
|
@RequestParam(value = "hits", required = false, defaultValue = "10") String hits,
|
507 |
|
|
@RequestParam(value = "type", required = false, defaultValue = FUZZY_NAME_CACHE) String type,
|
508 |
|
|
HttpServletRequest request, HttpServletResponse response) throws IOException {
|
509 |
|
|
ModelAndView mv = new ModelAndView();
|
510 |
|
|
List<RemoteResponse> nsList = new ArrayList<RemoteResponse>();
|
511 |
|
|
float acc = 0.5f;
|
512 |
|
|
int h = 10;
|
513 |
|
|
try {
|
514 |
|
|
acc = Float.parseFloat(accuracy);
|
515 |
|
|
h = Integer.parseInt(hits);
|
516 |
|
|
} catch(NumberFormatException nfe) {
|
517 |
|
|
ErrorResponse er = new ErrorResponse();
|
518 |
|
|
er.setErrorMessage("accuracy or hits parameter is not a number");
|
519 |
|
|
mv.addObject(er);
|
520 |
|
|
return mv;
|
521 |
|
|
}
|
522 |
|
|
|
523 |
|
|
if(acc < 0.0 || acc >= 1.0) {
|
524 |
|
|
ErrorResponse er = new ErrorResponse();
|
525 |
|
|
er.setErrorMessage("accuracy should be >= 0.0 and < 1.0");
|
526 |
|
|
mv.addObject(er);
|
527 |
|
|
return mv;
|
528 |
|
|
}
|
529 |
|
|
// search through each query
|
530 |
|
|
for (String query : queries) {
|
531 |
|
|
if(query.equals("")) {
|
532 |
|
|
ErrorResponse er = new ErrorResponse();
|
533 |
|
|
er.setErrorMessage("Empty query field");
|
534 |
|
|
nsList.add(er);
|
535 |
|
|
continue;
|
536 |
|
|
}
|
537 |
|
|
// remove wildcards if any
|
538 |
|
|
String queryWOWildcards = getQueryWithoutWildCards(query);
|
539 |
|
|
// convert first char to upper case
|
540 |
|
|
char[] stringArray = queryWOWildcards.toCharArray();
|
541 |
|
|
stringArray[0] = Character.toUpperCase(stringArray[0]);
|
542 |
|
|
queryWOWildcards = new String(stringArray);
|
543 |
|
|
logger.info("doGetNameSearch()" + request.getRequestURI() + " for query \"" + queryWOWildcards + " with accuracy " + accuracy);
|
544 |
|
|
//List<NonViralName> nameList = new ArrayList<NonViralName>();
|
545 |
|
|
List<DocumentSearchResult> nameSearchList = new ArrayList<DocumentSearchResult>();
|
546 |
|
|
try {
|
547 |
|
|
if(type.equals(FUZZY_ATOMISED)) {
|
548 |
|
|
nameSearchList = service.findByNameFuzzySearch(
|
549 |
|
|
queryWOWildcards,
|
550 |
|
|
acc,
|
551 |
|
|
null,
|
552 |
|
|
false,
|
553 |
|
|
h);
|
554 |
|
|
} else {
|
555 |
|
|
nameSearchList = service.findByFuzzyNameCacheSearch(
|
556 |
|
|
queryWOWildcards,
|
557 |
|
|
acc,
|
558 |
|
|
null,
|
559 |
|
|
false,
|
560 |
|
|
h);
|
561 |
|
|
}
|
562 |
|
|
} catch (ParseException e) {
|
563 |
|
|
// TODO Auto-generated catch block
|
564 |
|
|
//e.printStackTrace();
|
565 |
|
|
ErrorResponse er = new ErrorResponse();
|
566 |
|
|
er.setErrorMessage("Could not parse name : " + queryWOWildcards);
|
567 |
|
|
nsList.add(er);
|
568 |
|
|
continue;
|
569 |
|
|
}
|
570 |
|
|
|
571 |
|
|
|
572 |
|
|
// if search is successful then get related information , else return error
|
573 |
|
|
if (nameSearchList == null || !nameSearchList.isEmpty()) {
|
574 |
|
|
NameSearch ns = new NameSearch();
|
575 |
|
|
ns.setRequest(query);
|
576 |
|
|
|
577 |
|
|
for (DocumentSearchResult searchResult : nameSearchList) {
|
578 |
|
|
for(Document doc : searchResult.getDocs()) {
|
579 |
|
|
|
580 |
|
|
// we need to retrieve both taxon uuid of name queried and
|
581 |
|
|
// the corresponding accepted taxa.
|
582 |
|
|
// reason to return accepted taxa also, is to be able to get from
|
583 |
|
|
// scientific name to taxon concept in two web service calls.
|
584 |
|
|
List<String> tbUuidList = new ArrayList<String>();//nvn.getTaxonBases();
|
585 |
|
|
List<String> accTbUuidList = new ArrayList<String>();
|
586 |
|
|
String[] tbUuids = doc.getValues("taxonBases.uuid");
|
587 |
|
|
String[] tbClassNames = doc.getValues("taxonBases.classInfo.name");
|
588 |
|
|
for(int i=0;i<tbUuids.length;i++) {
|
589 |
|
|
if(tbClassNames[i].equals("eu.etaxonomy.cdm.model.taxon.Taxon")) {
|
590 |
|
|
accTbUuidList.add(tbUuids[i]);
|
591 |
|
|
}
|
592 |
|
|
}
|
593 |
|
|
// update name search object
|
594 |
|
|
ns.addToResponseList(doc.getValues("titleCache")[0],
|
595 |
|
|
doc.getValues("nameCache")[0],
|
596 |
|
|
searchResult.getMaxScore(),
|
597 |
|
|
doc.getValues("uuid")[0].toString(),
|
598 |
|
|
doc.getValues("taxonBases.uuid"),
|
599 |
|
|
mergeSynAccTaxonUuids(doc.getValues("taxonBases.accTaxon.uuids")));
|
600 |
|
|
}
|
601 |
|
|
}
|
602 |
|
|
nsList.add(ns);
|
603 |
|
|
|
604 |
|
|
} else {
|
605 |
|
|
ErrorResponse er = new ErrorResponse();
|
606 |
|
|
er.setErrorMessage("No Taxon Name matches : " + query + ", for given accuracy");
|
607 |
|
|
nsList.add(er);
|
608 |
|
|
}
|
609 |
|
|
}
|
610 |
|
|
|
611 |
|
|
mv.addObject(nsList);
|
612 |
|
|
return mv;
|
613 |
|
|
}
|
614 |
|
|
|
615 |
|
|
private String[] mergeSynAccTaxonUuids(String[] accTaxonUuids) {
|
616 |
|
|
List<String> accTaxonUuidList = new ArrayList<String>();
|
617 |
|
|
for(String accTaxonUuid : accTaxonUuids) {
|
618 |
|
|
for(String uuidListAsString : accTaxonUuid.split(AcceptedTaxonBridge.ACCEPTED_TAXON_UUID_LIST_SEP)) {
|
619 |
|
|
accTaxonUuidList.add(uuidListAsString);
|
620 |
|
|
}
|
621 |
|
|
}
|
622 |
|
|
return accTaxonUuidList.toArray(new String[0]);
|
623 |
|
|
|
624 |
|
|
}
|
625 |
|
|
/**
|
626 |
|
|
* Returns a documentation page for the Name Information API.
|
627 |
|
|
* <p>
|
628 |
|
|
* URI: <b>/{datasource-name}/name_catalogue/name</b>
|
629 |
|
|
*
|
630 |
|
|
* @param request Http servlet request.
|
631 |
|
|
* @param response Http servlet response.
|
632 |
|
|
* @return Html page describing the Name Information API
|
633 |
|
|
* @throws IOException
|
634 |
|
|
*/
|
635 |
|
|
@RequestMapping(value = { "name" }, method = RequestMethod.GET, params = {})
|
636 |
|
|
public ModelAndView doGetNameInformationDocumentation(
|
637 |
|
|
HttpServletRequest request, HttpServletResponse response)
|
638 |
|
|
throws IOException {
|
639 |
|
|
ModelAndView mv = new ModelAndView();
|
640 |
|
|
// Read apt documentation file.
|
641 |
|
|
Resource resource = resourceLoader.getResource("classpath:eu/etaxonomy/cdm/doc/remote/apt/name-catalogue-name-info.apt");
|
642 |
|
|
// using input stream as this works for both files in the classes directory
|
643 |
|
|
// as well as files inside jars
|
644 |
|
|
InputStream aptInputStream = resource.getInputStream();
|
645 |
|
|
// Build Html View
|
646 |
|
|
Map<String, String> modelMap = new HashMap<String, String>();
|
647 |
|
|
// Convert Apt to Html
|
648 |
|
|
modelMap.put("html", DocUtils.convertAptToHtml(aptInputStream));
|
649 |
|
|
mv.addAllObjects(modelMap);
|
650 |
|
|
|
651 |
|
|
HtmlView hv = new HtmlView();
|
652 |
|
|
mv.setView(hv);
|
653 |
|
|
return mv;
|
654 |
|
|
}
|
655 |
|
|
|
656 |
|
|
/**
|
657 |
|
|
* Returns information related to the scientific name matching the given
|
658 |
|
|
* <code>{nameUuid}</code>. The information includes the name string,
|
659 |
|
|
* relationships, rank, list of related lsids / taxon uuids, etc.
|
660 |
|
|
* <p>
|
661 |
|
|
* Endpoint documentation can be found <a href="{@docRoot}/../remote/name-catalogue-name-info.html">here</a>
|
662 |
|
|
* <p>
|
663 |
|
|
* URI: <b>/{datasource-name}/name_catalogue/name</b>
|
664 |
|
|
*
|
665 |
|
|
* @param nameUuids uuid(s) of the scientific name to search for.
|
666 |
|
|
* @param request Http servlet request.
|
667 |
|
|
* @param response Http servlet response.
|
668 |
|
|
* @return a List of {@link NameInformation} objects each corresponding to a
|
669 |
|
|
* single name uuid. These are built from {@link TaxonNameBase} entities
|
670 |
|
|
* which are in turn initialized using the {@link #NAME_INFORMATION_INIT_STRATEGY}
|
671 |
|
|
* @throws IOException
|
672 |
|
|
*/
|
673 |
|
|
@RequestMapping(value = { "name" }, method = RequestMethod.GET, params = {"nameUuid"})
|
674 |
|
|
public ModelAndView doGetNameInformation(@RequestParam(value = "nameUuid", required = true) String[] nameUuids,
|
675 |
|
|
HttpServletRequest request, HttpServletResponse response) throws IOException {
|
676 |
|
|
ModelAndView mv = new ModelAndView();
|
677 |
|
|
List<RemoteResponse> niList = new ArrayList<RemoteResponse>();
|
678 |
|
|
// loop through each name uuid
|
679 |
|
|
for (String nameUuid : nameUuids) {
|
680 |
|
|
logger.info("doGetNameInformation()" + request.getRequestURI() + " for name uuid \""
|
681 |
|
|
+ nameUuid + "\"");
|
682 |
|
|
// find name by uuid
|
683 |
|
|
NonViralName nvn = service.findNameByUuid(UUID.fromString(nameUuid),
|
684 |
|
|
NAME_INFORMATION_INIT_STRATEGY);
|
685 |
|
|
|
686 |
|
|
// if search is successful then get related information, else return error
|
687 |
|
|
if (nvn != null) {
|
688 |
|
|
NameInformation ni = new NameInformation();
|
689 |
|
|
ni.setRequest(nameUuid);
|
690 |
|
|
Reference ref = (Reference) nvn.getNomenclaturalReference();
|
691 |
|
|
String citation = "";
|
692 |
|
|
String citation_details = "";
|
693 |
|
|
if (ref != null) {
|
694 |
|
|
citation = ref.getTitleCache();
|
695 |
|
|
}
|
696 |
|
|
// update name information object
|
697 |
|
|
ni.setResponse(nvn.getTitleCache(), nvn.getNameCache(), nvn.getRank().getTitleCache(),
|
698 |
|
|
nvn.getStatus(), citation, nvn.getRelationsFromThisName(),
|
699 |
|
|
nvn.getRelationsToThisName(), nvn.getTaxonBases());
|
700 |
|
|
niList.add(ni);
|
701 |
|
|
} else {
|
702 |
|
|
ErrorResponse re = new ErrorResponse();
|
703 |
|
|
|
704 |
|
|
if(isValid(nameUuid)) {
|
705 |
|
|
re.setErrorMessage("No Name for given UUID : " + nameUuid);
|
706 |
|
|
} else {
|
707 |
|
|
re.setErrorMessage(nameUuid + " not a valid UUID");
|
708 |
|
|
}
|
709 |
|
|
niList.add(re);
|
710 |
|
|
}
|
711 |
|
|
}
|
712 |
|
|
mv.addObject(niList);
|
713 |
|
|
return mv;
|
714 |
|
|
}
|
715 |
|
|
|
716 |
|
|
/**
|
717 |
|
|
* Returns a documentation page for the Taxon Information API.
|
718 |
|
|
* <p>
|
719 |
|
|
* URI: <b>/{datasource-name}/name_catalogue/taxon</b>
|
720 |
|
|
*
|
721 |
|
|
* @param request Http servlet request.
|
722 |
|
|
* @param response Http servlet response.
|
723 |
|
|
* @return Html page describing the Taxon Information API
|
724 |
|
|
* @throws IOException
|
725 |
|
|
*/
|
726 |
|
|
@RequestMapping(value = { "taxon" }, method = RequestMethod.GET, params = {})
|
727 |
|
|
public ModelAndView doGetTaxonInformation(
|
728 |
|
|
HttpServletRequest request, HttpServletResponse response)
|
729 |
|
|
throws IOException {
|
730 |
|
|
ModelAndView mv = new ModelAndView();
|
731 |
|
|
// Read apt documentation file.
|
732 |
|
|
Resource resource = resourceLoader.getResource("classpath:eu/etaxonomy/cdm/doc/remote/apt/name-catalogue-taxon-info.apt");
|
733 |
|
|
// using input stream as this works for both files in the classes directory
|
734 |
|
|
// as well as files inside jars
|
735 |
|
|
InputStream aptInputStream = resource.getInputStream();
|
736 |
|
|
// Build Html View
|
737 |
|
|
Map<String, String> modelMap = new HashMap<String, String>();
|
738 |
|
|
// Convert Apt to Html
|
739 |
|
|
modelMap.put("html", DocUtils.convertAptToHtml(aptInputStream));
|
740 |
|
|
mv.addAllObjects(modelMap);
|
741 |
|
|
|
742 |
|
|
HtmlView hv = new HtmlView();
|
743 |
|
|
mv.setView(hv);
|
744 |
|
|
return mv;
|
745 |
|
|
}
|
746 |
|
|
|
747 |
|
|
/**
|
748 |
|
|
* Returns information related to the taxon matching the given
|
749 |
|
|
* <code>{taxonUuid}</code>. The information includes the name cache, title cache
|
750 |
|
|
* relationship type, taxonomic status, information , etc.
|
751 |
|
|
* <p>
|
752 |
|
|
* Endpoint documentation can be found <a href="{@docRoot}/../remote/name-catalogue-taxon-info.html">here</a>
|
753 |
|
|
* <p>
|
754 |
|
|
* URI: <b>/{datasource-name}/name_catalogue</b>
|
755 |
|
|
*
|
756 |
|
|
* @param taxonUuid
|
757 |
|
|
* The taxon uuid to query for. The classification returned corresponds
|
758 |
|
|
* to the first in the alphabetically sorted list of classifications
|
759 |
|
|
* currently available in the database.
|
760 |
|
|
*
|
761 |
|
|
* @param request Http servlet request.
|
762 |
|
|
* @param response Http servlet response.
|
763 |
|
|
* @return a List of {@link TaxonInformation} objects each corresponding to a
|
764 |
|
|
* single query. These are built from {@TaxonBase} entities which are
|
765 |
|
|
* in turn initialized using the {@link #TAXON_INFORMATION_INIT_STRATEGY}
|
766 |
|
|
* @throws IOException
|
767 |
|
|
*/
|
768 |
|
|
// @RequestMapping(value = { "taxon" }, method = RequestMethod.GET,params = {"taxonUuid"})
|
769 |
|
|
// public ModelAndView doGetTaxonInformation(
|
770 |
|
|
// @RequestParam(value = "taxonUuid", required = true) String[] taxonUuids,
|
771 |
|
|
// HttpServletRequest request, HttpServletResponse response) throws IOException {
|
772 |
|
|
// return doGetTaxonInformation(taxonUuids,CLASSIFICATION_DEFAULT, new String[]{},request, response);
|
773 |
|
|
// }
|
774 |
|
|
|
775 |
|
|
/**
|
776 |
|
|
* Returns information related to the taxon matching the given
|
777 |
|
|
* <code>{taxonUuid}</code>. The information includes the name cache, title cache
|
778 |
|
|
* relationship type, taxonomic status, information , etc.
|
779 |
|
|
* <p>
|
780 |
|
|
* Endpoint documentation can be found <a href="{@docRoot}/../remote/name-catalogue-taxon-info.html">here</a>
|
781 |
|
|
* <p>
|
782 |
|
|
* URI: <b>/{datasource-name}/name_catalogue/taxon</b>
|
783 |
|
|
*
|
784 |
|
|
* @param taxonUuid
|
785 |
|
|
* The taxon uuid to query for.
|
786 |
|
|
* @param classification
|
787 |
|
|
* [Optional] String representing the taxonomic classification to use for
|
788 |
|
|
* building the classification tree. Defaults to the first in the alphabetically
|
789 |
|
|
* sorted list of classifications currently available in the database.
|
790 |
|
|
* @param include
|
791 |
|
|
* Array of data types to be included in addition to the normal response
|
792 |
|
|
*
|
793 |
|
|
* @param request Http servlet request.
|
794 |
|
|
* @param response Http servlet response.
|
795 |
|
|
* @return a List of {@link TaxonInformation} objects each corresponding to a
|
796 |
|
|
* single query. These are built from {@TaxonBase} entities which are
|
797 |
|
|
* in turn initialized using the {@link #TAXON_INFORMATION_INIT_STRATEGY}
|
798 |
|
|
* @throws IOException
|
799 |
|
|
*/
|
800 |
|
|
@RequestMapping(value = { "taxon" }, method = RequestMethod.GET, params = {"taxonUuid"})
|
801 |
|
|
public ModelAndView doGetTaxonInformation(
|
802 |
|
|
@RequestParam(value = "taxonUuid", required = true) String[] taxonUuids,
|
803 |
|
|
@RequestParam(value = "classification", required = false, defaultValue = CLASSIFICATION_DEFAULT) String classificationType,
|
804 |
|
|
@RequestParam(value = "include", required = false, defaultValue = "") String[] includes,
|
805 |
|
|
HttpServletRequest request, HttpServletResponse response) throws IOException {
|
806 |
|
|
ModelAndView mv = new ModelAndView();
|
807 |
|
|
List<RemoteResponse> tiList = new ArrayList<RemoteResponse>();
|
808 |
|
|
// loop through each name uuid
|
809 |
|
|
for (String taxonUuid : taxonUuids) {
|
810 |
|
|
logger.info("doGetTaxonInformation()" + request.getRequestURI() + " for taxon uuid \""
|
811 |
|
|
+ taxonUuid);
|
812 |
|
|
// find name by uuid
|
813 |
|
|
TaxonBase tb = taxonService.findTaxonByUuid(UUID.fromString(taxonUuid),
|
814 |
|
|
TAXON_INFORMATION_INIT_STRATEGY);
|
815 |
|
|
|
816 |
|
|
// if search is successful then get related information, else return error
|
817 |
|
|
if (tb != null) {
|
818 |
|
|
TaxonInformation ti = new TaxonInformation();
|
819 |
|
|
ti.setRequest(taxonUuid);
|
820 |
|
|
// check if result (taxon base) is a taxon or synonym
|
821 |
|
|
if (tb.isInstanceOf(Taxon.class)) {
|
822 |
|
|
Taxon taxon = (Taxon) tb;
|
823 |
|
|
// build classification map
|
824 |
|
|
boolean includeUuids = Arrays.asList(includes).contains(INCLUDE_CLUUIDS);
|
825 |
|
|
Map classificationMap = getClassification(taxon, classificationType, includeUuids);
|
826 |
|
|
|
827 |
162aa018
|
l.morris
|
logger.info("taxon uuid " + taxon.getUuid().toString() + " original hash code : " + System.identityHashCode(taxon) + ", name class " + taxon.getName().getClass().getName());
|
828 |
236ab161
|
Andreas Müller
|
// update taxon information object with taxon related data
|
829 |
|
|
NonViralName nvn = CdmBase.deproxy(taxon.getName(),NonViralName.class);
|
830 |
|
|
|
831 |
|
|
String secTitle = "" ;
|
832 |
|
|
String modified = "";
|
833 |
|
|
if(taxon.getSec() != null) {
|
834 |
|
|
secTitle = taxon.getSec().getTitleCache();
|
835 |
|
|
DateTime dt = taxon.getUpdated();
|
836 |
|
|
modified = fmt.print(dt);
|
837 |
|
|
}
|
838 |
|
|
|
839 |
|
|
Set<IdentifiableSource> sources = taxon.getSources();
|
840 |
|
|
String[] didname = getDatasetIdName(sources);
|
841 |
|
|
|
842 |
|
|
ti.setResponseTaxon(tb.getTitleCache(),
|
843 |
|
|
nvn.getTitleCache(),
|
844 |
|
|
nvn.getRank().getTitleCache(),
|
845 |
|
|
ACCEPTED_NAME_STATUS,
|
846 |
|
|
buildFlagMap(tb),
|
847 |
|
|
classificationMap,
|
848 |
|
|
"",
|
849 |
|
|
didname[0],
|
850 |
|
|
didname[1],
|
851 |
|
|
secTitle,
|
852 |
|
|
modified,
|
853 |
|
|
taxon.getLsid().toString());
|
854 |
|
|
|
855 |
|
|
|
856 |
|
|
Set<SynonymRelationship> synRelationships = taxon.getSynonymRelations();
|
857 |
|
|
// add synonyms (if exists) to taxon information object
|
858 |
|
|
for (SynonymRelationship sr : synRelationships) {
|
859 |
|
|
Synonym syn = sr.getSynonym();
|
860 |
|
|
String uuid = syn.getUuid().toString();
|
861 |
|
|
String title = syn.getTitleCache();
|
862 |
|
|
TaxonNameBase synnvn = syn.getName();
|
863 |
|
|
String name = synnvn.getTitleCache();
|
864 |
|
|
String rank = (synnvn.getRank() == null)? "" : synnvn.getRank().getTitleCache();
|
865 |
|
|
String status = SYNONYM_STATUS;
|
866 |
|
|
String relLabel = sr.getType()
|
867 |
|
|
.getInverseRepresentation(Language.DEFAULT())
|
868 |
|
|
.getLabel();
|
869 |
|
|
|
870 |
|
|
secTitle = "" ;
|
871 |
|
|
modified = "";
|
872 |
|
|
if(syn.getSec() != null) {
|
873 |
|
|
secTitle = syn.getSec().getTitleCache();
|
874 |
|
|
DateTime dt = syn.getUpdated();
|
875 |
|
|
modified = fmt.print(dt);
|
876 |
|
|
}
|
877 |
|
|
|
878 |
|
|
sources = syn.getSources();
|
879 |
|
|
didname = getDatasetIdName(sources);
|
880 |
|
|
|
881 |
|
|
ti.addToResponseRelatedTaxa(uuid,
|
882 |
|
|
title,
|
883 |
|
|
name,
|
884 |
|
|
rank,
|
885 |
|
|
status,
|
886 |
|
|
relLabel,
|
887 |
|
|
"",
|
888 |
|
|
didname[0],
|
889 |
|
|
didname[1],
|
890 |
|
|
secTitle,
|
891 |
|
|
modified);
|
892 |
|
|
}
|
893 |
|
|
|
894 |
|
|
// build relationship information as,
|
895 |
|
|
// - relationships from the requested taxon
|
896 |
|
|
Set<TaxonRelationship> trFromSet = taxon.getRelationsFromThisTaxon();
|
897 |
|
|
for (TaxonRelationship tr : trFromSet) {
|
898 |
|
|
String titleTo = tr.getToTaxon().getTitleCache();
|
899 |
|
|
TaxonNameBase tonvn = tr.getToTaxon().getName();
|
900 |
|
|
String name = tonvn.getTitleCache();
|
901 |
|
|
String rank = tonvn.getRank().getTitleCache();
|
902 |
|
|
String uuid = tr.getToTaxon().getUuid().toString();
|
903 |
|
|
String status = ACCEPTED_NAME_STATUS;
|
904 |
|
|
String relLabel = tr.getType().getRepresentation(Language.DEFAULT())
|
905 |
|
|
.getLabel();
|
906 |
|
|
|
907 |
|
|
secTitle = "" ;
|
908 |
|
|
modified = "";
|
909 |
|
|
if(tr.getToTaxon().getSec() != null) {
|
910 |
|
|
secTitle = tr.getToTaxon().getSec().getTitleCache();
|
911 |
|
|
DateTime dt = tr.getToTaxon().getUpdated();
|
912 |
|
|
modified = fmt.print(dt);
|
913 |
|
|
}
|
914 |
|
|
|
915 |
|
|
sources = tr.getToTaxon().getSources();
|
916 |
|
|
didname = getDatasetIdName(sources);
|
917 |
|
|
|
918 |
|
|
ti.addToResponseRelatedTaxa(uuid,
|
919 |
|
|
titleTo,
|
920 |
|
|
name,
|
921 |
|
|
rank,
|
922 |
|
|
status,
|
923 |
|
|
relLabel,
|
924 |
|
|
"",
|
925 |
|
|
didname[0],
|
926 |
|
|
didname[1],
|
927 |
|
|
secTitle,
|
928 |
|
|
modified);
|
929 |
|
|
//logger.info("titleTo : " + titleTo + " , name : " + name);
|
930 |
|
|
}
|
931 |
|
|
|
932 |
|
|
// - relationships to the requested taxon
|
933 |
|
|
Set<TaxonRelationship> trToSet = taxon.getRelationsToThisTaxon();
|
934 |
|
|
for (TaxonRelationship tr : trToSet) {
|
935 |
|
|
String titleFrom = tr.getFromTaxon().getTitleCache();
|
936 |
|
|
TaxonNameBase fromnvn = tr.getFromTaxon().getName();
|
937 |
|
|
String name = fromnvn.getTitleCache();
|
938 |
|
|
String rank = fromnvn.getRank().getTitleCache();
|
939 |
|
|
String uuid = tr.getFromTaxon().getUuid().toString();
|
940 |
|
|
String status = ACCEPTED_NAME_STATUS;
|
941 |
|
|
String relLabel = tr.getType()
|
942 |
|
|
.getInverseRepresentation(Language.DEFAULT())
|
943 |
|
|
.getLabel();
|
944 |
|
|
|
945 |
|
|
if(tr.getFromTaxon().getSec() != null) {
|
946 |
|
|
secTitle = tr.getFromTaxon().getSec().getTitleCache();
|
947 |
|
|
DateTime dt = tr.getFromTaxon().getSec().getUpdated();
|
948 |
|
|
modified = fmt.print(dt);
|
949 |
|
|
}
|
950 |
|
|
|
951 |
|
|
sources = tr.getFromTaxon().getSources();
|
952 |
|
|
didname = getDatasetIdName(sources);
|
953 |
|
|
|
954 |
|
|
secTitle = (tr.getFromTaxon().getSec() == null) ? "" : tr.getFromTaxon().getSec().getTitleCache();
|
955 |
|
|
ti.addToResponseRelatedTaxa(uuid,
|
956 |
|
|
titleFrom,
|
957 |
|
|
name,
|
958 |
|
|
rank,
|
959 |
|
|
status,
|
960 |
|
|
relLabel,
|
961 |
|
|
"",
|
962 |
|
|
didname[0],
|
963 |
|
|
didname[1],
|
964 |
|
|
secTitle,
|
965 |
|
|
modified);
|
966 |
|
|
//logger.info("titleFrom : " + titleFrom + " , name : " + name);
|
967 |
|
|
}
|
968 |
|
|
} else if (tb instanceof Synonym) {
|
969 |
|
|
Synonym synonym = (Synonym) tb;
|
970 |
|
|
TaxonNameBase nvn = synonym.getName();
|
971 |
|
|
// update taxon information object with synonym related data
|
972 |
|
|
DateTime dt = synonym.getUpdated();
|
973 |
|
|
String modified = fmt.print(dt);
|
974 |
|
|
|
975 |
|
|
Set<IdentifiableSource> sources = synonym.getSources();
|
976 |
|
|
String[] didname = getDatasetIdName(sources);
|
977 |
|
|
|
978 |
|
|
String secTitle = (synonym.getSec() == null) ? "" : synonym.getSec().getTitleCache();
|
979 |
|
|
ti.setResponseTaxon(synonym.getTitleCache(),
|
980 |
|
|
nvn.getTitleCache(),
|
981 |
|
|
nvn.getRank().getTitleCache(),
|
982 |
|
|
SYNONYM_STATUS,
|
983 |
|
|
buildFlagMap(synonym),
|
984 |
|
|
new TreeMap<String,Map>(),
|
985 |
|
|
"",
|
986 |
|
|
didname[0],
|
987 |
|
|
didname[1],
|
988 |
|
|
secTitle,
|
989 |
|
|
modified, null);
|
990 |
|
|
// add accepted taxa (if exists) to taxon information object
|
991 |
|
|
|
992 |
|
|
Set<SynonymRelationship> synRelationships = synonym.getSynonymRelations();
|
993 |
|
|
for (SynonymRelationship sr : synRelationships) {
|
994 |
|
|
Taxon accTaxon = sr.getAcceptedTaxon();
|
995 |
|
|
String uuid = accTaxon.getUuid().toString();
|
996 |
|
|
logger.info("acc taxon uuid " + accTaxon.getUuid().toString() + " original hash code : " + System.identityHashCode(accTaxon) + ", name class " + accTaxon.getName().getClass().getName());
|
997 |
|
|
String title = accTaxon.getTitleCache();
|
998 |
|
|
logger.info("taxon title cache : " + accTaxon.getTitleCache());
|
999 |
|
|
|
1000 |
|
|
TaxonNameBase accnvn = accTaxon.getName();
|
1001 |
|
|
String name = accnvn.getTitleCache();
|
1002 |
|
|
String rank = accnvn.getRank().getTitleCache();
|
1003 |
|
|
String status = ACCEPTED_NAME_STATUS;
|
1004 |
|
|
String relLabel = sr.getType().getRepresentation(Language.DEFAULT())
|
1005 |
|
|
.getLabel();
|
1006 |
|
|
dt = accTaxon.getUpdated();
|
1007 |
|
|
modified = fmt.print(dt);
|
1008 |
|
|
|
1009 |
|
|
sources = accTaxon.getSources();
|
1010 |
|
|
didname = getDatasetIdName(sources);
|
1011 |
|
|
|
1012 |
|
|
secTitle = (accTaxon.getSec() == null) ? "" : accTaxon.getSec().getTitleCache();
|
1013 |
|
|
ti.addToResponseRelatedTaxa(uuid,
|
1014 |
|
|
title,
|
1015 |
|
|
name,
|
1016 |
|
|
rank,
|
1017 |
|
|
status,
|
1018 |
|
|
relLabel,
|
1019 |
|
|
"",
|
1020 |
|
|
didname[0],
|
1021 |
|
|
didname[1],
|
1022 |
|
|
secTitle,
|
1023 |
|
|
modified);
|
1024 |
|
|
}
|
1025 |
|
|
|
1026 |
|
|
}
|
1027 |
|
|
tiList.add(ti);
|
1028 |
|
|
} else {
|
1029 |
|
|
ErrorResponse re = new ErrorResponse();
|
1030 |
|
|
if(isValid(taxonUuid)) {
|
1031 |
|
|
re.setErrorMessage("No Taxon for given UUID : " + taxonUuid);
|
1032 |
|
|
} else {
|
1033 |
|
|
re.setErrorMessage(taxonUuid + " not a valid UUID");
|
1034 |
|
|
}
|
1035 |
|
|
tiList.add(re);
|
1036 |
|
|
}
|
1037 |
|
|
}
|
1038 |
|
|
mv.addObject(tiList);
|
1039 |
|
|
return mv;
|
1040 |
|
|
}
|
1041 |
|
|
|
1042 |
|
|
/**
|
1043 |
|
|
* Returns a documentation page for the Accepted Name Search API.
|
1044 |
|
|
* <p>
|
1045 |
|
|
* URI: <b>/{datasource-name}/name_catalogue/accepted</b>
|
1046 |
|
|
*
|
1047 |
|
|
* @param request Http servlet request.
|
1048 |
|
|
* @param response Http servlet response.
|
1049 |
|
|
* @return Html page describing the Accepted Name Search API
|
1050 |
|
|
* @throws IOException
|
1051 |
|
|
*/
|
1052 |
|
|
@RequestMapping(value = { "accepted" }, method = RequestMethod.GET, params = {})
|
1053 |
|
|
public ModelAndView doGetAcceptedNameSearchDocumentation(
|
1054 |
|
|
HttpServletRequest request, HttpServletResponse response)
|
1055 |
|
|
throws IOException {
|
1056 |
|
|
ModelAndView mv = new ModelAndView();
|
1057 |
|
|
// Read apt documentation file.
|
1058 |
|
|
Resource resource = resourceLoader.getResource("classpath:eu/etaxonomy/cdm/doc/remote/apt/name-catalogue-accepted.apt");
|
1059 |
|
|
// using input stream as this works for both files in the classes directory
|
1060 |
|
|
// as well as files inside jars
|
1061 |
|
|
InputStream aptInputStream = resource.getInputStream();
|
1062 |
|
|
// Build Html View
|
1063 |
|
|
Map<String, String> modelMap = new HashMap<String, String>();
|
1064 |
|
|
// Convert Apt to Html
|
1065 |
|
|
modelMap.put("html", DocUtils.convertAptToHtml(aptInputStream));
|
1066 |
|
|
mv.addAllObjects(modelMap);
|
1067 |
|
|
|
1068 |
|
|
HtmlView hv = new HtmlView();
|
1069 |
|
|
mv.setView(hv);
|
1070 |
|
|
return mv;
|
1071 |
|
|
}
|
1072 |
|
|
|
1073 |
|
|
/**
|
1074 |
|
|
* Returns a list of accepted names of input scientific names matching the <code>{query}</code>
|
1075 |
|
|
* string pattern. Each of these scientific names is accompanied by a list of
|
1076 |
|
|
* name uuids, a list of taxon uuids and a list of accepted taxon uuids.
|
1077 |
|
|
* <p>
|
1078 |
|
|
* Endpoint documentation can be found <a href="{@docRoot}/../remote/name-catalogue-default.html">here</a>
|
1079 |
|
|
* <p>
|
1080 |
|
|
* URI: <b>/{datasource-name}/name_catalogue</b>
|
1081 |
|
|
*
|
1082 |
|
|
* @param query
|
1083 |
|
|
* The scientific name pattern(s) to query for. The query can
|
1084 |
|
|
* contain wildcard characters ('*'). The query can be
|
1085 |
|
|
* performed with no wildcard or with the wildcard at the
|
1086 |
|
|
* begin and / or end depending on the search pattern.
|
1087 |
|
|
* @param type
|
1088 |
|
|
* The type of name to query. This be either
|
1089 |
|
|
* "name" : scientific name corresponding to 'name cache' in CDM or
|
1090 |
|
|
* "title" : complete name corresponding to 'title cache' in CDM
|
1091 |
|
|
* @param request Http servlet request.
|
1092 |
|
|
* @param response Http servlet response.
|
1093 |
|
|
* @return a List of {@link NameSearch} objects each corresponding to a
|
1094 |
|
|
* single query. These are built from {@link TaxonNameBase} entities
|
1095 |
|
|
* which are in turn initialized using the {@link #NAME_SEARCH_INIT_STRATEGY}
|
1096 |
|
|
* @throws IOException
|
1097 |
|
|
*/
|
1098 |
|
|
@RequestMapping(value = { "accepted" }, method = RequestMethod.GET, params = {"query"})
|
1099 |
|
|
public ModelAndView doGetAcceptedNameSearch(@RequestParam(value = "query", required = true) String[] queries,
|
1100 |
|
|
HttpServletRequest request, HttpServletResponse response) throws IOException {
|
1101 |
|
|
return doGetAcceptedNameSearch(queries, DEFAULT_SEARCH_TYPE, request, response);
|
1102 |
|
|
}
|
1103 |
|
|
/**
|
1104 |
|
|
* Returns a list of accepted names of input scientific names matching the <code>{query}</code>
|
1105 |
|
|
* string pattern. Each of these scientific names is accompanied by a list of
|
1106 |
|
|
* name uuids, a list of taxon uuids and a list of accepted taxon uuids.
|
1107 |
|
|
* <p>
|
1108 |
|
|
* Endpoint documentation can be found <a href="{@docRoot}/../remote/name-catalogue-default.html">here</a>
|
1109 |
|
|
* <p>
|
1110 |
|
|
* URI: <b>/{datasource-name}/name_catalogue</b>
|
1111 |
|
|
*
|
1112 |
|
|
* @param query
|
1113 |
|
|
* The scientific name pattern(s) to query for. The query can
|
1114 |
|
|
* contain wildcard characters ('*'). The query can be
|
1115 |
|
|
* performed with no wildcard or with the wildcard at the
|
1116 |
|
|
* begin and / or end depending on the search pattern.
|
1117 |
|
|
* @param type
|
1118 |
|
|
* The type of name to query. This be either
|
1119 |
|
|
* "name" : scientific name corresponding to 'name cache' in CDM or
|
1120 |
|
|
* "title" : complete name corresponding to 'title cache' in CDM
|
1121 |
|
|
* @param request Http servlet request.
|
1122 |
|
|
* @param response Http servlet response.
|
1123 |
|
|
* @return a List of {@link NameSearch} objects each corresponding to a
|
1124 |
|
|
* single query. These are built from {@link TaxonNameBase} entities
|
1125 |
|
|
* which are in turn initialized using the {@link #NAME_SEARCH_INIT_STRATEGY}
|
1126 |
|
|
* @throws IOException
|
1127 |
|
|
*/
|
1128 |
|
|
@RequestMapping(value = { "accepted" }, method = RequestMethod.GET, params = {"query", "type"})
|
1129 |
|
|
public ModelAndView doGetAcceptedNameSearch(@RequestParam(value = "query", required = true) String[] queries,
|
1130 |
|
|
@RequestParam(value = "type", required = false, defaultValue = DEFAULT_SEARCH_TYPE) String searchType,
|
1131 |
|
|
HttpServletRequest request, HttpServletResponse response) throws IOException {
|
1132 |
|
|
ModelAndView mv = new ModelAndView();
|
1133 |
|
|
List<RemoteResponse> ansList = new ArrayList<RemoteResponse>();
|
1134 |
|
|
logger.info("doGetAcceptedNameSearch()");
|
1135 |
|
|
|
1136 |
|
|
// if search type is not known then return error
|
1137 |
|
|
if (!searchType.equals(NAME_SEARCH) && !searchType.equals(TITLE_SEARCH)) {
|
1138 |
|
|
ErrorResponse er = new ErrorResponse();
|
1139 |
|
|
er.setErrorMessage("searchType parameter can only be set as" + NAME_SEARCH + " or "
|
1140 |
|
|
+ TITLE_SEARCH);
|
1141 |
|
|
mv.addObject(er);
|
1142 |
|
|
return mv;
|
1143 |
|
|
}
|
1144 |
|
|
|
1145 |
|
|
// search through each query
|
1146 |
|
|
for (String query : queries) {
|
1147 |
|
|
|
1148 |
|
|
//String queryWOWildcards = getQueryWithoutWildCards(query);
|
1149 |
|
|
//MatchMode mm = getMatchModeFromQuery(query);
|
1150 |
|
|
logger.info("doGetAcceptedNameSearch()" + request.getRequestURI() + " for query \"" + query);
|
1151 |
|
|
List<NonViralName> nameList = new ArrayList<NonViralName>();
|
1152 |
|
|
|
1153 |
|
|
// if "name" search then find by name cache
|
1154 |
|
|
if (searchType.equals(NAME_SEARCH)) {
|
1155 |
|
|
nameList = service.findNamesByNameCache(query, MatchMode.EXACT,
|
1156 |
|
|
ACC_NAME_SEARCH_INIT_STRATEGY);
|
1157 |
|
|
}
|
1158 |
|
|
|
1159 |
|
|
//if "title" search then find by title cache
|
1160 |
|
|
if (searchType.equals(TITLE_SEARCH)) {
|
1161 |
|
|
nameList = service.findNamesByTitleCache(query, MatchMode.EXACT,
|
1162 |
|
|
ACC_NAME_SEARCH_INIT_STRATEGY);
|
1163 |
|
|
}
|
1164 |
|
|
|
1165 |
|
|
// if search is successful then get related information , else return error
|
1166 |
|
|
if (nameList == null || !nameList.isEmpty()) {
|
1167 |
|
|
AcceptedNameSearch ans = new AcceptedNameSearch();
|
1168 |
|
|
ans.setRequest(query);
|
1169 |
|
|
|
1170 |
|
|
for (NonViralName nvn : nameList) {
|
1171 |
|
|
// we need to retrieve both taxon uuid of name queried and
|
1172 |
|
|
// the corresponding accepted taxa.
|
1173 |
|
|
// reason to return accepted taxa also, is to be able to get from
|
1174 |
|
|
// scientific name to taxon concept in two web service calls.
|
1175 |
|
|
Set<TaxonBase> tbSet = nvn.getTaxonBases();
|
1176 |
|
|
Set<TaxonBase> accTbSet = new HashSet<TaxonBase>();
|
1177 |
|
|
for (TaxonBase tb : tbSet) {
|
1178 |
|
|
// if synonym then get accepted taxa.
|
1179 |
|
|
if (tb instanceof Synonym) {
|
1180 |
|
|
Synonym synonym = (Synonym) tb;
|
1181 |
|
|
Set<SynonymRelationship> synRelationships = synonym.getSynonymRelations();
|
1182 |
|
|
for (SynonymRelationship sr : synRelationships) {
|
1183 |
|
|
Taxon accTaxon = sr.getAcceptedTaxon();
|
1184 |
|
|
NonViralName accNvn = CdmBase.deproxy(accTaxon.getName(),NonViralName.class);
|
1185 |
|
|
Map classificationMap = getClassification(accTaxon, CLASSIFICATION_DEFAULT, false);
|
1186 |
|
|
ans.addToResponseList(accNvn.getNameCache(),accNvn.getAuthorshipCache(), accNvn.getRank().getTitleCache(),classificationMap);
|
1187 |
|
|
}
|
1188 |
|
|
} else {
|
1189 |
|
|
Taxon taxon = (Taxon)tb;
|
1190 |
|
|
Set<TaxonRelationship> trFromSet = taxon.getRelationsFromThisTaxon();
|
1191 |
|
|
boolean isConceptRelationship = true;
|
1192 |
|
|
if(trFromSet.size() == 1) {
|
1193 |
|
|
for (TaxonRelationship tr : trFromSet) {
|
1194 |
|
|
if(!tr.getType().isConceptRelationship()) {
|
1195 |
|
|
// this is not a concept relationship, so it does not have an
|
1196 |
|
|
// accepted name
|
1197 |
|
|
isConceptRelationship = false;
|
1198 |
|
|
|
1199 |
|
|
}
|
1200 |
|
|
}
|
1201 |
|
|
}
|
1202 |
|
|
if(isConceptRelationship) {
|
1203 |
|
|
Map classificationMap = getClassification(taxon, CLASSIFICATION_DEFAULT, false);
|
1204 |
|
|
ans.addToResponseList(nvn.getNameCache(), nvn.getAuthorshipCache(),nvn.getRank().getTitleCache(), classificationMap);
|
1205 |
|
|
}
|
1206 |
|
|
|
1207 |
|
|
}
|
1208 |
|
|
}
|
1209 |
|
|
// update name search object
|
1210 |
|
|
|
1211 |
|
|
}
|
1212 |
|
|
ansList.add(ans);
|
1213 |
|
|
|
1214 |
|
|
} else {
|
1215 |
|
|
ErrorResponse er = new ErrorResponse();
|
1216 |
|
|
er.setErrorMessage("No Taxon Name for given query : " + query);
|
1217 |
|
|
ansList.add(er);
|
1218 |
|
|
}
|
1219 |
|
|
}
|
1220 |
|
|
|
1221 |
|
|
mv.addObject(ansList);
|
1222 |
|
|
return mv;
|
1223 |
|
|
}
|
1224 |
|
|
|
1225 |
|
|
/**
|
1226 |
|
|
* Returns a list of all available classifications (with associated referenc information) and the default classification.
|
1227 |
|
|
* <p>
|
1228 |
|
|
* Endpoint documentation can be found <a href="{@docRoot}/../remote/name-catalogue-classification-info.html">here</a>
|
1229 |
|
|
* <p>
|
1230 |
|
|
* URI: <b>/{datasource-name}/name_catalogue/voc/classification</b>
|
1231 |
|
|
*
|
1232 |
|
|
* @param request Http servlet request.
|
1233 |
|
|
* @param response Http servlet response.
|
1234 |
|
|
* @return a List of {@link Classification} objects represebted as strings.
|
1235 |
|
|
* These are initialized using the {@link #VOC_CLASSIFICATION_INIT_STRATEGY}
|
1236 |
|
|
* @throws IOException
|
1237 |
|
|
*/
|
1238 |
|
|
@RequestMapping(value = { "voc/classification" }, method = RequestMethod.GET, params = {})
|
1239 |
|
|
public ModelAndView doGetClassificationMap(HttpServletRequest request, HttpServletResponse response) throws IOException {
|
1240 |
|
|
List<Map> cmapList = new ArrayList<Map>();
|
1241 |
|
|
Map<String, String> classifications = new HashMap<String, String>();
|
1242 |
|
|
ModelAndView mv = new ModelAndView();
|
1243 |
|
|
List<Classification> clist = getClassificationList(100);
|
1244 |
|
|
boolean isFirst = true;
|
1245 |
|
|
Iterator itr = clist.iterator();
|
1246 |
|
|
// loop through all classifications and populate map with
|
1247 |
|
|
// (classificationKey, reference) elements
|
1248 |
|
|
while(itr.hasNext()) {
|
1249 |
|
|
Classification c = (Classification) itr.next();
|
1250 |
|
|
String refTitleCache = "";
|
1251 |
|
|
String classificationKey = removeInternalWhitespace(c.getTitleCache());
|
1252 |
|
|
if(c.getReference() != null) {
|
1253 |
|
|
refTitleCache = c.getReference().getTitleCache();
|
1254 |
|
|
c.getAllNodes();
|
1255 |
|
|
}
|
1256 |
|
|
// default is the first element of the list
|
1257 |
|
|
// always created with the same sorting (DESCENDING)
|
1258 |
|
|
if(isFirst) {
|
1259 |
|
|
Map<String, String> defaultMap = new HashMap<String, String>();
|
1260 |
|
|
defaultMap.put("default", classificationKey);
|
1261 |
|
|
cmapList.add(defaultMap);
|
1262 |
|
|
isFirst = false;
|
1263 |
|
|
}
|
1264 |
|
|
classifications.put(classificationKey, refTitleCache);
|
1265 |
|
|
|
1266 |
|
|
}
|
1267 |
|
|
Map<String, Map> cmap = new HashMap<String, Map>();
|
1268 |
|
|
cmap.put("classification",classifications);
|
1269 |
|
|
cmapList.add(cmap);
|
1270 |
|
|
mv.addObject(cmapList);
|
1271 |
|
|
return mv;
|
1272 |
|
|
}
|
1273 |
|
|
|
1274 |
|
|
/**
|
1275 |
|
|
* Returns the Dataset ID / Name of the given original source.
|
1276 |
|
|
* FIXME: Very hacky and needs to be revisited. Mainly for deciding on which objects to use during import.
|
1277 |
|
|
* FIXME: dataset id is mapped to a DWC term - is that right?
|
1278 |
|
|
*
|
1279 |
|
|
* @param sources Set of sources attached to a taxa / synonym
|
1280 |
|
|
*
|
1281 |
|
|
*
|
1282 |
|
|
* @return String array where [0] is the datsetID and [1] is the datsetName
|
1283 |
|
|
*/
|
1284 |
|
|
private String[] getDatasetIdName(Set<IdentifiableSource> sources) {
|
1285 |
|
|
String didname[] = {"",""};
|
1286 |
|
|
Iterator<IdentifiableSource> itr = sources.iterator();
|
1287 |
|
|
while(itr.hasNext()) {
|
1288 |
|
|
IdentifiableSource source = itr.next();
|
1289 |
|
|
Reference ref = source.getCitation();
|
1290 |
|
|
Set<IdentifiableSource> ref_sources = ref.getSources();
|
1291 |
|
|
Iterator<IdentifiableSource> ref_itr = ref_sources.iterator();
|
1292 |
|
|
while(ref_itr.hasNext()) {
|
1293 |
|
|
IdentifiableSource ref_source = ref_itr.next();
|
1294 |
|
|
if(ref_source.getIdNamespace().equals(DWC_DATASET_ID)) {
|
1295 |
|
|
didname[0] = ref_source.getIdInSource();
|
1296 |
|
|
break;
|
1297 |
|
|
}
|
1298 |
|
|
}
|
1299 |
|
|
if(!didname[0].isEmpty()) {
|
1300 |
|
|
didname[1] = ref.getTitleCache();
|
1301 |
|
|
break;
|
1302 |
|
|
}
|
1303 |
|
|
}
|
1304 |
|
|
return didname;
|
1305 |
|
|
}
|
1306 |
|
|
|
1307 |
|
|
/**
|
1308 |
|
|
* Returns the match mode by parsing the input string of wildcards.
|
1309 |
|
|
*
|
1310 |
|
|
* @param query
|
1311 |
|
|
* String to parse.
|
1312 |
|
|
*
|
1313 |
|
|
* @return {@link MatchMode} depending on the the position of the wildcard (*)
|
1314 |
|
|
*/
|
1315 |
|
|
private MatchMode getMatchModeFromQuery(String query) {
|
1316 |
|
|
if (query.startsWith("*") && query.endsWith("*")) {
|
1317 |
|
|
return MatchMode.ANYWHERE;
|
1318 |
|
|
} else if (query.startsWith("*")) {
|
1319 |
|
|
return MatchMode.END;
|
1320 |
|
|
} else if (query.endsWith("*")) {
|
1321 |
|
|
return MatchMode.BEGINNING;
|
1322 |
|
|
} else {
|
1323 |
|
|
return MatchMode.EXACT;
|
1324 |
|
|
}
|
1325 |
|
|
}
|
1326 |
|
|
|
1327 |
|
|
/**
|
1328 |
|
|
* Removes wildcards from the input string.
|
1329 |
|
|
*
|
1330 |
|
|
* @param query
|
1331 |
|
|
* String to parse.
|
1332 |
|
|
*
|
1333 |
|
|
* @return input string with wildcards removed
|
1334 |
|
|
*/
|
1335 |
|
|
private String getQueryWithoutWildCards(String query) {
|
1336 |
|
|
|
1337 |
|
|
String newQuery = query;
|
1338 |
|
|
|
1339 |
|
|
if (query.startsWith("*")) {
|
1340 |
|
|
newQuery = newQuery.substring(1, newQuery.length());
|
1341 |
|
|
}
|
1342 |
|
|
|
1343 |
|
|
if (query.endsWith("*")) {
|
1344 |
|
|
newQuery = newQuery.substring(0, newQuery.length() - 1);
|
1345 |
|
|
}
|
1346 |
|
|
|
1347 |
|
|
return newQuery.trim();
|
1348 |
|
|
}
|
1349 |
|
|
|
1350 |
|
|
/**
|
1351 |
|
|
* Build map with taxon flag key-value pairs.
|
1352 |
|
|
*/
|
1353 |
|
|
private Map<String, String> buildFlagMap(TaxonBase tb) {
|
1354 |
|
|
Map<String, String> flags = new Hashtable<String, String>();
|
1355 |
|
|
flags.put(DOUBTFUL_FLAG, Boolean.toString(tb.isDoubtful()));
|
1356 |
|
|
return flags;
|
1357 |
|
|
}
|
1358 |
|
|
|
1359 |
|
|
/**
|
1360 |
|
|
* Build classification map.
|
1361 |
|
|
*/
|
1362 |
|
|
private Map<String, Map> getClassification(Taxon taxon, String classificationType, boolean includeUuids) {
|
1363 |
|
|
// Using TreeMap is important, because we need the sorting of the classification keys
|
1364 |
|
|
// in the map to be stable.
|
1365 |
|
|
TreeMap<String, Map> sourceClassificationMap = buildClassificationMap(taxon, includeUuids);
|
1366 |
|
|
|
1367 |
|
|
// if classification key is 'default' then return the default element of the map
|
1368 |
|
|
if(classificationType.equals(CLASSIFICATION_DEFAULT) && !sourceClassificationMap.isEmpty()) {
|
1369 |
|
|
List<Classification> clist = getClassificationList(1);
|
1370 |
|
|
String defaultKey = removeInternalWhitespace(clist.get(0).getTitleCache());
|
1371 |
|
|
return sourceClassificationMap.get(defaultKey);
|
1372 |
|
|
// if classification key is provided then return the classification corresponding to the key
|
1373 |
|
|
} else if(sourceClassificationMap.containsKey(classificationType)) {
|
1374 |
|
|
return sourceClassificationMap.get(classificationType);
|
1375 |
|
|
// if classification key is 'all' then return the entire map
|
1376 |
|
|
} else if(classificationType.equals(CLASSIFICATION_ALL)) {
|
1377 |
|
|
return sourceClassificationMap;
|
1378 |
|
|
} else {
|
1379 |
|
|
return new TreeMap<String,Map>();
|
1380 |
|
|
}
|
1381 |
|
|
}
|
1382 |
|
|
|
1383 |
|
|
/**
|
1384 |
|
|
* Build classification map.
|
1385 |
162aa018
|
l.morris
|
*/
|
1386 |
236ab161
|
Andreas Müller
|
private TreeMap<String, Map> buildClassificationMap(Taxon taxon, boolean includeUuid) {
|
1387 |
162aa018
|
l.morris
|
// Using TreeMap is important, because we need the sorting of the classification keys
|
1388 |
236ab161
|
Andreas Müller
|
// in the map to be stable.
|
1389 |
|
|
TreeMap<String, Map> sourceClassificationMap = new TreeMap<String, Map>();
|
1390 |
|
|
Set<TaxonNode> taxonNodes = taxon.getTaxonNodes();
|
1391 |
|
|
//loop through taxon nodes and build classification map for each classification key
|
1392 |
|
|
for (TaxonNode tn : taxonNodes) {
|
1393 |
|
|
Map<String, Object> classificationMap = new LinkedHashMap<String, Object>();
|
1394 |
|
|
List<TaxonNode> tnList = classificationService.loadTreeBranchToTaxon(taxon,
|
1395 |
|
|
tn.getClassification(), null, TAXON_NODE_INIT_STRATEGY);
|
1396 |
|
|
for (TaxonNode classificationtn : tnList) {
|
1397 |
|
|
if(includeUuid) {
|
1398 |
|
|
// creating map object with <name, uuid> elements
|
1399 |
|
|
Map<String, String> clMap = new HashMap<String, String>();
|
1400 |
|
|
clMap.put("name",classificationtn.getTaxon().getName().getTitleCache());
|
1401 |
|
|
clMap.put("uuid",classificationtn.getTaxon().getUuid().toString());
|
1402 |
|
|
classificationMap.put(classificationtn.getTaxon().getName().getRank().getTitleCache(), clMap);
|
1403 |
|
|
} else {
|
1404 |
|
|
classificationMap.put(classificationtn.getTaxon().getName().getRank().getTitleCache(),
|
1405 |
|
|
classificationtn.getTaxon().getName().getTitleCache());
|
1406 |
|
|
}
|
1407 |
|
|
}
|
1408 |
|
|
String cname = removeInternalWhitespace(tn.getClassification().getTitleCache());
|
1409 |
|
|
logger.info("Building classification map " + cname);
|
1410 |
|
|
sourceClassificationMap.put(cname, classificationMap);
|
1411 |
|
|
}
|
1412 |
|
|
return sourceClassificationMap;
|
1413 |
|
|
}
|
1414 |
|
|
|
1415 |
|
|
private String removeInternalWhitespace(String withWSpace) {
|
1416 |
|
|
String[] words = withWSpace.split("\\s+");
|
1417 |
|
|
// "\\s+" in regular expression language meaning one or
|
1418 |
|
|
// more spaces
|
1419 |
|
|
StringBuilder builder = new StringBuilder();
|
1420 |
|
|
for (String word : words) {
|
1421 |
|
|
builder.append(word);
|
1422 |
|
|
}
|
1423 |
|
|
return builder.toString();
|
1424 |
|
|
}
|
1425 |
|
|
|
1426 |
|
|
private List<Classification> getClassificationList(int limit) {
|
1427 |
|
|
List<OrderHint> orderHints = new ArrayList<OrderHint>();
|
1428 |
|
|
orderHints.add(new OrderHint("titleCache", SortOrder.DESCENDING));
|
1429 |
|
|
List<Classification> clist = classificationService.listClassifications(limit, 0, orderHints, VOC_CLASSIFICATION_INIT_STRATEGY);
|
1430 |
|
|
return clist;
|
1431 |
|
|
}
|
1432 |
|
|
|
1433 |
|
|
private boolean isValid(String uuid){
|
1434 |
|
|
if( uuid == null) {
|
1435 |
|
|
return false;
|
1436 |
|
|
}
|
1437 |
|
|
try {
|
1438 |
|
|
// we have to convert to object and back to string because the built in fromString does not have
|
1439 |
|
|
// good validation logic.
|
1440 |
|
|
|
1441 |
|
|
UUID fromStringUUID = UUID.fromString(uuid);
|
1442 |
|
|
String toStringUUID = fromStringUUID.toString();
|
1443 |
|
|
|
1444 |
|
|
System.out.println("input uuid : " + uuid + " , parsed uuid : " + toStringUUID);
|
1445 |
|
|
return toStringUUID.equals(uuid);
|
1446 |
|
|
} catch(IllegalArgumentException e) {
|
1447 |
|
|
return false;
|
1448 |
|
|
}
|
1449 |
|
|
}
|
1450 |
|
|
@Override
|
1451 |
|
|
public void setResourceLoader(ResourceLoader resourceLoader) {
|
1452 |
|
|
this.resourceLoader = resourceLoader;
|
1453 |
|
|
|
1454 |
|
|
}
|
1455 |
|
|
}
|