001/** 002 * UnitSet -- A container for self-consistent and often coherent unit sets. 003 * 004 * Copyright (C) 2015-2017, 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 jahuwaldt.js.unit; 019 020import java.beans.PropertyChangeListener; 021import java.beans.PropertyChangeSupport; 022import java.util.HashMap; 023import javax.measure.converter.ConversionException; 024import javax.measure.converter.UnitConverter; 025import javax.measure.quantity.*; 026import javax.measure.unit.NonSI; 027import static javax.measure.unit.NonSI.*; 028import static javax.measure.unit.SI.*; 029import javax.measure.unit.Unit; 030import javolution.xml.XMLFormat; 031import javolution.xml.stream.XMLStreamException; 032 033/** 034 * A class that contains a set of units (generally made self-consistent if not coherent) 035 * and also provides lists (sets) of related units. 036 * 037 * <p> Modified by: Joseph A. Huwaldt </p> 038 * 039 * @author Joseph A. Huwaldt, Date: July 12, 2008 040 * @version March 19, 2017 041 */ 042public final class UnitSet implements Cloneable { 043 044 /** 045 * The type of unit system being used. 046 */ 047 public enum UnitSystem { 048 049 /** 050 * Constant used to indicate that the coherent SI units should be used. 051 */ 052 SI_MKS, 053 /** 054 * Constant used to indicate that the coherent metric (cm-gram-sec) units should be used. 055 */ 056 CGS, 057 /** 058 * Constant used to indicate that coherent US Customary units (ft-slug-sec) should be 059 * used. 060 */ 061 US_FSS, 062 /** 063 * Constant used to indicate that coherent US Customary units (ft-lbm-sec) should be 064 * used. 065 */ 066 US_FPS, 067 /** 068 * Constant used to indicate that a custom set of units is being used which 069 * may or may <em>not</em> be coherent. 070 */ 071 CUSTOM 072 } 073 074 /** 075 * The different unit set types in this collection. 076 */ 077 public enum SetType { 078 079 TIME, LENGTH, MASS, ANGLE, AREA, VOLUME, VELOCITY, ACCELERATION, FORCE, INERTIA, MASS_DENSITY, 080 ANGULAR_VELOCITY, TORQUE 081 } 082 083 /** 084 * The angular unit "degree". 085 */ 086 public static final Unit DEG = DEGREE_ANGLE; 087 088 /** 089 * The angular unit "radian". 090 */ 091 public static final Unit RAD = Angle.UNIT; 092 093 private static final Unit<Acceleration> FPS2 = FOOT.divide(SECOND.pow(2)).asType(Acceleration.class); 094 095 // A collection of unit sets (arrays of related and compatible units). 096 private static final HashMap<SetType, Unit[]> sets = new HashMap(); 097 098 static { 099 Unit<Length> CM = CENTIMETER; 100 Unit<Length> MM = MILLIMETER; 101 Unit MM2 = MM.pow(2); 102 Unit CM2 = CM.pow(2); 103 Unit M2 = SQUARE_METRE; 104 Unit IN2 = INCH.pow(2); 105 Unit FT2 = FOOT.pow(2); 106 Unit MM3 = MM2.times(MM); 107 Unit CM3 = CM2.times(CM); 108 Unit M3 = CUBIC_METRE; 109 Unit IN3 = CUBIC_INCH; 110 Unit FT3 = FT2.times(FOOT); 111 112 { 113 Unit[] unitArr = {SECOND, MINUTE, HOUR, DAY}; 114 sets.put(SetType.TIME, unitArr); 115 } 116 { 117 Unit[] unitArr = {MM, CM, METER, KILOMETER, 118 INCH, FOOT, NAUTICAL_MILE, MILE}; 119 sets.put(SetType.LENGTH, unitArr); 120 } 121 { 122 Unit[] unitArr = {GRAM, KILOGRAM, METRIC_TON, OUNCE, POUND, SLUG, TON_US}; 123 sets.put(SetType.MASS, unitArr); 124 } 125 { 126 Unit[] unitArr = {RADIAN, DEGREE_ANGLE, REVOLUTION, SECOND_ANGLE, MINUTE_ANGLE }; 127 sets.put(SetType.ANGLE, unitArr); 128 } 129 130 { 131 Unit[] unitArr = {MM2, CM2, M2, HECTARE, KILOMETER.pow(2), 132 IN2, FT2, MILE.pow(2)}; 133 sets.put(SetType.AREA, unitArr); 134 } 135 { 136 Unit[] unitArr = {MM3, CM3, LITER, M3, KILOMETER.pow(3), 137 IN3, FT3, MILE.pow(3)}; 138 sets.put(SetType.VOLUME, unitArr); 139 } 140 141 { 142 Unit[] unitArr = {MM.divide(SECOND), CM.divide(SECOND), METERS_PER_SECOND, KILOMETERS_PER_HOUR, 143 INCH.divide(SECOND), FEET_PER_SECOND, KNOT, MILES_PER_HOUR}; 144 sets.put(SetType.VELOCITY, unitArr); 145 } 146 { 147 Unit[] unitArr = {MM.divide(SECOND.pow(2)), CM.divide(SECOND.pow(2)), METERS_PER_SQUARE_SECOND, 148 INCH.divide(SECOND.pow(2)), FPS2, NonSI.G}; 149 sets.put(SetType.ACCELERATION, unitArr); 150 } 151 152 { 153 Unit[] unitArr = {DYNE, NEWTON, KILOGRAM_FORCE, POUND_FORCE, POUNDAL}; 154 sets.put(SetType.FORCE, unitArr); 155 } 156 157 { 158 Unit[] unitArr = {KILOGRAM.times(CM2), KILOGRAM.times(M2), 159 POUND.times(IN2), POUND.times(FT2), SLUG.times(FT2)}; 160 sets.put(SetType.INERTIA, unitArr); 161 } 162 { 163 Unit[] unitArr = {KILOGRAM.divide(CM3), KILOGRAM.divide(M3), 164 POUND.divide(IN3), POUND.divide(FT3), SLUG.divide(IN3), SLUG.divide(FT3)}; 165 sets.put(SetType.MASS_DENSITY, unitArr); 166 } 167 { 168 Unit[] unitArr = {RADIAN.divide(SECOND), DEGREE_ANGLE.divide(SECOND), REVOLUTION.divide(SECOND)}; 169 sets.put(SetType.ANGULAR_VELOCITY, unitArr); 170 } 171 { 172 Unit[] unitArr = {NEWTON.times(CM), Torque.UNIT, POUND_FORCE.times(INCH), POUND_FORCE.times(FOOT)}; 173 sets.put(SetType.TORQUE, unitArr); 174 } 175 } 176 177 // The type code for this unit set. 178 private UnitSystem type = UnitSystem.SI_MKS; 179 180 // Flag indicating if the unit set is coherent or not. 181 private boolean isCoherent = false; 182 183 // Flag indicating if the unit set is consistent or not. 184 private boolean isConsistent = false; 185 186 // The various units in this unit set. 187 private Unit<Duration> time; 188 private Unit<Length> length; 189 private Unit<Mass> mass; 190 private Unit<Angle> angle; 191 192 // Derived units. 193 private Unit<Area> area; 194 private Unit<Volume> volume; 195 private Unit<Velocity> velocity; 196 private Unit<Acceleration> accel; 197 private Unit<Force> force; 198 private Unit<VolumetricDensity> massDensity; 199 private Unit<Inertia> inertia; 200 private Unit<AngularVelocity> angularVelocity; 201 private Unit<Torque> torque; 202 203 204 /** 205 * Construct a coherent set of units based on the system identification provided. 206 * 207 * @param system The Unit System type. 208 */ 209 public UnitSet(UnitSystem system) { 210 // Define the fundamental units. 211 time = SECOND; 212 angle = DEG; 213 214 switch (system) { 215 case US_FSS: 216 length = FOOT; 217 mass = SLUG; 218 break; 219 220 case US_FPS: 221 length = FOOT; 222 mass = POUND; 223 break; 224 225 case CGS: 226 length = CENTIMETER; 227 mass = GRAM; 228 break; 229 230 case SI_MKS: 231 case CUSTOM: 232 length = METER; 233 mass = KILOGRAM; 234 break; 235 } 236 this.type = system; 237 this.isCoherent = true; 238 239 // Create a consistent derived unit set. 240 makeConsistent(); 241 242 } 243 244 /** 245 * Method that returns the Unit System for this unit set. 246 * 247 * @return The UnitSystem for this unit set. 248 */ 249 public UnitSystem getSystem() { 250 return type; 251 } 252 253 /** 254 * Returns an array of units of the specified type (array of related units). 255 * 256 * @param type The unit type to return an array of units for. 257 * @return The unit set array requested 258 */ 259 public static Unit[] getSet(SetType type) { 260 return sets.get(type); 261 } 262 263 /** 264 * Return <code>true</code> if this unit set is coherent. Coherent means that the 265 * derived units in this set are a product of powers of base units with no other 266 * proportionality factor than one. 267 * 268 * @return true if this unit set is coherent. 269 * @see #isConsistent() 270 */ 271 public boolean isCoherent() { 272 return isCoherent; 273 } 274 275 /** 276 * Return <code>true</code> if this unit set is consistent. Consistent means that the 277 * derived units are made up of powers of the base units though there may be 278 * conversion factors involved. 279 * 280 * @return true if this unit set is consistent. 281 * @see #makeConsistent() 282 * @see #isCoherent() 283 */ 284 public boolean isConsistent() { 285 return isConsistent; 286 } 287 288 /** 289 * Makes this unit set consistent by deriving a complete set of units for this 290 * application based on the fundamental or base units of time, length, mass and angle. 291 * Consistent means that the derived units in this set are all derived from the base 292 * units. 293 * 294 * @see #isConsistent() 295 * @see #isCoherent() 296 */ 297 public void makeConsistent() { 298 299 area = length.pow(2).asType(Area.class); 300 volume = length.pow(3).asType(Volume.class); 301 velocity = length.divide(time).asType(Velocity.class); 302 accel = length.divide(time.pow(2)).asType(Acceleration.class); 303 304 if (equivUnit(mass, KILOGRAM) && equivUnit(accel, METERS_PER_SQUARE_SECOND)) 305 force = NEWTON; 306 else if (equivUnit(mass, KILOGRAM) && equivUnit(accel, G)) 307 force = KILOGRAM_FORCE; 308 else if (equivUnit(mass, SLUG) && equivUnit(accel, FPS2)) 309 force = POUND_FORCE; 310 else if (equivUnit(mass, POUND) && equivUnit(accel, FPS2)) 311 force = POUNDAL; 312 else if (equivUnit(mass, POUND) && equivUnit(accel, G)) 313 force = POUND_FORCE; 314 else 315 force = mass.times(accel).asType(Force.class); 316 317 massDensity = mass.divide(volume).asType(VolumetricDensity.class); 318 inertia = mass.times(area).asType(Inertia.class); 319 320 angularVelocity = angle.divide(time).asType(AngularVelocity.class); 321 torque = force.times(length).asType(Torque.class); 322 323 // The derived units are not consistent. 324 isConsistent = true; 325 326 // Make sure the newly derived units are in the unit sets. 327 addUnitsToSets(); 328 329 // Does this represent one of the standard coherent unit systems? 330 if (time.equals(SECOND)) { 331 if (length.equals(METER) && mass.equals(KILOGRAM)) { 332 type = UnitSystem.SI_MKS; 333 isCoherent = true; 334 } else if (length.equals(CENTIMETER) && mass.equals(GRAM)) { 335 type = UnitSystem.CGS; 336 isCoherent = true; 337 } else if (length.equals(FOOT)) { 338 if (mass.equals(SLUG)) { 339 type = UnitSystem.US_FSS; 340 isCoherent = true; 341 } else if (mass.equals(POUND)) { 342 type = UnitSystem.US_FPS; 343 isCoherent = true; 344 } 345 } 346 } 347 348 } 349 350 /** 351 * Return true if two units are equivalent to one another. Equivalent means that 352 * the conversion from unit1 to unit 2 is an identity (1.0). 353 * 354 * @param unit1 The 1st unit to compare. 355 * @param unit2 The 2nd unit to compare. 356 * @return true if no conversion is required between unit1 and unit2. 357 */ 358 private boolean equivUnit(Unit unit1, Unit unit2) { 359 try { 360 UnitConverter cvt = unit1.getConverterTo(unit2); 361 return cvt.equals(UnitConverter.IDENTITY); 362 363 } catch (ConversionException ignore) { 364 return false; 365 } 366 } 367 368 /** 369 * Method that ensures that all the current units are in the unit sets. 370 */ 371 private void addUnitsToSets() { 372 // Time 373 Unit[] set = getSet(SetType.TIME); 374 if (!unitInSet(set, time)) 375 addUnitToSet(set, time); 376 377 // Mass 378 set = getSet(SetType.MASS); 379 if (!unitInSet(set, mass)) 380 addUnitToSet(set, mass); 381 382 // Length 383 set = getSet(SetType.LENGTH); 384 if (!unitInSet(set, length)) 385 addUnitToSet(set, length); 386 387 // Angle 388 set = getSet(SetType.ANGLE); 389 if (!unitInSet(set, angle)) 390 addUnitToSet(set, angle); 391 392 393 // Area 394 set = getSet(SetType.AREA); 395 if (!unitInSet(set, area)) 396 addUnitToSet(set, area); 397 398 // Volume 399 set = getSet(SetType.VOLUME); 400 if (!unitInSet(set, volume)) 401 addUnitToSet(set, volume); 402 403 // Velocity 404 set = getSet(SetType.VELOCITY); 405 if (!unitInSet(set, velocity)) 406 addUnitToSet(set, velocity); 407 408 // Acceleration 409 set = getSet(SetType.ACCELERATION); 410 if (!unitInSet(set, accel)) 411 addUnitToSet(set, accel); 412 413 // Force 414 set = getSet(SetType.FORCE); 415 if (!unitInSet(set, force)) 416 addUnitToSet(set, force); 417 418 // Mass density 419 set = getSet(SetType.MASS_DENSITY); 420 if (!unitInSet(set, massDensity)) 421 addUnitToSet(set, massDensity); 422 423 // Inertia 424 set = getSet(SetType.INERTIA); 425 if (!unitInSet(set, inertia)) 426 addUnitToSet(set, inertia); 427 428 // Angular velocity 429 set = getSet(SetType.ANGULAR_VELOCITY); 430 if (!unitInSet(set, angularVelocity)) 431 addUnitToSet(set, angularVelocity); 432 433 // Torque 434 set = getSet(SetType.TORQUE); 435 if (!unitInSet(set, torque)) 436 addUnitToSet(set, torque); 437 438 } 439 440 /** 441 * Returns true if the specified unit is in the given array of units. 442 */ 443 private boolean unitInSet(Unit[] set, Unit unit) { 444 int size = set.length; 445 for (int i = 0; i < size; ++i) 446 if (set[i].equals(unit)) 447 return true; 448 return false; 449 } 450 451 /** 452 * Method that adds the specified unit to the specified array of units by creating a 453 * new array and adding the unit to the end of it. 454 */ 455 private Unit[] addUnitToSet(Unit[] set, Unit unit) { 456 int size = set.length; 457 Unit[] newArray = new Unit[size + 1]; 458 System.arraycopy(set, 0, newArray, 0, size); 459 newArray[size] = unit; 460 return newArray; 461 } 462 463 /** 464 * Return the duration of time units for this unit set. 465 * 466 * @return The units of durations of time. 467 */ 468 public Unit<Duration> time() { 469 return time; 470 } 471 472 /** 473 * Method used to set the time units in this unit set. All units that have a 474 * time/duration component will be changed to use this time value after a call to 475 * "makeCoherent()". 476 * 477 * @param unit the time unit to set this unit set to. 478 * @throws ConversionException if the provided unit is not compatible with time 479 * (seconds). 480 * @see #makeConsistent() 481 */ 482 public void setTime(Unit<Duration> unit) throws ConversionException { 483 if (!unit.isCompatible(time)) 484 throw new ConversionException("Time units must be compatible with seconds. Input unit = " + unit); 485 if (!unit.equals(time)) { 486 Unit old = time; 487 time = unit; 488 isConsistent = false; 489 if (!time.equals(SECOND)) { 490 isCoherent = false; 491 type = UnitSystem.CUSTOM; 492 } 493 propertyChange.firePropertyChange("Time", old, unit); 494 } 495 } 496 497 /** 498 * Return the length units for this unit set. 499 * 500 * @return The length units. 501 */ 502 public Unit<Length> length() { 503 return length; 504 } 505 506 /** 507 * Method used to set the length units in this unit set. All units that have a length 508 * component will be changed to use this length value after a call to "makeCoherent()". 509 * 510 * @param unit the length unit to set this unit set to. 511 * @throws ConversionException if the provided unit is not compatible with length 512 * (meters). 513 * @see #makeConsistent() 514 */ 515 public void setLength(Unit<Length> unit) throws ConversionException { 516 if (!unit.isCompatible(length)) 517 throw new ConversionException("Length units must be compatible with meters. Input unit = " + unit); 518 if (!unit.equals(length)) { 519 Unit old = length; 520 type = UnitSystem.CUSTOM; 521 isConsistent = false; 522 isCoherent = false; 523 length = unit; 524 propertyChange.firePropertyChange("Length", old, unit); 525 } 526 } 527 528 /** 529 * Return the mass units for this unit set. 530 * 531 * @return The mass units. 532 */ 533 public Unit<Mass> mass() { 534 return mass; 535 } 536 537 /** 538 * Method used to set the mass units in this unit set. All units that have a mass 539 * component will be changed to use this mass value after a call to "makeCoherent()". 540 * 541 * @param unit the mass unit to set this unit set to. 542 * @throws ConversionException if the provided unit is not compatible with mass (kg). 543 * @see #makeConsistent() 544 */ 545 public void setMass(Unit<Mass> unit) throws ConversionException { 546 if (!unit.isCompatible(mass)) 547 throw new ConversionException("Mass units must be compatible with kilograms. Input unit = " + unit); 548 if (!unit.equals(mass)) { 549 Unit old = mass; 550 type = UnitSystem.CUSTOM; 551 isConsistent = false; 552 isCoherent = false; 553 mass = unit; 554 propertyChange.firePropertyChange("Mass", old, unit); 555 } 556 } 557 558 /** 559 * Return the angular measure units for this unit set. 560 * 561 * @return The angular measure units. 562 */ 563 public Unit<Angle> angle() { 564 return angle; 565 } 566 567 /** 568 * Method used to set the angular measure units in this unit set. All units that have an angular measure 569 * component will be changed to use this value after a call to "makeCoherent()". 570 * 571 * @param unit the angle unit to set this unit set to. 572 * @throws ConversionException if the provided unit is not compatible with angles (radian). 573 * @see #makeConsistent() 574 */ 575 public void setAngle(Unit<Angle> unit) throws ConversionException { 576 if (!unit.isCompatible(angle)) 577 throw new ConversionException("Angle units must be compatible with radians. Input unit = " + unit); 578 if (!unit.equals(angle)) { 579 Unit old = angle; 580 angle = unit; 581 isConsistent = false; 582 propertyChange.firePropertyChange("Angle", old, unit); 583 } 584 } 585 586 /** 587 * Return the area units. 588 * 589 * @return Area units 590 */ 591 public Unit<Area> area() { 592 return area; 593 } 594 595 /** 596 * Return the volume units. 597 * 598 * @return Volume units 599 */ 600 public Unit<Volume> volume() { 601 return volume; 602 } 603 604 /** 605 * Return the velocity units. 606 * 607 * @return Velocity units 608 */ 609 public Unit<Velocity> velocity() { 610 return velocity; 611 } 612 613 /** 614 * Return the acceleration units. 615 * 616 * @return Acceleration units 617 */ 618 public Unit<Acceleration> acceleration() { 619 return accel; 620 } 621 622 /** 623 * Return the force units. 624 * 625 * @return Force units 626 */ 627 public Unit<Force> force() { 628 return force; 629 } 630 631 /** 632 * Return the inertia units. 633 * 634 * @return Inertia units 635 */ 636 public Unit<Inertia> inertia() { 637 return inertia; 638 } 639 640 /** 641 * Return the mass density units. 642 * 643 * @return Mass density units. 644 */ 645 public Unit<VolumetricDensity> massDensity() { 646 return massDensity; 647 } 648 649 /** 650 * Return the angular velocity units. 651 * 652 * @return Angular velocity units. 653 */ 654 public Unit<AngularVelocity> angularVelocity() { 655 return angularVelocity; 656 } 657 658 /** 659 * Return the torque units. 660 * 661 * @return The torque units. 662 */ 663 public Unit<Torque> torque() { 664 return torque; 665 } 666 667 /** 668 * Creates and returns a copy of this object. 669 * 670 * @return A clone of this UnitSet. 671 */ 672 @Override 673 public Object clone() { 674 Object result = null; 675 676 try { 677 // Make a shallow copy. 678 result = super.clone(); 679 680 } catch (Exception e) { 681 // Can't happen if this object implements Cloneable. 682 e.printStackTrace(); 683 } 684 685 return result; 686 } 687 688 /** 689 * Compares this UnitSet against the specified object for strict equality. 690 * 691 * @param obj the object to compare with. 692 * @return <code>true</code> if this UnitSet is identical to that object; 693 * <code>false</code> otherwise. 694 */ 695 @Override 696 public boolean equals(Object obj) { 697 if (this == obj) 698 return true; 699 if ((obj == null) || (obj.getClass() != this.getClass())) 700 return false; 701 702 UnitSet that = (UnitSet)obj; 703 if (type != that.type) 704 return false; 705 if (!time.equals(that.time)) 706 return false; 707 if (!angle.equals(that.angle)) 708 return false; 709 if (!length.equals(that.length)) 710 return false; 711 if (!mass.equals(that.mass)) 712 return false; 713 if (!area.equals(that.area)) 714 return false; 715 if (!volume.equals(that.volume)) 716 return false; 717 if (!velocity.equals(that.velocity)) 718 return false; 719 if (!accel.equals(that.accel)) 720 return false; 721 if (!massDensity.equals(that.massDensity)) 722 return false; 723 if (!inertia.equals(that.inertia)) 724 return false; 725 if (!angularVelocity.equals(that.angularVelocity)) 726 return false; 727 728 return torque.equals(that.torque); 729 } 730 731 /** 732 * Returns the hash code for this UnitSet. 733 * 734 * @return the hash code value. 735 */ 736 @Override 737 public int hashCode() { 738 int hash = 7; 739 hash = 53 * hash + this.type.hashCode(); 740 hash = 53 * hash + this.time.hashCode(); 741 hash = 53 * hash + this.length.hashCode(); 742 hash = 53 * hash + this.mass.hashCode(); 743 hash = 53 * hash + this.angle.hashCode(); 744 745 hash = 53 * hash + this.area.hashCode(); 746 hash = 53 * hash + this.volume.hashCode(); 747 hash = 53 * hash + this.velocity.hashCode(); 748 hash = 53 * hash + this.accel.hashCode(); 749 hash = 53 * hash + this.massDensity.hashCode(); 750 hash = 53 * hash + this.inertia.hashCode(); 751 hash = 53 * hash + this.angularVelocity.hashCode(); 752 hash = 53 * hash + this.torque.hashCode(); 753 754 return hash; 755 } 756 757 /** 758 * Use this object to notify uses of changes in the inputs to a body part. 759 */ 760 private transient final PropertyChangeSupport propertyChange = new PropertyChangeSupport(this); 761 762 public synchronized void addPropertyChangeListener(PropertyChangeListener l) { 763 propertyChange.addPropertyChangeListener(l); 764 } 765 766 public synchronized void addPropertyChangeListener(String propertyName, PropertyChangeListener l) { 767 propertyChange.addPropertyChangeListener(propertyName, l); 768 } 769 770 public synchronized void removePropertyChangeListener(PropertyChangeListener l) { 771 propertyChange.removePropertyChangeListener(l); 772 } 773 774 public synchronized void removePropertyChangeListener(String propertyName, PropertyChangeListener l) { 775 propertyChange.removePropertyChangeListener(propertyName, l); 776 } 777 778 public synchronized void clearPropertyChangeListeners() { 779 PropertyChangeListener[] listeners = propertyChange.getPropertyChangeListeners(); 780 for (PropertyChangeListener l : listeners) { 781 this.removePropertyChangeListener(l); 782 } 783 } 784 785 /** 786 * Holds the default XML representation for this class. 787 */ 788 protected static final XMLFormat<UnitSet> XML = new XMLFormat<UnitSet>(UnitSet.class) { 789 790 @Override 791 public UnitSet newInstance(Class<UnitSet> cls, XMLFormat.InputElement xml) throws XMLStreamException { 792 793 // Parse out the unit system type. 794 UnitSystem[] systems = UnitSystem.values(); 795 int typeIdx = xml.getAttribute("type", 0); 796 UnitSystem type = systems[typeIdx]; 797 798 // Create the new unit set. 799 UnitSet db = new UnitSet(type); 800 801 // Parse out the units. 802 boolean isConsistent = xml.getAttribute("isConsistent", true); 803 Unit time = Unit.valueOf(xml.getAttribute("time", db.time.toString())); 804 Unit length = Unit.valueOf(xml.getAttribute("length", db.length.toString())); 805 Unit mass = Unit.valueOf(xml.getAttribute("mass", db.mass.toString())); 806 Unit angle = Unit.valueOf(xml.getAttribute("angle", db.angle.toString())); 807 db.setTime(time); 808 db.setLength(length); 809 db.setMass(mass); 810 db.setAngle(angle); 811 812 if (isConsistent) 813 db.makeConsistent(); 814 815 return db; 816 } 817 818 @Override 819 public void read(XMLFormat.InputElement xml, UnitSet db) throws XMLStreamException { 820 // Nothing to do. Already read everything in in "newInstance". 821 } 822 823 @Override 824 public void write(UnitSet db, XMLFormat.OutputElement xml) throws XMLStreamException { 825 xml.setAttribute("type", db.type.ordinal()); 826 xml.setAttribute("isConsistent", db.isConsistent); 827 xml.setAttribute("time", db.time.toString()); 828 xml.setAttribute("length", db.length.toString()); 829 xml.setAttribute("mass", db.mass.toString()); 830 xml.setAttribute("angle", db.angle.toString()); 831 } 832 }; 833 834}