Project

General

Profile

Download (17.6 KB) Statistics
| Branch: | Tag: | Revision:
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
import java.io.IOException;
11
import java.io.InputStream;
12
import java.util.ArrayList;
13
import java.util.Arrays;
14
import java.util.HashMap;
15
import java.util.List;
16
import java.util.Map;
17
import java.util.Set;
18
import java.util.UUID;
19

    
20
import javax.servlet.http.HttpServletRequest;
21
import javax.servlet.http.HttpServletResponse;
22

    
23
import org.apache.poi.ss.formula.functions.T;
24
import org.apache.tools.ant.taskdefs.condition.Os;
25
import org.springframework.beans.factory.annotation.Autowired;
26
import org.springframework.context.ResourceLoaderAware;
27
import org.springframework.core.io.Resource;
28
import org.springframework.core.io.ResourceLoader;
29
import org.springframework.stereotype.Controller;
30
import org.springframework.web.bind.annotation.RequestMapping;
31
import org.springframework.web.bind.annotation.RequestMethod;
32
import org.springframework.web.bind.annotation.RequestParam;
33
import org.springframework.web.servlet.ModelAndView;
34

    
35
import eu.etaxonomy.cdm.api.facade.DerivedUnitFacade;
36
import eu.etaxonomy.cdm.api.facade.DerivedUnitFacadeConfigurator;
37
import eu.etaxonomy.cdm.api.facade.DerivedUnitFacadeNotSupportedException;
38
import eu.etaxonomy.cdm.api.service.IClassificationService;
39
import eu.etaxonomy.cdm.api.service.ICommonService;
40
import eu.etaxonomy.cdm.api.service.IDescriptionService;
41
import eu.etaxonomy.cdm.api.service.IOccurrenceService;
42
import eu.etaxonomy.cdm.api.service.ITaxonService;
43
import eu.etaxonomy.cdm.api.service.ITermService;
44
import eu.etaxonomy.cdm.api.service.pager.Pager;
45
import eu.etaxonomy.cdm.api.service.pager.impl.DefaultPagerImpl;
46
import eu.etaxonomy.cdm.api.service.util.TaxonRelationshipEdge;
47
import eu.etaxonomy.cdm.common.DocUtils;
48
import eu.etaxonomy.cdm.model.common.CdmBase;
49
import eu.etaxonomy.cdm.model.description.DescriptionBase;
50
import eu.etaxonomy.cdm.model.description.Feature;
51
import eu.etaxonomy.cdm.model.occurrence.DerivedUnit;
52
import eu.etaxonomy.cdm.model.occurrence.GatheringEvent;
53
import eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationBase;
54
import eu.etaxonomy.cdm.model.taxon.Taxon;
55
import eu.etaxonomy.cdm.persistence.query.OrderHint;
56
import eu.etaxonomy.cdm.remote.controller.IdentifiableListController;
57
import eu.etaxonomy.cdm.remote.controller.util.ControllerUtils;
58
import eu.etaxonomy.cdm.remote.controller.util.PagerParameters;
59
import eu.etaxonomy.cdm.remote.dto.common.ErrorResponse;
60
import eu.etaxonomy.cdm.remote.dto.occurrencecatalogue.OccurrenceSearch;
61
import eu.etaxonomy.cdm.remote.dto.occurrencecatalogue.OccurrenceSearch.OccurrenceSearchResponse;
62
import eu.etaxonomy.cdm.remote.view.HtmlView;
63

    
64
/**
65
 * The controller class for the namespace 'occurrence_catalogue'. This web service namespace
66
 * is an add-on to the already existing CDM REST API and provides information relating
67
 * to scientific names as well as taxa present in the underlying datasource.
68
 *
69
 * @author p.kelbert
70
 * @version 1.1.0
71
 * @created March-2014
72
 */
73

    
74
@Controller
75
@RequestMapping(value = { "/occurrence_catalogue" })
76
public class OccurrenceCatalogueController extends IdentifiableListController<SpecimenOrObservationBase, IOccurrenceService> implements ResourceLoaderAware{
77

    
78
    private ResourceLoader resourceLoader;
79

    
80
    /** Base scientific name search type */
81
    public static final String NAME_SEARCH = "occurrence";
82

    
83
    /** Complete scientific name search type */
84
    public static final String TITLE_SEARCH = "title";
85

    
86
    /** Default name search type */
87
    public static final String DEFAULT_SEARCH_TYPE = NAME_SEARCH;
88

    
89
    public static final String DEFAULT_PAGE_NUMBER = "0";
90

    
91
    public static final String DEFAULT_PAGE_SIZE = "50";
92

    
93

    
94
    /** Default max number of hits for the exact name search */
95
    public static final String DEFAULT_MAX_NB_FOR_EXACT_SEARCH = "100";
96

    
97
    /** Classifcation 'default' key */
98
    public static final String CLASSIFICATION_DEFAULT = "default";
99

    
100
    /** Classifcation 'all' key */
101
    public static final String CLASSIFICATION_ALL = "all";
102

    
103

    
104
    @Autowired
105
    private ITaxonService taxonService;
106

    
107
    @Autowired
108
    private IOccurrenceService occurrenceService;
109

    
110
    @Autowired
111
    private IDescriptionService descriptionService;
112

    
113
    @Autowired
114
    private IClassificationService classificationService;
115

    
116
    @Autowired
117
    private ICommonService commonService;
118

    
119
    @Autowired
120
    private ITermService termService;
121

    
122

    
123
    private static final List<String> OCCURRENCE_INIT_STRATEGY = Arrays.asList(new String []{    		
124
    		"sources"
125
    });
126

    
127
    private static final List<String> FACADE_INIT_STRATEGY = Arrays.asList(new String []{
128
    		"collector",
129
    		"collection.institute.name",
130
    		"fieldNotes",
131
    		"type",
132
    		"individualCount",
133
    		"kindOfUnit.label",
134
    		"absoluteElevation",
135
    		"absoluteElevationMaximum",
136
    		"distanceToGround",
137
    		"distanceToGroundMax",
138
    		"gatheringPeriod.start",
139
    		"gatheringPeriod.end",
140
    		"exactLocation.latitude",
141
    		"exactLocation.longitude",
142
    		"exactLocation.errorRadius",
143
    		"exactLocation.referenceSystem",
144
    		"country",
145
    		"locality"
146
    });
147

    
148

    
149

    
150
    public OccurrenceCatalogueController() {
151
        super();
152
        setInitializationStrategy(Arrays.asList(new String[] { "$" }));
153
    }
154

    
155
    /**
156
     * Returns a documentation page for the Occurrence Search API.
157
     * <p>
158
     * URI: <b>&#x002F;{datasource-name}&#x002F;occurrence_catalogue</b>
159
     *
160
     * @param request
161
     * @param response
162
     * @return Html page describing the Name Search API
163
     * @throws IOException
164
     */
165
    @RequestMapping(value = { "" }, method = RequestMethod.GET, params = {})
166
    public ModelAndView doGetNameSearchDocumentation(
167
            HttpServletRequest request, HttpServletResponse response)
168
            throws IOException {
169
        ModelAndView mv = new ModelAndView();
170
        // Read apt documentation file.
171
        Resource resource = resourceLoader.getResource("classpath:eu/etaxonomy/cdm/doc/remote/apt/occurrence-catalogue-default.apt");
172
        // using input stream as this works for both files in the classes directory
173
        // as well as files inside jars
174
        InputStream aptInputStream = resource.getInputStream();
175
        // Build Html View
176
        Map<String, String> modelMap = new HashMap<String, String>();
177
        // Convert Apt to Html
178
        modelMap.put("html", DocUtils.convertAptToHtml(aptInputStream));
179
        mv.addAllObjects(modelMap);
180

    
181
        HtmlView hv = new HtmlView();
182
        mv.setView(hv);
183
        return mv;
184
    }
185

    
186
    /**
187
     * Returns a list of occurrences matching the <code>{query}</code>
188
     * Taxon UUID.
189
     * <p>
190
     * Endpoint documentation can be found <a href="{@docRoot}/../remote/occurrence-catalogue-default.html">here</a>
191
     * <p>
192
     * URI: <b>&#x002F;{datasource-name}&#x002F;occurrence_catalogue</b>
193
     *
194
     * @param query
195
     *  The UUID of the taxon to query for. The query can
196
     *  not contain wildcard characters.
197
     *
198
     * @param request Http servlet request.
199
     * @param response Http servlet response.
200
     * @return a List of {@link OccurrenceSearch} objects each corresponding to a
201
     * single query. These are built from {@link SpecimenOrObservationBase} entities
202
     * which are in turn initialized using the {@link #OCCURRENCE_INIT_STRATEGY} and {@link #FACADE_INIT_STRATEGY}
203
     * Redirect the query with the default page size and the default page number.
204
     * @throws IOException
205
     */
206
    @RequestMapping(value = { "" }, method = RequestMethod.GET, params = {"taxonUuid"})
207
    public ModelAndView doGetOccurrenceSearch(@RequestParam(value = "taxonUuid", required = true) String query,
208
            HttpServletRequest request, HttpServletResponse response) throws IOException {
209
        return doGetOccurrenceSearch(query, DEFAULT_PAGE_NUMBER, DEFAULT_PAGE_SIZE, request, response);
210
    }
211

    
212

    
213

    
214

    
215
    /**
216
     * Returns a list of occurrences matching the <code>{query}</code>
217
     * Taxon UUID.
218
     * <p>
219
     * Endpoint documentation can be found <a href="{@docRoot}/../remote/occurrence-catalogue-default.html">here</a>
220
     * <p>
221
     * URI: <b>&#x002F;{datasource-name}&#x002F;occurrence_catalogue</b>
222
     *
223
     * @param query
224
     * 	The UUID of the taxon to query for. The query can
225
     * 	not contain wildcard characters.
226
     *
227
     * @param pageNumber
228
     * 	The number of the page to be returned.
229
     *  * @param pageSize
230
     *  The number of responses per page to be returned.
231
     * @param request Http servlet request.
232
     * @param response Http servlet response.
233
     * @return a List of {@link OccurrenceSearch} objects each corresponding to a
234
     * single query. These are built from {@link SpecimenOrObservationBase} entities
235
     * which are in turn initialized using the {@link #OCCURRENCE_INIT_STRATEGY} and {@link #FACADE_INIT_STRATEGY}
236
     * @throws IOException
237
     */
238
    @SuppressWarnings({ "rawtypes", "unchecked" })
239
    @RequestMapping(value = { "" }, method = RequestMethod.GET, params = {"taxonUuid","pageNumber","pageSize"})
240
    public ModelAndView doGetOccurrenceSearch(
241
            @RequestParam(value = "taxonUuid", required = true) String taxonUuid,
242
            @RequestParam(value = "pageNumber", required = false, defaultValue = DEFAULT_PAGE_NUMBER) String pageNumber,
243
            @RequestParam(value = "pageSize", required = false, defaultValue = DEFAULT_PAGE_SIZE) String pageSize,
244
            HttpServletRequest request, HttpServletResponse response) throws IOException {
245
        logger.info("doGetOccurrenceSearch, " + "taxonUuid : " + taxonUuid + " - pageSize : " + pageSize + " - " + "pageNumber : " + pageNumber);
246
        ModelAndView mv = new ModelAndView();
247
        List nsList = new ArrayList<T>();
248

    
249
        Integer pS = null;
250
        Integer pN = null;
251

    
252
        try {
253
            pN=Integer.valueOf(pageNumber);
254
        } catch (Exception e) {
255
            pN=Integer.valueOf(DEFAULT_PAGE_NUMBER);
256
            logger.info("pagenumber is not a number");
257
        }
258
        try {
259
            pS=Integer.valueOf(pageSize);
260
        } catch (Exception e) {
261
            pS=Integer.valueOf(DEFAULT_PAGE_SIZE);
262
            logger.info("pagesize is not a number");
263
        }
264

    
265
        PagerParameters pagerParams = new PagerParameters(pS, pN);
266
        pagerParams.normalizeAndValidate(response);
267

    
268
        //FIXME : Does not seem to be used ?
269
//        List<Feature> features = new ArrayList<Feature>();
270
//        features.add(Feature.OBSERVATION());
271
//        features.add(Feature.OCCURRENCE());
272
//        features.add(Feature.INDIVIDUALS_ASSOCIATION());
273
//        features.add(Feature.MATERIALS_EXAMINED());
274
//        features.add(Feature.SPECIMEN());
275

    
276
        List<DerivedUnit> records = new ArrayList<DerivedUnit>();
277

    
278
        OccurrenceSearch ns = new OccurrenceSearch();
279
        ns.setRequest(taxonUuid);
280

    
281
        Pager<DerivedUnit> specimenOrObs = null;
282
        int total=0;
283
        OccurrenceSearch os = new OccurrenceSearch();
284
        // search through each query
285
        if(taxonUuid.equals("") || !isValid(taxonUuid)) {
286
            ErrorResponse er = new ErrorResponse();
287
            er.setErrorMessage("Empty taxon uuid field");
288
            nsList.add(er);
289
        } else {       
290

    
291
            Taxon taxon = (Taxon) taxonService.find(UUID.fromString(taxonUuid));
292
            pagerParams.normalizeAndValidate(response);
293
            List<OrderHint> orderHints = null;
294
            
295
            
296
            // TODO load the full strategy once the method gets debuged
297
//            Set<TaxonRelationshipEdge> includeRelationships = ControllerUtils.loadIncludeRelationships(null, null, termService);
298
//            specimenOrObs= service.pageByAssociatedTaxon(null, includeRelationships, associatedTaxon,
299
//                    null, pagerParams.getPageSize(), pagerParams.getPageIndex(),
300
//                    orderHints, getInitializationStrategy());
301
            
302
          // Only derived units are requested in the pager method since we expect
303
          // occurrences to be of type DerivedUnit
304
          // The only possibility for an occurrence to be of type FieldUnit is the 
305
          // describedSpecimenOrObservation in the DescriptionBase class, which ideally
306
          // should not be used for storing occurrences
307
          specimenOrObs= service.pageByAssociatedTaxon(DerivedUnit.class, 
308
        		  null, 
309
        		  taxon,
310
        		  null, 
311
        		  pagerParams.getPageSize(), 
312
        		  pagerParams.getPageIndex(),
313
        		  orderHints, 
314
        		  OCCURRENCE_INIT_STRATEGY);            
315
            
316
          //FIXME We already have the list of occurrences in specimenOrObs pager
317
//            total = (service.listByAssociatedTaxon(null, includeRelationships, associatedTaxon,
318
//                    null, null, null, orderHints, null)).size();
319

    
320
            records = specimenOrObs.getRecords();
321

    
322
            DerivedUnit derivedUnit=null;
323
            List<DerivedUnitFacade> facades = new ArrayList<DerivedUnitFacade>();
324
            for (SpecimenOrObservationBase<?> specimen:records){
325
                
326
                               
327
                //FIXME SpecimenOrObservationBase is not an instanceof GatheringEvent ?
328
//                if (specimen instanceof GatheringEvent){
329
//                    GatheringEvent gath = CdmBase.deproxy(specimen, GatheringEvent.class);
330
//                    nsList.add(ns.createOccurrence( query, specimen, gath,  associatedTaxon.getTitleCache() ));
331
//                }
332
                
333
                //if (specimen.isInstanceOf(DerivedUnit.class)){
334
                    derivedUnit = CdmBase.deproxy(specimen, DerivedUnit.class);
335
                    DerivedUnitFacade derivedUnitFacade =null;
336
                    try {
337
                    	//TODO : This is a potential performance hurdle (especially for large number
338
                    	//       of derived units). 
339
                    	//       A possible solution is to already initialise the required derived unit facade 
340
                    	//       in the 'pageByAssociatedTaxon' call. This can be done either via the initialisation
341
                    	//       strategy technique or by writing a new optimised hql query for this.
342
                    	//       This will ensure that the DerivedUnits are loaded with required fields already
343
                    	//       initialised and will not require the loading and initialisation of each DerivedUnit
344
                    	//       object as is done in the 'getDerivedUnitFacade' method.
345
                        derivedUnitFacade = occurrenceService.getDerivedUnitFacade(derivedUnit, FACADE_INIT_STRATEGY);                                            
346
                        os.addToResponse(taxon.getTitleCache(), taxonUuid, derivedUnitFacade);
347
                    } catch (DerivedUnitFacadeNotSupportedException e) {
348
                       derivedUnitFacade=null;
349
                    }
350
//                    if (derivedUnitFacade !=null) {
351
//                        facades.add(derivedUnitFacade);
352
//                    }
353

    
354
                    //FIXME:Do we need this, isnt this already covered in pageByAssociatedTaxon
355
//                    Set<DescriptionBase> descriptions = specimen.getDescriptions();
356
//                    for (DescriptionBase<?> dbase:descriptions) {
357
//                        facades.addAll(occurrenceService.listDerivedUnitFacades(dbase, FACADE_INIT_STRATEGY));
358
//                    }
359
                }
360
            //}
361
            
362
            // FIXME : Isn't it easier to just add each occurrence to the list when we 
363
            //         loop over derived unit facade objects (above)
364
//                for (DerivedUnitFacade facade:facades) {
365
//                    nsList.add(ns.createOccurrence(taxonUuid, 
366
//                    		derivedUnit, 
367
//                    		facade, 
368
//                    		specimen,
369
//                    		associatedTaxon.getTitleCache()));
370
//                }
371
            
372
        }
373

    
374

    
375
        if (os.getResponse().isEmpty()){
376
            mv.addObject(nsList);
377
        } else {
378
            DefaultPagerImpl<OccurrenceSearchResponse> dpi = 
379
            		new DefaultPagerImpl<OccurrenceSearchResponse>(specimenOrObs.getCurrentIndex(), 
380
            					specimenOrObs.getCount(), 
381
            					specimenOrObs.getPageSize(), 
382
            					os.getResponse());
383
            mv.addObject(dpi);
384
        }
385

    
386
        return mv;
387
    }
388

    
389

    
390
    private boolean isValid(String uuid){
391
        if( uuid == null) {
392
            return false;
393
        }
394
        try {
395
            // we have to convert to object and back to string because the built in fromString does not have
396
            // good validation logic.
397

    
398
            UUID fromStringUUID = UUID.fromString(uuid);
399
            String toStringUUID = fromStringUUID.toString();
400

    
401
            System.out.println("input uuid : " + uuid + " , parsed uuid : " + toStringUUID);
402
            return toStringUUID.equals(uuid);
403
        } catch(IllegalArgumentException e) {
404
            return false;
405
        }
406
    }
407

    
408

    
409
    /* (non-Javadoc)
410
     * @see eu.etaxonomy.cdm.remote.controller.BaseListController#setService(eu.etaxonomy.cdm.api.service.IService)
411
     */
412
    @Override
413
    @Autowired
414
    public void setService(IOccurrenceService service) {
415
        this.service = service;
416
    }
417

    
418
	@Override
419
	public void setResourceLoader(ResourceLoader resourceLoader) {
420
		 this.resourceLoader = resourceLoader;
421
		
422
	}
423
}
(2-2/3)