/** * Overridden to copy property editor registration to the new bean wrapper. * * <p>This is necessary because spring only copies over the editors when a new bean wrapper is * created. The wrapper is then cached and use for subsequent calls. But the get calls could bring in * new custom editors we need to copy.</p> * * {@inheritDoc} */ @Override protected BeanWrapperImpl getBeanWrapperForPropertyPath(String propertyPath) { BeanWrapperImpl beanWrapper = super.getBeanWrapperForPropertyPath(propertyPath); PropertyTokenHolder tokens = getPropertyNameTokens(propertyPath); String canonicalName = tokens.canonicalName; int pos = PropertyAccessorUtils.getFirstNestedPropertySeparatorIndex(canonicalName); if (pos != -1) { canonicalName = canonicalName.substring(0, pos); } copyCustomEditorsTo(beanWrapper, canonicalName); return beanWrapper; }
/** * Set the parent for bi-directional relationships when adding a line to a collection. * * @param model the view data * @param collectionPath the path to the collection being linked * @param addedIndex the index of the added line * @return whether the linked line needs further processing */ protected boolean linkAddedLine(Object model, String collectionPath, int addedIndex) { int lastSepIndex = PropertyAccessorUtils.getLastNestedPropertySeparatorIndex(collectionPath); if (lastSepIndex != -1) { String collectionParentPath = collectionPath.substring(0, lastSepIndex); Object parent = ObjectPropertyUtils.getPropertyValue(model, collectionParentPath); if (parent != null && getDataObjectService().supports(parent.getClass())) { DataObjectWrapper<?> wrappedParent = getDataObjectService().wrap(parent); String collectionName = collectionPath.substring(lastSepIndex + 1); wrappedParent.linkChanges(Sets.newHashSet(collectionName + "[" + addedIndex + "]")); } // check if its edit line in dialog line action, and if it is we want to set the collection as // the dialog's parent and do not want further processing of the dialog object if (collectionParentPath.equalsIgnoreCase(UifPropertyPaths.DIALOG_DATA_OBJECT)) { ((UifFormBase) model).setDialogDataObject(parent); return false; } } return true; }
@Override public Map<String, String> getInquiryParameters(Object dataObject, List<String> keys, String propertyName) { Map<String, String> inquiryParameters = new HashMap<String, String>(); org.kuali.rice.krad.data.metadata.DataObjectRelationship dataObjectRelationship = null; DataObjectMetadata objectMetadata = KRADServiceLocator.getDataObjectService().getMetadataRepository().getMetadata(dataObject.getClass()); if (objectMetadata != null) { dataObjectRelationship = objectMetadata.getRelationshipByLastAttributeInRelationship(propertyName); } for (String keyName : keys) { String keyConversion = keyName; if (dataObjectRelationship != null) { keyConversion = dataObjectRelationship.getParentAttributeNameRelatedToChildAttributeName(keyName); } else if (PropertyAccessorUtils.isNestedOrIndexedProperty(propertyName)) { String nestedAttributePrefix = KRADUtils.getNestedAttributePrefix(propertyName); keyConversion = nestedAttributePrefix + "." + keyName; } inquiryParameters.put(keyConversion, keyName); } return inquiryParameters; }
/** * Gets the property type for a property name. * * @param objectMetadata the metadata object. * @param propertyName the name of the property. * @return the property type for a property name. */ private Class<?> getPropertyTypeChild(DataObjectMetadata objectMetadata, String propertyName){ if(PropertyAccessorUtils.isNestedOrIndexedProperty(propertyName)){ String attributePrefix = StringUtils.substringBefore(propertyName,"."); String attributeName = StringUtils.substringAfter(propertyName,"."); if(StringUtils.isNotBlank(attributePrefix) && StringUtils.isNotBlank(attributeName) && objectMetadata!= null){ Class<?> propertyType = traverseRelationship(objectMetadata,attributePrefix,attributeName); if(propertyType != null){ return propertyType; } } } return getPropertyType(propertyName); }
/** * Gets the property type for a property name in a relationship. * * @param objectMetadata the metadata object. * @param attributePrefix the prefix of the property that indicated it was in a relationship. * @param attributeName the name of the property. * @return the property type for a property name. */ private Class<?> traverseRelationship(DataObjectMetadata objectMetadata,String attributePrefix, String attributeName){ DataObjectRelationship rd = objectMetadata.getRelationship(attributePrefix); if(rd != null){ DataObjectMetadata relatedObjectMetadata = dataObjectService.getMetadataRepository().getMetadata(rd.getRelatedType()); if(relatedObjectMetadata != null){ if(PropertyAccessorUtils.isNestedOrIndexedProperty(attributeName)){ return getPropertyTypeChild(relatedObjectMetadata,attributeName); } else{ if(relatedObjectMetadata.getAttribute(attributeName) == null && relatedObjectMetadata.getRelationship(attributeName)!=null){ DataObjectRelationship relationship = relatedObjectMetadata.getRelationship(attributeName); return relationship.getRelatedType(); } return relatedObjectMetadata.getAttribute(attributeName).getDataType().getType(); } } } return null; }
/** * Gets indexes that have been modified. * * <p> * Returns a set of indexes which have been modified in the given collection. If the returned set contains * {@link java.lang.Integer#MAX_VALUE} then it means that it should be treated as if all items in the collection * have been modified. * </p> * * @return indexes which have been modified in the given collection */ private Set<Integer> extractModifiedIndicies(DataObjectCollection collectionMetadata, Map<String, Set<String>> decomposedPaths) { String relationshipName = collectionMetadata.getName(); Set<Integer> modifiedIndicies = Sets.newHashSet(); // if it contains *exactly* the collection relationship name, then indicate that all items modified if (decomposedPaths.containsKey(relationshipName)) { modifiedIndicies.add(Integer.valueOf(Integer.MAX_VALUE)); } for (String propertyName : decomposedPaths.keySet()) { if (relationshipName.equals(PropertyAccessorUtils.getPropertyName(relationshipName))) { Integer index = extractIndex(propertyName); if (index != null) { modifiedIndicies.add(index); } } } return modifiedIndicies; }
@Override public Map<String, String> getInquiryParameters(Object dataObject, List<String> keys, String propertyName) { Map<String, String> inquiryParameters = new HashMap<String, String>(); Class<?> objectClass = ObjectUtils.materializeClassForProxiedObject(dataObject); org.kuali.rice.krad.bo.DataObjectRelationship relationship = dataObjectMetaDataService.getDataObjectRelationship(dataObject, objectClass, propertyName, "", true, false, true); for (String keyName : keys) { String keyConversion = keyName; if (relationship != null) { keyConversion = relationship.getParentAttributeForChildAttribute(keyName); } else if (PropertyAccessorUtils.isNestedOrIndexedProperty(propertyName)) { String nestedAttributePrefix = KRADUtils.getNestedAttributePrefix(propertyName); keyConversion = nestedAttributePrefix + "." + keyName; } inquiryParameters.put(keyConversion, keyName); } return inquiryParameters; }
private boolean isPersonProperty(Object bo, String propertyName) { try { if (PropertyAccessorUtils.isNestedOrIndexedProperty( propertyName ) // is a nested property && !StringUtils.contains(propertyName, "add.") ) {// exclude add line properties (due to path parsing problems in PropertyUtils.getPropertyType) int lastIndex = PropertyAccessorUtils.getLastNestedPropertySeparatorIndex(propertyName); String propertyTypeName = lastIndex != -1 ? StringUtils.substring(propertyName, 0, lastIndex) : StringUtils.EMPTY; Class<?> type = PropertyUtils.getPropertyType(bo, propertyTypeName); // property type indicates a Person object if ( type != null ) { return Person.class.isAssignableFrom(type); } LOG.warn( "Unable to determine type of nested property: " + bo.getClass().getName() + " / " + propertyName ); } } catch (Exception ex) { if ( LOG.isDebugEnabled() ) { LOG.debug("Unable to determine if property on " + bo.getClass().getName() + " to a person object: " + propertyName, ex ); } } return false; }
@Override protected BeanWrapperImpl getBeanWrapperForPropertyPath(String propertyPath) { BeanWrapperImpl beanWrapper = super.getBeanWrapperForPropertyPath(propertyPath); PropertyTokenHolder tokens = getPropertyNameTokens(propertyPath); String canonicalName = tokens.canonicalName; int pos = PropertyAccessorUtils.getFirstNestedPropertySeparatorIndex(canonicalName); if (pos != -1) { canonicalName = canonicalName.substring(0, pos); } copyCustomEditorsTo(beanWrapper, canonicalName); return beanWrapper; }
/** * Check the given property values against the allowed fields, * removing values for fields that are not allowed. * @param mpvs the property values to be bound (can be modified) * @see #getAllowedFields * @see #isAllowed(String) */ protected void checkAllowedFields(MutablePropertyValues mpvs) { PropertyValue[] pvs = mpvs.getPropertyValues(); for (PropertyValue pv : pvs) { String field = PropertyAccessorUtils.canonicalPropertyName(pv.getName()); if (!isAllowed(field)) { mpvs.removePropertyValue(pv); getBindingResult().recordSuppressedField(field); if (logger.isDebugEnabled()) { logger.debug("Field [" + field + "] has been removed from PropertyValues " + "and will not be bound, because it has not been found in the list of allowed fields"); } } } }
/** * {@inheritDoc} */ @Override public void refreshReferences(String referencesToRefresh) { Object model = ViewLifecycle.getModel(); for (String reference : StringUtils.split(referencesToRefresh, KRADConstants.REFERENCES_TO_REFRESH_SEPARATOR)) { if (StringUtils.isBlank(reference)) { continue; } //ToDo: handle add line if (PropertyAccessorUtils.isNestedOrIndexedProperty(reference)) { String parentPath = KRADUtils.getNestedAttributePrefix(reference); Object parentObject = ObjectPropertyUtils.getPropertyValue(model, parentPath); String referenceObjectName = KRADUtils.getNestedAttributePrimitive(reference); if (parentObject == null) { LOG.warn("Unable to refresh references for " + referencesToRefresh + ". Object not found in model. Nothing refreshed."); continue; } refreshReference(parentObject, referenceObjectName); } else { refreshReference(model, reference); } } }
/** * @see org.kuali.rice.krad.service.DictionaryValidationService#validateReferenceExistsAndIsActive(java.lang.Object * dataObject, * String, String, String) */ @Override public boolean validateReferenceExistsAndIsActive(Object dataObject, String referenceName, String attributeToHighlightOnFail, String displayFieldName) { // if we're dealing with a nested attribute, we need to resolve down to the BO where the primitive attribute is located // this is primarily to deal with the case of a defaultExistenceCheck that uses an "extension", i.e referenceName // would be extension.attributeName if (PropertyAccessorUtils.isNestedOrIndexedProperty(referenceName)) { String nestedAttributePrefix = KRADUtils.getNestedAttributePrefix(referenceName); String nestedAttributePrimitive = KRADUtils.getNestedAttributePrimitive(referenceName); Object nestedObject = KradDataServiceLocator.getDataObjectService().wrap(dataObject) .getPropertyValueNullSafe(nestedAttributePrefix); return validateReferenceExistsAndIsActive(nestedObject, nestedAttributePrimitive, attributeToHighlightOnFail, displayFieldName); } boolean hasReferences = validateFkFieldsPopulated(dataObject, referenceName); boolean referenceExists = hasReferences && validateReferenceExists(dataObject, referenceName); boolean canIncludeActiveReference = referenceExists && (!(dataObject instanceof Inactivatable) || ((Inactivatable) dataObject).isActive()); boolean referenceActive = canIncludeActiveReference && validateReferenceIsActive(dataObject, referenceName); if(hasReferences && !referenceExists) { GlobalVariables.getMessageMap().putError(attributeToHighlightOnFail, RiceKeyConstants.ERROR_EXISTENCE, displayFieldName); return false; } else if(canIncludeActiveReference && !referenceActive) { GlobalVariables.getMessageMap().putError(attributeToHighlightOnFail, RiceKeyConstants.ERROR_INACTIVE, displayFieldName); return false; } return true; }
@Override /** * Recursively calls getPropertyTypeChild if nested property to allow it to properly look it up */ public Class<?> getPropertyType(Object object, String propertyName) { DataObjectWrapper<?> wrappedObject = dataObjectService.wrap(object); if (PropertyAccessorUtils.isNestedOrIndexedProperty(propertyName)) { return wrappedObject.getPropertyTypeNullSafe(wrappedObject.getWrappedClass(), propertyName); } return wrappedObject.getPropertyType(propertyName); }
/** * Recursively navigate to return a BeanWrapper for the nested property path. * * @param propertyPath * property property path, which may be nested * @return a BeanWrapper for the target bean */ PropertyDescriptor getPropertyDescriptorForPropertyPath(String propertyPath, Class<?> propertyType) { int pos = PropertyAccessorUtils.getFirstNestedPropertySeparatorIndex(propertyPath); // Handle nested properties recursively. if (pos > -1) { String nestedProperty = propertyPath.substring(0, pos); String nestedPath = propertyPath.substring(pos + 1); PropertyDescriptor propertyDescriptor = BeanUtils.getPropertyDescriptor(propertyType, nestedProperty); // BeanWrapperImpl nestedBw = getNestedBeanWrapper(nestedProperty); return getPropertyDescriptorForPropertyPath(nestedPath, propertyDescriptor.getPropertyType()); } else { return BeanUtils.getPropertyDescriptor(propertyType, propertyPath); } }
/** * Actually set the nested path. * Delegated to by setNestedPath and pushNestedPath. * <p/> * Note: Not yet known what to use the path for. Possibly if the Asset has * second class Asset contained inside it. * * @param nestedPath Path */ protected void doSetNestedPath(String nestedPath) { if (nestedPath == null) { nestedPath = ""; } nestedPath = PropertyAccessorUtils.canonicalPropertyName(nestedPath); if (nestedPath.length() > 0 && !nestedPath.endsWith(NESTED_PATH_SEPARATOR)) { nestedPath += NESTED_PATH_SEPARATOR; } this.nestedPath = nestedPath; }
/** * Transform the given field into its full path, * regarding the nested path of this instance. * * @param field a {@link java.lang.String} object. * @return {@link java.lang.String} object. */ protected String fixedField(String field) { if (StringUtils.hasLength(field)) { return getNestedPath() + PropertyAccessorUtils.canonicalPropertyName(field); } else { String path = getNestedPath(); return (path.endsWith(org.springframework.validation.Errors.NESTED_PATH_SEPARATOR) ? path.substring(0, path.length() - NESTED_PATH_SEPARATOR.length()) : path); } }
/** * <p>Constructor for PropertyPath.</p> * * @param nestedPath a {@link java.lang.String} object. */ public PropertyPath(String nestedPath) { String canonicalPath = PropertyAccessorUtils.canonicalPropertyName(nestedPath); int lastIndex = PropertyAccessorUtils.getLastNestedPropertySeparatorIndex(canonicalPath); if (lastIndex < 0) { propertyName = PropertyAccessorUtils.getPropertyName(canonicalPath); key = computeKey(canonicalPath); } else { parent = new PropertyPath(canonicalPath.substring(0, lastIndex)); String lastProperty = canonicalPath.substring(lastIndex+1); propertyName = PropertyAccessorUtils.getPropertyName(lastProperty); key = computeKey(lastProperty); } }
/** * Returns the canonical property name. * @see org.springframework.beans.PropertyAccessorUtils#canonicalPropertyName */ @Override protected String canonicalFieldName(String field) { return PropertyAccessorUtils.canonicalPropertyName(field); }
/** * Register fields that are required for each binding process. * <p>If one of the specified fields is not contained in the list of * incoming property values, a corresponding "missing field" error * will be created, with error code "required" (by the default * binding error processor). * @param requiredFields array of field names * @see #setBindingErrorProcessor * @see DefaultBindingErrorProcessor#MISSING_FIELD_ERROR_CODE */ public void setRequiredFields(String... requiredFields) { this.requiredFields = PropertyAccessorUtils.canonicalPropertyNames(requiredFields); if (logger.isDebugEnabled()) { logger.debug("DataBinder requires binding of required fields [" + StringUtils.arrayToCommaDelimitedString(requiredFields) + "]"); } }