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