1
|
// $Id$
|
2
|
/**
|
3
|
* Copyright (C) 2009 EDIT 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
|
7
|
* 1.1 See LICENSE.TXT at the top of this package for the full license terms.
|
8
|
*/
|
9
|
|
10
|
package eu.etaxonomy.cdm.remote.controller;
|
11
|
|
12
|
import java.io.IOException;
|
13
|
import java.net.URI;
|
14
|
import java.net.URISyntaxException;
|
15
|
import java.util.ArrayList;
|
16
|
import java.util.Arrays;
|
17
|
import java.util.HashSet;
|
18
|
import java.util.Hashtable;
|
19
|
import java.util.List;
|
20
|
import java.util.Set;
|
21
|
import java.util.UUID;
|
22
|
import java.util.regex.Matcher;
|
23
|
import java.util.regex.Pattern;
|
24
|
|
25
|
import javax.servlet.http.HttpServletRequest;
|
26
|
import javax.servlet.http.HttpServletResponse;
|
27
|
|
28
|
import org.apache.commons.io.FilenameUtils;
|
29
|
import org.apache.log4j.Logger;
|
30
|
import org.springframework.beans.factory.annotation.Autowired;
|
31
|
import org.springframework.stereotype.Controller;
|
32
|
import org.springframework.web.bind.WebDataBinder;
|
33
|
import org.springframework.web.bind.annotation.InitBinder;
|
34
|
import org.springframework.web.bind.annotation.RequestMapping;
|
35
|
import org.springframework.web.bind.annotation.RequestMethod;
|
36
|
import org.springframework.web.bind.annotation.RequestParam;
|
37
|
|
38
|
import eu.etaxonomy.cdm.api.service.IReferenceService;
|
39
|
import eu.etaxonomy.cdm.api.service.ITaxonService;
|
40
|
import eu.etaxonomy.cdm.model.name.Rank;
|
41
|
import eu.etaxonomy.cdm.model.reference.ReferenceBase;
|
42
|
import eu.etaxonomy.cdm.model.taxon.Taxon;
|
43
|
import eu.etaxonomy.cdm.model.taxon.TaxonBase;
|
44
|
import eu.etaxonomy.cdm.remote.editor.RankPropertyEditor;
|
45
|
import eu.etaxonomy.cdm.remote.editor.UUIDPropertyEditor;
|
46
|
import eu.etaxonomy.cdm.strategy.exceptions.UnknownCdmTypeException;
|
47
|
|
48
|
/**
|
49
|
* @author a.kohlbecker
|
50
|
* @date 20.03.2009
|
51
|
*/
|
52
|
@Controller
|
53
|
public class TaxonomyController extends AbstractListController<TaxonBase, ITaxonService> {
|
54
|
|
55
|
/**
|
56
|
*
|
57
|
*/
|
58
|
private static final List<String> TAXON_INIT_STRATEGY = Arrays.asList(new String[]{
|
59
|
"sec",
|
60
|
"relationsToThisTaxon.toTaxon.$",
|
61
|
//"name.rank.$",
|
62
|
"name.rank.representations",
|
63
|
"name.nomenclaturalReference.authorTeam.*"
|
64
|
});
|
65
|
|
66
|
private static final List<String> CHILD_TAXON_INIT_STRATEGY = Arrays.asList(new String[]{
|
67
|
"sec",
|
68
|
"relationsToThisTaxon.fromTaxon.$",
|
69
|
"relationsToThisTaxon.fromTaxon.name.taggedName",
|
70
|
"name.taggedName",
|
71
|
});
|
72
|
|
73
|
private static final List<String> PARENT_TAXON_INIT_STRATEGY = Arrays.asList(new String[]{
|
74
|
"sec",
|
75
|
"relationsFromThisTaxon.toTaxon.$",
|
76
|
"relationsFromThisTaxon.toTaxon.name.rank.representations",
|
77
|
"name.taggedName",
|
78
|
"name.rank.representations",
|
79
|
"name.nomenclaturalReference.authorTeam.*"
|
80
|
});
|
81
|
|
82
|
//TODO get rid of the bloodyRankLabelMap ------ can be deleted once the FIXME in getPathToRoot is solved
|
83
|
private static Hashtable<String, String> bloodyRankLabelMap = new Hashtable<String, String>();
|
84
|
static{
|
85
|
bloodyRankLabelMap.put("Subfamily", "Subfamilia");
|
86
|
bloodyRankLabelMap.put("Family", "Familia");
|
87
|
bloodyRankLabelMap.put("Suborder", "Subordo");
|
88
|
bloodyRankLabelMap.put("Order", "Ordo");
|
89
|
|
90
|
}
|
91
|
// --------------------------------------------
|
92
|
|
93
|
public static final Logger logger = Logger.getLogger(TaxonomyController.class);
|
94
|
|
95
|
private ITaxonService service;
|
96
|
|
97
|
private IReferenceService referenceService;
|
98
|
|
99
|
private Pattern parameterPattern = Pattern.compile("^/(?:[^/]+)/taxonomy/([^?#&\\.]+).*");
|
100
|
|
101
|
@Autowired
|
102
|
public void setService(ITaxonService service) {
|
103
|
this.service = service;
|
104
|
}
|
105
|
|
106
|
@Autowired
|
107
|
public void setReferenceService(IReferenceService referenceService) {
|
108
|
this.referenceService = referenceService;
|
109
|
}
|
110
|
|
111
|
@InitBinder
|
112
|
public void initBinder(WebDataBinder binder) {
|
113
|
binder.registerCustomEditor(UUID.class, new UUIDPropertyEditor());
|
114
|
binder.registerCustomEditor(Rank.class, new RankPropertyEditor());
|
115
|
}
|
116
|
|
117
|
/**
|
118
|
* @param uuid
|
119
|
* @param rank
|
120
|
* @param request
|
121
|
* @param response
|
122
|
* @return
|
123
|
* @throws IOException
|
124
|
*/
|
125
|
@RequestMapping(
|
126
|
value = {"/*/taxonomy/"},
|
127
|
params = {"uuid"},
|
128
|
method = RequestMethod.GET)
|
129
|
public String findTaxon(
|
130
|
@RequestParam(value = "uuid", required = true) UUID uuid,
|
131
|
@RequestParam(value = "rank", required = false) Rank rank,
|
132
|
//TODO implement view uuid parameter
|
133
|
HttpServletRequest request, HttpServletResponse response) throws IOException {
|
134
|
|
135
|
String msg404 = rank != null ? "Taxon not found within rank "+ rank.getLabel() : "Taxon not found.";
|
136
|
|
137
|
TaxonBase tb = service.load(uuid, TAXON_INIT_STRATEGY);
|
138
|
|
139
|
if(tb != null && Taxon.class.isAssignableFrom(tb.getClass())){
|
140
|
Taxon t = (Taxon)tb;
|
141
|
String relPath = "";
|
142
|
String basePath = FilenameUtils.removeExtension(request.getServletPath());
|
143
|
basePath += "/" + t.getSec().getUuid().toString();
|
144
|
if(rank != null){
|
145
|
basePath += "," + rank.getLabel();
|
146
|
}
|
147
|
|
148
|
// compose path of parent uuids
|
149
|
Taxon taxon = t;
|
150
|
while( taxon != null && (rank == null || taxon.getName().getRank() == null || taxon.getName().getRank().compareTo(rank) <= 0) ) {
|
151
|
relPath = "/" + taxon.getUuid().toString() + relPath;
|
152
|
taxon = taxon.getTaxonomicParent();
|
153
|
if(taxon != null){
|
154
|
taxon = (Taxon)service.load(taxon.getUuid(), TAXON_INIT_STRATEGY);
|
155
|
}
|
156
|
};
|
157
|
|
158
|
if(relPath.length() > 0){
|
159
|
URI redirectUri;
|
160
|
try {
|
161
|
redirectUri = relativeToFullUri(request, basePath + relPath);
|
162
|
if(logger.isInfoEnabled()){
|
163
|
logger.info("redirecting to " + redirectUri);
|
164
|
}
|
165
|
response.sendRedirect(redirectUri.toString());
|
166
|
return "";
|
167
|
} catch (URISyntaxException e) {
|
168
|
logger.error(e.getMessage(), e);
|
169
|
}
|
170
|
}
|
171
|
} else {
|
172
|
msg404 = "The taxon is not accepted";
|
173
|
}
|
174
|
response.sendError(HttpServletResponse.SC_NOT_FOUND, msg404);
|
175
|
return "";
|
176
|
}
|
177
|
|
178
|
/**
|
179
|
* @param request
|
180
|
* @return
|
181
|
*/
|
182
|
@RequestMapping(
|
183
|
value = {"/*/taxonomy/*"},
|
184
|
method = RequestMethod.GET)
|
185
|
public List<Taxon> getRootTaxa(HttpServletRequest request, HttpServletResponse response) throws IOException {
|
186
|
|
187
|
List<String> uriParams = readUriParameters(request);
|
188
|
ReferenceBase secref = null;
|
189
|
Rank rank = null;
|
190
|
if(uriParams == null){
|
191
|
return (List<Taxon>) service.getRootTaxa(rank, null, true, false, TAXON_INIT_STRATEGY);
|
192
|
//response.sendError(HttpServletResponse.SC_NOT_FOUND, "");
|
193
|
//return null;
|
194
|
}
|
195
|
if(uriParams.size() == 1){
|
196
|
// get secuuid and rank
|
197
|
secref = readSecByUuid(uriParams.get(0));
|
198
|
rank = readRankByLabel(uriParams.get(0));
|
199
|
|
200
|
if(secref == null) {
|
201
|
response.sendError(404 , "SecReference not found by " + stringToUuid(uriParams.get(0)) );
|
202
|
return null;
|
203
|
}
|
204
|
|
205
|
}
|
206
|
if(uriParams.size() > 1){
|
207
|
response.sendError(400, "Only one uuid parameter expected but found " + uriParams.size());
|
208
|
return null;
|
209
|
}
|
210
|
return (List<Taxon>) service.getRootTaxa(rank, secref, true, false, TAXON_INIT_STRATEGY);
|
211
|
}
|
212
|
|
213
|
|
214
|
|
215
|
/**
|
216
|
* @param request
|
217
|
* @return
|
218
|
* @throws IOException
|
219
|
*/
|
220
|
@RequestMapping(
|
221
|
value = {"/*/taxonomy/*/*", "/*/taxonomy/*/**/*"},
|
222
|
method = RequestMethod.GET)
|
223
|
public Set<Taxon> getChildTaxa(HttpServletRequest request, HttpServletResponse response) throws IOException {
|
224
|
|
225
|
List<String> uriParameters = readUriParameters(request);
|
226
|
if(uriParameters.size() <= 1){
|
227
|
response.sendError(400, "At least two uuid parameters expected but found " + uriParameters.size());
|
228
|
return null;
|
229
|
}
|
230
|
try {
|
231
|
UUID uuid = stringToUuid(uriParameters.get(uriParameters.size() - 1));
|
232
|
Taxon taxon = (Taxon) service.load(uuid, CHILD_TAXON_INIT_STRATEGY);
|
233
|
return taxon.getTaxonomicChildren();
|
234
|
} catch (ClassCastException cce) {
|
235
|
response.sendError(500, "The specified instance is not a taxon");
|
236
|
return null;
|
237
|
}
|
238
|
}
|
239
|
|
240
|
/**
|
241
|
* @param request
|
242
|
* @return
|
243
|
* @throws IOException
|
244
|
*/
|
245
|
@RequestMapping(
|
246
|
value = {"/*/taxonomy/*/*/path", "/*/taxonomy/*/**/*/path"},
|
247
|
method = RequestMethod.GET)
|
248
|
public List<Taxon> getPathToRoot(HttpServletRequest request, HttpServletResponse response) throws IOException {
|
249
|
|
250
|
List<Taxon> pathToRoot = new ArrayList<Taxon>();
|
251
|
List<String> uriParameters = readUriParameters(request);
|
252
|
if(uriParameters.size() <= 1){
|
253
|
response.sendError(400, "At least two uuid parameters expected but found " + uriParameters.size());
|
254
|
return null;
|
255
|
}
|
256
|
// get rank
|
257
|
Rank rank = readRankByLabel(uriParameters.get(0));
|
258
|
|
259
|
try {
|
260
|
UUID uuid = stringToUuid(uriParameters.get(uriParameters.size() - 2));
|
261
|
Taxon parentTaxon = (Taxon) service.load(uuid, PARENT_TAXON_INIT_STRATEGY);
|
262
|
while(parentTaxon != null){
|
263
|
//FIXME orderindex in parentTaxon.getName().getRank() is not set !!!
|
264
|
// original: if(rank != null && rank.isLower(parentTaxon.getName().getRank())){
|
265
|
// Preliminary solution below:
|
266
|
if(rank != null){
|
267
|
try {
|
268
|
String bloodyRankLabel = bloodyRankLabelMap.get(parentTaxon.getName().getRank().getLabel());
|
269
|
if(bloodyRankLabel == null){
|
270
|
bloodyRankLabel = parentTaxon.getName().getRank().getLabel();
|
271
|
}
|
272
|
Rank compareToRank = Rank.getRankByName(bloodyRankLabel);
|
273
|
if(rank.isLower(compareToRank)){
|
274
|
break;
|
275
|
}
|
276
|
} catch (UnknownCdmTypeException e) {
|
277
|
logger.error(e);
|
278
|
}
|
279
|
}
|
280
|
pathToRoot.add(parentTaxon);
|
281
|
parentTaxon = parentTaxon.getTaxonomicParent();
|
282
|
if(parentTaxon != null){
|
283
|
parentTaxon = (Taxon)service.load(parentTaxon.getUuid(), PARENT_TAXON_INIT_STRATEGY);
|
284
|
}
|
285
|
}
|
286
|
|
287
|
return pathToRoot;
|
288
|
} catch (ClassCastException cce) {
|
289
|
logger.warn("The specified instance is not a taxon", cce);
|
290
|
response.sendError(500, "The specified instance is not a taxon");
|
291
|
return null;
|
292
|
}
|
293
|
}
|
294
|
|
295
|
/**
|
296
|
* reads <code>{secuuid},{rank label}/..</code> from <code>/{database key}/taxonomy/{secuuid},{rank label}/..<code>
|
297
|
* @param request
|
298
|
* @return
|
299
|
*/
|
300
|
protected List<String> readUriParameters(HttpServletRequest request) {
|
301
|
|
302
|
List<String> parameters = null;
|
303
|
String path = request.getServletPath();
|
304
|
if(path != null) {
|
305
|
Matcher uuidMatcher = parameterPattern .matcher(path);
|
306
|
if(uuidMatcher.matches() && uuidMatcher.groupCount() > 0){
|
307
|
String[] pa = uuidMatcher.group(1).split("/");
|
308
|
parameters = Arrays.asList(pa);
|
309
|
}
|
310
|
}
|
311
|
return parameters;
|
312
|
}
|
313
|
|
314
|
/**
|
315
|
* @param string
|
316
|
* @return
|
317
|
*/
|
318
|
private Rank readRankByLabel(String paramStr) throws IllegalArgumentException{
|
319
|
int pos;
|
320
|
if((pos = paramStr.indexOf(',')) > 0){
|
321
|
String rankLabel = paramStr.substring(pos + 1);
|
322
|
try {
|
323
|
return Rank.getRankByName(rankLabel);
|
324
|
} catch (UnknownCdmTypeException e) {
|
325
|
throw new IllegalArgumentException("400Not a valid rank name");
|
326
|
}
|
327
|
}
|
328
|
return null;
|
329
|
}
|
330
|
|
331
|
/**
|
332
|
* @param paramStr
|
333
|
* @return
|
334
|
*/
|
335
|
private ReferenceBase readSecByUuid(String paramStr) {
|
336
|
UUID secRefuuid;
|
337
|
int pos;
|
338
|
if((pos = paramStr.indexOf(',')) > 0){
|
339
|
|
340
|
secRefuuid = stringToUuid(paramStr.substring(0, pos));
|
341
|
} else {
|
342
|
secRefuuid = stringToUuid(paramStr);
|
343
|
}
|
344
|
return referenceService.findByUuid(secRefuuid);
|
345
|
}
|
346
|
|
347
|
/**
|
348
|
* @param uuidStr
|
349
|
* @return
|
350
|
*/
|
351
|
private UUID stringToUuid(String uuidStr) {
|
352
|
|
353
|
try {
|
354
|
UUID uuid = UUID.fromString(uuidStr);
|
355
|
return uuid;
|
356
|
} catch (Exception e) {
|
357
|
throw new IllegalArgumentException(uuidStr + " is not a uuid");
|
358
|
}
|
359
|
}
|
360
|
|
361
|
private URI relativeToFullUri(HttpServletRequest request,
|
362
|
String relativePath) throws URISyntaxException {
|
363
|
return new URI(
|
364
|
request.getScheme(),
|
365
|
null, //user info
|
366
|
request.getServerName(),
|
367
|
request.getServerPort(),
|
368
|
relativePath,
|
369
|
null,
|
370
|
null);
|
371
|
}
|
372
|
|
373
|
}
|