1 /** 2 * Copyright 2005-2012 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.view; 17 18 import org.apache.commons.lang.StringUtils; 19 import org.kuali.rice.krad.uif.container.CollectionGroup; 20 import org.kuali.rice.krad.uif.component.Component; 21 import org.kuali.rice.krad.uif.field.DataField; 22 import org.kuali.rice.krad.uif.util.ComponentUtils; 23 24 import java.io.Serializable; 25 import java.util.HashMap; 26 import java.util.Map; 27 28 /** 29 * Holds field indexes of a <code>View</code> instance for retrieval 30 * 31 * @author Kuali Rice Team (rice.collab@kuali.org) 32 */ 33 public class ViewIndex implements Serializable { 34 private static final long serialVersionUID = 4700818801272201371L; 35 36 private Map<String, Component> index; 37 private Map<String, DataField> dataFieldIndex; 38 private Map<String, CollectionGroup> collectionsIndex; 39 40 private Map<String, Component> initialComponentStates; 41 42 /** 43 * Constructs new instance 44 */ 45 public ViewIndex() { 46 index = new HashMap<String, Component>(); 47 dataFieldIndex = new HashMap<String, DataField>(); 48 collectionsIndex = new HashMap<String, CollectionGroup>(); 49 initialComponentStates = new HashMap<String, Component>(); 50 } 51 52 /** 53 * Walks through the View tree and indexes all components found. All components 54 * are indexed by their IDs with the special indexing done for certain components 55 * 56 * <p> 57 * <code>DataField</code> instances are indexed by the attribute path. 58 * This is useful for retrieving the InputField based on the incoming 59 * request parameter 60 * </p> 61 * 62 * <p> 63 * <code>CollectionGroup</code> instances are indexed by the collection 64 * path. This is useful for retrieving the CollectionGroup based on the 65 * incoming request parameter 66 * </p> 67 */ 68 protected void index(View view) { 69 index = new HashMap<String, Component>(); 70 dataFieldIndex = new HashMap<String, DataField>(); 71 collectionsIndex = new HashMap<String, CollectionGroup>(); 72 73 indexComponent(view); 74 } 75 76 /** 77 * Adds an entry to the main index for the given component. If the component 78 * is of type <code>DataField</code> or <code>CollectionGroup</code> an 79 * entry is created in the corresponding indexes for those types as well. Then 80 * the #indexComponent method is called for each of the component's children 81 * 82 * <p> 83 * If the component is already contained in the indexes, it will be replaced 84 * </p> 85 * 86 * @param component - component instance to index 87 */ 88 public void indexComponent(Component component) { 89 if (component == null) { 90 return; 91 } 92 93 index.put(component.getId(), component); 94 95 if (component instanceof DataField) { 96 DataField field = (DataField) component; 97 dataFieldIndex.put(field.getBindingInfo().getBindingPath(), field); 98 } else if (component instanceof CollectionGroup) { 99 CollectionGroup collectionGroup = (CollectionGroup) component; 100 collectionsIndex.put(collectionGroup.getBindingInfo().getBindingPath(), collectionGroup); 101 } 102 103 for (Component nestedComponent : component.getComponentsForLifecycle()) { 104 indexComponent(nestedComponent); 105 } 106 } 107 108 /** 109 * Retrieves a <code>Component</code> from the view index by Id 110 * 111 * @param id - id for the component to retrieve 112 * @return Component instance found in index, or null if no such component exists 113 */ 114 public Component getComponentById(String id) { 115 return index.get(id); 116 } 117 118 /** 119 * Retrieves a <code>DataField</code> instance from the index 120 * 121 * @param attributePath - full path of the attribute (from the form) 122 * @return DataField instance for the path or Null if not found 123 */ 124 public DataField getDataFieldByPath(String attributePath) { 125 return dataFieldIndex.get(attributePath); 126 } 127 128 /** 129 * Retrieves a <code>DataField</code> instance that has the given property name 130 * specified (note this is not the full binding path and first match is returned) 131 * 132 * @param propertyName - property name for field to retrieve 133 * @return DataField instance found or null if not found 134 */ 135 public DataField getDataFieldByPropertyName(String propertyName) { 136 DataField dataField = null; 137 138 for (DataField field : dataFieldIndex.values()) { 139 if (StringUtils.equals(propertyName, field.getPropertyName())) { 140 dataField = field; 141 break; 142 } 143 } 144 145 return dataField; 146 } 147 148 /** 149 * Gets the Map that contains attribute field indexing information. The Map 150 * key points to an attribute binding path, and the Map value is the 151 * <code>DataField</code> instance 152 * 153 * @return Map<String, DataField> data fields index map 154 */ 155 public Map<String, DataField> getDataFieldIndex() { 156 return this.dataFieldIndex; 157 } 158 159 /** 160 * Gets the Map that contains collection indexing information. The Map key 161 * gives the binding path to the collection, and the Map value givens the 162 * <code>CollectionGroup</code> instance 163 * 164 * @return Map<String, CollectionGroup> collection index map 165 */ 166 public Map<String, CollectionGroup> getCollectionsIndex() { 167 return this.collectionsIndex; 168 } 169 170 /** 171 * Retrieves a <code>CollectionGroup</code> instance from the index 172 * 173 * @param collectionPath - full path of the collection (from the form) 174 * @return CollectionGroup instance for the collection path or Null if not 175 * found 176 */ 177 public CollectionGroup getCollectionGroupByPath(String collectionPath) { 178 return collectionsIndex.get(collectionPath); 179 } 180 181 /** 182 * Preserves initial state of components needed for doing component refreshes 183 * 184 * <p> 185 * Some components, such as those that are nested or created in code cannot be requested from the 186 * spring factory to get new instances. For these a copy of the component in its initial state is 187 * set in this map which will be used when doing component refreshes (which requires running just that 188 * component's lifecycle) 189 * </p> 190 * 191 * <p> 192 * Map entries are added during the perform initialize phase from {@link org.kuali.rice.krad.uif.service.ViewHelperService} 193 * </p> 194 * 195 * @return Map<String, Component> - map with key giving the factory id for the component and the value the 196 * component 197 * instance 198 */ 199 public Map<String, Component> getInitialComponentStates() { 200 return initialComponentStates; 201 } 202 203 /** 204 * Adds a copy of the given component instance to the map of initial component states keyed by the component 205 * factory id 206 * 207 * @param component - component instance to add 208 */ 209 public void addInitialComponentState(Component component) { 210 initialComponentStates.put(component.getFactoryId(), ComponentUtils.copy(component)); 211 } 212 213 /** 214 * Setter for the map holding initial component states 215 * 216 * @param initialComponentStates 217 */ 218 public void setInitialComponentStates(Map<String, Component> initialComponentStates) { 219 this.initialComponentStates = initialComponentStates; 220 } 221 }