Coverage Report - org.kuali.rice.kim.impl.type.KimTypeServiceBase
 
Classes in this File Line Coverage Branch Coverage Complexity
KimTypeServiceBase
0%
0/401
0%
0/262
4.976
KimTypeServiceBase$KimTypeAttributeValidationException
0%
0/4
N/A
4.976
 
 1  
 /*
 2  
  * Copyright 2007-2008 The Kuali Foundation
 3  
  *
 4  
  * Licensed under the Educational Community License, Version 2.0 (the "License");
 5  
  * you may not use this file except in compliance with the License.
 6  
  * You may obtain a copy of the License at
 7  
  *
 8  
  * http://www.opensource.org/licenses/ecl2.php
 9  
  *
 10  
  * Unless required by applicable law or agreed to in writing, software
 11  
  * distributed under the License is distributed on an "AS IS" BASIS,
 12  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13  
  * See the License for the specific language governing permissions and
 14  
  * limitations under the License.
 15  
  */
 16  
 package org.kuali.rice.kim.impl.type;
 17  
 
 18  
 import org.apache.commons.beanutils.PropertyUtils;
 19  
 import org.apache.commons.lang.StringUtils;
 20  
 import org.kuali.rice.core.api.uif.RemotableAttributeError;
 21  
 import org.kuali.rice.core.api.util.ClassLoaderUtils;
 22  
 import org.kuali.rice.core.api.util.RiceKeyConstants;
 23  
 import org.kuali.rice.core.api.util.type.TypeUtils;
 24  
 import org.kuali.rice.core.web.format.Formatter;
 25  
 import org.kuali.rice.kew.doctype.bo.DocumentType;
 26  
 import org.kuali.rice.kim.api.services.KimApiServiceLocator;
 27  
 import org.kuali.rice.kim.api.type.KimType;
 28  
 import org.kuali.rice.kim.api.type.KimTypeAttribute;
 29  
 import org.kuali.rice.kim.api.type.KimTypeInfoService;
 30  
 import org.kuali.rice.kim.api.type.KimTypeService;
 31  
 import org.kuali.rice.kim.bo.types.dto.AttributeDefinitionMap;
 32  
 import org.kuali.rice.kns.lookup.LookupUtils;
 33  
 import org.kuali.rice.kns.util.FieldUtils;
 34  
 import org.kuali.rice.kns.web.ui.Field;
 35  
 import org.kuali.rice.krad.bo.BusinessObject;
 36  
 import org.kuali.rice.krad.comparator.StringValueComparator;
 37  
 import org.kuali.rice.krad.datadictionary.AttributeDefinition;
 38  
 import org.kuali.rice.krad.datadictionary.KimAttributeDefinition;
 39  
 import org.kuali.rice.krad.datadictionary.KimDataDictionaryAttributeDefinition;
 40  
 import org.kuali.rice.krad.datadictionary.PrimitiveAttributeDefinition;
 41  
 import org.kuali.rice.krad.datadictionary.RelationshipDefinition;
 42  
 import org.kuali.rice.krad.datadictionary.control.ControlDefinition;
 43  
 import org.kuali.rice.krad.datadictionary.validation.ValidationPattern;
 44  
 import org.kuali.rice.krad.keyvalues.KimAttributeValuesFinder;
 45  
 import org.kuali.rice.krad.service.BusinessObjectService;
 46  
 import org.kuali.rice.krad.service.DataDictionaryService;
 47  
 import org.kuali.rice.krad.service.DictionaryValidationService;
 48  
 import org.kuali.rice.krad.service.KRADServiceLocator;
 49  
 import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
 50  
 import org.kuali.rice.krad.util.ErrorMessage;
 51  
 import org.kuali.rice.krad.util.GlobalVariables;
 52  
 import org.kuali.rice.krad.util.KRADUtils;
 53  
 import org.kuali.rice.krad.util.ObjectUtils;
 54  
 
 55  
 import java.beans.PropertyDescriptor;
 56  
 import java.lang.reflect.Method;
 57  
 import java.lang.reflect.Modifier;
 58  
 import java.math.BigDecimal;
 59  
 import java.util.ArrayList;
 60  
 import java.util.Arrays;
 61  
 import java.util.Collections;
 62  
 import java.util.HashMap;
 63  
 import java.util.Iterator;
 64  
 import java.util.List;
 65  
 import java.util.Map;
 66  
 import java.util.Set;
 67  
 import java.util.regex.Pattern;
 68  
 
 69  0
 public class KimTypeServiceBase implements KimTypeService {
 70  
 
 71  0
         private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(KimTypeServiceBase.class);
 72  
 
 73  
         private BusinessObjectService businessObjectService;
 74  
         private DictionaryValidationService dictionaryValidationService;
 75  
         private DataDictionaryService dataDictionaryService;
 76  
         private KimTypeInfoService typeInfoService;
 77  
         
 78  0
         protected final List<String> workflowRoutingAttributes = new ArrayList<String>();
 79  0
         protected final List<String> requiredAttributes = new ArrayList<String>();
 80  
         protected boolean checkRequiredAttributes;
 81  
 
 82  
         @Override
 83  
         public String getWorkflowDocumentTypeName() {
 84  0
                 return null;
 85  
         }
 86  
 
 87  
         @Override
 88  
         public List<String> getWorkflowRoutingAttributes(String routeLevel) {
 89  0
                 return Collections.unmodifiableList(workflowRoutingAttributes);
 90  
         }
 91  
 
 92  
     @Override
 93  
         public AttributeDefinitionMap getAttributeDefinitions(String kimTypeId) {
 94  0
         List<String> uniqueAttributes = getUniqueAttributes(kimTypeId);
 95  0
         AttributeDefinitionMap definitions = new AttributeDefinitionMap();
 96  0
         KimType kimType = getTypeInfoService().getKimType(kimTypeId);
 97  0
         if ( kimType != null ) {
 98  0
             String nsCode = kimType.getNamespaceCode();
 99  0
             for (KimTypeAttribute typeAttribute : kimType.getAttributeDefinitions()) {
 100  0
                 AttributeDefinition definition = null;
 101  0
                 if (typeAttribute.getKimAttribute().getComponentName() == null) {
 102  0
                     definition = getNonDataDictionaryAttributeDefinition(typeAttribute);
 103  
                 } else {
 104  0
                     definition = getDataDictionaryAttributeDefinition(nsCode,kimTypeId,typeAttribute);
 105  
                 }
 106  
 
 107  0
                 if (definition != null) {
 108  0
                     if(uniqueAttributes!=null && uniqueAttributes.contains(definition.getName())){
 109  0
                         definition.setUnique(Boolean.TRUE);
 110  
                     }
 111  0
                     definitions.put(typeAttribute.getSortCode(), definition);
 112  
                 }
 113  0
             }
 114  0
         } else {
 115  0
             LOG.warn( "Unable to resolve KIM Type: " + kimTypeId + " - returning an empty AttributeDefinitionMap." );
 116  
         }
 117  0
                 return definitions;
 118  
         }
 119  
 
 120  
 
 121  
     /**
 122  
          * This is the default implementation.  It calls into the service for each attribute to
 123  
          * validate it there.  No combination validation is done.  That should be done
 124  
          * by overriding this method.
 125  
          */
 126  
         @Override
 127  
         public List<RemotableAttributeError> validateAttributes(String kimTypeId, Map<String, String> attributes) {
 128  0
                 final List<RemotableAttributeError> validationErrors = new ArrayList<RemotableAttributeError>();
 129  0
                 if ( attributes == null ) {
 130  0
                         return Collections.emptyList();
 131  
                 }
 132  0
                 KimType kimType = getTypeInfoService().getKimType(kimTypeId);
 133  
 
 134  0
                 for ( Map.Entry<String, String> entry : attributes.entrySet() ) {
 135  0
             KimTypeAttribute attr = kimType.getAttributeDefinitionByName(entry.getKey());
 136  
                         final List<RemotableAttributeError> attributeErrors;
 137  0
             if ( attr.getKimAttribute().getComponentName() == null) {
 138  0
                 attributeErrors = validateNonDataDictionaryAttribute(attr, entry.getKey(), entry.getValue());
 139  
             } else {
 140  0
                 attributeErrors = validateDataDictionaryAttribute(attr, entry.getKey(), entry.getValue());
 141  
             }
 142  
 
 143  0
                         if ( attributeErrors != null ) {
 144  0
                 validationErrors.addAll(attributeErrors);
 145  
                         }
 146  0
                 }
 147  
 
 148  0
                 final List<RemotableAttributeError> referenceCheckErrors = validateReferencesExistAndActive(kimType, attributes, validationErrors);
 149  0
         validationErrors.addAll(referenceCheckErrors);
 150  
 
 151  0
                 return validationErrors;
 152  
         }
 153  
 
 154  
     @Override
 155  
         public List<RemotableAttributeError> validateAttributesAgainstExisting(String kimTypeId, Map<String, String> newAttributes, Map<String, String> oldAttributes){
 156  0
         final List<RemotableAttributeError> errors = new ArrayList<RemotableAttributeError>();
 157  0
         errors.addAll(validateUniqueAttributes(kimTypeId, newAttributes, oldAttributes));
 158  0
         errors.addAll(validateUnmodifiableAttributes(kimTypeId, newAttributes, oldAttributes));
 159  0
         return Collections.unmodifiableList(errors);
 160  
         }
 161  
 
 162  
         /**
 163  
          *
 164  
          * This method matches input attribute set entries and standard attribute set entries using literal string match.
 165  
          *
 166  
          */
 167  
         protected boolean performMatch(Map<String, String> inputAttributes, Map<String, String> storedAttributes) {
 168  0
                 if ( storedAttributes == null || inputAttributes == null ) {
 169  0
                         return true;
 170  
                 }
 171  0
                 for ( Map.Entry<String, String> entry : storedAttributes.entrySet() ) {
 172  0
                         if (inputAttributes.containsKey(entry.getKey()) && !StringUtils.equals(inputAttributes.get(entry.getKey()), entry.getValue()) ) {
 173  0
                                 return false;
 174  
                         }
 175  
                 }
 176  0
                 return true;
 177  
         }
 178  
 
 179  
         protected Map<String, String> translateInputAttributes(Map<String, String> qualification){
 180  0
                 return qualification;
 181  
         }
 182  
 
 183  
 
 184  
 
 185  
         private Object getAttributeValue(PropertyDescriptor propertyDescriptor, String attributeValue){
 186  0
                 Object attributeValueObject = null;
 187  0
                 if(propertyDescriptor!=null && attributeValue!=null){
 188  0
                         Class<?> propertyType = propertyDescriptor.getPropertyType();
 189  0
                         if(String.class.equals(propertyType)){
 190  0
                                 attributeValueObject = KRADUtils
 191  
                         .createObject(propertyType, new Class[]{String.class}, new Object[]{attributeValue});
 192  
                         } else {
 193  0
                                 attributeValueObject = attributeValue;
 194  
                         }
 195  
                 }
 196  0
                 return attributeValueObject;
 197  
         }
 198  
         
 199  
         protected List<RemotableAttributeError> validateReferencesExistAndActive( KimType kimType, Map<String, String> attributes, List<RemotableAttributeError> previousValidationErrors) {
 200  0
                 Map<String, BusinessObject> componentClassInstances = new HashMap<String, BusinessObject>();
 201  0
                 List<RemotableAttributeError> errors = new ArrayList<RemotableAttributeError>();
 202  
                 
 203  0
                 for ( String attributeName : attributes.keySet() ) {
 204  0
                         KimTypeAttribute attr = kimType.getAttributeDefinitionByName(attributeName);
 205  
                         
 206  0
                         if (StringUtils.isNotBlank(attr.getKimAttribute().getComponentName())) {
 207  0
                                 if (!componentClassInstances.containsKey(attr.getKimAttribute().getComponentName())) {
 208  
                                         try {
 209  0
                                                 Class<?> componentClass = Class.forName( attr.getKimAttribute().getComponentName() );
 210  0
                                                 if (!BusinessObject.class.isAssignableFrom(componentClass)) {
 211  0
                                                         LOG.warn("Class " + componentClass.getName() + " does not implement BusinessObject.  Unable to perform reference existence and active validation");
 212  0
                                                         continue;
 213  
                                                 }
 214  0
                                                 BusinessObject componentInstance = (BusinessObject) componentClass.newInstance();
 215  0
                                                 componentClassInstances.put(attr.getKimAttribute().getComponentName(), componentInstance);
 216  0
                                         } catch (Exception e) {
 217  0
                                                 LOG.error("Unable to instantiate class for attribute: " + attributeName, e);
 218  0
                                         }
 219  
                                 }
 220  
                         }
 221  0
                 }
 222  
                 
 223  
                 // now that we have instances for each component class, try to populate them with any attribute we can, assuming there were no other validation errors associated with it
 224  0
                 for ( Map.Entry<String, String> entry : attributes.entrySet() ) {
 225  0
                         if (!RemotableAttributeError.containsAttribute(entry.getKey(), previousValidationErrors)) {
 226  0
                                 for (Object componentInstance : componentClassInstances.values()) {
 227  
                                         try {
 228  0
                                                 ObjectUtils.setObjectProperty(componentInstance, entry.getKey(), entry.getValue());
 229  0
                                         } catch (NoSuchMethodException e) {
 230  
                                                 // this is expected since not all attributes will be in all components
 231  0
                                         } catch (Exception e) {
 232  0
                                                 LOG.error("Unable to set object property class: " + componentInstance.getClass().getName() + " property: " + entry.getKey(), e);
 233  0
                                         }
 234  
                                 }
 235  
                         }
 236  
                 }
 237  
                 
 238  0
                 for (Map.Entry<String, BusinessObject> entry : componentClassInstances.entrySet()) {
 239  0
                         List<RelationshipDefinition> relationships = getDataDictionaryService().getDataDictionary().getBusinessObjectEntry(entry.getKey()).getRelationships();
 240  0
                         if (relationships == null) {
 241  0
                                 continue;
 242  
                         }
 243  
                         
 244  0
                         for (RelationshipDefinition relationshipDefinition : relationships) {
 245  0
                                 List<PrimitiveAttributeDefinition> primitiveAttributes = relationshipDefinition.getPrimitiveAttributes();
 246  
                                 
 247  
                                 // this code assumes that the last defined primitiveAttribute is the attributeToHighlightOnFail
 248  0
                                 String attributeToHighlightOnFail = primitiveAttributes.get(primitiveAttributes.size() - 1).getSourceName();
 249  
                                 
 250  
                                 // TODO: will this work for user ID attributes?
 251  
                                 
 252  0
                                 if (!attributes.containsKey(attributeToHighlightOnFail)) {
 253  
                                         // if the attribute to highlight wasn't passed in, don't bother validating
 254  0
                                         continue;
 255  
                                 }
 256  
                                 
 257  
 
 258  0
                                 KimTypeAttribute attr = kimType.getAttributeDefinitionByName(attributeToHighlightOnFail);
 259  0
                                 if (attr != null) {
 260  
                                         final String attributeDisplayLabel;
 261  0
                     if (StringUtils.isNotBlank(attr.getKimAttribute().getComponentName())) {
 262  0
                                                 attributeDisplayLabel = getDataDictionaryService().getAttributeLabel(attr.getKimAttribute().getComponentName(), attributeToHighlightOnFail);
 263  
                                         } else {
 264  0
                                                 attributeDisplayLabel = attr.getKimAttribute().getAttributeLabel();
 265  
                                         }
 266  
 
 267  0
                                         getDictionaryValidationService().validateReferenceExistsAndIsActive(entry.getValue(), relationshipDefinition.getObjectAttributeName(),
 268  
                                                         attributeToHighlightOnFail, attributeDisplayLabel);
 269  
                                 }
 270  0
                                 errors.add(RemotableAttributeError.Builder.create(attributeToHighlightOnFail, extractErrorsFromGlobalVariablesErrorMap(attributeToHighlightOnFail)).build());
 271  0
                         }
 272  0
                 }
 273  0
                 return errors;
 274  
         }
 275  
         
 276  
     protected List<RemotableAttributeError> validateAttributeRequired(String kimTypeId, String objectClassName, String attributeName, Object attributeValue, String errorKey) {
 277  0
         List<RemotableAttributeError> errors = new ArrayList<RemotableAttributeError>();
 278  
         // check if field is a required field for the business object
 279  0
         if (attributeValue == null || (attributeValue instanceof String && StringUtils.isBlank((String) attributeValue))) {
 280  0
                 AttributeDefinitionMap map = getAttributeDefinitions(kimTypeId);
 281  0
                 AttributeDefinition definition = map.getByAttributeName(attributeName);
 282  
                 
 283  0
             Boolean required = definition.isRequired();
 284  0
             ControlDefinition controlDef = definition.getControl();
 285  
 
 286  0
             if (required != null && required.booleanValue() && !(controlDef != null && controlDef.isHidden())) {
 287  
 
 288  
                 // get label of attribute for message
 289  0
                 String errorLabel = getAttributeErrorLabel(definition);
 290  0
                 errors.add(RemotableAttributeError.Builder.create(errorKey, createErrorString(RiceKeyConstants.ERROR_REQUIRED, errorLabel)).build());
 291  
             }
 292  
         }
 293  0
         return errors;
 294  
     }
 295  
     
 296  
         protected List<RemotableAttributeError> validateDataDictionaryAttribute(String kimTypeId, String entryName, Object object, PropertyDescriptor propertyDescriptor) {
 297  0
                 return validatePrimitiveFromDescriptor(kimTypeId, entryName, object, propertyDescriptor);
 298  
         }
 299  
 
 300  
     protected List<RemotableAttributeError> validatePrimitiveFromDescriptor(String kimTypeId, String entryName, Object object, PropertyDescriptor propertyDescriptor) {
 301  0
         List<RemotableAttributeError> errors = new ArrayList<RemotableAttributeError>();
 302  
         // validate the primitive attributes if defined in the dictionary
 303  0
         if (null != propertyDescriptor && getDataDictionaryService().isAttributeDefined(entryName, propertyDescriptor.getName())) {
 304  0
             Object value = ObjectUtils.getPropertyValue(object, propertyDescriptor.getName());
 305  0
             Class<?> propertyType = propertyDescriptor.getPropertyType();
 306  
 
 307  0
             if (TypeUtils.isStringClass(propertyType) || TypeUtils.isIntegralClass(propertyType) || TypeUtils.isDecimalClass(propertyType) || TypeUtils.isTemporalClass(propertyType)) {
 308  
 
 309  
                 // check value format against dictionary
 310  0
                 if (value != null && StringUtils.isNotBlank(value.toString())) {
 311  0
                     if (!TypeUtils.isTemporalClass(propertyType)) {
 312  0
                         errors.addAll(validateAttributeFormat(kimTypeId, entryName, propertyDescriptor.getName(), value.toString(), propertyDescriptor.getName()));
 313  
                     }
 314  
                 }
 315  
                 else {
 316  
                         // if it's blank, then we check whether the attribute should be required
 317  0
                     errors.addAll(validateAttributeRequired(kimTypeId, entryName, propertyDescriptor.getName(), value, propertyDescriptor.getName()));
 318  
                 }
 319  
             }
 320  
         }
 321  0
         return errors;
 322  
     }
 323  
     
 324  
     /**
 325  
      * Constant defines a validation method for an attribute value.
 326  
      * <p>Value is "validate"
 327  
      */
 328  
     protected static final String VALIDATE_METHOD="validate";
 329  
     
 330  
     protected String getAttributeErrorLabel(AttributeDefinition definition) {
 331  0
         String longAttributeLabel = definition.getLabel();
 332  0
         String shortAttributeLabel = definition.getShortLabel();
 333  0
         return longAttributeLabel + " (" + shortAttributeLabel + ")";
 334  
     }
 335  
     
 336  
     protected Pattern getAttributeValidatingExpression(AttributeDefinition definition) {
 337  0
             Pattern regex = null;
 338  0
         if (definition != null) {
 339  0
             if (definition.hasValidationPattern()) {
 340  0
                 regex = definition.getValidationPattern().getRegexPattern();
 341  
             } else {
 342  
                 // workaround for existing calls which don't bother checking for null return values
 343  0
                 regex = Pattern.compile(".*");
 344  
             }
 345  
         }
 346  
 
 347  0
         return regex;
 348  
     }
 349  
     
 350  
         protected Class<? extends Formatter> getAttributeFormatter(AttributeDefinition definition) {
 351  0
         Class<? extends Formatter> formatterClass = null;
 352  0
         if (definition != null && definition.hasFormatterClass()) {
 353  
                         try {
 354  0
                                 formatterClass = ClassLoaderUtils.getClass(definition.getFormatterClass(), Formatter.class);
 355  0
                         } catch (ClassNotFoundException e) {
 356  
                                 // supressing the ClassNotFoundException here to keep in consistent in the logic which is
 357  
                                 // calling this code (it's doing a null check) though it really seems like we should
 358  
                                 // be rethrowing this as a RuntimeException!  Either way, will log a WARN here.
 359  0
                                 LOG.warn("Failed to resolve formatter class: " + definition.getFormatterClass(), e);
 360  0
                         }
 361  
         }
 362  0
         return formatterClass;
 363  
     }
 364  
     
 365  
         protected String getAttributeValidatingErrorMessageKey(AttributeDefinition definition) {
 366  0
         if (definition != null) {
 367  0
                 if (definition.hasValidationPattern()) {
 368  0
                         ValidationPattern validationPattern = definition.getValidationPattern();
 369  0
                         return validationPattern.getValidationErrorMessageKey();
 370  
                 }
 371  
         }
 372  0
         return null;
 373  
         }
 374  
         
 375  
         protected List<String> getAttributeValidatingErrorMessageParameters(AttributeDefinition definition) {
 376  0
         if (definition != null) {
 377  0
                 if (definition.hasValidationPattern()) {
 378  0
                         ValidationPattern validationPattern = definition.getValidationPattern();
 379  0
                         String attributeLabel = getAttributeErrorLabel(definition);
 380  0
                         return Arrays.asList(validationPattern.getValidationErrorMessageParameters(attributeLabel));
 381  
                 }
 382  
         }
 383  0
         return Collections.emptyList();
 384  
         }
 385  
     
 386  
         protected String getAttributeExclusiveMin(AttributeDefinition definition) {
 387  0
         return definition == null ? null : definition.getExclusiveMin();
 388  
     }
 389  
 
 390  
         protected String getAttributeInclusiveMax(AttributeDefinition definition) {
 391  0
         return definition == null ? null : definition.getInclusiveMax();
 392  
     }
 393  
         
 394  
     protected List<RemotableAttributeError> validateAttributeFormat(String kimTypeId, String objectClassName, String attributeName, String attributeValue, String errorKey) {
 395  0
             List<RemotableAttributeError> errors = new ArrayList<RemotableAttributeError>();
 396  
 
 397  0
         AttributeDefinitionMap attributeDefinitions = getAttributeDefinitions(kimTypeId);
 398  0
             AttributeDefinition definition = attributeDefinitions.getByAttributeName(attributeName);
 399  
             
 400  0
         String errorLabel = getAttributeErrorLabel(definition);
 401  
 
 402  0
         if ( LOG.isDebugEnabled() ) {
 403  0
                 LOG.debug("(bo, attributeName, attributeValue) = (" + objectClassName + "," + attributeName + "," + attributeValue + ")");
 404  
         }
 405  
 
 406  0
         if (StringUtils.isNotBlank(attributeValue)) {
 407  0
             Integer maxLength = definition.getMaxLength();
 408  0
             if ((maxLength != null) && (maxLength.intValue() < attributeValue.length())) {
 409  0
                 errors.add(RemotableAttributeError.Builder.create(errorKey, createErrorString(RiceKeyConstants.ERROR_MAX_LENGTH, errorLabel, maxLength.toString())).build());
 410  0
                 return errors;
 411  
             }
 412  0
             Pattern validationExpression = getAttributeValidatingExpression(definition);
 413  0
             if (validationExpression != null && !".*".equals(validationExpression.pattern())) {
 414  0
                     if ( LOG.isDebugEnabled() ) {
 415  0
                             LOG.debug("(bo, attributeName, validationExpression) = (" + objectClassName + "," + attributeName + "," + validationExpression + ")");
 416  
                     }
 417  
 
 418  0
                 if (!validationExpression.matcher(attributeValue).matches()) {
 419  0
                     boolean isError=true;
 420  
                     // Calling formatter class
 421  0
                     Class<?> formatterClass=getAttributeFormatter(definition);
 422  0
                     if (formatterClass != null) {
 423  
                         try {
 424  0
                             Method validatorMethod=formatterClass.getDeclaredMethod(
 425  
                                     VALIDATE_METHOD, String.class);
 426  0
                             Object o=validatorMethod.invoke(
 427  
                                     formatterClass.newInstance(), attributeValue);
 428  0
                             if (o instanceof Boolean) {
 429  0
                                 isError=!((Boolean)o).booleanValue();
 430  
                             }
 431  0
                         } catch (Exception e) {
 432  0
                             LOG.warn(e.getMessage(), e);
 433  0
                         }
 434  
                     }
 435  0
                     if (isError) {
 436  0
                             String errorMessageKey = getAttributeValidatingErrorMessageKey(definition);
 437  0
                             List<String> errorMessageParameters = getAttributeValidatingErrorMessageParameters(definition);
 438  0
                         errors.add(RemotableAttributeError.Builder.create(errorKey, createErrorString(errorMessageKey, errorMessageParameters.toArray(new String[] {}))).build());
 439  
                     }
 440  0
                     return errors;
 441  
                 }
 442  
             }
 443  0
             String exclusiveMin = getAttributeExclusiveMin(definition);
 444  0
             if (StringUtils.isNotBlank(exclusiveMin)) {
 445  
                 try {
 446  0
                         BigDecimal exclusiveMinBigDecimal = new BigDecimal(exclusiveMin);
 447  0
                     if (exclusiveMinBigDecimal.compareTo(new BigDecimal(attributeValue)) >= 0) {
 448  0
                         errors.add(RemotableAttributeError.Builder.create(errorKey, createErrorString(RiceKeyConstants.ERROR_EXCLUSIVE_MIN, errorLabel, exclusiveMin.toString())).build());
 449  0
                         return errors;
 450  
                     }
 451  
                 }
 452  0
                 catch (NumberFormatException e) {
 453  
                     // quash; this indicates that the DD contained a min for a non-numeric attribute
 454  0
                 }
 455  
             }
 456  0
             String inclusiveMax = getAttributeInclusiveMax(definition);
 457  0
             if (StringUtils.isNotBlank(inclusiveMax)) {
 458  
                 try {
 459  0
                         BigDecimal inclusiveMaxBigDecimal = new BigDecimal(inclusiveMax);
 460  0
                     if (inclusiveMaxBigDecimal.compareTo(new BigDecimal(attributeValue)) < 0) {
 461  0
                         errors.add(RemotableAttributeError.Builder.create(errorKey, createErrorString(RiceKeyConstants.ERROR_INCLUSIVE_MAX, errorLabel, inclusiveMax.toString())).build());
 462  0
                         return errors;
 463  
                     }
 464  
                 }
 465  0
                 catch (NumberFormatException e) {
 466  
                     // quash; this indicates that the DD contained a max for a non-numeric attribute
 467  0
                 }
 468  
             }
 469  
         }
 470  0
         return errors;
 471  
     }
 472  
 
 473  
     /** will create a string like the following:
 474  
      * errorKey:param1;param2;param3;
 475  
      *
 476  
      * @param errorKey the errorKey
 477  
      * @param params the error params
 478  
      * @return error string
 479  
      */
 480  
     private static String createErrorString(String errorKey, String... params) {
 481  0
         final StringBuilder s = new StringBuilder(errorKey).append(':');
 482  0
         if (params != null) {
 483  0
             for (String p : params) {
 484  0
                 if (p != null) {
 485  0
                     s.append(p);
 486  0
                     s.append(';');
 487  
                 }
 488  
             }
 489  
         }
 490  0
         return s.toString();
 491  
     }
 492  
 
 493  
     /*
 494  
      * will create a list of errors in the following format:
 495  
      *
 496  
      *
 497  
      * error_key:param1;param2;param3;
 498  
      */
 499  
         protected List<String> extractErrorsFromGlobalVariablesErrorMap(String attributeName) {
 500  0
                 Object results = GlobalVariables.getMessageMap().getErrorMessagesForProperty(attributeName);
 501  0
                 List<String> errors = new ArrayList<String>();
 502  0
         if (results instanceof String) {
 503  0
                 errors.add((String)results);
 504  0
         } else if ( results != null) {
 505  0
                 if (results instanceof List) {
 506  0
                         List<?> errorList = (List<?>)results;
 507  0
                         for (Object msg : errorList) {
 508  0
                                 ErrorMessage errorMessage = (ErrorMessage)msg;
 509  0
                                 errors.add(createErrorString(errorMessage.getErrorKey(), errorMessage.getMessageParameters()));
 510  0
                                 }
 511  0
                 } else {
 512  0
                         String [] temp = (String []) results;
 513  0
                         for (String string : temp) {
 514  0
                                         errors.add(string);
 515  
                                 }
 516  
                 }
 517  
         }
 518  0
         GlobalVariables.getMessageMap().removeAllErrorMessagesForProperty(attributeName);
 519  0
         return errors;
 520  
         }
 521  
 
 522  
         protected List<RemotableAttributeError> validateNonDataDictionaryAttribute(KimTypeAttribute attr, String key, String value) {
 523  0
                 return Collections.emptyList();
 524  
         }
 525  
 
 526  
     protected List<RemotableAttributeError> validateDataDictionaryAttribute(KimTypeAttribute attr, String key, String value) {
 527  
                 try {
 528  
             // create an object of the proper type per the component
 529  0
             Object componentObject = Class.forName( attr.getKimAttribute().getComponentName() ).newInstance();
 530  
 
 531  0
             if ( attr.getKimAttribute().getAttributeName() != null ) {
 532  
                 // get the bean utils descriptor for accessing the attribute on that object
 533  0
                 PropertyDescriptor propertyDescriptor = PropertyUtils.getPropertyDescriptor(componentObject, attr.getKimAttribute().getAttributeName());
 534  0
                 if ( propertyDescriptor != null ) {
 535  
                     // set the value on the object so that it can be checked
 536  0
                     Object attributeValue = getAttributeValue(propertyDescriptor, value);
 537  0
                     propertyDescriptor.getWriteMethod().invoke( componentObject, attributeValue);
 538  0
                     return validateDataDictionaryAttribute(attr.getKimTypeId(), attr.getKimAttribute().getComponentName(), componentObject, propertyDescriptor);
 539  
                 }
 540  
             }
 541  0
         } catch (Exception e) {
 542  0
             throw new KimTypeAttributeValidationException(e);
 543  0
         }
 544  0
         return Collections.emptyList();
 545  
         }
 546  
 
 547  
 
 548  
         /**
 549  
          * @param namespaceCode
 550  
          * @param typeAttribute
 551  
          * @return an AttributeDefinition for the given KimTypeAttribute, or null no base AttributeDefinition 
 552  
          * matches the typeAttribute parameter's attributeName.
 553  
          */
 554  
         @SuppressWarnings("unchecked")
 555  
         protected AttributeDefinition getDataDictionaryAttributeDefinition( String namespaceCode, String kimTypeId, KimTypeAttribute typeAttribute) {
 556  
                 // TODO: this method looks like it could use some refactoring
 557  0
                 KimDataDictionaryAttributeDefinition definition = null;
 558  0
                 String componentClassName = typeAttribute.getKimAttribute().getComponentName();
 559  0
                 String attributeName = typeAttribute.getKimAttribute().getAttributeName();
 560  0
                 AttributeDefinition baseDefinition = null;
 561  
                 
 562  
                 // try to resolve the component name - if not possible - try to pull the definition from the app mediation service
 563  
                 try {
 564  0
                         Class.forName(componentClassName);
 565  
                         try {
 566  0
                                 baseDefinition = getDataDictionaryService().getDataDictionary().getBusinessObjectEntry(componentClassName).getAttributeDefinition(attributeName);
 567  0
                         } catch ( Exception ex ) {
 568  0
                                 LOG.error( "Unable to get base DD definition for " + componentClassName + "." + attributeName, ex );
 569  0
                                 return definition;
 570  0
                         }
 571  0
                 } catch (ClassNotFoundException ex) {
 572  0
                         if ( LOG.isDebugEnabled() ) {
 573  0
                                 LOG.debug( "Unable to find class " + componentClassName + " in available classloaders. Deferring to the service bus." );
 574  
                         }
 575  0
                         baseDefinition = KRADServiceLocatorWeb.getRiceApplicationConfigurationMediationService().getBusinessObjectAttributeDefinition(componentClassName, attributeName);
 576  0
                 }
 577  
                 
 578  0
                 if (baseDefinition != null) {
 579  
 
 580  0
                         definition = new KimDataDictionaryAttributeDefinition();
 581  
                         // copy all the base attributes
 582  0
                         definition.setName( baseDefinition.getName() );
 583  0
                         definition.setLabel( baseDefinition.getLabel() );
 584  0
                         definition.setShortLabel( baseDefinition.getShortLabel() );
 585  0
                         definition.setMaxLength( baseDefinition.getMaxLength() );
 586  0
                         definition.setRequired( baseDefinition.isRequired() );
 587  
 
 588  0
                         if (baseDefinition.getFormatterClass() != null) {
 589  0
                                 definition.setFormatterClass(baseDefinition.getFormatterClass());
 590  
                         }
 591  0
                         ControlDefinition control = copy(baseDefinition.getControl());
 592  0
                         if ( StringUtils.isNotBlank( control.getValuesFinderClass() ) ) {
 593  
 //                                try {
 594  
 //                                        Class.forName(control.getValuesFinderClass());
 595  
 //                                } catch ( ClassNotFoundException ex ) {
 596  
                                         // not found locally, add the KimAttributeValuesFinder as a proxy
 597  0
                                         control.setValuesFinderClass(KimAttributeValuesFinder.class.getName());
 598  
 //                                }
 599  
                         }
 600  0
                         definition.setControl(control);
 601  0
                         definition.setSortCode(typeAttribute.getSortCode());
 602  0
                         definition.setKimAttrDefnId(typeAttribute.getKimAttribute().getId());
 603  0
                         definition.setKimTypeId(kimTypeId);
 604  
 
 605  0
                         definition.setForceUppercase(baseDefinition.getForceUppercase());
 606  
                         
 607  0
                         Map<String, String> lookupInputPropertyConversionsMap = new HashMap<String, String>();
 608  0
                         Map<String, String> lookupReturnPropertyConversionsMap = new HashMap<String, String>();
 609  
 
 610  
                         try {
 611  0
                                 Class<? extends BusinessObject> componentClass = (Class<? extends BusinessObject>)Class.forName(componentClassName);
 612  0
                                 BusinessObject sampleComponent = componentClass.newInstance();
 613  0
                                 List<String> displayedFieldNames = new ArrayList<String>( 1 );
 614  0
                                 displayedFieldNames.add( attributeName );
 615  0
                                 Field field = FieldUtils.getPropertyField(componentClass, attributeName, false);
 616  0
                                 if ( field != null ) {
 617  0
                                         field = LookupUtils.setFieldQuickfinder( sampleComponent, attributeName, field, displayedFieldNames );
 618  0
                                         if ( StringUtils.isNotBlank( field.getQuickFinderClassNameImpl() ) ) {
 619  0
                                                 Class<? extends BusinessObject> lookupClass = (Class<? extends BusinessObject>)Class.forName( field.getQuickFinderClassNameImpl() );
 620  0
                                                 definition.setLookupBoClass( lookupClass.getName() );
 621  0
                                                 if ( field.getLookupParameters() != null ) {
 622  0
                                                         String [] lookupInputPropertyConversions = field.getLookupParameters().split(",");
 623  0
                                                         for (String string : lookupInputPropertyConversions) {
 624  0
                                                                 String [] keyVal = string.split(":");
 625  0
                                                                 lookupInputPropertyConversionsMap.put(keyVal[0], keyVal[1]);
 626  
                                                         }
 627  0
                                                         definition.setLookupInputPropertyConversions(lookupInputPropertyConversionsMap);
 628  
                                                 }
 629  0
                                                 if ( field.getFieldConversions() != null ) {
 630  0
                                                         String [] lookupReturnPropertyConversions = field.getFieldConversions().split(",");
 631  0
                                                         for (String string : lookupReturnPropertyConversions) {
 632  0
                                                                 String [] keyVal = string.split(":");
 633  0
                                                                 lookupReturnPropertyConversionsMap.put(keyVal[0], keyVal[1]);
 634  
                                                         }
 635  0
                                                         definition.setLookupReturnPropertyConversions(lookupReturnPropertyConversionsMap);
 636  
                                                 }
 637  
                                         }
 638  
                                 }
 639  0
                         } catch (Exception e) {
 640  0
                                 LOG.warn("Unable to get DD data for: " + typeAttribute, e);
 641  0
                         }
 642  
                 }
 643  0
                 return definition;
 644  
         }
 645  
 
 646  
         @SuppressWarnings("unchecked")
 647  
         private <T> T copy(final T original) {
 648  0
                 if ( original == null ) {
 649  0
                         return null;
 650  
                 }
 651  0
                 T copy = null;
 652  
                 try {
 653  0
                         copy = (T) original.getClass().newInstance();
 654  0
                         Class copyClass = copy.getClass();
 655  
                     do {
 656  0
                             for (java.lang.reflect.Field copyField : copyClass.getDeclaredFields()) {
 657  0
                                     copyField.setAccessible(true);
 658  0
                                     int mods = copyField.getModifiers();
 659  0
                                     if (!Modifier.isFinal(mods) && !Modifier.isStatic(mods)) {
 660  0
                                             copyField.set(copy, copyField.get(original));
 661  
                                     }
 662  
                             }
 663  0
                             copyClass = copyClass.getSuperclass();
 664  0
                     } while (copyClass != null && !(copyClass.equals(Object.class)));
 665  0
                 } catch (Exception e) {
 666  0
                         LOG.error("Unable to copy " + original, e);
 667  0
                 }
 668  0
                 return copy;
 669  
         }
 670  
 
 671  
 
 672  
 
 673  
         protected AttributeDefinition getNonDataDictionaryAttributeDefinition(KimTypeAttribute typeAttribute) {
 674  0
                 KimAttributeDefinition definition = new KimAttributeDefinition();
 675  0
                 definition.setName(typeAttribute.getKimAttribute().getAttributeName());
 676  0
                 definition.setLabel(typeAttribute.getKimAttribute().getAttributeLabel());
 677  0
                 definition.setSortCode(typeAttribute.getSortCode());
 678  0
                 definition.setKimAttrDefnId(typeAttribute.getKimAttribute().getId());
 679  0
                 return definition;
 680  
         }
 681  
 
 682  
 
 683  
 
 684  0
         protected final String COMMA_SEPARATOR = ", ";
 685  
 
 686  
         protected void validateRequiredAttributesAgainstReceived(Map<String, String> receivedAttributes){
 687  
                 // abort if type does not want the qualifiers to be checked
 688  0
                 if ( !isCheckRequiredAttributes() ) {
 689  0
                         return;
 690  
                 }
 691  
                 // abort if the list is empty, no attributes need to be checked
 692  0
                 if ( requiredAttributes == null || requiredAttributes.isEmpty() ) {
 693  0
                         return;
 694  
                 }
 695  0
                 List<String> missingAttributes = new ArrayList<String>();
 696  
                 // if attributes are null or empty, they're all missing
 697  0
                 if ( receivedAttributes == null || receivedAttributes.isEmpty() ) {
 698  0
                         return;                
 699  
                 } else {
 700  0
                         for( String requiredAttribute : requiredAttributes ) {
 701  0
                                 if( !receivedAttributes.containsKey(requiredAttribute) ) {
 702  0
                                         missingAttributes.add(requiredAttribute);
 703  
                                 }
 704  
                         }
 705  
                 }
 706  0
         if(!missingAttributes.isEmpty()) {
 707  0
                 StringBuilder errorMessage = new StringBuilder();
 708  0
                 Iterator<String> attribIter = missingAttributes.iterator();
 709  0
                 while ( attribIter.hasNext() ) {
 710  0
                         errorMessage.append( attribIter.next() );
 711  0
                         if( attribIter.hasNext() ) {
 712  0
                                 errorMessage.append( COMMA_SEPARATOR );
 713  
                         }
 714  
                 }
 715  0
                 errorMessage.append( " not found in required attributes for this type." );
 716  0
             throw new KimTypeAttributeValidationException(errorMessage.toString());
 717  
         }
 718  0
         }
 719  
 
 720  
 
 721  
         
 722  
         protected List<RemotableAttributeError> validateUniqueAttributes(String kimTypeId, Map<String, String> newAttributes, Map<String, String> oldAttributes) {
 723  0
                 List<String> uniqueAttributes = getUniqueAttributes(kimTypeId);
 724  0
                 if(uniqueAttributes==null || uniqueAttributes.isEmpty()){
 725  0
                         return Collections.emptyList();
 726  
                 } else{
 727  0
                         List<RemotableAttributeError> m = new ArrayList<RemotableAttributeError>();
 728  0
             if(areAttributesEqual(uniqueAttributes, newAttributes, oldAttributes)){
 729  
                                 //add all unique attrs to error map
 730  0
                 for (String a : uniqueAttributes) {
 731  0
                     m.add(RemotableAttributeError.Builder.create(a, RiceKeyConstants.ERROR_DUPLICATE_ENTRY).build());
 732  
                 }
 733  
 
 734  0
                 return m;
 735  
                         }
 736  
                 }
 737  0
                 return Collections.emptyList();
 738  
         }
 739  
         
 740  
         protected boolean areAttributesEqual(List<String> uniqueAttributeNames, Map<String, String> aSet1, Map<String, String> aSet2){
 741  0
                 StringValueComparator comparator = StringValueComparator.getInstance();
 742  0
                 for(String uniqueAttributeName: uniqueAttributeNames){
 743  0
                         String attrVal1 = getAttributeValue(aSet1, uniqueAttributeName);
 744  0
                         String attrVal2 = getAttributeValue(aSet2, uniqueAttributeName);
 745  0
                         if(comparator.compare(attrVal1, attrVal2)!=0){
 746  0
                                 return false;
 747  
                         }
 748  0
                 }
 749  0
                 return true;
 750  
         }
 751  
 
 752  
         protected String getAttributeValue(Map<String, String> aSet, String attributeName){
 753  0
                 if(StringUtils.isEmpty(attributeName)) {
 754  0
                         return null;
 755  
                 }
 756  0
                 for(Map.Entry<String, String> entry : aSet.entrySet()){
 757  0
                         if(attributeName.equals(entry.getKey())) {
 758  0
                                 return entry.getValue();
 759  
                         }
 760  
                 }
 761  0
                 return null;
 762  
         }
 763  
 
 764  
         protected List<String> getUniqueAttributes(String kimTypeId){
 765  0
                 KimType kimType = getTypeInfoService().getKimType(kimTypeId);
 766  0
         List<String> uniqueAttributes = new ArrayList<String>();
 767  0
         if ( kimType != null ) {
 768  0
                 for(KimTypeAttribute attributeDefinition: kimType.getAttributeDefinitions()){
 769  0
                         uniqueAttributes.add(attributeDefinition.getKimAttribute().getAttributeName());
 770  
                 }
 771  
         } else {
 772  0
                 LOG.error("Unable to retrieve a KimTypeInfo for a null kimTypeId in getUniqueAttributes()");
 773  
         }
 774  0
         return Collections.unmodifiableList(uniqueAttributes);
 775  
         }
 776  
 
 777  
         protected List<RemotableAttributeError> validateUnmodifiableAttributes(String kimTypeId, Map<String, String> originalAttributes, Map<String, String> newAttributes){
 778  0
                 List<RemotableAttributeError> validationErrors = new ArrayList<RemotableAttributeError>();
 779  0
                 KimType kimType = getTypeInfoService().getKimType(kimTypeId);
 780  0
                 List<String> uniqueAttributes = getUniqueAttributes(kimTypeId);
 781  0
                 for(String attributeNameKey: uniqueAttributes){
 782  0
                         KimTypeAttribute attr = kimType.getAttributeDefinitionByName(attributeNameKey);
 783  0
                         String mainAttributeValue = getAttributeValue(originalAttributes, attributeNameKey);
 784  0
                         String delegationAttributeValue = getAttributeValue(newAttributes, attributeNameKey);
 785  
 
 786  0
                         if(!StringUtils.equals(mainAttributeValue, delegationAttributeValue)){
 787  0
                                 validationErrors.add(RemotableAttributeError.Builder.create(attributeNameKey, createErrorString(RiceKeyConstants.ERROR_CANT_BE_MODIFIED,
 788  
                                         dataDictionaryService.getAttributeLabel(attr.getKimAttribute().getComponentName(), attributeNameKey))).build());
 789  
                         }
 790  0
                 }
 791  0
                 return validationErrors;
 792  
         }
 793  
 
 794  
         protected boolean isCheckRequiredAttributes() {
 795  0
                 return this.checkRequiredAttributes;
 796  
         }
 797  
 
 798  
         protected void setCheckRequiredAttributes(boolean checkRequiredAttributes) {
 799  0
                 this.checkRequiredAttributes = checkRequiredAttributes;
 800  0
         }
 801  
 
 802  
         protected String getClosestParentDocumentTypeName(
 803  
                         DocumentType documentType,
 804  
                         Set<String> potentialParentDocumentTypeNames) {
 805  0
                 if ( potentialParentDocumentTypeNames == null || documentType == null ) {
 806  0
                         return null;
 807  
                 }
 808  0
                 if (potentialParentDocumentTypeNames.contains(documentType.getName())) {
 809  0
                         return documentType.getName();
 810  
                 } 
 811  0
                 if ((documentType.getDocTypeParentId() == null)
 812  
                                 || documentType.getDocTypeParentId().equals(
 813  
                                                 documentType.getDocumentTypeId())) {
 814  0
                         return null;
 815  
                 } 
 816  0
                 return getClosestParentDocumentTypeName(documentType
 817  
                                 .getParentDocType(), potentialParentDocumentTypeNames);
 818  
         }
 819  
 
 820  0
     protected static class KimTypeAttributeValidationException extends RuntimeException {
 821  
 
 822  
         protected KimTypeAttributeValidationException(String message) {
 823  0
             super( message );
 824  0
         }
 825  
 
 826  
         protected KimTypeAttributeValidationException(Throwable cause) {
 827  0
             super( cause );
 828  0
         }
 829  
 
 830  
         private static final long serialVersionUID = 8220618846321607801L;
 831  
 
 832  
     }
 833  
 
 834  
     protected KimTypeInfoService getTypeInfoService() {
 835  0
                 if ( typeInfoService == null ) {
 836  0
                         typeInfoService = KimApiServiceLocator.getKimTypeInfoService();
 837  
                 }
 838  0
                 return typeInfoService;
 839  
         }
 840  
 
 841  
         protected BusinessObjectService getBusinessObjectService() {
 842  0
                 if ( businessObjectService == null ) {
 843  0
                         businessObjectService = KRADServiceLocator.getBusinessObjectService();
 844  
                 }
 845  0
                 return businessObjectService;
 846  
         }
 847  
 
 848  
         protected DictionaryValidationService getDictionaryValidationService() {
 849  0
                 if ( dictionaryValidationService == null ) {
 850  0
                         dictionaryValidationService = KRADServiceLocatorWeb.getDictionaryValidationService();
 851  
                 }
 852  0
                 return dictionaryValidationService;
 853  
         }
 854  
 
 855  
         protected DataDictionaryService getDataDictionaryService() {
 856  0
                 if ( dataDictionaryService == null ) {
 857  0
                         dataDictionaryService = KRADServiceLocatorWeb.getDataDictionaryService();
 858  
                 }
 859  0
                 return this.dataDictionaryService;
 860  
         }
 861  
 }