001package squidpony.squidgrid.zone; 002 003import squidpony.squidgrid.zone.Zone.Skeleton; 004import squidpony.squidmath.Coord; 005import squidpony.squidmath.CoordPacker; 006import squidpony.squidmath.CrossHash; 007 008import java.util.*; 009 010/** 011 * A zone constructed by {@link CoordPacker}. 012 * 013 * @author smelC 014 */ 015public class CoordPackerZone extends Skeleton implements Collection<Coord>, ImmutableZone { 016 017 protected final short[] shorts; 018 019 protected transient List<Coord> unpacked; 020 021 private static final long serialVersionUID = -3718415979846804238L; 022 023 public CoordPackerZone(short[] shorts) { 024 this.shorts = shorts; 025 } 026 027 @Override 028 public boolean isEmpty() { 029 return CoordPacker.isEmpty(shorts); 030 } 031 032 /** 033 * Returns <tt>true</tt> if this collection contains the specified element. 034 * More formally, returns <tt>true</tt> if and only if this collection 035 * contains at least one element <tt>e</tt> such that 036 * <tt>(o==null ? e==null : o.equals(e))</tt>. 037 * 038 * @param o element whose presence in this collection is to be tested 039 * @return <tt>true</tt> if this collection contains the specified 040 * element 041 * @throws ClassCastException if the type of the specified element 042 * is incompatible with this collection 043 * (<a href="#optional-restrictions">optional</a>) 044 * @throws NullPointerException if the specified element is null and this 045 * collection does not permit null elements 046 * (<a href="#optional-restrictions">optional</a>) 047 */ 048 @Override 049 public boolean contains(Object o) { 050 return (o instanceof Coord) && CoordPacker.queryPacked(shorts, ((Coord) o).x, ((Coord) o).y); 051 } 052 053 /** 054 * Returns an array containing all of the elements in this collection. 055 * If this collection makes any guarantees as to what order its elements 056 * are returned by its iterator, this method must return the elements in 057 * the same order. 058 * <p> 059 * <p>The returned array will be "safe" in that no references to it are 060 * maintained by this collection. (In other words, this method must 061 * allocate a new array even if this collection is backed by an array). 062 * The caller is thus free to modify the returned array. 063 * <p> 064 * <p>This method acts as bridge between array-based and collection-based 065 * APIs. 066 * 067 * @return an array containing all of the elements in this collection 068 */ 069 @Override 070 public Object[] toArray() { 071 return CoordPacker.allPacked(shorts); 072 } 073 074 /** 075 * Returns an array containing all of the elements in this collection; 076 * the runtime type of the returned array is that of the specified array. 077 * If the collection fits in the specified array, it is returned therein. 078 * Otherwise, a new array is allocated with the runtime type of the 079 * specified array and the size of this collection. 080 * <p> 081 * <p>If this collection fits in the specified array with room to spare 082 * (i.e., the array has more elements than this collection), the element 083 * in the array immediately following the end of the collection is set to 084 * <tt>null</tt>. (This is useful in determining the length of this 085 * collection <i>only</i> if the caller knows that this collection does 086 * not contain any <tt>null</tt> elements.) 087 * <p> 088 * <p>If this collection makes any guarantees as to what order its elements 089 * are returned by its iterator, this method must return the elements in 090 * the same order. 091 * <p> 092 * <p>Like the {@link #toArray()} method, this method acts as bridge between 093 * array-based and collection-based APIs. Further, this method allows 094 * precise control over the runtime type of the output array, and may, 095 * under certain circumstances, be used to save allocation costs. 096 * <p> 097 * <p>Suppose <tt>x</tt> is a collection known to contain only strings. 098 * The following code can be used to dump the collection into a newly 099 * allocated array of <tt>String</tt>: 100 * <p> 101 * <pre> 102 * String[] y = x.toArray(new String[0]);</pre> 103 * <p> 104 * Note that <tt>toArray(new Object[0])</tt> is identical in function to 105 * <tt>toArray()</tt>. 106 * 107 * @param a the array into which the elements of this collection are to be 108 * stored, if it is big enough; otherwise, a new array of the same 109 * runtime type is allocated for this purpose. 110 * @return an array containing all of the elements in this collection 111 * @throws ArrayStoreException if the runtime type of the specified array 112 * is not a supertype of the runtime type of every element in 113 * this collection 114 * @throws NullPointerException if the specified array is null 115 */ 116 @Override 117 @SuppressWarnings("unchecked") 118 public <T> T[] toArray(T[] a) { 119 if (a == null) 120 throw new NullPointerException("Array passed to CoordPackerZone.toArray() must not be null"); 121 final int size = a.length, ssize = CoordPacker.count(shorts); 122 if (ssize == size) 123 return (T[]) CoordPacker.allPacked(shorts); 124 a = Arrays.copyOf(a, ssize); 125 for (int i = 0; i < ssize; i++) { 126 a[i] = (T) CoordPacker.nth(shorts, i); 127 } 128 return a; 129 } 130 131 /** 132 * Does nothing (this Zone is immutable). 133 */ 134 @Override 135 public boolean add(Coord coord) { 136 return false; 137 } 138 139 /** 140 * Does nothing (this Zone is immutable). 141 */ 142 @Override 143 public boolean remove(Object o) { 144 return false; 145 } 146 147 /** 148 * Returns <tt>true</tt> if this collection contains all of the elements 149 * in the specified collection. 150 * 151 * @param c collection to be checked for containment in this collection 152 * @return <tt>true</tt> if this collection contains all of the elements 153 * in the specified collection 154 * @throws ClassCastException if the types of one or more elements 155 * in the specified collection are not Coord 156 * @see #contains(Object) 157 */ 158 @Override 159 @SuppressWarnings("unchecked") 160 public boolean containsAll(Collection<?> c) { 161 return CoordPacker.count(shorts) == CoordPacker.count(CoordPacker.insertSeveralPacked(shorts, (Collection) c)); 162 } 163 164 /** 165 * Does nothing (this Zone is immutable). 166 */ 167 @Override 168 public boolean addAll(Collection<? extends Coord> c) { 169 return false; 170 } 171 172 /** 173 * Does nothing (this Zone is immutable). 174 */ 175 @Override 176 public boolean removeAll(Collection<?> c) { 177 return false; 178 } 179 180 /** 181 * Does nothing (this Zone is immutable). 182 */ 183 @Override 184 public boolean retainAll(Collection<?> c) { 185 return false; 186 } 187 188 /** 189 * Does nothing (this Zone is immutable). 190 */ 191 @Override 192 public void clear() { 193 194 } 195 196 @Override 197 public int size() { 198 return CoordPacker.count(shorts); 199 } 200 201 @Override 202 public boolean contains(int x, int y) { 203 return CoordPacker.regionsContain(shorts, CoordPacker.packOne(x, y)); 204 } 205 206 @Override 207 public boolean contains(Coord c) { 208 return CoordPacker.regionsContain(shorts, CoordPacker.packOne(c)); 209 } 210 211 @Override 212 public List<Coord> getAll() { 213 if (unpacked == null) { 214 final Coord[] allPacked = CoordPacker.allPacked(shorts); 215 unpacked = new ArrayList<Coord>(allPacked.length); 216 Collections.addAll(unpacked, allPacked); 217 } 218 return unpacked; 219 } 220 221 @Override 222 public CoordPackerZone expand(int distance) { 223 return new CoordPackerZone(CoordPacker.expand(shorts, distance, 256, 256)); 224 } 225 226 @Override 227 public CoordPackerZone expand8way(int distance) { 228 return new CoordPackerZone(CoordPacker.expand(shorts, distance, 256, 256, true)); 229 } 230 231 @Override 232 public boolean contains(Zone other) { 233 return CoordPacker.count(shorts) == CoordPacker.count(CoordPacker.insertSeveralPacked(shorts, other.getAll())); 234 } 235 236 @Override 237 public boolean intersectsWith(Zone other) { 238 if (other instanceof CoordPackerZone) 239 return CoordPacker.intersects(shorts, ((CoordPackerZone) other).shorts); 240 for (Coord c : other) { 241 if (CoordPacker.queryPacked(shorts, c.x, c.y)) 242 return true; 243 } 244 return false; 245 } 246 247 @Override 248 public Zone extend() { 249 return new CoordPackerZone(CoordPacker.expand(shorts, 1, 256, 256, true)); 250 } 251 252 @Override 253 public Collection<Coord> getInternalBorder() { 254 return new CoordPackerZone(CoordPacker.surface(shorts, 1, 256, 256, true)); 255 } 256 257 @Override 258 public Collection<Coord> getExternalBorder() { 259 return new CoordPackerZone(CoordPacker.fringe(shorts, 1, 256, 256, true)); 260 } 261 262 @Override 263 public Zone translate(int x, int y) { 264 return new CoordPackerZone(CoordPacker.translate(shorts, x, y, 256, 256)); 265 } 266 267 @Override 268 public String toString() { 269 return (unpacked == null ? shorts : unpacked).toString(); 270 } 271 272 @Override 273 public boolean equals(Object o) { 274 if (this == o) return true; 275 if (o == null || getClass() != o.getClass()) return false; 276 277 CoordPackerZone that = (CoordPackerZone) o; 278 279 return Arrays.equals(shorts, that.shorts); 280 } 281 282 @Override 283 public int hashCode() { 284 return CrossHash.hash(shorts); 285 } 286}