| Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
| ViewModelUtils |
|
| 4.166666666666667;4.167 |
| 1 | /* | |
| 2 | * Copyright 2007 The Kuali Foundation Licensed under the Educational Community | |
| 3 | * License, Version 1.0 (the "License"); you may not use this file except in | |
| 4 | * compliance with the License. You may obtain a copy of the License at | |
| 5 | * http://www.opensource.org/licenses/ecl1.php Unless required by applicable law | |
| 6 | * or agreed to in writing, software distributed under the License is | |
| 7 | * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | |
| 8 | * KIND, either express or implied. See the License for the specific language | |
| 9 | * governing permissions and limitations under the License. | |
| 10 | */ | |
| 11 | package org.kuali.rice.krad.uif.util; | |
| 12 | ||
| 13 | import org.apache.commons.lang.StringUtils; | |
| 14 | import org.kuali.rice.krad.uif.container.View; | |
| 15 | import org.kuali.rice.krad.uif.field.AttributeField; | |
| 16 | ||
| 17 | import java.util.Collection; | |
| 18 | import java.util.Map; | |
| 19 | ||
| 20 | /** | |
| 21 | * Provides methods for getting property values, types, and paths within the | |
| 22 | * context of a <code>View</code> | |
| 23 | * | |
| 24 | * <p> | |
| 25 | * The view provides a special map named 'abstractTypeClasses' that indicates | |
| 26 | * concrete classes that should be used in place of abstract property types that | |
| 27 | * are encountered on the object graph. This classes takes into account that map | |
| 28 | * while dealing with properties. e.g. suppose we have propertyPath | |
| 29 | * 'document.name' on the form, with the type of the document property set to | |
| 30 | * the interface Document. Using class introspection we would get back the | |
| 31 | * interface type for document and this would not be able to get the property | |
| 32 | * type for name. Using the view map, we can replace document with a concrete | |
| 33 | * class and then use it to get the name property | |
| 34 | * </p> | |
| 35 | * | |
| 36 | * @author Kuali Rice Team (rice.collab@kuali.org) | |
| 37 | */ | |
| 38 | 0 | public class ViewModelUtils { |
| 39 | ||
| 40 | /** | |
| 41 | * Determines the associated type for the property within the View context | |
| 42 | * | |
| 43 | * <p> | |
| 44 | * Property path is full path to property from the View Form class. The abstract type classes | |
| 45 | * map configured on the View will be consulted for any entries that match the property path. If the | |
| 46 | * property path given contains a partial match to an abstract class (somewhere on path is an abstract | |
| 47 | * class), the property type will be retrieved based on the given concrete class to use and the part | |
| 48 | * of the path remaining. If no matching entry is found, standard reflection is used to get the type | |
| 49 | * </p> | |
| 50 | * | |
| 51 | * @param view - view instance providing the context (abstract map) | |
| 52 | * @param propertyPath - full path to property to retrieve type for (relative to the form class) | |
| 53 | * @return Class<?> type of property in model, or Null if type could not be determined | |
| 54 | * @see org.kuali.rice.krad.uif.container.View#getAbstractTypeClasses() | |
| 55 | */ | |
| 56 | public static Class<?> getPropertyTypeByClassAndView(View view, String propertyPath) { | |
| 57 | 0 | Class<?> propertyType = null; |
| 58 | ||
| 59 | 0 | if (StringUtils.isBlank(propertyPath)) { |
| 60 | 0 | return propertyType; |
| 61 | } | |
| 62 | ||
| 63 | // in case of partial match, holds the class that matched and the | |
| 64 | // property so we can get by reflection | |
| 65 | 0 | Class<?> modelClass = view.getFormClass(); |
| 66 | 0 | String modelProperty = propertyPath; |
| 67 | ||
| 68 | 0 | int bestMatchLength = 0; |
| 69 | ||
| 70 | // removed collection indexes from path for matching | |
| 71 | 0 | String flattenedPropertyPath = propertyPath.replaceAll("\\[.+\\]", ""); |
| 72 | ||
| 73 | // check if property path matches one of the modelClass entries | |
| 74 | 0 | Map<String, Class<?>> modelClasses = view.getAbstractTypeClasses(); |
| 75 | 0 | for (String path : modelClasses.keySet()) { |
| 76 | // full match | |
| 77 | 0 | if (StringUtils.equals(path, flattenedPropertyPath)) { |
| 78 | 0 | propertyType = modelClasses.get(path); |
| 79 | 0 | break; |
| 80 | } | |
| 81 | ||
| 82 | // partial match | |
| 83 | 0 | if (flattenedPropertyPath.startsWith(path) && (path.length() > bestMatchLength)) { |
| 84 | 0 | bestMatchLength = path.length(); |
| 85 | ||
| 86 | 0 | modelClass = modelClasses.get(path); |
| 87 | 0 | modelProperty = StringUtils.removeStart(flattenedPropertyPath, path); |
| 88 | 0 | modelProperty = StringUtils.removeStart(modelProperty, "."); |
| 89 | } | |
| 90 | } | |
| 91 | ||
| 92 | // if full match not found, get type based on reflection | |
| 93 | 0 | if (propertyType == null) { |
| 94 | 0 | propertyType = ObjectPropertyUtils.getPropertyType(modelClass, modelProperty); |
| 95 | } | |
| 96 | ||
| 97 | 0 | return propertyType; |
| 98 | } | |
| 99 | ||
| 100 | public static String getParentObjectPath(AttributeField field) { | |
| 101 | 0 | String parentObjectPath = ""; |
| 102 | ||
| 103 | 0 | String objectPath = field.getBindingInfo().getBindingObjectPath(); |
| 104 | 0 | String propertyPrefix = field.getBindingInfo().getBindByNamePrefix(); |
| 105 | ||
| 106 | 0 | if (!field.getBindingInfo().isBindToForm() && StringUtils.isNotBlank(objectPath)) { |
| 107 | 0 | parentObjectPath = objectPath; |
| 108 | } | |
| 109 | ||
| 110 | 0 | if (StringUtils.isNotBlank(propertyPrefix)) { |
| 111 | 0 | if (StringUtils.isNotBlank(parentObjectPath)) { |
| 112 | 0 | parentObjectPath += "."; |
| 113 | } | |
| 114 | ||
| 115 | 0 | parentObjectPath += propertyPrefix; |
| 116 | } | |
| 117 | ||
| 118 | 0 | return parentObjectPath; |
| 119 | } | |
| 120 | ||
| 121 | public static Class<?> getParentObjectClassForMetadata(View view, AttributeField field) { | |
| 122 | 0 | String parentObjectPath = getParentObjectPath(field); |
| 123 | ||
| 124 | 0 | return getPropertyTypeByClassAndView(view, parentObjectPath); |
| 125 | } | |
| 126 | ||
| 127 | public static Class<?> getParentObjectClassForMetadata(View view, Object model, AttributeField field) { | |
| 128 | 0 | String parentObjectPath = getParentObjectPath(field); |
| 129 | ||
| 130 | 0 | return getObjectClassForMetadata(view, model, parentObjectPath); |
| 131 | } | |
| 132 | ||
| 133 | public static Class<?> getObjectClassForMetadata(View view, Object model, String propertyPath) { | |
| 134 | // get class by object instance if not null | |
| 135 | 0 | Object parentObject = ObjectPropertyUtils.getPropertyValue(model, propertyPath); |
| 136 | 0 | if (parentObject != null) { |
| 137 | 0 | return parentObject.getClass(); |
| 138 | } | |
| 139 | ||
| 140 | // get class by property type with abstract map check | |
| 141 | 0 | return getPropertyTypeByClassAndView(view, propertyPath); |
| 142 | } | |
| 143 | ||
| 144 | public static Object getParentObjectForMetadata(View view, Object model, AttributeField field) { | |
| 145 | // default to model as parent | |
| 146 | 0 | Object parentObject = model; |
| 147 | ||
| 148 | 0 | String parentObjectPath = getParentObjectPath(field); |
| 149 | 0 | if (StringUtils.isNotBlank(parentObjectPath)) { |
| 150 | 0 | parentObject = ObjectPropertyUtils.getPropertyValue(model, parentObjectPath); |
| 151 | ||
| 152 | // attempt to create new instance if parent is null or is a | |
| 153 | // collection or map | |
| 154 | 0 | if ((parentObject == null) || Collection.class.isAssignableFrom(parentObject.getClass()) || |
| 155 | Map.class.isAssignableFrom(parentObject.getClass())) { | |
| 156 | try { | |
| 157 | 0 | Class<?> parentObjectClass = getPropertyTypeByClassAndView(view, parentObjectPath); |
| 158 | 0 | parentObject = parentObjectClass.newInstance(); |
| 159 | 0 | } catch (InstantiationException e) { |
| 160 | // swallow exception and let null be returned | |
| 161 | 0 | } catch (IllegalAccessException e) { |
| 162 | // swallow exception and let null be returned | |
| 163 | 0 | } |
| 164 | } | |
| 165 | } | |
| 166 | ||
| 167 | 0 | return parentObject; |
| 168 | } | |
| 169 | } |