View Javadoc

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 }