001/** 002 * NoteTrans -- A GeomTransform that has a GenScreenNote for a child. 003 * 004 * Copyright (C) 2014-2018, 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 Lesser 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.awt.Font; 021import java.text.MessageFormat; 022import java.util.Objects; 023import static java.util.Objects.requireNonNull; 024import javax.measure.converter.ConversionException; 025import javax.measure.quantity.Length; 026import javax.measure.unit.Unit; 027import javax.swing.event.ChangeListener; 028import javolution.context.ObjectFactory; 029import javolution.lang.Immutable; 030import javolution.xml.XMLFormat; 031import javolution.xml.stream.XMLStreamException; 032 033/** 034 * A {@link GeomTransform} element that refers to a {@link GenScreenNote} object and 035 * masquerades as a GenScreenNote object itself. 036 * 037 * <p> Modified by: Joseph A. Huwaldt </p> 038 * 039 * @author Joseph A. Huwaldt, Date: February 6, 2014 040 * @version April 10, 2018 041 */ 042@SuppressWarnings({"serial", "CloneableImplementsClone"}) 043public final class NoteTrans extends GenScreenNote implements GeomTransform<GenScreenNote> { 044 045 /** 046 * The transformation represented by this transformation element. 047 */ 048 private GTransform _TM; 049 050 /** 051 * The object that is the child of this transform. 052 */ 053 private GenScreenNote _child; 054 055 /** 056 * Reference to a change listener for this object's child. 057 */ 058 private ChangeListener _childChangeListener = new ForwardingChangeListener(this); 059 060 /** 061 * Returns a 3D {@link NoteTrans} instance holding the specified {@link GenScreenNote} 062 * and {@link GTransform}. 063 * 064 * @param child The note that is the child of this transform element (may not be 065 * <code>null</code>). 066 * @param transform The transform held by this transform element (may not be 067 * <code>null</code>). 068 * @return the transform element having the specified values. 069 * @throws DimensionException if the input element is not 3D. 070 */ 071 public static NoteTrans newInstance(GenScreenNote child, GTransform transform) { 072 requireNonNull(child, MessageFormat.format(RESOURCES.getString("paramNullErr"), "child")); 073 requireNonNull(transform, MessageFormat.format(RESOURCES.getString("paramNullErr"), "transform")); 074 075 if (child.getPhyDimension() != 3) 076 throw new DimensionException( 077 MessageFormat.format(RESOURCES.getString("dimensionNot3"), "note", child.getPhyDimension())); 078 079 NoteTrans obj = FACTORY.object(); 080 obj._TM = transform; 081 obj._child = child; 082 child.copyState(obj); 083 084 // Listen for changes to the child object and pass them on. 085 if (!(child instanceof Immutable)) 086 child.addChangeListener(obj._childChangeListener); 087 088 return obj; 089 } 090 091 /** 092 * Returns the transformation represented by this transformation element. 093 */ 094 @Override 095 public GTransform getTransform() { 096 return _TM; 097 } 098 099 /** 100 * Returns the total transformation represented by an entire chain of GeomTransform 101 * objects below this one. 102 */ 103 @Override 104 public GTransform getTotalTransform() { 105 return GeomUtil.getTotalTransform(this); 106 } 107 108 /** 109 * Sets the transformation represented by this transformation element. 110 * 111 * @param transform The transform to set this transform element to (may not be 112 * <code>null</code>). 113 */ 114 @Override 115 public void setTransform(GTransform transform) { 116 requireNonNull(transform, MessageFormat.format(RESOURCES.getString("paramNullErr"), "transform")); 117 _TM = transform; 118 fireChangeEvent(); 119 } 120 121 /** 122 * Returns the child object transformed by this transform element. 123 */ 124 @Override 125 public GenScreenNote getChild() { 126 return _child; 127 } 128 129 /** 130 * Return the text string associated with this note object. 131 * 132 * @return The text string associated with this note object. 133 */ 134 @Override 135 public String getNote() { 136 return _child.getNote(); 137 } 138 139 /** 140 * Return the location of this note in model space. 141 * 142 * @return The location of this note in model space. 143 */ 144 @Override 145 public Point getLocation() { 146 return _TM.transform(_child.getLocation()); 147 } 148 149 /** 150 * Return the font used to display this note. 151 * 152 * @return The font used to display this note. 153 */ 154 @Override 155 public Font getFont() { 156 return _child.getFont(); 157 } 158 159 /** 160 * Return an immutable version of this note. 161 * 162 * @return An immutable version of this note. 163 */ 164 @Override 165 public Note immutable() { 166 return copyToReal(); 167 } 168 169 /** 170 * Return a new note object identical to this one, but with the specified font. 171 * 172 * @param font The font for the copy of this note. May not be null. 173 * @return A new note object identical to this one, but with the specified font. 174 */ 175 @Override 176 public NoteTrans changeFont(Font font) { 177 requireNonNull(font); 178 NoteTrans note = NoteTrans.newInstance(_child.changeFont(font), _TM); 179 copyState(note); 180 return note; 181 } 182 183 /** 184 * Return a new note object identical to this one, but with the specified location in 185 * model space. 186 * 187 * @param location The location for the copy of this note. May not be null. 188 * @return A new note object identical to this one, but with the specified location in 189 * model space. 190 */ 191 @Override 192 public NoteTrans changeLocation(GeomPoint location) { 193 requireNonNull(location); 194 NoteTrans note = NoteTrans.newInstance(_child.changeLocation(location), _TM); 195 copyState(note); 196 return note; 197 } 198 199 /** 200 * Return a copy of the child object transformed by this transformation. 201 * 202 * @return A copy of the child object transformed by this transformation. 203 */ 204 @Override 205 public Note copyToReal() { 206 // Transform the geometry. 207 Point location = getLocation(); 208 209 // Create a new note from the old one. 210 Note note = _child.immutable().changeLocation(location); 211 copyState(note); 212 213 return note; 214 } 215 216 /** 217 * Returns the number of physical dimensions of the geometry element. This 218 * implementation always returns 3. 219 */ 220 @Override 221 public int getPhyDimension() { 222 return 3; 223 } 224 225 /** 226 * Return <code>true</code> if this Note contains valid and finite numerical 227 * components. A value of <code>false</code> will be returned if any of the coordinate 228 * values are NaN or Inf. 229 */ 230 @Override 231 public boolean isValid() { 232 return _child.isValid(); 233 } 234 235 /** 236 * Returns a copy of this NoteTrans instance 237 * {@link javolution.context.AllocatorContext allocated} by the calling thread 238 * (possibly on the stack). 239 * 240 * @return an identical and independent copy of this note. 241 */ 242 @Override 243 public NoteTrans copy() { 244 return copyOf(this); 245 } 246 247 /** 248 * Returns the unit in which the note location Point is stored. 249 */ 250 @Override 251 public final Unit<Length> getUnit() { 252 return _child.getUnit(); 253 } 254 255 /** 256 * Returns the equivalent to this note but with the location stated in the specified 257 * unit. 258 * 259 * @param unit the length unit of the note to be returned. May not be null. 260 * @return an equivalent of this note but with location stated in the specified unit. 261 * @throws ConversionException if the the input unit is not a length unit. 262 */ 263 @Override 264 public NoteTrans to(Unit<Length> unit) throws ConversionException { 265 if (unit.equals(getUnit())) 266 return this; 267 268 NoteTrans note = NoteTrans.newInstance(_child.to(unit), _TM); 269 copyState(note); 270 return note; 271 } 272 273 /** 274 * Return the equivalent of this note converted to the specified number of physical 275 * dimensions. This implementation will throw an exception if the specified dimension 276 * is anything other than 3. 277 * 278 * @param newDim The dimension of the point to return. MUST equal 3. 279 * @return The equivalent of this note converted to the new dimensions. 280 * @throws IllegalArgumentException if the new dimension is anything other than 3. 281 */ 282 @Override 283 public NoteTrans toDimension(int newDim) { 284 if (newDim == 3) 285 return this; 286 287 throw new IllegalArgumentException( 288 MessageFormat.format(RESOURCES.getString("dimensionNot3_2"), this.getClass().getName())); 289 } 290 291 /** 292 * Compares this NoteTrans against the specified object for strict equality (same 293 * values and same units). 294 * 295 * @param obj the object to compare with. 296 * @return <code>true</code> if this note is identical to that note; 297 * <code>false</code> otherwise. 298 */ 299 @Override 300 public boolean equals(Object obj) { 301 if (this == obj) 302 return true; 303 if ((obj == null) || (obj.getClass() != this.getClass())) 304 return false; 305 306 NoteTrans that = (NoteTrans)obj; 307 return this._TM.equals(that._TM) 308 && this._child.equals(that._child) 309 && super.equals(obj); 310 } 311 312 /** 313 * Returns the hash code for this parameter. 314 * 315 * @return the hash code value. 316 */ 317 @Override 318 public int hashCode() { 319 return 31*super.hashCode() + Objects.hash(_TM, _child); 320 } 321 322 /** 323 * Holds the default XML representation for this object. 324 */ 325 @SuppressWarnings("FieldNameHidesFieldInSuperclass") 326 protected static final XMLFormat<NoteTrans> XML = new XMLFormat<NoteTrans>(NoteTrans.class) { 327 328 @Override 329 public NoteTrans newInstance(Class<NoteTrans> cls, XMLFormat.InputElement xml) throws XMLStreamException { 330 return FACTORY.object(); 331 } 332 333 @Override 334 public void read(XMLFormat.InputElement xml, NoteTrans obj) throws XMLStreamException { 335 GenScreenNote.XML.read(xml, obj); // Call parent read. 336 337 obj._TM = xml.getNext(); 338 GenScreenNote child = xml.getNext(); 339 obj._child = child; 340 341 // Listen for changes to the child object and pass them on. 342 if (!(child instanceof Immutable)) 343 child.addChangeListener(obj._childChangeListener); 344 345 } 346 347 @Override 348 public void write(NoteTrans obj, XMLFormat.OutputElement xml) throws XMLStreamException { 349 GenScreenNote.XML.write(obj, xml); // Call parent write. 350 351 xml.add(obj._TM); 352 xml.add(obj._child); 353 } 354 }; 355 356 /////////////////////// 357 // Factory creation. // 358 /////////////////////// 359 private NoteTrans() { } 360 361 @SuppressWarnings("unchecked") 362 private static final ObjectFactory<NoteTrans> FACTORY = new ObjectFactory<NoteTrans>() { 363 @Override 364 protected NoteTrans create() { 365 return new NoteTrans(); 366 } 367 368 @Override 369 protected void cleanup(NoteTrans obj) { 370 obj.reset(); 371 obj._TM = null; 372 if (!(obj._child instanceof Immutable)) 373 obj._child.removeChangeListener(obj._childChangeListener); 374 obj._child = null; 375 } 376 }; 377 378 @SuppressWarnings("unchecked") 379 private static NoteTrans copyOf(NoteTrans original) { 380 NoteTrans obj = FACTORY.object(); 381 obj._TM = original._TM.copy(); 382 obj._child = original._child.copy(); 383 original.copyState(obj); 384 if (!(obj._child instanceof Immutable)) 385 obj._child.addChangeListener(obj._childChangeListener); 386 return obj; 387 } 388 389}