001/**
002 * GeomList -- A concrete list of GeomElement 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 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.Arrays;
022import java.util.Collection;
023import static java.util.Objects.requireNonNull;
024import javax.measure.converter.ConversionException;
025import javax.measure.quantity.Length;
026import javax.measure.unit.Unit;
027import javolution.context.ObjectFactory;
028import javolution.util.FastTable;
029import javolution.xml.XMLFormat;
030import javolution.xml.stream.XMLStreamException;
031
032/**
033 * A concrete list of arbitrary {@link GeomElement} objects.
034 * <p>
035 * WARNING: This list allows geometry to be stored in different units and with different
036 * physical dimensions. If consistent units or dimensions are required, then the user must
037 * specifically convert the list items.
038 * </p>
039 *
040 * <p> Modified by: Joseph A. Huwaldt </p>
041 *
042 * @author Joseph A. Huwaldt, Date: May 1, 2009
043 * @version February 17, 2025
044 *
045 * @param <E> The type of element stored in this list.
046 */
047@SuppressWarnings({"serial", "CloneableImplementsClone"})
048public final class GeomList<E extends GeomElement> extends AbstractGeomList<GeomList, E> {
049
050    private FastTable<E> _list;
051
052    /**
053     * Return the list underlying this geometry list.
054     *
055     * @return The list underlying this geometry list.
056     */
057    @Override
058    protected FastTable<E> getList() {
059        return _list;
060    }
061
062    /**
063     * Returns a new, empty, preallocated or recycled <code>GeomList</code> instance (on
064     * the stack when executing in a <code>StackContext</code>) that can store a list of
065     * {@link GeomElement} objects.
066     *
067     * @return A new, empty GeomList.
068     */
069    public static GeomList newInstance() {
070        GeomList list = FACTORY.object();
071        list._list = FastTable.newInstance();
072        return list;
073    }
074
075    /**
076     * Returns a new, empty, preallocated or recycled <code>GeomList</code> instance (on
077     * the stack when executing in a <code>StackContext</code>) with the specified name,
078     * that can store a list of {@link GeomElement} objects.
079     *
080     * @param name The name to be assigned to this list (may be <code>null</code>).
081     * @return A new, empty GeomList with the specified name.
082     */
083    public static GeomList newInstance(String name) {
084        GeomList list = GeomList.newInstance();
085        list.setName(name);
086        return list;
087    }
088
089    /**
090     * Return a GeomList containing the {@link GeomElement} objects in the specified
091     * collection.
092     *
093     * @param name     The name to be assigned to this list (may be <code>null</code>).
094     * @param elements A collection of geometry elements. May not be null.
095     * @return A new GeomList containing the elements in the specified collection.
096     */
097    public static GeomList valueOf(String name, Collection<? extends GeomElement> elements) {
098        for (Object element : elements) {
099            requireNonNull(element, RESOURCES.getString("collectionElementsNullErr"));
100            if (!(element instanceof GeomElement))
101                throw new ClassCastException(MessageFormat.format(
102                        RESOURCES.getString("listElementTypeErr"), "GeomList", "GeomElement"));
103        }
104
105        GeomList list = GeomList.newInstance(name);
106        list.addAll(elements);
107
108        return list;
109    }
110
111    /**
112     * Return a GeomList containing the {@link GeomElement} objects in the specified list.
113     *
114     * @param name     The name to be assigned to this list (may be <code>null</code>).
115     * @param elements A list of geometry elements. May not be null.
116     * @return A new GeomList containing the elements in the specified list.
117     */
118    public static GeomList valueOf(String name, GeomElement... elements) {
119
120        GeomList list = GeomList.newInstance(name);
121        list.addAll(Arrays.asList(requireNonNull(elements)));
122
123        return list;
124    }
125
126    /**
127     * Return a GeomList containing the {@link GeomElement} objects in the specified
128     * array.
129     *
130     * @param elements An array of geometry elements. May not be null.
131     * @return A new GeomList containing the elements in the specified array.
132     */
133    public static GeomList valueOf(GeomElement... elements) {
134
135        GeomList list = GeomList.newInstance();
136        list.addAll(Arrays.asList(requireNonNull(elements)));
137
138        return list;
139    }
140
141    /**
142     * Returns the range of elements in this list from the specified start and ending
143     * indexes.
144     *
145     * @param first index of the first element to return (0 returns the 1st element, -1
146     *              returns the last, etc).
147     * @param last  index of the last element to return (0 returns the 1st element, -1
148     *              returns the last, etc).
149     * @return the list of elements in the given range from this list.
150     * @throws IndexOutOfBoundsException if the given index is out of range:
151     * <code>index &ge; size()</code>
152     */
153    @Override
154    public GeomList getRange(int first, int last) {
155        first = normalizeIndex(first);
156        last = normalizeIndex(last);
157
158        GeomList list = GeomList.newInstance();
159        for (int i = first; i <= last; ++i)
160            list.add(get(i));
161        return list;
162    }
163
164    /**
165     * Returns an new {@link GeomList} with the elements in this list in reverse order.
166     *
167     * @return A new GeomList with the elements in this list in reverse order.
168     */
169    @Override
170    public GeomList reverse() {
171        GeomList list = GeomList.newInstance();
172        int size = this.size();
173        for (int i = size - 1; i >= 0; --i) {
174            list.add(get(i));
175        }
176        return list;
177    }
178
179    /**
180     * Return a copy of this list converted to the specified number of physical
181     * dimensions. If the number of dimensions is greater than this element, then zeros
182     * are added to the additional dimensions. If the number of dimensions is less than
183     * this element, then the extra dimensions are simply dropped (truncated). If the new
184     * dimensions are the same as the dimension of this element, then this list is simply
185     * returned.
186     *
187     * @param newDim The dimension of the element to return.
188     * @return A copy of this list converted to the new dimensions.
189     */
190    @Override
191    public GeomList toDimension(int newDim) {
192        GeomList newList = GeomList.newInstance();
193        copyState(newList);
194        int size = this.size();
195        for (int i = 0; i < size; ++i) {
196            E element = this.get(i);
197            newList.add(element.toDimension(newDim));
198        }
199        return newList;
200    }
201
202    /**
203     * Returns the equivalent to this list but with <I>all</I> the elements stated in the
204     * specified unit.
205     *
206     * @param unit the length unit of the list to be returned. May not be null.
207     * @return an equivalent to this list but stated in the specified unit.
208     * @throws ConversionException if the the input unit is not a length unit.
209     */
210    @Override
211    public GeomList to(Unit<Length> unit) throws ConversionException {
212        requireNonNull(unit);
213        GeomList newList = GeomList.newInstance();
214        copyState(newList);
215        int size = this.size();
216        for (int i = 0; i < size; ++i) {
217            E element = this.get(i);
218            newList.add(element.to(unit));
219        }
220        return newList;
221    }
222
223    /**
224     * Returns a copy of this <code>GeomList</code> instance
225     * {@link javolution.context.AllocatorContext allocated} by the calling thread
226     * (possibly on the stack).
227     *
228     * @return an identical and independent copy of this object.
229     */
230    @Override
231    public GeomList copy() {
232        return copyOf(this);
233    }
234
235    /**
236     * Return a copy of this object with any transformations or subranges removed
237     * (applied).
238     *
239     * @return A copy of this list with any transformations or subranges removed.
240     */
241    @Override
242    public GeomList copyToReal() {
243        GeomList newList = GeomList.newInstance();
244        copyState(newList);
245        int size = this.size();
246        for (int i = 0; i < size; ++i) {
247            E element = this.get(i);
248            newList.add(element.copyToReal());
249        }
250        return newList;
251    }
252
253    /**
254     * Returns transformed version of this element. The returned object implements
255     * {@link GeomTransform} and contains transformed versions of the contents of this
256     * list as children. Any list elements that are not transformable will simply be added
257     * to the output list without transformation.
258     *
259     * @param transform The transform to apply to this geometry element. May not be null.
260     * @return A transformed version of this geometry element.
261     * @throws DimensionException if this element is not 3D.
262     */
263    @Override
264    public GeomList getTransformed(GTransform transform) {
265        requireNonNull(transform);
266        GeomList list = GeomList.newInstance();
267        copyState(list);    //  Copy over the super-class state for this object to the new one.
268        int size = this.size();
269        for (int i = 0; i < size; ++i) {
270            E element = this.get(i);
271            if (element instanceof Transformable)
272                list.add(((Transformable)element).getTransformed(transform));
273            else
274                list.add(element);
275        }
276        return list;
277    }
278
279    /**
280     * Holds the default XML representation for this object.
281     */
282    @SuppressWarnings("FieldNameHidesFieldInSuperclass")
283    protected static final XMLFormat<GeomList> XML = new XMLFormat<GeomList>(GeomList.class) {
284
285        @Override
286        public GeomList newInstance(Class<GeomList> cls, XMLFormat.InputElement xml) throws XMLStreamException {
287            GeomList obj = GeomList.newInstance();
288            return obj;
289        }
290
291        @Override
292        public void read(XMLFormat.InputElement xml, GeomList obj) throws XMLStreamException {
293            AbstractPointGeomList.XML.read(xml, obj);     // Call parent read.
294        }
295
296        @Override
297        public void write(GeomList obj, XMLFormat.OutputElement xml) throws XMLStreamException {
298            AbstractPointGeomList.XML.write(obj, xml);    // Call parent write.
299        }
300    };
301
302    //////////////////////
303    // Factory Creation //
304    //////////////////////
305    private static final ObjectFactory<GeomList> FACTORY = new ObjectFactory<GeomList>() {
306        @Override
307        protected GeomList create() {
308            return new GeomList();
309        }
310
311        @Override
312        protected void cleanup(GeomList obj) {
313            obj.reset();
314        }
315    };
316
317    /**
318     * Recycles a GeomList instance immediately (on the stack when executing in a
319     * StackContext).
320     *
321     * @param instance The instance to be recycled.
322     */
323    public static void recycle(GeomList instance) {
324        FACTORY.recycle(instance);
325    }
326
327    /**
328     * Do not allow the default constructor to be used except by subclasses.
329     */
330    protected GeomList() { }
331
332    private static GeomList copyOf(GeomList<? extends GeomElement> original) {
333        GeomList o = GeomList.newInstance();
334        original.copyState(o);
335        int size = original.size();
336        for (int i = 0; i < size; ++i) {
337            GeomElement element = original.get(i);
338            o.add(element.copy());
339        }
340        return o;
341    }
342}