001    package edu.nrao.sss.model.resource;
002    
003    import java.util.ArrayList;
004    import java.util.Date;
005    import java.util.List;
006    
007    import edu.nrao.sss.math.Polynomial;
008    import edu.nrao.sss.measure.TimeDuration;
009    import edu.nrao.sss.measure.TimeInterval;
010    import edu.nrao.sss.measure.TimeUnits;
011    import edu.nrao.sss.util.Identifiable;
012    import edu.nrao.sss.util.LookupTable;
013    
014    /**
015     * A resource specification for pulsar observations.
016     * <p>
017     * <b>Version Info:</b>
018     * <table style="margin-left:2em">
019     *   <tr><td>$Revision: 1709 $</td></tr>
020     *   <tr><td>$Date: 2008-11-14 11:22:37 -0700 (Fri, 14 Nov 2008) $</td></tr>
021     *   <tr><td>$Author: dharland $</td></tr>
022     * </table></p>
023     *  
024     * @author David M. Harland
025     * @since 2006-09-08
026     */
027    public class PulsarSpecification
028      implements Cloneable
029    {
030      //This ID field is here for the persistance layer
031      @SuppressWarnings("unused")
032      private Long id;
033      
034      //These 3 properties will probably be removed, because they are
035      //really properties of the source.
036      private TimeDuration                  pulsePeriod;
037      private double                        dispersion;
038      private LookupTable<Date, Polynomial> phasePolynomials;
039      //Wrote email to Walter B. re: phase bins
040      private List<TimeInterval>            phaseBins;
041      
042      /** Creates a new instance. */
043      public PulsarSpecification()
044      {
045        id = Identifiable.UNIDENTIFIED;
046        
047        //TODO find out what default values are reasonable
048        
049        pulsePeriod      = new TimeDuration("0.0", TimeUnits.MILLISECOND);
050        dispersion       = 0.0;
051        phasePolynomials = new LookupTable<Date, Polynomial>();
052        phaseBins        = new ArrayList<TimeInterval>();
053      }
054    
055      /**
056       * Resets the identifier of this specification
057       * to a value that represents the unidentified state.
058       * <p>
059       * This method is useful for preparing a specification for storage in a
060       * database.
061       * The ID property (as of now, though this may change in the future) is
062       * used by our persistence mechanism to identify objects.  If you are
063       * persisting this object for the first time, you may need to call
064       * this method before performing a save.  This is especially true if
065       * you have created this object from XML, as the XML unmarshalling
066       * brings along the ID property.</p> 
067       */
068      void clearId()
069      {
070        this.id = Identifiable.UNIDENTIFIED;
071      }
072    
073      //============================================================================
074      // 
075      //============================================================================
076      
077      /**
078       *  Returns a pulsar specification that is a copy of this one.
079       *  <p>
080       *  If anything goes wrong during the cloning procedure,
081       *  a {@code RuntimeException} will be thrown.</p>
082       */
083      @Override
084      public PulsarSpecification clone()
085      {
086        PulsarSpecification clone = null;
087        
088        try
089        {
090          //This line handles the primitive and immutable instance variables
091          clone = (PulsarSpecification)super.clone();
092    
093          //We do NOT want the clone to have the same ID as the original.
094          //The ID is here for the persistence layer; it is in charge of
095          //setting IDs.  To help it, we put the clone's ID in the uninitialized
096          //state.
097          clone.id = Identifiable.UNIDENTIFIED;
098    
099          clone.pulsePeriod = this.pulsePeriod.clone();
100          
101          //Clone the collection AND the contained elements
102          clone.phasePolynomials = new LookupTable<Date, Polynomial>();
103          for (Date key : this.phasePolynomials.getKeySet())
104            clone.phasePolynomials.put(key, this.phasePolynomials.get(key).clone());
105            
106          clone.phaseBins = new ArrayList<TimeInterval>();
107          for (TimeInterval interval : this.phaseBins)
108            clone.phaseBins.add(interval.clone());
109        }
110        catch (Exception ex)
111        {
112          throw new RuntimeException(ex);
113        }
114        
115        return clone;
116      }
117      
118      /** Returns <i>true</i> if {@code o} is equal to this specification. */
119      @Override
120      public boolean equals(Object o)
121      {
122        //Quick exit if o is this
123        if (o == this)
124          return true;
125        
126        //Quick exit if o is null
127        if (o == null)
128          return false;
129        
130        //Quick exit if classes are different
131        if (!o.getClass().equals(this.getClass()))
132          return false;
133        
134        PulsarSpecification other = (PulsarSpecification)o;
135        
136        //NOTE: absence of ID field is intentional
137    
138        return other.dispersion == this.dispersion        &&
139               other.pulsePeriod.equals(this.pulsePeriod) &&
140               other.phaseBins.equals(this.phaseBins)     &&
141               other.phasePolynomials.equals(this.phasePolynomials);
142      }    
143    
144      /** Returns a hash code value for this specification. */
145      @Override
146      public int hashCode()
147      {
148        //Taken from the Effective Java book by Joshua Bloch.
149        //The constants 17 & 37 are arbitrary & carry no meaning.
150        int result = 17;
151        
152        result = 37 * result + new Double(dispersion).hashCode();
153        result = 37 * result + pulsePeriod.hashCode();
154        result = 37 * result + phaseBins.hashCode();
155        result = 37 * result + phasePolynomials.hashCode();
156        
157        return result;
158      }
159    }