View Javadoc
1   package fr.ifremer.dali.service.control;
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.base.Joiner;
27  import com.google.common.collect.HashMultimap;
28  import com.google.common.collect.Multimap;
29  import fr.ifremer.dali.config.DaliConfiguration;
30  import fr.ifremer.dali.dao.system.rule.DaliRuleDao;
31  import fr.ifremer.dali.dao.system.rule.DaliRuleListDao;
32  import fr.ifremer.dali.dto.DaliBeanFactory;
33  import fr.ifremer.dali.dto.DaliBeans;
34  import fr.ifremer.dali.dto.configuration.control.ControlRuleDTO;
35  import fr.ifremer.dali.dto.configuration.control.PreconditionRuleDTO;
36  import fr.ifremer.dali.dto.configuration.control.RuleListDTO;
37  import fr.ifremer.dali.dto.configuration.control.RulePmfmDTO;
38  import fr.ifremer.dali.dto.data.survey.SurveyDTO;
39  import fr.ifremer.dali.dto.enums.ControlElementValues;
40  import fr.ifremer.dali.dto.enums.ControlFeatureMeasurementValues;
41  import fr.ifremer.dali.dto.enums.ControlFunctionValues;
42  import fr.ifremer.dali.dto.referential.pmfm.QualitativeValueDTO;
43  import fr.ifremer.dali.dto.system.extraction.PmfmPresetDTO;
44  import fr.ifremer.dali.service.DaliDataContext;
45  import fr.ifremer.dali.service.referential.ReferentialService;
46  import fr.ifremer.dali.vo.PresetVO;
47  import fr.ifremer.quadrige3.core.dao.system.rule.RuleListDao;
48  import fr.ifremer.quadrige3.core.dao.technical.Assert;
49  import fr.ifremer.quadrige3.core.dao.technical.Dates;
50  import fr.ifremer.quadrige3.core.dao.technical.Times;
51  import fr.ifremer.quadrige3.core.dao.technical.factorization.CombinationList;
52  import fr.ifremer.quadrige3.core.dao.technical.factorization.Factorizations;
53  import fr.ifremer.quadrige3.core.dao.technical.factorization.pmfm.AllowedQualitativeValuesMap;
54  import fr.ifremer.quadrige3.core.dao.technical.factorization.pmfm.QualitativeValuesCombinationValidator;
55  import fr.ifremer.quadrige3.core.security.AuthenticationInfo;
56  import fr.ifremer.quadrige3.core.vo.system.rule.RuleListVO;
57  import fr.ifremer.quadrige3.synchro.service.client.SynchroRestClientService;
58  import fr.ifremer.quadrige3.ui.core.dto.referential.BaseReferentialDTO;
59  import org.apache.commons.collections4.CollectionUtils;
60  import org.apache.commons.lang3.StringUtils;
61  import org.apache.commons.lang3.mutable.MutableInt;
62  import org.apache.commons.logging.Log;
63  import org.apache.commons.logging.LogFactory;
64  import org.springframework.beans.factory.annotation.Autowired;
65  import org.springframework.stereotype.Service;
66  
67  import javax.annotation.Resource;
68  import java.util.*;
69  import java.util.stream.Collectors;
70  
71  /**
72   * Implementation rules control service.
73   */
74  @Service("daliRuleListService")
75  public class RuleListServiceImpl implements RuleListService {
76  
77      private static final Log LOG = LogFactory.getLog(RuleListServiceImpl.class);
78  
79      @Resource(name = "daliRuleListDao")
80      private DaliRuleListDao ruleListDao;
81      @Resource(name = "daliRuleDao")
82      private DaliRuleDao ruleDao;
83      @Resource(name = "daliDataContext")
84      private DaliDataContext dataContext;
85      @Resource(name = "synchroRestClientService")
86      private SynchroRestClientService synchroRestClientService;
87      @Resource(name = "daliReferentialService")
88      private ReferentialService referentialService;
89      @Resource(name = "daliControlRuleService")
90      private ControlRuleService controlRuleService;
91      @Autowired
92      protected DaliConfiguration configuration;
93  
94      /**
95       * {@inheritDoc}
96       */
97      @Override
98      public RuleListDTO getRuleList(String ruleListCode) {
99          Assert.notBlank(ruleListCode);
100         return ruleListDao.getRuleList(ruleListCode);
101     }
102 
103     /**
104      * {@inheritDoc}
105      */
106     @Override
107     public List<RuleListDTO> getAllRuleLists() {
108         return ruleListDao.getAllRuleLists();
109     }
110 
111     @Override
112     public List<RuleListDTO> getRuleListsForProgram(String programCode) {
113         return ruleListDao.getRuleListsForProgram(programCode);
114     }
115 
116     @Override
117     public boolean ruleListCodeExists(String ruleListCode) {
118         return ruleListDao.ruleListExists(ruleListCode);
119     }
120 
121     /**
122      * {@inheritDoc}
123      */
124     @Override
125     public void saveRuleLists(AuthenticationInfo authenticationInfo, List<? extends RuleListDTO> ruleLists) {
126 
127         Assert.notNull(ruleLists);
128 
129         // get rule lists to save
130         List<RuleListDTO> dirtyRuleList = ruleLists.stream().filter(RuleListDTO::isDirty).collect(Collectors.toList());
131 
132         // nothing to save
133         if (CollectionUtils.isEmpty(dirtyRuleList)) return;
134 
135         // Collect rule list codes
136         Set<String> ruleListCodes = dirtyRuleList.stream().map(ruleList -> {
137 
138             // preconditions
139             Assert.notBlank(ruleList.getCode());
140             Assert.notEmpty(ruleList.getPrograms());
141 
142             // save locally
143             ruleListDao.saveRuleList(ruleList, dataContext.getRecorderPersonId());
144 
145             return ruleList.getCode();
146 
147         }).collect(Collectors.toSet());
148 
149         // Save remote rule lists
150         saveRuleListsOnServer(authenticationInfo, ruleListCodes);
151 
152         // Reset newCode flags
153         dirtyRuleList.forEach(ruleList -> {
154             ruleList.setDirty(false);
155             ruleList.setNewCode(false);
156             ruleList.getControlRules().forEach(controlRule -> controlRule.setNewCode(false));
157         });
158 
159     }
160 
161     private void saveRuleListsOnServer(AuthenticationInfo authenticationInfo, Set<String> ruleListCodes) {
162 
163         if (CollectionUtils.isEmpty(ruleListCodes)) return;
164 
165         if (LOG.isDebugEnabled()) {
166             LOG.debug(String.format("Sending rule lists [%s] to server", Joiner.on(',').join(ruleListCodes)));
167         }
168 
169         // get VOs
170         List<RuleListVO> ruleListToSend = ruleListCodes.stream()
171             .map(ruleListCode -> (RuleListVO) ruleListDao.load(RuleListDao.TRANSFORM_RULELISTVO, ruleListCode))
172             .collect(Collectors.toList());
173 
174         // send to server
175         List<RuleListVO> savedRuleLists = synchroRestClientService.saveRuleLists(authenticationInfo, ruleListToSend);
176 
177         if (LOG.isDebugEnabled()) {
178             LOG.debug(String.format("Saving rule lists [%s] from server response", Joiner.on(',').join(savedRuleLists)));
179         }
180 
181         // save locally (update)
182         for (RuleListVO savedRuleList : savedRuleLists) {
183             ruleListDao.save(savedRuleList);
184         }
185 
186     }
187 
188     /**
189      * {@inheritDoc}
190      */
191     @Override
192     public void deleteRuleLists(AuthenticationInfo authenticationInfo, List<String> ruleListCodes) {
193         if (CollectionUtils.isEmpty(ruleListCodes)) return;
194 
195         // delete locally
196         for (String ruleListCode : ruleListCodes) {
197             ruleListDao.remove(ruleListCode);
198         }
199 
200         deleteRuleListsOnServer(authenticationInfo, ruleListCodes);
201     }
202 
203     private void deleteRuleListsOnServer(AuthenticationInfo authenticationInfo, List<String> ruleListCodes) {
204 
205         if (LOG.isDebugEnabled()) {
206             LOG.debug(String.format("Delete rule lists [%s] from server", Joiner.on(',').join(ruleListCodes)));
207         }
208 
209         // Check if each ruleLists exists on server (it happens that a deleted ruleList had not been saved before)
210         Set<String> ruleListCodesToDelete = ruleListCodes.stream()
211             .filter(ruleListCode -> {
212                 if (synchroRestClientService.getRuleListByCode(authenticationInfo, ruleListCode) != null) {
213                     return true;
214                 } else {
215                     if (LOG.isDebugEnabled()) {
216                         LOG.debug(String.format("Rule List [%s] doesn't exists on server, skip it", ruleListCode));
217                     }
218                     return false;
219                 }
220             })
221             .collect(Collectors.toSet());
222 
223         if (CollectionUtils.isNotEmpty(ruleListCodesToDelete)) {
224 
225             // remote delete
226             synchroRestClientService.deleteRuleLists(authenticationInfo, ruleListCodesToDelete);
227 
228             if (LOG.isDebugEnabled()) {
229                 LOG.debug(String.format("Rule lists [%s] deleted from server", Joiner.on(',').join(ruleListCodesToDelete)));
230             }
231         }
232     }
233 
234     /**
235      * {@inheritDoc}
236      */
237     @Override
238     public RuleListDTO duplicateRuleList(RuleListDTO ruleList, String newCode, boolean isDuplicateRulesEnabled) {
239 
240         RuleListDTO duplicatedRuleList = DaliBeans.clone(ruleList);
241 
242         duplicatedRuleList.setCode(newCode);
243         duplicatedRuleList.setNewCode(true);
244         duplicatedRuleList.setDepartments(DaliBeans.clone(ruleList.getDepartments()));
245         duplicatedRuleList.setPrograms(DaliBeans.clone(ruleList.getPrograms()));
246         duplicatedRuleList.setErrors(new ArrayList<>());
247 
248         duplicatedRuleList.setControlRules(new ArrayList<>());
249         if (isDuplicateRulesEnabled && CollectionUtils.isNotEmpty(ruleList.getControlRules())) {
250             MutableInt suffixIndex = getUniqueMutableIndex();
251             for (ControlRuleDTO controlRule : ruleList.getControlRules()) {
252                 ControlRuleDTO duplicatedControlRule = duplicateControlRule(controlRule, duplicatedRuleList);
253                 // clone preconditions if exists
254                 if (!controlRule.isPreconditionsEmpty()) {
255                     duplicatePreconditions(controlRule, duplicatedControlRule, suffixIndex);
256                 }
257                 duplicatedRuleList.addControlRules(duplicatedControlRule);
258             }
259         }
260 
261         return duplicatedRuleList;
262     }
263 
264     private ControlRuleDTO duplicateControlRule(ControlRuleDTO controlRule, RuleListDTO ruleList) {
265 
266         ControlRuleDTO duplicatedControlRule = DaliBeans.clone(controlRule);
267 
268         duplicatedControlRule.setCode(ruleList != null ? getNextRuleCode(ruleList) : null);
269         duplicatedControlRule.setNewCode(true);
270         duplicatedControlRule.setErrors(null);
271         duplicatedControlRule.setPreconditions(null);
272         duplicatedControlRule.setRulePmfms(null);
273 
274         // clone pmfm list and reset RULE_PMFM ids
275         for (RulePmfmDTO rulePmfm : controlRule.getRulePmfms()) {
276             RulePmfmDTO duplicatedRulePmfm = DaliBeans.clone(rulePmfm);
277             duplicatedRulePmfm.setId(null);
278             duplicatedControlRule.addRulePmfms(duplicatedRulePmfm);
279         }
280 
281         return duplicatedControlRule;
282     }
283 
284     private void duplicatePreconditions(ControlRuleDTO controlRule, ControlRuleDTO duplicatedControlRule, MutableInt suffixIndex) {
285 
286         for (PreconditionRuleDTO precondition : controlRule.getPreconditions()) {
287             PreconditionRuleDTO duplicatedPrecondition = DaliBeans.clone(precondition);
288             duplicatedPrecondition.setId(null);
289             duplicatedPrecondition.setRule(duplicatedControlRule);
290             duplicatedPrecondition.setName(duplicatedControlRule.getCode());
291             duplicatedPrecondition.setBaseRule(duplicateControlRule(precondition.getBaseRule(), null));
292             duplicatedPrecondition.getBaseRule().setCode(getNextRuleCode(duplicatedControlRule.getCode(), suffixIndex));
293             duplicatedPrecondition.setUsedRule(duplicateControlRule(precondition.getUsedRule(), null));
294             duplicatedPrecondition.getUsedRule().setCode(getNextRuleCode(duplicatedControlRule.getCode(), suffixIndex));
295             duplicatedControlRule.addPreconditions(duplicatedPrecondition);
296         }
297 
298     }
299 
300     /**
301      * {@inheritDoc}
302      */
303     @Override
304     public ControlRuleDTO newControlRule(RuleListDTO ruleList) {
305 
306         Assert.notNull(ruleList);
307         Assert.notBlank(ruleList.getCode());
308 
309         ControlRuleDTO rule = DaliBeanFactory.newControlRuleDTO();
310 
311         rule.setCode(getNextRuleCode(ruleList));
312         rule.setNewCode(true);
313         rule.setActive(true);
314         rule.setBlocking(false);
315 
316         return rule;
317     }
318 
319     @Override
320     public boolean ruleCodeExists(String ruleCode) {
321         Assert.notBlank(ruleCode);
322 
323         return ruleDao.ruleExists(ruleCode);
324     }
325 
326     @Override
327     public List<ControlRuleDTO> getPreconditionedControlRulesForSurvey(SurveyDTO survey) {
328         Assert.notNull(survey);
329         Assert.notNull(survey.getDate());
330         Assert.notNull(survey.getProgram());
331         Assert.notNull(survey.getDepartment());
332 
333         List<ControlRuleDTO> preconditionedRules = ruleDao.findActivePreconditionedRules(
334             Dates.convertToDate(survey.getDate(), configuration.getDbTimezone()),
335             survey.getProgram().getCode(),
336             survey.getDepartment().getId());
337         buildPmfmInformation(preconditionedRules);
338         return preconditionedRules;
339     }
340 
341     @Override
342     public List<ControlRuleDTO> getPreconditionedControlRulesForProgramCodes(List<String> programCodes) {
343         Assert.notEmpty(programCodes);
344 
345         List<ControlRuleDTO> preconditionedRules = ruleDao.findActivePreconditionedRules(programCodes);
346         buildPmfmInformation(preconditionedRules);
347         return preconditionedRules;
348 
349     }
350 
351     @Override
352     public void buildAllowedValuesByPmfmId(int sourcePmfmId, Object sourceValue, Collection<Integer> targetPmfmIds,
353                                            Collection<PreconditionRuleDTO> preconditions,
354                                            AllowedQualitativeValuesMap allowedQualitativeValuesMap) {
355 
356         for (PreconditionRuleDTO precondition : preconditions) {
357 
358             ControlRuleDTO sourceRule;
359             ControlRuleDTO targetRule;
360 
361             // determine base and used rule
362             if (!precondition.isBidirectional() || Objects.equals(precondition.getBaseRule().getRulePmfms(0).getPmfm().getId(), sourcePmfmId)) {
363                 // simple case for mono directional rule or if the base rule pmfm is the actual pmfm
364                 sourceRule = precondition.getBaseRule();
365                 targetRule = precondition.getUsedRule();
366             } else {
367                 // source and target are switched
368                 sourceRule = precondition.getUsedRule();
369                 targetRule = precondition.getBaseRule();
370             }
371 
372             // find now the target column, and stop here if not found
373             int targetPmfmId = targetRule.getRulePmfms(0).getPmfm().getId();
374             if (!targetPmfmIds.contains(targetPmfmId)) continue; // if the target pmfm is not part of target pmfms, continue to next precondition
375 
376             // valid source && target rules
377             if (DaliBeans.isQualitativeControlRule(targetRule)) {
378 
379                 if (DaliBeans.isQualitativeControlRule(sourceRule)) {
380 
381                     BaseReferentialDTO qualitativeValue = (BaseReferentialDTO) sourceValue;
382 
383                     // control the value on qualitative source rule
384                     if (controlRuleService.controlUniqueObject(sourceRule, qualitativeValue)) {
385 
386                         // add allowed values from the target rule
387                         allowedQualitativeValuesMap.addTargetValues(targetPmfmId, sourcePmfmId, qualitativeValue.getId(), getAllowedValueIds(targetRule));
388 
389                     } else {
390 
391                         // if this rule is invalid, try to reset allowed values if possible
392                         AllowedQualitativeValuesMap.AllowedValues allowedValues =
393                             allowedQualitativeValuesMap.getAllowedValues(targetPmfmId, sourcePmfmId, qualitativeValue != null ? qualitativeValue.getId() : null);
394                         if (qualitativeValue == null) {
395                             allowedValues.setAllAllowed();
396                         }
397 
398                     }
399 
400                 } else {
401                     // invalid source rule
402                     LOG.warn(String.format("invalid source rule %s", sourceRule.getCode()));
403                 }
404 
405             } else {
406 
407                 // invalid target rule
408                 LOG.warn(String.format("invalid target rule %s", targetRule.getCode()));
409             }
410 
411         }
412     }
413 
414     private Set<Integer> getAllowedValueIds(ControlRuleDTO rule) {
415         return DaliBeans.getIntegerSetFromString(rule.getAllowedValues(), configuration.getValueSeparator());
416     }
417 
418     @Override
419     public Multimap<QualitativeValueDTO, QualitativeValueDTO> buildQualitativeValueMapFromPreconditions(Collection<PreconditionRuleDTO> preconditions) {
420         Multimap<QualitativeValueDTO, QualitativeValueDTO> multimap = HashMultimap.create();
421         for (PreconditionRuleDTO precondition : preconditions) {
422 
423             List<QualitativeValueDTO> baseValues = referentialService.getQualitativeValues(
424                 DaliBeans.getIntegerSetFromString(precondition.getBaseRule().getAllowedValues(), configuration.getValueSeparator()));
425             for (QualitativeValueDTO baseValue : baseValues) {
426                 multimap.putAll(
427                     baseValue,
428                     referentialService.getQualitativeValues(
429                         DaliBeans.getIntegerSetFromString(precondition.getUsedRule().getAllowedValues(), configuration.getValueSeparator()))
430                 );
431             }
432         }
433         return multimap;
434 
435     }
436 
437     @Override
438     public void buildPreconditionsFromQualitativeValueMap(ControlRuleDTO rule, Multimap<QualitativeValueDTO, QualitativeValueDTO> multimap) {
439 
440         Map<String, PreconditionRuleDTO> existingRulePreconditionsByBaseValues = getRulePreconditionsByBaseValues(rule);
441 
442         MutableInt suffixIndex = getUniqueMutableIndex();
443 
444         for (QualitativeValueDTO baseValue : multimap.keySet()) {
445             PreconditionRuleDTO precondition = existingRulePreconditionsByBaseValues.remove(baseValue.getId().toString());
446             if (precondition == null) {
447                 precondition = createPreconditionRule(rule);
448             }
449             precondition.setActive(rule.isActive());
450             precondition.setName(rule.getCode());
451 
452             ControlRuleDTO baseRule = precondition.getBaseRule();
453             if (baseRule == null) {
454                 baseRule = createQualitativeControlRule(rule, suffixIndex);
455                 baseRule.addRulePmfms(cloneRulePmfm(rule.getRulePmfms(0)));
456                 baseRule.setAllowedValues(baseValue.getId().toString());
457                 precondition.setBaseRule(baseRule);
458             }
459 
460             ControlRuleDTO usedRule = precondition.getUsedRule();
461             if (usedRule == null) {
462                 usedRule = createQualitativeControlRule(rule, suffixIndex);
463                 usedRule.addRulePmfms(cloneRulePmfm(rule.getRulePmfms(1)));
464                 precondition.setUsedRule(usedRule);
465             }
466             usedRule.setAllowedValues(DaliBeans.joinIds(multimap.get(baseValue), configuration.getValueSeparator()));
467 
468         }
469 
470         // remove unused preconditions
471         rule.removeAllPreconditions(existingRulePreconditionsByBaseValues.values());
472     }
473 
474     @Override
475     public CombinationList buildAndFactorizeAllowedValues(PresetVO preset, Multimap<Integer, PreconditionRuleDTO> preconditionRulesByPmfmId, int maxCombinationCount) {
476         // build allowed values by target pmfm id
477         AllowedQualitativeValuesMap allowedQualitativeValuesMap = new AllowedQualitativeValuesMap();
478         long start = System.currentTimeMillis();
479         for (Integer pmfmId : preset.getPmfmIds()) {
480             for (Integer qvId : preset.getQualitativeValueIds(pmfmId)) {
481                 buildAllowedValuesByPmfmId(
482                     pmfmId,
483                     DaliBeans.newDummyBaseReferentialBean(qvId),
484                     preset.getPmfmIds(),
485                     preconditionRulesByPmfmId.get(pmfmId),
486                     allowedQualitativeValuesMap
487                 );
488             }
489         }
490         if (LOG.isDebugEnabled())
491             LOG.debug(String.format("allowed values built in %s", Times.durationToString(System.currentTimeMillis() - start)));
492 
493         // factorize all combinations with a predicate ti filter allowed values only
494         // and a maximum of combinations count (1000 by default)
495         start = System.currentTimeMillis();
496         CombinationList allPmfmValues = Factorizations.factorize(
497             preset.getPmfmPresets(),
498             new QualitativeValuesCombinationValidator(maxCombinationCount, allowedQualitativeValuesMap));
499         if (LOG.isDebugEnabled())
500             LOG.debug(String.format("combinations built in %s", Times.durationToString(System.currentTimeMillis() - start)));
501 
502         return allPmfmValues;
503     }
504 
505     @Override
506     public CombinationList buildAndFactorizeAllowedValues(Collection<PmfmPresetDTO> pmfmPresets, Multimap<Integer, PreconditionRuleDTO> preconditionRulesByPmfmId, int maxCombinationCount) {
507         // Convert the PmfmPresetDTO collection to a PresetVO
508         return buildAndFactorizeAllowedValues(toPresetVO(pmfmPresets), preconditionRulesByPmfmId, maxCombinationCount);
509     }
510 
511     private PresetVO toPresetVO(Collection<PmfmPresetDTO> pmfmPresets) {
512         PresetVO preset = new PresetVO();
513         pmfmPresets.forEach(
514             pmfmPreset -> preset.addPmfmPreset(
515                 pmfmPreset.getPmfm().getId(),
516                 pmfmPreset.getQualitativeValues().stream().map(QualitativeValueDTO::getId).collect(Collectors.toList())));
517         return preset;
518     }
519 
520     private Map<String, PreconditionRuleDTO> getRulePreconditionsByBaseValues(ControlRuleDTO rule) {
521         return DaliBeans.mapByProperty(rule.getPreconditions(), PreconditionRuleDTO.PROPERTY_BASE_RULE + "." + ControlRuleDTO.PROPERTY_ALLOWED_VALUES);
522     }
523 
524     private PreconditionRuleDTO createPreconditionRule(ControlRuleDTO parentRule) {
525         PreconditionRuleDTO precondition = DaliBeanFactory.newPreconditionRuleDTO();
526         precondition.setRule(parentRule);
527         precondition.setBidirectional(true); // by default
528         parentRule.addPreconditions(precondition);
529         return precondition;
530     }
531 
532     private ControlRuleDTO createQualitativeControlRule(ControlRuleDTO parentRule, MutableInt suffixIndex) {
533         ControlRuleDTO rule = DaliBeanFactory.newControlRuleDTO();
534         rule.setCode(getNextRuleCode(parentRule.getCode(), suffixIndex));
535         rule.setFunction(ControlFunctionValues.IS_AMONG.toFunctionDTO());
536         rule.setControlElement(ControlElementValues.MEASUREMENT.toControlElementDTO());
537         rule.setControlFeature(ControlFeatureMeasurementValues.QUALITATIVE_VALUE.toControlFeatureDTO());
538         rule.setActive(false); // must be false
539         return rule;
540     }
541 
542     private RulePmfmDTO cloneRulePmfm(RulePmfmDTO rulePmfm) {
543         RulePmfmDTO result = DaliBeanFactory.newRulePmfmDTO();
544         result.setPmfm(DaliBeans.clone(rulePmfm.getPmfm()));
545         return result;
546     }
547 
548     private void buildPmfmInformation(List<ControlRuleDTO> controlRules) {
549         if (controlRules != null) {
550             for (ControlRuleDTO preconditionedRule : controlRules) {
551                 // Affect all pmfmId with the real pmfm Id from the referential: Allow better handling in UI
552                 for (RulePmfmDTO rulePmfm : preconditionedRule.getRulePmfms()) {
553                     rulePmfm.getPmfm().setId(referentialService.getUniquePmfmIdFromPmfm(rulePmfm.getPmfm()));
554                 }
555                 for (PreconditionRuleDTO precondition : preconditionedRule.getPreconditions()) {
556                     precondition.getBaseRule().getRulePmfms(0).getPmfm().setId(referentialService.getUniquePmfmIdFromPmfm(precondition.getBaseRule().getRulePmfms(0).getPmfm()));
557                     precondition.getUsedRule().getRulePmfms(0).getPmfm().setId(referentialService.getUniquePmfmIdFromPmfm(precondition.getUsedRule().getRulePmfms(0).getPmfm()));
558                 }
559             }
560         }
561     }
562 
563     /**
564      * rule code = <rule_list_code>_<auto_incremental_number>
565      *
566      * @param ruleList the rule list
567      * @return the next rule code
568      */
569     private String getNextRuleCode(RuleListDTO ruleList) {
570         Assert.notNull(ruleList);
571         int maxSuffix = 0;
572 
573         if (CollectionUtils.isNotEmpty(ruleList.getControlRules())) {
574 
575             for (ControlRuleDTO r : ruleList.getControlRules()) {
576                 String ruleCode = r.getCode();
577 
578                 if (StringUtils.isNotBlank(ruleCode)
579                     && ruleCode.startsWith(ruleList.getCode())) {
580                     int lastIndex = ruleCode.lastIndexOf('_');
581                     if (lastIndex == -1) {
582                         lastIndex = ruleCode.lastIndexOf('-');
583                     }
584 
585                     if (lastIndex != -1 && lastIndex != ruleCode.length() - 1) {
586                         String suffix = ruleCode.substring(lastIndex + 1);
587                         try {
588                             maxSuffix = Math.max(maxSuffix, Integer.parseInt(suffix));
589                         } catch (NumberFormatException e) {
590                             // skip this rule code
591                         }
592                     }
593                 }
594             }
595         }
596 
597         // return (max + 1)
598         return String.format("%s_%d", ruleList.getCode(), maxSuffix + 1);
599     }
600 
601     @Override
602     public MutableInt getUniqueMutableIndex() {
603         return new MutableInt(System.currentTimeMillis() / 1000);
604     }
605 
606     /**
607      * @param targetName  the target rule name
608      * @param suffixIndex incremental suffix
609      * @return the next non existing rule code
610      */
611     @Override
612     public String getNextRuleCode(String targetName, MutableInt suffixIndex) {
613         String ruleCode;
614         do {
615             // trim to 40 characters
616             String suffix = Integer.toString(suffixIndex.getAndIncrement());
617             int suffixLength = suffix.length() + 1;
618             int totalLength = targetName.length() + suffixLength;
619             if (totalLength > 40) {
620                 targetName = targetName.substring(0, 40 - suffixLength);
621             }
622             ruleCode = String.format("%s_%s", targetName, suffix);
623         } while (ruleCodeExists(ruleCode));
624         return ruleCode;
625     }
626 
627 }