001    package edu.nrao.sss.validation;
002    
003    import java.util.ArrayList;
004    import java.util.HashMap;
005    import java.util.List;
006    import java.util.Map;
007    
008    /**
009     * Partial implementation of a {@link Validator}.
010     * <p>
011     * <b>Version Info:</b>
012     * <table style="margin-left:2em">
013     *   <tr><td>$Revision: 1511 $</td></tr>
014     *   <tr><td>$Date: 2008-08-19 11:43:17 -0600 (Tue, 19 Aug 2008) $</td></tr>
015     *   <tr><td>$Author: dharland $</td></tr>
016     * </table></p>
017     * 
018     * @author David M. Harland
019     * @since 2007-02-05
020     */
021    public abstract class AbstractValidator<T>
022      implements Validator
023    {
024      private String             name;
025      private Class<? extends T> expectedClass;
026      
027      private Map<ValidationPurpose, List<Validation<T>>> validationMap;
028    
029      /**
030       * The object most recently subject to validation by this validator.
031       * This variable is initially null.
032       */
033      protected T target;
034    
035      /**
036       * The purpose for which this validator was most recently run.
037       * This variable is initially ValidationPurpose.CERTIFY_READY_TO_USE.
038       */
039      protected ValidationPurpose purpose;
040      
041      /**
042       * List of all validation failures.
043       * This list is never null, but may be empty.
044       */
045      protected List<ValidationFailure> failures;
046      
047      /**
048       * The manager for which this validator is performing its duties.
049       * This variable is initially null.
050       */
051      protected ValidationManager manager;
052    
053      /**
054       * When true, validate() stops running tests after first failure.
055       * This variable is initially false.
056       */
057      protected boolean failFast;
058    
059      /** Helps create a new instance. */
060      protected AbstractValidator(String validatorName, Class<? extends T> targetClass)
061      {
062        //TODO null handling?
063        
064        name          = validatorName;
065        expectedClass = targetClass;
066        
067        target   = null;
068        purpose  = ValidationPurpose.CERTIFY_READY_TO_USE;
069        failFast = false;
070        failures = new ArrayList<ValidationFailure>();
071        manager  = null;
072    
073        validationMap = new HashMap<ValidationPurpose, List<Validation<T>>>(); 
074      }
075      
076      //============================================================================
077      // IMPLEMENTATION OF INTERFACE Validator
078      //============================================================================
079    
080      /* (non-Javadoc)
081       * @see edu.nrao.sss.validation.Validator#getName()
082       */
083      public String getName()  { return name; }
084      
085      /* (non-Javadoc)
086       * @see edu.nrao.sss.validation.Validator#getTarget()
087       */
088      public T getTarget()  { return target; }
089      
090      /* (non-Javadoc)
091       * @see edu.nrao.sss.validation.Validator#getPurpose()
092       */
093      public ValidationPurpose getPurpose()  { return purpose; }
094      
095      /* (non-Javadoc)
096       * @see edu.nrao.sss.validation.Validator#setManager(ValidationManager)
097       */
098      public void setManager(ValidationManager newManager)
099      {
100        manager = newManager;
101      }
102      
103      /**
104       * Tells this validator whether it should run all validations or stop
105       * after the first validation that fails.  The concrete implementations
106       * of this abstract are free to override this default implementation,
107       * whic is to run all validations.
108       * 
109       * @param stop if <i>true</i> this validator will stop running its
110       *             validations after the first one fails.
111       */
112      public void stopTestingAfterFirstFailure(boolean stop)
113      {
114        failFast = stop;
115      }
116    
117      
118      /* (non-Javadoc)
119       * @see Validator#validate(Object, ValidationPurpose)
120       */
121      @SuppressWarnings("unchecked")
122      public List<ValidationFailure> validate(Object            target,
123                                              ValidationPurpose purpose)
124      {
125        failures.clear();
126    
127        if (expectedClass.isInstance(target))
128        {
129          this.target  = (T)target;
130          this.purpose = purpose;
131          
132          validate();
133        }
134        else //target is wrong class
135        {
136          failures.add(ValidationFailure.wrongObjectType(name, target,
137                                                         expectedClass.getName()));
138        }
139    
140        return failures;
141      }
142      
143      //============================================================================
144      // 
145      //============================================================================
146      
147      /**
148       * Runs each of the individual validations.
149       * <p>
150       * Subclasses may use this default implementation or override it.
151       * Subclasses that contain collections of component objects will typically
152       * override this method, call {@code super.validate()}, and then invoke
153       * other validators on the elements of the collections.</p>
154       */
155      protected void validate()
156      {
157        if (failFast)
158          validateUntilFailure();
159        else
160          validateAll();
161      }
162      
163      /** Runs all validations. */
164      private void validateAll()
165      {
166        for (Validation<T> test : getValidations())
167          test.run(failures);
168      }
169      
170      /** Runs validations until one fails. */
171      private void validateUntilFailure()
172      {
173        for (Validation<T> test : getValidations())
174          if (!test.run(failures))
175            break;
176      }
177      
178      /**
179       * Returns the list of validations to be used for this validator's
180       * current purpose.
181       * 
182       * @return the list of validations for the current purpose.
183       */
184      private List<Validation<T>> getValidations()
185      {
186        List<Validation<T>> validations = validationMap.get(purpose);
187        
188        //Just-in-time creation
189        if (validations == null)
190        {
191          validations = makeValidationList(purpose);
192          validationMap.put(purpose, validations);
193        }
194        
195        return validations;
196      }
197      
198      //============================================================================
199      // OBLIGATIONS OF SUBCLASSES
200      //============================================================================
201    
202      /**
203       * Creates and returns a list of validations to be performed for the given
204       * {@code purpose}.
205       * 
206       * @param purpose the reason for which a validation is performed.
207       * 
208       * @return a list of validations to be used for {@code purpose}.
209       */
210      abstract
211        protected List<Validation<T>> makeValidationList(ValidationPurpose purpose);
212    }