Java tutorial
/** * Copyright 2010 The Kuali Foundation Licensed under the Educational Community License, Version 2.0 (the "License"); you may * not use this file except in compliance with the License. You may obtain a copy of the License at * http://www.osedu.org/licenses/ECL-2.0 Unless required by applicable law or agreed to in writing, software distributed * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. See the License for the specific language governing permissions and limitations under the License. */ package org.kuali.student.r2.common.validator; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.Collection; import java.util.Date; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Stack; import org.apache.commons.beanutils.PropertyUtils; import org.kuali.student.common.util.MessageUtils; import org.kuali.student.r1.common.dictionary.dto.CaseConstraint; import org.kuali.student.r1.common.dictionary.dto.CommonLookupParam; import org.kuali.student.r1.common.dictionary.dto.Constraint; import org.kuali.student.r1.common.dictionary.dto.DataType; import org.kuali.student.r1.common.dictionary.dto.FieldDefinition; import org.kuali.student.r1.common.dictionary.dto.LookupConstraint; import org.kuali.student.r1.common.dictionary.dto.MustOccurConstraint; import org.kuali.student.r1.common.dictionary.dto.ObjectStructureDefinition; import org.kuali.student.r1.common.dictionary.dto.RequiredConstraint; import org.kuali.student.r1.common.dictionary.dto.ValidCharsConstraint; import org.kuali.student.r1.common.dictionary.dto.WhenConstraint; import org.kuali.student.r2.core.search.dto.SearchParamInfo; import org.kuali.student.r2.core.search.dto.SearchRequestInfo; import org.kuali.student.r2.core.search.dto.SearchResultInfo; import org.kuali.student.r2.core.search.service.SearchService; import org.kuali.student.r1.common.validator.BeanConstraintDataProvider; import org.kuali.student.r1.common.validator.ConstraintDataProvider; import org.kuali.student.r1.common.validator.DateParser; import org.kuali.student.r1.common.validator.ServerDateParser; import org.kuali.student.r1.common.validator.ValidatorUtils; import org.kuali.student.r2.common.dto.AttributeInfo; import org.kuali.student.r2.common.dto.ContextInfo; import org.kuali.student.r2.common.dto.LocaleInfo; import org.kuali.student.r2.common.dto.ValidationResultInfo; import org.kuali.student.r2.common.exceptions.DoesNotExistException; import org.kuali.student.r2.common.exceptions.InvalidParameterException; import org.kuali.student.r2.common.exceptions.MissingParameterException; import org.kuali.student.r2.common.exceptions.OperationFailedException; import org.kuali.student.r2.common.exceptions.PermissionDeniedException; import org.kuali.student.r2.common.infc.ValidationResult.ErrorLevel; import org.kuali.student.r2.common.messages.dto.MessageInfo; import org.kuali.student.r2.common.messages.service.MessageService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.BeanUtils; // This class is a special case, this class/equivelent doesn't exist in R2 // packages and is a common and has methods used in both R1 and R2 packages, // this class was duplicated to R2 and modified to work with R2 services // BaseAbstractValidator, BaseAbstractValidator, Validator, ValidatorFactory public class DefaultValidatorImpl extends BaseAbstractValidator { private static final Logger LOG = LoggerFactory.getLogger(DefaultValidatorImpl.class); private MessageService messageService = null; private SearchService searchDispatcher; private String messageLocaleKey = "en"; private String messageGroupKey = "validation"; private DateParser dateParser = new ServerDateParser(); private boolean serverSide = true; public MessageService getMessageService() { return messageService; } public void setMessageService(MessageService messageService) { this.messageService = messageService; } public String getMessageLocaleKey() { return messageLocaleKey; } public void setMessageLocaleKey(String messageLocaleKey) { this.messageLocaleKey = messageLocaleKey; } public String getMessageGroupKey() { return messageGroupKey; } public void setMessageGroupKey(String messageGroupKey) { this.messageGroupKey = messageGroupKey; } public void setDateParser(DateParser dateParser) { this.dateParser = dateParser; } /** * @return the serverSide */ public boolean isServerSide() { return serverSide; } /** * @param serverSide * the serverSide to set */ public void setServerSide(boolean serverSide) { this.serverSide = serverSide; } /** * @return the dateParser */ public DateParser getDateParser() { return dateParser; } public List<ValidationResultInfo> validateObject(Object data, ObjectStructureDefinition objStructure, ContextInfo contextInfo) { ObjectStructureHierarchy objectStructureHierarchy = new ObjectStructureHierarchy(); objectStructureHierarchy.setObjectStructure(objStructure); return validateObject(data, objectStructureHierarchy, contextInfo); } /** * Validate Object and all its nested child objects for given type and state * * @param data * @param objStructure * @return */ public List<ValidationResultInfo> validateObject(Object data, ObjectStructureHierarchy objStructure, ContextInfo contextInfo) { List<ValidationResultInfo> results = new ArrayList<ValidationResultInfo>(); Stack<String> elementStack = new Stack<String>(); validateObject(results, data, objStructure, elementStack, data, objStructure.getObjectStructure(), true, null, contextInfo); return results; } private void validateObject(List<ValidationResultInfo> results, Object data, ObjectStructureHierarchy objStructure, Stack<String> elementStack, Object rootData, ObjectStructureDefinition rootObjStructure, boolean isRoot, ConstraintDataProvider parentDataProvider, ContextInfo contextInfo) { ConstraintDataProvider dataProvider = new BeanConstraintDataProvider(); dataProvider.initialize(data); if (parentDataProvider != null) { dataProvider.setParent(parentDataProvider); } // Push object structure to the top of the stack StringBuilder objXPathElement = new StringBuilder(dataProvider.getPath()); if (!isRoot && !objXPathElement.toString().isEmpty()) { elementStack.push(objXPathElement.toString()); } /* * Do nothing if the object to be validated is not type/state or if the objectstructure with constraints is not * provided */ if (null == objStructure.getObjectStructure()) { return; } for (FieldDefinition f : objStructure.getObjectStructure().getAttributes()) { validateField(results, f, objStructure, dataProvider, elementStack, rootData, rootObjStructure, contextInfo); // Use Custom Validators if (f.getCustomValidatorClass() != null || f.isServerSide() && serverSide) { Validator customValidator = validatorFactory.getValidator(f.getCustomValidatorClass()); if (customValidator == null) { throw new RuntimeException("Custom Validator " + f.getCustomValidatorClass() + " was not configured in this context"); } List<ValidationResultInfo> l = customValidator.validateObject(f, data, objStructure.getObjectStructure(), elementStack, null); results.addAll(l); } } if (!isRoot && !objXPathElement.toString().isEmpty()) { elementStack.pop(); } /* All Field validations are returned right now */ // List<ValidationResultInfo> resultsBuffer = new // ArrayList<ValidationResultInfo>(); // for (ValidationResultContainer vc : results) { // if (skipFields.contains(vc.getElement()) == false) { // resultsBuffer.add(vc); // } // } // results = resultsBuffer; } public void validateField(List<ValidationResultInfo> results, FieldDefinition field, ObjectStructureHierarchy objStruct, ConstraintDataProvider dataProvider, Stack<String> elementStack, Object rootData, ObjectStructureDefinition rootObjectStructure, ContextInfo contextInfo) { Object value = dataProvider.getValue(field.getName()); // Handle null values in field if (value == null || "".equals(value.toString().trim())) { processConstraint(results, field, objStruct, value, dataProvider, elementStack, rootData, rootObjectStructure, contextInfo); return; // no need to do further processing } /* * For complex object structures only the following constraints apply 1. TypeStateCase 2. MinOccurs 3. MaxOccurs */ if (DataType.COMPLEX.equals(field.getDataType())) { ObjectStructureHierarchy nestedObjStruct = new ObjectStructureHierarchy(); nestedObjStruct.setParentObjectStructureHierarchy(objStruct); if (null != field.getDataObjectStructure()) { nestedObjStruct.setObjectStructure(field.getDataObjectStructure()); } elementStack.push(field.getName()); // beanPathStack.push(field.isDynamic()?"attributes("+field.getName()+")":field.getName()); if (value instanceof Collection) { String xPathForCollection = getElementXpath(elementStack) + "/*"; int i = 0; for (Object o : (Collection<?>) value) { elementStack.push(Integer.toString(i)); // beanPathStack.push(!beanPathStack.isEmpty()?beanPathStack.pop():""+"["+i+"]"); processNestedObjectStructure(results, o, nestedObjStruct, field, elementStack, rootData, rootObjectStructure, dataProvider, contextInfo); // beanPathStack.pop(); // beanPathStack.push(field.isDynamic()?"attributes("+field.getName()+")":field.getName()); elementStack.pop(); i++; } if (field.getMinOccurs() != null && field.getMinOccurs() > ((Collection<?>) value).size()) { ValidationResultInfo valRes = new ValidationResultInfo(xPathForCollection, value); valRes.setError(MessageUtils.interpolate(getMessage("validation.minOccurs", contextInfo), toMap(field))); results.add(valRes); } Integer maxOccurs = tryParse(field.getMaxOccurs()); if (maxOccurs != null && maxOccurs < ((Collection<?>) value).size()) { ValidationResultInfo valRes = new ValidationResultInfo(xPathForCollection, value); valRes.setError(MessageUtils.interpolate(getMessage("validation.maxOccurs", contextInfo), toMap(field))); results.add(valRes); } } else { if (null != value) { processNestedObjectStructure(results, value, nestedObjStruct, field, elementStack, rootData, rootObjectStructure, dataProvider, contextInfo); } else { if (field.getMinOccurs() != null && field.getMinOccurs() > 0) { ValidationResultInfo val = new ValidationResultInfo(getElementXpath(elementStack), value); if (field.getLabelKey() != null) { val.setError(getMessage(field.getLabelKey(), contextInfo)); } else { val.setError(getMessage("validation.required", contextInfo)); } results.add(val); } } } // beanPathStack.pop(); elementStack.pop(); } else { // If non complex data type if (value instanceof Collection) { if (((Collection<?>) value).isEmpty()) { processConstraint(results, field, objStruct, "", dataProvider, elementStack, rootData, rootObjectStructure, contextInfo); } int i = 0; for (Object o : (Collection<?>) value) { // This is tricky, change the field name to the index in the elementStack(this is for lists of non // complex types) elementStack.push(field.getName()); FieldDefinition tempField = new FieldDefinition(); BeanUtils.copyProperties(field, tempField); tempField.setName(Integer.toBinaryString(i)); processConstraint(results, tempField, objStruct, o, dataProvider, elementStack, rootData, rootObjectStructure, contextInfo); elementStack.pop(); i++; } String xPath = getElementXpath(elementStack) + "/" + field.getName() + "/*"; if (field.getMinOccurs() != null && field.getMinOccurs() > ((Collection<?>) value).size()) { ValidationResultInfo valRes = new ValidationResultInfo(xPath, value); valRes.setError(MessageUtils.interpolate(getMessage("validation.minOccurs", contextInfo), toMap(field))); results.add(valRes); } Integer maxOccurs = tryParse(field.getMaxOccurs()); if (maxOccurs != null && maxOccurs < ((Collection<?>) value).size()) { ValidationResultInfo valRes = new ValidationResultInfo(xPath, value); valRes.setError(MessageUtils.interpolate(getMessage("validation.maxOccurs", contextInfo), toMap(field))); results.add(valRes); } } else { processConstraint(results, field, objStruct, value, dataProvider, elementStack, rootData, rootObjectStructure, contextInfo); } } } protected Integer tryParse(String s) { Integer result = null; if (s != null) { try { result = Integer.valueOf(s); } catch (NumberFormatException e) { // do nothing } } return result; } protected void processNestedObjectStructure(List<ValidationResultInfo> results, Object value, ObjectStructureHierarchy nestedObjStruct, FieldDefinition field, Stack<String> elementStack, Object rootData, ObjectStructureDefinition rootObjStructure, ConstraintDataProvider parentDataProvider, ContextInfo contextInfo) { validateObject(results, value, nestedObjStruct, elementStack, rootData, rootObjStructure, false, parentDataProvider, contextInfo); } protected void processConstraint(List<ValidationResultInfo> valResults, FieldDefinition field, ObjectStructureHierarchy objStructure, Object value, ConstraintDataProvider dataProvider, Stack<String> elementStack, Object rootData, ObjectStructureDefinition rootObjStructure, ContextInfo contextInfo) { // Process Case Constraint // Case Constraint are only evaluated on the field. Nested case constraints are currently ignored Constraint caseConstraint = processCaseConstraint(valResults, field.getCaseConstraint(), objStructure, value, dataProvider, elementStack, rootData, rootObjStructure, contextInfo); Constraint constraint = (null != caseConstraint) ? caseConstraint : field; processBaseConstraints(valResults, constraint, field, value, elementStack, contextInfo); // Stop other checks if value is null if (value == null || "".equals(value.toString().trim())) { return; } String elementPath = getElementXpath(elementStack) + "/" + field.getName(); // Process Valid Chars if (null != constraint.getValidChars()) { ValidationResultInfo val = processValidCharConstraint(elementPath, constraint.getValidChars(), dataProvider, value, contextInfo); if (null != val) { valResults.add(val); } } // Process Require Constraints (only if this field has value) if (value != null && !"".equals(value.toString().trim())) { if (null != constraint.getRequireConstraint() && constraint.getRequireConstraint().size() > 0) { for (RequiredConstraint rc : constraint.getRequireConstraint()) { ValidationResultInfo val = processRequireConstraint(elementPath, rc, field, objStructure.getObjectStructure(), dataProvider, contextInfo); if (null != val) { valResults.add(val); // FIXME: For clarity, might be better to handle this in the processRequireConstraint method instead. processCrossFieldWarning(valResults, rc, val.getErrorLevel(), field.getName(), contextInfo); } } } } // Process Occurs Constraint if (null != constraint.getOccursConstraint() && constraint.getOccursConstraint().size() > 0) { for (MustOccurConstraint oc : constraint.getOccursConstraint()) { ValidationResultInfo val = processOccursConstraint(elementPath, oc, field, objStructure.getObjectStructure(), dataProvider, contextInfo); if (null != val) { valResults.add(val); } } } // Process lookup Constraint if (null != constraint.getLookupDefinition()) { processLookupConstraint(valResults, constraint.getLookupDefinition(), field, elementStack, dataProvider, objStructure.getObjectStructure(), rootData, rootObjStructure, value, contextInfo); } } protected ValidationResultInfo processRequireConstraint(String element, RequiredConstraint constraint, FieldDefinition field, ObjectStructureDefinition objStructure, ConstraintDataProvider dataProvider, ContextInfo contextInfo) { ValidationResultInfo val = null; String fieldName = constraint.getFieldPath();// TODO parse fieldname from here Object fieldValue = dataProvider.getValue(fieldName); boolean result = true; if (fieldValue instanceof java.lang.String) { result = hasText((String) fieldValue); } else if (fieldValue instanceof Collection) { result = (((Collection<?>) fieldValue).size() > 0); } else { result = (null != fieldValue) ? true : false; } if (!result) { Map<String, Object> rMap = new HashMap<String, Object>(); rMap.put("field1", field.getName()); rMap.put("field2", fieldName); val = new ValidationResultInfo(element, fieldValue); val.setMessage(MessageUtils.interpolate(getMessage("validation.requiresField", contextInfo), rMap)); val.setLevel(constraint.getErrorLevel()); } return val; } /** * Process caseConstraint tag and sets any of the base constraint items if any of the when condition matches * * @param valResults * @param caseConstraint * @param objStructure */ protected Constraint processCaseConstraint(List<ValidationResultInfo> valResults, CaseConstraint caseConstraint, ObjectStructureHierarchy objStructure, Object value, ConstraintDataProvider dataProvider, Stack<String> elementStack, Object rootData, ObjectStructureDefinition rootObjStructure, ContextInfo contextInfo) { if (null == caseConstraint) { return null; } String operator = (hasText(caseConstraint.getOperator())) ? caseConstraint.getOperator() : "EQUALS"; FieldDefinition caseField = null; boolean absolutePath = false; if (hasText(caseConstraint.getFieldPath())) { if (caseConstraint.getFieldPath().startsWith("/")) { absolutePath = true; caseField = ValidatorUtils.getField(caseConstraint.getFieldPath().substring(1), rootObjStructure); } else { caseField = ValidatorUtils.getField(caseConstraint.getFieldPath(), objStructure); } } // TODO: What happens when the field is not in the dataProvider? Object fieldValue = value; if (caseField != null) { if (absolutePath) { try { if (caseField.isDynamic()) { // Pull the value from the dynamic attribute map // TODO There needs to be some mapping from PropertyUtils to the KS path // Until then, this will only work for root level properties Map<String, String> attributes = null; Object atts = PropertyUtils.getNestedProperty(rootData, "attributes"); if (atts instanceof Map<?, ?>) { attributes = (Map<String, String>) atts; } else { List<AttributeInfo> attToMap = (List<AttributeInfo>) atts; if (attToMap != null) { for (AttributeInfo atin : attToMap) { try { attributes.put(atin.getKey(), atin.getValue()); } catch (Exception e) { System.out.print("Failed at " + rootData.getClass().getName() + " for object attributes"); } } } } if (attributes != null) { fieldValue = attributes.get(caseConstraint.getFieldPath().substring(1)); } } else { fieldValue = PropertyUtils.getNestedProperty(rootData, caseConstraint.getFieldPath().substring(1)); } } catch (IllegalAccessException e) { } catch (InvocationTargetException e) { } catch (NoSuchMethodException e) { } } else { fieldValue = ValidatorUtils.getFieldValue(caseConstraint.getFieldPath(), dataProvider); } } DataType fieldDataType = (null != caseField ? caseField.getDataType() : null); // If fieldValue is null then skip Case check if (null == fieldValue) { return null; } // Extract value for field Key for (WhenConstraint wc : caseConstraint.getWhenConstraint()) { if (hasText(wc.getValuePath())) { Object whenValue = null; if (wc.getValuePath().startsWith("/")) { try { whenValue = PropertyUtils.getNestedProperty(rootData, wc.getValuePath().substring(1)); } catch (IllegalAccessException e) { } catch (InvocationTargetException e) { } catch (NoSuchMethodException e) { } } else { whenValue = dataProvider.getValue(wc.getValuePath()); } if (ValidatorUtils.compareValues(fieldValue, whenValue, fieldDataType, operator, caseConstraint.isCaseSensitive(), dateParser) && null != wc.getConstraint()) { Constraint constraint = wc.getConstraint(); if (constraint.getCaseConstraint() != null) { return processCaseConstraint(valResults, constraint.getCaseConstraint(), objStructure, value, dataProvider, elementStack, rootData, rootObjStructure, contextInfo); } else { processCrossFieldWarning(valResults, caseConstraint, constraint, value, constraint.getErrorLevel(), contextInfo); return constraint; } } } else { List<Object> whenValueList = wc.getValues(); for (Object whenValue : whenValueList) { if (ValidatorUtils.compareValues(fieldValue, whenValue, fieldDataType, operator, caseConstraint.isCaseSensitive(), dateParser) && null != wc.getConstraint()) { Constraint constraint = wc.getConstraint(); if (constraint.getCaseConstraint() != null) { return processCaseConstraint(valResults, constraint.getCaseConstraint(), objStructure, value, dataProvider, elementStack, rootData, rootObjStructure, contextInfo); } else { processCrossFieldWarning(valResults, caseConstraint, constraint, value, constraint.getErrorLevel(), contextInfo); return constraint; } } } } } return null; } public ValidationResultInfo processValidCharConstraint(String element, ValidCharsConstraint vcConstraint, ConstraintDataProvider dataProvider, Object value, ContextInfo contextInfo) { ValidationResultInfo val = null; StringBuilder fieldValue = new StringBuilder(); String validChars = vcConstraint.getValue(); fieldValue.append(ValidatorUtils.getString(value)); int typIdx = validChars.indexOf(":"); String processorType = "regex"; if (-1 == typIdx) { validChars = "[" + validChars + "]*"; } else { processorType = validChars.substring(0, typIdx); validChars = validChars.substring(typIdx + 1); } if ("regex".equalsIgnoreCase(processorType)) { if (fieldValue == null || !fieldValue.toString().matches(validChars)) { val = new ValidationResultInfo(element, fieldValue); if (vcConstraint.getLabelKey() != null) { val.setError(getMessage(vcConstraint.getLabelKey(), contextInfo)); } else { val.setError(getMessage("validation.validCharsFailed", contextInfo)); } } } return val; } /** * Computes if all the filed required in the occurs clause are between the min and max * * @param element * @param constraint * @param field * @param objStructure * @param dataProvider * @return */ protected ValidationResultInfo processOccursConstraint(String element, MustOccurConstraint constraint, FieldDefinition field, ObjectStructureDefinition objStructure, ConstraintDataProvider dataProvider, ContextInfo contextInfo) { boolean result = false; int trueCount = 0; ValidationResultInfo val = null; for (RequiredConstraint rc : constraint.getRequiredFields()) { trueCount += (processRequireConstraint("", rc, field, objStructure, dataProvider, contextInfo) != null) ? 1 : 0; } for (MustOccurConstraint oc : constraint.getOccurs()) { trueCount += (processOccursConstraint("", oc, field, objStructure, dataProvider, contextInfo) != null) ? 1 : 0; } result = (trueCount >= constraint.getMin() && trueCount <= constraint.getMax()) ? true : false; if (!result) { // TODO: figure out what data should go here instead of null val = new ValidationResultInfo(element, null); val.setMessage(getMessage("validation.occurs", contextInfo)); val.setLevel(constraint.getErrorLevel()); } return val; } // TODO: Implement lookup constraint protected void processLookupConstraint(List<ValidationResultInfo> valResults, LookupConstraint lookupConstraint, FieldDefinition field, Stack<String> elementStack, ConstraintDataProvider dataProvider, ObjectStructureDefinition objStructure, Object rootData, ObjectStructureDefinition rootObjStructure, Object value, ContextInfo contextInfo) { if (lookupConstraint == null) { return; } // Create search params based on the param mapping List<SearchParamInfo> params = new ArrayList<SearchParamInfo>(); for (CommonLookupParam paramMapping : lookupConstraint.getParams()) { // Skip params that are the search param id key if (lookupConstraint.getSearchParamIdKey() != null && lookupConstraint.getSearchParamIdKey().equals(paramMapping.getKey())) { continue; } SearchParamInfo param = new SearchParamInfo(); param.setKey(paramMapping.getKey()); // If the value of the search param comes form another field then get it if (paramMapping.getFieldPath() != null && !paramMapping.getFieldPath().isEmpty()) { FieldDefinition lookupField = null; boolean absolutePath = false; if (hasText(paramMapping.getFieldPath())) { if (paramMapping.getFieldPath().startsWith("/")) { absolutePath = true; lookupField = ValidatorUtils.getField(paramMapping.getFieldPath().substring(1), rootObjStructure); } else { lookupField = ValidatorUtils.getField(paramMapping.getFieldPath(), objStructure); } } Object fieldValue = null; if (lookupField != null) { if (absolutePath) { try { if (lookupField.isDynamic()) { // Pull the value from the dynamic attribute map // Until then, this will only work for root level properties Map<String, String> attributes = (Map<String, String>) PropertyUtils .getNestedProperty(rootData, "attributes"); if (attributes != null) { fieldValue = attributes.get(paramMapping.getFieldPath().substring(1)); } } else { fieldValue = PropertyUtils.getNestedProperty(rootData, paramMapping.getFieldPath().substring(1)); } } catch (IllegalAccessException e) { } catch (InvocationTargetException e) { } catch (NoSuchMethodException e) { } } else { fieldValue = dataProvider.getValue(lookupField.getName()); } } else { fieldValue = dataProvider.getValue(paramMapping.getFieldPath()); } if (fieldValue instanceof String) { param.getValues().add((String) fieldValue); } else if (fieldValue instanceof List<?>) { param.setValues((List<String>) fieldValue); } } else if (paramMapping.getDefaultValueString() != null) { param.getValues().add(paramMapping.getDefaultValueString()); } else { param.setValues(paramMapping.getDefaultValueList()); } params.add(param); } if (lookupConstraint.getSearchParamIdKey() != null) { SearchParamInfo param = new SearchParamInfo(); param.setKey(lookupConstraint.getSearchParamIdKey()); if (value instanceof String) { param.getValues().add((String) value); } else if (value instanceof List<?>) { param.setValues((List<String>) value); } params.add(param); } SearchRequestInfo searchRequest = new SearchRequestInfo(); searchRequest.setMaxResults(1); searchRequest.setStartAt(0); searchRequest.setNeededTotalResults(false); searchRequest.setSearchKey(lookupConstraint.getSearchTypeId()); searchRequest.setParams(params); SearchResultInfo searchResult = null; try { searchResult = searchDispatcher.search(searchRequest, contextInfo); } catch (Exception e) { LOG.info("Error calling Search", e); } // If there are no search results then make a validation result if (searchResult == null || searchResult.getRows() == null || searchResult.getRows().isEmpty()) { ValidationResultInfo val = new ValidationResultInfo( getElementXpath(elementStack) + "/" + field.getName(), value); val.setLevel(lookupConstraint.getErrorLevel()); val.setMessage(getMessage("validation.lookup", contextInfo)); valResults.add(val); processCrossFieldWarning(valResults, lookupConstraint, lookupConstraint.getErrorLevel(), contextInfo); } } protected void processBaseConstraints(List<ValidationResultInfo> valResults, Constraint constraint, FieldDefinition field, Object value, Stack<String> elementStack, ContextInfo contextInfo) { DataType dataType = field.getDataType(); String name = field.getName(); if (value == null || "".equals(value.toString().trim())) { if (constraint.getMinOccurs() != null && constraint.getMinOccurs() > 0) { ValidationResultInfo val = new ValidationResultInfo(getElementXpath(elementStack) + "/" + name, value); if (constraint.getLabelKey() != null) { val.setError(getMessage(constraint.getLabelKey(), contextInfo)); } else { val.setMessage(getMessage("validation.required", contextInfo)); } val.setLevel(constraint.getErrorLevel()); valResults.add(val); } return; } String elementPath = getElementXpath(elementStack) + "/" + name; if (DataType.STRING.equals(dataType)) { validateString(value, constraint, elementPath, valResults, contextInfo); } else if (DataType.INTEGER.equals(dataType)) { validateInteger(value, constraint, elementPath, valResults, contextInfo); } else if (DataType.LONG.equals(dataType)) { validateLong(value, constraint, elementPath, valResults, contextInfo); } else if (DataType.DOUBLE.equals(dataType)) { validateDouble(value, constraint, elementPath, valResults, contextInfo); } else if (DataType.FLOAT.equals(dataType)) { validateFloat(value, constraint, elementPath, valResults, contextInfo); } else if (DataType.BOOLEAN.equals(dataType)) { validateBoolean(value, constraint, elementPath, valResults, contextInfo); } else if (DataType.DATE.equals(dataType)) { validateDate(value, constraint, elementPath, valResults, dateParser, contextInfo); } } /** * This adds a warning on the related field when a processed case-constraint results in a warning * * @param valResults * @param crossConstraint * @param constraint */ protected void processCrossFieldWarning(List<ValidationResultInfo> valResults, CaseConstraint crossConstraint, Constraint constraint, Object value, ErrorLevel errorLevel, ContextInfo contextInfo) { if ((ErrorLevel.WARN == errorLevel || ErrorLevel.ERROR == errorLevel) && (value == null || "".equals(value.toString().trim()))) { if (constraint.getMinOccurs() != null && constraint.getMinOccurs() > 0) { String crossFieldPath = crossConstraint.getFieldPath(); String crossFieldMessageId = crossConstraint.getFieldPathMessageId() == null ? "validation.required" : crossConstraint.getFieldPathMessageId(); addCrossFieldWarning(valResults, crossFieldPath, getMessage(crossFieldMessageId, contextInfo), errorLevel); } } } /** * This adds a warning on the related field when a processed case-constraint results in a warning * * @param valResults * @param requiredConstraint * @param field */ protected void processCrossFieldWarning(List<ValidationResultInfo> valResults, RequiredConstraint requiredConstraint, ErrorLevel errorLevel, String field, ContextInfo contextInfo) { if ((ErrorLevel.WARN == errorLevel || ErrorLevel.ERROR == errorLevel) && requiredConstraint != null) { String crossFieldPath = requiredConstraint.getFieldPath(); String crossFieldMessageId = requiredConstraint.getFieldPathMessageId() == null ? "validation.required" : requiredConstraint.getFieldPathMessageId(); addCrossFieldWarning(valResults, crossFieldPath, getMessage(crossFieldMessageId, contextInfo), errorLevel); } } /** * This adds a warning on the related field when a processed lookup-constraint results in a warning * * @param valResults * @param lookupConstraint */ protected void processCrossFieldWarning(List<ValidationResultInfo> valResults, LookupConstraint lookupConstraint, ErrorLevel errorLevel, ContextInfo contextInfo) { if ((ErrorLevel.WARN == errorLevel || ErrorLevel.ERROR == errorLevel) && lookupConstraint != null) { for (CommonLookupParam param : lookupConstraint.getParams()) { if (param.getFieldPath() != null && !param.getFieldPath().isEmpty()) { String crossFieldPath = param.getFieldPath(); String crossFieldMessageId = param.getFieldPathMessageId() == null ? "validation.lookup.cause" : param.getFieldPathMessageId(); addCrossFieldWarning(valResults, crossFieldPath, getMessage(crossFieldMessageId, contextInfo), errorLevel); } } } } protected void addCrossFieldWarning(List<ValidationResultInfo> valResults, String crossFieldPath, String message, ErrorLevel errorLevel) { // Check to see if the exact same validation message already exists on referenced field boolean warnAlreadyExists = false; for (ValidationResultInfo vr : valResults) { if (vr.getElement().equals(crossFieldPath) && vr.getMessage().equals(message)) { warnAlreadyExists = true; } } // Only add this warning, if it hasn't been already added if (!warnAlreadyExists) { ValidationResultInfo val = new ValidationResultInfo(crossFieldPath, null); val.setMessage(message); val.setLevel(errorLevel); valResults.add(val); } } protected void validateBoolean(Object value, Constraint constraint, String element, List<ValidationResultInfo> results, ContextInfo contextInfo) { if (!(value instanceof Boolean)) { try { Boolean.valueOf(value.toString()); } catch (Exception e) { ValidationResultInfo val = new ValidationResultInfo(element, value); val.setError(getMessage("validation.mustBeBoolean", contextInfo)); results.add(val); } } } protected void validateDouble(Object value, Constraint constraint, String element, List<ValidationResultInfo> results, ContextInfo contextInfo) { Double v = null; ValidationResultInfo val = new ValidationResultInfo(element, value); if (value instanceof Number) { v = ((Number) value).doubleValue(); } else { try { v = Double.valueOf(value.toString()); } catch (Exception e) { val.setError(getMessage("validation.mustBeDouble", contextInfo)); } } if (val.isOk()) { Double maxValue = ValidatorUtils.getDouble(constraint.getInclusiveMax()); Double minValue = ValidatorUtils.getDouble(constraint.getExclusiveMin()); if (maxValue != null && minValue != null) { // validate range if (v > maxValue || v < minValue) { val.setError(MessageUtils.interpolate(getMessage("validation.outOfRange", contextInfo), toMap(constraint))); } } else if (maxValue != null) { if (v > maxValue) { val.setError(MessageUtils.interpolate(getMessage("validation.maxValueFailed", contextInfo), toMap(constraint))); } } else if (minValue != null) { if (v < minValue) { val.setError(MessageUtils.interpolate(getMessage("validation.minValueFailed", contextInfo), toMap(constraint))); } } } if (!val.isOk()) { results.add(val); } } protected void validateFloat(Object value, Constraint constraint, String element, List<ValidationResultInfo> results, ContextInfo contextInfo) { Float v = null; ValidationResultInfo val = new ValidationResultInfo(element, value); if (value instanceof Number) { v = ((Number) value).floatValue(); } else { try { v = Float.valueOf(value.toString()); } catch (Exception e) { val.setError(getMessage("validation.mustBeFloat", contextInfo)); } } if (val.isOk()) { Float maxValue = ValidatorUtils.getFloat(constraint.getInclusiveMax()); Float minValue = ValidatorUtils.getFloat(constraint.getExclusiveMin()); if (maxValue != null && minValue != null) { // validate range if (v > maxValue || v < minValue) { val.setError(MessageUtils.interpolate(getMessage("validation.outOfRange", contextInfo), toMap(constraint))); } } else if (maxValue != null) { if (v > maxValue) { val.setError(MessageUtils.interpolate(getMessage("validation.maxValueFailed", contextInfo), toMap(constraint))); } } else if (minValue != null) { if (v < minValue) { val.setError(MessageUtils.interpolate(getMessage("validation.minValueFailed", contextInfo), toMap(constraint))); } } } if (!val.isOk()) { results.add(val); } } protected void validateLong(Object value, Constraint constraint, String element, List<ValidationResultInfo> results, ContextInfo contextInfo) { Long v = null; ValidationResultInfo val = new ValidationResultInfo(element, value); if (value instanceof Number) { v = ((Number) value).longValue(); } else { try { v = Long.valueOf(value.toString()); } catch (Exception e) { val.setError(getMessage("validation.mustBeLong", contextInfo)); } } if (val.isOk()) { Long maxValue = ValidatorUtils.getLong(constraint.getInclusiveMax()); Long minValue = ValidatorUtils.getLong(constraint.getExclusiveMin()); if (maxValue != null && minValue != null) { // validate range if (v > maxValue || v < minValue) { val.setError(MessageUtils.interpolate(getMessage("validation.outOfRange", contextInfo), toMap(constraint))); } } else if (maxValue != null) { if (v > maxValue) { val.setError(MessageUtils.interpolate(getMessage("validation.maxValueFailed", contextInfo), toMap(constraint))); } } else if (minValue != null) { if (v < minValue) { val.setError(MessageUtils.interpolate(getMessage("validation.minValueFailed", contextInfo), toMap(constraint))); } } } if (!val.isOk()) { results.add(val); } } protected void validateInteger(Object value, Constraint constraint, String element, List<ValidationResultInfo> results, ContextInfo contextInfo) { Integer v = null; ValidationResultInfo val = new ValidationResultInfo(element, value); if (value instanceof Number) { v = ((Number) value).intValue(); } else { try { v = Integer.valueOf(value.toString()); } catch (Exception e) { val.setError(getMessage("validation.mustBeInteger", contextInfo)); } } if (val.isOk()) { Integer maxValue = ValidatorUtils.getInteger(constraint.getInclusiveMax()); Integer minValue = ValidatorUtils.getInteger(constraint.getExclusiveMin()); if (maxValue != null && minValue != null) { // validate range if (v > maxValue || v < minValue) { val.setError(MessageUtils.interpolate(getMessage("validation.outOfRange", contextInfo), toMap(constraint))); } } else if (maxValue != null) { if (v > maxValue) { val.setError(MessageUtils.interpolate(getMessage("validation.maxValueFailed", contextInfo), toMap(constraint))); } } else if (minValue != null) { if (v < minValue) { val.setError(MessageUtils.interpolate(getMessage("validation.minValueFailed", contextInfo), toMap(constraint))); } } } if (!val.isOk()) { results.add(val); } } protected void validateDate(Object value, Constraint constraint, String element, List<ValidationResultInfo> results, DateParser dateParser, ContextInfo contextInfo) { ValidationResultInfo val = new ValidationResultInfo(element, value); Date v = null; if (value instanceof Date) { v = (Date) value; } else { try { v = dateParser.parseDate(value.toString()); } catch (Exception e) { val.setError(getMessage("validation.mustBeDate", contextInfo)); } } if (val.isOk()) { Date maxValue = ValidatorUtils.getDate(constraint.getInclusiveMax(), dateParser); Date minValue = ValidatorUtils.getDate(constraint.getExclusiveMin(), dateParser); if (maxValue != null && minValue != null) { // validate range if (v.getTime() > maxValue.getTime() || v.getTime() < minValue.getTime()) { val.setError(MessageUtils.interpolate(getMessage("validation.outOfRange", contextInfo), toMap(constraint))); } } else if (maxValue != null) { if (v.getTime() > maxValue.getTime()) { val.setError(MessageUtils.interpolate(getMessage("validation.maxValueFailed", contextInfo), toMap(constraint))); } } else if (minValue != null) { if (v.getTime() < minValue.getTime()) { val.setError(MessageUtils.interpolate(getMessage("validation.minValueFailed", contextInfo), toMap(constraint))); } } } if (!val.isOk()) { results.add(val); } } protected void validateString(Object value, Constraint constraint, String element, List<ValidationResultInfo> results, ContextInfo contextInfo) { if (value == null) { value = ""; } String s = value.toString().trim(); ValidationResultInfo val = new ValidationResultInfo(element, value); Integer maxLength = tryParse(constraint.getMaxLength()); if (maxLength != null && constraint.getMinLength() != null && constraint.getMinLength() > 0) { if (s.length() > maxLength || s.length() < constraint.getMinLength()) { val.setError(MessageUtils.interpolate(getMessage("validation.lengthOutOfRange", contextInfo), toMap(constraint))); } } else if (maxLength != null) { if (s.length() > Integer.parseInt(constraint.getMaxLength())) { val.setError(MessageUtils.interpolate(getMessage("validation.maxLengthFailed", contextInfo), toMap(constraint))); } } else if (constraint.getMinLength() != null && constraint.getMinLength() > 0) { if (s.length() < constraint.getMinLength()) { val.setError(MessageUtils.interpolate(getMessage("validation.minLengthFailed", contextInfo), toMap(constraint))); } } if (!val.isOk()) { results.add(val); } } protected String getMessage(String messageId, ContextInfo contextInfo) { if (null == messageService) { return messageId; } // TODO: this need to be properly implemented. LocaleInfo locale = new LocaleInfo(); locale.setLocaleLanguage(messageLocaleKey); MessageInfo msg = null; try { msg = messageService.getMessage(locale, messageGroupKey, messageId, contextInfo); } catch (DoesNotExistException e) { return ""; } catch (InvalidParameterException e) { return ""; } catch (MissingParameterException e) { return ""; } catch (OperationFailedException e) { return ""; } catch (PermissionDeniedException e) { return ""; } return msg.getValue(); } protected String getElementXpath(Stack<String> elementStack) { StringBuilder xPath = new StringBuilder(); Iterator<String> itr = elementStack.iterator(); while (itr.hasNext()) { xPath.append(itr.next()); if (itr.hasNext()) { xPath.append("/"); } } return xPath.toString(); } /* * Homemade has text so we dont need outside libs. */ protected boolean hasText(String string) { if (string == null || string.length() < 1) { return false; } int stringLength = string.length(); for (int i = 0; i < stringLength; i++) { char currentChar = string.charAt(i); if (' ' != currentChar || '\t' != currentChar || '\n' != currentChar) { return true; } } return false; } protected Map<String, Object> toMap(Constraint c) { Map<String, Object> result = new HashMap<String, Object>(); result.put("minOccurs", c.getMinOccurs()); result.put("maxOccurs", c.getMaxOccurs()); result.put("minLength", c.getMinLength()); result.put("maxLength", c.getMaxLength()); result.put("minValue", c.getExclusiveMin()); result.put("maxValue", c.getInclusiveMax()); // result.put("dataType", c.getDataType()); return result; } public SearchService getSearchDispatcher() { return searchDispatcher; } public void setSearchDispatcher(SearchService searchDispatcher) { this.searchDispatcher = searchDispatcher; } @Override public List<ValidationResultInfo> validateObject(FieldDefinition field, Object o, ObjectStructureDefinition objStructure, Stack<String> elementStack, ContextInfo contextInfo) { return null; } }