001/*
002 *  Copyright 2026 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.jcr;
017
018import java.util.Collection;
019import java.util.Map;
020import java.util.Optional;
021
022import javax.jcr.Node;
023
024import org.xml.sax.ContentHandler;
025import org.xml.sax.SAXException;
026
027import org.ametys.plugins.repository.AmetysObject;
028import org.ametys.plugins.repository.data.external.ExternalizableDataProvider.ExternalizableDataStatus;
029import org.ametys.plugins.repository.data.holder.ModifiableModelAwareDataHolder;
030import org.ametys.plugins.repository.data.holder.group.ModifiableModelAwareComposite;
031import org.ametys.plugins.repository.data.holder.group.ModifiableRepeater;
032import org.ametys.plugins.repository.data.holder.impl.DataHolderHelper;
033import org.ametys.plugins.repository.data.holder.impl.DefaultModifiableModelAwareDataHolder;
034import org.ametys.plugins.repository.data.holder.values.SynchronizationContext;
035import org.ametys.plugins.repository.data.holder.values.SynchronizationResult;
036import org.ametys.plugins.repository.data.repositorydata.ModifiableRepositoryData;
037import org.ametys.plugins.repository.data.repositorydata.impl.JCRRepositoryData;
038import org.ametys.plugins.repository.data.type.ModelItemTypeExtensionPoint;
039import org.ametys.runtime.model.Model;
040import org.ametys.runtime.model.ModelItem;
041import org.ametys.runtime.model.ViewItemAccessor;
042import org.ametys.runtime.model.exception.BadDataPathCardinalityException;
043import org.ametys.runtime.model.exception.BadItemTypeException;
044import org.ametys.runtime.model.exception.UndefinedItemPathException;
045import org.ametys.runtime.model.type.DataContext;
046import org.ametys.runtime.model.type.ModelItemType;
047
048/**
049 * Model aware implementation of an {@link AmetysObject}, backed by a JCR node.<br>
050 * @param <F> the actual type of factory.
051 */
052public class SimpleModelAwareAmetysObject<F extends SimpleModelAwareAmetysObjectFactory> extends SimpleAmetysObject<F> implements ModelAwareJCRAmetysObject
053{
054    /**
055     * Creates an {@link SimpleModelAwareAmetysObject}.
056     * @param node the node backing this {@link AmetysObject}
057     * @param parentPath the parentPath in the Ametys hierarchy
058     * @param factory the {@link SimpleModelAwareAmetysObjectFactory} which created the AmetysObject
059     */
060    public SimpleModelAwareAmetysObject(Node node, String parentPath, F factory)
061    {
062        super(node, parentPath, factory);
063    }
064
065    @Override
066    protected ModifiableModelAwareDataHolder getDataHolder()
067    {
068        ModifiableRepositoryData repositoryData = new JCRRepositoryData(getNode());
069        return new DefaultModifiableModelAwareDataHolder(repositoryData, _getFactory().getModel(this), Optional.empty(), Optional.empty(), Optional.of(this));
070    }
071    
072    @Override
073    public ModifiableModelAwareComposite getComposite(String compositePath) throws IllegalArgumentException, BadItemTypeException, UndefinedItemPathException, BadDataPathCardinalityException
074    {
075        return getDataHolder().getComposite(compositePath);
076    }
077    
078    @Override
079    public ModifiableModelAwareComposite getLocalComposite(String compositePath) throws IllegalArgumentException, BadItemTypeException, UndefinedItemPathException, BadDataPathCardinalityException
080    {
081        return getDataHolder().getLocalComposite(compositePath);
082    }
083    
084    @Override
085    public ModifiableModelAwareComposite getExternalComposite(String compositePath) throws IllegalArgumentException, BadItemTypeException, UndefinedItemPathException, BadDataPathCardinalityException
086    {
087        return getDataHolder().getExternalComposite(compositePath);
088    }
089
090    @Override
091    public ModifiableRepeater getRepeater(String repeaterPath) throws IllegalArgumentException, BadItemTypeException, UndefinedItemPathException, BadDataPathCardinalityException
092    {
093        return getDataHolder().getRepeater(repeaterPath);
094    }
095    
096    @Override
097    public ModifiableRepeater getLocalRepeater(String repeaterPath) throws IllegalArgumentException, BadItemTypeException, UndefinedItemPathException, BadDataPathCardinalityException
098    {
099        return getDataHolder().getLocalRepeater(repeaterPath);
100    }
101    
102    @Override
103    public ModifiableRepeater getExternalRepeater(String repeaterPath) throws IllegalArgumentException, BadItemTypeException, UndefinedItemPathException, BadDataPathCardinalityException
104    {
105        return getDataHolder().getExternalRepeater(repeaterPath);
106    }
107
108    @Override
109    public ModifiableModelAwareComposite getComposite(String compositePath, boolean createNew) throws IllegalArgumentException, BadItemTypeException, UndefinedItemPathException, BadDataPathCardinalityException
110    {
111        return getDataHolder().getComposite(compositePath, createNew);
112    }
113    
114    public ModifiableModelAwareComposite getLocalComposite(String compositePath, boolean createNew) throws IllegalArgumentException, BadItemTypeException, UndefinedItemPathException, BadDataPathCardinalityException
115    {
116        return getDataHolder().getLocalComposite(compositePath, createNew);
117    }
118    
119    public ModifiableModelAwareComposite getExternalComposite(String compositePath, boolean createNew) throws IllegalArgumentException, BadItemTypeException, UndefinedItemPathException, BadDataPathCardinalityException
120    {
121        return getDataHolder().getExternalComposite(compositePath, createNew);
122    }
123
124    public ModifiableRepeater getRepeater(String repeaterPath, boolean createNew) throws IllegalArgumentException, BadItemTypeException, UndefinedItemPathException, BadDataPathCardinalityException
125    {
126        return getDataHolder().getRepeater(repeaterPath, createNew);
127    }
128    
129    public ModifiableRepeater getLocalRepeater(String repeaterPath, boolean createNew) throws IllegalArgumentException, BadItemTypeException, UndefinedItemPathException, BadDataPathCardinalityException
130    {
131        return getDataHolder().getLocalRepeater(repeaterPath, createNew);
132    }
133    
134    public ModifiableRepeater getExternalRepeater(String repeaterPath, boolean createNew) throws IllegalArgumentException, BadItemTypeException, UndefinedItemPathException, BadDataPathCardinalityException
135    {
136        return getDataHolder().getExternalRepeater(repeaterPath, createNew);
137    }
138    
139    @Override
140    public boolean hasValue(String dataPath) throws IllegalArgumentException, BadDataPathCardinalityException
141    {
142        return getDataHolder().hasValue(dataPath);
143    }
144    
145    @Override
146    public boolean hasValue(String dataPath, String dataTypeId) throws IllegalArgumentException, BadDataPathCardinalityException
147    {
148        return ModelAwareJCRAmetysObject.super.hasValue(dataPath, dataTypeId);
149    }
150    
151    public boolean hasLocalValue(String dataPath) throws IllegalArgumentException, BadDataPathCardinalityException
152    {
153        return getDataHolder().hasLocalValue(dataPath);
154    }
155    
156    public boolean hasExternalValue(String dataPath) throws IllegalArgumentException, BadDataPathCardinalityException
157    {
158        return getDataHolder().hasExternalValue(dataPath);
159    }
160    
161    @Override
162    public boolean hasValueOrEmpty(String dataPath) throws IllegalArgumentException, BadDataPathCardinalityException
163    {
164        return getDataHolder().hasValueOrEmpty(dataPath);
165    }
166    
167    public boolean hasLocalValueOrEmpty(String dataPath) throws IllegalArgumentException, BadDataPathCardinalityException
168    {
169        return getDataHolder().hasLocalValueOrEmpty(dataPath);
170    }
171    
172    public boolean hasExternalValueOrEmpty(String dataPath) throws IllegalArgumentException, BadDataPathCardinalityException
173    {
174        return getDataHolder().hasExternalValueOrEmpty(dataPath);
175    }
176    
177    public <T> T getLocalValue(String dataPath) throws IllegalArgumentException, BadItemTypeException, UndefinedItemPathException, BadDataPathCardinalityException
178    {
179        return getDataHolder().getLocalValue(dataPath);
180    }
181    
182    public <T> T getExternalValue(String dataPath) throws IllegalArgumentException, BadItemTypeException, UndefinedItemPathException, BadDataPathCardinalityException
183    {
184        return getDataHolder().getExternalValue(dataPath);
185    }
186    
187    public <T> T getValueOrDefault(String dataPath) throws IllegalArgumentException, BadItemTypeException, UndefinedItemPathException, BadDataPathCardinalityException
188    {
189        return getDataHolder().getValueOrDefault(dataPath);
190    }
191    
192    @Override
193    public <T> T getValueOfType(String dataPath, String dataTypeId) throws IllegalArgumentException, BadItemTypeException, UndefinedItemPathException, BadDataPathCardinalityException
194    {
195        return ModelAwareJCRAmetysObject.super.getValueOfType(dataPath, dataTypeId);
196    }
197    
198    @Override
199    public <T> T getValueOfTypeOrDefault(String dataPath, String dataTypeId, T defaultValue) throws IllegalArgumentException, BadItemTypeException, UndefinedItemPathException, BadDataPathCardinalityException
200    {
201        return ModelAwareJCRAmetysObject.super.getValueOfTypeOrDefault(dataPath, dataTypeId, defaultValue);
202    }
203    
204    public <T> T getValueWithMultiValuedPathSegments(String dataPath) throws IllegalArgumentException, BadItemTypeException, UndefinedItemPathException, BadDataPathCardinalityException
205    {
206        return getDataHolder().getValueWithMultiValuedPathSegments(dataPath);
207    }
208    
209    public ExternalizableDataStatus getStatus(String dataPath) throws IllegalArgumentException, BadItemTypeException, UndefinedItemPathException, BadDataPathCardinalityException
210    {
211        return getDataHolder().getStatus(dataPath);
212    }
213    
214    @Override
215    public boolean isMultiple(String dataPath) throws IllegalArgumentException, UndefinedItemPathException
216    {
217        return ModelAwareJCRAmetysObject.super.isMultiple(dataPath);
218    }
219    
220    @Override
221    public boolean isMultiple(String dataPath, String dataTypeId) throws IllegalArgumentException, UndefinedItemPathException
222    {
223        return ModelAwareJCRAmetysObject.super.isMultiple(dataPath, dataTypeId);
224    }
225    
226    @Override
227    public <X extends ModelItemType> X getType(String path) throws IllegalArgumentException, UndefinedItemPathException
228    {
229        return ModelAwareJCRAmetysObject.super.getType(path);
230    }
231    
232    @Override
233    public ModelItemTypeExtensionPoint getModelItemTypeExtensionPoint()
234    {
235        return ModelAwareJCRAmetysObject.super.getModelItemTypeExtensionPoint();
236    }
237    
238    @SuppressWarnings("unchecked")
239    public Collection<? extends Model> getModel()
240    {
241        return (Collection<? extends Model>) getDataHolder().getModel();
242    }
243    
244    @Override
245    public ModelItem getDefinition(String path) throws IllegalArgumentException, UndefinedItemPathException
246    {
247        return getDataHolder().getDefinition(path);
248    }
249    
250    @Override
251    public boolean hasDefinition(String path) throws IllegalArgumentException
252    {
253        return getDataHolder().hasDefinition(path);
254    }
255    
256    @Override
257    public void dataToSAX(ContentHandler contentHandler, DataContext context) throws SAXException, BadItemTypeException
258    {
259        ModelAwareJCRAmetysObject.super.dataToSAX(contentHandler, context);
260    }
261    
262    public void dataToSAX(ContentHandler contentHandler, ViewItemAccessor viewItemAccessor, DataContext context) throws SAXException, BadItemTypeException
263    {
264        getDataHolder().dataToSAX(contentHandler, viewItemAccessor, context);
265    }
266    
267    public void dataToSAXForEdition(ContentHandler contentHandler, ViewItemAccessor viewItemAccessor, DataContext context) throws SAXException, BadItemTypeException
268    {
269        getDataHolder().dataToSAXForEdition(contentHandler, viewItemAccessor, context);
270    }
271    
272    @Override
273    public Map<String, Object> dataToJSON(DataContext context) throws BadItemTypeException
274    {
275        return ModelAwareJCRAmetysObject.super.dataToJSON(context);
276    }
277    
278    public Map<String, Object> dataToJSON(ViewItemAccessor viewItemAccessor, DataContext context) throws BadItemTypeException
279    {
280        return getDataHolder().dataToJSON(viewItemAccessor, context);
281    }
282    
283    public Map<String, Object> dataToJSONForEdition(ViewItemAccessor viewItemAccessor, DataContext context) throws BadItemTypeException
284    {
285        return getDataHolder().dataToJSONForEdition(viewItemAccessor, context);
286    }
287    
288    public Map<String, Object> dataToMap(ViewItemAccessor viewItemAccessor, DataContext context) throws BadItemTypeException
289    {
290        return getDataHolder().dataToMap(viewItemAccessor, context);
291    }
292    
293    @Override
294    public boolean hasDifferences(Map<String, Object> values) throws BadItemTypeException
295    {
296        ViewItemAccessor viewItemAccessor = DataHolderHelper.createViewItemAccessorFromValues(getModel(), values);
297        Map<String, Object> convertedValues = DataHolderHelper.convertValues(viewItemAccessor, values, DataHolderHelper::convertValue);
298        return getDataHolder().hasDifferences(viewItemAccessor, convertedValues);
299    }
300    
301    @Override
302    public boolean hasDifferences(Map<String, Object> values, SynchronizationContext context) throws BadItemTypeException
303    {
304        ViewItemAccessor viewItemAccessor = DataHolderHelper.createViewItemAccessorFromValues(getModel(), values);
305        Map<String, Object> convertedValues = DataHolderHelper.convertValues(viewItemAccessor, values, context.ignoreIncompatibleValues()
306                ? DataHolderHelper::convertValueIgnoringIncompatibleOnes
307                : DataHolderHelper::convertValue);
308        return getDataHolder().hasDifferences(viewItemAccessor, convertedValues, context);
309    }
310    
311    public boolean hasDifferences(ViewItemAccessor viewItemAccessor, Map<String, Object> values) throws BadItemTypeException
312    {
313        Map<String, Object> convertedValues = DataHolderHelper.convertValues(viewItemAccessor, values, DataHolderHelper::convertValue);
314        return getDataHolder().hasDifferences(viewItemAccessor, convertedValues);
315    }
316    
317    public boolean hasDifferences(ViewItemAccessor viewItemAccessor, Map<String, Object> values, SynchronizationContext context) throws BadItemTypeException
318    {
319        Map<String, Object> convertedValues = DataHolderHelper.convertValues(viewItemAccessor, values, context.ignoreIncompatibleValues()
320                                                                                                            ? DataHolderHelper::convertValueIgnoringIncompatibleOnes
321                                                                                                            : DataHolderHelper::convertValue);
322        return getDataHolder().hasDifferences(viewItemAccessor, convertedValues, context);
323    }
324
325    @Override
326    public Collection<ModelItem> getDifferences(Map<String, Object> values) throws BadItemTypeException
327    {
328        ViewItemAccessor viewItemAccessor = DataHolderHelper.createViewItemAccessorFromValues(getModel(), values);
329        Map<String, Object> convertedValues = DataHolderHelper.convertValues(viewItemAccessor, values, DataHolderHelper::convertValue);
330        return getDataHolder().getDifferences(viewItemAccessor, convertedValues);
331    }
332    
333    @Override
334    public Collection<ModelItem> getDifferences(Map<String, Object> values, SynchronizationContext context) throws BadItemTypeException
335    {
336        ViewItemAccessor viewItemAccessor = DataHolderHelper.createViewItemAccessorFromValues(getModel(), values);
337        Map<String, Object> convertedValues = DataHolderHelper.convertValues(viewItemAccessor, values, context.ignoreIncompatibleValues()
338                ? DataHolderHelper::convertValueIgnoringIncompatibleOnes
339                : DataHolderHelper::convertValue);
340        return getDataHolder().getDifferences(viewItemAccessor, convertedValues, context);
341    }
342    
343    public Collection<ModelItem> getDifferences(ViewItemAccessor viewItemAccessor, Map<String, Object> values) throws BadItemTypeException
344    {
345        Map<String, Object> convertedValues = DataHolderHelper.convertValues(viewItemAccessor, values, DataHolderHelper::convertValue);
346        return getDataHolder().getDifferences(viewItemAccessor, convertedValues);
347    }
348    
349    public Collection<ModelItem> getDifferences(ViewItemAccessor viewItemAccessor, Map<String, Object> values, SynchronizationContext context) throws BadItemTypeException
350    {
351        Map<String, Object> convertedValues = DataHolderHelper.convertValues(viewItemAccessor, values, context.ignoreIncompatibleValues()
352                                                                                                            ? DataHolderHelper::convertValueIgnoringIncompatibleOnes
353                                                                                                            : DataHolderHelper::convertValue);
354        return getDataHolder().getDifferences(viewItemAccessor, convertedValues, context);
355    }
356    
357    @Override
358    public <T extends SynchronizationResult> T synchronizeValues(Map<String, Object> values) throws BadItemTypeException, UndefinedItemPathException
359    {
360        return getDataHolder().synchronizeValues(values);
361    }
362    
363    public <T extends SynchronizationResult> T synchronizeValues(Map<String, Object> values, SynchronizationContext context) throws BadItemTypeException, UndefinedItemPathException
364    {
365        return getDataHolder().synchronizeValues(values, context);
366    }
367    
368    public <T extends SynchronizationResult> T synchronizeValues(ViewItemAccessor viewItemAccessor, Map<String, Object> values) throws BadItemTypeException, UndefinedItemPathException
369    {
370        return getDataHolder().synchronizeValues(viewItemAccessor, values);
371    }
372    
373    public <T extends SynchronizationResult> T synchronizeValues(ViewItemAccessor viewItemAccessor, Map<String, Object> values, SynchronizationContext context) throws BadItemTypeException, UndefinedItemPathException
374    {
375        return getDataHolder().synchronizeValues(viewItemAccessor, values, context);
376    }
377
378    @Override
379    public void setValue(String dataPath, Object value) throws IllegalArgumentException, BadItemTypeException, UndefinedItemPathException, BadDataPathCardinalityException
380    {
381        getDataHolder().setValue(dataPath, value);
382    }
383    
384    public void setLocalValue(String dataPath, Object localValue) throws IllegalArgumentException, BadItemTypeException, UndefinedItemPathException, BadDataPathCardinalityException
385    {
386        getDataHolder().setLocalValue(dataPath, localValue);
387    }
388    
389    public void setExternalValue(String dataPath, Object externalValue) throws IllegalArgumentException, BadItemTypeException, UndefinedItemPathException, BadDataPathCardinalityException
390    {
391        getDataHolder().setExternalValue(dataPath, externalValue);
392    }
393    
394    public void setStatus(String dataPath, ExternalizableDataStatus status) throws IllegalArgumentException, BadItemTypeException, UndefinedItemPathException, BadDataPathCardinalityException
395    {
396        getDataHolder().setStatus(dataPath, status);
397    }
398    
399    @Override
400    public void setValue(String dataPath, Object value, String dataTypeId) throws IllegalArgumentException, BadItemTypeException, UndefinedItemPathException, BadDataPathCardinalityException
401    {
402        ModelAwareJCRAmetysObject.super.setValue(dataPath, value, dataTypeId);
403    }
404    
405    @Override
406    public void removeValue(String dataPath) throws IllegalArgumentException, BadItemTypeException, UndefinedItemPathException, BadDataPathCardinalityException
407    {
408        getDataHolder().removeValue(dataPath);
409    }
410
411    public void removeLocalValue(String dataPath) throws IllegalArgumentException, BadItemTypeException, UndefinedItemPathException, BadDataPathCardinalityException
412    {
413        getDataHolder().removeLocalValue(dataPath);
414    }
415
416    public void removeExternalValue(String dataPath) throws IllegalArgumentException, BadItemTypeException, UndefinedItemPathException, BadDataPathCardinalityException
417    {
418        getDataHolder().removeExternalValue(dataPath);
419    }
420    
421    public void removeExternalizableMetadataIfExists(String dataPath) throws IllegalArgumentException, BadItemTypeException, UndefinedItemPathException, BadDataPathCardinalityException
422    {
423        getDataHolder().removeExternalizableMetadataIfExists(dataPath);
424    }
425    
426    @Override
427    public ModifiableRepositoryData getRepositoryData()
428    {
429        return getDataHolder().getRepositoryData();
430    }
431
432    @Override
433    public Optional<? extends ModifiableModelAwareDataHolder> getParentDataHolder()
434    {
435        return getDataHolder().getParentDataHolder();
436    }
437    
438    @Override
439    public ModifiableModelAwareDataHolder getRootDataHolder()
440    {
441        return getDataHolder().getRootDataHolder();
442    }
443}