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 | } |