View Javadoc

1   /*
2    * Copyright 2007 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 1.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/ecl1.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.container;
17  
18  import org.apache.commons.lang.StringUtils;
19  import org.kuali.rice.krad.uif.core.BindingInfo;
20  import org.kuali.rice.krad.uif.core.Component;
21  import org.kuali.rice.krad.uif.field.AttributeField;
22  
23  import java.io.Serializable;
24  import java.util.HashMap;
25  import java.util.Map;
26  
27  /**
28   * Holds field indexes of a <code>View</code> instance for retrieval
29   *
30   * @author Kuali Rice Team (rice.collab@kuali.org)
31   */
32  public class ViewIndex implements Serializable {
33      private static final long serialVersionUID = 4700818801272201371L;
34  
35      private Map<String, Component> index;
36      private Map<String, AttributeField> attributeFieldIndex;
37      private Map<String, CollectionGroup> collectionsIndex;
38  
39      /**
40       * Constructs new instance and performs indexing on View instance
41       *
42       * @param view - view instance to index
43       */
44      public ViewIndex(View view) {
45          index(view);
46      }
47  
48      /**
49       * Walks through the View tree and indexes all components found. All components
50       * are indexed by their IDs with the special indexing done for certain components
51       *
52       * <p>
53       * <code>AttributeField</code> instances are indexed by the attribute path.
54       * This is useful for retrieving the AttributeField based on the incoming
55       * request parameter
56       * </p>
57       *
58       * <p>
59       * <code>CollectionGroup</code> instances are indexed by the collection
60       * path. This is useful for retrieving the CollectionGroup based on the
61       * incoming request parameter
62       * </p>
63       */
64      protected void index(View view) {
65          index = new HashMap<String, Component>();
66          attributeFieldIndex = new HashMap<String, AttributeField>();
67          collectionsIndex = new HashMap<String, CollectionGroup>();
68  
69          indexComponent(view);
70      }
71  
72      /**
73       * Adds an entry to the main index for the given component. If the component
74       * is of type <code>AttributeField</code> or <code>CollectionGroup</code> an
75       * entry is created in the corresponding indexes for those types as well. Then
76       * the #indexComponent method is called for each of the component's children
77       *
78       * <p>
79       * If the component is already contained in the indexes, it will be replaced
80       * </p>
81       *
82       * @param component - component instance to index
83       */
84      public void indexComponent(Component component) {
85          if (component == null) {
86              return;
87          }
88  
89          index.put(component.getId(), component);
90  
91          if (component instanceof AttributeField) {
92              AttributeField field = (AttributeField) component;
93              attributeFieldIndex.put(field.getBindingInfo().getBindingPath(), field);
94          } else if (component instanceof CollectionGroup) {
95              CollectionGroup collectionGroup = (CollectionGroup) component;
96              collectionsIndex.put(collectionGroup.getBindingInfo().getBindingPath(), collectionGroup);
97          }
98  
99          for (Component nestedComponent : component.getNestedComponents()) {
100             indexComponent(nestedComponent);
101         }
102     }
103 
104     /**
105      * Retrieves a <code>Component</code> from the view index by Id
106      *
107      * @param id - id for the component to retrieve
108      * @return Component instance found in index, or null if no such component exists
109      */
110     public Component getComponentById(String id) {
111         return index.get(id);
112     }
113 
114     /**
115      * Retrieves a <code>AttributeField</code> instance from the index
116      *
117      * @param attributePath - full path of the attribute (from the form)
118      * @return AttributeField instance for the path or Null if not found
119      */
120     public AttributeField getAttributeFieldByPath(String attributePath) {
121         return attributeFieldIndex.get(attributePath);
122     }
123 
124     /**
125      * Retrieves a <code>AttributeField</code> instance that has the given property name
126      * specified (note this is not the full binding path and first match is returned)
127      *
128      * @param propertyName - property name for field to retrieve
129      * @return AttributeField instance found or null if not found
130      */
131     public AttributeField getAttributeFieldByPropertyName(String propertyName) {
132         AttributeField attributeField = null;
133 
134         for (AttributeField field : attributeFieldIndex.values()) {
135             if (StringUtils.equals(propertyName, field.getPropertyName())) {
136                 attributeField = field;
137                 break;
138             }
139         }
140 
141         return attributeField;
142     }
143 
144     /**
145      * Gets the Map that contains attribute field indexing information. The Map
146      * key points to an attribute binding path, and the Map value is the
147      * <code>AttributeField</code> instance
148      *
149      * @return Map<String, AttributeField> attribute fields index map
150      */
151     public Map<String, AttributeField> getAttributeFieldIndex() {
152         return this.attributeFieldIndex;
153     }
154 
155     /**
156      * Gets the Map that contains collection indexing information. The Map key
157      * gives the binding path to the collection, and the Map value givens the
158      * <code>CollectionGroup</code> instance
159      *
160      * @return Map<String, CollectionGroup> collection index map
161      */
162     public Map<String, CollectionGroup> getCollectionsIndex() {
163         return this.collectionsIndex;
164     }
165 
166     /**
167      * Retrieves a <code>CollectionGroup</code> instance from the index
168      *
169      * @param collectionPath - full path of the collection (from the form)
170      * @return CollectionGroup instance for the collection path or Null if not
171      *         found
172      */
173     public CollectionGroup getCollectionGroupByPath(String collectionPath) {
174         return collectionsIndex.get(collectionPath);
175     }
176 
177     /**
178      * Searches for the <code>AttributeField</code> based on the given binding path.
179      *
180      * <p>It first searches for the matching <code>AttributeField</code> by path. If not
181      * matching field found, it searches in the <code>CollectionGroup</code>
182      * </p>
183      *
184      * @param bindingInfo - <code>AttributeField</code> will be searched based on this binding path
185      * @return the attribute field for the binding path
186      */
187     public AttributeField getAttributeField(BindingInfo bindingInfo) {
188         // Find in the attribute index first.
189         AttributeField attributeField = getAttributeFieldByPath(bindingInfo.getBindingPath());
190 
191         if (attributeField == null) {
192             // Lets search the collections (by collection's binding path)
193             String path = bindingInfo.getBindingObjectPath() + "." + bindingInfo.getBindByNamePrefix();
194 
195             CollectionGroup collectionGroup = getCollectionGroupByPath(stripIndexesFromPropertyPath(path));
196             if (collectionGroup != null) {
197                 for (Component item : ((CollectionGroup) collectionGroup).getItems()) {
198                     if (item instanceof AttributeField) {
199                         if (StringUtils
200                                 .equals(((AttributeField) item).getPropertyName(), bindingInfo.getBindingName())) {
201                             attributeField = (AttributeField) item;
202                             break;
203                         }
204                     }
205                 }
206             }
207         }
208 
209         return attributeField;
210     }
211 
212     /**
213      * Strips indexes from the property path.
214      *
215      * <p> For example, it returns bo.fiscalOfficer.accounts.name for the input bo.fiscalOfficer.accounts[0].name,
216      * which can be used to find the components from the CollectionGroup index
217      * </p>
218      *
219      * @param propertyPath - property path
220      * @return the stripped index path
221      */
222     private String stripIndexesFromPropertyPath(String propertyPath) {
223 
224         String returnValue = propertyPath;
225         String index = StringUtils.substringBetween(propertyPath, "[", "]");
226 
227         if (StringUtils.isNotBlank(index)) {
228             returnValue = StringUtils.remove(propertyPath, "[" + index + "]");
229             return stripIndexesFromPropertyPath(returnValue);
230         } else {
231             return returnValue;
232         }
233     }
234 }