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.holder.group.Composite;
027import org.ametys.plugins.repository.data.holder.impl.DataHolderHelper;
028import org.ametys.plugins.repository.data.repositorydata.RepositoryData;
029import org.ametys.plugins.repository.data.type.ModelItemTypeExtensionPoint;
030import org.ametys.runtime.model.exception.BadDataPathCardinalityException;
031import org.ametys.runtime.model.exception.BadItemTypeException;
032import org.ametys.runtime.model.exception.NotUniqueTypeException;
033import org.ametys.runtime.model.exception.UndefinedItemPathException;
034import org.ametys.runtime.model.exception.UnknownTypeException;
035import org.ametys.runtime.model.type.DataContext;
036import org.ametys.runtime.model.type.ModelItemType;
037
038/**
039 * Interface for data containers
040 */
041public interface DataHolder
042{
043    /**
044     * Retrieves the composite at the given path
045     * @param compositePath path of the composite to retrieve
046     * @return the composite or <code>null</code> if not exists or is empty
047     * @throws IllegalArgumentException if the given composite path is null or empty
048     * @throws UnknownTypeException if the type composite is not available for this data holder
049     * @throws BadItemTypeException if the stored value at the given path is not a composite
050     * @throws UndefinedItemPathException if the data holder has a model and the given composite path is not defined by this model
051     * @throws BadDataPathCardinalityException if the data holder has a model and the definition of a part of the data path is multiple. Only the last part can be multiple
052     */
053    public Composite getComposite(String compositePath) throws IllegalArgumentException, UnknownTypeException, BadItemTypeException, UndefinedItemPathException, BadDataPathCardinalityException;
054    
055    /**
056     * Checks if there is a non empty value for the data at the given path
057     * @param dataPath path of the data
058     * @return <code>true</code> if there is a non empty value for the data. If the data holder has a model and the data at the given path is defined by the model and the type of this value matches the type of the definition. <code>false</code> otherwise
059     * @throws IllegalArgumentException if the given data path is null or empty
060     * @throws BadDataPathCardinalityException if the data holder has a model and the definition of a part of the data path is multiple. Only the last part can be multiple
061     * 
062     * 
063     */
064    public boolean hasValue(String dataPath) throws IllegalArgumentException, BadDataPathCardinalityException;
065    
066    /**
067     * Checks if there is a non empty value for the data at the given path
068     * @param dataPath path of the data
069     * @param dataTypeId type identifier of the data
070     * @return <code>true</code> if there is a non empty value for the data. If the data holder has a model and the data at the given path is defined by the model and the type of this value matches the type of the definition. <code>false</code> otherwise
071     * @throws IllegalArgumentException if the given data path is null or empty
072     * @throws UnknownTypeException if the given type isn't available for this data holder's type extension point
073     * @throws BadDataPathCardinalityException if the data holder has a model and the definition of a part of the data path is multiple. Only the last part can be multiple
074     * 
075     */
076    public boolean hasValue(String dataPath, String dataTypeId) throws IllegalArgumentException, UnknownTypeException, BadDataPathCardinalityException;
077    
078    /**
079     * Checks if there is a value, even empty, for the data at the given path
080     * @param dataPath path of the data
081     * @return <code>true</code> if there is value, even empty, for the data. If the data holder has a model and the data at the given path is defined by the model and the type of this value matches the type of the definition. <code>false</code> otherwise
082     * @throws IllegalArgumentException if the given data path is null or empty
083     * @throws BadDataPathCardinalityException if the data holder has a model and the definition of a part of the data path is multiple. Only the last part can be multiple
084     * 
085     */
086    public boolean hasValueOrEmpty(String dataPath) throws IllegalArgumentException, BadDataPathCardinalityException;
087    
088    /**
089     * Retrieves the names of data contained by this data holder
090     * Retrieves only the data at first level, does not check composite data 
091     * @return the names of all data contained by this data holder
092     */
093    public Collection<String> getDataNames();
094    
095    /**
096     * Retrieves the value of the data at the given path
097     * @param <T> type of the value to retrieve. If the data holder has a model, the type of the value is defined by this model. Otherwise, the type of the value will be deduced from the stored data. In some cases, the type can be wrong. For example, it is impossible to know if a stored date is a date or a date time
098     * @param dataPath path of the data
099     * @return the 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
100     * @throws IllegalArgumentException if the given data path is null or empty
101     * @throws UnknownTypeException if there is no compatible type with the data at the given data path
102     * @throws NotUniqueTypeException if there are many compatible types (there is no way to determine which type is the good one)
103     * @throws BadItemTypeException if the data holder has a model and the type defined by the model doesn't match the type of the stored value, or if value at the sub data path of the given one is not a composite
104     * @throws UndefinedItemPathException if the data holder has a model and the given data path is not defined by this model
105     * @throws BadDataPathCardinalityException if the data holder has a model and the definition of a part of the data path is multiple. Only the last part can be multiple
106     */
107    public <T> T getValue(String dataPath) throws IllegalArgumentException, UnknownTypeException, NotUniqueTypeException, BadItemTypeException, UndefinedItemPathException, BadDataPathCardinalityException;
108    
109    /**
110     * Retrieves the value of the data at the given path if exists and is not empty, or the given default value
111     * @param <T> type of the value to retrieve. If the data holder has a model, the type of the value is defined by this model. Otherwise, the type of the value will be deduced from the stored data. In some cases, the type can be wrong. For example, it is impossible to know if a stored date is a date or a date time
112     * @param dataPath path of the data
113     * @param defaultValue default value
114     * @return the value of the data, or the given default value if the value does not exist or is empty
115     * @throws IllegalArgumentException if the given data path is null or empty
116     * @throws UnknownTypeException if there is no compatible type with the data at the given data path
117     * @throws NotUniqueTypeException if there are many compatible types (there is no way to determine which type is the good one)
118     * @throws BadItemTypeException if value at the sub data path of the given one is not a composite
119     * @throws UndefinedItemPathException if the data holder has a model and the given data path is not defined by this model
120     * @throws BadDataPathCardinalityException if the data holder has a model and the definition of a part of the data path is multiple. Only the last part can be multiple
121     */
122    public <T> T getValueOrDefault(String dataPath, T defaultValue) throws IllegalArgumentException, UnknownTypeException, NotUniqueTypeException, BadItemTypeException, UndefinedItemPathException, BadDataPathCardinalityException;
123    
124    /**
125     * Retrieves the value of the data at the given path
126     * @param <T> type of the value to retrieve. Should match the given data type
127     * @param dataPath path of the data
128     * @param dataTypeId type identifier of the data
129     * @return the 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
130     * @throws IllegalArgumentException if the given data path is null or empty
131     * @throws UnknownTypeException if the given type isn't available for this data holder's type extension point
132     * @throws BadItemTypeException if the given type doesn't match the type of the stored value at the given path
133     * @throws UndefinedItemPathException if the data holder has a model and the given data path is not defined by this model
134     * @throws BadDataPathCardinalityException if the data holder has a model and the definition of a part of the data path is multiple. Only the last part can be multiple
135     */
136    public <T> T getValueOfType(String dataPath, String dataTypeId) throws IllegalArgumentException, UnknownTypeException, BadItemTypeException, UndefinedItemPathException, BadDataPathCardinalityException;
137    
138    /**
139     * Retrieves the value of the data at the given path if exists and is not empty, or the given default value
140     * @param <T> type of the value to retrieve. Should match the given data type
141     * @param dataPath path of the data
142     * @param dataTypeId type identifier of the data
143     * @param defaultValue default value
144     * @return the value of the data, or the given default value if the value does not exist or is empty
145     * @throws IllegalArgumentException if the given data path is null or empty
146     * @throws UnknownTypeException if the given type isn't available for this data holder's type extension point
147     * @throws BadItemTypeException if the given type doesn't match the type of the stored value at the given path
148     * @throws UndefinedItemPathException if the data holder has a model and the given data path is not defined by this model
149     * @throws BadDataPathCardinalityException if the data holder has a model and the definition of a part of the data path is multiple. Only the last part can be multiple
150     */
151    public <T> T getValueOfTypeOrDefault(String dataPath, String dataTypeId, T defaultValue) throws IllegalArgumentException, UnknownTypeException, BadItemTypeException, UndefinedItemPathException, BadDataPathCardinalityException;
152    
153    /**
154     * Checks if the value of the data at the given path is multiple
155     * @param dataPath path of the data to check
156     * @return <code>true</code> if the value of the data is multiple, <code>false</code> otherwise
157     * @throws IllegalArgumentException if the given data path is null or empty
158     * @throws UnknownDataException the data at the given path does not exist
159     * @throws UnknownTypeException if there is no compatible type with the data at the given data path
160     * @throws NotUniqueTypeException if there are many compatible types (there is no way to determine which type is the good one)
161     * @throws BadItemTypeException if value at the sub data path of the given one is not a composite
162     * @throws UndefinedItemPathException if the data holder has a model and the given data path is not defined by this model
163     */
164    public boolean isMultiple(String dataPath) throws IllegalArgumentException, UnknownDataException, UnknownTypeException, NotUniqueTypeException, BadItemTypeException, UndefinedItemPathException;
165    
166    /**
167     * Checks if the value of the data at the given path is multiple
168     * @param dataPath path of the data to check
169     * @param dataTypeId type identifier of the data
170     * @return <code>true</code> if the value of the data is multiple, <code>false</code> otherwise
171     * @throws IllegalArgumentException if the given data path is null or empty
172     * @throws UnknownDataException the data at the given path does not exist
173     * @throws UnknownTypeException if there is no compatible type with the data at the given data path
174     * @throws BadItemTypeException if value at the sub data path of the given one is not a composite
175     * @throws UndefinedItemPathException if the data holder has a model and the given data path is not defined by this model
176     */
177    public boolean isMultiple(String dataPath, String dataTypeId) throws IllegalArgumentException, UnknownDataException, UnknownTypeException, BadItemTypeException, UndefinedItemPathException;
178    
179    /**
180     * Retrieves the type of the data at the given path
181     * @param <X> type of the item type
182     * @param dataPath path of the data
183     * @return the type of the data
184     * @throws IllegalArgumentException if the given data path is null or empty
185     * @throws UnknownDataException if there is no data stored at the given path
186     * @throws UnknownTypeException if there is no compatible type with the data at the given data path or if the data is a repeater entry but the composite type is not available
187     * @throws NotUniqueTypeException if there are many compatible types (there is no way to determine which type is the good one)
188     * @throws UndefinedItemPathException if the data holder has a model and the given data path is not defined by this model
189     */
190    public <X extends ModelItemType> X getType(String dataPath) throws IllegalArgumentException, UnknownDataException, UnknownTypeException, NotUniqueTypeException, UndefinedItemPathException;
191    
192    /**
193     * Provide the ModelItemTypeExtensionPoint linked with the DataHolder
194     * @return the extension point
195     */
196    public ModelItemTypeExtensionPoint getModelItemTypeExtensionPoint();
197    
198    /**
199     * Copies the current {@link DataHolder} to the given {@link ModifiableDataHolder}.
200     * @param dataHolder The destination dataHolder. Can not be null.
201     * @throws UnknownTypeException if there is no available type compatible with the copied value for the type extension point of the destination {@link ModifiableDataHolder}
202     * @throws NotUniqueTypeException if there is more than one available types compatibles with the copied value for the type extension point of the destination {@link ModifiableDataHolder}
203     * @throws BadItemTypeException if type of a data to copy doesn't match the data with same path in the destination {@link ModifiableDataHolder}
204     * @throws UndefinedItemPathException if the data holder has a model and the given data path is not defined by this model
205     */
206    public default void copyTo(ModifiableDataHolder dataHolder) throws UnknownTypeException, NotUniqueTypeException, BadItemTypeException, UndefinedItemPathException
207    {
208        copyTo(dataHolder, DataContext.newInstance());
209    }
210    
211    /**
212     * Copies the current {@link DataHolder} to the given {@link ModifiableDataHolder}.
213     * @param dataHolder The destination dataHolder. Can not be null.
214     * @param context The context of the data to copy
215     * @throws UnknownTypeException if there is no available type compatible with the copied value for the type extension point of the destination {@link ModifiableDataHolder}
216     * @throws NotUniqueTypeException if there is more than one available types compatibles with the copied value for the type extension point of the destination {@link ModifiableDataHolder}
217     * @throws BadItemTypeException if type of a data to copy doesn't match the data with same path in the destination {@link ModifiableDataHolder}
218     * @throws UndefinedItemPathException if the data holder has a model and the given data path is not defined by this model
219     */
220    public default void copyTo(ModifiableDataHolder dataHolder, DataContext context) throws UnknownTypeException, NotUniqueTypeException, BadItemTypeException, UndefinedItemPathException
221    {
222        DataHolderHelper.copyTo(this, dataHolder, context);
223    }
224    
225    /**
226     * Generates SAX events for data contained in this {@link DataHolder}
227     * @param contentHandler the {@link ContentHandler} that will receive the SAX events
228     * @throws SAXException if an error occurs during the SAX events generation
229     * @throws UnknownTypeException if there is no compatible type with the saxed value
230     * @throws NotUniqueTypeException if there are many compatible types (there is no way to determine which type is the good one) with the saxed value
231     * @throws BadItemTypeException if the saxed value's type does not matches the stored data
232     */
233    public default void dataToSAX(ContentHandler contentHandler) throws SAXException, UnknownTypeException, NotUniqueTypeException, BadItemTypeException
234    {
235        dataToSAX(contentHandler, DataContext.newInstance());
236    }
237    
238    /**
239     * Generates SAX events for data contained in this {@link DataHolder}
240     * @param contentHandler the {@link ContentHandler} that will receive the SAX events
241     * @param context The context of the data to SAX
242     * @throws SAXException if an error occurs during the SAX events generation
243     * @throws UnknownTypeException if there is no compatible type with the saxed value
244     * @throws NotUniqueTypeException if there are many compatible types (there is no way to determine which type is the good one) with the saxed value
245     * @throws BadItemTypeException if the saxed value's type does not matches the stored data
246     */
247    public void dataToSAX(ContentHandler contentHandler, DataContext context) throws SAXException, UnknownTypeException, NotUniqueTypeException, BadItemTypeException;
248    
249    /**
250     * Generates SAX events for the data at the given data path in the current {@link DataHolder}
251     * Do not generate any event if there is no values at the given path
252     * @param contentHandler the {@link ContentHandler} that will receive the SAX events
253     * @param dataPath the path of the data to SAX
254     * @throws SAXException if an error occurs during the SAX events generation
255     * @throws UnknownTypeException if there is no compatible type with the saxed value
256     * @throws NotUniqueTypeException if there are many compatible types (there is no way to determine which type is the good one) with the saxed value
257     * @throws BadItemTypeException if the saxed value's type does not matches the stored data
258     * @throws UndefinedItemPathException if the data holder has a model and the given data path is not defined by this model
259     */
260    public default void dataToSAX(ContentHandler contentHandler, String dataPath) throws SAXException, UnknownTypeException, NotUniqueTypeException, BadItemTypeException, UndefinedItemPathException
261    {
262        dataToSAX(contentHandler, dataPath, DataContext.newInstance());
263    }
264    
265    /**
266     * Generates SAX events for the data at the given data path in the current {@link DataHolder}
267     * Do not generate any event if there is no values at the given path
268     * @param contentHandler the {@link ContentHandler} that will receive the SAX events
269     * @param dataPath the path of the data to SAX
270     * @param context The context of the data to SAX
271     * @throws SAXException if an error occurs during the SAX events generation
272     * @throws UnknownTypeException if there is no compatible type with the saxed value
273     * @throws NotUniqueTypeException if there are many compatible types (there is no way to determine which type is the good one) with the saxed value
274     * @throws BadItemTypeException if the saxed value's type does not matches the stored data
275     * @throws UndefinedItemPathException if the data holder has a model and the given data path is not defined by this model
276     */
277    public void dataToSAX(ContentHandler contentHandler, String dataPath, DataContext context) throws SAXException, UnknownTypeException, NotUniqueTypeException, BadItemTypeException, UndefinedItemPathException;
278    
279    /**
280     * Convert the data contained in this {@link DataHolder}
281     * @return The data of the current {@link DataHolder} as JSON
282     * @throws UnknownTypeException if there is no compatible type with the value to convert
283     * @throws NotUniqueTypeException if there are many compatible types (there is no way to determine which type is the good one) with the value to convert
284     * @throws BadItemTypeException if the value's type does not matches the stored data
285     */
286    public default Map<String, Object> dataToJSON() throws UnknownTypeException, NotUniqueTypeException, BadItemTypeException
287    {
288        return dataToJSON(DataContext.newInstance());
289    }
290    
291    /**
292     * Convert the data contained in this {@link DataHolder}
293     * @param context The context of the data to convert
294     * @return The data of the current {@link DataHolder} as JSON
295     * @throws UnknownTypeException if there is no compatible type with the value to convert
296     * @throws NotUniqueTypeException if there are many compatible types (there is no way to determine which type is the good one) with the value to convert
297     * @throws BadItemTypeException if the value's type does not matches the stored data
298     */
299    public Map<String, Object> dataToJSON(DataContext context) throws UnknownTypeException, NotUniqueTypeException, BadItemTypeException;
300    
301    /**
302     * Convert the data at the given path into a JSON object
303     * @param dataPath the path of the data to convert
304     * @return The value as JSON
305     * @throws UnknownTypeException if there is no compatible type with the value to convert
306     * @throws NotUniqueTypeException if there are many compatible types (there is no way to determine which type is the good one) with the value to convert
307     * @throws BadItemTypeException if the value's type does not matches the stored data
308     * @throws UndefinedItemPathException if the data holder has a model and the given data path is not defined by this model
309     */
310    public default Object dataToJSON(String dataPath) throws UnknownTypeException, NotUniqueTypeException, BadItemTypeException, UndefinedItemPathException
311    {
312        return dataToJSON(dataPath, DataContext.newInstance());
313    }
314    
315    /**
316     * Convert the data at the given path into a JSON object
317     * @param dataPath the path of the data to convert
318     * @param context The context of the data to convert
319     * @return The value as JSON
320     * @throws UnknownTypeException if there is no compatible type with the value to convert
321     * @throws NotUniqueTypeException if there are many compatible types (there is no way to determine which type is the good one) with the value to convert
322     * @throws BadItemTypeException if the value's type does not matches the stored data
323     * @throws UndefinedItemPathException if the data holder has a model and the given data path is not defined by this model
324     */
325    public Object dataToJSON(String dataPath, DataContext context) throws UnknownTypeException, NotUniqueTypeException, BadItemTypeException, UndefinedItemPathException;
326    
327    /**
328     * Check if there are differences between the given values and the current ones
329     * @param values the values to check
330     * @return <code>true</code> if there are differences, <code>false</code> otherwise
331     * @throws UnknownTypeException if there is no available type compatible with a given value for this data holder's type extension point
332     * @throws NotUniqueTypeException if there is more than one available types compatibles with the a value for this data holder's type extension point
333     * @throws BadItemTypeException if value doesn't match the data at corresponding path
334     */
335    public boolean hasDifferences(Map<String, Object> values) throws UnknownTypeException, NotUniqueTypeException, BadItemTypeException;
336    
337    /**
338     * Retrieves the repository data used by this {@link DataHolder}
339     * @return the repository data used by this {@link DataHolder}
340     */
341    public RepositoryData getRepositoryData();
342    
343    /**
344     * Retrieves the optional parent of the current {@link DataHolder}
345     * There can be no parent if the current {@link DataHolder} is the root
346     * @return the parent of the current {@link DataHolder}
347     */
348    public Optional<? extends DataHolder> getParentDataHolder();
349    
350    /**
351     * Retrieves the {@link DataHolder} that is the root of the current one
352     * @return the root {@link DataHolder}
353     */
354    public DataHolder getRootDataHolder();
355}