001/*
002 *  Copyright 2018 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.plugins.repository.data.holder;
017
018import java.util.Collection;
019import java.util.Map;
020import java.util.Optional;
021
022import org.xml.sax.ContentHandler;
023import org.xml.sax.SAXException;
024
025import org.ametys.plugins.repository.data.UnknownDataException;
026import org.ametys.plugins.repository.data.external.ExternalizableDataProvider.ExternalizableDataStatus;
027import org.ametys.plugins.repository.data.holder.group.ModelAwareComposite;
028import org.ametys.plugins.repository.data.holder.group.Repeater;
029import org.ametys.plugins.repository.data.holder.impl.DataHolderHelper;
030import org.ametys.plugins.repository.data.holder.values.SynchronizationContext;
031import org.ametys.plugins.repository.data.type.ModelItemTypeExtensionPoint;
032import org.ametys.plugins.repository.model.RepeaterDefinition;
033import org.ametys.runtime.model.ElementDefinition;
034import org.ametys.runtime.model.ModelHelper;
035import org.ametys.runtime.model.ModelItem;
036import org.ametys.runtime.model.ModelItemContainer;
037import org.ametys.runtime.model.ViewHelper;
038import org.ametys.runtime.model.ViewItemAccessor;
039import org.ametys.runtime.model.exception.BadDataPathCardinalityException;
040import org.ametys.runtime.model.exception.BadItemTypeException;
041import org.ametys.runtime.model.exception.NotUniqueTypeException;
042import org.ametys.runtime.model.exception.UndefinedItemPathException;
043import org.ametys.runtime.model.exception.UnknownTypeException;
044import org.ametys.runtime.model.type.DataContext;
045import org.ametys.runtime.model.type.ModelItemType;
046
047/**
048 * Interface for data containers with models
049 */
050public interface ModelAwareDataHolder extends DataHolder
051{
052    /** Suffix used for the alternative value */
053    public static final String ALTERNATIVE_SUFFIX = "__alt";
054    /** Suffix used for the status value */
055    public static final String STATUS_SUFFIX = "__status";
056    
057    @Override
058    public ModelAwareComposite getComposite(String compositePath) throws IllegalArgumentException, UnknownTypeException, BadItemTypeException, UndefinedItemPathException, BadDataPathCardinalityException;
059    
060    /**
061     * Retrieves the local composite at the given path
062     * @param compositePath path of the externalizable composite to retrieve
063     * @return the composite or <code>null</code> if not exists or is empty
064     * @throws IllegalArgumentException if the given composite path is null or empty
065     * @throws BadItemTypeException if the stored value at the given path is not a composite
066     * @throws UndefinedItemPathException if the given composite path is not defined by the model
067     * @throws BadDataPathCardinalityException if the definition of a part of the data path is multiple. Only the last part can be multiple
068     */
069    public ModelAwareComposite getLocalComposite(String compositePath) throws IllegalArgumentException, BadItemTypeException, UndefinedItemPathException, BadDataPathCardinalityException;
070    
071    /**
072     * Retrieves the external composite at the given path
073     * @param compositePath path of the externalizable composite to retrieve
074     * @return the composite or <code>null</code> if not exists or is empty
075     * @throws IllegalArgumentException if the given composite path is null or empty
076     * @throws BadItemTypeException if the stored value at the given path is not a composite
077     * @throws UndefinedItemPathException if the given composite path is not defined by the model
078     * @throws BadDataPathCardinalityException if the definition of a part of the data path is multiple. Only the last part can be multiple
079     */
080    public ModelAwareComposite getExternalComposite(String compositePath) throws IllegalArgumentException, BadItemTypeException, UndefinedItemPathException, BadDataPathCardinalityException;
081    
082    /**
083     * Retrieves the repeater at the given path
084     * @param repeaterPath path of the repeater to retrieve
085     * @return the repeater or <code>null</code> if not exists or is empty
086     * @throws IllegalArgumentException if the given repeater path is null or empty
087     * @throws BadItemTypeException if the stored value at the given path is not a repeater
088     * @throws UndefinedItemPathException if the given repeater path is not defined by the model
089     * @throws BadDataPathCardinalityException if the definition of a part of the data path is multiple. Only the last part can be multiple
090     */
091    public Repeater getRepeater(String repeaterPath) throws IllegalArgumentException, BadItemTypeException, UndefinedItemPathException, BadDataPathCardinalityException;
092    
093    /**
094     * Retrieves the local repeater at the given path
095     * @param repeaterPath path of the externalizable repeater to retrieve
096     * @return the repeater or <code>null</code> if not exists or is empty
097     * @throws IllegalArgumentException if the given repeater path is null or empty
098     * @throws BadItemTypeException if the stored value at the given path is not a repeater
099     * @throws UndefinedItemPathException if the given repeater path is not defined by the model
100     * @throws BadDataPathCardinalityException if the definition of a part of the data path is multiple. Only the last part can be multiple
101     */
102    public Repeater getLocalRepeater(String repeaterPath) throws IllegalArgumentException, BadItemTypeException, UndefinedItemPathException, BadDataPathCardinalityException;
103    
104    /**
105     * Retrieves the external repeater at the given path
106     * @param repeaterPath path of the externalizable repeater to retrieve
107     * @return the repeater or <code>null</code> if not exists or is empty
108     * @throws IllegalArgumentException if the given repeater path is null or empty
109     * @throws BadItemTypeException if the stored value at the given path is not a repeater
110     * @throws UndefinedItemPathException if the given repeater path is not defined by the model
111     * @throws BadDataPathCardinalityException if the definition of a part of the data path is multiple. Only the last part can be multiple
112     */
113    public Repeater getExternalRepeater(String repeaterPath) throws IllegalArgumentException, BadItemTypeException, UndefinedItemPathException, BadDataPathCardinalityException;
114    
115    /**
116     * Checks if there is a non empty local value for the data at the given path
117     * @param dataPath path of the externalizable data
118     * @return <code>true</code> if the data at the given path is defined by the model, if there is a non empty local value for the data and if the type of this value matches the type of the definition. <code>false</code> otherwise
119     * @throws IllegalArgumentException if the given data path is null or empty
120     * @throws BadDataPathCardinalityException if the definition of a part of the data path is multiple. Only the last part can be multiple
121     */
122    public boolean hasLocalValue(String dataPath) throws IllegalArgumentException, BadDataPathCardinalityException;
123    
124    /**
125     * Checks if there is a non empty external value for the data at the given path
126     * @param dataPath path of the externalizable data
127     * @return <code>true</code> if the data at the given path is defined by the model, if there is a non empty external value for the data and if the type of this value matches the type of the definition. <code>false</code> otherwise
128     * @throws IllegalArgumentException if the given data path is null or empty
129     * @throws BadDataPathCardinalityException if the definition of a part of the data path is multiple. Only the last part can be multiple
130     */
131    public boolean hasExternalValue(String dataPath) throws IllegalArgumentException, BadDataPathCardinalityException;
132    
133    @Override
134    public default boolean hasValue(String dataPath, String dataTypeId) throws IllegalArgumentException, UnknownTypeException, BadDataPathCardinalityException
135    {
136        return hasValue(dataPath);
137    }
138    
139    /**
140     * Checks if there is a local value for the data at the given path
141     * @param dataPath path of the externalizable data
142     * @return <code>true</code> if the data at the given path is defined by the model, if there is a local value for the data, even empty, and if the type of this value matches the type of the definition. <code>false</code> otherwise
143     * @throws IllegalArgumentException if the given data path is null or empty
144     * @throws BadDataPathCardinalityException if the definition of a part of the data path is multiple. Only the last part can be multiple
145     */
146    public boolean hasLocalValueOrEmpty(String dataPath) throws IllegalArgumentException, BadDataPathCardinalityException;
147    
148    /**
149     * Checks if there is an external value for the data at the given path
150     * @param dataPath path of the externalizable data
151     * @return <code>true</code> if the data at the given path is defined by the model, if there is an external value for the data, even empty, and if the type of this value matches the type of the definition. <code>false</code> otherwise
152     * @throws IllegalArgumentException if the given data path is null or empty
153     * @throws BadDataPathCardinalityException if the definition of a part of the data path is multiple. Only the last part can be multiple
154     */
155    public boolean hasExternalValueOrEmpty(String dataPath) throws IllegalArgumentException, BadDataPathCardinalityException;
156    
157    /**
158     * {@inheritDoc}
159     * @return the names of the data contained by this data holder and that are defined by the model
160     */
161    @Override
162    public Collection<String> getDataNames();
163    
164    /**
165     * Retrieves the local value of the data at the given path
166     * @param <T> type of the value to retrieve. The type of the value is defined by the model.
167     * @param dataPath path of the externalizable data
168     * @return the local value of the data or <code>null</code> if not exists or is empty. A typed array is returned for multiple empty data, and an empty string is returned for single empty string data
169     * @throws IllegalArgumentException if the given data path is null or empty
170     * @throws BadItemTypeException if the type defined by the model doesn't match the type of the stored value
171     * @throws UndefinedItemPathException if the given data path is not defined by the model
172     * @throws BadDataPathCardinalityException if the definition of a part of the data path is multiple. Only the last part can be multiple
173     */
174    public <T> T getLocalValue(String dataPath) throws IllegalArgumentException, BadItemTypeException, UndefinedItemPathException, BadDataPathCardinalityException;
175    
176    /**
177     * Retrieves the external value of the data at the given path
178     * @param <T> type of the value to retrieve. The type of the value is defined by the model.
179     * @param dataPath path of the externalizable data
180     * @return the external value of the data or <code>null</code> if not exists or is empty. A typed array is returned for multiple empty data, and an empty string is returned for single empty string data
181     * @throws IllegalArgumentException if the given data path is null or empty
182     * @throws BadItemTypeException if the type defined by the model doesn't match the type of the stored value
183     * @throws UndefinedItemPathException if the given data path is not defined by the model
184     * @throws BadDataPathCardinalityException if the definition of a part of the data path is multiple. Only the last part can be multiple
185     */
186    public <T> T getExternalValue(String dataPath) throws IllegalArgumentException, BadItemTypeException, UndefinedItemPathException, BadDataPathCardinalityException;
187    
188    /**
189     * Retrieves the value of the data at the given path if exists and is not empty, or the default value from the model
190     * @param <T> type of the value to retrieve. The type of the value is defined by the model.
191     * @param dataPath path of the data
192     * @return the value of the data, or the default value from the model if the value does not exist or is empty
193     * @throws IllegalArgumentException if the given data path is null or empty
194     * @throws BadItemTypeException if the type defined by the model doesn't match the type of the stored value
195     * @throws UndefinedItemPathException if the given data path is not defined by the model
196     * @throws BadDataPathCardinalityException if the definition of a part of the data path is multiple. Only the last part can be multiple
197     */
198    public <T> T getValueOrDefault(String dataPath) throws IllegalArgumentException, BadItemTypeException, UndefinedItemPathException, BadDataPathCardinalityException;
199    
200    /**
201     * {@inheritDoc}
202     * @param <T> type of the value to retrieve. The type of the value is defined by the model.
203     */
204    @Override
205    public default <T> T getValueOfType(String dataPath, String dataTypeId) throws IllegalArgumentException, UnknownTypeException, NotUniqueTypeException, BadItemTypeException, UndefinedItemPathException, BadDataPathCardinalityException
206    {
207        return getValue(dataPath);
208    }
209    
210    /**
211     * {@inheritDoc}
212     * @param <T> type of the value to retrieve. The type of the value is defined by the model.
213     */
214    @Override
215    public default <T> T getValueOfTypeOrDefault(String dataPath, String dataTypeId, T defaultValue) throws IllegalArgumentException, UnknownTypeException, NotUniqueTypeException, BadItemTypeException, UndefinedItemPathException, BadDataPathCardinalityException
216    {
217        return getValueOrDefault(dataPath, defaultValue);
218    }
219    
220    /**
221     * Retrieves the value of the data at the given path, allowing multi-valued segments in the path (not necessarily at the last segment)
222     * if there is no indicated entry for a repeater, the values of all the entries are retrieved
223     * if there are multiple values, all data are retrieved in one array
224     * @param <T> type of the value to retrieve
225     * @param dataPath path of the data
226     * @return the value of the data
227     * @throws IllegalArgumentException if the given data path is null or empty
228     * @throws BadItemTypeException if the type defined by the model doesn't match the type of the stored value
229     * @throws UndefinedItemPathException if the given data path is not defined by the model
230     * @throws BadDataPathCardinalityException if the managesMultiples boolean is <code>false</code> and the definition of a part of the data path is multiple. Only the last part can be multiple
231     */
232    public <T> T getValueWithMultiValuedPathSegments(String dataPath) throws IllegalArgumentException, BadItemTypeException, UndefinedItemPathException, BadDataPathCardinalityException;
233    
234    /**
235     * Retrieves the status of the externalizable data at the given path
236     * Warning: This method won't check that your data is externalizable. But there is no sense to call it with a non externalizable data
237     * @param dataPath path of the externalizable data
238     * @return the status of the externalizable data at the given path
239     * @throws IllegalArgumentException if the given data path is null or empty
240     * @throws BadItemTypeException if the given data path is composed of an item  with type defined by the model that doesn't match the type of the stored value
241     * @throws UndefinedItemPathException if the given data path is not defined by the model
242     * @throws BadDataPathCardinalityException if the definition of a part of the data path is multiple. Only the last part can be multiple
243     */
244    public ExternalizableDataStatus getStatus(String dataPath) throws IllegalArgumentException, BadItemTypeException, UndefinedItemPathException, BadDataPathCardinalityException;
245    
246    /**
247     * Checks if the definition of the element at the given path is multiple<br/>
248     * {@inheritDoc}
249     * @param dataPath path of the element. No matter if it is a definition or data path (with repeater entry positions)
250     * @return <code>true</code> if the element is multiple, <code>false</code> otherwise
251     */
252    @Override
253    public default boolean isMultiple(String dataPath) throws IllegalArgumentException, UnknownDataException, UnknownTypeException, NotUniqueTypeException, BadItemTypeException, UndefinedItemPathException
254    {
255        ModelItem item = getDefinition(dataPath);
256        if (item instanceof ElementDefinition)
257        {
258            return ((ElementDefinition) item).isMultiple();
259        }
260        else if (item instanceof RepeaterDefinition)
261        {
262            // If the given path represents a repeater , but with no specified entry, consider the data as multiple
263            return !DataHolderHelper.isRepeaterEntryPath(dataPath);
264        }
265        else
266        {
267            // Composites are not multiples
268            return false;
269        }
270    }
271    
272    /**
273     * Checks if the definition of the element at the given path is multiple<br/>
274     * {@inheritDoc}
275     * @param dataPath path of the element. No matter if it is a definition or data path (with repeater entry positions)
276     * @return <code>true</code> if the element is multiple, <code>false</code> otherwise
277     */
278    @Override
279    public default boolean isMultiple(String dataPath, String dataTypeId) throws IllegalArgumentException, UnknownDataException, UnknownTypeException, BadItemTypeException, UndefinedItemPathException
280    {
281        return isMultiple(dataPath);
282    }
283    
284    /**
285     * {@inheritDoc}
286     * @param dataPath path of the data. No matter if it is a definition or data path (with repeater entry positions)
287     */
288    @SuppressWarnings("unchecked")
289    @Override
290    public default <X extends ModelItemType> X getType(String dataPath) throws IllegalArgumentException, UnknownDataException, UnknownTypeException, NotUniqueTypeException, UndefinedItemPathException
291    {
292        return (X) getDefinition(dataPath).getType();
293    }
294
295    @Override
296    public default ModelItemTypeExtensionPoint getModelItemTypeExtensionPoint()
297    {
298        throw new UnsupportedOperationException("ModelAwareDataHolder does not need an extension point for managed types. The model itself defines the types of the data.");
299    }
300    
301    /**
302     * Retrieves the data holder's model
303     * @return the data holder's model
304     */
305    public Collection<? extends ModelItemContainer> getModel();
306    
307    /**
308     * Retrieves the definition of the data at the given path
309     * @param path path of the data. No matter if it is a definition or data path (with repeater entry positions)
310     * @return the definition of the data
311     * @throws IllegalArgumentException if the given path is null or empty
312     * @throws UndefinedItemPathException if the given path is not defined by the model
313     */
314    public default ModelItem getDefinition(String path) throws IllegalArgumentException, UndefinedItemPathException
315    {
316        return ModelHelper.getModelItem(path, getModel());
317    }
318    
319    /**
320     * Checks if there is a definition at the given path
321     * @param path path of the data. No matter if it is a definition or data path (with repeater entry positions)
322     * @return <code>true</code> if there is definition at the given path, <code>false</code> otherwise
323     * @throws IllegalArgumentException if the given path is null or empty
324     */
325    public default boolean hasDefinition(String path) throws IllegalArgumentException
326    {
327        try
328        {
329            getDefinition(path);
330            return true;
331        }
332        catch (UndefinedItemPathException e)
333        {
334            return false;
335        }
336    }
337    
338    /**
339     * {@inheritDoc}
340     * <br />Generates SAX events only for the data in the model
341     */
342    @Override
343    public default void dataToSAX(ContentHandler contentHandler) throws SAXException, UnknownTypeException, NotUniqueTypeException, BadItemTypeException
344    {
345        DataHolder.super.dataToSAX(contentHandler);
346    }
347    
348    /**
349     * {@inheritDoc}
350     * <br />Generates SAX events only for the data in the model
351     */
352    @Override
353    public default void dataToSAX(ContentHandler contentHandler, DataContext context) throws SAXException, UnknownTypeException, NotUniqueTypeException, BadItemTypeException
354    {
355        ViewItemAccessor viewItemAccessor = ViewHelper.createViewItemAccessor(getModel());
356        dataToSAX(contentHandler, viewItemAccessor, context);
357    }
358    
359    /**
360     * Generates SAX events for the data in the given view in the current {@link DataHolder}
361     * @param contentHandler the {@link ContentHandler} that will receive the SAX events
362     * @param viewItemAccessor the {@link ViewItemAccessor} referencing the items for which generate SAX events
363     * @throws SAXException if an error occurs during the SAX events generation
364     * @throws BadItemTypeException if the saxed value's type does not matches the stored data
365     */
366    public default void dataToSAX(ContentHandler contentHandler, ViewItemAccessor viewItemAccessor) throws SAXException, BadItemTypeException
367    {
368        dataToSAX(contentHandler, viewItemAccessor, DataContext.newInstance());
369    }
370    
371    /**
372     * Generates SAX events for the data in the given view in the current {@link DataHolder}
373     * @param contentHandler the {@link ContentHandler} that will receive the SAX events
374     * @param viewItemAccessor the {@link ViewItemAccessor} referencing the items for which generate SAX events
375     * @param context The context of the data to SAX
376     * @throws SAXException if an error occurs during the SAX events generation
377     * @throws BadItemTypeException if the saxed value's type does not matches the stored data
378     */
379    public void dataToSAX(ContentHandler contentHandler, ViewItemAccessor viewItemAccessor, DataContext context) throws SAXException, BadItemTypeException;
380    
381    /**
382     * Generates SAX events for the data in the given view in edition mode in the current {@link DataHolder}
383     * @param contentHandler the {@link ContentHandler} that will receive the SAX events
384     * @param viewItemAccessor the {@link ViewItemAccessor} referencing the items for which generate SAX events
385     * @param context The context of the data to SAX
386     * @throws SAXException if an error occurs during the SAX events generation
387     * @throws BadItemTypeException if the saxed value's type does not matches the stored data
388     */
389    public void dataToSAXForEdition(ContentHandler contentHandler, ViewItemAccessor viewItemAccessor, DataContext context) throws SAXException, BadItemTypeException;
390    
391    /**
392     * Convert the data in the model of the current {@link DataHolder}
393     * @return The data of the current {@link DataHolder} as JSON
394     * @throws BadItemTypeException if the value's type does not matches the stored data
395     */
396    
397    /**
398     * {@inheritDoc}
399     * <br />Only converts the data in the model
400     */
401    public default Map<String, Object> dataToJSON() throws BadItemTypeException
402    {
403        return DataHolder.super.dataToJSON();
404    }
405    
406    /**
407     * {@inheritDoc}
408     * <br />Only converts the data in the model
409     */
410    public default Map<String, Object> dataToJSON(DataContext context) throws BadItemTypeException
411    {
412        ViewItemAccessor viewItemAccessor = ViewHelper.createViewItemAccessor(getModel());
413        return dataToJSON(viewItemAccessor, context);
414    }
415    
416    /**
417     * Convert the data in the given view of the current {@link DataHolder}
418     * @param viewItemAccessor the {@link ViewItemAccessor} referencing the items to convert
419     * @return The data of the given view as JSON
420     * @throws BadItemTypeException if the value's type does not matches the stored data
421     */
422    public default Map<String, Object> dataToJSON(ViewItemAccessor viewItemAccessor) throws BadItemTypeException
423    {
424        return dataToJSON(viewItemAccessor, DataContext.newInstance());
425    }
426    
427    /**
428     * Convert the data in the given view of the current {@link DataHolder}
429     * @param viewItemAccessor the {@link ViewItemAccessor} referencing the items to convert
430     * @param context The context of the data to convert
431     * @return The data of the given view as JSON
432     * @throws BadItemTypeException if the value's type does not matches the stored data
433     */
434    public Map<String, Object> dataToJSON(ViewItemAccessor viewItemAccessor, DataContext context) throws BadItemTypeException;
435    
436    /**
437     * Convert the data in the given view in edition mode in the current {@link DataHolder}
438     * @param viewItemAccessor the {@link ViewItemAccessor} referencing the items to convert
439     * @param context The context of the data to convert
440     * @return The data of the given view as JSON
441     * @throws BadItemTypeException if the value's type does not matches the stored data
442     */
443    public Map<String, Object> dataToJSONForEdition(ViewItemAccessor viewItemAccessor, DataContext context) throws BadItemTypeException;
444    
445    /**
446     * Retrieves all data of this DataHolder as a typed-values Map.
447     * @return a Map containing all data.
448     * @throws BadItemTypeException if the value's type does not matches the stored data
449     */
450    public default Map<String, Object> dataToMap() throws BadItemTypeException
451    {
452        return dataToMap(DataContext.newInstance());
453    }
454    
455    /**
456     * Retrieves all data of this DataHolder as a typed-values Map.
457     * @param context The context of the data
458     * @return  a Map containing all data.
459     * @throws BadItemTypeException if the value's type does not matches the stored data
460     */
461    public default Map<String, Object> dataToMap(DataContext context) throws BadItemTypeException
462    {
463        ViewItemAccessor viewItemAccessor = ViewHelper.createViewItemAccessor(getModel());
464        return dataToMap(viewItemAccessor, context);
465    }
466    
467    /**
468     * Retrieves data of this DataHolder as a typed-values Map.
469     * @param viewItemAccessor the {@link ViewItemAccessor} referencing the items to include in the resulting Map
470     * @return a Map containing all data.
471     * @throws BadItemTypeException if the value's type does not matches the stored data
472     */
473    public default Map<String, Object> dataToMap(ViewItemAccessor viewItemAccessor) throws BadItemTypeException
474    {
475        return dataToMap(viewItemAccessor, DataContext.newInstance());
476    }
477    
478    /**
479     * Retrieves data of this DataHolder as a typed-values Map.
480     * @param viewItemAccessor the {@link ViewItemAccessor} referencing the items to include in the resulting Map
481     * @param context The context of the data
482     * @return a Map containing all data.
483     * @throws BadItemTypeException if the value's type does not matches the stored data
484     */
485    public Map<String, Object> dataToMap(ViewItemAccessor viewItemAccessor, DataContext context) throws BadItemTypeException;
486    
487    /**
488     * {@inheritDoc}
489     * @throws BadItemTypeException if the type defined by the model of one of the Map's key doesn't match the corresponding value
490     */
491    @Override
492    public default boolean hasDifferences(Map<String, Object> values) throws UnknownTypeException, NotUniqueTypeException, BadItemTypeException
493    {
494        ViewItemAccessor viewItemAccessor = DataHolderHelper.createViewItemAccessorFromValues(getModel(), values);
495        return hasDifferences(viewItemAccessor, values);
496    }
497    
498    /**
499     * Check if there are differences between the given values and the current ones
500     * @param values the values to check
501     * @param context the context of the synchronization
502     * @return <code>true</code> if there are differences, <code>false</code> otherwise
503     * @throws BadItemTypeException if the type defined by the model of one of the Map's key doesn't match the corresponding value
504     */
505    public default boolean hasDifferences(Map<String, Object> values, SynchronizationContext context) throws BadItemTypeException
506    {
507        ViewItemAccessor viewItemAccessor = DataHolderHelper.createViewItemAccessorFromValues(getModel(), values);
508        return hasDifferences(viewItemAccessor, values, context);
509    }
510    
511    /**
512     * Check if there are differences between the given values and the current ones
513     * @param viewItemAccessor The {@link ViewItemAccessor} for all items to check
514     * @param values the values to check
515     * @return <code>true</code> if there are differences, <code>false</code> otherwise
516     * @throws BadItemTypeException if the type defined by the model of one of the Map's key doesn't match the corresponding value
517     */
518    public boolean hasDifferences(ViewItemAccessor viewItemAccessor, Map<String, Object> values) throws BadItemTypeException;
519    
520    /**
521     * Check if there are differences between the given values and the current ones
522     * @param viewItemAccessor The {@link ViewItemAccessor} for all items to check
523     * @param values the values to check
524     * @param context the context of the synchronization
525     * @return <code>true</code> if there are differences, <code>false</code> otherwise
526     * @throws BadItemTypeException if the type defined by the model of one of the Map's key doesn't match the corresponding value
527     */
528    public boolean hasDifferences(ViewItemAccessor viewItemAccessor, Map<String, Object> values, SynchronizationContext context) throws BadItemTypeException;
529
530    /**
531     * Get the collection of model items where there are differences between the given values and the current ones
532     * @param values the values to check
533     * @return a collection of model items with differences
534     * @throws BadItemTypeException if the type defined by the model of one of the Map's key doesn't match the corresponding value
535     */
536    public default Collection<ModelItem> getDifferences(Map<String, Object> values) throws BadItemTypeException
537    {
538        ViewItemAccessor viewItemAccessor = DataHolderHelper.createViewItemAccessorFromValues(getModel(), values);
539        return getDifferences(viewItemAccessor, values);
540    }
541    
542    /**
543     * Get the collection of model items where there are differences between the given values and the current ones
544     * @param values the values to check
545     * @param context the context of the synchronization
546     * @return a collection of model items with differences
547     * @throws BadItemTypeException if the type defined by the model of one of the Map's key doesn't match the corresponding value
548     */
549    public default Collection<ModelItem> getDifferences(Map<String, Object> values, SynchronizationContext context) throws BadItemTypeException
550    {
551        ViewItemAccessor viewItemAccessor = DataHolderHelper.createViewItemAccessorFromValues(getModel(), values);
552        return getDifferences(viewItemAccessor, values, context);
553    }
554    
555    /**
556     * Get the collection of model items where there are differences between the given values and the current ones
557     * @param viewItemAccessor The {@link ViewItemAccessor} for all items to check
558     * @param values the values to check
559     * @return a collection of model items with differences
560     * @throws BadItemTypeException if the type defined by the model of one of the Map's key doesn't match the corresponding value
561     */
562    public Collection<ModelItem> getDifferences(ViewItemAccessor viewItemAccessor, Map<String, Object> values) throws BadItemTypeException;
563
564    /**
565     * Get the collection of model items where there are differences between the given values and the current ones
566     * @param viewItemAccessor The {@link ViewItemAccessor} for all items to check
567     * @param values the values to check
568     * @param context the context of the synchronization
569     * @return a collection of model items with differences
570     * @throws BadItemTypeException if the type defined by the model of one of the Map's key doesn't match the corresponding value
571     */
572    public Collection<ModelItem> getDifferences(ViewItemAccessor viewItemAccessor, Map<String, Object> values, SynchronizationContext context) throws BadItemTypeException;
573    
574    @Override
575    public Optional<? extends ModelAwareDataHolder> getParentDataHolder();
576    
577    @Override
578    public ModelAwareDataHolder getRootDataHolder();
579}