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