Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
ViewModelUtils |
|
| 4.285714285714286;4.286 |
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.krad.uif.util; | |
17 | ||
18 | import org.apache.commons.lang.StringUtils; | |
19 | import org.kuali.rice.krad.uif.field.DataField; | |
20 | import org.kuali.rice.krad.uif.view.View; | |
21 | import org.springframework.beans.PropertyValues; | |
22 | import org.springframework.beans.factory.config.TypedStringValue; | |
23 | ||
24 | import java.util.Collection; | |
25 | import java.util.Map; | |
26 | ||
27 | /** | |
28 | * Provides methods for getting property values, types, and paths within the | |
29 | * context of a <code>View</code> | |
30 | * | |
31 | * <p> | |
32 | * The view provides a special map named 'abstractTypeClasses' that indicates | |
33 | * concrete classes that should be used in place of abstract property types that | |
34 | * are encountered on the object graph. This classes takes into account that map | |
35 | * while dealing with properties. e.g. suppose we have propertyPath | |
36 | * 'document.name' on the form, with the type of the document property set to | |
37 | * the interface Document. Using class introspection we would get back the | |
38 | * interface type for document and this would not be able to get the property | |
39 | * type for name. Using the view map, we can replace document with a concrete | |
40 | * class and then use it to get the name property | |
41 | * </p> | |
42 | * | |
43 | * @author Kuali Rice Team (rice.collab@kuali.org) | |
44 | */ | |
45 | 0 | public class ViewModelUtils { |
46 | ||
47 | /** | |
48 | * Determines the associated type for the property within the View context | |
49 | * | |
50 | * <p> | |
51 | * Property path is full path to property from the View Form class. The abstract type classes | |
52 | * map configured on the View will be consulted for any entries that match the property path. If the | |
53 | * property path given contains a partial match to an abstract class (somewhere on path is an abstract | |
54 | * class), the property type will be retrieved based on the given concrete class to use and the part | |
55 | * of the path remaining. If no matching entry is found, standard reflection is used to get the type | |
56 | * </p> | |
57 | * | |
58 | * @param view - view instance providing the context (abstract map) | |
59 | * @param propertyPath - full path to property to retrieve type for (relative to the form class) | |
60 | * @return Class<?> type of property in model, or Null if type could not be determined | |
61 | * @see org.kuali.rice.krad.uif.view.View#getAbstractTypeClasses() | |
62 | */ | |
63 | public static Class<?> getPropertyTypeByClassAndView(View view, String propertyPath) { | |
64 | 0 | Class<?> propertyType = null; |
65 | ||
66 | 0 | if (StringUtils.isBlank(propertyPath)) { |
67 | 0 | return propertyType; |
68 | } | |
69 | ||
70 | // in case of partial match, holds the class that matched and the | |
71 | // property so we can get by reflection | |
72 | 0 | Class<?> modelClass = view.getFormClass(); |
73 | 0 | String modelProperty = propertyPath; |
74 | ||
75 | 0 | int bestMatchLength = 0; |
76 | ||
77 | // removed collection indexes from path for matching | |
78 | 0 | String flattenedPropertyPath = propertyPath.replaceAll("\\[.+\\]", ""); |
79 | ||
80 | // check if property path matches one of the modelClass entries | |
81 | 0 | Map<String, Class<?>> modelClasses = view.getAbstractTypeClasses(); |
82 | 0 | for (String path : modelClasses.keySet()) { |
83 | // full match | |
84 | 0 | if (StringUtils.equals(path, flattenedPropertyPath)) { |
85 | 0 | propertyType = modelClasses.get(path); |
86 | 0 | break; |
87 | } | |
88 | ||
89 | // partial match | |
90 | 0 | if (flattenedPropertyPath.startsWith(path) && (path.length() > bestMatchLength)) { |
91 | 0 | bestMatchLength = path.length(); |
92 | ||
93 | 0 | modelClass = modelClasses.get(path); |
94 | 0 | modelProperty = StringUtils.removeStart(flattenedPropertyPath, path); |
95 | 0 | modelProperty = StringUtils.removeStart(modelProperty, "."); |
96 | } | |
97 | } | |
98 | ||
99 | // if full match not found, get type based on reflection | |
100 | 0 | if (propertyType == null) { |
101 | 0 | propertyType = ObjectPropertyUtils.getPropertyType(modelClass, modelProperty); |
102 | } | |
103 | ||
104 | 0 | return propertyType; |
105 | } | |
106 | ||
107 | public static String getParentObjectPath(DataField field) { | |
108 | 0 | String parentObjectPath = ""; |
109 | ||
110 | 0 | String objectPath = field.getBindingInfo().getBindingObjectPath(); |
111 | 0 | String propertyPrefix = field.getBindingInfo().getBindByNamePrefix(); |
112 | ||
113 | 0 | if (!field.getBindingInfo().isBindToForm() && StringUtils.isNotBlank(objectPath)) { |
114 | 0 | parentObjectPath = objectPath; |
115 | } | |
116 | ||
117 | 0 | if (StringUtils.isNotBlank(propertyPrefix)) { |
118 | 0 | if (StringUtils.isNotBlank(parentObjectPath)) { |
119 | 0 | parentObjectPath += "."; |
120 | } | |
121 | ||
122 | 0 | parentObjectPath += propertyPrefix; |
123 | } | |
124 | ||
125 | 0 | return parentObjectPath; |
126 | } | |
127 | ||
128 | public static Class<?> getParentObjectClassForMetadata(View view, DataField field) { | |
129 | 0 | String parentObjectPath = getParentObjectPath(field); |
130 | ||
131 | 0 | return getPropertyTypeByClassAndView(view, parentObjectPath); |
132 | } | |
133 | ||
134 | public static Class<?> getParentObjectClassForMetadata(View view, Object model, DataField field) { | |
135 | 0 | String parentObjectPath = getParentObjectPath(field); |
136 | ||
137 | 0 | return getObjectClassForMetadata(view, model, parentObjectPath); |
138 | } | |
139 | ||
140 | public static Class<?> getObjectClassForMetadata(View view, Object model, String propertyPath) { | |
141 | // get class by object instance if not null | |
142 | 0 | Object parentObject = ObjectPropertyUtils.getPropertyValue(model, propertyPath); |
143 | 0 | if (parentObject != null) { |
144 | 0 | return parentObject.getClass(); |
145 | } | |
146 | ||
147 | // get class by property type with abstract map check | |
148 | 0 | return getPropertyTypeByClassAndView(view, propertyPath); |
149 | } | |
150 | ||
151 | public static Object getParentObjectForMetadata(View view, Object model, DataField field) { | |
152 | // default to model as parent | |
153 | 0 | Object parentObject = model; |
154 | ||
155 | 0 | String parentObjectPath = getParentObjectPath(field); |
156 | 0 | if (StringUtils.isNotBlank(parentObjectPath)) { |
157 | 0 | parentObject = ObjectPropertyUtils.getPropertyValue(model, parentObjectPath); |
158 | ||
159 | // attempt to create new instance if parent is null or is a | |
160 | // collection or map | |
161 | 0 | if ((parentObject == null) || Collection.class.isAssignableFrom(parentObject.getClass()) || |
162 | Map.class.isAssignableFrom(parentObject.getClass())) { | |
163 | try { | |
164 | 0 | Class<?> parentObjectClass = getPropertyTypeByClassAndView(view, parentObjectPath); |
165 | 0 | parentObject = parentObjectClass.newInstance(); |
166 | 0 | } catch (InstantiationException e) { |
167 | // swallow exception and let null be returned | |
168 | 0 | } catch (IllegalAccessException e) { |
169 | // swallow exception and let null be returned | |
170 | 0 | } |
171 | } | |
172 | } | |
173 | ||
174 | 0 | return parentObject; |
175 | } | |
176 | ||
177 | /** | |
178 | * Helper method for getting the string value of a property from a {@link PropertyValues} | |
179 | * | |
180 | * @param propertyValues - property values instance to pull from | |
181 | * @param propertyName - name of property whose value should be retrieved | |
182 | * @return String value for property or null if property was not found | |
183 | */ | |
184 | public static String getStringValFromPVs(PropertyValues propertyValues, String propertyName) { | |
185 | 0 | String propertyValue = null; |
186 | ||
187 | 0 | if ((propertyValues != null) && propertyValues.contains(propertyName)) { |
188 | 0 | Object pvValue = propertyValues.getPropertyValue(propertyName).getValue(); |
189 | 0 | if (pvValue instanceof TypedStringValue) { |
190 | 0 | TypedStringValue typedStringValue = (TypedStringValue) pvValue; |
191 | 0 | propertyValue = typedStringValue.getValue(); |
192 | 0 | } else if (pvValue instanceof String) { |
193 | 0 | propertyValue = (String) pvValue; |
194 | } | |
195 | } | |
196 | ||
197 | 0 | return propertyValue; |
198 | } | |
199 | } |