001    package edu.nrao.sss.model.project;
002    
003    import java.util.ArrayList;
004    import java.util.List;
005    
006    import edu.nrao.sss.model.project.scan.Scan;
007    import edu.nrao.sss.model.project.scan.ScanLoop;
008    import edu.nrao.sss.model.project.scan.ScanLoopValidator;
009    import edu.nrao.sss.validation.AbstractValidator;
010    import edu.nrao.sss.validation.FailureSeverity;
011    import edu.nrao.sss.validation.Validation;
012    import edu.nrao.sss.validation.ValidationFailure;
013    import edu.nrao.sss.validation.ValidationPurpose;
014    import edu.nrao.sss.validation.Validator;
015    
016    /**
017     * A validator of {@link SchedulingBlock scheduling blocks}.
018     * <p>
019     * <u>Validations Performed for All Purposes</u>
020     * <ol>
021     *   <li>Short name and/or long name was set.</li>
022     *   <li>Scheduling block has at least one scan.</li>
023     *   <li>Scan sequence is valid.<sup>1</sup></li>
024     * </ol>
025     * <sup>1</sup><i>The particular validations performed depend on the
026     * validator used by this one to operate on the target's scan
027     * sequence.</i></p>
028     * <p>
029     * <b>Version Info:</b>
030     * <table style="margin-left:2em">
031     *   <tr><td>$Revision: 2205 $</td></tr>
032     *   <tr><td>$Date: 2009-04-16 12:29:48 -0600 (Thu, 16 Apr 2009) $</td></tr>
033     *   <tr><td>$Author: btruitt $</td></tr>
034     * </table></p>
035     * 
036     * @author David M. Harland
037     * @since 2007-02-09
038     */
039    public class SchedulingBlockValidator
040      extends AbstractValidator<SchedulingBlock>
041    {
042      private ScanLoopValidator scanLoopValidator;
043       
044      /** Creates a new instance. */
045      public SchedulingBlockValidator()
046      {
047        super(SchedulingBlockValidator.class.getName(), SchedulingBlock.class);
048      }
049    
050      public void setScanLoopValidator(ScanLoopValidator scanLoopValidator)
051      {
052              this.scanLoopValidator = scanLoopValidator;
053      }
054      
055      /* (non-Javadoc)
056       * @see AbstractValidator#makeValidationList(ValidationPurpose)
057       */
058      @Override
059      protected List<Validation<SchedulingBlock>>
060        makeValidationList(ValidationPurpose purpose)
061      {
062        List<Validation<SchedulingBlock>> validations =
063          new ArrayList<Validation<SchedulingBlock>>();
064        
065        validations.add(new HasName(this, purpose));
066        validations.add(new HasScan(this, purpose));
067        validations.add(new TimingValidation(this, purpose));
068        validations.add(new HasValidCount(this, purpose));
069        validations.add(new FixedDateHasStartTime(this, purpose));
070        
071        return validations; 
072      }
073    
074      /* (non-Javadoc)
075       * @see edu.nrao.sss.model.validation.AbstractValidator#validate()
076       */
077      @Override
078      protected void validate()
079      {
080        super.validate();
081        
082        //Validate the scan sequence
083        ScanLoopValidator slv = (ScanLoopValidator)getScanLoopValidator();
084        slv.setSkipOutermostLoop(true);
085        failures.addAll(slv.validate(target.getScanSequence(), purpose));
086      }
087    
088      //============================================================================
089      // COMPONENT VALIDATORS
090      //============================================================================
091    
092      /** Returns a validator for program blocks. */
093      public Validator getScanLoopValidator()
094      {
095        Validator validator = scanLoopValidator;
096        
097        if (validator == null)
098        {
099          if (manager != null)
100            validator = manager.getValidator(ScanLoop.class);
101        
102          if (validator == null)
103            validator = new ScanLoopValidator();
104        }
105        
106        return validator;
107      }
108    
109      //============================================================================
110      // 
111      //============================================================================
112    
113      /** Make sure one or both of shortName & longName have been entered. */
114      class HasName extends HasNameValidation<SchedulingBlock>
115      {
116        protected HasName(AbstractValidator<SchedulingBlock> validationContainer,
117                          ValidationPurpose                  reasonForValidation)
118        {
119          super(validationContainer, reasonForValidation, "scheduling blocks");
120        }
121        
122        /** This test is passed if at least one name is not in default state. */
123        @Override
124        protected boolean passesTest()
125        {
126          return !target.getName().equals(SchedulingBlock.DEFAULT_NAME);
127        }
128      }
129    
130      //============================================================================
131      // ENSURE SCHEDULING BLOCK HAS SCANS
132      //============================================================================
133      
134      /** Make sure scheduling block has at least 1 scan. */
135      class HasScan extends Validation<SchedulingBlock>
136      {
137        protected HasScan(AbstractValidator<SchedulingBlock> validationContainer,
138                          ValidationPurpose                  reasonForValidation)
139        {
140          super(validationContainer, reasonForValidation);
141          
142          severity = 
143            (reasonForValidation == ValidationPurpose.CERTIFY_READY_TO_USE) ?
144              FailureSeverity.ERROR : FailureSeverity.WARNING;
145        }
146        
147        /** This test is passed if SB has > 0 scans. */
148        @Override
149        protected boolean passesTest()
150        {
151          return target.getScanSequence().size() > 0;
152        }
153        
154        /* (non-Javadoc)
155         * @see edu.nrao.sss.validation.Validation#displayMessage()
156         */
157        @Override
158        protected String displayMessage()
159        {
160          StringBuilder buff = new StringBuilder("Scheduling block '");
161          
162          buff.append(target.getName()).append("' has no scans.  ");
163    
164          if (purpose == ValidationPurpose.CERTIFY_READY_TO_USE)
165          {
166            buff.append("A scheduling block must have at least one scan")
167                .append(" in order to be executed.  Please add a scan to this")
168                .append(" scheduling block, or remove this scheduling block from")
169                .append(" its program block.");
170          }
171          else
172          {
173            buff.append("This is fine for now, but you will need to add at least")
174                .append(" one scan before this scheduling block is ready")
175                .append(" for execution.");
176          }
177    
178          return buff.toString();
179        }
180    
181        /* (non-Javadoc)
182         * @see edu.nrao.sss.validation.Validation#debugMessage()
183         */
184        @Override
185        protected String debugMessage()
186        {
187          StringBuilder buff = new StringBuilder("SchedBlock '");
188          
189          buff.append(target.getName()).append("' has no scans.");
190          
191          return buff.toString();
192        }
193      }
194      
195      /** Makes sure scheduling block has a start time specified iff it has a type of FIXED_DATE. */
196      class FixedDateHasStartTime extends Validation<SchedulingBlock>
197      {
198        protected FixedDateHasStartTime(AbstractValidator<SchedulingBlock> validationContainer,
199                          ValidationPurpose                  reasonForValidation)
200        {
201          super(validationContainer, reasonForValidation);
202          
203          severity = 
204            (reasonForValidation == ValidationPurpose.CERTIFY_READY_TO_USE) ?
205              FailureSeverity.ERROR : FailureSeverity.WARNING;
206        }
207        
208        /** Makes sure scheduling block has a start time specified iff it has a type of FIXED_DATE. */
209        @Override
210        protected boolean passesTest()
211        {
212          if (target.getType().equals(SchedulingType.FIXED_DATE))
213            return target.hasFixedStartTime();
214    
215          else
216            return !target.hasFixedStartTime();
217        }
218        
219        /* (non-Javadoc)
220         * @see edu.nrao.sss.validation.Validation#displayMessage()
221         */
222        @Override
223        protected String displayMessage()
224        {
225          StringBuilder buff = new StringBuilder("Scheduling block '");
226          
227          buff.append(target.getName()).append("' must have a start time specified if the scheduling type is fixed date and it must NOT have a start time specified otherwise.  ");
228    
229          if (purpose != ValidationPurpose.CERTIFY_READY_TO_USE)
230          {
231            buff.append("This is fine for now, but you will need to address this")
232                .append(" before this scheduling block is ready for execution.");
233          }
234    
235          return buff.toString();
236        }
237    
238        /* (non-Javadoc)
239         * @see edu.nrao.sss.validation.Validation#debugMessage()
240         */
241        @Override
242        protected String debugMessage()
243        {
244          StringBuilder buff = new StringBuilder("Scheduling block '");
245          
246          buff.append(target.getName()).append("' must have a start time specified if the scheduling type is fixed date and it must NOT have a start time specified otherwise.  ");
247          return buff.toString();
248        }
249      }
250    
251      /**
252       * Authorized Count must be greater than 1, and for fixed scheduled SB's it
253       * must be exactly 1.
254       */
255      class HasValidCount extends Validation<SchedulingBlock>
256      {
257        protected HasValidCount(AbstractValidator<SchedulingBlock> validationContainer,
258                                ValidationPurpose                  reasonForValidation)
259        {
260          super(validationContainer, reasonForValidation);
261          
262          severity = 
263            (reasonForValidation == ValidationPurpose.CERTIFY_READY_TO_USE) ?
264              FailureSeverity.ERROR : FailureSeverity.WARNING;
265        }
266        
267        /** This test is passed if SB's authorized count is > 0. */
268        @Override
269        protected boolean passesTest()
270        {
271          if (!target.getExecutionStatus().isFinal())
272          {
273            if (target.mayBeScheduledDynamically())
274              return target.getAuthorizedCount() > 0;
275            else
276              return target.getAuthorizedCount() == 1;
277          }
278          
279          return true;
280    
281          //Note: for fixed-date we could allow count > 1 if and only if
282          //SB has no start-time scans, no stop-time scans, and the
283          //iterations are to be run consecutively.
284        }
285        
286        /* (non-Javadoc)
287         * @see edu.nrao.sss.validation.Validation#displayMessage()
288         */
289        @Override
290        protected String displayMessage()
291        {
292          StringBuilder buff = new StringBuilder("Scheduling block '");
293          
294          buff.append(target.getName()).append("' has an authorized count of ");
295          buff.append(target.getAuthorizedCount());
296    
297          if (target.mayBeScheduledDynamically())
298            buff.append(".  It should be > 0.  ");
299          else
300            buff.append(".  It should be 1");
301    
302          if (purpose == ValidationPurpose.CERTIFY_READY_TO_USE)
303          {
304            buff.append("Please set a valid athorized count for this")
305                .append(" scheduling block, or remove this scheduling block from")
306                .append(" its program block.");
307          }
308          else
309          {
310            buff.append("This is fine for now, but you will need to set a valid")
311                .append(" authorized count before this scheduling block is ready")
312                .append(" for execution.");
313          }
314    
315          return buff.toString();
316        }
317    
318        /* (non-Javadoc)
319         * @see edu.nrao.sss.validation.Validation#debugMessage()
320         */
321        @Override
322        protected String debugMessage()
323        {
324          StringBuilder buff = new StringBuilder("Scheduling block '");
325          
326          buff.append(target.getName()).append("' has an authorized count of ");
327          buff.append(target.getAuthorizedCount());
328    
329          /*
330          if (is fixed scheduled)
331            buff.append(".  It should be 1");
332    
333          else
334          */
335            buff.append(".  It should be > 0");
336    
337          return buff.toString();
338        }
339      }
340      
341      /**
342       * Validates the structure of the scans and loops held by this SB.
343       * Validations:
344       * 1) dynamically scheduled SB's have no stop-time scans
345       * 2) make sure a stop-time scan doesn't cut the previous scan short
346       * 3) stop-time scans are not allowed inside loops w/ iteration counts greater than 1.
347       */
348      class TimingValidation extends Validation<SchedulingBlock>
349      {
350        private ScanTimingValidationHelper helper = new ScanTimingValidationHelper();
351        private List<Scan> stopTimeScansInLoops = new ArrayList<Scan>();
352    
353        protected TimingValidation(AbstractValidator<SchedulingBlock> validationContainer,
354                                   ValidationPurpose                  reasonForValidation)
355        {
356          super(validationContainer, reasonForValidation);
357          
358          severity = 
359            (reasonForValidation == ValidationPurpose.CERTIFY_READY_TO_USE) ?
360              FailureSeverity.ERROR : FailureSeverity.WARNING;
361        }
362        
363        /** 
364         * Validations:
365         * 1) dynamically scheduled SB's have no stop-time scans
366         * 2) make sure a stop-time scan doesn't cut the previous scan short
367         * 3) stop-time scans are not allowed inside loops w/ iteration counts greater than 1.
368         */
369        @Override
370        protected boolean passesTest()
371        {
372          List<Scan> scans = target.getScanSequence().toScanList();
373          int n = scans.size();
374          for (int i = 0; i < n; i++)
375          {
376            Scan s = scans.get(i);
377            if (s.getTimeSpec().isStopTime())
378            {
379              for (int j = i + 1; j < n; j++)
380              {
381                if (s.equals(scans.get(j)))
382                {
383                  this.stopTimeScansInLoops.add(s);
384                }
385              }
386            }
387          }
388    
389          /*
390             The validation helper class will issue warnings that are meant to just
391             be warnings even if the ValidationPurpose is CERTIFY_READY_TO_USE.  So
392             we make sure we keep this test's overall severity at the warning level
393             unless we get a real error.
394          */
395          if (this.stopTimeScansInLoops.isEmpty())
396          {
397            this.helper.validateScanTiming(target);
398    
399            if (purpose == ValidationPurpose.CERTIFY_READY_TO_USE)
400            {
401              // default severity to WARNING unless one of these failures is higher than a warning.
402              severity = FailureSeverity.WARNING;
403              for (ValidationFailure f : this.helper.getValidationFailures())
404              {
405                FailureSeverity fsev = f.getSeverity();
406                if (FailureSeverity.ERROR == fsev || FailureSeverity.FATAL == fsev)
407                {
408                  severity = FailureSeverity.ERROR;
409                  break;
410                }
411              }
412            }
413          }
414    
415          return this.stopTimeScansInLoops.isEmpty() && this.helper.getValidationFailures().isEmpty();
416        }
417        
418        /* (non-Javadoc)
419         * @see edu.nrao.sss.validation.Validation#displayMessage()
420         */
421        @Override
422        protected String displayMessage()
423        {
424          StringBuilder buff = new StringBuilder();
425    
426          if (!this.stopTimeScansInLoops.isEmpty())
427          {
428            buff = new StringBuilder("The following Scans in Scheduling block '");
429            buff.append(target.getName()).append("' have a Stop Time specified but are inside a loop:  ");
430    
431            for (Scan s : this.stopTimeScansInLoops)
432            {
433              buff.append(s.getName());
434              buff.append(", ");
435            }
436    
437            // remove the last ", "
438            int len = buff.length();
439            buff.delete(len - 2, len);
440    
441            buff.append(".");
442          }
443    
444          // The above errors preclude these errors from even being checked for.
445          else if (!this.helper.getValidationFailures().isEmpty())
446          {
447            buff.append("Scheduling Block '");
448            buff.append(target.getName()).append("' may be invalid for the following reasons:  ");
449    
450            for (ValidationFailure f : this.helper.getValidationFailures())
451            {
452              buff.append(f.getDisplayMessage());
453              buff.append("  ");
454            }
455          }
456    
457          return buff.toString();
458        }
459    
460        /* (non-Javadoc)
461         * @see edu.nrao.sss.validation.Validation#debugMessage()
462         */
463        @Override
464        protected String debugMessage()
465        {
466          return displayMessage();
467        }
468      }
469    }