001    package edu.nrao.sss.model.project.scan;
002    
003    import java.util.ArrayList;
004    import java.util.Date;
005    import java.util.HashSet;
006    import java.util.List;
007    import java.util.Set;
008    
009    import javax.xml.bind.annotation.XmlElement;
010    import javax.xml.bind.annotation.XmlElementWrapper;
011    import javax.xml.bind.annotation.XmlRootElement;
012    import javax.xml.bind.annotation.XmlType;
013    
014    import edu.nrao.sss.measure.TimeDuration;
015    import edu.nrao.sss.model.source.Source;
016    
017    /**
018     * A scan that holds a list of {@link SwitchSetting switch settings}.
019     * <p>
020     * <b>CVS Info:</b>
021     * <table style="margin-left:2em">
022     *   <tr><td>$Revision: 161 $</td></tr>
023     *   <tr><td>$Date: 2006-12-15 11:48:34 -0700 (Fri, 15 Dec 2006) $</td></tr>
024     *   <tr><td>$Author: btruitt $</td></tr>
025     * </table></p>
026     *  
027     * @author David M. Harland
028     * @since 2006-07-10
029     */
030    @XmlRootElement
031    @XmlType(propOrder= {"timeOnTarget", "applyAutoPhasing", "switchSettings"})
032    public class SwitchingScan
033      extends Scan
034    {
035      private TimeDuration        timeOnTarget;
036      private boolean             applyAutoPhasing;
037      private List<SwitchSetting> switchSettings;
038    
039      /** Creates a new instance. */
040      SwitchingScan()
041      {
042        super();
043        
044        timeOnTarget     = new TimeDuration();
045        applyAutoPhasing = false;
046        switchSettings   = new ArrayList<SwitchSetting>();
047      }
048      
049      /**
050       * Sets the amount of time that this scan should spend on the target
051       * source, as opposed to on a calibrator.
052       * <p>
053       * If {@code duration} is <i>null</i>, it will be treated as
054       * a non-null duration of size zero.</p>
055       * 
056       * @param duration the amount of time to be spent on the target source.
057       */
058      public void setTimeOnTarget(TimeDuration duration)
059      {
060        timeOnTarget = (duration == null) ? new TimeDuration() : duration;
061      }
062      
063      /**
064       * Returns the amount of time that this scan should spend on the target
065       * source, as opposed to on a calibrator.
066       * <p>
067       * The returned value is guaranteed to be non-null.  It is also the 
068       * duration that is held internally by this scan, so any changes
069       * made to the returned duration will be reflected in this object.</p>
070       *  
071       * @return the amount of time to be spent on the target source.
072       */
073      public TimeDuration getTimeOnTarget()
074      {
075        return timeOnTarget;
076      }
077      
078      /**
079       * Indicates whether or not autophasing should be applied to this scan.
080       * 
081       * @param apply <i>true</i> if autophasing is desired.
082       */
083      public void setApplyAutoPhasing(boolean apply)
084      {
085        applyAutoPhasing = apply;
086      }
087      
088      /**
089       * Returns true if this scan should perform autophasing.
090       * @return true if this scan should perform autophasing.
091       */
092      public boolean getApplyAutoPhasing()  { return applyAutoPhasing; }
093    
094      /**
095       * Sets the list of switch settings held by this scan.
096       * A <i>null</i> {@code replacementList} will be interpreted
097       * as a new empty list.
098       * <p>
099       * This scan will hold a reference to {@code replacementList}
100       * (unless it is <i>null</i>), so any changes made to the list
101       * after calling this method will be reflected in this object.</p>
102       * 
103       * @param replacementList a list of switch settings to be held by this
104       *                        scan.
105       */
106      public void setSwitchSettings(List<SwitchSetting> replacementList)
107      {
108        switchSettings = (replacementList == null) ? new ArrayList<SwitchSetting>()
109                                                   : replacementList;
110    
111      }
112      
113      /**
114       * Returns the list of pointing positions held by this scan.
115       * The returned list is guaranteed to be non-null, but may
116       * be empty.
117       * <p>
118       * The returned list is the actual list held by this scan,
119       * so changes made to the list will be reflected in this
120       * object.</p>
121       * 
122       * @return the list of pointing positions held by this scan.
123       */
124      @XmlElementWrapper(name="settings")
125      @XmlElement(name="switchSetting")
126      public List<SwitchSetting> getSwitchSettings()
127      {
128        //Tell each setting that it is part of this scan.
129        //(Note that, with the way the switchSetting methods are currently written,
130        //we cannot rely on the settings knowing about this scan.  Though it may be
131        //redundant, we ensure the settings are up to date on a just-in-time basis.
132        //If this becomes too costly, we would need to:
133        //  1. Have this method return a clone of the settings.
134        //  2. Stop saving a reference to the incoming list in setSwitchSettings
135        //  3. Add an addSwitchSetting method and do the setScan call there.)
136        for (SwitchSetting ss : switchSettings)
137          ss.setScan(this);
138    
139        return switchSettings;
140      }
141    
142      /**
143       * Returns the set of all sources used by this scan.  This includes the main
144       * source and the source used in each of the switch settings.
145       * @param dateTime the time for which sources are requested.
146       * @return the set of all sources used by this scan.
147       */
148      public Set<Source> getSources(Date dateTime)
149      {
150        Set<Source> sources = new HashSet<Source>();
151        
152        sources.add(getSource(dateTime));
153       
154        for (SwitchSetting setting : switchSettings)
155          sources.add(setting.getSource(dateTime));
156        
157        return sources;
158    
159      }
160      
161      //============================================================================
162      // 
163      //============================================================================
164    
165      /* (non-Javadoc)
166       * @see edu.nrao.sss.model.project.scan.ScanLoopElement#toString()
167       */
168      public String toSummaryString()
169      {
170        StringBuilder buff = new StringBuilder(super.toSummaryString());
171        
172        buff.append(", timeOnTgt=").append(timeOnTarget);
173        buff.append(", applyAutoPhas=").append(applyAutoPhasing);
174        buff.append(", switchCnt=").append(switchSettings.size());
175        
176        return buff.toString();
177      }
178    
179      /**
180       *  Returns a switching scan that is a copy of this one.
181       *  <p>
182       *  The returned scan is, for the most part, a deep copy of this one.
183       *  However, there are a few exceptions noted in the
184       *  {@link ScanLoop#clone() clone method} of this class's parent.</p>
185       *  <p>
186       *  If anything goes wrong during the cloning procedure,
187       *  a {@code RuntimeException} will be thrown.</p>
188       */
189      public SwitchingScan clone()
190      {
191        SwitchingScan clone = null;
192    
193        try
194        {
195          //This line takes care of the primitive & immutable fields properly
196          clone = (SwitchingScan)super.clone();
197          
198          clone.timeOnTarget = this.timeOnTarget.clone();
199          
200          //Need to clone set AND contained elements.
201          clone.switchSettings = new ArrayList<SwitchSetting>();
202          for (SwitchSetting ss : this.switchSettings)
203            clone.switchSettings.add(ss.clone());
204        }
205        catch (Exception ex)
206        {
207          throw new RuntimeException(ex);
208        }
209        
210        return clone;
211      }
212    
213      /**
214       * Returns <i>true</i> if {@code o} is equal to this switching scan.
215       * <p>
216       * In order for {@code o} to be equal to this scan, it must have
217       * equal switch settings in the same order as those of this scan.
218       * It must also follow the rules set forth in the
219       * {@link ScanLoop#equals(Object) equals method} of this class's parent.</p>
220       */
221      public boolean equals(Object o)
222      {
223        //Quick exit if o is this object
224        if (o == this)
225          return true;
226        
227        //Not equal if super class says not equal
228        if (!super.equals(o))
229          return false;
230        
231        //Super class tested for Class equality, so cast is safe
232        SwitchingScan other = (SwitchingScan)o;
233        
234        //Compare attributes
235        return (other.applyAutoPhasing == this.applyAutoPhasing) &&
236                other.timeOnTarget.equals(this.timeOnTarget) &&
237                other.switchSettings.equals(this.switchSettings);
238      }
239    
240      /* (non-Javadoc)
241       * @see edu.nrao.sss.model.project.scan.Scan#hashCode()
242       */
243      public int hashCode()
244      {
245        //Taken from the Effective Java book by Joshua Bloch.
246        //The constant 37 is arbitrary & carries no meaning.
247        int result = 37 * super.hashCode();
248        
249        result = 37 * result + new Boolean(applyAutoPhasing).hashCode();
250        result = 37 * result + timeOnTarget.hashCode();
251        result = 37 * result + switchSettings.hashCode();
252        
253        return result;
254      }
255    }