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