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.runtime.model.type;
017
018import java.util.ArrayList;
019import java.util.List;
020import java.util.Locale;
021import java.util.Optional;
022
023import org.apache.commons.lang3.StringUtils;
024
025import org.ametys.runtime.model.ModelItem;
026
027/**
028 * Object that gives some context for data manipulation
029 */
030public class DataContext
031{
032    private Locale _locale;
033    private List<String> _fullDataPath = new ArrayList<>();
034    private List<String> _dataPath = new ArrayList<>();
035    private Optional<String> _objectId = Optional.empty();
036    private Optional<String> _rootObjectId = Optional.empty();
037    private boolean _renderEmptyValues = true;
038    private Optional<ModelItem> _modelItem = Optional.empty();
039
040    /**
041     * Creates a new instance of a {@link DataContext}
042     */
043    protected DataContext()
044    {
045        // Empty private constructor
046    }
047    
048    /**
049     * Creates a new instance of a {@link DataContext}
050     * @return the created instance
051     */
052    public static DataContext newInstance()
053    {
054        return new DataContext();
055    }
056    
057    /**
058     * Creates a new instance of a {@link DataContext}, with the current context values
059     * @return the created instance
060     */
061    public DataContext cloneContext()
062    {
063        DataContext clone = new DataContext();
064        _cloneContextData(clone);
065        return clone;
066    }
067    
068    /**
069     * Clone the data of the current context in the given one
070     * @param context the clone
071     */
072    protected void _cloneContextData(DataContext context)
073    {
074        context.withLocale(getLocale())
075            .withDataPath(getDataPath())
076            .withFullDataPath(getFullDataPath())
077            .withObjectId(getObjectId().orElse(null))
078            .withRootObjectId(getRootObjectId().orElse(null))
079            .withEmptyValues(renderEmptyValues())
080            .withModelItem(getModelItem().orElse(null));
081    }
082    
083    /**
084     * Retrieves the locale to use for localized data
085     * @return the locale, or null if no local has been set
086     */
087    public Locale getLocale()
088    {
089        return _locale;
090    }
091    
092    /**
093     * Sets the locale to use for localized data.
094     * Do not set {@link Locale} to use all existing ones.
095     * @param locale the locale to set
096     * @return the current {@link DataContext}
097     */
098    public DataContext withLocale(Locale locale)
099    {
100        _locale = locale;
101        return this;
102    }
103    
104    /**
105     * Retrieves the data path
106     * @return the dataPath
107     */
108    public String getDataPath()
109    {
110        return StringUtils.join(_dataPath, ModelItem.ITEM_PATH_SEPARATOR);
111    }
112    
113    /**
114     * Retrieves the last segment of the data path
115     * @return the last segment of the data path
116     */
117    public String getDataPathLastSegment()
118    {
119        return _dataPath.get(_dataPath.size() - 1);
120    }
121    
122    /**
123     * Sets the data path
124     * @param dataPath the data path to set
125     * @return the current {@link DataContext}
126     */
127    public DataContext withDataPath(String dataPath)
128    {
129        _dataPath = new ArrayList<>();
130        if (StringUtils.isNotEmpty(dataPath))
131        {
132            _dataPath.add(dataPath);
133            _fullDataPath.add(dataPath);
134        }
135        return this;
136    }
137    
138    /**
139     * Retrieves the full data path
140     * @return the fullDataPath
141     */
142    public String getFullDataPath()
143    {
144        return StringUtils.join(_fullDataPath, ModelItem.ITEM_PATH_SEPARATOR);
145    }
146    
147    /**
148     * Sets the full data path
149     * @param fullDataPath the full data path to set
150     * @return the current {@link DataContext}
151     */
152    public DataContext withFullDataPath(String fullDataPath)
153    {
154        _fullDataPath = new ArrayList<>();
155        if (StringUtils.isNotEmpty(fullDataPath))
156        {
157            _fullDataPath.add(fullDataPath);
158        }
159        return this;
160    }
161    
162    /**
163     * Add a segment to the data path
164     * @param segment the segment to add to the data path
165     * @return the current {@link DataContext}
166     */
167    public DataContext addSegmentToDataPath(String segment)
168    {
169        _dataPath.add(segment);
170        _fullDataPath.add(segment);
171        return this;
172    }
173    
174    /**
175     * Add a suffix to the last segment of the data path
176     * @param suffix the suffix to add to the last segment
177     * @return the current {@link DataContext}
178     */
179    public DataContext addSuffixToLastSegment(String suffix)
180    {
181        int segmentIndex = _dataPath.size() - 1;
182        _dataPath.set(segmentIndex, _dataPath.get(segmentIndex) + suffix);
183        
184        segmentIndex = _fullDataPath.size() - 1;
185        _fullDataPath.set(segmentIndex, _fullDataPath.get(segmentIndex) + suffix);
186        
187        return this;
188    }
189    
190    /**
191     * Retrieves the identifier of the object from which the data path is computed
192     * @return the object identifier
193     */
194    public Optional<String> getObjectId()
195    {
196        return _objectId;
197    }
198    
199    /**
200     * Set the identifier of the object from which the data path is computed
201     * @param objectId the object identifier to set
202     * @return the current {@link DataContext}
203     */
204    public DataContext withObjectId(String objectId)
205    {
206        _objectId = Optional.ofNullable(objectId)
207                .filter(StringUtils::isNotEmpty);
208        if (_rootObjectId.isEmpty())
209        {
210            _rootObjectId = _objectId;
211        }
212        return this;
213    }
214
215    /**
216     * Retrieves the identifier of the object from which the full data path is computed
217     * @return the root object identifier
218     */
219    public Optional<String> getRootObjectId()
220    {
221        return _rootObjectId;
222    }
223    
224    /**
225     * Set the identifier of the object from which the full data path is computed
226     * @param rootObjectId the root object identifier to set
227     * @return the current {@link DataContext}
228     */
229    public DataContext withRootObjectId(String rootObjectId)
230    {
231        _rootObjectId = Optional.ofNullable(rootObjectId)
232                .filter(StringUtils::isNotEmpty);
233        return this;
234    }
235    
236    /**
237     * Determines if the empty values have to be rendered
238     * @return <code>true</code> if empty values must be rendered (default), <code>false</code> otherwise
239     */
240    public boolean renderEmptyValues()
241    {
242        return _renderEmptyValues;
243    }
244    
245    /**
246     * Set to <code>false</code> to not render empty values (default to <code>true</code>) 
247     * @param renderEmptyValues <code>true</code> to render the empty values, <code>false</code> otherwise
248     * @return the current {@link DataContext}
249     */
250    public DataContext withEmptyValues(boolean renderEmptyValues)
251    {
252        _renderEmptyValues = renderEmptyValues;
253        return this;
254    }
255    
256    /**
257     * Retrieves the model item
258     * @return the model item
259     */
260    public Optional<ModelItem> getModelItem()
261    {
262        return _modelItem;
263    }
264    
265    /**
266     * Sets the model item
267     * @param modelItem the model item to set
268     * @return the current {@link DataContext}
269     */
270    public DataContext withModelItem(ModelItem modelItem)
271    {
272        _modelItem = Optional.ofNullable(modelItem);
273        return this;
274    }
275}