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