View Javadoc
1   package fr.ifremer.dali.dao.system.rule;
2   
3   /*
4    * #%L
5    * Dali :: Core
6    * $Id:$
7    * $HeadURL:$
8    * %%
9    * Copyright (C) 2014 - 2015 Ifremer
10   * %%
11   * This program is free software: you can redistribute it and/or modify
12   * it under the terms of the GNU Affero General Public License as published by
13   * the Free Software Foundation, either version 3 of the License, or
14   * (at your option) any later version.
15   * 
16   * This program is distributed in the hope that it will be useful,
17   * but WITHOUT ANY WARRANTY; without even the implied warranty of
18   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19   * GNU General Public License for more details.
20   * 
21   * You should have received a copy of the GNU Affero General Public License
22   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
23   * #L%
24   */
25  
26  import com.google.common.collect.ImmutableList;
27  import com.google.common.collect.Lists;
28  import fr.ifremer.dali.config.DaliConfiguration;
29  import fr.ifremer.dali.dao.referential.pmfm.DaliPmfmDao;
30  import fr.ifremer.dali.dao.technical.Daos;
31  import fr.ifremer.dali.dto.DaliBeanFactory;
32  import fr.ifremer.dali.dto.DaliBeans;
33  import fr.ifremer.dali.dto.FunctionDTO;
34  import fr.ifremer.dali.dto.configuration.control.ControlFeatureDTO;
35  import fr.ifremer.dali.dto.configuration.control.ControlRuleDTO;
36  import fr.ifremer.dali.dto.configuration.control.PreconditionRuleDTO;
37  import fr.ifremer.dali.dto.configuration.control.RulePmfmDTO;
38  import fr.ifremer.dali.dto.enums.*;
39  import fr.ifremer.quadrige3.core.dao.system.rule.*;
40  import fr.ifremer.quadrige3.core.dao.technical.Assert;
41  import fr.ifremer.quadrige3.core.dao.technical.QuadrigeEnumerationDef;
42  import fr.ifremer.quadrige3.core.dao.technical.hibernate.TemporaryDataHelper;
43  import fr.ifremer.quadrige3.core.exception.QuadrigeTechnicalException;
44  import org.apache.commons.collections4.CollectionUtils;
45  import org.apache.commons.collections4.ListValuedMap;
46  import org.apache.commons.collections4.MapUtils;
47  import org.apache.commons.collections4.MultiMapUtils;
48  import org.apache.commons.lang3.StringUtils;
49  import org.apache.commons.lang3.mutable.MutableBoolean;
50  import org.apache.commons.lang3.time.DateUtils;
51  import org.apache.commons.logging.Log;
52  import org.apache.commons.logging.LogFactory;
53  import org.hibernate.Query;
54  import org.hibernate.SessionFactory;
55  import org.hibernate.type.DateType;
56  import org.hibernate.type.IntegerType;
57  import org.hibernate.type.StringType;
58  import org.nuiton.i18n.I18n;
59  import org.nuiton.util.DateUtil;
60  import org.springframework.beans.factory.InitializingBean;
61  import org.springframework.beans.factory.annotation.Autowired;
62  import org.springframework.dao.DataRetrievalFailureException;
63  import org.springframework.stereotype.Repository;
64  
65  import javax.annotation.Resource;
66  import java.text.ParseException;
67  import java.util.*;
68  
69  /**
70   * <p>DaliRuleListDaoImpl class.</p>
71   *
72   * @author Ludovic
73   */
74  @Repository("daliRuleDao")
75  public class DaliRuleDaoImpl extends RuleDaoImpl implements DaliRuleDao, InitializingBean {
76  
77      private static final Log log = LogFactory.getLog(DaliRuleDaoImpl.class);
78      public static final String DATE_PATTERN = "yyyy-MM-dd";
79  
80      @Resource
81      protected DaliConfiguration config;
82      @Resource(name = "ruleParameterDao")
83      private RuleParameterDao ruleParameterDao;
84      @Resource(name = "daliRulePmfmDao")
85      private DaliRulePmfmDao rulePmfmDao;
86      @Resource(name = "rulePreconditionDao")
87      private RulePreconditionExtendDao rulePreconditionDao;
88      @Resource(name = "daliPmfmDao")
89      protected DaliPmfmDao pmfmDao;
90  
91      private int functionIdMinMax;
92      private int functionIdIn;
93      private int functionIdDateMinMax;
94      private int functionParameterIdMin;
95      private int functionParameterIdMax;
96      private int functionParameterIdIn;
97      private int functionParameterIdDateMin;
98      private int functionParameterIdDateMax;
99  
100     /**
101      * <p>Constructor for DaliRuleListDaoImpl.</p>
102      *
103      * @param sessionFactory a {@link SessionFactory} object.
104      */
105     @Autowired
106     public DaliRuleDaoImpl(SessionFactory sessionFactory) {
107         super(sessionFactory);
108     }
109 
110     /**
111      * {@inheritDoc}
112      */
113     @Override
114     public void afterPropertiesSet() {
115         initConstants();
116     }
117 
118     private void initConstants() {
119 
120         functionIdMinMax = ControlFunctionValues.MIN_MAX.getFunctionId();
121         functionIdDateMinMax = ControlFunctionValues.MIN_MAX_DATE.getFunctionId();
122         functionIdIn = ControlFunctionValues.IS_AMONG.getFunctionId();
123 
124         functionParameterIdMin = FunctionParameterId.MIN.getValue();
125         functionParameterIdMax = FunctionParameterId.MAX.getValue();
126         functionParameterIdIn = FunctionParameterId.IN.getValue();
127         functionParameterIdDateMin = FunctionParameterId.DATE_MIN.getValue();
128         functionParameterIdDateMax = FunctionParameterId.DATE_MAX.getValue();
129     }
130 
131     @Override
132     public List<ControlRuleDTO> getRulesByRuleListCode(String ruleListCode, boolean onlyActive, MutableBoolean incompatibleRule) {
133         Assert.notBlank(ruleListCode);
134 
135         Iterator<Object[]> it = queryIterator("rulesWithoutPreconditionedRuleByRuleListCode",
136                 "ruleListCode", StringType.INSTANCE, ruleListCode,
137                 "functionIdMinMax", IntegerType.INSTANCE, functionIdMinMax,
138                 "functionParameterIdMin", IntegerType.INSTANCE, functionParameterIdMin,
139                 "functionParameterIdMax", IntegerType.INSTANCE, functionParameterIdMax,
140                 "functionIdDateMinMax", IntegerType.INSTANCE, functionIdDateMinMax,
141                 "functionParameterIdDateMin", IntegerType.INSTANCE, functionParameterIdDateMin,
142                 "functionParameterIdDateMax", IntegerType.INSTANCE, functionParameterIdDateMax,
143                 "functionIdIn", IntegerType.INSTANCE, functionIdIn,
144                 "functionParameterIdIn", IntegerType.INSTANCE, functionParameterIdIn,
145                 "ruleIsActive", StringType.INSTANCE, onlyActive ? Daos.convertToString(true) : null);
146 
147         List<ControlRuleDTO> result = Lists.newArrayList();
148         while (it.hasNext()) {
149             Object[] source = it.next();
150             ControlRuleDTO rule = toRuleDTO(Arrays.asList(source).iterator());
151             if (rule != null) {
152                 // Add this rule if valid (!= null)
153                 result.add(rule);
154             } else if (incompatibleRule != null) {
155                 incompatibleRule.setTrue();
156             }
157         }
158 
159         for (ControlRuleDTO rule : result) {
160 
161             // add pmfm list
162             rule.setRulePmfms(rulePmfmDao.getRulePmfmByRuleCode(rule.getCode()));
163         }
164 
165         return result;
166 
167     }
168 
169     @Override
170     public List<ControlRuleDTO> getPreconditionedRulesByRuleListCode(String ruleListCode, boolean onlyActive, MutableBoolean incompatibleRule) {
171 
172         List<ControlRuleDTO> result = new ArrayList<>();
173         List<PreconditionRuleDTO> preconditionRules = getPreconditionsByRuleListCode(ruleListCode, onlyActive, incompatibleRule);
174 
175         // group preconditions by their name
176         ListValuedMap<String, PreconditionRuleDTO> preconditionRulesByName = MultiMapUtils.newListValuedHashMap();
177         for (PreconditionRuleDTO preconditionRule : preconditionRules) {
178             preconditionRulesByName.put(preconditionRule.getName(), preconditionRule);
179         }
180 
181         // iterate on the map to created preconditioned ('virtual') control rule
182         for (String name : preconditionRulesByName.keySet()) {
183             ControlRuleDTO preconditionedControlRule = DaliBeanFactory.newControlRuleDTO();
184             // set the name as rule code
185             preconditionedControlRule.setCode(name);
186             // set specific function
187             preconditionedControlRule.setFunction(ControlFunctionValues.PRECONDITION.toFunctionDTO());
188             // set control element to measurement
189             preconditionedControlRule.setControlElement(ControlElementValues.MEASUREMENT.toControlElementDTO());
190             // set control attribute to qualitative value
191             preconditionedControlRule.setControlFeature(ControlFeatureMeasurementValues.QUALITATIVE_VALUE.toControlFeatureDTO());
192 
193             // add all preconditions
194             boolean active = true;
195             Set<String> pmfmPairKeys = new HashSet<>();
196             for (PreconditionRuleDTO preconditionRule: preconditionRulesByName.get(name)) {
197                 active &= preconditionRule.isActive();
198                 // compute pmfm pair key and add it to the hash set
199                 pmfmPairKeys.add(String.format("%s#%s",
200                     getRulePmfmKey(preconditionRule.getBaseRule().getRulePmfms(0)),
201                     getRulePmfmKey(preconditionRule.getUsedRule().getRulePmfms(0))));
202                 // affect parent link
203                 preconditionRule.setRule(preconditionedControlRule);
204                 preconditionedControlRule.addPreconditions(preconditionRule);
205             }
206             // set the active flag (if 1 precondition is inactive, all control rule is inactive)
207             preconditionedControlRule.setActive(active);
208 
209             // check rule pmfm equality
210             if (pmfmPairKeys.size() > 1) {
211                 // if more than 1 pmfm is detected, it is an incompatible rule precondition
212                 if (log.isWarnEnabled()) {
213                     log.warn(String.format("Only 1 pair of PMFM is allowed in preconditioned rules (preconditionLb=%s)",
214                             preconditionedControlRule.getCode()));
215                 }
216                 if (incompatibleRule != null) incompatibleRule.setTrue();
217                 // skip it
218                 continue;
219             }
220             // populate the rulePmfm from the first precondition
221             preconditionedControlRule.addRulePmfms(preconditionedControlRule.getPreconditions(0).getBaseRule().getRulePmfms(0));
222             preconditionedControlRule.addRulePmfms(preconditionedControlRule.getPreconditions(0).getUsedRule().getRulePmfms(0));
223 
224             result.add(preconditionedControlRule);
225         }
226 
227         return result;
228     }
229 
230     /**
231      * {@inheritDoc}
232      */
233     @Override
234     public List<ControlRuleDTO> findActiveRules(Date date, String programCode, Integer departmentId) {
235 
236         List<ControlRuleDTO> result = Lists.newArrayList();
237         Iterator<String> ruleListCodeIt = getActiveRuleListCodes(date, programCode, departmentId);
238         while (ruleListCodeIt.hasNext()) {
239             String ruleListCode = ruleListCodeIt.next();
240             result.addAll(getRulesByRuleListCode(ruleListCode, true /*active only*/, null));
241         }
242         return result;
243     }
244 
245     @Override
246     public List<ControlRuleDTO> findActivePreconditionedRules(Date date, String programCode, Integer departmentId) {
247 
248         return findActivePreconditionedRules(getActiveRuleListCodes(date, programCode, departmentId));
249     }
250 
251     @Override
252     public List<ControlRuleDTO> findActivePreconditionedRules(List<String> programCodes) {
253 
254         return findActivePreconditionedRules(getActiveRuleListCodes(programCodes));
255     }
256 
257     private List<ControlRuleDTO> findActivePreconditionedRules(Iterator<String> ruleListCodeIt) {
258         List<ControlRuleDTO> result = Lists.newArrayList();
259         while (ruleListCodeIt.hasNext()) {
260             String ruleListCode = ruleListCodeIt.next();
261             result.addAll(getPreconditionedRulesByRuleListCode(ruleListCode, true /*active only*/, null));
262         }
263         return result;
264     }
265 
266     /**
267      * {@inheritDoc}
268      */
269     @Override
270     public void save(final ControlRuleDTO source, String ruleListCd) {
271         Assert.notNull(source);
272         Assert.notNull(source.getFunction());
273         Assert.notNull(source.getFunction().getId());
274 
275         // Parent
276         RuleList parent = get(RuleListImpl.class, ruleListCd);
277 
278         if (source.isPreconditionsEmpty()) {
279             save(source, parent);
280         } else {
281             saveWithPreconditions(source, parent);
282         }
283     }
284 
285     private void save(ControlRuleDTO source, RuleList parent) {
286 
287         Assert.isTrue(source.isPreconditionsEmpty());
288 
289         // delete previous rule with the same code (Mantis #45374) but not itself (Mantis #45919)
290         deleteObsoleteRulePreconditions(source.getCode());
291 
292         // Load entity
293         Rule entity = get(source.getCode());
294         boolean isNew = false;
295         if (entity == null) {
296             entity = Rule.Factory.newInstance();
297             entity.setRuleCd(source.getCode());
298             parent.addRules(entity);
299             entity.setRuleList(parent);
300             isNew = true;
301         }
302 
303         // DTO -> VO
304         beanToEntity(source, entity);
305 
306         // Save it
307         if (isNew) {
308             getSession().save(entity);
309         } else {
310             getSession().update(entity);
311         }
312 
313         // Save rule parameters
314         {
315             Integer functionId = source.getFunction().getId();
316             Map<Integer, RuleParameter> ruleParametersByFunctionParamIds = DaliBeans.mapByProperty(entity.getRuleParameters(), "functionParameter.functionParId");
317             final Iterator<Integer> ruleParIdSequence = TemporaryDataHelper.getNewNegativeTemporarySequence(getSession(), RuleParameterImpl.class, true);
318 
319             // MinMax function
320             if (functionId.equals(functionIdMinMax)) {
321                 if (source.getMin() != null && source.getMin() instanceof Double) {
322                     updateOrCreateRuleParameter(entity,
323                             ruleParIdSequence,
324                             ruleParametersByFunctionParamIds,
325                             functionParameterIdMin,
326                             source.getMin().toString());
327                 }
328                 if (source.getMax() != null && source.getMax() instanceof Double) {
329                     updateOrCreateRuleParameter(entity,
330                             ruleParIdSequence,
331                             ruleParametersByFunctionParamIds,
332                             functionParameterIdMax,
333                             source.getMax().toString());
334                 }
335             }
336 
337             // MinMax Date function
338             else if (functionId.equals(functionIdDateMinMax)) {
339                 if (source.getMin() != null && source.getMin() instanceof Date) {
340                     updateOrCreateRuleParameter(entity,
341                             ruleParIdSequence,
342                             ruleParametersByFunctionParamIds,
343                             functionParameterIdDateMin,
344                             DateUtil.formatDate((Date) source.getMin(), DATE_PATTERN)
345                     );
346                 }
347                 if (source.getMax() != null && source.getMax() instanceof Date) {
348                     updateOrCreateRuleParameter(entity,
349                             ruleParIdSequence,
350                             ruleParametersByFunctionParamIds,
351                             functionParameterIdDateMax,
352                             DateUtil.formatDate((Date) source.getMax(), DATE_PATTERN)
353                     );
354                 }
355             }
356 
357             // In function
358             else if (functionId.equals(functionIdIn) && source.getAllowedValues() != null) {
359                 updateOrCreateRuleParameter(entity,
360                         ruleParIdSequence,
361                         ruleParametersByFunctionParamIds,
362                         functionParameterIdIn,
363                         source.getAllowedValues());
364             }
365 
366             // Remove unused rule parameters
367             if (MapUtils.isNotEmpty(ruleParametersByFunctionParamIds)) {
368                 ruleParameterDao.remove(ruleParametersByFunctionParamIds.values());
369                 entity.getRuleParameters().removeAll(ruleParametersByFunctionParamIds.values());
370             }
371 
372             getSession().update(entity);
373         }
374 
375         getSession().flush();
376 
377         // Save rule pmfms
378         {
379             final Map<Integer, RulePmfm> rulePmfmsToRemove = DaliBeans.mapByProperty(entity.getRulePmfms(), "rulePmfmId");
380             if (CollectionUtils.isNotEmpty(source.getRulePmfms())) {
381                 source.getRulePmfms().forEach(rulePmfm -> {
382                     rulePmfm = rulePmfmDao.save(rulePmfm, source.getCode());
383                     rulePmfmsToRemove.remove(rulePmfm.getId());
384                 });
385             }
386 
387             // Remove unused rule pmfms
388             if (MapUtils.isNotEmpty(rulePmfmsToRemove)) {
389                 rulePmfmDao.removeByIds(rulePmfmsToRemove.keySet());
390                 entity.getRulePmfms().removeAll(rulePmfmsToRemove.values());
391             }
392         }
393 
394         // reset this flag to avoid code changes
395         source.setNewCode(false);
396     }
397 
398     private void saveWithPreconditions(ControlRuleDTO source, RuleList parent) {
399 
400         Assert.isTrue(!source.isPreconditionsEmpty());
401 
402         // delete previous rule with the same code (Mantis #45374)
403         deleteObsoleteRule(source.getCode());
404 
405         final Iterator<Integer> preconditionIdSequence = TemporaryDataHelper.getNewNegativeTemporarySequence(getSession(), RulePreconditionImpl.class, true);
406 
407         for (PreconditionRuleDTO precondition: source.getPreconditions()) {
408 
409             // save base and used rules first
410             save(precondition.getBaseRule(), parent);
411             save(precondition.getUsedRule(), parent);
412 
413             RulePrecondition entity = precondition.getId() != null ? rulePreconditionDao.get(precondition.getId()) : null;
414             boolean isNew = false;
415             if (entity == null) {
416                 entity = RulePrecondition.Factory.newInstance();
417                 entity.setRulePrecondId(preconditionIdSequence.next());
418                 isNew = true;
419             }
420 
421             Rule baseRule = get(precondition.getBaseRule().getCode());
422             Assert.notNull(baseRule);
423             Rule usedRule = get(precondition.getUsedRule().getCode());
424             Assert.notNull(usedRule);
425 
426             entity.setRule(baseRule);
427             entity.setUsedRule(usedRule);
428             // the precondition is active if user active the virtual rule
429             entity.setRulePrecondIsActive(Daos.convertToString(source.isActive()));
430             entity.setRulePrecondIsBidir(Daos.convertToString(precondition.isBidirectional()));
431             // use the virtual rule code
432             entity.setRulePrecondLb(source.getCode());
433 
434             if (isNew) {
435                 getSession().save(entity);
436                 precondition.setId(entity.getRulePrecondId());
437             } else {
438                 getSession().update(entity);
439             }
440         }
441 
442     }
443 
444     /**
445      * {@inheritDoc}
446      */
447     @Override
448     public List<FunctionDTO> getAllFunction() {
449 
450         // TODO by query or by FunctionDao ??
451 
452         Iterator<Function> it = queryIteratorTyped("allFunctionEntity");
453 
454         List<FunctionDTO> result = Lists.newArrayList();
455         while (it.hasNext()) {
456             Function source = it.next();
457             FunctionDTO target = toFunctionDTO(source, true /*Could return null if not found in rule function enumeration*/);
458             if (target != null) {
459                 result.add(target);
460             }
461         }
462 
463         return result;
464     }
465 
466     @Override
467     public boolean ruleExists(String ruleCode) {
468         Assert.notBlank(ruleCode);
469 
470         return queryUnique("ruleExists", "ruleCode", StringType.INSTANCE, ruleCode) != null;
471     }
472 
473     /* -- private methods -- */
474 
475     private void deleteObsoleteRule(String ruleCode) {
476         Rule rule = get(ruleCode);
477         if (rule != null) {
478             remove(rule);
479         }
480     }
481 
482     private void deleteObsoleteRulePreconditions(String ruleCode) {
483         List<RulePrecondition> rulePreconditions = rulePreconditionDao.getByCode(ruleCode);
484         if (CollectionUtils.isNotEmpty(rulePreconditions)) {
485             rulePreconditionDao.remove(rulePreconditions);
486         }
487     }
488 
489     private Iterator<String> getActiveRuleListCodes(Date date, String programCode, Integer departmentId) {
490 
491         // find active rule list corresponding to criteria
492         return queryIteratorTyped("activeRuleListCodesByCriteria",
493                 "programCode", StringType.INSTANCE, programCode,
494                 "departmentId", IntegerType.INSTANCE, departmentId,
495                 "date", DateType.INSTANCE, date);
496 
497     }
498 
499     @SuppressWarnings("unchecked")
500     private Iterator<String> getActiveRuleListCodes(List<String> programCodes) {
501 
502         // find active rule list corresponding to program codes
503         Query query = createQuery("activeRuleListCodesByProgramCodes").setParameterList("programCodes", programCodes);
504         return query.iterate();
505 
506     }
507 
508     private ControlRuleDTO getRule(String ruleCode) {
509 
510         Object[] row = queryUnique("ruleByCode",
511                 "ruleCode", StringType.INSTANCE, ruleCode,
512                 "functionIdMinMax", IntegerType.INSTANCE, functionIdMinMax,
513                 "functionParameterIdMin", IntegerType.INSTANCE, functionParameterIdMin,
514                 "functionParameterIdMax", IntegerType.INSTANCE, functionParameterIdMax,
515                 "functionIdDateMinMax", IntegerType.INSTANCE, functionIdDateMinMax,
516                 "functionParameterIdDateMin", IntegerType.INSTANCE, functionParameterIdDateMin,
517                 "functionParameterIdDateMax", IntegerType.INSTANCE, functionParameterIdDateMax,
518                 "functionIdIn", IntegerType.INSTANCE, functionIdIn,
519                 "functionParameterIdIn", IntegerType.INSTANCE, functionParameterIdIn);
520 
521         if (row == null)
522             throw new DataRetrievalFailureException("can't load rule with code = " + ruleCode);
523 
524         // the returned value can be null is rule is incompatible
525         ControlRuleDTO controlRule = toRuleDTO(Arrays.asList(row).iterator());
526         if (controlRule != null) {
527             controlRule.setRulePmfms(rulePmfmDao.getRulePmfmByRuleCode(controlRule.getCode()));
528         }
529         return controlRule;
530     }
531 
532     private List<PreconditionRuleDTO> getPreconditionsByRuleListCode(String ruleListCode, boolean onlyActive, MutableBoolean incompatibleRule) {
533 
534         Iterator<Object[]> rows = queryIterator("rulePreconditionsByRuleListCode",
535                 "ruleListCode", StringType.INSTANCE, ruleListCode,
536                 "rulePreconditionIsActive", StringType.INSTANCE, onlyActive ? Daos.convertToString(true) : null);
537 
538         List<PreconditionRuleDTO> result = new ArrayList<>();
539         while (rows.hasNext()) {
540             Object[] row = rows.next();
541             PreconditionRuleDTO preconditionRule = toPreconditionRuleDTO(Arrays.asList(row).iterator());
542             if (preconditionRule != null) {
543                 // a null preconditionRule means one the rule is incompatible
544                 result.add(preconditionRule);
545             } else if (incompatibleRule != null) {
546                 incompatibleRule.setTrue();
547             }
548         }
549         return result;
550     }
551 
552     private PreconditionRuleDTO toPreconditionRuleDTO(Iterator<Object> source) {
553         PreconditionRuleDTO result = DaliBeanFactory.newPreconditionRuleDTO();
554         result.setId((Integer) source.next());
555         result.setName((String) source.next());
556         result.setActive(Daos.safeConvertToBoolean(source.next()));
557         result.setBidirectional(Daos.safeConvertToBoolean(source.next()));
558 
559         // get base and used rules
560         ControlRuleDTO baseRule = getRule((String) source.next());
561         ControlRuleDTO usedRule = getRule((String) source.next());
562 
563         // if one of the rules is incompatible the precondition also
564         if (baseRule == null || usedRule == null) return null;
565 
566         for (ControlRuleDTO rule : ImmutableList.of(baseRule, usedRule)) {
567             // for now, accept only rules on measurement's qualitative value (Mantis #58417)
568             if (!DaliBeans.isQualitativeControlRule(rule)) {
569                 if (log.isWarnEnabled()) {
570                     log.warn(String.format("Only measurement's qualitative are allowed in preconditioned rule (precondition=%s, rule=%s)",
571                             result.getId(), rule.getCode()));
572                 }
573                 return null;
574             }
575 
576             // accept only with 1 PMFM
577             if (rule.sizeRulePmfms() != 1) {
578                 if (log.isWarnEnabled()) {
579                     log.warn(String.format("Only 1 PMFM is allowed in preconditioned rule (precondition=%s, rule=%s)",
580                             result.getId(), rule.getCode()));
581                 }
582                 return null;
583             }
584         }
585 
586         // affect if all ok
587         result.setBaseRule(baseRule);
588         result.setUsedRule(usedRule);
589         return result;
590     }
591 
592     private ControlRuleDTO toRuleDTO(Iterator<Object> source) {
593         ControlRuleDTO result = DaliBeanFactory.newControlRuleDTO();
594         result.setCode((String) source.next());
595 
596         String controlledElementString = (String) source.next();
597 
598         if (controlledElementString != null) {
599             int separatorIndex = controlledElementString.lastIndexOf(config.getAttributeSeparator());
600             // Check if a separator exists (if not: skip controlled element)
601             if (separatorIndex == -1 || separatorIndex == controlledElementString.length() - 1) {
602                 if (log.isWarnEnabled()) {
603                     log.warn(I18n.t("dali.error.dao.ruleList.parse.controlledAttribute", controlledElementString, result.getCode()));
604                 }
605                 return null;
606             } else {
607                 String controlElementCode = controlledElementString.substring(0, separatorIndex);
608                 String controlFeatureCode = controlledElementString.substring(separatorIndex + 1);
609 
610                 // get control element by its code
611                 ControlElementValues controlElementValue = ControlElementValues.getByCode(controlElementCode);
612                 if (controlElementValue != null) {
613                     result.setControlElement(controlElementValue.toControlElementDTO());
614 
615                     ControlFeatureDTO controlFeature = null;
616                     // get control feature depending in control element
617                     switch (controlElementValue) {
618 
619                         case SURVEY:
620                             controlFeature = ControlFeatureSurveyValues.toControlFeatureDTO(controlFeatureCode);
621                             break;
622                         case SAMPLING_OPERATION:
623                             controlFeature = ControlFeatureSamplingOperationValues.toControlFeatureDTO(controlFeatureCode);
624                             break;
625                         case MEASUREMENT:
626                             controlFeature = ControlFeatureMeasurementValues.toControlFeatureDTO(controlFeatureCode);
627                             break;
628                         // TODO Les mesures sur taxons ne sont pas encore prises en charge
629 //                        case TAXON_MEASUREMENT:
630 //                            controlFeature = ControlFeatureTaxonMeasurementValues.toControlFeatureDTO(controlFeatureCode);
631 //                            break;
632                     }
633 
634                     if (controlFeature != null) {
635                         result.setControlFeature(controlFeature);
636                     } else {
637                         if (log.isWarnEnabled()) {
638                             log.warn(I18n.t("dali.error.dao.ruleList.rule.skip", result.getCode(), controlledElementString));
639                         }
640                         return null;
641                     }
642 
643                 } else {
644                     if (log.isWarnEnabled()) {
645                         log.warn(I18n.t("dali.error.dao.ruleList.rule.skip", result.getCode(), controlElementCode));
646                     }
647                     return null;
648                 }
649             }
650         }
651 
652         result.setDescription((String) source.next());
653         result.setActive(Daos.safeConvertToBoolean(source.next()));
654         result.setBlocking(Daos.safeConvertToBoolean(source.next()));
655         result.setMessage((String) source.next());
656 
657         // function entity
658         Function function = (Function) source.next();
659         result.setFunction(toFunctionDTO(function, false/*Could not return null value*/));
660 
661         // function parameters
662         String valueMin = (String) source.next();
663         String valueMax = (String) source.next();
664         String dateMin = (String) source.next();
665         String dateMax = (String) source.next();
666         if (result.getFunction().getId().equals(functionIdDateMinMax)) {
667             try {
668                 result.setMin(dateMin == null ? null : parseDate(dateMin));
669             } catch (ParseException e) {
670                 log.warn(String.format("Error when parsing %s as date", dateMin), e);
671                 return null;
672             }
673             try {
674                 result.setMax(dateMax == null ? null : parseDate(dateMax));
675             } catch (ParseException e) {
676                 log.warn(String.format("Error when parsing %s as date", dateMax), e);
677                 return null;
678             }
679         } else {
680             result.setMin(valueMin == null ? null : Double.parseDouble(valueMin));
681             result.setMax(valueMax == null ? null : Double.parseDouble(valueMax));
682         }
683         result.setAllowedValues((String) source.next());
684 
685         return result;
686     }
687 
688     private Date parseDate(String s) throws ParseException {
689         return StringUtils.isNumeric(s) ? new Date(Long.parseLong(s)) : DateUtils.parseDate(s, DATE_PATTERN);
690     }
691 
692     private FunctionDTO toFunctionDTO(Function function, boolean couldBeNull) {
693         FunctionDTO result = DaliBeanFactory.newFunctionDTO();
694         result.setId(function.getFunctionId());
695         ControlFunctionValues functionControlValue = ControlFunctionValues.getFunctionValue(function.getFunctionId());
696         if (functionControlValue == null) {
697             String errorMessage = String.format("Could not found enumeration for Function with id [%s]. Make sure enumeration file has a property like '%s functionId.(...)=%s'",
698                     function.getFunctionId(),
699                     QuadrigeEnumerationDef.CONFIG_OPTION_PREFIX,
700                     function.getFunctionId());
701             if (!couldBeNull) {
702                 throw new QuadrigeTechnicalException(errorMessage);
703             }
704 
705             log.warn(errorMessage);
706             return null;
707         }
708 
709         result.setName(functionControlValue.getLabel());
710         return result;
711     }
712 
713     private String getRulePmfmKey(RulePmfmDTO rulePmfm) {
714 
715         return String.format("%s|%s|%s|%s|%s",
716                 rulePmfm.getPmfm().getParameter().getCode(),
717                 rulePmfm.getPmfm().getMatrix() != null ? rulePmfm.getPmfm().getMatrix().getId() : "null",
718                 rulePmfm.getPmfm().getFraction() != null ? rulePmfm.getPmfm().getFraction().getId() : "null",
719                 rulePmfm.getPmfm().getMethod() != null ? rulePmfm.getPmfm().getMethod().getId() : "null",
720                 rulePmfm.getPmfm().getUnit() != null ? rulePmfm.getPmfm().getUnit().getId() : "null"
721         );
722     }
723 
724     private void beanToEntity(ControlRuleDTO source, Rule target) {
725         // Basic properties
726         target.setRuleDc(source.getDescription());
727         String controlledAttribute = source.getControlElement().getCode()
728                 .concat(config.getAttributeSeparator())
729                 .concat(source.getControlFeature().getCode());
730         target.setRuleControledAttribut(controlledAttribute);
731         target.setRuleErrorMsg(source.getMessage());
732         target.setRuleIsActive(Daos.convertToString(source.isActive()));
733         target.setRuleIsBlocking(Daos.convertToString(source.isBlocking()));
734 
735         // Function
736         Function function;
737         if (source.getFunction() != null) {
738             function = load(FunctionImpl.class, source.getFunction().getId());
739         } else {
740             throw new DataRetrievalFailureException("ControlRuleDTO object has no Function");
741         }
742         if (function == null) {
743             throw new DataRetrievalFailureException(String.format("function not found with id=%s", source.getFunction().getId()));
744         }
745         target.setFunction(function);
746     }
747 
748     private void updateOrCreateRuleParameter(Rule parent,
749                                              Iterator<Integer> ruleParIdSequence,
750                                              Map<Integer, RuleParameter> ruleParametersByFunctionParamIds,
751                                              int functionParameterId,
752                                              String ruleParameterValue) {
753         RuleParameter rp = ruleParametersByFunctionParamIds.remove(functionParameterId);
754         boolean isNew = false;
755         if (rp == null) {
756             rp = RuleParameterImpl.Factory.newInstance();
757             rp.setFunctionParameter(load(FunctionParameterImpl.class, functionParameterId));
758             rp.setRuleParId(ruleParIdSequence.next());
759             parent.addRuleParameters(rp);
760             rp.setRule(parent);
761             isNew = true;
762         }
763 
764         // Update properties
765         rp.setRuleParValue(ruleParameterValue);
766 
767         // Save to session
768         if (isNew) {
769             getSession().save(rp);
770         } else {
771             getSession().update(rp);
772         }
773 
774         // todo vérifier si utile ?
775 //        getSession().flush();
776     }
777 }