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 ≥ 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}