Project

General

Profile

Download (21 KB) Statistics
| Branch: | Tag: | Revision:
1
package org.bgbm.biovel.drf.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.bgbm.biovel.drf.client.ServiceProviderInfo;
15
import org.bgbm.biovel.drf.query.RestClient;
16
import org.bgbm.biovel.drf.tnr.msg.Classification;
17
import org.bgbm.biovel.drf.tnr.msg.NameType;
18
import org.bgbm.biovel.drf.tnr.msg.Query;
19
import org.bgbm.biovel.drf.tnr.msg.Response;
20
import org.bgbm.biovel.drf.tnr.msg.Source;
21
import org.bgbm.biovel.drf.tnr.msg.Synonym;
22
import org.bgbm.biovel.drf.tnr.msg.Taxon;
23
import org.bgbm.biovel.drf.tnr.msg.TaxonName;
24
import org.bgbm.biovel.drf.tnr.msg.TnrMsg;
25
import org.bgbm.biovel.drf.utils.IdentifierUtils;
26
import org.bgbm.biovel.drf.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
    @Override
81
    public int getMaxPageSize() {
82
        return 10;
83
    }
84

    
85
    /**
86
     * Adds the acceptedTaxonUuids found in the <code>responseBody</code> to the
87
     * private field <code>taxonIdQueryMap</code>
88
     * and populates the <code>taxonIdMatchStringMap</code>
89
     *
90
     * @param queryList
91
     * @param responseBodyJson
92
     * @throws DRFChecklistException
93
     */
94
    private void buildTaxonIdMapsFromCatalogueServiceResponse(List<Query> queryList , String responseBody) throws DRFChecklistException {
95

    
96
        JSONArray responseBodyJson = parseResponseBody(responseBody, JSONArray.class);
97

    
98
        if(responseBodyJson.size() != queryList.size()){
99
            throw new DRFChecklistException("Query and Response lists have different lengths");
100
        }
101

    
102
        Iterator<JSONObject> itrNameMsgs = responseBodyJson.iterator();
103

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

    
130
    /**
131
     * Adds the acceptedTaxonUuids found in the <code>responseBody</code> to the
132
     * private field <code>taxonIdQueryMap</code>
133
     * and populates the <code>taxonIdMatchStringMap</code>
134
     *
135
     * @param queryList
136
     * @param responseBodyJson
137
     * @throws DRFChecklistException
138
     */
139
    private void addTaxaToTaxonIdMapFromIdentifierServiceResponse(List<Query> queryList , String responseBody) throws DRFChecklistException {
140

    
141
        /*
142
         * {"class":"DefaultPagerImpl","count":8,"currentIndex":0,"firstRecord":1,"indices":[0],"lastRecord":8,"nextIndex":0,"pageSize":30,"pagesAvailable":1,"prevIndex":0,
143
            "records":[
144
                  {
145
                  "cdmEntity":{
146
                      "cdmUuid":"0cf5a6fe-b1df-4f4f-85a8-31fef4be2a68",
147
                      "class":"CdmEntity",
148
                      "titleCache":"Abies alba Mill. sec. SCHMEIL-FITSCHEN, Flora von Deutschland und angrenzenden Ländern, 89. Aufl"},
149
                  "class":"FindByIdentifierDTO",
150
                   "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":""}
151
        */
152

    
153
        if(queryList.size() > 1){
154
            throw new DRFChecklistException("Only single Querys are supported");
155
        }
156

    
157
        Query query = queryList.get(0);
158

    
159
        JSONObject jsonPager = parseResponseBody(responseBody, JSONObject.class);
160

    
161
        JSONArray jsonRecords = (JSONArray) jsonPager.get("records");
162

    
163

    
164
        Iterator<JSONObject> resIterator = jsonRecords.iterator();
165
        while (resIterator.hasNext()) {
166
            JSONObject record = resIterator.next();
167
            JSONObject cdmEntity = (JSONObject) record.get("cdmEntity");
168
            String uuid = cdmEntity.get("cdmUuid").toString();
169
            taxonIdQueryMap.put(uuid, query);
170
        }
171

    
172
    }
173

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

    
176

    
177
        if(queryList.size() > 1){
178
            throw new DRFChecklistException("Only single Querys are supported");
179
        }
180

    
181
        Query query = queryList.get(0);
182

    
183
        JSONObject cdmEntity = parseResponseBody(responseBody, JSONObject.class);
184
        String uuid = cdmEntity.get("uuid").toString();
185
        taxonIdQueryMap.put(uuid, query);
186

    
187
    }
188

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

    
205
            logger.error("parseResponseBody() - ", e);
206
            throw new DRFChecklistException(e);
207
        }
208

    
209
        if(jsonType.isAssignableFrom(obj.getClass())){
210
            return jsonType.cast(obj);
211
        } else {
212
            throw new DRFChecklistException("parseResponseBody() - deserialized responseBody is not of type " + jsonType ) ;
213
        }
214

    
215
    }
216

    
217
    private Taxon generateAccName(JSONObject taxon) {
218
        Taxon accTaxon = new Taxon();
219
        TaxonName taxonName = new TaxonName();
220

    
221
        String resName = (String) taxon.get("name");
222
        taxonName.setFullName(resName);
223
        NameParser ecatParser = new NameParser();
224
        String nameCanonical = ecatParser.parseToCanonical(resName);
225
        taxonName.setCanonicalName(nameCanonical);
226

    
227
        taxonName.setRank((String) taxon.get("rank"));
228
        String lsid = (String) taxon.get("lsid");
229

    
230
        JSONObject scrutinyjs = (JSONObject)taxon.get("taxonomicScrutiny");
231
        String accordingTo = (String) scrutinyjs.get("accordingTo");
232
        String modified = (String) scrutinyjs.get("modified");
233

    
234
        accTaxon.setTaxonName(taxonName);
235
        accTaxon.setTaxonomicStatus((String)taxon.get("taxonStatus"));
236
        accTaxon.setAccordingTo(accordingTo);
237
        accTaxon.setIdentifier(lsid);
238

    
239
        JSONObject sourcejs = (JSONObject)taxon.get("source");
240
        String sourceUrl = (String) sourcejs.get("url");
241
        String sourceDatasetID =  (String) sourcejs.get("datasetID");
242
        String sourceDatasetName = (String) sourcejs.get("datasetName");
243
        String sourceName = "";
244

    
245
        Source source = new Source();
246
        source.setIdentifier(sourceDatasetID);
247
        source.setDatasetName(sourceDatasetName);
248
        source.setName(sourceName);
249
        source.setUrl(sourceUrl);
250
        accTaxon.getSources().add(source);
251

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

    
266
    private void generateSynonyms(JSONArray relatedTaxa, Response tnrResponse) {
267

    
268

    
269
        Iterator<JSONObject> itrSynonyms = relatedTaxa.iterator();
270
        while(itrSynonyms.hasNext()) {
271

    
272
            JSONObject synonymjs = itrSynonyms.next();
273
            String status = (String) synonymjs.get("taxonStatus");
274
            if(status != null && status.equals("synonym")) {
275
                Synonym synonym = new Synonym();
276
                TaxonName taxonName = new TaxonName();
277

    
278
                String resName = (String) synonymjs.get("name");
279
                taxonName.setFullName(resName);
280
                NameParser ecatParser = new NameParser();
281
                String nameCanonical = ecatParser.parseToCanonical(resName);
282
                taxonName.setCanonicalName(nameCanonical);
283
                synonym.setTaxonomicStatus((String)synonymjs.get("taxonStatus"));
284

    
285
                taxonName.setRank((String) synonymjs.get("rank"));
286

    
287
                synonym.setTaxonName(taxonName);
288

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

    
292
                JSONObject sourcejs = (JSONObject)synonymjs.get("source");
293
                String sourceUrl = (String) sourcejs.get("url");
294
                String sourceDatasetID =  (String) sourcejs.get("datasetID");
295
                String sourceDatasetName = (String) sourcejs.get("datasetName");
296
                String sourceName = "";
297

    
298
                Source source = new Source();
299
                source.setIdentifier(sourceDatasetID);
300
                source.setDatasetName(sourceDatasetName);
301
                source.setName(sourceName);
302
                source.setUrl(sourceUrl);
303
                synonym.getSources().add(source);
304

    
305
                tnrResponse.getSynonym().add(synonym);
306
            }
307
        }
308
    }
309

    
310
    @Override
311
    public void resolveScientificNamesExact(TnrMsg tnrMsg) throws DRFChecklistException {
312

    
313
        List<Query> queryList = tnrMsg.getQuery();
314

    
315
        // selecting one request as representative, only
316
        // the search mode and addSynonmy flag are important
317
        // for the further usage of the request object
318
        Query.Request request = queryList.get(0).getRequest();
319

    
320
        for (ServiceProviderInfo checklistInfo : getServiceProviderInfo().getSubChecklists()) {
321
            URI namesUri = queryClient.buildUriFromQueryList(queryList,
322
                    SERVER_PATH_PREFIX + checklistInfo.getId() + "/name_catalogue.json",
323
                    "query",
324
                    "*", null);
325

    
326
            String searchResponseBody = queryClient.processRESTService(namesUri);
327

    
328
            buildTaxonIdMapsFromCatalogueServiceResponse(queryList, searchResponseBody);
329

    
330
            List<String> taxonIdList = new ArrayList<String>(taxonIdQueryMap.keySet());
331

    
332
            if(taxonIdList.size() > 0) {
333
                URI taxonUri = queryClient.buildUriFromQueryStringList(taxonIdList,
334
                        SERVER_PATH_PREFIX + checklistInfo.getId() + "/name_catalogue/taxon.json",
335
                        "taxonUuid",
336
                        null);
337
                String taxonResponseBody = queryClient.processRESTService(taxonUri);
338
                updateQueriesWithResponse(taxonResponseBody, checklistInfo, request);
339
            }
340
        }
341
    }
342

    
343
    @Override
344
    public void resolveScientificNamesLike(TnrMsg tnrMsg) throws DRFChecklistException {
345
        // delegate to resolveScientificNamesExact, since the like search mode is handled in buildUriFromQueryList
346
        resolveScientificNamesExact(tnrMsg);
347

    
348
    }
349

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

    
354
    }
355

    
356
    @Override
357
    public void resolveVernacularNamesLike(TnrMsg tnrMsg) throws DRFChecklistException {
358
        // TODO Auto-generated method stub
359

    
360
    }
361

    
362
    @Override
363
    public void findByIdentifier(TnrMsg tnrMsg) throws DRFChecklistException {
364

    
365
        List<Query> queryList = tnrMsg.getQuery();
366

    
367
        if(queryList.size() > 1){
368
            throw new DRFChecklistException("Only single Querys are supported");
369
        }
370

    
371
        Query.Request request = queryList.get(0).getRequest();
372
        Map<String, String> findByIdentifierParameters = new HashMap<String,String>();
373
        findByIdentifierParameters.put("includeEntity", "1");
374

    
375
        String identifier = request.getQueryString();
376

    
377

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

    
387
                String responseBody = queryClient.processRESTService(namesUri);
388
                addTaxonToTaxonIdMap(queryList, responseBody);
389
            } else {
390
                URI namesUri = queryClient.buildUriFromQueryList(queryList,
391
                        SERVER_PATH_PREFIX + checklistInfo.getId() + "/taxon/findByIdentifier.json",
392
                        "identifier",
393
                        null, // like search for identifiers not supported by this client
394
                        findByIdentifierParameters );
395

    
396
                String responseBody = queryClient.processRESTService(namesUri);
397
                addTaxaToTaxonIdMapFromIdentifierServiceResponse(queryList, responseBody);
398
            }
399

    
400
            List<String> taxonIdList = new ArrayList<String>(taxonIdQueryMap.keySet());
401

    
402
            if(taxonIdList.size() > 0) {
403
                URI taxonUri = queryClient.buildUriFromQueryStringList(taxonIdList,
404
                        SERVER_PATH_PREFIX + checklistInfo.getId() + "/name_catalogue/taxon.json",
405
                        "taxonUuid",
406
                        null);
407
                String taxonResponseBody = queryClient.processRESTService(taxonUri);
408
                updateQueriesWithResponse(taxonResponseBody, checklistInfo, request);
409
            }
410
        }
411

    
412
    }
413

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

    
416
        if(responseBody == null || responseBody.isEmpty()){
417
            return;
418
        }
419

    
420
        JSONArray responseBodyJson = parseResponseBody(responseBody, JSONArray.class);
421

    
422
        Iterator<JSONObject> itrTaxonMsgs = responseBodyJson.iterator();
423

    
424
        int i = -1;
425
        while(itrTaxonMsgs.hasNext()) {
426
            i++;
427
            JSONObject taxonInfo = itrTaxonMsgs.next();
428
            JSONObject taxonResponse = (JSONObject) taxonInfo.get("response");
429
            JSONObject taxon = (JSONObject) taxonResponse.get("taxon");
430
            JSONArray relatedTaxa = (JSONArray) taxonResponse.get("relatedTaxa");
431

    
432
            JSONObject taxonRequest = (JSONObject) taxonInfo.get("request");
433
            String taxonUuid = (String) taxonRequest.get("taxonUuid");
434

    
435
            if(taxon != null) {
436
                Response tnrResponse = TnrMsgUtils.tnrResponseFor(ci);
437

    
438
                String matchingName = taxonIdMatchStringMap.get(taxonUuid);
439
                if(matchingName != null){
440
                    tnrResponse.setMatchingNameString(matchingName);
441
                    tnrResponse.setMatchingNameType(matchingName.equals(taxon.get("name").toString()) ? NameType.TAXON : NameType.SYNONYM);
442
                }
443

    
444
                Taxon accName = generateAccName(taxon);
445
                tnrResponse.setTaxon(accName);
446

    
447
                if(request.isAddSynonymy()){
448
                    generateSynonyms(relatedTaxa, tnrResponse);
449
                }
450

    
451
                Query query = taxonIdQueryMap.get(taxonUuid);
452
                if(query != null) {
453
                    query.getResponse().add(tnrResponse);
454
                }
455
            }
456
        }
457
    }
458

    
459
    @Override
460
    public EnumSet<SearchMode> getSearchModes() {
461
        return SEARCH_MODES ;
462
    }
463

    
464
    @Override
465
    public boolean isSupportedIdentifier(String value) {
466
        // return IdentifierUtils.checkLSID(value) || IdentifierUtils.checkUUID(value);
467
        return value != null;
468
    }
469

    
470

    
471

    
472
}
(3-3/12)