001/*
002 *  Copyright 2024 Anyware Services
003 *
004 *  Licensed under the Apache License, Version 2.0 (the "License");
005 *  you may not use this file except in compliance with the License.
006 *  You may obtain a copy of the License at
007 *
008 *      http://www.apache.org/licenses/LICENSE-2.0
009 *
010 *  Unless required by applicable law or agreed to in writing, software
011 *  distributed under the License is distributed on an "AS IS" BASIS,
012 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 *  See the License for the specific language governing permissions and
014 *  limitations under the License.
015 */
016package org.ametys.cms.data.holder;
017
018import java.util.Arrays;
019import java.util.List;
020import java.util.Map;
021import java.util.Optional;
022import java.util.Set;
023
024import org.apache.avalon.framework.service.ServiceException;
025import org.apache.avalon.framework.service.ServiceManager;
026import org.apache.avalon.framework.service.Serviceable;
027import org.apache.commons.lang3.StringUtils;
028import org.apache.commons.lang3.tuple.Pair;
029
030import org.ametys.cms.data.ContentValue;
031import org.ametys.cms.data.ametysobject.ModelAwareDataAwareAmetysObject;
032import org.ametys.cms.model.ContentElementDefinition;
033import org.ametys.cms.search.model.SystemPropertyExtensionPoint;
034import org.ametys.plugins.repository.data.holder.DataHolder;
035import org.ametys.plugins.repository.data.holder.ModelAwareDataHolder;
036import org.ametys.plugins.repository.data.holder.group.ModelAwareRepeater;
037import org.ametys.plugins.repository.data.holder.impl.DataHolderHelper;
038import org.ametys.plugins.repository.data.holder.values.SynchronizableRepeater;
039import org.ametys.plugins.repository.data.holder.values.SynchronizableValue;
040import org.ametys.plugins.repository.data.holder.values.SynchronizableValue.Mode;
041import org.ametys.plugins.repository.data.holder.values.SynchronizationContext;
042import org.ametys.plugins.repository.data.holder.values.UntouchedValue;
043import org.ametys.plugins.repository.model.CompositeDefinition;
044import org.ametys.plugins.repository.model.RepeaterDefinition;
045import org.ametys.runtime.model.ModelHelper;
046import org.ametys.runtime.model.ModelItem;
047import org.ametys.runtime.model.ModelItemAccessor;
048import org.ametys.runtime.model.disableconditions.AbstractDisableConditionsEvaluator;
049import org.ametys.runtime.model.disableconditions.DisableCondition;
050import org.ametys.runtime.model.disableconditions.DisableConditions;
051import org.ametys.runtime.model.exception.BadItemTypeException;
052import org.ametys.runtime.model.exception.UndefinedItemPathException;
053import org.ametys.runtime.model.type.ElementType;
054
055/**
056 * Evaluator for {@link DisableConditions} on model aware {@link DataHolder}
057 * @param <T> Type of data holder holding the data to evaluate
058 */
059public class DataHolderDisableConditionsEvaluator<T extends ModelAwareDataHolder> extends AbstractDisableConditionsEvaluator<T> implements Serviceable
060{
061    /** The Avalon role */
062    public static final String ROLE = DataHolderDisableConditionsEvaluator.class.getName();
063    
064    /** The contextual parameter key for synchronization context */
065    public static final String SYNCHRONIZATION_CONTEXT_PARAMETER_KEY = "synchronizationContext";
066    
067    /** The contextual parameter key for ametys object */
068    protected static final String __AMETYS_OBJECT_PARAMETER_KEY = "ametysObject";
069    /** The contextual parameter key for old condition path */
070    protected static final String __OLD_CONDITION_PATH_PARAMETER_KEY = "oldCondtitionPath";
071    
072    /** The system property extension point */
073    protected SystemPropertyExtensionPoint _systemPropertyExtensionPoint;
074    
075    public void service(ServiceManager manager) throws ServiceException
076    {
077        _systemPropertyExtensionPoint = (SystemPropertyExtensionPoint) manager.lookup(SystemPropertyExtensionPoint.ROLE);
078    }
079    
080    @Override
081    protected ModelItem getModelItem(ModelItemAccessor modelItemAccessor, String conditionDataPath, Map<String, Object> contextualParameters) throws UndefinedItemPathException
082    {
083        String conditionPathLastSegment = conditionDataPath.contains(ModelItem.ITEM_PATH_SEPARATOR)
084                ? StringUtils.substring(conditionDataPath, conditionDataPath.lastIndexOf(ModelItem.ITEM_PATH_SEPARATOR))
085                : conditionDataPath;
086        
087        return _systemPropertyExtensionPoint.hasExtension(conditionPathLastSegment)
088                ? _systemPropertyExtensionPoint.getExtension(conditionPathLastSegment)
089                : super.getModelItem(modelItemAccessor, conditionDataPath, contextualParameters);
090        
091    }
092    
093    @Override
094    protected DefinitionAndValue _getConditionDefinitionAndValue(ModelItem definition, DisableCondition condition, String dataPath, Optional<String> oldDataPath, Map<String, Object> values, Optional<T> ametsyObject, Map<String, Object> contextualParameters)
095    {
096        if (ametsyObject.isPresent())
097        {
098            contextualParameters.put(__AMETYS_OBJECT_PARAMETER_KEY, ametsyObject.get());
099            
100            Optional<String> oldConditionAbsoluteDataPath = oldDataPath.map(path -> ModelHelper.getDisableConditionAbsolutePath(condition, path));
101            contextualParameters.put(__OLD_CONDITION_PATH_PARAMETER_KEY, oldConditionAbsoluteDataPath);
102        }
103
104        return super._getConditionDefinitionAndValue(definition, condition, dataPath, oldDataPath, values, ametsyObject, contextualParameters);
105    }
106    
107    /**
108     * {@inheritDoc}
109     * @throws BadItemTypeException if a value does not correspond to the model of given object
110     */
111    @Override
112    protected boolean containsValue(ModelItemAccessor modelItemAccessor, String conditionDataPath, Map<String, Object> values, Map<String, Object> contextualParameters) throws BadItemTypeException
113    {
114        String[] conditionDataPathSegments = StringUtils.split(conditionDataPath, ModelItem.ITEM_PATH_SEPARATOR);
115
116        ModelItem modelItem = ModelHelper.getModelItem(conditionDataPathSegments[0], List.of(modelItemAccessor));
117        if (conditionDataPathSegments.length == 1)
118        {
119            return _containsElementValue(modelItem, conditionDataPathSegments[0], values, contextualParameters);
120        }
121        else
122        {
123            String subConditionDataPath = StringUtils.join(conditionDataPathSegments, ModelItem.ITEM_PATH_SEPARATOR, 1, conditionDataPathSegments.length);
124            if (modelItem instanceof CompositeDefinition compositeDefinition)
125            {
126                return _doesCompositeContainValue(compositeDefinition, conditionDataPathSegments[0], subConditionDataPath, values, contextualParameters);
127            }
128            else if (modelItem instanceof RepeaterDefinition repeaterDefinition)
129            {
130                return _doesRepeaterContainValue(repeaterDefinition, conditionDataPathSegments[0], subConditionDataPath, values, contextualParameters);
131            }
132            else if (modelItem instanceof ContentElementDefinition contentElementDefinition)
133            {
134                return _containsElementValue(contentElementDefinition, conditionDataPathSegments[0], values, contextualParameters);
135            }
136            else
137            {
138                throw new BadItemTypeException("Unable to retrieve the value at the given path. the the segment '" + conditionDataPathSegments[0] + "' is not a model item accessor");
139            }
140        }
141    }
142    
143    /**
144     * Check if the values {@link Map} contains a value for the condition
145     * @param compositeDefinition the composite definition 
146     * @param compositeName the name of the composite
147     * @param subConditionDataPath the data path of the condition, relative to the composite
148     * @param values the values {@link Map}
149     * @param contextualParameters the contextual parameters
150     * @return <code>true</code> if there is a value (even empty) for the condition in the composite value, <code>false</code> otherwise
151     * @throws BadItemTypeException if the found value is not a composite
152     */
153    @SuppressWarnings("unchecked")
154    protected boolean _doesCompositeContainValue(CompositeDefinition compositeDefinition, String compositeName, String subConditionDataPath, Map<String, Object> values, Map<String, Object> contextualParameters)
155    {
156        if (!values.containsKey(compositeName))
157        {
158            // There is no corresponding value in the map
159            return false;
160        }
161        
162        Object value = values.get(compositeName);
163        if (value == null)
164        {
165            // An empty value has been found
166            return true;
167        }
168        
169        if (value instanceof UntouchedValue)
170        {
171            // If value is untouched, we should check in stored values
172            return false;
173        }
174        
175        if (value instanceof Map)
176        {
177            return containsValue(compositeDefinition, subConditionDataPath, (Map<String, Object>) value, contextualParameters);
178        }
179        else
180        {
181            throw new BadItemTypeException("Unable to retrieve the value at the given path. the value of the segment '" + compositeName + "' for composite is not a Map");
182        }
183    }
184    
185    /**
186     * Check if the values {@link Map} contains a value for the condition
187     * @param repeaterDefinition the repeater definition
188     * @param repeaterNameAndPositionSegment the segment of the repeater, containing repeater name and entry position
189     * @param subConditionDataPath the data path of the condition, relative to the repeater
190     * @param values the values {@link Map}
191     * @param contextualParameters the contextual parameters
192     * @return <code>true</code> if there is a value (even empty) for the condition in the repeater value, <code>false</code> otherwise
193     * @throws BadItemTypeException if the found value is not a repeater
194     */
195    @SuppressWarnings("unchecked")
196    protected boolean _doesRepeaterContainValue(RepeaterDefinition repeaterDefinition, String repeaterNameAndPositionSegment, String subConditionDataPath, Map<String, Object> values, Map<String, Object> contextualParameters) throws BadItemTypeException
197    {
198        Pair<String, Integer> repeaterNameAndEntryPosition = DataHolderHelper.getRepeaterNameAndEntryPosition(repeaterNameAndPositionSegment);
199        String repeaterName = repeaterNameAndEntryPosition.getLeft();
200        
201        if (!values.containsKey(repeaterName))
202        {
203            // There is no corresponding value in the map
204            return false;
205        }
206        
207        Object value = values.get(repeaterName);
208        if (value == null)
209        {
210            // An empty value has been found
211            return true;
212        }
213        
214        if (value instanceof UntouchedValue)
215        {
216            // If value is untouched, we should check in stored values
217            return false;
218        }
219        
220        if (value instanceof List || value instanceof SynchronizableRepeater)
221        {
222            Integer entryPosition = repeaterNameAndEntryPosition.getRight();
223            List<Map<String, Object>> entries = value instanceof List ? (List<Map<String, Object>>) value : ((SynchronizableRepeater) value).getEntries();
224            SynchronizableRepeater.Mode mode = value instanceof SynchronizableRepeater ? ((SynchronizableRepeater) value).getMode() : SynchronizableRepeater.Mode.REPLACE_ALL;
225            
226            if (mode == SynchronizableRepeater.Mode.REPLACE_ALL)
227            {
228                Map<String, Object> entry = entries.get(entryPosition - 1);
229                return containsValue(repeaterDefinition, subConditionDataPath, entry, contextualParameters);
230            }
231            else if (mode == SynchronizableRepeater.Mode.REPLACE)
232            {
233                int indexOfEntryInReplaceEntries = ((SynchronizableRepeater) value).getReplacePositions().indexOf(entryPosition);
234                if (indexOfEntryInReplaceEntries >= 0)
235                {
236                    Map<String, Object> entry = entries.get(indexOfEntryInReplaceEntries);
237                    return containsValue(repeaterDefinition, subConditionDataPath, entry, contextualParameters);
238                }
239                else
240                {
241                    return false;
242                }
243            }
244            else // mode == SynchronizableRepeater.Mode.APPEND
245            {
246                Set<Integer> removedEntries = ((SynchronizableRepeater) value).getRemovedEntries();
247                if (removedEntries.contains(entryPosition))
248                {
249                    // Non sense, we should not evaluate disable conditions on a data in a repeater entry that will be removed
250                    return true;
251                }
252                
253                ModelAwareRepeater repeater = null;
254                if (contextualParameters.containsKey(__AMETYS_OBJECT_PARAMETER_KEY))
255                {
256                    ModelAwareDataAwareAmetysObject ametysObject = (ModelAwareDataAwareAmetysObject) contextualParameters.get(__AMETYS_OBJECT_PARAMETER_KEY);
257                    repeater = ametysObject.getRepeater(repeaterDefinition.getPath());
258                }
259                
260                int actualRepeaterSize = repeater != null ? repeater.getSize() : 0;
261                if (removedEntries.size() > actualRepeaterSize)
262                {
263                    throw new IllegalArgumentException("Try to remove more entries than exist in repeater at path '" + repeaterDefinition.getPath() + "'.");
264                }
265                
266                int repeaterSizeAfterRemoving = actualRepeaterSize - removedEntries.size();
267                return entryPosition > repeaterSizeAfterRemoving;
268            }
269        }
270        else
271        {
272            throw new BadItemTypeException("Unable to retrieve the value at the given path. the value of the segment '" + repeaterNameAndPositionSegment + "' for repeater is not a List<Map>");
273        }
274    }
275    
276    /**
277     * Check if the values {@link Map} contains a value for the condition
278     * @param definition the definition of the condition element
279     * @param dataName the name of the condition element
280     * @param values the values {@link Map}
281     * @param contextualParameters the contextual parameters
282     * @return <code>true</code> if there is a value (even empty), <code>false</code> otherwise
283     */
284    protected boolean _containsElementValue(ModelItem definition, String dataName, Map<String, Object> values, Map<String, Object> contextualParameters)
285    {
286        if (!values.containsKey(dataName))
287        {
288            // There is no corresponding value in the map
289            return false;
290        }
291        
292        Object value = values.get(dataName);
293        if (value == null)
294        {
295            // An empty value has been found
296            return true;
297        }
298        
299        if (value instanceof UntouchedValue)
300        {
301            // If value is untouched, we should check in stored values
302            return false;
303        }
304        
305        SynchronizationContext synchronizationContext = (SynchronizationContext) contextualParameters.get(SYNCHRONIZATION_CONTEXT_PARAMETER_KEY);
306        return synchronizationContext == null
307                || !(_getValueFromSynchronizableValue(definition, value, synchronizationContext, contextualParameters) instanceof UntouchedValue);
308    }
309    
310    /**
311     * {@inheritDoc}
312     * @throws BadItemTypeException if a value does not correspond to the model of given object
313     */
314    @Override
315    protected Object getValueFromMap(ModelItemAccessor modelItemAccessor, String conditionDataPath, Map<String, Object> values, Map<String, Object> contextualParameters) throws BadItemTypeException
316    {
317        String[] conditionDataPathSegments = StringUtils.split(conditionDataPath, ModelItem.ITEM_PATH_SEPARATOR);
318        
319        ModelItem modelItem = ModelHelper.getModelItem(conditionDataPathSegments[0], List.of(modelItemAccessor));
320        if (conditionDataPathSegments.length == 1)
321        {
322            return _getElementValueFromMap(modelItem, conditionDataPathSegments[0], values, contextualParameters);
323        }
324        else
325        {
326            String subConditionDataPath = StringUtils.join(conditionDataPathSegments, ModelItem.ITEM_PATH_SEPARATOR, 1, conditionDataPathSegments.length);
327            if (modelItem instanceof CompositeDefinition compositeDefinition)
328            {
329                return _getValueFromComposite(compositeDefinition, conditionDataPathSegments[0], subConditionDataPath, values, contextualParameters);
330            }
331            else if (modelItem instanceof RepeaterDefinition repeaterDefinition)
332            {
333                return _getValueFromRepeater(repeaterDefinition, conditionDataPathSegments[0], subConditionDataPath, values, contextualParameters);
334            }
335            else if (modelItem instanceof ContentElementDefinition contentElementDefinition)
336            {
337                return _getValueFromContentValue(contentElementDefinition, conditionDataPathSegments[0], subConditionDataPath, values, contextualParameters);
338            }
339            else
340            {
341                throw new BadItemTypeException("Unable to retrieve the value at the given path. the the segment '" + conditionDataPathSegments[0] + "' is not a model item accessor");
342            }
343        }
344    }
345    
346    /**
347     * Retrieves the condition value in the given composite, from the values {@link Map}
348     * @param compositeDefinition the composite definition 
349     * @param compositeName the name of the composite
350     * @param subConditionDataPath the data path of the condition, relative to the composite
351     * @param values the values {@link Map}
352     * @param contextualParameters the contextual parameters
353     * @return the condition value if found in the values {@link Map}
354     * @throws BadItemTypeException if the found value is not a composite
355     */
356    @SuppressWarnings("unchecked")
357    protected Object _getValueFromComposite(CompositeDefinition compositeDefinition, String compositeName, String subConditionDataPath, Map<String, Object> values, Map<String, Object> contextualParameters) throws BadItemTypeException
358    {
359        Object value = values.get(compositeName);
360        if (value == null)
361        {
362            return null;
363        }
364        
365        if (value instanceof Map)
366        {
367            return getValueFromMap(compositeDefinition, subConditionDataPath, (Map<String, Object>) value, contextualParameters);
368        }
369        else
370        {
371            throw new BadItemTypeException("Unable to retrieve the value at the given path. the value of the segment '" + compositeName + "' for composite is not a Map");
372        }
373    }
374    
375    /**
376     * Retrieves the condition value in the given repeater, from the values {@link Map}
377     * @param repeaterDefinition the repeater definition 
378     * @param repeaterNameAndPositionSegment the segment of the repeater, containing repeater name and entry position
379     * @param subConditionDataPath the data path of the condition, relative to the repeater
380     * @param values the values {@link Map}
381     * @param contextualParameters the contextual parameters
382     * @return the condition value if found in the values {@link Map}
383     * @throws BadItemTypeException if the found value is not a repeater
384     */
385    @SuppressWarnings("unchecked")
386    protected Object _getValueFromRepeater(RepeaterDefinition repeaterDefinition, String repeaterNameAndPositionSegment, String subConditionDataPath, Map<String, Object> values, Map<String, Object> contextualParameters) throws BadItemTypeException
387    {
388        Pair<String, Integer> repeaterNameAndEntryPosition = DataHolderHelper.getRepeaterNameAndEntryPosition(repeaterNameAndPositionSegment);
389        String repeaterName = repeaterNameAndEntryPosition.getLeft();
390        
391        Object value = values.get(repeaterName);
392        if (value == null)
393        {
394            return null;
395        }
396        
397        if (value instanceof List || value instanceof SynchronizableRepeater)
398        {
399            Integer entryPosition = repeaterNameAndEntryPosition.getRight();
400            List<Map<String, Object>> entries = value instanceof List ? (List<Map<String, Object>>) value : ((SynchronizableRepeater) value).getEntries();
401            SynchronizableRepeater.Mode mode = value instanceof SynchronizableRepeater ? ((SynchronizableRepeater) value).getMode() : SynchronizableRepeater.Mode.REPLACE_ALL;
402            
403            if (mode == SynchronizableRepeater.Mode.REPLACE_ALL)
404            {
405                Map<String, Object> entry = entries.get(entryPosition - 1);
406                return getValueFromMap(repeaterDefinition, subConditionDataPath, entry, contextualParameters);
407            }
408            else if (mode == SynchronizableRepeater.Mode.REPLACE)
409            {
410                int indexOfEntryInReplaceEntries = ((SynchronizableRepeater) value).getReplacePositions().indexOf(entryPosition);
411                Map<String, Object> entry = entries.get(indexOfEntryInReplaceEntries);
412                return getValueFromMap(repeaterDefinition, subConditionDataPath, entry, contextualParameters);
413            }
414            else // mode == SynchronizableRepeater.Mode.APPEND
415            {
416                Set<Integer> removedEntries = ((SynchronizableRepeater) value).getRemovedEntries();
417                if (removedEntries.contains(entryPosition))
418                {
419                    // Non sense, we should not evaluate disable conditions on a data in a repeater entry that will be removed
420                    return null;
421                }
422                
423                ModelAwareRepeater repeater = null;
424                if (contextualParameters.containsKey(__AMETYS_OBJECT_PARAMETER_KEY))
425                {
426                    ModelAwareDataAwareAmetysObject ametysObject = (ModelAwareDataAwareAmetysObject) contextualParameters.get(__AMETYS_OBJECT_PARAMETER_KEY);
427                    repeater = ametysObject.getRepeater(repeaterDefinition.getPath());
428                }
429                
430                int actualRepeaterSize = repeater != null ? repeater.getSize() : 0;
431                
432                int repeaterSizeAfterRemoving = actualRepeaterSize - removedEntries.size();
433                int positionInAppendedEntries = entryPosition - repeaterSizeAfterRemoving;
434                int indexInAppendedEntries = positionInAppendedEntries - 1;
435                Map<String, Object> entry = entries.get(indexInAppendedEntries);
436                return getValueFromMap(repeaterDefinition, subConditionDataPath, entry, contextualParameters);
437            }
438        }
439        else
440        {
441            throw new BadItemTypeException("Unable to retrieve the value at the given path. the value of the segment '" + repeaterNameAndPositionSegment + "' for repeater is not a List<Map>");
442        }
443    }
444    
445    /**
446     * Retrieves the condition value in the given content value, from the values {@link Map}
447     * @param contentElementDefinition the content element definition 
448     * @param dataName the name of the content data
449     * @param subConditionDataPath the data path of the condition, relative to the content value
450     * @param values the values {@link Map}
451     * @param contextualParameters the contextual parameters
452     * @return the condition value if found in the values {@link Map}
453     */
454    protected Object _getValueFromContentValue(ContentElementDefinition contentElementDefinition, String dataName, String subConditionDataPath, Map<String, Object> values, Map<String, Object> contextualParameters)
455    {
456        ElementType<ContentValue> type = contentElementDefinition.getType();
457        Object contentValue = _getElementValueFromMap(contentElementDefinition, dataName, values, contextualParameters);
458        
459        if (contentValue == null)
460        {
461            return null;
462        }
463        
464        if (contentValue.getClass().isArray())
465        {
466            return Arrays.stream((Object[]) contentValue)
467                         .map(type::castValue)
468                         .map(ContentValue::getContent)
469                         .map(content -> content.getValue(subConditionDataPath))
470                         .toArray();
471        }
472
473        return Optional.of(contentValue)
474                       .map(type::castValue)
475                       .map(ContentValue::getContent)
476                       .map(content -> content.getValue(subConditionDataPath))
477                       .orElse(null);
478    }
479    
480    /**
481     * Retrieves the condition value from the values {@link Map}
482     * @param definition the definition of the condition element
483     * @param dataName the name of the condition element
484     * @param values the values {@link Map}
485     * @param contextualParameters the contextual parameters
486     * @return the condition value if found in the values {@link Map}
487     */
488    protected Object _getElementValueFromMap(ModelItem definition, String dataName, Map<String, Object> values, Map<String, Object> contextualParameters)
489    {
490        Object value = values.get(dataName);
491        if (value == null)
492        {
493            return null;
494        }
495        
496        Object valueFromSyncValue = value;
497        SynchronizationContext synchronizationContext = (SynchronizationContext) contextualParameters.get(SYNCHRONIZATION_CONTEXT_PARAMETER_KEY);
498        if (synchronizationContext != null)
499        {
500            valueFromSyncValue = _getValueFromSynchronizableValue(definition, value, synchronizationContext, contextualParameters);
501        
502            if (valueFromSyncValue == null
503                || value instanceof SynchronizableValue syncValue && syncValue.getMode() == Mode.REMOVE)
504            {
505                return null;
506            }
507        }
508        
509        return valueFromSyncValue;
510    }
511    
512    /**
513     * Returns the value of the synchronized value corresponding of the given value
514     * @param definition the definition of the condition element
515     * @param value the value
516     * @param synchronizationContext the synchronization context
517     * @param contextualParameters the contextual parameters
518     * @return the value of the synchronized value
519     */
520    protected Object _getValueFromSynchronizableValue(ModelItem definition, Object value, SynchronizationContext synchronizationContext, Map<String, Object> contextualParameters)
521    {
522        Object valueFromSyncValue = value instanceof SynchronizableValue synchronizableValue ? synchronizableValue.getLocalValue() : value;
523        if (contextualParameters.containsKey(__AMETYS_OBJECT_PARAMETER_KEY))
524        {
525            ModelAwareDataAwareAmetysObject ametysObject = (ModelAwareDataAwareAmetysObject) contextualParameters.get(__AMETYS_OBJECT_PARAMETER_KEY);
526            @SuppressWarnings("unchecked")
527            Optional<String> oldConditionDataPath = (Optional<String>) contextualParameters.get(__OLD_CONDITION_PATH_PARAMETER_KEY);
528            valueFromSyncValue = DataHolderHelper.getValueFromSynchronizableValue(value, ametysObject, definition, oldConditionDataPath, synchronizationContext);
529        }
530        
531        return valueFromSyncValue;
532    }
533
534    @Override
535    protected Object getStoredValue(String conditionDataPath, Optional<String> oldConditionDataPath, T ametysObject, Map<String, Object> contextualParameters)
536    {
537        String dataPath = oldConditionDataPath.orElse(conditionDataPath);
538        return ametysObject.getValue(dataPath, true);
539    }
540}