001    /**
002     * Do we want to use this space for anything?  Eg, revision history?
003     */
004    package edu.nrao.sss.model.proposal;
005    
006    import java.math.BigDecimal;
007    import java.util.HashSet;
008    import java.util.Set;
009    
010    import edu.nrao.sss.measure.TimeDuration;
011    import edu.nrao.sss.util.Identifiable;
012    
013    /** <i>Placeholder for time when we integrate proposal work.</i>
014     * A proposed observation session.
015     * <p>
016     * A session consists of one or more {@link SessionPair session pairs}, each
017     * of which brings together a source and a resource.  A session is a close
018     * cousin of a scheduling block, which is used
019     * during the observation preparation and scheduling phases.</p>
020     * <p>
021     * <b>CVS Info:</b>
022     * <table style="margin-left:2em">
023     *   <tr><td>$Revision: 1709 $</td>
024     *   <tr><td>$Date: 2008-11-14 11:22:37 -0700 (Fri, 14 Nov 2008) $</td>
025     *   <tr><td>$Author: dharland $</td>
026     * </table></p>
027     * 
028     * @since 2006-03-23
029     */
030    public class Session
031      implements Identifiable//, XmlSerializable
032    {
033      private static final String DEFAULT_NAME      = "[Not Specified]";
034      private static final String NO_CONSTRAINT     = "";
035      private static final String NO_COMMENTS       = "";
036      private static final String NO_MINIMUM_LST    = "00:00:00";
037      private static final String NO_MAXIMUM_LST    = "23:59:59";
038      private static final double MINIMUM_ELEVATION =  0.0;
039    //private static final double MAXIMUM_ELEVATION = 90.0;
040      
041      //IDENTIFICATION
042      private Long   id;  //A unique identifier for the persistence layer.
043      private String name;
044    
045      //CONTAINED OBJECTS (SessionPairs)
046      private Set<SessionPair> sessionPairs;
047      
048      //CONSTRAINTS
049      private String minimumLst;
050      private String maximumLst;
051      private double minimumElevation;
052      private String schedulingConstraint;
053      
054      //LENGTH OF OBSERVATION
055      private int          repeatCount;
056      private TimeDuration sessionTime;
057      private TimeDuration separation;
058      
059      //OTHER ATTRIBUTES
060      private String comments;
061    
062      /** Creates a new instance. */
063      public Session()
064      {
065        sessionPairs = new HashSet<SessionPair>();
066        sessionTime  = new TimeDuration();
067        separation   = new TimeDuration();
068        
069        initialize();
070      }
071      
072      /** Initializes the instance variables of this class.  */
073      private void initialize()
074      {
075        id   = Identifiable.UNIDENTIFIED;
076        name = DEFAULT_NAME;
077        
078        minimumLst           = NO_MINIMUM_LST;
079        maximumLst           = NO_MAXIMUM_LST;
080        minimumElevation     = MINIMUM_ELEVATION;
081        schedulingConstraint = NO_CONSTRAINT;
082        
083        repeatCount = 0;
084        
085        comments = NO_COMMENTS; 
086      }
087      
088      /**
089       *  Resets this session to its initial state.
090       *  A reset session has the same state as a new session. 
091       */
092      public void reset()
093      {
094        initialize();
095        
096        sessionPairs.clear();
097        sessionTime.reset();
098        separation.reset();
099      }
100    
101      //============================================================================
102      // IDENTIFICATION
103      //============================================================================
104    
105      /* (non-Javadoc)
106       * @see edu.nrao.sss.util.Identifiable#getId()
107       */
108      public Long getId()
109      {
110        return id;
111      }
112    
113      @SuppressWarnings("unused")
114      private void setId(Long id)
115      {
116        this.id = id;
117      }
118      
119      /**
120       * Sets the name of this session to {@code newName}.
121       * <p>
122       * If {@code newName} is <i>null</i> or the empty string
123       * (<tt>""</tt>), the request to change the name will be
124       * denied and the current name will remain in place.</p>
125       * 
126       * @param newName the name of this session.
127       */
128      public void setName(String newName)
129      {
130        if (newName != name && newName.length() > 0)
131          name = newName;
132      }
133      
134      /**
135       * Returns the name of this session.
136       * 
137       * @return the name of this session.
138       */
139      public String getName()
140      {
141        return name;
142      }
143    
144      //============================================================================
145      // CONTAINED OBJECTS (SessionPairs)
146      //============================================================================
147      
148      /**
149       * Add a new session pair to this session.
150       * 
151       * @param newPair the new session pair to be added to this session.  If
152       *                {@code newPair} is <i>null</i> this method does nothing.
153       */
154      public void addSessionPair(SessionPair newPair)
155      {
156        if (newPair != null)
157          sessionPairs.add(newPair);
158      }
159      
160      /**
161       * Removes the given session pair from this session.
162       * 
163       * @param oldPair the session pair to be removed from this session.
164       */
165      public void removeSessionPair(SessionPair oldPair)
166      {
167        if (oldPair != null)
168          sessionPairs.remove(oldPair);
169      }
170      
171      /**
172       * Returns this session's set of session pairs.
173       * <p>
174       * Note that this session's actual collection of pairs is returned, not
175       * a clone thereof.  This means that any changes a client makes to the
176       * collection will be reflected in this session.</p>
177       * <p>
178       * This method is guaranteed to return a non-null value, though the
179       * set it returns may be empty.</p>
180       * 
181       * @return this session's set of session pairs.
182       */
183      public Set<SessionPair> getSessionPairs()
184      {
185        return sessionPairs;
186      }
187      
188      /**
189       * Replaces this session's session pairs with the given collection.
190       * <p>
191       * If {@code replacementSet} is <i>null</i>, this session's collection
192       * will be replaced with an empty collection.</p>
193       * 
194       * @param replacementSet a new set of session pairs that replaces this
195       *                       session's current set.
196       */
197      @SuppressWarnings("unused")
198      private void setSessionPairs(Set<SessionPair> replacementSet)
199      {
200        sessionPairs = (replacementSet == null) ? new HashSet<SessionPair>()
201                                                : replacementSet;
202      }
203    
204      //============================================================================
205      // OBSERVATION CONSTRAINTS
206      //============================================================================
207    
208      /**
209       * Sets the earliest local sidereal time for this observation session.
210       * <p>
211       * The general form of the string is <tt>hh:mm:ss.sss</tt>.  Leading zeroes
212       * are not required, though all three parts (hours, minutes, seconds) are
213       * required.  Fractional seconds are not required.
214       * Examples of valid text strings:
215       * <ul>
216       *   <li>00:00:00</li>
217       *   <li>23:59:59.987</li>
218       *   <li>2:3:4.</li>
219       *   <li>0:07:8.1</li>
220       * </ul></p>
221       * <p>
222       * Valid ranges for each part:
223       * <ul>
224       *   <li>hh - [0-23]</li>
225       *   <li>mm - [0-59]</li>
226       *   <li>ss.sss... - [0-60) (i.e., zero up to, but not including, 60)</li>
227       * </ul></p>
228       * <p>
229       * If the parameter string is not well formed, or if the values are not in
230       * the ranges given above, an {@code IllegalArgumentException} is thrown.</p>
231       * <p>
232       * See the note in {@link #getMinimumLst()} about the relationship between
233       * the minimum and maximum LST values.</p>
234       * 
235       * @param lstMin the earliest local sidereal time for this observation
236       *               session.
237       *               
238       * @throws IllegalArgumentException if {@code lstMin} does not follow the
239       *         conventions set forth in the method comments.
240       */
241      public void setMinimumLst(String lstMin)
242      {
243        /* TODO: Reactivate once R.A.'s parsing is restored
244        //Using the R.A. class to parse the string is a temporary kludge.
245        //My hunch is that we'll want to have some kind of LST class, or
246        //a general time class that be expressed in LST.
247        if (hhmmssParser == null)
248          hhmmssParser = new Longitude();
249        
250        //If lstMin is not valid, the next line will throw an illegal arg ex
251        hhmmssParser.parse(lstMin);
252        */
253        minimumLst = lstMin;
254      }
255      
256      /**
257       * Returns the earliest local sidereal time for this observation session.
258       * <p>
259       * Note that, using a 24-hour clock that resets to zero at LST midnight,
260       * the "minimum" might be a greater number than the maximum.  In this
261       * situation, it will be understood that the interval crosses midnight
262       * and into the next day.</p>
263       * 
264       * @return the earliest local sidereal time for this observation session.
265       */
266      public String getMinimumLst()
267      {
268        return minimumLst;
269      }
270    
271      /**
272       * Sets the latest local sidereal time for this observation session.
273       * <p>
274       * See {@link #setMinimumLst(String)} for information about the form of
275       * the parameter string.  See also the note in {@link #getMinimumLst()}
276       * about the relationship between the minimum and maximum LST values.</p>
277       * 
278       * @param lstMax the latest local sidereal time for this observation
279       *               session.
280       *               
281       * @throws IllegalArgumentException if {@code lstMax} does not follow the
282       *         conventions set forth in the method comments of
283       *         {@code setLstMinimum}.
284       */
285      public void setMaximumLst(String lstMax)
286      {
287        /* TODO: Reactivate once R.A.'s parsing is restored
288        //Using the R.A. class to parse the string is a temporary kludge.
289        //My hunch is that we'll want to have some kind of LST class, or
290        //a general time class that be expressed in LST.
291        if (hhmmssParser == null)
292          hhmmssParser = new Longitude();
293        
294        //If lstMin is not valid, the next line will throw an illegal arg ex
295        hhmmssParser.parse(lstMax);
296        */
297        maximumLst = lstMax;
298      }
299      
300      /**
301       * Returns the latest local sidereal time for this observation session.
302       * See note in {@link #getMinimumLst()}.
303       * 
304       * @return the latest local sidereal time for this observation session.
305       */
306      public String getMaximumLst()
307      {
308        return maximumLst;
309      }
310      
311      /**
312       * Sets the minimum acceptable elevation (of which source?) for this session.
313       * 
314       * @param degrees the minimum acceptable elevation, measured in degrees above
315       *                the horizon.  This value must be between 0.0 and 90.0,
316       *                inclusive.
317       * 
318       * @throws IllegalArgumentException if {@code degrees} is not within the range
319       *                                  stated above.
320       */
321      public void setMinimumElevation(double degrees)
322      {
323        if (degrees < 0.0 || degrees > 90.0)
324          throw new IllegalArgumentException(
325            "Minimum elevation must be between 0 and 90 degrees, inclusive.");
326      
327        minimumElevation = degrees;
328      }
329      
330      /**
331       * Returns the minimum acceptable elevation (of which source?) for this session.
332       * 
333       * @return the minimum acceptable elevation (of which source?) for this session,
334       *         in degrees above the horizon.
335       */
336      public double getMinimumElevation()
337      {
338        return minimumElevation;
339      }
340    
341      /**
342       * Sets the scheduling constraints specified by a proposal's author
343       * for this session.  These constraints are for human consumption; there
344       * is no required format for this text.
345       * 
346       * @param constraint the scheduling constraints for this session.
347       */
348      public void setSchedulingConstraint(String constraint)
349      {
350        this.schedulingConstraint = (constraint == null) ? NO_CONSTRAINT
351                                                         : constraint;
352      }
353      
354      /**
355       * Returns the scheduling constraints for this session.  If there
356       * are no constraints, the empty string is returned.  This method
357       * will never return <i>null</i>.
358       * 
359       * @return this session's scheduling restraints.
360       */
361      public String getSchedulingConstraint()
362      {
363        return schedulingConstraint;
364      }
365    
366      //============================================================================
367      // LENGTH OF OBSERVATION
368      //============================================================================
369      
370      /**
371       * Sets the number of times to repeat this session.
372       * 
373       * @param repeats the number of times to repeat this session.
374       *                If {@code repeats} is less than zero, it will be treated
375       *                as zero.
376       */
377      public void setRepeatCount(int repeats)
378      {
379        repeatCount = (repeats < 0) ? 0 : repeats;
380      }
381      
382      /**
383       * Returns the number of times to repeat this session.
384       * 
385       * @return the number of times to repeat this session.
386       */
387      public int getRepeatCount()
388      {
389        return repeatCount;
390      }
391      
392      /**
393       * Sets the separation in time between successive repeats of this session.
394       * 
395       * @param separation the time duration between successive repeats of this
396       *                   session.  If {@code separation} is <i>null</i>, it
397       *                   will be treated as a duration of no time.
398       */
399      @SuppressWarnings("unused")
400      private void setSeparation(TimeDuration separation)
401      {
402        this.separation = (separation == null) ? new TimeDuration() : separation;
403      }
404    
405      /**
406       * Returns the separation in time between successive repeats of this session.
407       * 
408       * @return the separation in time between successive repeats of this session.
409       */
410      public TimeDuration getSeparation()
411      {
412        return separation;
413      }
414      
415      /**
416       * Sets the amount of time needed for one execution of this session.
417       * 
418       * @param neededTime the amount of time needed for one execution of this
419       *                   session.  If {@code neededTime} is <i>null</i>, it
420       *                   will be treated as a duration of no time.
421       */
422      @SuppressWarnings("unused")
423      private void setSessionTime(TimeDuration neededTime)
424      {
425        sessionTime = (neededTime == null) ? new TimeDuration() : neededTime;
426      }
427      
428      /**
429       * Returns the amount of time needed for one execution of this session.
430       * 
431       * @return the amount of time needed for one execution of this session.
432       */
433      public TimeDuration getSessionTime()
434      {
435        return sessionTime;
436      }
437      
438      /**
439       * Returns the total time needed for this session after accounting for
440       * the desired number of executions.
441       * 
442       * @return total time needed for this session.
443       */
444      public TimeDuration getTotalTime()
445      {
446        TimeDuration totalTime = (TimeDuration)getSessionTime().clone();
447        
448        if (repeatCount > 1)
449          totalTime.multiplyBy(BigDecimal.valueOf(repeatCount));
450        
451        return totalTime;
452      }
453      
454      //============================================================================
455      // MISCELLANEOUS
456      //============================================================================
457      
458      /**
459       * Sets the proposer's comments for this observation session.
460       * 
461       * @param comments the proposer's comments for this observation session.
462       *                 If {@code comments} is <i>null</i>, it will be treated
463       *                 as the empty string.
464       */
465      public void setComments(String comments)
466      {
467        this.comments = (comments == null) ? NO_COMMENTS : comments;
468      }
469      
470      /**
471       * Returns the proposer's comments for this observation session.
472       * 
473       * @return the proposer's comments for this observation session.
474       */
475      public String getComments()
476      {
477        return comments;
478      }
479      
480      //TODO toXml
481      //TODO clone
482      //TODO equals
483      //TODO hashCode
484    }