View Javadoc
1   /*
2    * To change this license header, choose License Headers in Project Properties.
3    * To change this template file, choose Tools | Templates
4    * and open the template in the editor.
5    */
6   
7   package fr.ifremer.reefdb.service.administration.context;
8   
9   /*
10   * #%L
11   * Reef DB :: Core
12   * $Id:$
13   * $HeadURL:$
14   * %%
15   * Copyright (C) 2014 - 2015 Ifremer
16   * %%
17   * This program is free software: you can redistribute it and/or modify
18   * it under the terms of the GNU Affero General Public License as published by
19   * the Free Software Foundation, either version 3 of the License, or
20   * (at your option) any later version.
21   *
22   * This program is distributed in the hope that it will be useful,
23   * but WITHOUT ANY WARRANTY; without even the implied warranty of
24   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
25   * GNU General Public License for more details.
26   *
27   * You should have received a copy of the GNU Affero General Public License
28   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
29   * #L%
30   */
31  
32  import com.google.common.collect.Lists;
33  import com.google.common.collect.Maps;
34  import com.google.common.collect.Sets;
35  import com.google.gson.JsonParseException;
36  import fr.ifremer.quadrige3.core.dao.technical.Assert;
37  import fr.ifremer.quadrige3.core.dao.technical.gson.Gsons;
38  import fr.ifremer.quadrige3.core.exception.Exceptions;
39  import fr.ifremer.quadrige3.core.exception.QuadrigeTechnicalException;
40  import fr.ifremer.quadrige3.ui.core.dto.QuadrigeBean;
41  import fr.ifremer.quadrige3.ui.core.dto.referential.BaseReferentialDTO;
42  import fr.ifremer.reefdb.dao.administration.program.ReefDbProgramDao;
43  import fr.ifremer.reefdb.dao.administration.user.ReefDbDepartmentDao;
44  import fr.ifremer.reefdb.dao.administration.user.ReefDbQuserDao;
45  import fr.ifremer.reefdb.dao.data.survey.ReefDbCampaignDao;
46  import fr.ifremer.reefdb.dao.referential.ReefDbAnalysisInstrumentDao;
47  import fr.ifremer.reefdb.dao.referential.ReefDbSamplingEquipmentDao;
48  import fr.ifremer.reefdb.dao.referential.monitoringLocation.ReefDbMonitoringLocationDao;
49  import fr.ifremer.reefdb.dao.referential.pmfm.ReefDbPmfmDao;
50  import fr.ifremer.reefdb.dao.referential.taxon.ReefDbTaxonGroupDao;
51  import fr.ifremer.reefdb.dao.referential.taxon.ReefDbTaxonNameDao;
52  import fr.ifremer.reefdb.dao.system.context.ReefDbContextDao;
53  import fr.ifremer.reefdb.dao.system.filter.ReefDbFilterDao;
54  import fr.ifremer.reefdb.dto.ReefDbBeanFactory;
55  import fr.ifremer.reefdb.dto.ReefDbBeans;
56  import fr.ifremer.reefdb.dto.configuration.context.ContextDTO;
57  import fr.ifremer.reefdb.dto.configuration.filter.FilterDTO;
58  import fr.ifremer.reefdb.dto.configuration.programStrategy.ProgramDTO;
59  import fr.ifremer.reefdb.dto.data.survey.CampaignDTO;
60  import fr.ifremer.reefdb.dto.enums.FilterTypeValues;
61  import fr.ifremer.reefdb.dto.referential.*;
62  import fr.ifremer.reefdb.dto.referential.pmfm.PmfmDTO;
63  import fr.ifremer.reefdb.service.ReefDbBusinessException;
64  import fr.ifremer.reefdb.service.ReefDbDataContext;
65  import fr.ifremer.reefdb.service.ReefDbTechnicalException;
66  import fr.ifremer.reefdb.vo.ContextProxy;
67  import fr.ifremer.reefdb.vo.ContextVO;
68  import fr.ifremer.reefdb.vo.FilterProxy;
69  import fr.ifremer.reefdb.vo.FilterVO;
70  import org.apache.commons.collections4.CollectionUtils;
71  import org.apache.commons.logging.Log;
72  import org.apache.commons.logging.LogFactory;
73  import org.springframework.dao.DataRetrievalFailureException;
74  import org.springframework.stereotype.Service;
75  
76  import javax.annotation.Resource;
77  import java.io.*;
78  import java.util.*;
79  import java.util.stream.Collectors;
80  
81  import static org.nuiton.i18n.I18n.t;
82  
83  /**
84   * Service allowing a user to manage context
85   *
86   * @author Lionel Touseau <lionel.touseau@e-is.pro>
87   */
88  @Service("reefdbContextService")
89  public class ContextServiceImpl implements ContextService {
90  
91      private static final Log LOG = LogFactory.getLog(ContextServiceImpl.class);
92  
93      @Resource(name = "reefDbFilterDao")
94      protected ReefDbFilterDao filterDao;
95  
96      @Resource(name = "reefDbContextDao")
97      protected ReefDbContextDao contextDao;
98  
99      @Resource(name = "reefdbDataContext")
100     protected ReefDbDataContext dataContext;
101 
102     @Resource(name = "reefDbAnalysisInstrumentDao")
103     protected ReefDbAnalysisInstrumentDao analysisInstrumentDao;
104 
105     @Resource(name = "reefDbSamplingEquipmentDao")
106     protected ReefDbSamplingEquipmentDao samplingEquipmentDao;
107 
108     @Resource(name = "reefDbTaxonNameDao")
109     protected ReefDbTaxonNameDao taxonNameDao;
110 
111     @Resource(name = "reefDbTaxonGroupDao")
112     protected ReefDbTaxonGroupDao taxonGroupDao;
113 
114     @Resource(name = "reefDbMonitoringLocationDao")
115     protected ReefDbMonitoringLocationDao locationDao;
116 
117     @Resource(name = "reefDbProgramDao")
118     protected ReefDbProgramDao programDao;
119 
120     @Resource(name = "reefDbCampaignDao")
121     protected ReefDbCampaignDao campaignDao;
122 
123     @Resource(name = "reefDbPmfmDao")
124     protected ReefDbPmfmDao pmfmDao;
125 
126     @Resource(name = "reefDbDepartmentDao")
127     protected ReefDbDepartmentDao departmentDao;
128 
129     @Resource(name = "reefDbQuserDao")
130     protected ReefDbQuserDao quserDao;
131 
132     /**
133      * {@inheritDoc}
134      */
135     @Override
136     public ContextDTO getContext(Integer contextId) {
137         Assert.notNull(contextId);
138 
139         try {
140             ContextDTO context = contextDao.getContextById(contextId);
141             context.setFilters(filterDao.getAllContextFilters(contextId, null));
142             return context;
143 
144         } catch (DataRetrievalFailureException e) {
145             // no context found, return silently
146             return null;
147         }
148 
149     }
150 
151     /**
152      * {@inheritDoc}
153      */
154     @Override
155     public List<ContextDTO> getAllContexts() {
156         List<ContextDTO> contexts = contextDao.getAllContext();
157         if (CollectionUtils.isNotEmpty(contexts)) {
158             for (ContextDTO context : contexts) {
159                 context.setFilters(filterDao.getAllContextFilters(context.getId(), null));
160             }
161         }
162         return contexts;
163     }
164 
165     /**
166      * {@inheritDoc}
167      */
168     @Override
169     public void saveContexts(List<? extends ContextDTO> contexts) {
170 
171         for (ContextDTO context : contexts) {
172             if (context.isDirty()) {
173                 contextDao.saveContext(context);
174                 context.setDirty(false);
175             }
176         }
177     }
178 
179     /**
180      * {@inheritDoc}
181      */
182     @Override
183     public void deleteContexts(List<? extends ContextDTO> contexts) {
184 
185         contextDao.deleteContexts(ReefDbBeans.collectIds(contexts));
186     }
187 
188     /**
189      * {@inheritDoc}
190      */
191     @Override
192     public ContextDTO duplicateContext(ContextDTO context) {
193         ContextDTO duplicatedContext = ReefDbBeans.clone(context);
194 
195         duplicatedContext.setId(null);
196         duplicatedContext.setName("");
197 
198         // filters must be copied in a different list to avoid sharing same list
199         duplicatedContext.setFilters(ReefDbBeans.clone(context.getFilters()));
200 
201         return duplicatedContext;
202     }
203 
204     /**
205      * {@inheritDoc}
206      */
207     @Override
208     public void exportContexts(Collection<ContextDTO> contexts, File targetContextFile) {
209 
210         // ensure filter elements are loaded
211         contexts.forEach(context -> context.getFilters().forEach(this::loadFilteredElements));
212 
213         // use json serialization
214         ContextProxy contextsToExport = new ContextProxy(contexts.stream().map(this::toContextVO).collect(Collectors.toList()));
215         Gsons.serializeToFile(contextsToExport, targetContextFile);
216 
217     }
218 
219     /**
220      * {@inheritDoc}
221      */
222     @Override
223     public List<ContextDTO> importContexts(File sourceContextFile) {
224 
225         try {
226 
227             // try first to read new json format (Mantis #43800)
228             ContextProxy contextProxy = Gsons.deserializeFile(sourceContextFile, ContextProxy.class);
229 
230             if (!contextProxy.getVersion().equals(ContextProxy.CURRENT_VERSION)) {
231                 LOG.warn(String.format("Context file %s to import is on version %s and the current version is %s",
232                         sourceContextFile, contextProxy.getVersion(), ContextProxy.CURRENT_VERSION));
233             }
234 
235             List<ContextDTO> contexts = new ArrayList<>();
236             if (CollectionUtils.isEmpty(contextProxy.getContexts())) {
237                 if (LOG.isWarnEnabled()) LOG.warn("no context found in file " + sourceContextFile);
238                 return contexts;
239             }
240 
241             for (ContextVO contextVO : contextProxy.getContexts()) {
242 
243                 ContextDTO context = toContextDTO(contextVO);
244                 context.setDirty(true);
245                 computeNextContextName(context);
246 
247                 for (FilterDTO filter : context.getFilters()) {
248 
249                     // assign null IDs to filters to force creation
250                     filter.setId(null);
251                     filter.setDirty(true);
252                     // Compute new filter name (see Mantis #30776)
253                     computeNextFilterName(filter);
254                     saveFilter(filter);
255                     filter.setDirty(false);
256                 }
257 
258                 contexts.add(context);
259             }
260 
261             saveContexts(contexts);
262             return contexts;
263 
264         } catch (QuadrigeTechnicalException e) {
265 
266             if (Exceptions.hasCause(e, JsonParseException.class)) {
267                 // if json deserialization failed, before throw, try the old format
268                 List<ContextDTO> oldContexts = importOldContexts(sourceContextFile);
269                 if (oldContexts != null) return oldContexts;
270             }
271 
272             // else throw exception
273             throw e;
274         }
275     }
276 
277     @SuppressWarnings("unchecked")
278     private List<ContextDTO> importOldContexts(File sourceContextFile) {
279 
280         try (FileInputStream fis = new FileInputStream(sourceContextFile);
281              ObjectInputStream ois = new ObjectInputStream(fis)) {
282 
283             List<ContextDTO> contexts = (List<ContextDTO>) ois.readObject();
284 
285             Map<Integer, Integer> savedFilterIds = Maps.newHashMap();
286             Map<Integer, String> savedFilterNames = Maps.newHashMap();
287 
288             for (ContextDTO c : contexts) {
289                 c.setId(null);
290                 c.setDirty(true);
291                 computeNextContextName(c);
292 
293                 for (FilterDTO f : c.getFilters()) {
294                     Integer importId = f.getId();
295                     if (!savedFilterIds.containsKey(importId)) {
296 
297                         // check if filters contains local elements. If it does, abort import
298                         for (QuadrigeBean bean : f.getElements()) {
299                             if (bean.getClass().isAssignableFrom(BaseReferentialDTO.class)
300                                     && ((BaseReferentialDTO) bean).getStatus() != null
301                                     && ReefDbBeans.isLocalStatus(((BaseReferentialDTO) bean).getStatus())) {
302                                 throw new ReefDbBusinessException(t("reefdb.error.filter.import.withLocalReferential", f.getName()));
303                             }
304                         }
305 
306                         // assign null IDs to filters to force creation
307                         f.setId(null);
308                         // Compute new filter name (see Mantis #30776)
309                         computeNextFilterName(f);
310                         saveFilter(f);
311                         savedFilterIds.put(importId, f.getId());
312                         savedFilterNames.put(importId, f.getName());
313                     } else {
314                         // affect already saved filter id and name
315                         f.setId(savedFilterIds.get(importId));
316                         f.setName(savedFilterNames.get(importId));
317                     }
318                 }
319             }
320 
321             saveContexts(contexts);
322             return contexts;
323 
324         } catch (IOException | ClassNotFoundException | ClassCastException ex) {
325             // Mantis #30776
326             throw new ReefDbTechnicalException(t("reefdb.error.context.import.error"), ex);
327         }
328 
329     }
330 
331     /**
332      * {@inheritDoc}
333      */
334     @Override
335     public List<FilterDTO> getFiltersByType(int filterTypeId) {
336         return filterDao.getAllContextFilters(null, filterTypeId);
337     }
338 
339     /**
340      * {@inheritDoc}
341      */
342     @Override
343     public boolean checkFiltersNotUsedInContext(List<? extends FilterDTO> filters) {
344 
345         List<Integer> filterIds = ReefDbBeans.collectIds(filters);
346         return filterIds.isEmpty() || filterDao.checkFiltersNotUsedInContext(filterIds);
347     }
348 
349     /**
350      * {@inheritDoc}
351      */
352     @Override
353     public void deleteFilters(List<? extends FilterDTO> filters) {
354         if (filters == null) return;
355         filterDao.deleteFilters(ReefDbBeans.collectIds(filters));
356     }
357 
358     /**
359      * {@inheritDoc}
360      */
361     @Override
362     public FilterDTO getFilter(Integer filterId) {
363         return filterDao.getFilterById(filterId);
364     }
365 
366     /**
367      * {@inheritDoc}
368      */
369     @Override
370     public void loadFilteredElements(FilterDTO filter) {
371 
372         if (filter == null) {
373             return;
374         }
375 
376         if (!filter.isFilterLoaded()) {
377 
378             filter.setElements(getFilteredElements(filter.getFilterTypeId(), filter.getId()));
379             filter.setFilterLoaded(true);
380         }
381     }
382 
383     private List<? extends QuadrigeBean> getFilteredElements(int filterTypeId, Integer filterId) {
384 
385         FilterTypeValues contextFilter = FilterTypeValues.getFilterType(filterTypeId);
386         if (contextFilter != null) {
387             switch (contextFilter) {
388                 case ANALYSIS_INSTRUMENT:
389                     return getFilteredAnalysisInstrumentsByFilterId(filterId);
390                 case SAMPLING_EQUIPMENT:
391                     return getFilteredSamplingEquipmentsByFilterId(filterId);
392                 case TAXON_GROUP:
393                     return getFilteredTaxonGroupsByFilterId(filterId);
394                 case LOCATION:
395                     return getFilteredLocationsByFilterId(filterId);
396                 case PROGRAM:
397                     return getFilteredProgramsByFilterId(filterId);
398                 case CAMPAIGN:
399                     return getFilteredCampaignsByFilterId(filterId);
400                 case PMFM:
401                     return getFilteredPmfmsByFilterId(filterId);
402                 case DEPARTMENT:
403                     return getFilteredDepartmentsByFilterId(filterId);
404                 case TAXON:
405                     return getFilteredTaxonsByFilterId(filterId);
406                 case USER:
407                     return getFilteredUsersByFilterId(filterId);
408             }
409         }
410         return null;
411     }
412 
413     /**
414      * {@inheritDoc}
415      */
416     @Override
417     public List<FilterDTO> getAllDepartmentFilters() {
418         return filterDao.getAllContextFilters(null, FilterTypeValues.DEPARTMENT.getFilterTypeId());
419     }
420 
421     /**
422      * {@inheritDoc}
423      */
424     @Override
425     public List<DepartmentDTO> getFilteredDepartments(Integer contextId) {
426         Set<DepartmentDTO> result = Sets.newHashSet();
427         List<FilterDTO> filters = filterDao.getAllContextFilters(contextId, FilterTypeValues.DEPARTMENT.getFilterTypeId());
428         if (CollectionUtils.isNotEmpty(filters)) {
429             for (FilterDTO filter : filters) {
430                 result.addAll(getFilteredDepartmentsByFilterId(filter.getId()));
431             }
432         }
433         return Lists.newArrayList(result);
434     }
435 
436     private List<DepartmentDTO> getFilteredDepartmentsByFilterId(Integer filterId) {
437         List<DepartmentDTO> deps = Lists.newArrayList();
438         List<Integer> depIds = ReefDbBeans.transformCollection(filterDao.getFilteredElementsByFilterId(filterId), ReefDbBeans.ID_MAPPER);
439         if (CollectionUtils.isNotEmpty(depIds)) {
440             deps.addAll(departmentDao.getDepartmentsByIds(depIds));
441         }
442         return deps;
443     }
444 
445     /**
446      * {@inheritDoc}
447      */
448     @Override
449     public List<FilterDTO> getAllSamplingEquipmentFilters() {
450         return filterDao.getAllContextFilters(null, FilterTypeValues.SAMPLING_EQUIPMENT.getFilterTypeId());
451     }
452 
453     /**
454      * {@inheritDoc}
455      */
456     @Override
457     public List<SamplingEquipmentDTO> getFilteredSamplingEquipments(Integer contextId) {
458         Set<SamplingEquipmentDTO> result = Sets.newHashSet();
459         List<FilterDTO> filters = filterDao.getAllContextFilters(contextId, FilterTypeValues.SAMPLING_EQUIPMENT.getFilterTypeId());
460         if (CollectionUtils.isNotEmpty(filters)) {
461             for (FilterDTO filter : filters) {
462                 result.addAll(getFilteredSamplingEquipmentsByFilterId(filter.getId()));
463             }
464         }
465         return Lists.newArrayList(result);
466     }
467 
468     private List<SamplingEquipmentDTO> getFilteredSamplingEquipmentsByFilterId(Integer filterId) {
469         List<SamplingEquipmentDTO> se = Lists.newArrayList();
470         List<Integer> seIds = ReefDbBeans.transformCollection(filterDao.getFilteredElementsByFilterId(filterId), ReefDbBeans.ID_MAPPER);
471         if (CollectionUtils.isNotEmpty(seIds)) {
472             se.addAll(samplingEquipmentDao.getSamplingEquipmentsByIds(seIds));
473         }
474         return se;
475     }
476 
477     /**
478      * {@inheritDoc}
479      */
480     @Override
481     public List<FilterDTO> getAllAnalysisInstrumentFilters() {
482         return filterDao.getAllContextFilters(null, FilterTypeValues.ANALYSIS_INSTRUMENT.getFilterTypeId());
483     }
484 
485     /**
486      * {@inheritDoc}
487      */
488     @Override
489     public List<AnalysisInstrumentDTO> getFilteredAnalysisInstruments(Integer contextId) {
490         Set<AnalysisInstrumentDTO> result = Sets.newHashSet();
491         List<FilterDTO> filters = filterDao.getAllContextFilters(contextId, FilterTypeValues.ANALYSIS_INSTRUMENT.getFilterTypeId());
492         if (CollectionUtils.isNotEmpty(filters)) {
493             for (FilterDTO filter : filters) {
494                 result.addAll(getFilteredAnalysisInstrumentsByFilterId(filter.getId()));
495             }
496         }
497         return Lists.newArrayList(result);
498     }
499 
500     private List<AnalysisInstrumentDTO> getFilteredAnalysisInstrumentsByFilterId(Integer filterId) {
501         List<AnalysisInstrumentDTO> analysisInstruments = Lists.newArrayList();
502         List<Integer> aiIds = ReefDbBeans.transformCollection(filterDao.getFilteredElementsByFilterId(filterId), ReefDbBeans.ID_MAPPER);
503         if (CollectionUtils.isNotEmpty(aiIds)) {
504             analysisInstruments.addAll(analysisInstrumentDao.getAnalysisInstrumentsByIds(aiIds));
505         }
506         return analysisInstruments;
507     }
508 
509     /**
510      * {@inheritDoc}
511      */
512     @Override
513     public void saveFilter(FilterDTO filter) {
514         Assert.notNull(filter.getFilterTypeId());
515         filterDao.saveFilter(filter, dataContext.getRecorderPersonId());
516     }
517 
518     /**
519      * {@inheritDoc}
520      */
521     @Override
522     public void saveFilters(List<? extends FilterDTO> filters) {
523         if (CollectionUtils.isNotEmpty(filters)) {
524             for (FilterDTO filter : filters) {
525                 if (filter.isDirty()) {
526                     saveFilter(filter);
527                     filter.setDirty(false);
528                 }
529             }
530         }
531     }
532 
533     /**
534      * {@inheritDoc}
535      */
536     @Override
537     public List<FilterDTO> getAllPmfmFilters() {
538         return filterDao.getAllContextFilters(null, FilterTypeValues.PMFM.getFilterTypeId());
539     }
540 
541     /**
542      * {@inheritDoc}
543      */
544     @Override
545     public List<PmfmDTO> getFilteredPmfms(Integer contextId) {
546         Set<PmfmDTO> result = Sets.newHashSet();
547         List<FilterDTO> filters = filterDao.getAllContextFilters(contextId, FilterTypeValues.PMFM.getFilterTypeId());
548         if (CollectionUtils.isNotEmpty(filters)) {
549             for (FilterDTO filter : filters) {
550                 result.addAll(getFilteredPmfmsByFilterId(filter.getId()));
551             }
552         }
553         return Lists.newArrayList(result);
554     }
555 
556     private List<PmfmDTO> getFilteredPmfmsByFilterId(Integer filterId) {
557         List<PmfmDTO> pmfms = Lists.newArrayList();
558         List<Integer> pmfmIds = ReefDbBeans.transformCollection(filterDao.getFilteredElementsByFilterId(filterId), ReefDbBeans.ID_MAPPER);
559         if (!pmfmIds.isEmpty()) {
560             pmfms.addAll(pmfmDao.getPmfmsByIds(pmfmIds));
561         }
562         return pmfms;
563     }
564 
565     /**
566      * {@inheritDoc}
567      */
568     @Override
569     public List<FilterDTO> getAllTaxonFilters() {
570         return filterDao.getAllContextFilters(null, FilterTypeValues.TAXON.getFilterTypeId());
571     }
572 
573     /**
574      * {@inheritDoc}
575      */
576     @Override
577     public List<TaxonDTO> getFilteredTaxons(Integer contextId) {
578         Set<TaxonDTO> result = Sets.newHashSet();
579         List<FilterDTO> filters = filterDao.getAllContextFilters(contextId, FilterTypeValues.TAXON.getFilterTypeId());
580         if (CollectionUtils.isNotEmpty(filters)) {
581             for (FilterDTO filter : filters) {
582                 result.addAll(getFilteredTaxonsByFilterId(filter.getId()));
583             }
584         }
585         return Lists.newArrayList(result);
586     }
587 
588     private List<TaxonDTO> getFilteredTaxonsByFilterId(Integer filterId) {
589         List<TaxonDTO> taxons = Lists.newArrayList();
590         List<Integer> taxonIds = ReefDbBeans.transformCollection(filterDao.getFilteredElementsByFilterId(filterId), ReefDbBeans.ID_MAPPER);
591         if (CollectionUtils.isNotEmpty(taxonIds)) {
592             taxons.addAll(taxonNameDao.getTaxonNamesByIds(taxonIds));
593         }
594         return taxons;
595     }
596 
597     /**
598      * {@inheritDoc}
599      */
600     @Override
601     public List<FilterDTO> getAllTaxonGroupFilters() {
602         return filterDao.getAllContextFilters(null, FilterTypeValues.TAXON_GROUP.getFilterTypeId());
603     }
604 
605     /**
606      * {@inheritDoc}
607      */
608     @Override
609     public List<TaxonGroupDTO> getFilteredTaxonGroups(Integer contextId) {
610         Set<TaxonGroupDTO> result = Sets.newHashSet();
611         List<FilterDTO> filters = filterDao.getAllContextFilters(contextId, FilterTypeValues.TAXON_GROUP.getFilterTypeId());
612         if (CollectionUtils.isNotEmpty(filters)) {
613             for (FilterDTO filter : filters) {
614                 result.addAll(getFilteredTaxonGroupsByFilterId(filter.getId()));
615             }
616         }
617         return Lists.newArrayList(result);
618     }
619 
620     private List<TaxonGroupDTO> getFilteredTaxonGroupsByFilterId(Integer filterId) {
621         List<TaxonGroupDTO> taxonGroups = Lists.newArrayList();
622         List<Integer> tgIds = ReefDbBeans.transformCollection(filterDao.getFilteredElementsByFilterId(filterId), ReefDbBeans.ID_MAPPER);
623         if (CollectionUtils.isNotEmpty(tgIds)) {
624             taxonGroups.addAll(taxonGroupDao.getTaxonGroupsByIds(tgIds));
625         }
626         return taxonGroups;
627     }
628 
629     /**
630      * {@inheritDoc}
631      */
632     @Override
633     public List<FilterDTO> getAllLocationFilter() {
634         return filterDao.getAllContextFilters(null, FilterTypeValues.LOCATION.getFilterTypeId());
635     }
636 
637     /**
638      * {@inheritDoc}
639      */
640     @Override
641     public List<LocationDTO> getFilteredLocations(Integer contextId) {
642         Set<LocationDTO> result = Sets.newHashSet();
643         List<FilterDTO> filters = filterDao.getAllContextFilters(contextId, FilterTypeValues.LOCATION.getFilterTypeId());
644         if (CollectionUtils.isNotEmpty(filters)) {
645             for (FilterDTO filter : filters) {
646                 result.addAll(getFilteredLocationsByFilterId(filter.getId()));
647             }
648         }
649         return Lists.newArrayList(result);
650     }
651 
652     private List<LocationDTO> getFilteredLocationsByFilterId(Integer filterId) {
653         List<LocationDTO> locations = Lists.newArrayList();
654         List<Integer> locationIds = ReefDbBeans.transformCollection(filterDao.getFilteredElementsByFilterId(filterId), ReefDbBeans.ID_MAPPER);
655         if (CollectionUtils.isNotEmpty(locationIds)) {
656             locations.addAll(locationDao.getLocationsByIds(locationIds));
657         }
658         return locations;
659     }
660 
661     /**
662      * {@inheritDoc}
663      */
664     @Override
665     public List<FilterDTO> getAllUserFilter() {
666         return filterDao.getAllContextFilters(null, FilterTypeValues.USER.getFilterTypeId());
667     }
668 
669     /**
670      * {@inheritDoc}
671      */
672     @Override
673     public List<PersonDTO> getFilteredUsers(Integer contextId) {
674         Set<PersonDTO> result = Sets.newHashSet();
675         List<FilterDTO> filters = filterDao.getAllContextFilters(contextId, FilterTypeValues.USER.getFilterTypeId());
676         if (CollectionUtils.isNotEmpty(filters)) {
677             for (FilterDTO filter : filters) {
678                 result.addAll(getFilteredUsersByFilterId(filter.getId()));
679             }
680         }
681         return Lists.newArrayList(result);
682     }
683 
684     private List<PersonDTO> getFilteredUsersByFilterId(Integer filterId) {
685         List<PersonDTO> result = Lists.newArrayList();
686         List<Integer> userIds = ReefDbBeans.transformCollection(filterDao.getFilteredElementsByFilterId(filterId), ReefDbBeans.ID_MAPPER);
687         if (CollectionUtils.isNotEmpty(userIds)) {
688             result.addAll(quserDao.getUsersByIds(userIds));
689         }
690         return result;
691     }
692 
693     /**
694      * {@inheritDoc}
695      */
696     @Override
697     public List<FilterDTO> getAllProgramFilter() {
698         return filterDao.getAllContextFilters(null, FilterTypeValues.PROGRAM.getFilterTypeId());
699     }
700 
701     /**
702      * {@inheritDoc}
703      */
704     @Override
705     public List<ProgramDTO> getFilteredPrograms(Integer contextId) {
706         Set<ProgramDTO> result = Sets.newHashSet();
707         List<FilterDTO> filters = filterDao.getAllContextFilters(contextId, FilterTypeValues.PROGRAM.getFilterTypeId());
708         if (CollectionUtils.isNotEmpty(filters)) {
709             for (FilterDTO filter : filters) {
710                 result.addAll(getFilteredProgramsByFilterId(filter.getId()));
711             }
712         }
713         return Lists.newArrayList(result);
714     }
715 
716     @Override
717     public List<FilterDTO> getAllCampaignFilter() {
718         return filterDao.getAllContextFilters(null, FilterTypeValues.CAMPAIGN.getFilterTypeId());
719     }
720 
721     @Override
722     public List<CampaignDTO> getFilteredCampaigns(Integer contextId) {
723         Set<CampaignDTO> result = Sets.newHashSet();
724         List<FilterDTO> filters = filterDao.getAllContextFilters(contextId, FilterTypeValues.CAMPAIGN.getFilterTypeId());
725         if (CollectionUtils.isNotEmpty(filters)) {
726             for (FilterDTO filter : filters) {
727                 result.addAll(getFilteredCampaignsByFilterId(filter.getId()));
728             }
729         }
730         return Lists.newArrayList(result);
731     }
732 
733     private List<CampaignDTO> getFilteredCampaignsByFilterId(Integer filterId) {
734         List<CampaignDTO> campaigns = Lists.newArrayList();
735         List<Integer> campaignIds = ReefDbBeans.transformCollection(filterDao.getFilteredElementsByFilterId(filterId), ReefDbBeans.ID_MAPPER);
736         if (CollectionUtils.isNotEmpty(campaignIds)) {
737             campaigns.addAll(campaignDao.getCampaignsByIds(campaignIds));
738         }
739         return campaigns;
740     }
741 
742     private List<ProgramDTO> getFilteredProgramsByFilterId(Integer filterId) {
743         List<ProgramDTO> programs = Lists.newArrayList();
744         List<String> programCodes = filterDao.getFilteredElementsByFilterId(filterId);
745         if (CollectionUtils.isNotEmpty(programCodes)) {
746             programs.addAll(programDao.getProgramsByCodes(programCodes));
747         }
748         return programs;
749     }
750 
751     /**
752      * {@inheritDoc}
753      */
754     @Override
755     public List<FilterDTO> importFilter(File sourceFilterFile, int filterTypeId) {
756 
757         try {
758 
759             // try first to read new json format (Mantis #43800)
760             FilterProxy filterProxy = Gsons.deserializeFile(sourceFilterFile, FilterProxy.class);
761 
762             if (!filterProxy.getVersion().equals(FilterProxy.CURRENT_VERSION)) {
763                 LOG.warn(String.format("Filter file %s to import is on version %s and the current version is %s",
764                         sourceFilterFile, filterProxy.getVersion(), FilterProxy.CURRENT_VERSION));
765             }
766 
767             List<FilterDTO> filters = new ArrayList<>();
768             if (CollectionUtils.isEmpty(filterProxy.getFilters())) {
769                 if (LOG.isWarnEnabled()) LOG.warn("no filter found in file " + sourceFilterFile);
770                 return filters;
771             }
772 
773             for (FilterVO filterVO : filterProxy.getFilters()) {
774 
775                 // check filter type
776                 if (filterVO.getType() != filterTypeId) {
777                     throw new ReefDbBusinessException(t("reefdb.error.filter.import.wrongFilterType",
778                             FilterTypeValues.getFilterType(filterTypeId),
779                             FilterTypeValues.getFilterType(filterVO.getType())));
780                 }
781 
782                 FilterDTO filter = toFilterDTO(filterVO);
783                 filter.setId(null);
784                 filter.setDirty(true);
785                 computeNextFilterName(filter);
786                 filters.add(filter);
787             }
788 
789             saveFilters(filters);
790             return filters;
791 
792         } catch (QuadrigeTechnicalException e) {
793 
794             if (Exceptions.hasCause(e, JsonParseException.class)) {
795                 // if json deserialization failed, before throw, try the old format
796                 List<FilterDTO> oldFilters = importOldFilter(sourceFilterFile, filterTypeId);
797                 if (oldFilters != null) return oldFilters;
798             }
799 
800             // else throw exception
801             throw e;
802         }
803 
804     }
805 
806     @SuppressWarnings("unchecked")
807     private List<FilterDTO> importOldFilter(File sourceFilterFile, int filterTypeId) {
808 
809         try (FileInputStream fis = new FileInputStream(sourceFilterFile);
810              ObjectInputStream ois = new ObjectInputStream(fis)) {
811 
812             // apparently yes, works like a charm
813             List<FilterDTO> filters = (List<FilterDTO>) ois.readObject();
814 
815             // assign null IDs to filters to force creation
816             for (FilterDTO f : filters) {
817 
818                 // check filter type
819                 if (f.getFilterTypeId() != filterTypeId) {
820                     throw new ReefDbBusinessException(t("reefdb.error.filter.import.wrongFilterType",
821                             FilterTypeValues.getFilterType(filterTypeId),
822                             FilterTypeValues.getFilterType(f.getFilterTypeId())));
823                 }
824 
825                 // check if filters contains local elements. If it does, abort import
826                 for (QuadrigeBean bean : f.getElements()) {
827                     if (bean.getClass().isAssignableFrom(BaseReferentialDTO.class)
828                             && ((BaseReferentialDTO) bean).getStatus() != null
829                             && ReefDbBeans.isLocalStatus(((BaseReferentialDTO) bean).getStatus())) {
830                         throw new ReefDbBusinessException(t("reefdb.error.filter.import.withLocalReferential", f.getName()));
831                     }
832                 }
833 
834                 f.setId(null);
835                 computeNextFilterName(f);
836                 saveFilter(f);
837             }
838 
839             return filters;
840         } catch (IOException | ClassNotFoundException | ClassCastException ex) {
841             throw new ReefDbTechnicalException(t("reefdb.error.filter.import.error"), ex);
842         }
843 
844     }
845 
846     /**
847      * {@inheritDoc}
848      */
849     @Override
850     public void exportFilter(Collection<FilterDTO> filters, File targetFilterFile) {
851 
852         // ensure filter elements are loaded
853         filters.forEach(this::loadFilteredElements);
854 
855         // use json serialization
856         FilterProxy filtersToExport = new FilterProxy(filters.stream().map(this::toFilterVO).collect(Collectors.toList()));
857         Gsons.serializeToFile(filtersToExport, targetFilterFile);
858 
859     }
860 
861     /**
862      * {@inheritDoc}
863      */
864     @Override
865     public FilterDTO duplicateFilter(FilterDTO filter) {
866         FilterDTO duplicatedFilter = ReefDbBeans.clone(filter);
867         duplicatedFilter.setId(null);
868         duplicatedFilter.setName("");
869 
870         duplicatedFilter.setElements(ReefDbBeans.clone(filter.getElements()));
871 
872         return duplicatedFilter;
873     }
874 
875     // PRIVATE METHODS
876 
877     private FilterVO toFilterVO(FilterDTO filter) {
878         FilterVO vo = new FilterVO();
879         vo.setType(filter.getFilterTypeId());
880         vo.setName(filter.getName());
881         vo.setElementIds(ReefDbBeans.getIdsAsString(filter.getElements()));
882         return vo;
883     }
884 
885     private FilterDTO toFilterDTO(FilterVO filter) {
886         FilterDTO dto = ReefDbBeanFactory.newFilterDTO();
887         dto.setName(filter.getName());
888         dto.setFilterTypeId(filter.getType());
889 
890         FilterTypeValues filterType = FilterTypeValues.getFilterType(dto.getFilterTypeId());
891         Assert.notNull(filterType);
892         List<? extends QuadrigeBean> elements = null;
893         switch (filterType) {
894             case PROGRAM:
895                 elements = programDao.getProgramsByCodes(filter.getElementIds());
896                 break;
897             case LOCATION:
898                 elements = locationDao.getLocationsByIds(getValidIds(filter));
899                 break;
900             case CAMPAIGN:
901                 elements = campaignDao.getCampaignsByIds(getValidIds(filter));
902                 break;
903             case SAMPLING_EQUIPMENT:
904                 elements = samplingEquipmentDao.getSamplingEquipmentsByIds(getValidIds(filter));
905                 break;
906             case ANALYSIS_INSTRUMENT:
907                 elements = analysisInstrumentDao.getAnalysisInstrumentsByIds(getValidIds(filter));
908                 break;
909             case TAXON:
910                 elements = taxonNameDao.getTaxonNamesByIds(getValidIds(filter));
911                 break;
912             case TAXON_GROUP:
913                 elements = taxonGroupDao.getTaxonGroupsByIds(getValidIds(filter));
914                 break;
915             case DEPARTMENT:
916                 elements = departmentDao.getDepartmentsByIds(getValidIds(filter));
917                 break;
918             case PMFM:
919                 elements = pmfmDao.getPmfmsByIds(getValidIds(filter));
920                 break;
921             case USER:
922                 elements = quserDao.getUsersByIds(getValidIds(filter));
923                 break;
924         }
925 
926         if (CollectionUtils.size(filter.getElementIds()) != CollectionUtils.size(elements)) {
927             throw new ReefDbBusinessException(t("reefdb.error.import.referentialNotFound.message", filterType.getLabel()));
928         }
929 
930         dto.setElements(elements);
931         dto.setFilterLoaded(true);
932         return dto;
933     }
934 
935     private List<Integer> getValidIds(FilterVO filter) {
936         List<Integer> ids = ReefDbBeans.transformCollection(filter.getElementIds(), ReefDbBeans.ID_MAPPER);
937         if (ids.stream().anyMatch(id -> id < 0)) {
938             throw new ReefDbBusinessException(t("reefdb.error.filter.import.withLocalReferential", filter.getName()));
939         }
940         return ids;
941     }
942 
943     private ContextVO toContextVO(ContextDTO context) {
944         ContextVO vo = new ContextVO();
945         vo.setName(context.getName());
946         vo.setDescription(context.getDescription());
947         vo.setFilters(context.getFilters().stream().map(this::toFilterVO).collect(Collectors.toList()));
948         return vo;
949     }
950 
951     private ContextDTO toContextDTO(ContextVO context) {
952         ContextDTO dto = ReefDbBeanFactory.newContextDTO();
953         dto.setName(context.getName());
954         dto.setDescription(context.getDescription());
955         dto.setFilters(context.getFilters().stream().map(this::toFilterDTO).collect(Collectors.toList()));
956         return dto;
957     }
958 
959     private void computeNextContextName(ContextDTO context) {
960         Assert.notNull(context);
961         String contextName = context.getName();
962         Assert.notBlank(contextName);
963 
964         // Check existence of context with same name
965         List<ContextDTO> existingContexts = getAllContexts();
966         if (CollectionUtils.isNotEmpty(existingContexts)) {
967             int suffixInc = 0;
968             boolean nameCorrect = false;
969             while (!nameCorrect && suffixInc < 100) {
970                 boolean nameAlreadyExists = false;
971                 suffixInc++;
972                 for (ContextDTO existingContext : existingContexts) {
973                     if (contextName.equalsIgnoreCase(existingContext.getName())) {
974                         nameAlreadyExists = true;
975                         // try to add a suffix
976                         String suffix = String.format(" (%d)", suffixInc);
977                         contextName = context.getName().concat(suffix);
978                         break;
979                     }
980                 }
981                 if (!nameAlreadyExists) {
982                     // now, this context has a unique name
983                     context.setName(contextName);
984                     nameCorrect = true;
985                 }
986             }
987         }
988     }
989 
990     private void computeNextFilterName(FilterDTO filter) {
991         Assert.notNull(filter);
992         String filterName = filter.getName();
993         Assert.notBlank(filterName);
994 
995         // Check existence of filter with same name
996         List<FilterDTO> existingFilters = getFiltersByType(filter.getFilterTypeId());
997         if (CollectionUtils.isNotEmpty(existingFilters)) {
998             int suffixInc = 0;
999             boolean nameCorrect = false;
1000             while (!nameCorrect && suffixInc < 100) {
1001                 boolean nameAlreadyExists = false;
1002                 suffixInc++;
1003                 for (FilterDTO existingFilter : existingFilters) {
1004                     if (filterName.equalsIgnoreCase(existingFilter.getName())) {
1005                         nameAlreadyExists = true;
1006                         // try to add a suffix
1007                         String suffix = String.format(" (%d)", suffixInc);
1008                         filterName = filter.getName().concat(suffix);
1009                         break;
1010                     }
1011                 }
1012                 if (!nameAlreadyExists) {
1013                     // now, this filter has a unique name
1014                     filter.setName(filterName);
1015                     nameCorrect = true;
1016                 }
1017             }
1018         }
1019     }
1020 
1021 }