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.io.IOException;
019import java.util.Collection;
020import java.util.Locale;
021
022import org.apache.commons.lang3.StringUtils;
023import org.xml.sax.ContentHandler;
024import org.xml.sax.SAXException;
025
026import org.ametys.plugins.repository.data.external.ExternalizableDataProvider.ExternalizableDataStatus;
027import org.ametys.plugins.repository.data.holder.group.impl.ModelAwareComposite;
028import org.ametys.plugins.repository.data.holder.group.impl.ModelAwareRepeater;
029import org.ametys.plugins.repository.data.holder.impl.DataHolderHelper;
030import org.ametys.plugins.repository.data.type.RepositoryModelItemType;
031import org.ametys.plugins.repository.metadata.MultilingualString;
032import org.ametys.plugins.repository.model.RepeaterDefinition;
033import org.ametys.runtime.model.ElementDefinition;
034import org.ametys.runtime.model.ModelItem;
035import org.ametys.runtime.model.ModelItemContainer;
036import org.ametys.runtime.model.ViewItemContainer;
037import org.ametys.runtime.model.exception.BadDataPathCardinalityException;
038import org.ametys.runtime.model.exception.BadItemTypeException;
039import org.ametys.runtime.model.exception.UndefinedItemPathException;
040
041/**
042 * Interface for data containers with models
043 */
044public interface ModelAwareDataHolder extends DataHolder
045{
046    /** Suffix used for the alternative value */
047    public static final String ALTERNATIVE_SUFFIX = "__alt";
048    /** Suffix used for the status value */
049    public static final String STATUS_SUFFIX = "__status";
050    
051    /**
052     * {@inheritDoc}
053     * @throws UndefinedItemPathException if the given composite path is not defined by the model
054     * @throws BadDataPathCardinalityException if the definition of a part of the data path is multiple. Only the last part can be multiple 
055     */
056    @Override
057    public ModelAwareComposite getComposite(String compositePath) throws IllegalArgumentException, UndefinedItemPathException, BadItemTypeException, BadDataPathCardinalityException;
058    
059    /**
060     * Retrieves the local composite at the given path
061     * @param compositePath path of the externalizable composite to retrieve
062     * @return the composite or <code>null</code> if not exists or is empty
063     * @throws IllegalArgumentException if the given composite path is null or empty
064     * @throws BadItemTypeException if the stored value at the given path is not a composite
065     * @throws UndefinedItemPathException if the given composite path is not defined by the model
066     * @throws BadDataPathCardinalityException if the definition of a part of the data path is multiple. Only the last part can be multiple 
067     */
068    public ModelAwareComposite getLocalComposite(String compositePath) throws IllegalArgumentException, UndefinedItemPathException, BadItemTypeException, BadDataPathCardinalityException;
069    
070    /**
071     * Retrieves the external composite at the given path
072     * @param compositePath path of the externalizable composite to retrieve
073     * @return the composite or <code>null</code> if not exists or is empty
074     * @throws IllegalArgumentException if the given composite path is null or empty
075     * @throws BadItemTypeException if the stored value at the given path is not a composite
076     * @throws UndefinedItemPathException if the given composite path is not defined by the model
077     * @throws BadDataPathCardinalityException if the definition of a part of the data path is multiple. Only the last part can be multiple 
078     */
079    public ModelAwareComposite getExternalComposite(String compositePath) throws IllegalArgumentException, UndefinedItemPathException, BadItemTypeException, BadDataPathCardinalityException;
080    
081    /**
082     * Retrieves the repeater at the given path
083     * @param repeaterPath path of the repeater to retrieve
084     * @return the repeater or <code>null</code> if not exists or is empty
085     * @throws IllegalArgumentException if the given repeater path is null or empty
086     * @throws BadItemTypeException if the stored value at the given path is not a repeater
087     * @throws UndefinedItemPathException if the given repeater path is not defined by the model
088     * @throws BadDataPathCardinalityException if the definition of a part of the data path is multiple. Only the last part can be multiple
089     */
090    public ModelAwareRepeater getRepeater(String repeaterPath) throws IllegalArgumentException, UndefinedItemPathException, BadItemTypeException, BadDataPathCardinalityException;
091    
092    /**
093     * Retrieves the local repeater at the given path
094     * @param repeaterPath path of the externalizable repeater to retrieve
095     * @return the repeater or <code>null</code> if not exists or is empty
096     * @throws IllegalArgumentException if the given repeater path is null or empty
097     * @throws BadItemTypeException if the stored value at the given path is not a repeater
098     * @throws UndefinedItemPathException if the given repeater path is not defined by the model
099     * @throws BadDataPathCardinalityException if the definition of a part of the data path is multiple. Only the last part can be multiple
100     */
101    public ModelAwareRepeater getLocalRepeater(String repeaterPath) throws IllegalArgumentException, UndefinedItemPathException, BadItemTypeException, BadDataPathCardinalityException;
102    
103    /**
104     * Retrieves the external repeater at the given path
105     * @param repeaterPath path of the externalizable repeater to retrieve
106     * @return the repeater or <code>null</code> if not exists or is empty
107     * @throws IllegalArgumentException if the given repeater path is null or empty
108     * @throws BadItemTypeException if the stored value at the given path is not a repeater
109     * @throws UndefinedItemPathException if the given repeater path is not defined by the model
110     * @throws BadDataPathCardinalityException if the definition of a part of the data path is multiple. Only the last part can be multiple
111     */
112    public ModelAwareRepeater getExternalRepeater(String repeaterPath) throws IllegalArgumentException, UndefinedItemPathException, BadItemTypeException, BadDataPathCardinalityException;
113    
114    /**
115     * {@inheritDoc}
116     * @return <code>true</code> if the data at the given path is defined by the model, if there is a value for the data (even empty) and if the type of this value matches the type of the definition. <code>false</code> otherwise
117     * @throws BadDataPathCardinalityException if the definition of a part of the data path is multiple. Only the last part can be multiple
118     */
119    @Override
120    public boolean hasValue(String dataPath) throws IllegalArgumentException, BadDataPathCardinalityException;
121    
122    /**
123     * Checks if there is a local value for the data at the given path
124     * @param dataPath path of the externalizable data
125     * @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
126     * @throws IllegalArgumentException if the given data path is null or empty
127     * @throws BadDataPathCardinalityException if the definition of a part of the data path is multiple. Only the last part can be multiple
128     */
129    public boolean hasLocalValue(String dataPath) throws IllegalArgumentException, BadDataPathCardinalityException;
130    
131    /**
132     * Checks if there is an external value for the data at the given path
133     * @param dataPath path of the externalizable data
134     * @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
135     * @throws IllegalArgumentException if the given data path is null or empty
136     * @throws BadDataPathCardinalityException if the definition of a part of the data path is multiple. Only the last part can be multiple
137     */
138    public boolean hasExternalValue(String dataPath) throws IllegalArgumentException, BadDataPathCardinalityException;
139    
140    /**
141     * {@inheritDoc}
142     * @return the names of the data contained by this data holder and that are defined by the model
143     */
144    @Override
145    public Collection<String> getDataNames();
146    
147    /**
148     * Retrieves the value of the data at the given path
149     * @param <T> type of the value to retrieve
150     * @param dataPath path of the data
151     * @return the value of the data or <code>null</code> if not exists or is empty. The object returned may be of a generic class defined by the storage (if the model is unknown). For example, an url may be returned as a String.
152     * @throws IllegalArgumentException if the given data path is null or empty
153     * @throws UndefinedItemPathException if the given data path is not defined by the model
154     * @throws BadItemTypeException if the type defined by the model doesn't match the type of the stored value
155     * @throws BadDataPathCardinalityException if the definition of a part of the data path is multiple. Only the last part can be multiple
156     */
157    public default <T extends Object> T getValue(String dataPath) throws IllegalArgumentException, UndefinedItemPathException, BadItemTypeException, BadDataPathCardinalityException
158    {
159        return getValue(dataPath, false);
160    }
161    
162    /**
163     * Retrieves the value of the data at the given path
164     * @param <T> type of the value to retrieve
165     * @param dataPath path of the data 
166     * @param allowMultiValuedPathSegments <code>true</code> to allow multi-valued segments in the path (not necessarily at the last segment), <code>false</code> otherwise.
167     *      If <code>true</code>, if there is no indicated entry for a repeater, the values of all the entries are retrieved
168     *      If <code>true</code> and if there are multiple values, all data are retrieved in one array
169     * @return the value of the data or <code>null</code> if managesMutiples is <code>false</code> and there is no non empty value. The object returned may be of a generic class defined by the storage (if the model is unknown). For example, an url may be returned as a String.
170     * @throws IllegalArgumentException if the given data path is null or empty
171     * @throws UndefinedItemPathException if the given data path is not defined by the model
172     * @throws BadItemTypeException if the type defined by the model doesn't match the type of the stored value
173     * @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
174     */
175    public <T extends Object> T getValue(String dataPath, boolean allowMultiValuedPathSegments) throws IllegalArgumentException, UndefinedItemPathException, BadItemTypeException, BadDataPathCardinalityException;
176    
177    /**
178     * Retrieves the value of the data at the given path, or the default value
179     * The returned value is one of those ones, in the order:
180     * <ol>
181     * <li>The value of the data if exists and is not empty</li>
182     * <li>The default value from the model if useDefaultFromModel is <code>true</code> and there is a default value defined by the model</li>
183     * <li>The given default value</li>
184     * </ol>
185     * @param <T> type of the value to retrieve
186     * @param dataPath path of the data
187     * @param useDefaultFromModel true to use the default value from the model, false to use the given default value
188     * @param defaultValue default value used if value is null and useDefaultFromModel is false, or if there is no default value on model
189     * @return the value of the data at the given path
190     * @throws IllegalArgumentException if the given data path is null or empty
191     * @throws UndefinedItemPathException if the given data path is not defined by the model
192     * @throws BadItemTypeException if the type defined by the model doesn't match the type of the stored value
193     * @throws BadDataPathCardinalityException if the definition of a part of the data path is multiple. Only the last part can be multiple
194     */
195    public <T extends Object> T getValue(String dataPath, boolean useDefaultFromModel, T defaultValue) throws IllegalArgumentException, UndefinedItemPathException, BadItemTypeException, BadDataPathCardinalityException;
196    
197    /**
198     * Retrieves the local value of the data at the given path
199     * @param <T> type of the value to retrieve
200     * @param dataPath path of the externalizable data
201     * @return the local value of the data or <code>null</code> if not exists or is empty. The object returned may be of a generic class defined by the storage (if the model is unknown). For example, an url may be returned as a String.
202     * @throws IllegalArgumentException if the given data path is null or empty
203     * @throws UndefinedItemPathException if the given data path is not defined by the model
204     * @throws BadItemTypeException if the type defined by the model doesn't match the type of the stored value
205     * @throws BadDataPathCardinalityException if the definition of a part of the data path is multiple. Only the last part can be multiple
206     */
207    public <T extends Object> T getLocalValue(String dataPath) throws IllegalArgumentException, UndefinedItemPathException, BadItemTypeException, BadDataPathCardinalityException;
208    
209    /**
210     * Retrieves the external value of the data at the given path
211     * @param <T> type of the value to retrieve
212     * @param dataPath path of the externalizable data
213     * @return the external value of the data or <code>null</code> if not exists or is empty. The object returned may be of a generic class defined by the storage (if the model is unknown). For example, an url may be returned as a String.
214     * @throws IllegalArgumentException if the given data path is null or empty
215     * @throws UndefinedItemPathException if the given data path is not defined by the model
216     * @throws BadItemTypeException if the type defined by the model doesn't match the type of the stored value
217     * @throws BadDataPathCardinalityException if the definition of a part of the data path is multiple. Only the last part can be multiple
218     */
219    public <T extends Object> T getExternalValue(String dataPath) throws IllegalArgumentException, UndefinedItemPathException, BadItemTypeException, BadDataPathCardinalityException;
220    
221    /**
222     * Retrieves the status of the externalizable data at the given path
223     * @param dataPath path of the externalizable data
224     * @return the status of the externalizable data at the given path
225     * @throws IllegalArgumentException if the given data path is null or empty
226     * @throws UndefinedItemPathException if the given data path is not defined by the model
227     * @throws BadDataPathCardinalityException if the definition of a part of the data path is multiple. Only the last part can be multiple
228     */
229    public ExternalizableDataStatus getStatus(String dataPath) throws IllegalArgumentException, UndefinedItemPathException, BadDataPathCardinalityException;
230    
231    /**
232     * Checks if the definition of the element at the given path is multiple
233     * @param path path of the element. No matter if it is a definition or data path (with repeater entry positions)
234     * @return <code>true</code> if the element is multiple, <code>false</code> otherwise
235     * @throws IllegalArgumentException if the given path is null or empty
236     * @throws UndefinedItemPathException if the given path is not defined by the model
237     */
238    public default boolean isMultiple(String path) throws IllegalArgumentException, UndefinedItemPathException
239    {
240        ModelItem item = getDefinition(path);
241        if (item instanceof ElementDefinition)
242        {
243            return ((ElementDefinition) item).isMultiple();
244        }
245        else if (item instanceof RepeaterDefinition)
246        {
247            // If the given path represents a repeater , but with no specified entry, consider the data as multiple
248            return !DataHolderHelper.isRepeaterEntryPath(path);
249        }
250        else
251        {
252            // Composites are not multiples
253            return false;
254        }
255    }
256    
257    /**
258     * Retrieves the type of the data at the given path
259     * @param path path of the data. No matter if it is a definition or data path (with repeater entry positions)
260     * @return the type of the data
261     * @throws IllegalArgumentException if the given data path is null or empty
262     * @throws UndefinedItemPathException if the given data path is not defined by the model
263     */
264    public default RepositoryModelItemType getType(String path) throws IllegalArgumentException, UndefinedItemPathException
265    {
266        return (RepositoryModelItemType) getDefinition(path).getType();
267    }
268    
269    /**
270     * Retrieves the data holder's model
271     * @return the data holder's model
272     */
273    public Collection<? extends ModelItemContainer> getModel();
274    
275    /**
276     * Retrieves the definition of the data at the given path
277     * @param path path of the data. No matter if it is a definition or data path (with repeater entry positions)
278     * @return the definition of the data
279     * @throws IllegalArgumentException if the given path is null or empty
280     * @throws UndefinedItemPathException if the given path is not defined by the model
281     */
282    public ModelItem getDefinition(String path) throws IllegalArgumentException, UndefinedItemPathException;
283    
284    /**
285     * Checks if there is a definition at the given path
286     * @param path path of the data. No matter if it is a definition or data path (with repeater entry positions)
287     * @return <code>true</code> if there is definition at the given path, <code>false</code> otherwise
288     * @throws IllegalArgumentException if the given path is null or empty
289     */
290    public boolean hasDefinition(String path) throws IllegalArgumentException;
291    
292    /**
293     * Generates SAX events for the data in the model of the current {@link DataHolder}
294     * @param contentHandler the {@link ContentHandler} that will receive the SAX events
295     * @throws SAXException if an error occurs during the SAX events generation
296     * @throws IOException if an error occurs while reading a value using the I/O API
297     * @throws BadItemTypeException if the saxed value's type does not matches the stored data
298     */
299    public default void dataToSAX(ContentHandler contentHandler) throws SAXException, IOException, BadItemTypeException
300    {
301        dataToSAX(contentHandler, (Locale) null);
302    }
303    
304    /**
305     * Generates SAX events for the data in the model of the current {@link DataHolder}
306     * @param contentHandler the {@link ContentHandler} that will receive the SAX events
307     * @param locale The locale to use for localized data, such as {@link MultilingualString}. Can be <code>null</code> to generate SAX events for all existing {@link Locale}s.
308     * @throws SAXException if an error occurs during the SAX events generation
309     * @throws IOException if an error occurs while reading a value using the I/O API
310     * @throws BadItemTypeException if the saxed value's type does not matches the stored data
311     */
312    public void dataToSAX(ContentHandler contentHandler, Locale locale) throws SAXException, IOException, BadItemTypeException;
313    
314    /**
315     * Generates SAX events for the data in the given view in the current {@link DataHolder}
316     * @param contentHandler the {@link ContentHandler} that will receive the SAX events
317     * @param viewItemContainer the {@link ViewItemContainer} containing referencing the model item for which generate SAX events
318     * @throws SAXException if an error occurs during the SAX events generation
319     * @throws IOException if an error occurs while reading a value using the I/O API
320     * @throws BadItemTypeException if the saxed value's type does not matches the stored data
321     */
322    public default void dataToSAX(ContentHandler contentHandler, ViewItemContainer viewItemContainer) throws SAXException, IOException, BadItemTypeException
323    {
324        dataToSAX(contentHandler, viewItemContainer, null);
325    }
326    
327    /**
328     * Generates SAX events for the data in the given view in the current {@link DataHolder}
329     * @param contentHandler the {@link ContentHandler} that will receive the SAX events
330     * @param viewItemContainer the {@link ViewItemContainer} containing referencing the model item for which generate SAX events
331     * @param locale The locale to use for localized data, such as {@link MultilingualString}. Can be <code>null</code> to generate SAX events for all existing {@link Locale}s.
332     * @throws SAXException if an error occurs during the SAX events generation
333     * @throws IOException if an error occurs while reading a value using the I/O API
334     * @throws BadItemTypeException if the saxed value's type does not matches the stored data
335     */
336    public default void dataToSAX(ContentHandler contentHandler, ViewItemContainer viewItemContainer, Locale locale) throws SAXException, IOException, BadItemTypeException
337    {
338        DataHolderHelper.dataToSAX(this, contentHandler, viewItemContainer, StringUtils.EMPTY, locale);
339    }
340}