001/**
002 * PointVehicle -- A list of PointComponent objects.
003 *
004 * Copyright (C) 2009-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.1 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 geomss.geom;
019
020import java.text.MessageFormat;
021import java.util.Collection;
022import static java.util.Objects.requireNonNull;
023import javax.measure.converter.ConversionException;
024import javax.measure.quantity.Length;
025import javax.measure.unit.Unit;
026import javolution.context.ObjectFactory;
027import javolution.util.FastTable;
028import javolution.xml.XMLFormat;
029import javolution.xml.stream.XMLStreamException;
030
031/**
032 * A list that holds only {@link PointComponent} objects.
033 * <p>
034 * WARNING: This list allows geometry to be stored in different units. If consistent units
035 * are required, then the user must specifically convert the list items.
036 * </p>
037 * 
038 * <p> Modified by: Joseph A. Huwaldt </p>
039 * 
040 * @author Joseph A. Huwaldt, Date: May 1, 2009
041 * @version February 17, 2025
042 */
043@SuppressWarnings({"serial", "CloneableImplementsClone"})
044public final class PointVehicle extends AbstractPointGeomList<PointVehicle, PointComponent> {
045
046    private FastTable<PointComponent> _list;
047
048    /**
049     * Return the list underlying this geometry list.
050     *
051     * @return The list underlying this geometry list.
052     */
053    @Override
054    protected FastTable<PointComponent> getList() {
055        return _list;
056    }
057
058    /**
059     * Returns a new, empty, preallocated or recycled <code>PointVehicle</code>
060     * instance (on the stack when executing in a <code>StackContext</code>),
061     * that can store a list of {@link PointComponent} objects.
062     *
063     * @return A new, empty PointVehicle.
064     */
065    public static PointVehicle newInstance() {
066        PointVehicle list = FACTORY.object();
067        list._list = FastTable.newInstance();
068        return list;
069    }
070
071    /**
072     * Returns a new, empty, preallocated or recycled <code>PointVehicle</code>
073     * instance (on the stack when executing in a <code>StackContext</code>)
074     * with the specified name, that can store a list of {@link PointComponent}
075     * objects.
076     *
077     * @param name The name to be assigned to this list (may be <code>null</code>).
078     * @return A new, empty PointVehicle.
079     */
080    public static PointVehicle newInstance(String name) {
081        PointVehicle list = PointVehicle.newInstance();
082        list.setName(name);
083        return list;
084    }
085
086    /**
087     * Return a PointVehicle made up of the {@link PointComponent} objects in
088     * the specified collection.
089     *
090     * @param name The name to be assigned to this list (may be <code>null</code>).
091     * @param elements A collection of PointComponent elements. May not be null.
092     * @return A new PointVehicle containing the elements in the specified collection.
093     */
094    public static PointVehicle valueOf(String name, Collection<? extends PointComponent> elements) {
095        for (Object element : elements) {
096            requireNonNull(element, RESOURCES.getString("collectionElementsNullErr"));
097            if (!(element instanceof PointComponent))
098                throw new ClassCastException(MessageFormat.format(
099                        RESOURCES.getString("listElementTypeErr"), "PointVehicle", "PointComponent"));
100        }
101
102        PointVehicle list = PointVehicle.newInstance(name);
103        list.addAll(elements);
104
105        return list;
106    }
107
108    /**
109     * Return a PointVehicle made up of the {@link PointComponent} objects in
110     * the specified array.
111     *
112     * @param name The name to be assigned to this list (may be <code>null</code>).
113     * @param elements A array of PointComponent elements. May not be null.
114     * @return A new PointVehicle containing the elements in the specified array.
115     */
116    public static PointVehicle valueOf(String name, PointComponent... elements) {
117        requireNonNull(elements);
118        PointVehicle list = PointVehicle.newInstance(name);
119        list.addAll(elements);
120
121        return list;
122    }
123
124    /**
125     * Return a PointVehicle made up of the {@link PointComponent} objects in
126     * the specified list.
127     *
128     * @param elements A list of PointComponent elements. May not be null.
129     * @return A new PointVehicle containing the elements in the specified array.
130     */
131    public static PointVehicle valueOf(PointComponent... elements) {
132        return PointVehicle.valueOf(null, elements);
133    }
134
135    /**
136     * Returns the range of elements in this list from the specified start and
137     * ending indexes.
138     *
139     * @param first index of the first element to return (0 returns the 1st
140     *      element, -1 returns the last, etc).
141     * @param last index of the last element to return (0 returns the 1st
142     *      element, -1 returns the last, etc).
143     * @return the list of elements in the given range from this list.
144     * @throws IndexOutOfBoundsException if the given index is out of range:
145     *      <code>index &ge; size()</code>
146     */
147    @Override
148    public PointVehicle getRange(int first, int last) {
149        first = normalizeIndex(first);
150        last = normalizeIndex(last);
151
152        PointVehicle list = PointVehicle.newInstance();
153        for (int i=first; i <= last; ++i)
154            list.add(get(i));
155        return list;
156    }
157
158    /**
159     * Returns an new {@link PointVehicle} with the elements in this list in
160     * reverse order.
161     *
162     * @return A new PointVehicle with the elements in this list in reverse order.
163     */
164    @Override
165    public PointVehicle reverse() {
166        PointVehicle list = PointVehicle.newInstance();
167        copyState(list);
168        int size = this.size();
169        for (int i=size-1; i >= 0; --i)
170            list.add(get(i));
171        return list;
172    }
173
174    /**
175     * Return the equivalent of this list converted to the specified number of
176     * physical dimensions. If the number of dimensions is greater than this
177     * element, then zeros are added to the additional dimensions. If the number
178     * of dimensions is less than this element, then the extra dimensions are
179     * simply dropped (truncated). If the new dimensions are the same as the
180     * dimension of this element, then this list is simply returned.
181     *
182     * @param newDim The dimension of the element to return.
183     * @return The equivalent of this list converted to the new dimensions.
184     */
185    @Override
186    public PointVehicle toDimension(int newDim) {
187        if (getPhyDimension() == newDim)
188            return this;
189        PointVehicle newList = PointVehicle.newInstance();
190        copyState(newList);
191        int size = this.size();
192        for (int i=0; i < size; ++i) {
193            PointComponent element = this.get(i);
194            newList.add(element.toDimension(newDim));
195        }
196        return newList;
197    }
198
199    /**
200     * Returns the equivalent to this list but with <I>all</I> the elements stated in the
201     * specified unit.
202     *
203     * @param unit the length unit of the list to be returned. May not be null.
204     * @return an equivalent to this list but stated in the specified unit.
205     * @throws ConversionException if the the input unit is not a length unit.
206     */
207    @Override
208    public PointVehicle to(Unit<Length> unit) {
209        requireNonNull(unit);
210        PointVehicle list = PointVehicle.newInstance();
211        copyState(list);
212        int size = this.size();
213        for (int i=0; i < size; ++i) {
214            PointComponent e = this.get(i);
215            list.add(e.to(unit));
216        }
217        return list;
218    }
219
220    /**
221     * Returns a copy of this <code>PointVehicle</code> instance
222     * {@link javolution.context.AllocatorContext allocated} by the calling
223     * thread (possibly on the stack).
224     *
225     * @return an identical and independent copy of this object.
226     */
227    @Override
228    public PointVehicle copy() {
229        return copyOf(this);
230    }
231
232    /**
233     * Return a copy of this object with any transformations or subranges
234     * removed (applied).
235     *
236     * @return A copy of this list with any sub-element transformations or
237     * subranges removed.
238     */
239    @Override
240    public PointVehicle copyToReal() {
241        PointVehicle newList = PointVehicle.newInstance();
242        copyState(newList);
243        int size = this.size();
244        for (int i=0; i < size; ++i) {
245            PointComponent element = this.get(i);
246            newList.add(element.copyToReal());
247        }
248        return newList;
249    }
250
251    /**
252     * Return the total number of quadrilateral panels in this vehicle.
253     *
254     * @return the total number of panels in this vehicle.
255     * @throws IndexOutOfBoundsException if the strings in any array in this
256     * vehicle have different lengths.
257     */
258    public int getNumberOfPanels() throws IndexOutOfBoundsException {
259        int sum = 0;
260        int size = this.size();
261        for (int i=0; i < size; ++i) {
262            PointComponent e = this.get(i);
263            sum += e.getNumberOfPanels();
264        }
265        return sum;
266    }
267
268    /**
269     * Returns transformed version of this element. The returned object
270     * implements {@link GeomTransform} and contains transformed versions of the
271     * contents of this list as children.
272     *
273     * @param transform The transform to apply to this geometry element. May not be null.
274     * @return A transformed version of this geometry element.
275     * @throws DimensionException if this element is not 3D.
276     */
277    @Override
278    public PointVehicle getTransformed(GTransform transform) {
279        requireNonNull(transform);
280        PointVehicle list = PointVehicle.newInstance();
281        copyState(list);
282        int size = this.size();
283        for (int i=0; i < size; ++i) {
284            PointComponent element = this.get(i);
285            list.add(element.getTransformed(transform));
286        }
287        return list;
288    }
289
290    /**
291     * Replaces the {@link PointComponent} at the specified position in this list with the
292     * specified element. Null elements are ignored. The input element must have the same
293     * physical dimensions as the other items in this list, or an exception is thrown.
294     *
295     * @param index   The index of the element to replace (0 returns the 1st element, -1
296     *                returns the last, -2 returns the 2nd from last, etc).
297     * @param element The element to be stored at the specified position.
298     *                <code>null</code> elements are ignored.
299     * @return The element previously at the specified position in this list. May not be
300     *         null.
301     * @throws java.lang.IndexOutOfBoundsException - if <code>index &gt; size()</code>
302     * @throws DimensionException if the input element's dimensions are different from
303     * this list's dimensions.
304     */
305    @Override
306    public PointComponent set(int index, PointComponent element) {
307        return super.set(index, requireNonNull(element));
308    }
309
310    /**
311     * Inserts the specified {@link PointComponent} at the specified position in this
312     * list. Shifts the element currently at that position (if any) and any subsequent
313     * elements to the right (adds one to their indices). Null values are ignored. The
314     * input value must have the same physical dimensions as the other items in this list,
315     * or an exception is thrown.
316     * <p>
317     * Note: If this method is used concurrent access must be synchronized (the list is
318     * not thread-safe).
319     * </p>
320     *
321     * @param index the index at which the specified element is to be inserted. (0 returns
322     *              the 1st element, -1 returns the last, -2 returns the 2nd from last,
323     *              etc).
324     * @param value the element to be inserted. May not be null.
325     * @throws IndexOutOfBoundsException if <code>index &gt; size()</code>
326     * @throws DimensionException if the input value dimensions are different from
327     * this list's dimensions.
328     */
329    @Override
330    public void add(int index, PointComponent value) {
331        super.add(index, requireNonNull(value));
332    }
333
334    /**
335     * Inserts all of the {@link PointComponent} objects in the specified collection into
336     * this list at the specified position. Shifts the element currently at that position
337     * (if any) and any subsequent elements to the right (increases their indices). The
338     * new elements will appear in this list in the order that they are returned by the
339     * specified collection's iterator. The behavior of this operation is unspecified if
340     * the specified collection is modified while the operation is in progress. Note that
341     * this will occur if the specified collection is this list, and it's nonempty.  The
342     * input elements must have the same physical dimensions as the other items in this
343     * list, or an exception is thrown.
344     *
345     * @param index index at which to insert first element from the specified collection.
346     * @param c     Elements to be inserted into this collection. May not be null.
347     * @return <code>true</code> if this collection changed as a result of the call.
348     * @throws DimensionException if the input element's dimensions are different from
349     * this list's dimensions.
350     */
351    @Override
352    public boolean addAll(int index, Collection<? extends PointComponent> c) {
353        int thisSize = this.size();
354        for (Object element : c) {
355            requireNonNull(element, RESOURCES.getString("collectionElementsNullErr"));
356            if (!(element instanceof PointComponent))
357                throw new ClassCastException(MessageFormat.format(
358                        RESOURCES.getString("listElementTypeErr"), "PointVehicle", "PointComponent"));
359            if (thisSize != 0) {
360                if (((GeomElement)element).getPhyDimension() != this.getPhyDimension())
361                    throw new DimensionException(RESOURCES.getString("dimensionErr"));
362            }
363        }
364        return super.addAll(index, c);
365    }
366
367    /**
368     * Holds the default XML representation for this object.
369     */
370    @SuppressWarnings("FieldNameHidesFieldInSuperclass")
371    protected static final XMLFormat<PointVehicle> XML = new XMLFormat<PointVehicle>(PointVehicle.class) {
372
373        @Override
374        public PointVehicle newInstance(Class<PointVehicle> cls, XMLFormat.InputElement xml) throws XMLStreamException {
375            PointVehicle obj = PointVehicle.newInstance();
376            return obj;
377        }
378
379        @Override
380        public void read(XMLFormat.InputElement xml, PointVehicle obj) throws XMLStreamException {
381            AbstractPointGeomList.XML.read(xml, obj);     // Call parent read.
382        }
383
384        @Override
385        public void write(PointVehicle obj, XMLFormat.OutputElement xml) throws XMLStreamException {
386            AbstractPointGeomList.XML.write(obj, xml);    // Call parent write.
387        }
388    };
389
390    //////////////////////
391    // Factory Creation //
392    //////////////////////
393    private static final ObjectFactory<PointVehicle> FACTORY = new ObjectFactory<PointVehicle>() {
394        @Override
395        protected PointVehicle create() {
396            return new PointVehicle();
397        }
398
399        @Override
400        protected void cleanup(PointVehicle obj) {
401            obj.reset();
402        }
403    };
404
405    /**
406     * Recycles a case instance immediately (on the stack when executing in a
407     * StackContext).
408     * 
409     * @param instance The instance to be recycled.
410     */
411    public static void recycle(PointVehicle instance) {
412        FACTORY.recycle(instance);
413    }
414
415    /**
416     * Do not allow the default constructor to be used except by subclasses.
417     */
418    protected PointVehicle() { }
419
420    private static PointVehicle copyOf(PointVehicle original) {
421        PointVehicle o = PointVehicle.newInstance();
422        original.copyState(o);
423        int size = original.size();
424        for (int i=0; i < size; ++i) {
425            PointComponent element = original.get(i);
426            o.add(element.copy());
427        }
428        return o;
429    }
430}