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