Coverage Report - org.kuali.rice.kns.kim.type.DataDictionaryTypeServiceBase
 
Classes in this File Line Coverage Branch Coverage Complexity
DataDictionaryTypeServiceBase
0%
0/341
0%
0/232
4.614
DataDictionaryTypeServiceBase$1
0%
0/2
N/A
4.614
DataDictionaryTypeServiceBase$2
0%
0/2
N/A
4.614
DataDictionaryTypeServiceBase$KimTypeAttributeException
0%
0/4
N/A
4.614
DataDictionaryTypeServiceBase$KimTypeAttributeValidationException
0%
0/4
N/A
4.614
 
 1  
 /**
 2  
  * Copyright 2005-2011 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.kns.kim.type;
 17  
 
 18  
 import com.google.common.base.Function;
 19  
 import com.google.common.collect.Lists;
 20  
 import org.apache.commons.beanutils.PropertyUtils;
 21  
 import org.apache.commons.collections.CollectionUtils;
 22  
 import org.apache.commons.lang.StringUtils;
 23  
 import org.kuali.rice.core.api.exception.RiceIllegalArgumentException;
 24  
 import org.kuali.rice.core.api.uif.RemotableAbstractWidget;
 25  
 import org.kuali.rice.core.api.uif.RemotableAttributeError;
 26  
 import org.kuali.rice.core.api.uif.RemotableAttributeField;
 27  
 import org.kuali.rice.core.api.uif.RemotableQuickFinder;
 28  
 import org.kuali.rice.core.api.util.RiceKeyConstants;
 29  
 import org.kuali.rice.core.api.util.type.TypeUtils;
 30  
 import org.kuali.rice.core.web.format.Formatter;
 31  
 import org.kuali.rice.kew.api.KewApiServiceLocator;
 32  
 import org.kuali.rice.kew.api.doctype.DocumentType;
 33  
 import org.kuali.rice.kew.api.doctype.DocumentTypeService;
 34  
 import org.kuali.rice.kim.api.services.KimApiServiceLocator;
 35  
 import org.kuali.rice.kim.api.type.KimAttributeField;
 36  
 import org.kuali.rice.kim.api.type.KimType;
 37  
 import org.kuali.rice.kim.api.type.KimTypeAttribute;
 38  
 import org.kuali.rice.kim.api.type.KimTypeInfoService;
 39  
 import org.kuali.rice.kim.framework.type.KimTypeService;
 40  
 import org.kuali.rice.kns.lookup.LookupUtils;
 41  
 import org.kuali.rice.kns.util.FieldUtils;
 42  
 import org.kuali.rice.kns.web.ui.Field;
 43  
 import org.kuali.rice.krad.bo.BusinessObject;
 44  
 import org.kuali.rice.krad.comparator.StringValueComparator;
 45  
 import org.kuali.rice.krad.datadictionary.AttributeDefinition;
 46  
 import org.kuali.rice.krad.datadictionary.PrimitiveAttributeDefinition;
 47  
 import org.kuali.rice.krad.datadictionary.RelationshipDefinition;
 48  
 import org.kuali.rice.krad.service.BusinessObjectService;
 49  
 import org.kuali.rice.krad.service.DataDictionaryService;
 50  
 import org.kuali.rice.krad.service.DictionaryValidationService;
 51  
 import org.kuali.rice.krad.service.KRADServiceLocator;
 52  
 import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
 53  
 import org.kuali.rice.krad.util.ErrorMessage;
 54  
 import org.kuali.rice.krad.util.GlobalVariables;
 55  
 import org.kuali.rice.krad.util.KRADUtils;
 56  
 import org.kuali.rice.krad.util.ObjectUtils;
 57  
 
 58  
 import java.beans.PropertyDescriptor;
 59  
 import java.util.AbstractMap;
 60  
 import java.util.ArrayList;
 61  
 import java.util.Collections;
 62  
 import java.util.Comparator;
 63  
 import java.util.HashMap;
 64  
 import java.util.Iterator;
 65  
 import java.util.List;
 66  
 import java.util.Map;
 67  
 import java.util.Set;
 68  
 import java.util.regex.Pattern;
 69  
 
 70  
 /**
 71  
  * @deprecated A krad integrated type service base class will be provided in the future.
 72  
  */
 73  
 @Deprecated
 74  0
 public class DataDictionaryTypeServiceBase implements KimTypeService {
 75  
 
 76  0
         private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(DataDictionaryTypeServiceBase.class);
 77  
     private static final String ANY_CHAR_PATTERN_S = ".*";
 78  0
     private static final Pattern ANY_CHAR_PATTERN = Pattern.compile(ANY_CHAR_PATTERN_S);
 79  
 
 80  
         private BusinessObjectService businessObjectService;
 81  
         private DictionaryValidationService dictionaryValidationService;
 82  
         private DataDictionaryService dataDictionaryService;
 83  
         private KimTypeInfoService typeInfoService;
 84  
     private DocumentTypeService documentTypeService;
 85  
 
 86  
         @Override
 87  
         public String getWorkflowDocumentTypeName() {
 88  0
                 return null;
 89  
         }
 90  
 
 91  
         @Override
 92  
         public List<String> getWorkflowRoutingAttributes(String routeLevel) {
 93  0
                 if (StringUtils.isBlank(routeLevel)) {
 94  0
             throw new RiceIllegalArgumentException("routeLevel was blank or null");
 95  
         }
 96  
 
 97  0
         return Collections.emptyList();
 98  
         }
 99  
 
 100  
     @Override
 101  
         public List<KimAttributeField> getAttributeDefinitions(String kimTypeId) {
 102  0
         final List<String> uniqueAttributes = getUniqueAttributes(kimTypeId);
 103  
 
 104  
         //using map.entry as a 2-item tuple
 105  0
         final List<Map.Entry<String,KimAttributeField>> definitions = new ArrayList<Map.Entry<String,KimAttributeField>>();
 106  0
         final KimType kimType = getTypeInfoService().getKimType(kimTypeId);
 107  0
         final String nsCode = kimType.getNamespaceCode();
 108  
 
 109  0
         for (KimTypeAttribute typeAttribute : kimType.getAttributeDefinitions()) {
 110  
             final KimAttributeField definition;
 111  0
             if (typeAttribute.getKimAttribute().getComponentName() == null) {
 112  0
                 definition = getNonDataDictionaryAttributeDefinition(nsCode,kimTypeId,typeAttribute, uniqueAttributes);
 113  
             } else {
 114  0
                 definition = getDataDictionaryAttributeDefinition(nsCode,kimTypeId,typeAttribute, uniqueAttributes);
 115  
             }
 116  
 
 117  0
             if (definition != null) {
 118  0
                 definitions.add(new AbstractMap.SimpleEntry<String,KimAttributeField>(typeAttribute.getSortCode() != null ? typeAttribute.getSortCode() : "", definition));
 119  
             }
 120  0
         }
 121  
 
 122  
         //sort by sortCode
 123  0
         Collections.sort(definitions, new Comparator<Map.Entry<String, KimAttributeField>>() {
 124  
             @Override
 125  
             public int compare(Map.Entry<String, KimAttributeField> o1, Map.Entry<String, KimAttributeField> o2) {
 126  0
                 return o1.getKey().compareTo(o2.getKey());
 127  
             }
 128  
         });
 129  
 
 130  
         //transform removing sortCode
 131  0
                 return Collections.unmodifiableList(Lists.transform(definitions, new Function<Map.Entry<String, KimAttributeField>, KimAttributeField>() {
 132  
             @Override
 133  
             public KimAttributeField apply(Map.Entry<String, KimAttributeField> v) {
 134  0
                 return v.getValue();
 135  
             }
 136  
         }));
 137  
         }
 138  
 
 139  
     /**
 140  
          * This is the default implementation.  It calls into the service for each attribute to
 141  
          * validate it there.  No combination validation is done.  That should be done
 142  
          * by overriding this method.
 143  
          */
 144  
         @Override
 145  
         public List<RemotableAttributeError> validateAttributes(String kimTypeId, Map<String, String> attributes) {
 146  0
                 if (StringUtils.isBlank(kimTypeId)) {
 147  0
             throw new RiceIllegalArgumentException("kimTypeId was null or blank");
 148  
         }
 149  
 
 150  0
         if (attributes == null) {
 151  0
             throw new RiceIllegalArgumentException("attributes was null or blank");
 152  
         }
 153  
 
 154  0
         final List<RemotableAttributeError> validationErrors = new ArrayList<RemotableAttributeError>();
 155  0
                 KimType kimType = getTypeInfoService().getKimType(kimTypeId);
 156  
 
 157  0
                 for ( Map.Entry<String, String> entry : attributes.entrySet() ) {
 158  0
             KimTypeAttribute attr = kimType.getAttributeDefinitionByName(entry.getKey());
 159  
                         final List<RemotableAttributeError> attributeErrors;
 160  0
             if ( attr.getKimAttribute().getComponentName() == null) {
 161  0
                 attributeErrors = validateNonDataDictionaryAttribute(attr, entry.getKey(), entry.getValue());
 162  
             } else {
 163  0
                 attributeErrors = validateDataDictionaryAttribute(attr, entry.getKey(), entry.getValue());
 164  
             }
 165  
 
 166  0
                         if ( attributeErrors != null ) {
 167  0
                 validationErrors.addAll(attributeErrors);
 168  
                         }
 169  0
                 }
 170  
 
 171  
 
 172  0
                 final List<RemotableAttributeError> referenceCheckErrors = validateReferencesExistAndActive(kimType, attributes, validationErrors);
 173  0
         validationErrors.addAll(referenceCheckErrors);
 174  
 
 175  0
                 return Collections.unmodifiableList(validationErrors);
 176  
         }
 177  
 
 178  
     @Override
 179  
         public List<RemotableAttributeError> validateAttributesAgainstExisting(String kimTypeId, Map<String, String> newAttributes, Map<String, String> oldAttributes){
 180  0
         if (StringUtils.isBlank(kimTypeId)) {
 181  0
             throw new RiceIllegalArgumentException("kimTypeId was null or blank");
 182  
         }
 183  
 
 184  0
         if (newAttributes == null) {
 185  0
             throw new RiceIllegalArgumentException("newAttributes was null or blank");
 186  
         }
 187  
 
 188  0
         if (oldAttributes == null) {
 189  0
             throw new RiceIllegalArgumentException("oldAttributes was null or blank");
 190  
         }
 191  
 
 192  0
         final List<RemotableAttributeError> errors = new ArrayList<RemotableAttributeError>();
 193  0
         errors.addAll(validateUniqueAttributes(kimTypeId, newAttributes, oldAttributes));
 194  0
         errors.addAll(validateUnmodifiableAttributes(kimTypeId, newAttributes, oldAttributes));
 195  0
         return Collections.unmodifiableList(errors);
 196  
         }
 197  
 
 198  
         /**
 199  
          *
 200  
          * This method matches input attribute set entries and standard attribute set entries using literal string match.
 201  
          *
 202  
          */
 203  
         protected boolean performMatch(Map<String, String> inputAttributes, Map<String, String> storedAttributes) {
 204  0
                 if ( storedAttributes == null || inputAttributes == null ) {
 205  0
                         return true;
 206  
                 }
 207  0
                 for ( Map.Entry<String, String> entry : storedAttributes.entrySet() ) {
 208  0
                         if (inputAttributes.containsKey(entry.getKey()) && !StringUtils.equals(inputAttributes.get(entry.getKey()), entry.getValue()) ) {
 209  0
                                 return false;
 210  
                         }
 211  
                 }
 212  0
                 return true;
 213  
         }
 214  
 
 215  
         protected Map<String, String> translateInputAttributes(Map<String, String> qualification){
 216  0
                 return qualification;
 217  
         }
 218  
 
 219  
         private Object getAttributeValue(PropertyDescriptor propertyDescriptor, String attributeValue){
 220  0
                 Object attributeValueObject = null;
 221  0
                 if(propertyDescriptor!=null && attributeValue!=null){
 222  0
                         Class<?> propertyType = propertyDescriptor.getPropertyType();
 223  0
                         if (String.class.equals(propertyType)){
 224  0
                                 attributeValueObject = KRADUtils
 225  
                         .createObject(propertyType, new Class[]{String.class}, new Object[]{attributeValue});
 226  
                         } else {
 227  0
                                 attributeValueObject = attributeValue;
 228  
                         }
 229  
                 }
 230  0
                 return attributeValueObject;
 231  
         }
 232  
         
 233  
         protected List<RemotableAttributeError> validateReferencesExistAndActive( KimType kimType, Map<String, String> attributes, List<RemotableAttributeError> previousValidationErrors) {
 234  0
                 Map<String, BusinessObject> componentClassInstances = new HashMap<String, BusinessObject>();
 235  0
                 List<RemotableAttributeError> errors = new ArrayList<RemotableAttributeError>();
 236  
                 
 237  0
                 for ( String attributeName : attributes.keySet() ) {
 238  0
                         KimTypeAttribute attr = kimType.getAttributeDefinitionByName(attributeName);
 239  
                         
 240  0
                         if (StringUtils.isNotBlank(attr.getKimAttribute().getComponentName())) {
 241  0
                                 if (!componentClassInstances.containsKey(attr.getKimAttribute().getComponentName())) {
 242  
                                         try {
 243  0
                                                 Class<?> componentClass = Class.forName( attr.getKimAttribute().getComponentName() );
 244  0
                                                 if (!BusinessObject.class.isAssignableFrom(componentClass)) {
 245  0
                                                         LOG.warn("Class " + componentClass.getName() + " does not implement BusinessObject.  Unable to perform reference existence and active validation");
 246  0
                                                         continue;
 247  
                                                 }
 248  0
                                                 BusinessObject componentInstance = (BusinessObject) componentClass.newInstance();
 249  0
                                                 componentClassInstances.put(attr.getKimAttribute().getComponentName(), componentInstance);
 250  0
                                         } catch (Exception e) {
 251  0
                                                 LOG.error("Unable to instantiate class for attribute: " + attributeName, e);
 252  0
                                         }
 253  
                                 }
 254  
                         }
 255  0
                 }
 256  
                 
 257  
                 // 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
 258  0
                 for ( Map.Entry<String, String> entry : attributes.entrySet() ) {
 259  0
                         if (!RemotableAttributeError.containsAttribute(entry.getKey(), previousValidationErrors)) {
 260  0
                                 for (Object componentInstance : componentClassInstances.values()) {
 261  
                                         try {
 262  0
                                                 ObjectUtils.setObjectProperty(componentInstance, entry.getKey(), entry.getValue());
 263  0
                                         } catch (NoSuchMethodException e) {
 264  
                                                 // this is expected since not all attributes will be in all components
 265  0
                                         } catch (Exception e) {
 266  0
                                                 LOG.error("Unable to set object property class: " + componentInstance.getClass().getName() + " property: " + entry.getKey(), e);
 267  0
                                         }
 268  
                                 }
 269  
                         }
 270  
                 }
 271  
                 
 272  0
                 for (Map.Entry<String, BusinessObject> entry : componentClassInstances.entrySet()) {
 273  0
                         List<RelationshipDefinition> relationships = getDataDictionaryService().getDataDictionary().getBusinessObjectEntry(entry.getKey()).getRelationships();
 274  0
                         if (relationships == null) {
 275  0
                                 continue;
 276  
                         }
 277  
                         
 278  0
                         for (RelationshipDefinition relationshipDefinition : relationships) {
 279  0
                                 List<PrimitiveAttributeDefinition> primitiveAttributes = relationshipDefinition.getPrimitiveAttributes();
 280  
                                 
 281  
                                 // this code assumes that the last defined primitiveAttribute is the attributeToHighlightOnFail
 282  0
                                 String attributeToHighlightOnFail = primitiveAttributes.get(primitiveAttributes.size() - 1).getSourceName();
 283  
                                 
 284  
                                 // TODO: will this work for user ID attributes?
 285  
                                 
 286  0
                                 if (!attributes.containsKey(attributeToHighlightOnFail)) {
 287  
                                         // if the attribute to highlight wasn't passed in, don't bother validating
 288  0
                                         continue;
 289  
                                 }
 290  
                                 
 291  
 
 292  0
                                 KimTypeAttribute attr = kimType.getAttributeDefinitionByName(attributeToHighlightOnFail);
 293  0
                                 if (attr != null) {
 294  
                                         final String attributeDisplayLabel;
 295  0
                     if (StringUtils.isNotBlank(attr.getKimAttribute().getComponentName())) {
 296  0
                                                 attributeDisplayLabel = getDataDictionaryService().getAttributeLabel(attr.getKimAttribute().getComponentName(), attributeToHighlightOnFail);
 297  
                                         } else {
 298  0
                                                 attributeDisplayLabel = attr.getKimAttribute().getAttributeLabel();
 299  
                                         }
 300  
 
 301  0
                                         getDictionaryValidationService().validateReferenceExistsAndIsActive(entry.getValue(), relationshipDefinition.getObjectAttributeName(),
 302  
                                                         attributeToHighlightOnFail, attributeDisplayLabel);
 303  
                                 }
 304  0
                 List<String> extractedErrors = extractErrorsFromGlobalVariablesErrorMap(attributeToHighlightOnFail);
 305  0
                 if (CollectionUtils.isNotEmpty(extractedErrors)) {
 306  0
                                     errors.add(RemotableAttributeError.Builder.create(attributeToHighlightOnFail, extractedErrors).build());
 307  
                 }
 308  0
                         }
 309  0
                 }
 310  0
                 return errors;
 311  
         }
 312  
         
 313  
     protected List<RemotableAttributeError> validateAttributeRequired(String kimTypeId, String objectClassName, String attributeName, Object attributeValue, String errorKey) {
 314  0
         List<RemotableAttributeError> errors = new ArrayList<RemotableAttributeError>();
 315  
         // check if field is a required field for the business object
 316  0
         if (attributeValue == null || (attributeValue instanceof String && StringUtils.isBlank((String) attributeValue))) {
 317  0
                 List<KimAttributeField> map = getAttributeDefinitions(kimTypeId);
 318  0
                 KimAttributeField definition = DataDictionaryTypeServiceHelper.findAttributeField(attributeName, map);
 319  
                 
 320  0
             boolean required = definition.getAttributeField().isRequired();
 321  0
             if (required) {
 322  
                 // get label of attribute for message
 323  0
                 String errorLabel = DataDictionaryTypeServiceHelper.getAttributeErrorLabel(definition);
 324  0
                 errors.add(RemotableAttributeError.Builder.create(errorKey, DataDictionaryTypeServiceHelper
 325  
                         .createErrorString(RiceKeyConstants.ERROR_REQUIRED, errorLabel)).build());
 326  
             }
 327  
         }
 328  0
         return errors;
 329  
     }
 330  
     
 331  
         protected List<RemotableAttributeError> validateDataDictionaryAttribute(String kimTypeId, String entryName, Object object, PropertyDescriptor propertyDescriptor) {
 332  0
                 return validatePrimitiveFromDescriptor(kimTypeId, entryName, object, propertyDescriptor);
 333  
         }
 334  
 
 335  
     protected List<RemotableAttributeError> validatePrimitiveFromDescriptor(String kimTypeId, String entryName, Object object, PropertyDescriptor propertyDescriptor) {
 336  0
         List<RemotableAttributeError> errors = new ArrayList<RemotableAttributeError>();
 337  
         // validate the primitive attributes if defined in the dictionary
 338  0
         if (null != propertyDescriptor && getDataDictionaryService().isAttributeDefined(entryName, propertyDescriptor.getName())) {
 339  0
             Object value = ObjectUtils.getPropertyValue(object, propertyDescriptor.getName());
 340  0
             Class<?> propertyType = propertyDescriptor.getPropertyType();
 341  
 
 342  0
             if (TypeUtils.isStringClass(propertyType) || TypeUtils.isIntegralClass(propertyType) || TypeUtils.isDecimalClass(propertyType) || TypeUtils.isTemporalClass(propertyType)) {
 343  
 
 344  
                 // check value format against dictionary
 345  0
                 if (value != null && StringUtils.isNotBlank(value.toString())) {
 346  0
                     if (!TypeUtils.isTemporalClass(propertyType)) {
 347  0
                         errors.addAll(validateAttributeFormat(kimTypeId, entryName, propertyDescriptor.getName(), value.toString(), propertyDescriptor.getName()));
 348  
                     }
 349  
                 }
 350  
                 else {
 351  
                         // if it's blank, then we check whether the attribute should be required
 352  0
                     errors.addAll(validateAttributeRequired(kimTypeId, entryName, propertyDescriptor.getName(), value, propertyDescriptor.getName()));
 353  
                 }
 354  
             }
 355  
         }
 356  0
         return errors;
 357  
     }
 358  
     
 359  
     protected Pattern getAttributeValidatingExpression(KimAttributeField definition) {
 360  0
         if (definition == null || StringUtils.isBlank(definition.getAttributeField().getRegexConstraint())) {
 361  0
             return ANY_CHAR_PATTERN;
 362  
         }
 363  
 
 364  0
         return Pattern.compile(definition.getAttributeField().getRegexConstraint());
 365  
      }
 366  
     
 367  
         protected Formatter getAttributeFormatter(KimAttributeField definition) {
 368  0
         if (definition.getAttributeField().getDataType() == null) {
 369  0
             return null;
 370  
         }
 371  
 
 372  0
         return Formatter.getFormatter(definition.getAttributeField().getDataType().getType());
 373  
     }
 374  
     
 375  
 
 376  
     
 377  
         protected Double getAttributeMinValue(KimAttributeField definition) {
 378  0
         return definition == null ? null : definition.getAttributeField().getMinValue();
 379  
     }
 380  
 
 381  
         protected Double getAttributeMaxValue(KimAttributeField definition) {
 382  0
         return definition == null ? null : definition.getAttributeField().getMaxValue();
 383  
     }
 384  
         
 385  
     protected List<RemotableAttributeError> validateAttributeFormat(String kimTypeId, String objectClassName, String attributeName, String attributeValue, String errorKey) {
 386  0
             List<RemotableAttributeError> errors = new ArrayList<RemotableAttributeError>();
 387  
 
 388  0
         List<KimAttributeField> attributeDefinitions = getAttributeDefinitions(kimTypeId);
 389  0
             KimAttributeField definition = DataDictionaryTypeServiceHelper.findAttributeField(attributeName,
 390  
                 attributeDefinitions);
 391  
             
 392  0
         String errorLabel = DataDictionaryTypeServiceHelper.getAttributeErrorLabel(definition);
 393  
 
 394  0
         if ( LOG.isDebugEnabled() ) {
 395  0
                 LOG.debug("(bo, attributeName, attributeValue) = (" + objectClassName + "," + attributeName + "," + attributeValue + ")");
 396  
         }
 397  
 
 398  0
         if (StringUtils.isNotBlank(attributeValue)) {
 399  0
             Integer maxLength = definition.getAttributeField().getMaxLength();
 400  0
             if ((maxLength != null) && (maxLength.intValue() < attributeValue.length())) {
 401  0
                 errors.add(RemotableAttributeError.Builder.create(errorKey, DataDictionaryTypeServiceHelper
 402  
                         .createErrorString(RiceKeyConstants.ERROR_MAX_LENGTH, errorLabel, maxLength.toString())).build());
 403  0
                 return errors;
 404  
             }
 405  0
             Pattern validationExpression = getAttributeValidatingExpression(definition);
 406  0
             if (!ANY_CHAR_PATTERN_S.equals(validationExpression.pattern())) {
 407  0
                     if ( LOG.isDebugEnabled() ) {
 408  0
                             LOG.debug("(bo, attributeName, validationExpression) = (" + objectClassName + "," + attributeName + "," + validationExpression + ")");
 409  
                     }
 410  
 
 411  0
                 if (!validationExpression.matcher(attributeValue).matches()) {
 412  0
                     boolean isError=true;
 413  0
                     final Formatter formatter = getAttributeFormatter(definition);
 414  0
                     if (formatter != null) {
 415  0
                         Object o = formatter.format(attributeValue);
 416  0
                         isError = !validationExpression.matcher(String.valueOf(o)).matches();
 417  
                     }
 418  0
                     if (isError) {
 419  0
                         errors.add(RemotableAttributeError.Builder.create(errorKey, DataDictionaryTypeServiceHelper
 420  
                                 .createErrorString(definition)).build());
 421  
                     }
 422  0
                     return errors;
 423  
                 }
 424  
             }
 425  0
             Double min = getAttributeMinValue(definition);
 426  0
             if (min != null) {
 427  
                 try {
 428  0
                     if (Double.parseDouble(attributeValue) < min) {
 429  0
                         errors.add(RemotableAttributeError.Builder.create(errorKey, DataDictionaryTypeServiceHelper
 430  
                                 .createErrorString(RiceKeyConstants.ERROR_INCLUSIVE_MIN, errorLabel, min.toString())).build());
 431  0
                         return errors;
 432  
                     }
 433  
                 }
 434  0
                 catch (NumberFormatException e) {
 435  
                     // quash; this indicates that the DD contained a min for a non-numeric attribute
 436  0
                 }
 437  
             }
 438  0
             Double max = getAttributeMaxValue(definition);
 439  0
             if (max != null) {
 440  
                 try {
 441  
 
 442  0
                     if (Double.parseDouble(attributeValue) > max) {
 443  0
                         errors.add(RemotableAttributeError.Builder.create(errorKey, DataDictionaryTypeServiceHelper
 444  
                                 .createErrorString(RiceKeyConstants.ERROR_INCLUSIVE_MAX, errorLabel, max.toString())).build());
 445  0
                         return errors;
 446  
                     }
 447  
                 }
 448  0
                 catch (NumberFormatException e) {
 449  
                     // quash; this indicates that the DD contained a max for a non-numeric attribute
 450  0
                 }
 451  
             }
 452  
         }
 453  0
         return errors;
 454  
     }
 455  
 
 456  
 
 457  
 
 458  
     /*
 459  
      * will create a list of errors in the following format:
 460  
      *
 461  
      *
 462  
      * error_key:param1;param2;param3;
 463  
      */
 464  
         protected List<String> extractErrorsFromGlobalVariablesErrorMap(String attributeName) {
 465  0
                 Object results = GlobalVariables.getMessageMap().getErrorMessagesForProperty(attributeName);
 466  0
                 List<String> errors = new ArrayList<String>();
 467  0
         if (results instanceof String) {
 468  0
                 errors.add((String)results);
 469  0
         } else if ( results != null) {
 470  0
                 if (results instanceof List) {
 471  0
                         List<?> errorList = (List<?>)results;
 472  0
                         for (Object msg : errorList) {
 473  0
                                 ErrorMessage errorMessage = (ErrorMessage)msg;
 474  0
                                 errors.add(DataDictionaryTypeServiceHelper.createErrorString(errorMessage.getErrorKey(),
 475  
                             errorMessage.getMessageParameters()));
 476  0
                                 }
 477  0
                 } else {
 478  0
                         String [] temp = (String []) results;
 479  0
                         for (String string : temp) {
 480  0
                                         errors.add(string);
 481  
                                 }
 482  
                 }
 483  
         }
 484  0
         GlobalVariables.getMessageMap().removeAllErrorMessagesForProperty(attributeName);
 485  0
         return errors;
 486  
         }
 487  
 
 488  
         protected List<RemotableAttributeError> validateNonDataDictionaryAttribute(KimTypeAttribute attr, String key, String value) {
 489  0
                 return Collections.emptyList();
 490  
         }
 491  
 
 492  
     protected List<RemotableAttributeError> validateDataDictionaryAttribute(KimTypeAttribute attr, String key, String value) {
 493  
                 try {
 494  
             // create an object of the proper type per the component
 495  0
             Object componentObject = Class.forName( attr.getKimAttribute().getComponentName() ).newInstance();
 496  
 
 497  0
             if ( attr.getKimAttribute().getAttributeName() != null ) {
 498  
                 // get the bean utils descriptor for accessing the attribute on that object
 499  0
                 PropertyDescriptor propertyDescriptor = PropertyUtils.getPropertyDescriptor(componentObject, attr.getKimAttribute().getAttributeName());
 500  0
                 if ( propertyDescriptor != null ) {
 501  
                     // set the value on the object so that it can be checked
 502  0
                     Object attributeValue = getAttributeValue(propertyDescriptor, value);
 503  0
                     propertyDescriptor.getWriteMethod().invoke( componentObject, attributeValue);
 504  0
                     return validateDataDictionaryAttribute(attr.getKimTypeId(), attr.getKimAttribute().getComponentName(), componentObject, propertyDescriptor);
 505  
                 }
 506  
             }
 507  0
         } catch (Exception e) {
 508  0
             throw new KimTypeAttributeValidationException(e);
 509  0
         }
 510  0
         return Collections.emptyList();
 511  
         }
 512  
 
 513  
 
 514  
         /**
 515  
          * @param namespaceCode
 516  
          * @param typeAttribute
 517  
          * @return an AttributeDefinition for the given KimTypeAttribute, or null no base AttributeDefinition 
 518  
          * matches the typeAttribute parameter's attributeName.
 519  
          */
 520  
         protected KimAttributeField getDataDictionaryAttributeDefinition( String namespaceCode, String kimTypeId, KimTypeAttribute typeAttribute, List<String> uniqueAttributes) {
 521  
 
 522  0
                 final String componentClassName = typeAttribute.getKimAttribute().getComponentName();
 523  0
                 final String attributeName = typeAttribute.getKimAttribute().getAttributeName();
 524  
                 final AttributeDefinition baseDefinition;
 525  
                 final Class<? extends BusinessObject> componentClass;
 526  
                 // try to resolve the component name - if not possible - try to pull the definition from the app mediation service
 527  
                 try {
 528  0
                         componentClass = (Class<? extends BusinessObject>) Class.forName(componentClassName);
 529  0
             baseDefinition = getDataDictionaryService().getDataDictionary().getBusinessObjectEntry(componentClassName).getAttributeDefinition(attributeName);
 530  0
                 } catch (ClassNotFoundException ex) {
 531  0
             throw new KimTypeAttributeException(ex);
 532  0
                 }
 533  
 
 534  0
         final RemotableAttributeField.Builder definition = RemotableAttributeField.Builder.create(baseDefinition.getName());
 535  
 
 536  0
         definition.setLongLabel(baseDefinition.getLabel());
 537  0
         definition.setShortLabel(baseDefinition.getShortLabel());
 538  0
         definition.setMaxLength(baseDefinition.getMaxLength());
 539  0
         definition.setRequired(baseDefinition.isRequired());
 540  0
         definition.setForceUpperCase(baseDefinition.getForceUppercase());
 541  0
         definition.setControl(DataDictionaryTypeServiceHelper.toRemotableAbstractControlBuilder(
 542  
                 baseDefinition));
 543  0
         final RemotableQuickFinder.Builder qf = createQuickFinder(componentClass, attributeName);
 544  0
         if (qf != null) {
 545  0
             definition.setWidgets(Collections.<RemotableAbstractWidget.Builder>singletonList(qf));
 546  
         }
 547  0
         final KimAttributeField.Builder kimField = KimAttributeField.Builder.create(definition, typeAttribute.getKimAttribute().getId());
 548  
 
 549  0
         if(uniqueAttributes!=null && uniqueAttributes.contains(definition.getName())){
 550  0
             kimField.setUnique(true);
 551  
         }
 552  
 
 553  0
                 return kimField.build();
 554  
         }
 555  
 
 556  
     private RemotableQuickFinder.Builder createQuickFinder(Class<? extends BusinessObject> componentClass, String attributeName) {
 557  
 
 558  0
         Field field = FieldUtils.getPropertyField(componentClass, attributeName, false);
 559  0
         if ( field != null ) {
 560  
             final BusinessObject sampleComponent;
 561  
             try {
 562  0
                 sampleComponent = componentClass.newInstance();
 563  0
             } catch(InstantiationException e) {
 564  0
                 throw new KimTypeAttributeException(e);
 565  0
             } catch (IllegalAccessException e) {
 566  0
                 throw new KimTypeAttributeException(e);
 567  0
             }
 568  
 
 569  0
             field = LookupUtils.setFieldQuickfinder( sampleComponent, attributeName, field, Collections.singletonList(attributeName) );
 570  0
             if ( StringUtils.isNotBlank( field.getQuickFinderClassNameImpl() ) ) {
 571  
                 final Class<? extends BusinessObject> lookupClass;
 572  
                 try {
 573  0
                     lookupClass = (Class<? extends BusinessObject>) Class.forName( field.getQuickFinderClassNameImpl() );
 574  0
                 } catch (ClassNotFoundException e) {
 575  0
                     throw new KimTypeAttributeException(e);
 576  0
                 }
 577  
 
 578  0
                 final RemotableQuickFinder.Builder builder = RemotableQuickFinder.Builder.create(
 579  
                         DataDictionaryTypeServiceHelper.getKimBasePath(), lookupClass.getName());
 580  0
                 builder.setLookupParameters(toMap(field.getLookupParameters()));
 581  0
                 builder.setFieldConversions(toMap(field.getFieldConversions()));
 582  0
                 return builder;
 583  
             }
 584  
         }
 585  0
         return null;
 586  
     }
 587  
 
 588  
     private static Map<String, String> toMap(String s) {
 589  0
         if (StringUtils.isBlank(s)) {
 590  0
             return Collections.emptyMap();
 591  
         }
 592  0
         final Map<String, String> map = new HashMap<String, String>();
 593  0
         for (String string : s.split(",")) {
 594  0
             String [] keyVal = string.split(":");
 595  0
             map.put(keyVal[0], keyVal[1]);
 596  
         }
 597  0
         return Collections.unmodifiableMap(map);
 598  
     }
 599  
 
 600  
         protected KimAttributeField getNonDataDictionaryAttributeDefinition(String namespaceCode, String kimTypeId, KimTypeAttribute typeAttribute, List<String> uniqueAttributes) {
 601  0
                 RemotableAttributeField.Builder field = RemotableAttributeField.Builder.create(typeAttribute.getKimAttribute().getAttributeName());
 602  0
                 field.setLongLabel(typeAttribute.getKimAttribute().getAttributeLabel());
 603  
 
 604  0
         KimAttributeField.Builder definition = KimAttributeField.Builder.create(field, typeAttribute.getKimAttribute().getId());
 605  
 
 606  0
         if(uniqueAttributes!=null && uniqueAttributes.contains(typeAttribute.getKimAttribute().getAttributeName())){
 607  0
             definition.setUnique(true);
 608  
         }
 609  0
                 return definition.build();
 610  
         }
 611  
 
 612  
         protected static final String COMMA_SEPARATOR = ", ";
 613  
 
 614  
         protected void validateRequiredAttributesAgainstReceived(Map<String, String> receivedAttributes){
 615  
                 // abort if type does not want the qualifiers to be checked
 616  0
                 if ( !isCheckRequiredAttributes() ) {
 617  0
                         return;
 618  
                 }
 619  
                 // abort if the list is empty, no attributes need to be checked
 620  0
                 if ( getRequiredAttributes() == null || getRequiredAttributes().isEmpty() ) {
 621  0
                         return;
 622  
                 }
 623  0
                 List<String> missingAttributes = new ArrayList<String>();
 624  
                 // if attributes are null or empty, they're all missing
 625  0
                 if ( receivedAttributes == null || receivedAttributes.isEmpty() ) {
 626  0
                         return;                
 627  
                 } else {
 628  0
                         for( String requiredAttribute : getRequiredAttributes() ) {
 629  0
                                 if( !receivedAttributes.containsKey(requiredAttribute) ) {
 630  0
                                         missingAttributes.add(requiredAttribute);
 631  
                                 }
 632  
                         }
 633  
                 }
 634  0
         if(!missingAttributes.isEmpty()) {
 635  0
                 StringBuilder errorMessage = new StringBuilder();
 636  0
                 Iterator<String> attribIter = missingAttributes.iterator();
 637  0
                 while ( attribIter.hasNext() ) {
 638  0
                         errorMessage.append( attribIter.next() );
 639  0
                         if( attribIter.hasNext() ) {
 640  0
                                 errorMessage.append( COMMA_SEPARATOR );
 641  
                         }
 642  
                 }
 643  0
                 errorMessage.append( " not found in required attributes for this type." );
 644  0
             throw new KimTypeAttributeValidationException(errorMessage.toString());
 645  
         }
 646  0
         }
 647  
 
 648  
 
 649  
         
 650  
         protected List<RemotableAttributeError> validateUniqueAttributes(String kimTypeId, Map<String, String> newAttributes, Map<String, String> oldAttributes) {
 651  0
                 List<String> uniqueAttributes = getUniqueAttributes(kimTypeId);
 652  0
                 if(uniqueAttributes==null || uniqueAttributes.isEmpty()){
 653  0
                         return Collections.emptyList();
 654  
                 } else{
 655  0
                         List<RemotableAttributeError> m = new ArrayList<RemotableAttributeError>();
 656  0
             if(areAttributesEqual(uniqueAttributes, newAttributes, oldAttributes)){
 657  
                                 //add all unique attrs to error map
 658  0
                 for (String a : uniqueAttributes) {
 659  0
                     m.add(RemotableAttributeError.Builder.create(a, RiceKeyConstants.ERROR_DUPLICATE_ENTRY).build());
 660  
                 }
 661  
 
 662  0
                 return m;
 663  
                         }
 664  
                 }
 665  0
                 return Collections.emptyList();
 666  
         }
 667  
         
 668  
         protected boolean areAttributesEqual(List<String> uniqueAttributeNames, Map<String, String> aSet1, Map<String, String> aSet2){
 669  0
                 StringValueComparator comparator = StringValueComparator.getInstance();
 670  0
                 for(String uniqueAttributeName: uniqueAttributeNames){
 671  0
                         String attrVal1 = getAttributeValue(aSet1, uniqueAttributeName);
 672  0
                         String attrVal2 = getAttributeValue(aSet2, uniqueAttributeName);
 673  0
                         if(comparator.compare(attrVal1, attrVal2)!=0){
 674  0
                                 return false;
 675  
                         }
 676  0
                 }
 677  0
                 return true;
 678  
         }
 679  
 
 680  
         protected String getAttributeValue(Map<String, String> aSet, String attributeName){
 681  0
                 if(StringUtils.isEmpty(attributeName)) {
 682  0
                         return null;
 683  
                 }
 684  0
                 for(Map.Entry<String, String> entry : aSet.entrySet()){
 685  0
                         if(attributeName.equals(entry.getKey())) {
 686  0
                                 return entry.getValue();
 687  
                         }
 688  
                 }
 689  0
                 return null;
 690  
         }
 691  
 
 692  
         protected List<String> getUniqueAttributes(String kimTypeId){
 693  0
                 KimType kimType = getTypeInfoService().getKimType(kimTypeId);
 694  0
         List<String> uniqueAttributes = new ArrayList<String>();
 695  0
         if ( kimType != null ) {
 696  0
                 for(KimTypeAttribute attributeDefinition: kimType.getAttributeDefinitions()){
 697  0
                         uniqueAttributes.add(attributeDefinition.getKimAttribute().getAttributeName());
 698  
                 }
 699  
         } else {
 700  0
                 LOG.error("Unable to retrieve a KimTypeInfo for a null kimTypeId in getUniqueAttributes()");
 701  
         }
 702  0
         return Collections.unmodifiableList(uniqueAttributes);
 703  
         }
 704  
 
 705  
         protected List<RemotableAttributeError> validateUnmodifiableAttributes(String kimTypeId, Map<String, String> originalAttributes, Map<String, String> newAttributes){
 706  0
                 List<RemotableAttributeError> validationErrors = new ArrayList<RemotableAttributeError>();
 707  0
                 KimType kimType = getTypeInfoService().getKimType(kimTypeId);
 708  0
                 List<String> uniqueAttributes = getUniqueAttributes(kimTypeId);
 709  0
                 for(String attributeNameKey: uniqueAttributes){
 710  0
                         KimTypeAttribute attr = kimType.getAttributeDefinitionByName(attributeNameKey);
 711  0
                         String mainAttributeValue = getAttributeValue(originalAttributes, attributeNameKey);
 712  0
                         String delegationAttributeValue = getAttributeValue(newAttributes, attributeNameKey);
 713  
 
 714  0
                         if(!StringUtils.equals(mainAttributeValue, delegationAttributeValue)){
 715  0
                                 validationErrors.add(RemotableAttributeError.Builder.create(attributeNameKey, DataDictionaryTypeServiceHelper
 716  
                         .createErrorString(RiceKeyConstants.ERROR_CANT_BE_MODIFIED,
 717  
                                 dataDictionaryService.getAttributeLabel(attr.getKimAttribute().getComponentName(),
 718  
                                         attributeNameKey))).build());
 719  
                         }
 720  0
                 }
 721  0
                 return validationErrors;
 722  
         }
 723  
 
 724  
     protected List<String> getRequiredAttributes() {
 725  0
         return Collections.emptyList();
 726  
     }
 727  
 
 728  
         protected boolean isCheckRequiredAttributes() {
 729  0
                 return false;
 730  
         }
 731  
 
 732  
         protected String getClosestParentDocumentTypeName(
 733  
                         DocumentType documentType,
 734  
                         Set<String> potentialParentDocumentTypeNames) {
 735  0
                 if ( potentialParentDocumentTypeNames == null || documentType == null ) {
 736  0
                         return null;
 737  
                 }
 738  0
                 if (potentialParentDocumentTypeNames.contains(documentType.getName())) {
 739  0
                         return documentType.getName();
 740  
                 } 
 741  0
                 if ((documentType.getParentId() == null)
 742  
                                 || documentType.getParentId().equals(
 743  
                                                 documentType.getId())) {
 744  0
                         return null;
 745  
                 } 
 746  0
                 return getClosestParentDocumentTypeName(getDocumentTypeService().getDocumentTypeById(documentType
 747  
                                 .getParentId()), potentialParentDocumentTypeNames);
 748  
         }
 749  
 
 750  
     protected static class KimTypeAttributeValidationException extends RuntimeException {
 751  
 
 752  
         protected KimTypeAttributeValidationException(String message) {
 753  0
             super( message );
 754  0
         }
 755  
 
 756  
         protected KimTypeAttributeValidationException(Throwable cause) {
 757  0
             super( cause );
 758  0
         }
 759  
 
 760  
         private static final long serialVersionUID = 8220618846321607801L;
 761  
 
 762  
     }
 763  
 
 764  0
     protected static class KimTypeAttributeException extends RuntimeException {
 765  
 
 766  
         protected KimTypeAttributeException(String message) {
 767  0
             super( message );
 768  0
         }
 769  
 
 770  
         protected KimTypeAttributeException(Throwable cause) {
 771  0
             super( cause );
 772  0
         }
 773  
 
 774  
         private static final long serialVersionUID = 8220618846321607801L;
 775  
 
 776  
     }
 777  
 
 778  
     protected KimTypeInfoService getTypeInfoService() {
 779  0
                 if ( typeInfoService == null ) {
 780  0
                         typeInfoService = KimApiServiceLocator.getKimTypeInfoService();
 781  
                 }
 782  0
                 return typeInfoService;
 783  
         }
 784  
 
 785  
         protected BusinessObjectService getBusinessObjectService() {
 786  0
                 if ( businessObjectService == null ) {
 787  0
                         businessObjectService = KRADServiceLocator.getBusinessObjectService();
 788  
                 }
 789  0
                 return businessObjectService;
 790  
         }
 791  
 
 792  
         protected DictionaryValidationService getDictionaryValidationService() {
 793  0
                 if ( dictionaryValidationService == null ) {
 794  0
                         dictionaryValidationService = KRADServiceLocatorWeb.getDictionaryValidationService();
 795  
                 }
 796  0
                 return dictionaryValidationService;
 797  
         }
 798  
 
 799  
         protected DataDictionaryService getDataDictionaryService() {
 800  0
                 if ( dataDictionaryService == null ) {
 801  0
                         dataDictionaryService = KRADServiceLocatorWeb.getDataDictionaryService();
 802  
                 }
 803  0
                 return this.dataDictionaryService;
 804  
         }
 805  
 
 806  
 
 807  
         protected DocumentTypeService getDocumentTypeService() {
 808  0
                 if ( documentTypeService == null ) {
 809  0
                         documentTypeService = KewApiServiceLocator.getDocumentTypeService();
 810  
                 }
 811  0
                 return this.documentTypeService;
 812  
         }
 813  
 }