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}