001/*
002 *  Copyright 2020 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.model;
017
018import java.util.HashSet;
019import java.util.Optional;
020import java.util.Set;
021
022import org.ametys.plugins.repository.AmetysObject;
023import org.ametys.plugins.repository.data.ametysobject.DataAwareAmetysObject;
024import org.ametys.plugins.repository.data.external.ExternalizableDataProvider.ExternalizableDataStatus;
025import org.ametys.runtime.model.ModelHelper;
026import org.ametys.runtime.model.type.DataContext;
027
028/**
029 * Object that gives some context for repository data manipulation
030 */
031public class RepositoryDataContext extends DataContext
032{
033    private Optional<? extends DataAwareAmetysObject> _object = Optional.empty();
034    private Optional<? extends DataAwareAmetysObject> _rootObject = Optional.empty();
035    
036    private Set<String> _externalizableData = new HashSet<>();
037    private boolean _copyExternalMetadata;
038    private Optional<ExternalizableDataStatus> _status = Optional.empty();
039    
040    /**
041     * Creates a new instance of a {@link RepositoryDataContext}
042     */
043    protected RepositoryDataContext()
044    {
045        // Empty constructor
046    }
047
048    /**
049     * Creates a new instance of a {@link RepositoryDataContext} from another {@link DataContext}
050     * @param context the data context to copy
051     */
052    protected RepositoryDataContext(DataContext context)
053    {
054        super(context);
055        if (context instanceof RepositoryDataContext repositoryContext)
056        {
057            withObject(repositoryContext.getObject().orElse(null));
058            _withRootObject(repositoryContext._getRootObject().orElse(null));
059            
060            withExternalizableData(repositoryContext.getExternalizableData());
061            withExternalMetadataInCopy(repositoryContext.copyExternalMetadata());
062        }
063    }
064    
065    /**
066     * Creates a new instance of a {@link RepositoryDataContext}
067     * @return the created instance
068     */
069    public static RepositoryDataContext newInstance()
070    {
071        return new RepositoryDataContext();
072    }
073    
074    /**
075     * Creates a new instance of a {@link RepositoryDataContext} from another {@link DataContext}.
076     * It can be the same implementation or another one, but it will be casted to the current implementation.
077     * @param context the data context to copy
078     * @return the created instance
079     */
080    public static RepositoryDataContext newInstance(DataContext context)
081    {
082        return new RepositoryDataContext(context);
083    }
084    
085    /**
086     * Creates a new instance of a {@link RepositoryDataContext}, with the current context values
087     * @return the created instance
088     */
089    @Override
090    public RepositoryDataContext cloneContext()
091    {
092        return newInstance(this);
093    }
094    
095    /**
096     * Retrieves the object from which the data path is computed
097     * @param <T> Type of the object
098     * @return the object
099     */
100    @SuppressWarnings("unchecked")
101    public <T extends DataAwareAmetysObject> Optional<T> getObject()
102    {
103        return (Optional<T>) _object;
104    }
105    
106    /**
107     * Retrieves the identifier of the object
108     * @return the object's identifier
109     */
110    public Optional<String> getObjectId()
111    {
112        return _object.map(AmetysObject::getId);
113    }
114    
115    /**
116     * Set the object from which the data path is computed
117     * @param <T> the type of the retrieved {@link DataContext}
118     * @param object the object to set
119     * @return the current {@link DataContext}
120     */
121    @SuppressWarnings("unchecked")
122    public <T extends RepositoryDataContext> T withObject(DataAwareAmetysObject object)
123    {
124        _object = Optional.ofNullable(object);
125        
126        if (_rootObject.isEmpty())
127        {
128            _withRootObject(object);
129        }
130
131        return (T) this;
132    }
133    
134    /**
135     * Retrieves the object from which the full data path is computed
136     * @param <T> Type of the root object
137     * @return the root object
138     */
139    @SuppressWarnings("unchecked")
140    protected <T extends DataAwareAmetysObject> Optional<T> _getRootObject()
141    {
142        return (Optional<T>) _rootObject;
143    }
144    
145    /**
146     * Retrieves the identifier of the root object
147     * @return the root object's identifier
148     */
149    public Optional<String> getRootObjectId()
150    {
151        return _rootObject.map(AmetysObject::getId);
152    }
153    
154    /**
155     * Set the object from which the full data path is computed
156     * @param <T> the type of the retrieved {@link DataContext}
157     * @param rootObject the root object to set
158     * @return the current {@link DataContext}
159     */
160    @SuppressWarnings("unchecked")
161    protected <T extends RepositoryDataContext> T _withRootObject(DataAwareAmetysObject rootObject)
162    {
163        _rootObject = Optional.ofNullable(rootObject);
164        return (T) this;
165    }
166    
167    /**
168     * Retrieves the externalizable data
169     * @return the externalizable data
170     */
171    public Set<String> getExternalizableData()
172    {
173        return _externalizableData;
174    }
175    
176    /**
177     * Check if the current data is externalizable
178     * @return <code>true</code> if the data is externalizable, <code>false</code> otherwise
179     */
180    public boolean isDataExternalizable()
181    {
182        String dataPath = getDataPath();
183        String definitionPath = ModelHelper.getDefinitionPathFromDataPath(dataPath);
184        return _externalizableData.contains(definitionPath);
185    }
186    
187    /**
188     * Sets the externalizable data
189     * @param <T> the type of the retrieved {@link DataContext}
190     * @param externalizableData the externalizable data to set
191     * @return the current {@link RepositoryDataContext}
192     */
193    @SuppressWarnings("unchecked")
194    public <T extends RepositoryDataContext> T withExternalizableData(Set<String> externalizableData)
195    {
196        _externalizableData = externalizableData;
197        return (T) this;
198    }
199    
200    /**
201     * Check if the external metadata (alternative and status) should be copied
202     * @return <code>true</code> if the external metadata should be copied, <code>false</code> otherwise
203     */
204    public boolean copyExternalMetadata()
205    {
206        return _copyExternalMetadata;
207    }
208    
209    /**
210     * Set to <code>true</code> to copy the external metadata (alternative and status) (default to <code>false</code>)
211     * @param <T> the type of the retrieved {@link DataContext}
212     * @param copyExternalMetadata <code>true</code> to copy the external metadata, <code>false</code> otherwise
213     * @return the current {@link RepositoryDataContext}
214     */
215    @SuppressWarnings("unchecked")
216    public <T extends RepositoryDataContext> T withExternalMetadataInCopy(boolean copyExternalMetadata)
217    {
218        _copyExternalMetadata = copyExternalMetadata;
219        return (T) this;
220    }
221    
222    /**
223     * Get the status of value to retrieve in the context (the local value or the external value). <code>null</code> to to get the value.
224     * @return the status of value to retrieve in the context
225     */
226    public Optional<ExternalizableDataStatus> getStatus()
227    {
228        return _status;
229    }
230
231    /**
232     * Set the status of value to retrieve
233     * @param <T> the type of the retrieved {@link DataContext}
234     * @param status the status of value to retrieve
235     * @return the current {@link RepositoryDataContext}
236     */
237    @SuppressWarnings("unchecked")
238    public <T extends RepositoryDataContext> T withStatus(ExternalizableDataStatus status)
239    {
240        _status = Optional.ofNullable(status);
241        return (T) this;
242    }
243}