001/** 002 * Copyright 2005-2016 The Kuali Foundation 003 * 004 * Licensed under the Educational Community License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.opensource.org/licenses/ecl2.php 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.kuali.rice.krad.web.bind; 017 018import org.kuali.rice.core.web.format.Formatter; 019import org.kuali.rice.krad.uif.field.DataField; 020import org.kuali.rice.krad.uif.view.ViewIndex; 021import org.kuali.rice.krad.uif.view.ViewModel; 022import org.springframework.beans.BeanWrapperImpl; 023import org.springframework.beans.BeansException; 024import org.springframework.beans.InvalidPropertyException; 025import org.springframework.beans.PropertyValue; 026 027import java.beans.PropertyDescriptor; 028import java.beans.PropertyEditor; 029import java.util.HashSet; 030import java.util.Set; 031 032/** 033 * Class is a top level BeanWrapper for a UIF View Model 034 * 035 * <p> 036 * Registers custom property editors configured on the field associated with the property name for which 037 * we are getting or setting a value. In addition determines if the field requires encryption and if so applies 038 * the {@link UifEncryptionPropertyEditorWrapper} 039 * </p> 040 * 041 * @author Kuali Rice Team (rice.collab@kuali.org) 042 */ 043public class UifViewBeanWrapper extends BeanWrapperImpl { 044 private static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(UifViewBeanWrapper.class); 045 046 // this is a handle to the target object so we don't have to cast so often 047 private ViewModel model; 048 049 // this stores all properties this wrapper has already checked 050 // with the view so the service isn't called again 051 private Set<String> processedProperties; 052 053 public UifViewBeanWrapper(ViewModel model) { 054 super(model); 055 056 this.model = model; 057 this.processedProperties = new HashSet<String>(); 058 } 059 060 /** 061 * Attempts to find a corresponding data field for the given property name in the current view or previous view, 062 * then if the field has a property editor configured it is registered with the property editor registry to use 063 * for this property 064 * 065 * @param propertyName - name of the property to find field and editor for 066 */ 067 protected void registerEditorFromView(String propertyName) { 068 if (LOG.isDebugEnabled()) { 069 LOG.debug("Attempting to find property editor for property '" + propertyName + "'"); 070 } 071 072 // check if we already processed this property for this BeanWrapper instance 073 if (processedProperties.contains(propertyName)) { 074 return; 075 } 076 077 // when rendering the page, we will use the view that was just built, for post 078 // we need to use the posted view (not the newly initialized view) 079 ViewIndex viewIndex = null; 080 if (model.getView() != null) { 081 viewIndex = model.getView().getViewIndex(); 082 } else if (model.getPostedView() != null) { 083 viewIndex = model.getPostedView().getViewIndex(); 084 } 085 086 // if view index instance not established we cannot determine property editors 087 if (viewIndex == null) { 088 return; 089 } 090 091 PropertyEditor propertyEditor = null; 092 boolean requiresEncryption = false; 093 094 if (viewIndex.getFieldPropertyEditors().containsKey(propertyName)) { 095 propertyEditor = viewIndex.getFieldPropertyEditors().get(propertyName); 096 } else if (viewIndex.getSecureFieldPropertyEditors().containsKey(propertyName)) { 097 propertyEditor = viewIndex.getSecureFieldPropertyEditors().get(propertyName); 098 requiresEncryption = true; 099 } 100 101 if (propertyEditor != null) { 102 if (LOG.isDebugEnabled()) { 103 LOG.debug("Registering custom editor for property path '" + propertyName 104 + "' and property editor class '" + propertyEditor.getClass().getName() + "'"); 105 } 106 107 if (requiresEncryption) { 108 if (LOG.isDebugEnabled()) { 109 LOG.debug("Enabling encryption for custom editor '" + propertyName + 110 "' and property editor class '" + propertyEditor.getClass().getName() + "'"); 111 } 112 this.registerCustomEditor(null, propertyName, new UifEncryptionPropertyEditorWrapper(propertyEditor)); 113 } else { 114 this.registerCustomEditor(null, propertyName, propertyEditor); 115 } 116 } else if (requiresEncryption) { 117 if (LOG.isDebugEnabled()) { 118 LOG.debug("No custom formatter for property path '" + propertyName 119 + "' but property does require encryption"); 120 } 121 122 this.registerCustomEditor(null, propertyName, new UifEncryptionPropertyEditorWrapper( 123 findEditorForPropertyName(propertyName))); 124 } 125 126 processedProperties.add(propertyName); 127 } 128 129 protected PropertyEditor findEditorForPropertyName(String propertyName) { 130 Class<?> clazz = getPropertyType(propertyName); 131 if (LOG.isDebugEnabled()) { 132 LOG.debug("Attempting retrieval of property editor using class '" 133 + clazz 134 + "' and property path '" 135 + propertyName 136 + "'"); 137 } 138 139 PropertyEditor editor = findCustomEditor(clazz, propertyName); 140 if (editor == null) { 141 if (LOG.isDebugEnabled()) { 142 LOG.debug("No custom property editor found using class '" 143 + clazz 144 + "' and property path '" 145 + propertyName 146 + "'. Attempting to find default property editor class."); 147 } 148 editor = getDefaultEditor(clazz); 149 } 150 151 return editor; 152 } 153 154 @Override 155 public Class<?> getPropertyType(String propertyName) throws BeansException { 156 try { 157 PropertyDescriptor pd = getPropertyDescriptorInternal(propertyName); 158 if (pd != null) { 159 return pd.getPropertyType(); 160 } 161 162 // Maybe an indexed/mapped property... 163 Object value = super.getPropertyValue(propertyName); 164 if (value != null) { 165 return value.getClass(); 166 } 167 168 // Check to see if there is a custom editor, 169 // which might give an indication on the desired target type. 170 Class<?> editorType = guessPropertyTypeFromEditors(propertyName); 171 if (editorType != null) { 172 return editorType; 173 } 174 } catch (InvalidPropertyException ex) { 175 // Consider as not determinable. 176 } 177 178 return null; 179 } 180 181 @Override 182 public Object getPropertyValue(String propertyName) throws BeansException { 183 registerEditorFromView(propertyName); 184 return super.getPropertyValue(propertyName); 185 } 186 187 @Override 188 public void setPropertyValue(PropertyValue pv) throws BeansException { 189 registerEditorFromView(pv.getName()); 190 super.setPropertyValue(pv); 191 } 192 193 @Override 194 public void setPropertyValue(String propertyName, Object value) throws BeansException { 195 registerEditorFromView(propertyName); 196 super.setPropertyValue(propertyName, value); 197 } 198 199 @Override 200 public void setWrappedInstance(Object object, String nestedPath, Object rootObject) { 201 //TODO clear cache? 202 model = (ViewModel) object; 203 super.setWrappedInstance(object, nestedPath, rootObject); 204 } 205 206 @Override 207 public void setWrappedInstance(Object object) { 208 //TODO clear cache? 209 model = (ViewModel) object; 210 super.setWrappedInstance(object); 211 } 212}