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