001/*
002 *  Copyright 2019 Anyware Services
003 *
004 *  Licensed under the Apache License, Version 2.0 (the "License");
005 *  you may not use this file except in compliance with the License.
006 *  You may obtain a copy of the License at
007 *
008 *      http://www.apache.org/licenses/LICENSE-2.0
009 *
010 *  Unless required by applicable law or agreed to in writing, software
011 *  distributed under the License is distributed on an "AS IS" BASIS,
012 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 *  See the License for the specific language governing permissions and
014 *  limitations under the License.
015 */
016package org.ametys.cms.data;
017
018import java.util.Collection;
019import java.util.List;
020import java.util.Map;
021import java.util.Objects;
022import java.util.Optional;
023
024import javax.jcr.RepositoryException;
025import javax.jcr.Session;
026
027import org.apache.commons.collections4.CollectionUtils;
028import org.xml.sax.ContentHandler;
029import org.xml.sax.SAXException;
030
031import org.ametys.cms.data.type.AbstractContentElementType;
032import org.ametys.cms.repository.ModifiableContent;
033import org.ametys.plugins.repository.AmetysObjectResolver;
034import org.ametys.plugins.repository.AmetysRepositoryException;
035import org.ametys.plugins.repository.UnknownAmetysObjectException;
036import org.ametys.plugins.repository.data.DataComment;
037import org.ametys.plugins.repository.data.UnknownDataException;
038import org.ametys.plugins.repository.data.external.ExternalizableDataProvider.ExternalizableDataStatus;
039import org.ametys.plugins.repository.data.holder.ModifiableModelAwareDataHolder;
040import org.ametys.plugins.repository.data.holder.group.impl.ModifiableModelAwareComposite;
041import org.ametys.plugins.repository.data.holder.group.impl.ModifiableModelAwareRepeater;
042import org.ametys.plugins.repository.data.holder.values.SynchronizationContext;
043import org.ametys.plugins.repository.data.holder.values.SynchronizationResult;
044import org.ametys.plugins.repository.data.repositorydata.ModifiableRepositoryData;
045import org.ametys.runtime.model.ModelItem;
046import org.ametys.runtime.model.ModelItemContainer;
047import org.ametys.runtime.model.ViewItemContainer;
048import org.ametys.runtime.model.exception.BadDataPathCardinalityException;
049import org.ametys.runtime.model.exception.BadItemTypeException;
050import org.ametys.runtime.model.exception.UndefinedItemPathException;
051import org.ametys.runtime.model.type.DataContext;
052
053/**
054 * Content wrapper used by attributes of type content
055 * @see AbstractContentElementType
056 */
057public class ContentValue implements ModifiableModelAwareDataHolder
058{
059    private AmetysObjectResolver _resolver;
060    private String _contentId;
061    private ModifiableContent _content;
062    private Session _session;
063
064    /**
065     * Constructor of the content wrapper
066     * @param content the existing content
067     */
068    public ContentValue(ModifiableContent content)
069    {
070        _content = content;
071        _contentId = content.getId();
072    }
073    
074    /**
075     * Constructor of the content wrapper
076     * @param resolver resolver used to get the content from its identifier
077     * @param contentId content identifier
078     */
079    public ContentValue(AmetysObjectResolver resolver, String contentId)
080    {
081        this(resolver, contentId, null);
082    }
083
084    /**
085     * Constructor of the content wrapper
086     * @param resolver resolver used to get the content from its identifier
087     * @param contentId content identifier
088     * @param session the current session. If <code>null</code>, a new session will be used to retrieve the content 
089     */
090    public ContentValue(AmetysObjectResolver resolver, String contentId, Session session)
091    {
092        _resolver = resolver;
093        _contentId = contentId;
094        _session = session;
095    }
096    
097    /**
098     * Retrieves the content's identifier
099     * @return the content's identifier
100     */
101    public String getContentId()
102    {
103        return _contentId;
104    }
105    
106    /**
107     * Retrieves the content
108     * @return the content
109     * @throws AmetysRepositoryException if an error occurs.
110     * @throws UnknownAmetysObjectException if no content exists for the identifier
111     */
112    public ModifiableContent getContent() throws AmetysRepositoryException, UnknownAmetysObjectException
113    {
114        if (_content == null)
115        {
116            if (_session != null)
117            {
118                try
119                {
120                    _content = _resolver.resolveById(_contentId, _session);
121                }
122                catch (RepositoryException e)
123                {
124                    throw new AmetysRepositoryException("Unable to retrieve the content with the id '" + _contentId + "'.", e);
125                }
126            }
127            else
128            {
129                _content = _resolver.resolveById(_contentId);
130            }
131        }
132        
133        return _content; 
134    }
135    
136    /**
137     * Retrieves an {@link Optional} describing the content, or an empty {@link Optional} if the content does not exist 
138     * @return an {@link Optional} describing the content
139     */
140    public Optional<ModifiableContent> getContentIfExists()
141    {
142        try
143        {
144            return Optional.ofNullable(getContent());
145        }
146        catch (AmetysRepositoryException e)
147        {
148            return Optional.empty();
149        }
150    }
151    
152    @Override
153    public int hashCode()
154    {
155        return Objects.hash(_contentId);
156    }
157
158    @Override
159    public boolean equals(Object obj)
160    {
161        if (!(obj instanceof ContentValue))
162        {
163            return false;
164        }
165        
166        return Objects.equals(_contentId, ((ContentValue) obj)._contentId);
167    }
168
169    public boolean hasValue(String dataPath) throws IllegalArgumentException, BadDataPathCardinalityException
170    {
171        return getContentIfExists()
172                .map(c -> c.hasValue(dataPath))
173                .orElse(false);
174    }
175    
176    public boolean hasLocalValue(String dataPath) throws IllegalArgumentException, BadDataPathCardinalityException
177    {
178        return getContentIfExists()
179                .map(c -> c.hasLocalValue(dataPath))
180                .orElse(false);
181    }
182    
183    public boolean hasExternalValue(String dataPath) throws IllegalArgumentException, BadDataPathCardinalityException
184    {
185        return getContentIfExists()
186                .map(c -> c.hasExternalValue(dataPath))
187                .orElse(false);
188    }
189
190    public boolean hasValueOrEmpty(String dataPath) throws IllegalArgumentException, BadDataPathCardinalityException
191    {
192        return getContentIfExists()
193                .map(c -> c.hasValueOrEmpty(dataPath))
194                .orElse(false);
195    }
196    
197    public boolean hasLocalValueOrEmpty(String dataPath) throws IllegalArgumentException, BadDataPathCardinalityException
198    {
199        return getContentIfExists()
200                .map(c -> c.hasLocalValueOrEmpty(dataPath))
201                .orElse(false);
202    }
203    
204    public boolean hasExternalValueOrEmpty(String dataPath) throws IllegalArgumentException, BadDataPathCardinalityException
205    {
206        return getContentIfExists()
207                .map(c -> c.hasExternalValueOrEmpty(dataPath))
208                .orElse(false);
209    }
210    
211    public boolean hasComments(String dataName) throws IllegalArgumentException, UndefinedItemPathException
212    {
213        return getContentIfExists()
214                .map(c -> c.hasComments(dataName))
215                .orElse(false);
216    }
217
218    public Collection<String> getDataNames()
219    {
220        return getContentIfExists()
221                .map(c -> c.getDataNames())
222                .orElse(CollectionUtils.EMPTY_COLLECTION);
223    }
224    
225    public <T> T getValue(String dataPath) throws IllegalArgumentException, UndefinedItemPathException, BadItemTypeException, BadDataPathCardinalityException
226    {
227        return getContentIfExists()
228                .map(c -> c.<T>getValue(dataPath))
229                .orElse(null);
230    }
231
232    public <T> T getValue(String dataPath, boolean allowMultiValuedPathSegments) throws IllegalArgumentException, UndefinedItemPathException, BadItemTypeException, BadDataPathCardinalityException
233    {
234        return getContentIfExists()
235                .map(c -> c.<T>getValue(dataPath, allowMultiValuedPathSegments))
236                .orElse(null);
237    }
238
239    public <T> T getValue(String dataPath, boolean useDefaultFromModel, T defaultValue) throws IllegalArgumentException, UndefinedItemPathException, BadItemTypeException, BadDataPathCardinalityException
240    {
241        return getContentIfExists()
242                .map(c -> c.<T>getValue(dataPath, useDefaultFromModel, defaultValue))
243                .orElse(null);
244    }
245    
246    public <T> T getLocalValue(String dataPath) throws IllegalArgumentException, UndefinedItemPathException, BadItemTypeException, BadDataPathCardinalityException
247    {
248        return getContentIfExists()
249                .map(c -> c.<T>getLocalValue(dataPath))
250                .orElse(null);
251    }
252    
253    public <T> T getExternalValue(String dataPath) throws IllegalArgumentException, UndefinedItemPathException, BadItemTypeException, BadDataPathCardinalityException
254    {
255        return getContentIfExists()
256                .map(c -> c.<T>getExternalValue(dataPath))
257                .orElse(null);
258    }
259    
260    public ExternalizableDataStatus getStatus(String dataPath) throws IllegalArgumentException, UndefinedItemPathException, BadDataPathCardinalityException
261    {
262        return getContentIfExists()
263                .map(c -> c.getStatus(dataPath))
264                .orElse(ExternalizableDataStatus.LOCAL);
265    }
266    
267    public List<DataComment> getComments(String dataName) throws IllegalArgumentException, UndefinedItemPathException
268    {
269        return getContentIfExists()
270                .map(c -> c.getComments(dataName))
271                .orElse(null);
272    }
273    
274    public Collection< ? extends ModelItemContainer> getModel()
275    {
276        return getContentIfExists()
277                .map(c -> c.getModel())
278                .orElse(null);
279    }
280
281    public ModelItem getDefinition(String path) throws IllegalArgumentException, UndefinedItemPathException
282    {
283        return getContentIfExists()
284                .map(c -> c.getDefinition(path))
285                .orElse(null);
286    }
287    
288    public boolean hasDefinition(String path) throws IllegalArgumentException
289    {
290        return getContentIfExists()
291                .map(c -> c.hasDefinition(path))
292                .orElse(null);
293    }
294
295    public ModifiableModelAwareComposite getComposite(String compositePath) throws IllegalArgumentException, UndefinedItemPathException, BadItemTypeException, BadDataPathCardinalityException
296    {
297        return getContentIfExists()
298                .map(c -> c.getComposite(compositePath))
299                .orElse(null);
300    }
301    
302    public ModifiableModelAwareComposite getLocalComposite(String compositePath) throws IllegalArgumentException, UndefinedItemPathException, BadItemTypeException, BadDataPathCardinalityException
303    {
304        return getContentIfExists()
305                .map(c -> c.getLocalComposite(compositePath))
306                .orElse(null);
307    }
308    
309    public ModifiableModelAwareComposite getExternalComposite(String compositePath) throws IllegalArgumentException, UndefinedItemPathException, BadItemTypeException, BadDataPathCardinalityException
310    {
311        return getContentIfExists()
312                .map(c -> c.getExternalComposite(compositePath))
313                .orElse(null);
314    }
315
316    public ModifiableModelAwareRepeater getRepeater(String repeaterPath) throws IllegalArgumentException, UndefinedItemPathException, BadItemTypeException, BadDataPathCardinalityException
317    {
318        return getContentIfExists()
319                .map(c -> c.getRepeater(repeaterPath))
320                .orElse(null);
321    }
322    
323    public ModifiableModelAwareRepeater getLocalRepeater(String repeaterPath) throws IllegalArgumentException, UndefinedItemPathException, BadItemTypeException, BadDataPathCardinalityException
324    {
325        return getContentIfExists()
326                .map(c -> c.getLocalRepeater(repeaterPath))
327                .orElse(null);
328    }
329    
330    public ModifiableModelAwareRepeater getExternalRepeater(String repeaterPath) throws IllegalArgumentException, UndefinedItemPathException, BadItemTypeException, BadDataPathCardinalityException
331    {
332        return getContentIfExists()
333                .map(c -> c.getExternalRepeater(repeaterPath))
334                .orElse(null);
335    }
336
337    public void dataToSAX(ContentHandler contentHandler, String dataPath, DataContext context) throws SAXException
338    {
339        getContent().dataToSAX(contentHandler, dataPath, context);
340    }
341    
342    public ModifiableModelAwareComposite getComposite(String compositePath, boolean createNew) throws IllegalArgumentException, UndefinedItemPathException, BadItemTypeException, BadDataPathCardinalityException
343    {
344        return getContentIfExists()
345                .map(c -> c.getComposite(compositePath, createNew))
346                .orElse(null);
347    }
348    
349    public ModifiableModelAwareComposite getLocalComposite(String compositePath, boolean createNew) throws IllegalArgumentException, UndefinedItemPathException, BadItemTypeException, BadDataPathCardinalityException
350    {
351        return getContentIfExists()
352                .map(c -> c.getLocalComposite(compositePath, createNew))
353                .orElse(null);
354    }
355    
356    public ModifiableModelAwareComposite getExternalComposite(String compositePath, boolean createNew) throws IllegalArgumentException, UndefinedItemPathException, BadItemTypeException, BadDataPathCardinalityException
357    {
358        return getContentIfExists()
359                .map(c -> c.getExternalComposite(compositePath, createNew))
360                .orElse(null);
361    }
362
363    public ModifiableModelAwareRepeater getRepeater(String repeaterPath, boolean createNew) throws IllegalArgumentException, UndefinedItemPathException, BadItemTypeException, BadDataPathCardinalityException
364    {
365        return getContentIfExists()
366                .map(c -> c.getRepeater(repeaterPath, createNew))
367                .orElse(null);
368    }
369    
370    public ModifiableModelAwareRepeater getLocalRepeater(String repeaterPath, boolean createNew) throws IllegalArgumentException, UndefinedItemPathException, BadItemTypeException, BadDataPathCardinalityException
371    {
372        return getContentIfExists()
373                .map(c -> c.getLocalRepeater(repeaterPath, createNew))
374                .orElse(null);
375    }
376    
377    public ModifiableModelAwareRepeater getExternalRepeater(String repeaterPath, boolean createNew) throws IllegalArgumentException, UndefinedItemPathException, BadItemTypeException, BadDataPathCardinalityException
378    {
379        return getContentIfExists()
380                .map(c -> c.getExternalRepeater(repeaterPath, createNew))
381                .orElse(null);
382    }
383    
384    public <T extends SynchronizationResult> T synchronizeValues(Map<String, Object> values) throws UndefinedItemPathException, BadItemTypeException
385    {
386        return getContent().synchronizeValues(values);
387    }
388    
389    public <T extends SynchronizationResult> T synchronizeValues(Map<String, Object> values, SynchronizationContext context) throws UndefinedItemPathException, BadItemTypeException
390    {
391        return getContent().synchronizeValues(values, context);
392    }
393    
394    public <T extends SynchronizationResult> T synchronizeValues(ViewItemContainer viewItemContainer, Map<String, Object> values) throws UndefinedItemPathException, BadItemTypeException
395    {
396        return getContent().synchronizeValues(viewItemContainer, values);
397    }
398    
399    public <T extends SynchronizationResult> T synchronizeValues(ViewItemContainer viewItemContainer, Map<String, Object> values, SynchronizationContext context) throws UndefinedItemPathException, BadItemTypeException
400    {
401        return getContent().synchronizeValues(viewItemContainer, values, context);
402    }
403
404    public void setValue(String dataPath, Object value) throws IllegalArgumentException, UndefinedItemPathException, BadItemTypeException, BadDataPathCardinalityException
405    {
406        getContent().setValue(dataPath, value);
407    }
408    
409    public void setLocalValue(String dataPath, Object localValue) throws IllegalArgumentException, UndefinedItemPathException, BadItemTypeException, BadDataPathCardinalityException
410    {
411        getContent().setLocalValue(dataPath, localValue);
412    }
413    
414    public void setExternalValue(String dataPath, Object externalValue) throws IllegalArgumentException, UndefinedItemPathException, BadItemTypeException, BadDataPathCardinalityException
415    {
416        getContent().setExternalValue(dataPath, externalValue);
417    }
418    
419    public void setStatus(String dataPath, ExternalizableDataStatus status) throws IllegalArgumentException, UndefinedItemPathException, BadItemTypeException, BadDataPathCardinalityException
420    {
421        getContent().setStatus(dataPath, status);
422    }
423    
424    public void setComments(String dataName, List<DataComment> comments) throws IllegalArgumentException, UndefinedItemPathException
425    {
426        getContent().setComments(dataName, comments);
427    }
428
429    public void removeValue(String dataPath) throws IllegalArgumentException, UndefinedItemPathException, UnknownDataException, BadDataPathCardinalityException
430    {
431        getContent().removeValue(dataPath);
432    }
433    
434    public void removeLocalValue(String dataPath) throws IllegalArgumentException, UndefinedItemPathException, BadItemTypeException, UnknownDataException, BadDataPathCardinalityException
435    {
436        getContent().removeLocalValue(dataPath);
437    }
438    
439    public void removeExternalValue(String dataPath) throws IllegalArgumentException, UndefinedItemPathException, BadItemTypeException, UnknownDataException, BadDataPathCardinalityException
440    {
441        getContent().removeExternalValue(dataPath);
442    }
443    
444    public void removeExternalizableMetadataIfExists(String dataPath) throws IllegalArgumentException, BadItemTypeException, UndefinedItemPathException, BadDataPathCardinalityException
445    {
446        getContent().removeExternalizableMetadataIfExists(dataPath);
447    }
448    
449    public ModifiableRepositoryData getRepositoryData()
450    {
451        return getContent().getRepositoryData();
452    }
453    
454    public Optional<? extends ModifiableModelAwareDataHolder> getParentDataHolder()
455    {
456        return getContent().getParentDataHolder();
457    }
458    
459    public ModifiableModelAwareDataHolder getRootDataHolder()
460    {
461        return getContent().getRootDataHolder();
462    }
463}