1
2
3
4
5
6
7
8
9
10
11
12
13
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
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
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
82 TypeChecker checker;
83 try {
84 checker = TypeCheckerFactory.getInstance().newInstance(definition.getDataType());
85
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
109 if (!validValues)
110 valid = false;
111 }
112 } else {
113 if (!skipValue(value.toString())) {
114
115
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
126 if (definition.hasValidValues()) {
127 if (!definition.getValues().contains(value.toString())) {
128 boolean foundValue = false;
129 boolean manipulated = false;
130
131
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
138 manipulated = true;
139 foundValue = true;
140 } else {
141
142 filteredValue = Utility.filterString(filteredValue.toUpperCase());
143 manipulated = true;
144 if (definition.getValues().contains(filteredValue))
145 foundValue = true;
146 }
147
148 if (!foundValue) {
149
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
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
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
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
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
201 if (checker instanceof NumericTypeChecker && castedValue instanceof Number && castedValue != null) {
202 NumericTypeChecker numericChecker = (NumericTypeChecker) checker;
203
204
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
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
227
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 }