001/** 002 * Copyright 2005-2014 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.uif.util; 017 018import org.kuali.rice.core.framework.util.ReflectionUtils; 019import org.kuali.rice.krad.uif.UifConstants; 020import org.kuali.rice.krad.uif.component.Component; 021import org.kuali.rice.krad.uif.container.CollectionGroup; 022import org.kuali.rice.krad.uif.lifecycle.ViewLifecycleUtils; 023 024import java.util.Collection; 025import java.util.Collections; 026import java.util.HashMap; 027import java.util.LinkedList; 028import java.util.List; 029import java.util.Map; 030import java.util.Queue; 031 032/** 033 * Utility methods related to handling context for components. 034 * 035 * @author Kuali Rice Team (rice.collab@kuali.org) 036 */ 037public class ContextUtils { 038 039 private ContextUtils() {} 040 041 /** 042 * places a key, value pair in each context map of a list of components 043 * 044 * @param elements the list of elements 045 * @param contextName a value to be used as a key to retrieve the object 046 * @param contextValue the value to be placed in the context 047 */ 048 public static void pushObjectToContextDeep(Collection<? extends LifecycleElement> elements, String contextName, 049 Object contextValue) { 050 if (elements == null || elements.isEmpty()) { 051 return; 052 } 053 054 Queue<LifecycleElement> elementQueue = new LinkedList<LifecycleElement>(); 055 056 try { 057 elementQueue.addAll(elements); 058 while (!elementQueue.isEmpty()) { 059 LifecycleElement currentElement = elementQueue.poll(); 060 061 if (currentElement == null) { 062 continue; 063 } 064 065 if (currentElement instanceof Component) { 066 ((Component) currentElement).pushObjectToContext(contextName, contextValue); 067 } 068 069 elementQueue.addAll(ViewLifecycleUtils.getElementsForLifecycle(currentElement).values()); 070 } 071 } finally { 072 elementQueue.clear(); 073 RecycleUtils.recycle(elementQueue); 074 } 075 } 076 077 /** 078 * Pushes object to a component's context so that it is available from 079 * {@link org.kuali.rice.krad.uif.component.Component#getContext()} 080 * 081 * <p>The component's nested components that are available via {@code Component#getComponentsForLifecycle} 082 * are also updated recursively</p> 083 * 084 * @param component the component whose context is to be updated 085 * @param contextName a value to be used as a key to retrieve the object 086 * @param contextValue the value to be placed in the context 087 */ 088 public static void pushObjectToContextDeep(Component component, String contextName, Object contextValue) { 089 if (component == null) { 090 return; 091 } 092 093 pushObjectToContextDeep(Collections.singletonList(component), contextName, contextValue); 094 } 095 096 /** 097 * Pushes object to a component's context so that it is available from 098 * {@link org.kuali.rice.krad.uif.component.Component#getContext()} 099 * 100 * <p>The component's nested components that are available via {@code Component#getComponentsForLifecycle} 101 * are also updated recursively</p> 102 * 103 * @param component the component whose context is to be updated 104 * @param sourceContext The source context map. 105 */ 106 public static void pushAllToContextDeep(Component component, Map<String, Object> sourceContext) { 107 if (component == null) { 108 return; 109 } 110 111 pushAllToContextDeep(Collections.singletonList(component), sourceContext); 112 } 113 114 /** 115 * Places a all entries from a map into each context map of a list of components. 116 * 117 * @param components The list components. 118 * @param sourceContext The source context map. 119 */ 120 public static void pushAllToContextDeep(List<? extends Component> components, Map<String, Object> sourceContext) { 121 if (components == null || components.isEmpty()) { 122 return; 123 } 124 125 @SuppressWarnings("unchecked") Queue<LifecycleElement> elementQueue = RecycleUtils.getInstance( 126 LinkedList.class); 127 try { 128 elementQueue.addAll(components); 129 while (!elementQueue.isEmpty()) { 130 LifecycleElement currentElement = elementQueue.poll(); 131 132 if (currentElement == null) { 133 continue; 134 } 135 136 if (currentElement instanceof Component) { 137 ((Component) currentElement).pushAllToContext(sourceContext); 138 } 139 140 elementQueue.addAll(ViewLifecycleUtils.getElementsForLifecycle(currentElement).values()); 141 } 142 } finally { 143 elementQueue.clear(); 144 RecycleUtils.recycle(elementQueue); 145 } 146 } 147 148 /** 149 * Update the contexts of the given components. 150 * 151 * @param components the components whose components to update 152 * @param collectionGroup collection group the components are associated with 153 * @param collectionLine an instance of the data object for the line 154 * @param lineIndex the line index 155 * @param lineSuffix id suffix for components in the line to make them unique 156 */ 157 public static void updateContextsForLine(List<? extends Component> components, CollectionGroup collectionGroup, 158 Object collectionLine, int lineIndex, String lineSuffix) { 159 for (Component component : components) { 160 updateContextForLine(component, collectionGroup, collectionLine, lineIndex, lineSuffix); 161 } 162 } 163 164 /** 165 * Update the context map for the given component with the collection context. 166 * 167 * <p>The values of {@code UifConstants.ContextVariableNames.LINE} and {@code UifConstants.ContextVariableNames.INDEX} 168 * are set to {@code collectionLine} and {@code lineIndex} respectively.</p> 169 * 170 * @param component the component whose context is to be updated 171 * @param collectionGroup collection group the component is associated with 172 * @param collectionLine an instance of the data object for the line 173 * @param lineIndex the line index 174 * @param lineSuffix id suffix for components in the line to make them unique 175 */ 176 public static void updateContextForLine(Component component, CollectionGroup collectionGroup, Object collectionLine, 177 int lineIndex, String lineSuffix) { 178 Map<String, Object> toUpdate = new HashMap<String, Object>(5); 179 180 toUpdate.put(UifConstants.ContextVariableNames.COLLECTION_GROUP, collectionGroup); 181 toUpdate.put(UifConstants.ContextVariableNames.LINE, collectionLine); 182 toUpdate.put(UifConstants.ContextVariableNames.INDEX, Integer.valueOf(lineIndex)); 183 toUpdate.put(UifConstants.ContextVariableNames.LINE_SUFFIX, lineSuffix); 184 185 boolean isAddLine = (lineIndex == -1); 186 toUpdate.put(UifConstants.ContextVariableNames.IS_ADD_LINE, isAddLine); 187 188 pushAllToContextDeep(component, toUpdate); 189 } 190 191 /** 192 * Sets the context of the given lifecycle element to null, then using reflection recursively finds any 193 * lifecycle element children and sets their context to null. 194 * 195 * @param lifecycleElement lifecycle element instance to clean 196 */ 197 public static void cleanContextDeep(LifecycleElement lifecycleElement) { 198 if (lifecycleElement == null) { 199 return; 200 } 201 202 lifecycleElement.setContext(null); 203 204 // find any children that are lifecycle elements and clean them as well 205 Class<?> elementClass = lifecycleElement.getClass(); 206 207 List<java.lang.reflect.Field> fields = ReflectionUtils.getAllFields(elementClass); 208 for (java.lang.reflect.Field field : fields) { 209 // Check for lists that can contain lifecycle elements 210 if (Collection.class.isAssignableFrom(field.getType())) { 211 ReflectionUtils.makeAccessible(field); 212 Collection<Object> elements = (Collection<Object>) ReflectionUtils.getField(field, lifecycleElement); 213 if (elements != null) { 214 for (Object element : elements) { 215 if (element != null && LifecycleElement.class.isAssignableFrom(element.getClass())) { 216 cleanContextDeep((LifecycleElement) element); 217 } 218 } 219 } 220 // Check for Maps that can contain lifecycle elements 221 } else if (Map.class.isAssignableFrom(field.getType())) { 222 ReflectionUtils.makeAccessible(field); 223 Map<Object, Object> elements = (Map<Object, Object>) ReflectionUtils.getField(field, lifecycleElement); 224 if (elements != null) { 225 for (Object element : elements.entrySet()) { 226 if (element != null && LifecycleElement.class.isAssignableFrom(element.getClass())) { 227 cleanContextDeep((LifecycleElement) element); 228 } 229 } 230 } 231 // Check if field is a lifecycle element itself 232 } else if (LifecycleElement.class.isAssignableFrom(field.getType())) { 233 ReflectionUtils.makeAccessible(field); 234 LifecycleElement nestedElement = (LifecycleElement) ReflectionUtils.getField(field, lifecycleElement); 235 236 cleanContextDeep(nestedElement); 237 } 238 } 239 } 240}