001/**
002 * DataCase -- A collection of parameters that make up a single case in a data set.
003 *
004 * Copyright (C) 2003-2025, by Joseph A. Huwaldt. All rights reserved.
005 *
006 * This library is free software; you can redistribute it and/or modify it under the terms
007 * of the GNU Lesser General Public License as published by the Free Software Foundation;
008 * either version 2 of the License, or (at your option) any later version.
009 *
010 * This library is distributed in the hope that it will be useful, but WITHOUT ANY
011 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
012 * PARTICULAR PURPOSE. See the GNU Library General Public License for more details.
013 *
014 * You should have received a copy of the GNU Lesser General Public License along with
015 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place -
016 * Suite 330, Boston, MA 02111-1307, USA. Or visit: http://www.gnu.org/licenses/lgpl.html
017 */
018package jahuwaldt.js.datareader;
019
020import java.util.Collection;
021import java.util.List;
022import java.util.ListIterator;
023import javolution.context.ObjectFactory;
024import javolution.util.FastTable;
025
026/**
027 * Defines a data element called a case. A case is a collection of
028 * {@link DataParam parameters} or variables that make up a single run or test case. Any
029 * number of parameters may be added to a case, but all array type parameters in a single
030 * case must have the same number of elements.
031 *
032 * <p> Modified by: Joseph A. Huwaldt </p>
033 *
034 * @author Joseph A. Huwaldt, Date: March 5, 2003
035 * @version February 23, 2025
036 */
037public final class DataCase extends DataElementList<DataParam> {
038
039    /**
040     * Return a case made up of any {@link DataParam parameters} found in the specified
041     * collection. Any objects that are not {@link DataParam} objects in the specified
042     * collection will be ignored.
043     *
044     * @param name   The name to be assigned to this case (may not be <code>null</code>).
045     * @param params A collection that contains a set of parameters.
046     * @return A new case made up of any DataParam objects found in the specified collection.
047     * @throws ArrayIndexOutOfBoundsException if an array added to this case has a
048     * different number of elements than an array already in this case.
049     */
050    public static DataCase valueOf(CharSequence name, Collection<?> params) throws ArrayIndexOutOfBoundsException {
051
052        DataCase aCase = DataCase.newInstance(name);
053
054        //  Determine the size allowed for arrays in this case.
055        int allowedSize = 0;
056
057        for (Object obj : params) {
058            if (obj instanceof ArrayParam) {
059
060                //  Check size of arrays for consistency before adding!
061                ArrayParam newArray = (ArrayParam)obj;
062                int size = newArray.size();
063                if (allowedSize > 0)
064                    if (size != allowedSize) {
065                        DataCase.recycle(aCase);
066                        throw new ArrayIndexOutOfBoundsException(RESOURCES.getString("arrSizeErr"));
067                    } else
068                        allowedSize = size;
069
070                aCase.add((DataParam)obj);
071
072            } else if (obj instanceof DataParam) {
073                aCase.add((DataParam)obj);
074            }
075        }
076
077        return aCase;
078    }
079
080    /**
081     * Replaces the parameter at the specified position in this case with the specified
082     * parameter. Null elements are ignored.
083     *
084     * @param index   The index of the parameter to replace.
085     * @param element The parameter to be stored a the specified position.
086     * @return The parameter previously at the specified position in this case.
087     * @throws ArrayIndexOutOfBoundsException if an array added to this case has a
088     * different number of elements than an array already in this case.
089     */
090    @Override
091    public DataParam set(int index, DataParam element) {
092        if (element == null)
093            return null;
094
095        if (element instanceof ArrayParam) {
096            //  Determine the size allowed for arrays in this case.
097            int allowedSize = getAllowedArraySize();
098
099            if (allowedSize > 0 && ((ArrayParam)element).size() != allowedSize)
100                throw new ArrayIndexOutOfBoundsException(RESOURCES.getString("arrSizeErr"));
101        }
102
103        return super.set(index, element);
104    }
105
106    /**
107     * Inserts the specified parameter at the specified position in this case. Shifts the
108     * parameter currently at that position (if any) and any subsequent parameters to the
109     * right (adds one to their indices). Null elements are ignored.
110     *
111     * @param index   Index at which the specified parameter is to be inserted.
112     * @param element DataParam to be inserted.
113     * @throws ClassCastException if the specified element is not a DataParam type object.
114     * @throws ArrayIndexOutOfBoundsException if an array added to this case has a
115     * different number of elements than an array already in this case.
116     */
117    @Override
118    public void add(int index, DataParam element) {
119        if (element == null)
120            return;
121
122        if (element instanceof ArrayParam) {
123            //  Determine the size allowed for arrays in this case.
124            int allowedSize = getAllowedArraySize();
125
126            if (allowedSize > 0 && ((ArrayParam)element).size() != allowedSize)
127                throw new ArrayIndexOutOfBoundsException(RESOURCES.getString("arrSizeErr"));
128        }
129
130        super.add(index, element);
131    }
132
133    /**
134     * Appends the specified list of parameters to the end of this case. Null elements are
135     * ignored.
136     *
137     * @param elements List of parameters to be inserted.
138     * @throws ArrayIndexOutOfBoundsException if an array added to this case has a
139     * different number of elements than an array already in this case.
140     */
141    @Override
142    public void addAll(DataParam[] elements) {
143        if (elements != null) {
144            int size = elements.length;
145            for (int i = 0; i < size; ++i) {
146                DataParam param = elements[i];
147                this.add(param);
148            }
149        }
150    }
151
152    /**
153     * Inserts the specified list of parameters at the specified position in this case.
154     * Shifts the parameter currently at that position (if any) and any subsequent
155     * parameters to the right (adds one to their indices) until all the parameters have
156     * been added. Null elements are ignored.
157     *
158     * @param index    Index at which the specified list of parameters is to be inserted.
159     * @param elements List of parameters to be inserted.
160     * @throws ArrayIndexOutOfBoundsException if an array added to this case has a
161     * different number of elements than an array already in this case.
162     */
163    @Override
164    public void addAll(int index, DataParam[] elements) {
165        if (elements != null) {
166
167            for (DataParam param : elements) {
168                this.add(index, param);
169                ++index;
170            }
171
172        }
173    }
174
175    /**
176     * Returns the size of any array in this case (since all arrays must have the same
177     * number of elements). If there are no arrays in this case, zero is returned.
178     *
179     * @return The size of any array in this case.
180     */
181    public int getAllowedArraySize() {
182        int size = 0;
183
184        for (DataParam param : this) {
185            if (param instanceof ArrayParam) {
186                size = ((ArrayParam)param).size();
187                break;
188            }
189        }
190
191        return size;
192    }
193
194    /**
195     * Returns a list of all the Text/Note objects in this case. If there are no text
196     * parameters, null is returned.
197     *
198     * @return A list of all the Text/Note objects in this case.
199     */
200    public List<TextParam> getAllText() {
201
202        List<TextParam> list = FastTable.newInstance();
203
204        //  Loop over all the parameters and collect the text parameters.
205        for (DataParam param : this) {
206            if (param instanceof TextParam)
207                list.add((TextParam)param);
208        }
209
210        return list;
211    }
212
213    /**
214     * Removes all TextParam objects from this case.
215     */
216    public void clearAllText() {
217
218        //  Loop over all the parameters and delete text parameters.
219        for (ListIterator<DataParam> i = listIterator(); i.hasNext();) {
220            Object obj = i.next();
221            if (obj instanceof TextParam)
222                i.remove();
223        }
224    }
225
226    /**
227     * Returns a list of all the {@link ScalarParam} objects in this case. If there are no scalar
228     * parameters, null is returned.
229     *
230     * @return A list of all the ScalarParam objects in this case.
231     */
232    public List<ScalarParam> getAllScalars() {
233
234        List<ScalarParam> list = FastTable.newInstance();
235
236        //  Loop over all the parameters and collect the scalar parameters.
237        for (DataParam param : this) {
238            if (param instanceof ScalarParam)
239                list.add((ScalarParam)param);
240        }
241
242        return list;
243    }
244
245    /**
246     * Returns a list of all the {@link ArrayParam} objects in this case. If there are no array
247     * parameters, null is returned.
248     *
249     * @return A list of all the ArrayParam objects in this case.
250     */
251    public List<ArrayParam> getAllArrays() {
252
253        List<ArrayParam> list = FastTable.newInstance();
254
255        //  Loop over all the parameters and collect the array parameters.
256        for (DataParam param : this) {
257            if (param instanceof ArrayParam)
258                list.add((ArrayParam)param);
259        }
260
261        return list;
262    }
263
264    //////////////////////
265    // Factory Creation //
266    //////////////////////
267    public static DataCase newInstance(CharSequence name) {
268        DataCase o = FACTORY.object();
269        try {
270            o.setName(name);
271        } catch (NullPointerException e) {
272            FACTORY.recycle(o);
273            throw e;
274        }
275        return o;
276    }
277    private static final ObjectFactory<DataCase> FACTORY = new ObjectFactory<DataCase>() {
278        @Override
279        protected DataCase create() {
280            return new DataCase();
281        }
282
283        @Override
284        protected void cleanup(DataCase obj) {
285            obj.reset();
286        }
287    };
288
289    /**
290     * Recycles a case instance immediately (on the stack when executing in a
291     * StackContext).
292     *
293     * @param instance The case instance to recycle.
294     */
295    public static void recycle(DataCase instance) {
296        FACTORY.recycle(instance);
297    }
298
299    /**
300     * Do not allow the default constructor to be used.
301     */
302    private DataCase() { }
303}