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 2930 2007-09-27 19:47:56Z pramirez $ 
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: 2930 $
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          return isValid(definition, attribute, new DefaultValidationListener());
54      }
55      
56      public static boolean isValid(ElementDefinition definition, AttributeStatement attribute, ValidationListener listener) 
57              throws UnsupportedTypeException {
58          boolean valid = true;
59          Value value = attribute.getValue();
60          
61          //Check length of namespace
62          if (attribute.hasNamespace()) {
63              if (attribute.getNamespace().length() > NAMESPACE_LENGTH) {
64                  valid = false;
65                  listener.reportError("Namespace exceeds max length of " + 
66                      ELEMENT_IDENT_LENGTH + " characters.");
67                  log.log(new ToolsLogRecord(Level.SEVERE, "Namespace exceeds max length of " + 
68                      ELEMENT_IDENT_LENGTH + " characters.", attribute.getFilename(), attribute.getContext(), attribute.getLineNumber()));
69              }
70          }
71          
72          //Check length of identifier
73          if (attribute.getElementIdentifier().length() > ELEMENT_IDENT_LENGTH) {
74              valid = false;
75              listener.reportError("Identifier exceeds max length of " + 
76                      ELEMENT_IDENT_LENGTH + " characters.");
77              log.log(new ToolsLogRecord(Level.SEVERE, "Identifier exceeds max length of " + 
78                      ELEMENT_IDENT_LENGTH + " characters.", attribute.getFilename(), attribute.getContext(), attribute.getLineNumber()));
79          }
80  
81          //Load the type checker
82          TypeChecker checker = TypeCheckerFactory.getInstance().newInstance(definition.getDataType());
83          //Validate the value
84          boolean valueValid = validate(definition, attribute, checker, value, listener);
85          //Don't want to set to true if has already been set to false
86          if (!valueValid)
87              valid = false;
88          
89          return valid;
90      }
91      
92      private static boolean validate(ElementDefinition definition, AttributeStatement attribute, TypeChecker checker, Value value, ValidationListener listener) 
93              throws UnsupportedTypeException {
94          boolean valid = true;
95          if (value == null) {
96              listener.reportWarning("Found no value for " + attribute.getIdentifier());
97              log.log(new ToolsLogRecord(Level.WARNING, "Found no value for " + attribute.getIdentifier(), 
98                      attribute.getFilename(), attribute.getContext(), attribute.getLineNumber()));
99          } else if (value instanceof Set || value instanceof Sequence) {
100             boolean validValues = true;
101             for (Iterator i = ((Collection) value).iterator(); i.hasNext();) {
102                 Value v = (Value) i.next();
103                 validValues = validate(definition, attribute, checker, v, listener);
104                 //Don't want to set to true if has already been set to false
105                 if (!validValues)
106                     valid = false;
107             }
108         } else {
109             if (!skipValue(value.toString())) {
110                 //Check against valid values if there are any
111                 if (definition.hasValidValues()) {
112                     if (!definition.getValues().contains(value.toString())) {
113                         boolean foundValue = false;
114                         boolean manipulated = false;
115                         
116                         //Perform whitespace stripping
117                         String filteredValue = Utility.stripNewLines(value.toString());
118                         
119                         if (definition.getValues().contains(filteredValue)) {
120                             foundValue = true;
121                         } else if (definition.getValues().contains(filteredValue.toUpperCase())) {
122                             //Matches if value match can be made by simply switching case.
123                             manipulated = true;
124                             foundValue = true;
125                         } else {
126                             //Continue with whitespace striping
127                             filteredValue = Utility.filterString(filteredValue.toUpperCase());
128                             manipulated = true;
129                             if (definition.getValues().contains(filteredValue))
130                                 foundValue = true;
131                         }
132                        
133                         if (!foundValue) {
134                             //Only produce a warning if the standard value list is anything other than static
135                             if (!VALUE_TYPE_STATIC.equals(definition.getValueType())) {
136                                 listener.reportWarning(value.toString() + 
137                                         " is not in the suggested list of valid values for " + attribute.getIdentifier());
138                                 log.log(new ToolsLogRecord(Level.WARNING, value.toString() + 
139                                         " is not in the suggested list of valid values for " + attribute.getIdentifier(), 
140                                         attribute.getFilename(), attribute.getContext(), attribute.getLineNumber()));
141                             } else {
142                                 valid = false;
143                                 listener.reportError(value.toString() + 
144                                          " is not in the list of valid values for " + attribute.getIdentifier());
145                                 log.log(new ToolsLogRecord(Level.SEVERE, value.toString() + 
146                                          " is not in the list of valid values for " + attribute.getIdentifier(),
147                                          attribute.getFilename(), attribute.getContext(), attribute.getLineNumber()));
148                             }
149                         } else if (manipulated) {
150                             //Value had to be manipulated to make a match
151                             log.log(new ToolsLogRecord(Level.INFO, "Element value was manipulated for " + attribute.getIdentifier() +
152                                        " in order to match value list.", attribute.getFilename(), attribute.getContext(), attribute.getLineNumber()));
153                         }
154                     }
155                 }
156                 
157                 Object castedValue = null;
158                 //Try to cast to an instance of the type
159                 try {
160                     castedValue = checker.cast(value.toString());
161                 } catch (InvalidTypeException ite) {
162                     valid = false;
163                     listener.reportError(ite.getMessage());
164                     log.log(new ToolsLogRecord(Level.SEVERE, ite.getMessage(), attribute.getFilename(), attribute.getContext(), attribute.getLineNumber()));
165                 }
166                 
167                 //Check min length
168                 try {
169                     checker.checkMinLength(value.toString(), definition.getMinLength());
170                 } catch (InvalidLengthException ile) {
171                     valid = false;
172                     listener.reportError(ile.getMessage());
173                     log.log(new ToolsLogRecord(Level.SEVERE, ile.getMessage(), attribute.getFilename(), attribute.getContext(), attribute.getLineNumber()));
174                 }
175                 
176                 //Check max length
177                 try {
178                     checker.checkMaxLength(value.toString(), definition.getMaxLength());
179                 } catch (InvalidLengthException ile) {
180                     valid = false;
181                     listener.reportError(ile.getMessage());
182                     log.log(new ToolsLogRecord(Level.SEVERE, ile.getMessage(), attribute.getFilename(), attribute.getContext(), attribute.getLineNumber()));
183                 }
184                 
185                 //Check to see if this is a numeric type checker if so then do further checking
186                 if (checker instanceof NumericTypeChecker && castedValue instanceof Number && castedValue != null) {
187                     NumericTypeChecker numericChecker = (NumericTypeChecker) checker;
188                     
189                     //Check min value
190                     if (definition.hasMinimum()) {
191                         try {
192                             numericChecker.checkMinValue((Number) castedValue, definition.getMinimum());
193                         } catch (OutOfRangeException oor) {
194                             valid = false;
195                             listener.reportError(oor.getMessage());
196                             log.log(new ToolsLogRecord(Level.SEVERE, oor.getMessage(), attribute.getFilename(), attribute.getContext(), attribute.getLineNumber()));
197                         }
198                     }
199                     
200                     //Check max value
201                     if (definition.hasMaximum()) {
202                         try {
203                             numericChecker.checkMaxValue((Number) castedValue, definition.getMaximum());
204                         } catch (OutOfRangeException oor) {
205                             valid = false;
206                             listener.reportError(oor.getMessage());
207                             log.log(new ToolsLogRecord(Level.SEVERE, oor.getMessage(), attribute.getFilename(), attribute.getContext(), attribute.getLineNumber()));
208                         }
209                     }
210                     
211                     //Check units if found and definition required.
212                     //Look at definition to see if we should check units
213                     if (value instanceof Numeric) {
214                         Numeric number = (Numeric) value;
215                         if (number.getUnits() != null && !definition.isUnitAllowed(number.getUnits())) {
216                             boolean unitsValid = false;
217                             if (number.getUnits().endsWith("s") && 
218                                     definition.isUnitAllowed(number.getUnits().substring(0, number.getUnits().length() - 1))) {
219                                 unitsValid = true;
220                             }
221                             if (!unitsValid) {
222                                 listener.reportWarning("Units (" + number.getUnits() + ") do not match " +
223                                         " those found in the dictionary.");
224                                 log.log(new ToolsLogRecord(Level.WARNING, "Units (" + number.getUnits() + ") do not match " +
225                                         " those found in the dictionary.", attribute.getFilename(), attribute.getContext(), 
226                                         attribute.getLineNumber()));
227                             }
228                         }
229                     }
230                 }
231             }
232         }
233         
234         return valid;
235     }
236     
237     private static boolean skipValue(String value) {
238         if ("N/A".equals(value) || "NULL".equals(value) || "UNK".equals(value))
239             return true;
240         return false;
241     }
242     
243     /***
244      * Checks to see whether an {@link AttributeStatement} is correct. Will look up the definition in
245      * the given dictionary. An object context may be supplied as elements can have aliases that are 
246      * appropriate within an object. Set objectContext to null if there if the lookup should be performed
247      * without care to the surrounding object.
248      * @param dictionary where to look up the element
249      * @param objectContext enclosing the element to be looked up
250      * @param attribute statement to be validated
251      * @return flag indicting whether or not the statement was valid against the definition found
252      * @throws DefinitionNotFoundException if definition for element is not found
253      * @throws UnsupportedTypeException if type of element is not supported
254      */
255     public static boolean isValid(Dictionary dictionary, String objectContext, AttributeStatement attribute) 
256             throws DefinitionNotFoundException, UnsupportedTypeException {
257         return isValid(dictionary, objectContext, attribute, new DefaultValidationListener());
258     }
259 
260     
261     public static boolean isValid(Dictionary dictionary, String objectContext, AttributeStatement attribute, ValidationListener listener) 
262             throws DefinitionNotFoundException, UnsupportedTypeException {
263         ElementDefinition definition = dictionary.getElementDefinition(objectContext, attribute.getIdentifier()); 
264         
265         if (definition == null)
266             throw new DefinitionNotFoundException("Undefined Element: " + attribute.getIdentifier());
267    
268         return isValid(definition, attribute, listener);
269     }
270     
271     public static boolean isValid(Dictionary dictionary, AttributeStatement attribute) 
272             throws DefinitionNotFoundException, UnsupportedTypeException {
273         return isValid(dictionary, attribute, new DefaultValidationListener());
274     }
275     
276     public static boolean isValid(Dictionary dictionary, AttributeStatement attribute, ValidationListener listener) 
277             throws DefinitionNotFoundException, UnsupportedTypeException {
278         return isValid(dictionary, null, attribute, listener);
279     }
280 
281 }