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 3361 2008-07-16 19:35:39Z 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.label.Scalar;
31  import gov.nasa.pds.tools.logging.ToolsLogRecord;
32  import gov.nasa.pds.tools.util.Utility;
33  import gov.nasa.pds.tools.dict.type.InvalidLengthException;
34  import gov.nasa.pds.tools.dict.type.NumericTypeChecker;
35  import gov.nasa.pds.tools.dict.type.OutOfRangeException;
36  import gov.nasa.pds.tools.dict.type.TypeChecker;
37  import gov.nasa.pds.tools.dict.type.TypeCheckerFactory;
38  import gov.nasa.pds.tools.dict.type.UnsupportedTypeException;
39  import gov.nasa.pds.tools.dict.type.InvalidTypeException;
40  import gov.nasa.pds.tools.dict.DictionaryTokens;
41  
42  /***
43   * This class will validate an element value or set of values against 
44   * an ElementDefinition.
45   * @author pramirez
46   * @version $Revision: 3361 $
47   * 
48   */
49  public class ElementValidator implements DictionaryTokens {
50      private static Logger log = Logger.getLogger(ElementValidator.class.getName());
51      
52      public static boolean isValid(ElementDefinition definition, AttributeStatement attribute) 
53              throws UnsupportedTypeException {
54          return isValid(definition, attribute, new DefaultValidationListener());
55      }
56      
57      public static boolean isValid(ElementDefinition definition, AttributeStatement attribute, ValidationListener listener) {
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;
83  		try {
84  			checker = TypeCheckerFactory.getInstance().newInstance(definition.getDataType());
85  			//Validate the value don't want to set to true if has already been set to false
86  	        if (!validate(definition, attribute, checker, value, listener))
87  	            valid = false;
88  		} catch (UnsupportedTypeException ute) {
89  			listener.reportError(ute.getMessage());
90  			log.log(new ToolsLogRecord(Level.SEVERE, ute.getMessage(), attribute.getFilename(), attribute.getContext(), attribute.getLineNumber()));
91  			valid = false;
92  		}
93          
94          return valid;
95      }
96      
97      private static boolean validate(ElementDefinition definition, AttributeStatement attribute, TypeChecker checker, Value value, ValidationListener listener) {
98          boolean valid = true;
99          if (value == null) {
100             listener.reportWarning("Found no value for " + attribute.getIdentifier());
101             log.log(new ToolsLogRecord(Level.WARNING, "Found no value for " + attribute.getIdentifier(), 
102                     attribute.getFilename(), attribute.getContext(), attribute.getLineNumber()));
103         } else if (value instanceof Set || value instanceof Sequence) {
104             boolean validValues = true;
105             for (Iterator i = ((Collection) value).iterator(); i.hasNext();) {
106                 Value v = (Value) i.next();
107                 validValues = validate(definition, attribute, checker, v, listener);
108                 //Don't want to set to true if has already been set to false
109                 if (!validValues)
110                     valid = false;
111             }
112         } else {
113             if (!skipValue(value.toString())) {
114             	 
115                 //Check to see if type defined in dictionary matches that found by the parser
116                 Scalar scalar = (Scalar) value;
117                 if (!scalar.isSupportedPDSType(definition.getDataType())) {
118                 	valid = false;
119                 	listener.reportError("Type Mismatch: " + attribute.getIdentifier() + " defined as " + definition.getDataType() + " found " + 
120                 			scalar.getClass().getName().substring(scalar.getClass().getName().lastIndexOf(".") + 1));
121                 	log.log(new ToolsLogRecord(Level.SEVERE, "Type Mismatch: " + attribute.getIdentifier() + " defined as " + definition.getDataType() + " found " + 
122                 			scalar.getClass().getName().substring(scalar.getClass().getName().lastIndexOf(".") + 1), attribute.getFilename(), attribute.getContext(), attribute.getLineNumber()));
123                 } else {
124                 
125 		            //Check against valid values if there are any
126 		            if (definition.hasValidValues()) {
127 		                if (!definition.getValues().contains(value.toString())) {
128 		                    boolean foundValue = false;
129 		                    boolean manipulated = false;
130 		                    
131 		                    //Perform whitespace stripping
132 		                    String filteredValue = Utility.stripNewLines(value.toString());
133 		                    
134 		                    if (definition.getValues().contains(filteredValue)) {
135 		                        foundValue = true;
136 		                    } else if (definition.getValues().contains(filteredValue.toUpperCase())) {
137 		                        //Matches if value match can be made by simply switching case.
138 		                        manipulated = true;
139 		                        foundValue = true;
140 		                    } else {
141 		                        //Continue with whitespace striping
142 		                        filteredValue = Utility.filterString(filteredValue.toUpperCase());
143 		                        manipulated = true;
144 		                        if (definition.getValues().contains(filteredValue))
145 		                            foundValue = true;
146 		                    }
147 		                   
148 		                    if (!foundValue) {
149 		                        //Only produce a warning if the standard value list is anything other than static
150 		                        if (!VALUE_TYPE_STATIC.equals(definition.getValueType())) {
151 		                            listener.reportWarning(value.toString() + 
152 		                                    " is not in the suggested list of valid values for " + attribute.getIdentifier());
153 		                            log.log(new ToolsLogRecord(Level.WARNING, value.toString() + 
154 		                                    " is not in the suggested list of valid values for " + attribute.getIdentifier(), 
155 		                                    attribute.getFilename(), attribute.getContext(), attribute.getLineNumber()));
156 		                        } else {
157 		                            valid = false;
158 		                            listener.reportError(value.toString() + 
159 		                                     " is not in the list of valid values for " + attribute.getIdentifier());
160 		                            log.log(new ToolsLogRecord(Level.SEVERE, value.toString() + 
161 		                                     " is not in the list of valid values for " + attribute.getIdentifier(),
162 		                                     attribute.getFilename(), attribute.getContext(), attribute.getLineNumber()));
163 		                        }
164 		                    } else if (manipulated) {
165 		                        //Value had to be manipulated to make a match
166 		                        log.log(new ToolsLogRecord(Level.INFO, "Element value was manipulated for " + attribute.getIdentifier() +
167 		                                   " in order to match value list.", attribute.getFilename(), attribute.getContext(), attribute.getLineNumber()));
168 		                    }
169 		                }
170 		            }
171 		            
172 		            Object castedValue = null;
173 		            //Try to cast to an instance of the type
174 		            try {
175 		                castedValue = checker.cast(value.toString());
176 		            } catch (InvalidTypeException ite) {
177 		                valid = false;
178 		                listener.reportError(ite.getMessage());
179 		                log.log(new ToolsLogRecord(Level.SEVERE, ite.getMessage(), attribute.getFilename(), attribute.getContext(), attribute.getLineNumber()));
180 		            }
181 		            
182 		            //Check min length
183 		            try {
184 		                checker.checkMinLength(value.toString(), definition.getMinLength());
185 		            } catch (InvalidLengthException ile) {
186 		                valid = false;
187 		                listener.reportError(ile.getMessage());
188 		                log.log(new ToolsLogRecord(Level.SEVERE, ile.getMessage(), attribute.getFilename(), attribute.getContext(), attribute.getLineNumber()));
189 		            }
190 		            
191 		            //Check max length
192 		            try {
193 		                checker.checkMaxLength(value.toString(), definition.getMaxLength());
194 		            } catch (InvalidLengthException ile) {
195 		                valid = false;
196 		                listener.reportError(ile.getMessage());
197 		                log.log(new ToolsLogRecord(Level.SEVERE, ile.getMessage(), attribute.getFilename(), attribute.getContext(), attribute.getLineNumber()));
198 		            }
199 		            
200 		            //Check to see if this is a numeric type checker if so then do further checking
201 		            if (checker instanceof NumericTypeChecker && castedValue instanceof Number && castedValue != null) {
202 		                NumericTypeChecker numericChecker = (NumericTypeChecker) checker;
203 		                
204 		                //Check min value
205 		                if (definition.hasMinimum()) {
206 		                    try {
207 		                        numericChecker.checkMinValue((Number) castedValue, definition.getMinimum());
208 		                    } catch (OutOfRangeException oor) {
209 		                        valid = false;
210 		                        listener.reportError(oor.getMessage());
211 		                        log.log(new ToolsLogRecord(Level.SEVERE, oor.getMessage(), attribute.getFilename(), attribute.getContext(), attribute.getLineNumber()));
212 		                    }
213 		                }
214 		                
215 		                //Check max value
216 		                if (definition.hasMaximum()) {
217 		                    try {
218 		                        numericChecker.checkMaxValue((Number) castedValue, definition.getMaximum());
219 		                    } catch (OutOfRangeException oor) {
220 		                        valid = false;
221 		                        listener.reportError(oor.getMessage());
222 		                        log.log(new ToolsLogRecord(Level.SEVERE, oor.getMessage(), attribute.getFilename(), attribute.getContext(), attribute.getLineNumber()));
223 		                    }
224 		                }
225 		                
226 		                //Check units if found and definition required.
227 		                //Look at definition to see if we should check units
228 		                if (value instanceof Numeric) {
229 		                    Numeric number = (Numeric) value;
230 		                    if (number.getUnits() != null && !definition.isUnitAllowed(number.getUnits())) {
231 		                        boolean unitsValid = false;
232 		                        if (number.getUnits().endsWith("s") && 
233 		                                definition.isUnitAllowed(number.getUnits().substring(0, number.getUnits().length() - 1))) {
234 		                            unitsValid = true;
235 		                        }
236 		                        if (!unitsValid) {
237 		                            listener.reportWarning("Units (" + number.getUnits() + ") do not match " +
238 		                                    " those found in the dictionary.");
239 		                            log.log(new ToolsLogRecord(Level.WARNING, "Units (" + number.getUnits() + ") do not match " +
240 		                                    " those found in the dictionary.", attribute.getFilename(), attribute.getContext(), 
241 		                                    attribute.getLineNumber()));
242 		                        }
243 		                    }
244 		                }
245 		            }
246                 }
247             }
248         }
249         
250         return valid;
251     }
252     
253     private static boolean skipValue(String value) {
254         if ("N/A".equals(value) || "NULL".equals(value) || "UNK".equals(value))
255             return true;
256         return false;
257     }
258     
259     /***
260      * Checks to see whether an {@link AttributeStatement} is correct. Will look up the definition in
261      * the given dictionary. An object context may be supplied as elements can have aliases that are 
262      * appropriate within an object. Set objectContext to null if there if the lookup should be performed
263      * without care to the surrounding object.
264      * @param dictionary where to look up the element
265      * @param objectContext enclosing the element to be looked up
266      * @param attribute statement to be validated
267      * @return flag indicating whether or not the statement was valid against the definition found
268      * @throws DefinitionNotFoundException if definition for element is not found
269      * @throws UnsupportedTypeException if type of element is not supported
270      */
271     public static boolean isValid(Dictionary dictionary, String objectContext, AttributeStatement attribute) {
272         return isValid(dictionary, objectContext, attribute, new DefaultValidationListener());
273     }
274 
275     
276     public static boolean isValid(Dictionary dictionary, String objectContext, AttributeStatement attribute, ValidationListener listener) {
277         ElementDefinition definition = dictionary.getElementDefinition(objectContext, attribute.getIdentifier()); 
278         
279         if (definition == null)
280         {
281         	listener.reportError("Undefined Element: " + attribute.getIdentifier());
282         	log.log(new ToolsLogRecord(Level.SEVERE, "Undefined Element: " + attribute.getIdentifier(), attribute.getFilename(), attribute.getContext(), attribute.getLineNumber()));
283             return false;
284         }
285    
286         return isValid(definition, attribute, listener);
287     }
288     
289     public static boolean isValid(Dictionary dictionary, AttributeStatement attribute) {
290         return isValid(dictionary, attribute, new DefaultValidationListener());
291     }
292     
293     public static boolean isValid(Dictionary dictionary, AttributeStatement attribute, ValidationListener listener) {
294         return isValid(dictionary, null, attribute, listener);
295     }
296 
297 }