Project

General

Profile

Download (20.9 KB) Statistics
| Branch: | Tag: | Revision:
1
package org.cybertaxonomy.utis.checklist;
2

    
3

    
4
import java.net.URI;
5
import java.util.ArrayList;
6
import java.util.EnumSet;
7
import java.util.HashMap;
8
import java.util.Iterator;
9
import java.util.List;
10
import java.util.Map;
11

    
12
import org.apache.http.HttpHost;
13
import org.apache.http.ParseException;
14
import org.cybertaxonomy.utis.client.ServiceProviderInfo;
15
import org.cybertaxonomy.utis.query.RestClient;
16
import org.cybertaxonomy.utis.tnr.msg.Classification;
17
import org.cybertaxonomy.utis.tnr.msg.NameType;
18
import org.cybertaxonomy.utis.tnr.msg.Query;
19
import org.cybertaxonomy.utis.tnr.msg.Response;
20
import org.cybertaxonomy.utis.tnr.msg.Source;
21
import org.cybertaxonomy.utis.tnr.msg.Synonym;
22
import org.cybertaxonomy.utis.tnr.msg.Taxon;
23
import org.cybertaxonomy.utis.tnr.msg.TaxonName;
24
import org.cybertaxonomy.utis.tnr.msg.TnrMsg;
25
import org.cybertaxonomy.utis.utils.IdentifierUtils;
26
import org.cybertaxonomy.utis.utils.TnrMsgUtils;
27
import org.gbif.nameparser.NameParser;
28
import org.json.simple.JSONArray;
29
import org.json.simple.JSONAware;
30
import org.json.simple.JSONObject;
31
import org.json.simple.parser.JSONParser;
32

    
33
public class BgbmEditClient extends AggregateChecklistClient<RestClient> {
34

    
35
    /**
36
     *
37
     */
38
    public static final String ID = "bgbm-cdm-server";
39
    public static final String LABEL = "Name catalogues served by the BGBM CDM Server";
40
    public static final String DOC_URL = "http://wp5.e-taxonomy.eu/cdmlib/rest-api-name-catalogue.html";
41
    public static final String COPYRIGHT_URL = "http://wp5.e-taxonomy.eu/cdmlib/license.html";
42
    private static final String SERVER_PATH_PREFIX = "/";
43
    private static final HttpHost HTTP_HOST = new HttpHost("api.cybertaxonomy.org", 80); // new HttpHost("test.e-taxonomy.eu", 80);
44

    
45

    
46
    private final Map<String,Query> taxonIdQueryMap = new HashMap<String,Query>();
47

    
48
    private final Map<String,String> taxonIdMatchStringMap = new HashMap<String, String>();
49

    
50
    public static final EnumSet<SearchMode> SEARCH_MODES = EnumSet.of(
51
            SearchMode.scientificNameExact,
52
            SearchMode.scientificNameLike,
53
            SearchMode.findByIdentifier
54
            );
55

    
56
    public BgbmEditClient() {
57
        super();
58
    }
59

    
60
    public BgbmEditClient(String checklistInfoJson) throws DRFChecklistException {
61
        super(checklistInfoJson);
62
    }
63

    
64
    @Override
65
    public void initQueryClient() {
66
        queryClient = new RestClient(HTTP_HOST);
67
    }
68

    
69

    
70
    @Override
71
    public ServiceProviderInfo buildServiceProviderInfo() {
72
        ServiceProviderInfo checklistInfo = new ServiceProviderInfo(ID,LABEL,DOC_URL,COPYRIGHT_URL, getSearchModes());
73
        checklistInfo.addSubChecklist(new ServiceProviderInfo("col",
74
                "Catalogue Of Life (EDIT - name catalogue end point)",
75
                "http://wp5.e-taxonomy.eu/cdmlib/rest-api-name-catalogue.html",
76
                "http://www.catalogueoflife.org/col/info/copyright", ServiceProviderInfo.DEFAULT_SEARCH_MODE));
77
        return checklistInfo;
78
    }
79

    
80
    /**
81
     * Adds the acceptedTaxonUuids found in the <code>responseBody</code> to the
82
     * private field <code>taxonIdQueryMap</code>
83
     * and populates the <code>taxonIdMatchStringMap</code>
84
     *
85
     * @param queryList
86
     * @param responseBodyJson
87
     * @throws DRFChecklistException
88
     */
89
    private void buildTaxonIdMapsFromCatalogueServiceResponse(List<Query> queryList , String responseBody) throws DRFChecklistException {
90

    
91
        JSONArray responseBodyJson = parseResponseBody(responseBody, JSONArray.class);
92

    
93
        if(responseBodyJson.size() != queryList.size()){
94
            throw new DRFChecklistException("Query and Response lists have different lengths");
95
        }
96

    
97
        Iterator<JSONObject> itrNameMsgs = responseBodyJson.iterator();
98

    
99
        for (Query query : queryList) {
100
            JSONArray responseArray = (JSONArray) itrNameMsgs.next().get("response");
101
            if(responseArray != null) {
102
                Iterator<JSONObject> resIterator = responseArray.iterator();
103
                while (resIterator.hasNext()) {
104
                    JSONObject res = resIterator.next();
105
                    JSONArray accTaxonUuidArray = (JSONArray) res.get("acceptedTaxonUuids");
106
                    String matchingName = res.get("title").toString();
107
                    Iterator<String> atIterator = accTaxonUuidArray.iterator();
108
                    while (atIterator.hasNext()) {
109
                        String acceptedTaxonId = atIterator.next();
110
                        boolean isAcceptedTaxonMatch = res.get("taxonConceptUuids").toString().contains(acceptedTaxonId);
111
                        if(!taxonIdQueryMap.containsKey(acceptedTaxonId) || isAcceptedTaxonMatch){
112
                            // matches for accepted taxa should be preferred here
113
                            // matches for synomymy or other types should never overwrite
114
                            // accepted taxon matches
115
                            taxonIdQueryMap.put(acceptedTaxonId, query);
116
                            taxonIdMatchStringMap.put(acceptedTaxonId, matchingName);
117
                        }
118
                        //System.out.println("Found accepted taxon id : " + accTaxonId);
119
                    }
120
                }
121
            }
122
        }
123
    }
124

    
125
    /**
126
     * Adds the acceptedTaxonUuids found in the <code>responseBody</code> to the
127
     * private field <code>taxonIdQueryMap</code>
128
     * and populates the <code>taxonIdMatchStringMap</code>
129
     *
130
     * @param queryList
131
     * @param responseBodyJson
132
     * @throws DRFChecklistException
133
     */
134
    private void addTaxaToTaxonIdMapFromIdentifierServiceResponse(List<Query> queryList , String responseBody) throws DRFChecklistException {
135

    
136
        /*
137
         * {"class":"DefaultPagerImpl","count":8,"currentIndex":0,"firstRecord":1,"indices":[0],"lastRecord":8,"nextIndex":0,"pageSize":30,"pagesAvailable":1,"prevIndex":0,
138
            "records":[
139
                  {
140
                  "cdmEntity":{
141
                      "cdmUuid":"0cf5a6fe-b1df-4f4f-85a8-31fef4be2a68",
142
                      "class":"CdmEntity",
143
                      "titleCache":"Abies alba Mill. sec. SCHMEIL-FITSCHEN, Flora von Deutschland und angrenzenden Ländern, 89. Aufl"},
144
                  "class":"FindByIdentifierDTO",
145
                   "identifier":{"class":"AlternativeIdentifier","identifier":"1","typeLabel":"Florein Identifier","typeUuid":"8b67291e-96e0-4556-8d6a-c94e8750b301"}},{"cdmEntity":{"cdmUuid":"3d256539-d7f7-4dd1-ad7f-cd6e4c141f24","class":"CdmEntity","titleCache":"Abies alba Mill. sec. OBERDORFER, Pflanzensoziologische Exkursionsflora, ed. 7"},"class":"FindByIdentifierDTO","identifier":{"class":"AlternativeIdentifier","identifier":"1","typeLabel":"Florein Identifier","typeUuid":"8b67291e-96e0-4556-8d6a-c94e8750b301"}},{"cdmEntity":{"cdmUuid":"61c2bc4f-a23d-4160-8f14-625b4484fc2f","class":"CdmEntity","titleCache":"Abies alba Mill. sec. HEGI, Illustrierte Flora von Mitteleuropa, Aufl. 2 u. 3"},"class":"FindByIdentifierDTO","identifier":{"class":"AlternativeIdentifier","identifier":"1","typeLabel":"Florein Identifier","typeUuid":"8b67291e-96e0-4556-8d6a-c94e8750b301"}},{"cdmEntity":{"cdmUuid":"7a63f215-0a41-4b7e-9394-bda4521d6ad1","class":"CdmEntity","titleCache":"Abies alba Mill. sec. GREUTER et. al., Med-Checklist bisher Bde. 1, 3 und 4"},"class":"FindByIdentifierDTO","identifier":{"class":"AlternativeIdentifier","identifier":"1","typeLabel":"Florein Identifier","typeUuid":"8b67291e-96e0-4556-8d6a-c94e8750b301"}},{"cdmEntity":{"cdmUuid":"872088a4-95f4-472c-ae79-a29028bb3fbf","class":"CdmEntity","titleCache":"Abies alba Mill. sec. Wisskirchen & Haeupler, 1998"},"class":"FindByIdentifierDTO","identifier":{"class":"AlternativeIdentifier","identifier":"1","typeLabel":"Florein Identifier","typeUuid":"8b67291e-96e0-4556-8d6a-c94e8750b301"}},{"cdmEntity":{"cdmUuid":"90ee17be-d455-4564-949d-9c53e27a6a6f","class":"CdmEntity","titleCache":"Abies alba Mill. sec. TUTIN et al., Flora Europaea"},"class":"FindByIdentifierDTO","identifier":{"class":"AlternativeIdentifier","identifier":"1","typeLabel":"Florein Identifier","typeUuid":"8b67291e-96e0-4556-8d6a-c94e8750b301"}},{"cdmEntity":{"cdmUuid":"b0d35335-63e6-41ab-bdb0-d01851134e9c","class":"CdmEntity","titleCache":"Abies alba Mill. sec. EHRENDORFER, Liste der Gefäßpflanzen Mitteleuropas, 2. Aufl"},"class":"FindByIdentifierDTO","identifier":{"class":"AlternativeIdentifier","identifier":"1","typeLabel":"Florein Identifier","typeUuid":"8b67291e-96e0-4556-8d6a-c94e8750b301"}},{"cdmEntity":{"cdmUuid":"b7a352aa-1f73-41f3-a4e3-b24fc1c2cd5f","class":"CdmEntity","titleCache":"Abies alba Mill. sec. ROTHMALER, 1990"},"class":"FindByIdentifierDTO","identifier":{"class":"AlternativeIdentifier","identifier":"1","typeLabel":"Florein Identifier","typeUuid":"8b67291e-96e0-4556-8d6a-c94e8750b301"}}],"suggestion":""}
146
        */
147

    
148
        if(queryList.size() > 1){
149
            throw new DRFChecklistException("Only single Querys are supported");
150
        }
151

    
152
        Query query = queryList.get(0);
153

    
154
        JSONObject jsonPager = parseResponseBody(responseBody, JSONObject.class);
155

    
156
        JSONArray jsonRecords = (JSONArray) jsonPager.get("records");
157

    
158

    
159
        Iterator<JSONObject> resIterator = jsonRecords.iterator();
160
        while (resIterator.hasNext()) {
161
            JSONObject record = resIterator.next();
162
            JSONObject cdmEntity = (JSONObject) record.get("cdmEntity");
163
            String uuid = cdmEntity.get("cdmUuid").toString();
164
            taxonIdQueryMap.put(uuid, query);
165
        }
166

    
167
    }
168

    
169
    private void addTaxonToTaxonIdMap(List<Query> queryList , String responseBody) throws DRFChecklistException {
170

    
171

    
172
        if(queryList.size() > 1){
173
            throw new DRFChecklistException("Only single Querys are supported");
174
        }
175

    
176
        Query query = queryList.get(0);
177

    
178
        JSONObject cdmEntity = parseResponseBody(responseBody, JSONObject.class);
179
        String uuid = cdmEntity.get("uuid").toString();
180
        taxonIdQueryMap.put(uuid, query);
181

    
182
    }
183

    
184
    /**
185
     * @param responseBody
186
     * @return
187
     * @throws DRFChecklistException
188
     */
189
    private <T extends JSONAware> T parseResponseBody(String responseBody, Class<T> jsonType) throws DRFChecklistException {
190
        // TODO use Jackson instead? it is much faster!
191
        JSONParser parser = new JSONParser();
192
        Object obj;
193
        try {
194
            obj = parser.parse(responseBody);
195
        } catch (ParseException e) {
196
            logger.error("parseResponseBody() - ", e);
197
            throw new DRFChecklistException(e);
198
        } catch (org.json.simple.parser.ParseException e) {
199

    
200
            logger.error("parseResponseBody() - ", e);
201
            throw new DRFChecklistException(e);
202
        }
203

    
204
        if(jsonType.isAssignableFrom(obj.getClass())){
205
            return jsonType.cast(obj);
206
        } else {
207
            throw new DRFChecklistException("parseResponseBody() - deserialized responseBody is not of type " + jsonType ) ;
208
        }
209

    
210
    }
211

    
212
    private Taxon generateAccName(JSONObject taxon) {
213
        Taxon accTaxon = new Taxon();
214
        TaxonName taxonName = new TaxonName();
215

    
216
        String resName = (String) taxon.get("name");
217
        taxonName.setFullName(resName);
218
        NameParser ecatParser = new NameParser();
219
        String nameCanonical = ecatParser.parseToCanonical(resName);
220
        taxonName.setCanonicalName(nameCanonical);
221

    
222
        taxonName.setRank((String) taxon.get("rank"));
223
        String lsid = (String) taxon.get("lsid");
224

    
225
        JSONObject scrutinyjs = (JSONObject)taxon.get("taxonomicScrutiny");
226
        String accordingTo = (String) scrutinyjs.get("accordingTo");
227
        String modified = (String) scrutinyjs.get("modified");
228

    
229
        accTaxon.setTaxonName(taxonName);
230
        accTaxon.setTaxonomicStatus((String)taxon.get("taxonStatus"));
231
        accTaxon.setAccordingTo(accordingTo);
232
        accTaxon.setIdentifier(lsid);
233

    
234
        JSONObject sourcejs = (JSONObject)taxon.get("source");
235
        String sourceUrl = (String) sourcejs.get("url");
236
        String sourceDatasetID =  (String) sourcejs.get("datasetID");
237
        String sourceDatasetName = (String) sourcejs.get("datasetName");
238
        String sourceName = "";
239

    
240
        Source source = new Source();
241
        source.setIdentifier(sourceDatasetID);
242
        source.setDatasetName(sourceDatasetName);
243
        source.setName(sourceName);
244
        source.setUrl(sourceUrl);
245
        accTaxon.getSources().add(source);
246

    
247
        JSONObject classification =(JSONObject)taxon.get("classification");
248
        if(classification != null) {
249
            Classification c = new Classification();
250
            c.setKingdom((String) classification.get("Kingdom"));
251
            c.setPhylum((String) classification.get("Phylum"));
252
            c.setClazz((String) classification.get("Class"));
253
            c.setOrder((String) classification.get("Order"));
254
            c.setFamily((String) classification.get("Family"));
255
            c.setGenus((String) classification.get("Genus"));
256
            accTaxon.setClassification(c);
257
        }
258
        return accTaxon;
259
    }
260

    
261
    private void generateSynonyms(JSONArray relatedTaxa, Response tnrResponse) {
262

    
263

    
264
        Iterator<JSONObject> itrSynonyms = relatedTaxa.iterator();
265
        while(itrSynonyms.hasNext()) {
266

    
267
            JSONObject synonymjs = itrSynonyms.next();
268
            String status = (String) synonymjs.get("taxonStatus");
269
            if(status != null && status.equals("synonym")) {
270
                Synonym synonym = new Synonym();
271
                TaxonName taxonName = new TaxonName();
272

    
273
                String resName = (String) synonymjs.get("name");
274
                taxonName.setFullName(resName);
275
                NameParser ecatParser = new NameParser();
276
                String nameCanonical = ecatParser.parseToCanonical(resName);
277
                taxonName.setCanonicalName(nameCanonical);
278
                synonym.setTaxonomicStatus((String)synonymjs.get("taxonStatus"));
279

    
280
                taxonName.setRank((String) synonymjs.get("rank"));
281

    
282
                synonym.setTaxonName(taxonName);
283

    
284
                JSONObject scrutinyjs = (JSONObject)synonymjs.get("taxonomicScrutiny"); // TODO this will change in future releases of the service
285
                synonym.setAccordingTo((String) scrutinyjs.get("accordingTo"));
286

    
287
                JSONObject sourcejs = (JSONObject)synonymjs.get("source");
288
                String sourceUrl = (String) sourcejs.get("url");
289
                String sourceDatasetID =  (String) sourcejs.get("datasetID");
290
                String sourceDatasetName = (String) sourcejs.get("datasetName");
291
                String sourceName = "";
292

    
293
                Source source = new Source();
294
                source.setIdentifier(sourceDatasetID);
295
                source.setDatasetName(sourceDatasetName);
296
                source.setName(sourceName);
297
                source.setUrl(sourceUrl);
298
                synonym.getSources().add(source);
299

    
300
                tnrResponse.getSynonym().add(synonym);
301
            }
302
        }
303
    }
304

    
305
    @Override
306
    public void resolveScientificNamesExact(TnrMsg tnrMsg) throws DRFChecklistException {
307

    
308
        List<Query> queryList = tnrMsg.getQuery();
309

    
310
        // selecting one request as representative, only
311
        // the search mode and addSynonmy flag are important
312
        // for the further usage of the request object
313
        Query.Request request = queryList.get(0).getRequest();
314

    
315
        for (ServiceProviderInfo checklistInfo : getServiceProviderInfo().getSubChecklists()) {
316
            URI namesUri = queryClient.buildUriFromQueryList(queryList,
317
                    SERVER_PATH_PREFIX + checklistInfo.getId() + "/name_catalogue.json",
318
                    "query",
319
                    "*", null);
320

    
321
            String searchResponseBody = queryClient.get(namesUri);
322

    
323
            buildTaxonIdMapsFromCatalogueServiceResponse(queryList, searchResponseBody);
324

    
325
            List<String> taxonIdList = new ArrayList<String>(taxonIdQueryMap.keySet());
326

    
327
            if(taxonIdList.size() > 0) {
328
                URI taxonUri = queryClient.buildUriFromQueryStringList(taxonIdList,
329
                        SERVER_PATH_PREFIX + checklistInfo.getId() + "/name_catalogue/taxon.json",
330
                        "taxonUuid",
331
                        null);
332
                String taxonResponseBody = queryClient.get(taxonUri);
333
                updateQueriesWithResponse(taxonResponseBody, checklistInfo, request);
334
            }
335
        }
336
    }
337

    
338
    @Override
339
    public void resolveScientificNamesLike(TnrMsg tnrMsg) throws DRFChecklistException {
340
        // delegate to resolveScientificNamesExact, since the like search mode is handled in buildUriFromQueryList
341
        resolveScientificNamesExact(tnrMsg);
342

    
343
    }
344

    
345
    @Override
346
    public void resolveVernacularNamesExact(TnrMsg tnrMsg) throws DRFChecklistException {
347
        // TODO Auto-generated method stub
348

    
349
    }
350

    
351
    @Override
352
    public void resolveVernacularNamesLike(TnrMsg tnrMsg) throws DRFChecklistException {
353
        // TODO Auto-generated method stub
354

    
355
    }
356

    
357
    @Override
358
    public void findByIdentifier(TnrMsg tnrMsg) throws DRFChecklistException {
359

    
360
        List<Query> queryList = tnrMsg.getQuery();
361

    
362
        if(queryList.size() > 1){
363
            throw new DRFChecklistException("Only single Querys are supported");
364
        }
365

    
366
        Query.Request request = queryList.get(0).getRequest();
367
        Map<String, String> findByIdentifierParameters = new HashMap<String,String>();
368
        findByIdentifierParameters.put("includeEntity", "1");
369

    
370
        String identifier = request.getQueryString();
371

    
372

    
373
        for (ServiceProviderInfo checklistInfo : getServiceProviderInfo().getSubChecklists()) {
374
            // taxon/findByIdentifier.json?identifier=1&includeEntity=1
375
            if(IdentifierUtils.checkLSID(identifier)){
376
                URI namesUri = queryClient.buildUriFromQueryList(queryList,
377
                        SERVER_PATH_PREFIX + checklistInfo.getId() + "/authority/metadata.do",
378
                        "lsid",
379
                        null,
380
                        null );
381

    
382
                String responseBody = queryClient.get(namesUri);
383
                addTaxonToTaxonIdMap(queryList, responseBody);
384
            } else {
385
                URI namesUri = queryClient.buildUriFromQueryList(queryList,
386
                        SERVER_PATH_PREFIX + checklistInfo.getId() + "/taxon/findByIdentifier.json",
387
                        "identifier",
388
                        null, // like search for identifiers not supported by this client
389
                        findByIdentifierParameters );
390

    
391
                String responseBody = queryClient.get(namesUri);
392
                addTaxaToTaxonIdMapFromIdentifierServiceResponse(queryList, responseBody);
393
            }
394

    
395
            List<String> taxonIdList = new ArrayList<String>(taxonIdQueryMap.keySet());
396

    
397
            if(taxonIdList.size() > 0) {
398
                URI taxonUri = queryClient.buildUriFromQueryStringList(taxonIdList,
399
                        SERVER_PATH_PREFIX + checklistInfo.getId() + "/name_catalogue/taxon.json",
400
                        "taxonUuid",
401
                        null);
402
                String taxonResponseBody = queryClient.get(taxonUri);
403
                updateQueriesWithResponse(taxonResponseBody, checklistInfo, request);
404
            }
405
        }
406

    
407
    }
408

    
409
    private void updateQueriesWithResponse(String responseBody, ServiceProviderInfo ci, Query.Request request) throws DRFChecklistException {
410

    
411
        if(responseBody == null || responseBody.isEmpty()){
412
            return;
413
        }
414

    
415
        JSONArray responseBodyJson = parseResponseBody(responseBody, JSONArray.class);
416

    
417
        Iterator<JSONObject> itrTaxonMsgs = responseBodyJson.iterator();
418

    
419
        int i = -1;
420
        while(itrTaxonMsgs.hasNext()) {
421
            i++;
422
            JSONObject taxonInfo = itrTaxonMsgs.next();
423
            JSONObject taxonResponse = (JSONObject) taxonInfo.get("response");
424
            JSONObject taxon = (JSONObject) taxonResponse.get("taxon");
425
            JSONArray relatedTaxa = (JSONArray) taxonResponse.get("relatedTaxa");
426

    
427
            JSONObject taxonRequest = (JSONObject) taxonInfo.get("request");
428
            String taxonUuid = (String) taxonRequest.get("taxonUuid");
429

    
430
            if(taxon != null) {
431
                Response tnrResponse = TnrMsgUtils.tnrResponseFor(ci);
432

    
433
                String matchingName = taxonIdMatchStringMap.get(taxonUuid);
434
                if(matchingName != null){
435
                    tnrResponse.setMatchingNameString(matchingName);
436
                    tnrResponse.setMatchingNameType(matchingName.equals(taxon.get("name").toString()) ? NameType.TAXON : NameType.SYNONYM);
437
                }
438

    
439
                Taxon accName = generateAccName(taxon);
440
                tnrResponse.setTaxon(accName);
441

    
442
                if(request.isAddSynonymy()){
443
                    generateSynonyms(relatedTaxa, tnrResponse);
444
                }
445

    
446
                Query query = taxonIdQueryMap.get(taxonUuid);
447
                if(query != null) {
448
                    query.getResponse().add(tnrResponse);
449
                }
450
            }
451
        }
452
    }
453

    
454
    @Override
455
    public EnumSet<SearchMode> getSearchModes() {
456
        return SEARCH_MODES ;
457
    }
458

    
459
    @Override
460
    public boolean isSupportedIdentifier(String value) {
461
        // return IdentifierUtils.checkLSID(value) || IdentifierUtils.checkUUID(value);
462
        return value != null;
463
    }
464

    
465

    
466

    
467
}
(3-3/12)