Project

General

Profile

Download (21.2 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
    /**
65
     * {@inheritDoc}
66
     */
67
    @Override
68
    public boolean isStatelessClient() {
69
        // TODO move taxonIdQueryMap and taxonIdMatchStringMap into
70
        //      session state object to make this a stateless client
71
        return false;
72
    }
73

    
74
    @Override
75
    public void initQueryClient() {
76
        queryClient = new RestClient(HTTP_HOST);
77
    }
78

    
79

    
80
    @Override
81
    public ServiceProviderInfo buildServiceProviderInfo() {
82
        ServiceProviderInfo checklistInfo = new ServiceProviderInfo(ID,LABEL,DOC_URL,COPYRIGHT_URL, getSearchModes());
83
        checklistInfo.addSubChecklist(new ServiceProviderInfo("col",
84
                "Catalogue Of Life (EDIT - name catalogue end point)",
85
                "http://wp5.e-taxonomy.eu/cdmlib/rest-api-name-catalogue.html",
86
                "http://www.catalogueoflife.org/col/info/copyright", ServiceProviderInfo.DEFAULT_SEARCH_MODE));
87
        return checklistInfo;
88
    }
89

    
90
    /**
91
     * Adds the acceptedTaxonUuids found in the <code>responseBody</code> to the
92
     * private field <code>taxonIdQueryMap</code>
93
     * and populates the <code>taxonIdMatchStringMap</code>
94
     *
95
     * @param queryList
96
     * @param responseBodyJson
97
     * @throws DRFChecklistException
98
     */
99
    private void buildTaxonIdMapsFromCatalogueServiceResponse(List<Query> queryList , String responseBody) throws DRFChecklistException {
100

    
101
        JSONArray responseBodyJson = parseResponseBody(responseBody, JSONArray.class);
102

    
103
        if(responseBodyJson.size() != queryList.size()){
104
            throw new DRFChecklistException("Query and Response lists have different lengths");
105
        }
106

    
107
        Iterator<JSONObject> itrNameMsgs = responseBodyJson.iterator();
108

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

    
135
    /**
136
     * Adds the acceptedTaxonUuids found in the <code>responseBody</code> to the
137
     * private field <code>taxonIdQueryMap</code>
138
     * and populates the <code>taxonIdMatchStringMap</code>
139
     *
140
     * @param queryList
141
     * @param responseBodyJson
142
     * @throws DRFChecklistException
143
     */
144
    private void addTaxaToTaxonIdMapFromIdentifierServiceResponse(List<Query> queryList , String responseBody) throws DRFChecklistException {
145

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

    
158
        if(queryList.size() > 1){
159
            throw new DRFChecklistException("Only single Querys are supported");
160
        }
161

    
162
        Query query = queryList.get(0);
163

    
164
        JSONObject jsonPager = parseResponseBody(responseBody, JSONObject.class);
165

    
166
        JSONArray jsonRecords = (JSONArray) jsonPager.get("records");
167

    
168

    
169
        Iterator<JSONObject> resIterator = jsonRecords.iterator();
170
        while (resIterator.hasNext()) {
171
            JSONObject record = resIterator.next();
172
            JSONObject cdmEntity = (JSONObject) record.get("cdmEntity");
173
            String uuid = cdmEntity.get("cdmUuid").toString();
174
            taxonIdQueryMap.put(uuid, query);
175
        }
176

    
177
    }
178

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

    
181

    
182
        if(queryList.size() > 1){
183
            throw new DRFChecklistException("Only single Querys are supported");
184
        }
185

    
186
        Query query = queryList.get(0);
187

    
188
        JSONObject cdmEntity = parseResponseBody(responseBody, JSONObject.class);
189
        String uuid = cdmEntity.get("uuid").toString();
190
        taxonIdQueryMap.put(uuid, query);
191

    
192
    }
193

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

    
210
            logger.error("parseResponseBody() - ", e);
211
            throw new DRFChecklistException(e);
212
        }
213

    
214
        if(jsonType.isAssignableFrom(obj.getClass())){
215
            return jsonType.cast(obj);
216
        } else {
217
            throw new DRFChecklistException("parseResponseBody() - deserialized responseBody is not of type " + jsonType ) ;
218
        }
219

    
220
    }
221

    
222
    private Taxon generateAccName(JSONObject taxon) {
223
        Taxon accTaxon = new Taxon();
224
        TaxonName taxonName = new TaxonName();
225

    
226
        String resName = (String) taxon.get("name");
227
        taxonName.setFullName(resName);
228
        NameParser ecatParser = new NameParser();
229
        String nameCanonical = ecatParser.parseToCanonical(resName);
230
        taxonName.setCanonicalName(nameCanonical);
231

    
232
        taxonName.setRank((String) taxon.get("rank"));
233
        String lsid = (String) taxon.get("lsid");
234

    
235
        JSONObject scrutinyjs = (JSONObject)taxon.get("taxonomicScrutiny");
236
        String accordingTo = (String) scrutinyjs.get("accordingTo");
237
        String modified = (String) scrutinyjs.get("modified");
238

    
239
        accTaxon.setTaxonName(taxonName);
240
        accTaxon.setTaxonomicStatus((String)taxon.get("taxonStatus"));
241
        accTaxon.setAccordingTo(accordingTo);
242
        accTaxon.setIdentifier(lsid);
243

    
244
        JSONObject sourcejs = (JSONObject)taxon.get("source");
245
        String sourceUrl = (String) sourcejs.get("url");
246
        String sourceDatasetID =  (String) sourcejs.get("datasetID");
247
        String sourceDatasetName = (String) sourcejs.get("datasetName");
248
        String sourceName = "";
249

    
250
        Source source = new Source();
251
        source.setIdentifier(sourceDatasetID);
252
        source.setDatasetName(sourceDatasetName);
253
        source.setName(sourceName);
254
        source.setUrl(sourceUrl);
255
        accTaxon.getSources().add(source);
256

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

    
271
    private void generateSynonyms(JSONArray relatedTaxa, Response tnrResponse) {
272

    
273

    
274
        Iterator<JSONObject> itrSynonyms = relatedTaxa.iterator();
275
        while(itrSynonyms.hasNext()) {
276

    
277
            JSONObject synonymjs = itrSynonyms.next();
278
            String status = (String) synonymjs.get("taxonStatus");
279
            if(status != null && status.equals("synonym")) {
280
                Synonym synonym = new Synonym();
281
                TaxonName taxonName = new TaxonName();
282

    
283
                String resName = (String) synonymjs.get("name");
284
                taxonName.setFullName(resName);
285
                NameParser ecatParser = new NameParser();
286
                String nameCanonical = ecatParser.parseToCanonical(resName);
287
                taxonName.setCanonicalName(nameCanonical);
288
                synonym.setTaxonomicStatus((String)synonymjs.get("taxonStatus"));
289

    
290
                taxonName.setRank((String) synonymjs.get("rank"));
291

    
292
                synonym.setTaxonName(taxonName);
293

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

    
297
                JSONObject sourcejs = (JSONObject)synonymjs.get("source");
298
                String sourceUrl = (String) sourcejs.get("url");
299
                String sourceDatasetID =  (String) sourcejs.get("datasetID");
300
                String sourceDatasetName = (String) sourcejs.get("datasetName");
301
                String sourceName = "";
302

    
303
                Source source = new Source();
304
                source.setIdentifier(sourceDatasetID);
305
                source.setDatasetName(sourceDatasetName);
306
                source.setName(sourceName);
307
                source.setUrl(sourceUrl);
308
                synonym.getSources().add(source);
309

    
310
                tnrResponse.getSynonym().add(synonym);
311
            }
312
        }
313
    }
314

    
315
    @Override
316
    public void resolveScientificNamesExact(TnrMsg tnrMsg) throws DRFChecklistException {
317

    
318
        List<Query> queryList = tnrMsg.getQuery();
319

    
320
        // selecting one request as representative, only
321
        // the search mode and addSynonmy flag are important
322
        // for the further usage of the request object
323
        Query.Request request = queryList.get(0).getRequest();
324

    
325
        for (ServiceProviderInfo checklistInfo : getServiceProviderInfo().getSubChecklists()) {
326
            URI namesUri = queryClient.buildUriFromQueryList(queryList,
327
                    SERVER_PATH_PREFIX + checklistInfo.getId() + "/name_catalogue.json",
328
                    "query",
329
                    "*", null);
330

    
331
            String searchResponseBody = queryClient.get(namesUri);
332

    
333
            buildTaxonIdMapsFromCatalogueServiceResponse(queryList, searchResponseBody);
334

    
335
            List<String> taxonIdList = new ArrayList<String>(taxonIdQueryMap.keySet());
336

    
337
            if(taxonIdList.size() > 0) {
338
                URI taxonUri = queryClient.buildUriFromQueryStringList(taxonIdList,
339
                        SERVER_PATH_PREFIX + checklistInfo.getId() + "/name_catalogue/taxon.json",
340
                        "taxonUuid",
341
                        null);
342
                String taxonResponseBody = queryClient.get(taxonUri);
343
                updateQueriesWithResponse(taxonResponseBody, checklistInfo, request);
344
            }
345
        }
346
    }
347

    
348
    @Override
349
    public void resolveScientificNamesLike(TnrMsg tnrMsg) throws DRFChecklistException {
350
        // delegate to resolveScientificNamesExact, since the like search mode is handled in buildUriFromQueryList
351
        resolveScientificNamesExact(tnrMsg);
352

    
353
    }
354

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

    
359
    }
360

    
361
    @Override
362
    public void resolveVernacularNamesLike(TnrMsg tnrMsg) throws DRFChecklistException {
363
        // TODO Auto-generated method stub
364

    
365
    }
366

    
367
    @Override
368
    public void findByIdentifier(TnrMsg tnrMsg) throws DRFChecklistException {
369

    
370
        List<Query> queryList = tnrMsg.getQuery();
371

    
372
        if(queryList.size() > 1){
373
            throw new DRFChecklistException("Only single Querys are supported");
374
        }
375

    
376
        Query.Request request = queryList.get(0).getRequest();
377
        Map<String, String> findByIdentifierParameters = new HashMap<String,String>();
378
        findByIdentifierParameters.put("includeEntity", "1");
379

    
380
        String identifier = request.getQueryString();
381

    
382

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

    
392
                String responseBody = queryClient.get(namesUri);
393
                addTaxonToTaxonIdMap(queryList, responseBody);
394
            } else {
395
                URI namesUri = queryClient.buildUriFromQueryList(queryList,
396
                        SERVER_PATH_PREFIX + checklistInfo.getId() + "/taxon/findByIdentifier.json",
397
                        "identifier",
398
                        null, // like search for identifiers not supported by this client
399
                        findByIdentifierParameters );
400

    
401
                String responseBody = queryClient.get(namesUri);
402
                addTaxaToTaxonIdMapFromIdentifierServiceResponse(queryList, responseBody);
403
            }
404

    
405
            List<String> taxonIdList = new ArrayList<String>(taxonIdQueryMap.keySet());
406

    
407
            if(taxonIdList.size() > 0) {
408
                URI taxonUri = queryClient.buildUriFromQueryStringList(taxonIdList,
409
                        SERVER_PATH_PREFIX + checklistInfo.getId() + "/name_catalogue/taxon.json",
410
                        "taxonUuid",
411
                        null);
412
                String taxonResponseBody = queryClient.get(taxonUri);
413
                updateQueriesWithResponse(taxonResponseBody, checklistInfo, request);
414
            }
415
        }
416

    
417
    }
418

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

    
421
        if(responseBody == null || responseBody.isEmpty()){
422
            return;
423
        }
424

    
425
        JSONArray responseBodyJson = parseResponseBody(responseBody, JSONArray.class);
426

    
427
        Iterator<JSONObject> itrTaxonMsgs = responseBodyJson.iterator();
428

    
429
        int i = -1;
430
        while(itrTaxonMsgs.hasNext()) {
431
            i++;
432
            JSONObject taxonInfo = itrTaxonMsgs.next();
433
            JSONObject taxonResponse = (JSONObject) taxonInfo.get("response");
434
            JSONObject taxon = (JSONObject) taxonResponse.get("taxon");
435
            JSONArray relatedTaxa = (JSONArray) taxonResponse.get("relatedTaxa");
436

    
437
            JSONObject taxonRequest = (JSONObject) taxonInfo.get("request");
438
            String taxonUuid = (String) taxonRequest.get("taxonUuid");
439

    
440
            if(taxon != null) {
441
                Response tnrResponse = TnrMsgUtils.tnrResponseFor(ci);
442

    
443
                String matchingName = taxonIdMatchStringMap.get(taxonUuid);
444
                if(matchingName != null){
445
                    tnrResponse.setMatchingNameString(matchingName);
446
                    tnrResponse.setMatchingNameType(matchingName.equals(taxon.get("name").toString()) ? NameType.TAXON : NameType.SYNONYM);
447
                }
448

    
449
                Taxon accName = generateAccName(taxon);
450
                tnrResponse.setTaxon(accName);
451

    
452
                if(request.isAddSynonymy()){
453
                    generateSynonyms(relatedTaxa, tnrResponse);
454
                }
455

    
456
                Query query = taxonIdQueryMap.get(taxonUuid);
457
                if(query != null) {
458
                    query.getResponse().add(tnrResponse);
459
                }
460
            }
461
        }
462
    }
463

    
464
    @Override
465
    public EnumSet<SearchMode> getSearchModes() {
466
        return SEARCH_MODES ;
467
    }
468

    
469
    @Override
470
    public boolean isSupportedIdentifier(String value) {
471
        // return IdentifierUtils.checkLSID(value) || IdentifierUtils.checkUUID(value);
472
        return value != null;
473
    }
474

    
475

    
476

    
477
}
(3-3/12)