View Javadoc

1   // Copyright 2006-2007, by the California Institute of Technology.
2   // ALL RIGHTS RESERVED. United States Government Sponsorship acknowledged.
3   // Any commercial use must be negotiated with the Office of Technology Transfer
4   // at the California Institute of Technology.
5   //
6   // This software is subject to U. S. export control laws and regulations
7   // (22 C.F.R. 120-130 and 15 C.F.R. 730-774). To the extent that the software
8   // is subject to U.S. export control laws and regulations, the recipient has
9   // the responsibility to obtain export licenses or other export authority as
10  // may be required before exporting such information to foreign countries or
11  // providing access to foreign nationals.
12  //
13  // $Id: ElementValidator.java 2631 2007-04-26 16:17:05Z mcayanan $ 
14  //
15  
16  package gov.nasa.pds.tools.label.validate;
17  
18  import java.util.Collection;
19  import java.util.Iterator;
20  import java.util.logging.Level;
21  import java.util.logging.Logger;
22  
23  import gov.nasa.pds.tools.dict.Dictionary;
24  import gov.nasa.pds.tools.dict.ElementDefinition;
25  import gov.nasa.pds.tools.label.AttributeStatement;
26  import gov.nasa.pds.tools.label.Numeric;
27  import gov.nasa.pds.tools.label.Sequence;
28  import gov.nasa.pds.tools.label.Set;
29  import gov.nasa.pds.tools.label.Value;
30  import gov.nasa.pds.tools.logging.ToolsLogRecord;
31  import gov.nasa.pds.tools.util.Utility;
32  import gov.nasa.pds.tools.dict.type.InvalidLengthException;
33  import gov.nasa.pds.tools.dict.type.NumericTypeChecker;
34  import gov.nasa.pds.tools.dict.type.OutOfRangeException;
35  import gov.nasa.pds.tools.dict.type.TypeChecker;
36  import gov.nasa.pds.tools.dict.type.TypeCheckerFactory;
37  import gov.nasa.pds.tools.dict.type.UnsupportedTypeException;
38  import gov.nasa.pds.tools.dict.type.InvalidTypeException;
39  import gov.nasa.pds.tools.dict.DictionaryTokens;
40  
41  /***
42   * This class will validate an element value or set of values against 
43   * an ElementDefinition.
44   * @author pramirez
45   * @version $Revision: 2631 $
46   * 
47   */
48  public class ElementValidator implements DictionaryTokens {
49      private static Logger log = Logger.getLogger(ElementValidator.class.getName());
50      
51      public static boolean isValid(ElementDefinition definition, AttributeStatement attribute) 
52              throws UnsupportedTypeException {
53          boolean valid = true;
54          Value value = attribute.getValue();
55          
56          //Check length of namespace
57          if (attribute.hasNamespace()) {
58              if (attribute.getNamespace().length() > NAMESPACE_LENGTH) {
59                  valid = false;
60                  log.log(new ToolsLogRecord(Level.SEVERE, "Namespace exceeds max length of " + 
61                      ELEMENT_IDENT_LENGTH + " characters.", attribute.getFilename(), attribute.getContext(), attribute.getLineNumber()));
62              }
63          }
64          
65          //Check length of identifier
66          if (attribute.getElementIdentifier().length() > ELEMENT_IDENT_LENGTH) {
67              valid = false;
68              log.log(new ToolsLogRecord(Level.SEVERE, "Identifier exceeds max length of " + 
69                      ELEMENT_IDENT_LENGTH + " characters.", attribute.getFilename(), attribute.getContext(), attribute.getLineNumber()));
70          }
71  
72          //Load the type checker
73          TypeChecker checker = TypeCheckerFactory.getInstance().newInstance(definition.getDataType());
74          //Validate the value
75          boolean valueValid = validate(definition, attribute, checker, value);
76          //Don't want to set to true if has already been set to false
77          if (!valueValid)
78              valid = false;
79          
80          return valid;
81      }
82      
83      private static boolean validate(ElementDefinition definition, AttributeStatement attribute, TypeChecker checker, Value value) 
84              throws UnsupportedTypeException {
85          boolean valid = true;
86          if (value == null) {
87              log.log(new ToolsLogRecord(Level.WARNING, "Found no value for " + attribute.getIdentifier(), 
88                      attribute.getFilename(), attribute.getContext(), attribute.getLineNumber()));
89          } else if (value instanceof Set || value instanceof Sequence) {
90              boolean validValues = true;
91              for (Iterator i = ((Collection) value).iterator(); i.hasNext();) {
92                  Value v = (Value) i.next();
93                  validValues = validate(definition, attribute, checker, v);
94                  //Don't want to set to true if has already been set to false
95                  if (!validValues)
96                      valid = false;
97              }
98          } else {
99              if (!skipValue(value.toString())) {
100                 //Check against valid values if there are any
101                 if (definition.hasValidValues()) {
102                     if (!definition.getValues().contains(value.toString())) {
103                         boolean foundValue = false;
104                         boolean manipulated = false;
105                         
106                         //Perform whitespace stripping
107                         String filteredValue = Utility.stripNewLines(value.toString());
108                         
109                         if (definition.getValues().contains(filteredValue)) {
110                             foundValue = true;
111                         } else if (definition.getValues().contains(filteredValue.toUpperCase())) {
112                             //Matches if value match can be made by simply switching case.
113                             manipulated = true;
114                             foundValue = true;
115                         } else {
116                             //Continue with whitespace striping
117                             filteredValue = Utility.filterString(filteredValue.toUpperCase());
118                             manipulated = true;
119                             if (definition.getValues().contains(filteredValue))
120                                 foundValue = true;
121                         }
122                        
123                         if (!foundValue) {
124                             //Only produce a warning if the standard value list is anything other than static
125                             if (!VALUE_TYPE_STATIC.equals(definition.getValueType())) {
126                                 log.log(new ToolsLogRecord(Level.WARNING, value.toString() + 
127                                         " is not in the suggested list of valid values for " + attribute.getIdentifier(), 
128                                         attribute.getFilename(), attribute.getContext(), attribute.getLineNumber()));
129                             } else {
130                                 valid = false;
131                                 log.log(new ToolsLogRecord(Level.SEVERE, value.toString() + 
132                                          " is not in the list of valid values for " + attribute.getIdentifier(),
133                                          attribute.getFilename(), attribute.getContext(), attribute.getLineNumber()));
134                             }
135                         } else if (manipulated) {
136                             //Value had to be manipulated to make a match
137                             log.log(new ToolsLogRecord(Level.INFO, "Element value was manipulated for " + attribute.getIdentifier() +
138                                        " in order to match value list.", attribute.getFilename(), attribute.getContext(), attribute.getLineNumber()));
139                         }
140                     }
141                 }
142                 
143                 Object castedValue = null;
144                 //Try to cast to an instance of the type
145                 try {
146                     castedValue = checker.cast(value.toString());
147                 } catch (InvalidTypeException ite) {
148                     valid = false;
149                     log.log(new ToolsLogRecord(Level.SEVERE, ite.getMessage(), attribute.getFilename(), attribute.getContext(), attribute.getLineNumber()));
150                 }
151                 
152                 //Check min length
153                 try {
154                     checker.checkMinLength(value.toString(), definition.getMinLength());
155                 } catch (InvalidLengthException ile) {
156                     valid = false;
157                     log.log(new ToolsLogRecord(Level.SEVERE, ile.getMessage(), attribute.getFilename(), attribute.getContext(), attribute.getLineNumber()));
158                 }
159                 
160                 //Check max length
161                 try {
162                     checker.checkMaxLength(value.toString(), definition.getMaxLength());
163                 } catch (InvalidLengthException ile) {
164                     valid = false;
165                     log.log(new ToolsLogRecord(Level.SEVERE, ile.getMessage(), attribute.getFilename(), attribute.getContext(), attribute.getLineNumber()));
166                 }
167                 
168                 //Check to see if this is a numeric type checker if so then do further checking
169                 if (checker instanceof NumericTypeChecker && castedValue instanceof Number && castedValue != null) {
170                     NumericTypeChecker numericChecker = (NumericTypeChecker) checker;
171                     
172                     //Check min value
173                     if (definition.hasMinimum()) {
174                         try {
175                             numericChecker.checkMinValue((Number) castedValue, definition.getMinimum());
176                         } catch (OutOfRangeException oor) {
177                             valid = false;
178                             log.log(new ToolsLogRecord(Level.SEVERE, oor.getMessage(), attribute.getFilename(), attribute.getContext(), attribute.getLineNumber()));
179                         }
180                     }
181                     
182                     //Check max value
183                     if (definition.hasMaximum()) {
184                         try {
185                             numericChecker.checkMaxValue((Number) castedValue, definition.getMaximum());
186                         } catch (OutOfRangeException oor) {
187                             valid = false;
188                             log.log(new ToolsLogRecord(Level.SEVERE, oor.getMessage(), attribute.getFilename(), attribute.getContext(), attribute.getLineNumber()));
189                         }
190                     }
191                     
192                     //Check units if found and definition required.
193                     //Look at definition to see if we should check units
194                     if (value instanceof Numeric) {
195                         Numeric number = (Numeric) value;
196                         if (number.getUnits() != null && !definition.isUnitAllowed(number.getUnits())) {
197                             boolean unitsValid = false;
198                             if (number.getUnits().endsWith("s") && 
199                                     definition.isUnitAllowed(number.getUnits().substring(0, number.getUnits().length() - 1))) {
200                                 unitsValid = true;
201                             }
202                             if (!unitsValid) {
203                                 valid = false;
204                                 log.log(new ToolsLogRecord(Level.SEVERE, "Units (" + number.getUnits() + ") do not match " +
205                                         "those found in the dictionary.", attribute.getFilename(), attribute.getContext(), 
206                                         attribute.getLineNumber()));
207                             }
208                         }
209                     }
210                 }
211             }
212         }
213         
214         return valid;
215     }
216     
217     private static boolean skipValue(String value) {
218         if ("N/A".equals(value) || "NULL".equals(value) || "UNK".equals(value))
219             return true;
220         return false;
221     }
222     
223     /***
224      * Checks to see whether an {@link AttributeStatement} is correct. Will look up the definition in
225      * the given dictionary. An object context may be supplied as elements can have aliases that are 
226      * appropriate within an object. Set objectContext to null if there if the lookup should be performed
227      * without care to the surrounding object.
228      * @param dictionary where to look up the element
229      * @param objectContext enclosing the element to be looked up
230      * @param attribute statement to be validated
231      * @return flag indicting whether or not the statement was valid against the definition found
232      * @throws DefinitionNotFoundException if definition for element is not found
233      * @throws UnsupportedTypeException if type of element is not supported
234      */
235     public static boolean isValid(Dictionary dictionary, String objectContext, AttributeStatement attribute) 
236             throws DefinitionNotFoundException, UnsupportedTypeException {
237         ElementDefinition definition = dictionary.getElementDefinition(objectContext, attribute.getIdentifier()); 
238         
239         if (definition == null)
240             throw new DefinitionNotFoundException("Undefined Element: " + attribute.getIdentifier());
241    
242         return isValid(definition, attribute);
243     }
244     
245     public static boolean isValid(Dictionary dictionary, AttributeStatement attribute) 
246             throws DefinitionNotFoundException, UnsupportedTypeException {
247         return isValid(dictionary, null, attribute);
248     }
249 
250 }