View Javadoc
1   /**
2    * Copyright 2005-2015 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 java.io.Serializable;
19  import java.util.HashMap;
20  import java.util.HashSet;
21  import java.util.Map;
22  import java.util.Set;
23  
24  import org.apache.commons.lang.StringUtils;
25  import org.apache.log4j.Logger;
26  import org.kuali.rice.krad.uif.component.Component;
27  import org.kuali.rice.krad.uif.container.CollectionGroup;
28  import org.kuali.rice.krad.uif.container.Container;
29  import org.kuali.rice.krad.uif.field.DataField;
30  import org.kuali.rice.krad.uif.util.CopyUtils;
31  import org.kuali.rice.krad.uif.util.LifecycleElement;
32  
33  /**
34   * Holds component indexes of a {@link View} instance for convenient retrieval during the lifecycle.
35   *
36   * @author Kuali Rice Team (rice.collab@kuali.org)
37   */
38  public class ViewIndex implements Serializable {
39      private static final long serialVersionUID = 4700818801272201371L;
40      private static final Logger LOG = Logger.getLogger(ViewIndex.class);
41  
42      private Map<String, Component> index;
43      private Map<String, DataField> dataFieldIndex;
44  
45      private Map<String, CollectionGroup> collectionsIndex;
46      private Map<String, LifecycleElement> lifecycleElementsByPath;
47  
48      private Set<String> assignedIds;
49  
50      /**
51       * Default Constructor.
52       */
53      public ViewIndex() {
54          index = new HashMap<String, Component>();
55          dataFieldIndex = new HashMap<String, DataField>();
56          collectionsIndex = new HashMap<String, CollectionGroup>();
57          lifecycleElementsByPath = new HashMap<String, LifecycleElement>();
58          assignedIds = new HashSet<String>();
59      }
60  
61      /**
62       * Clears view indexes, for reinitializing indexes at the start of each phase.
63       */
64      protected void clearIndex(View view) {
65          index = new HashMap<String, Component>();
66          dataFieldIndex = new HashMap<String, DataField>();
67          collectionsIndex = new HashMap<String, CollectionGroup>();
68          lifecycleElementsByPath = new HashMap<String, LifecycleElement>();
69      }
70  
71      /**
72       * Adds an entry to the main index for the given component. If the component is of type
73       * <code>DataField</code> or <code>CollectionGroup</code> an entry is created in the
74       * corresponding indexes for those types as well. Then the #indexComponent method is called for
75       * each of the component's children
76       *
77       * <p>
78       * If the component is already contained in the indexes, it will be replaced
79       * </p>
80       *
81       * <p>
82       * <code>DataField</code> instances are indexed by the attribute path. This is useful for
83       * retrieving the InputField based on the incoming request parameter
84       * </p>
85       *
86       * <p>
87       * <code>CollectionGroup</code> instances are indexed by the collection path. This is useful for
88       * retrieving the CollectionGroup based on the incoming request parameter
89       * </p>
90       *
91       * @param component component instance to index
92       */
93      public void indexComponent(Component component) {
94          if (component == null) {
95              return;
96          }
97  
98          synchronized (index) {
99              index.put(component.getId(), component);
100         }
101 
102         synchronized (lifecycleElementsByPath) {
103             lifecycleElementsByPath.put(component.getViewPath(), component);
104         }
105 
106         if (component instanceof Container) {
107             Container container = (Container) component;
108             if (container.getLayoutManager() != null) {
109                 synchronized (lifecycleElementsByPath) {
110                     lifecycleElementsByPath.put(container.getLayoutManager().getViewPath(),
111                             container.getLayoutManager());
112                 }
113             }
114         }
115 
116         if (component instanceof DataField) {
117             DataField field = (DataField) component;
118 
119             synchronized (dataFieldIndex) {
120                 dataFieldIndex.put(field.getBindingInfo().getBindingPath(), field);
121             }
122         } else if (component instanceof CollectionGroup) {
123             CollectionGroup collectionGroup = (CollectionGroup) component;
124 
125             synchronized (collectionsIndex) {
126                 collectionsIndex.put(collectionGroup.getBindingInfo().getBindingPath(), collectionGroup);
127             }
128         }
129     }
130 
131     /**
132      * Observe an assigned ID.
133      *
134      * @param id ID to observe
135      * @return true if the ID is unique, false if the ID has already been observed
136      */
137     public boolean observeAssignedId(String id) {
138         if (assignedIds.contains(id)) {
139             return false;
140         }
141 
142         synchronized (assignedIds) {
143             return assignedIds.add(id);
144         }
145     }
146 
147     /**
148      * Retrieves a <code>Component</code> from the view index by Id
149      *
150      * @param id id for the component to retrieve
151      * @return Component instance found in index, or null if no such component exists
152      */
153     public Component getComponentById(String id) {
154         return index.get(id);
155     }
156 
157     /**
158      * Retrieves a <code>DataField</code> instance from the index
159      *
160      * @param propertyPath full path of the data field (from the form)
161      * @return DataField instance for the path or Null if not found
162      */
163     public DataField getDataFieldByPath(String propertyPath) {
164         return dataFieldIndex.get(propertyPath);
165     }
166 
167     /**
168      * Retrieves a <code>DataField</code> instance that has the given property name specified (note
169      * this is not the full binding path and first match is returned)
170      *
171      * @param propertyName property name for field to retrieve
172      * @return DataField instance found or null if not found
173      */
174     public DataField getDataFieldByPropertyName(String propertyName) {
175         DataField dataField = null;
176 
177         for (DataField field : dataFieldIndex.values()) {
178             if (StringUtils.equals(propertyName, field.getPropertyName())) {
179                 dataField = field;
180                 break;
181             }
182         }
183 
184         return dataField;
185     }
186 
187     /**
188      * Gets the Map of lifecycle elements that are indexed by their path relative to the view.
189      *
190      * @return map of all indexed lifecycle elements, key is the element path and value is the element instance
191      */
192     public Map<String, LifecycleElement> getLifecycleElementsByPath() {
193         return lifecycleElementsByPath;
194     }
195 
196     /**
197      * Gets a lifecycle element instance by the given path (relative to the view).
198      *
199      * @param path path of the element that should be returned
200      * @return lifecycle element instance for given path or null if element at that path does not exist
201      */
202     public LifecycleElement getLifecycleElementByPath(String path) {
203         if ((lifecycleElementsByPath != null) && lifecycleElementsByPath.containsKey(path)) {
204             return lifecycleElementsByPath.get(path);
205         }
206 
207         return null;
208     }
209 
210     /**
211      * Gets the Map that contains attribute field indexing information. The Map key points to an
212      * attribute binding path, and the Map value is the <code>DataField</code> instance
213      *
214      * @return data fields index map
215      */
216     public Map<String, DataField> getDataFieldIndex() {
217         return this.dataFieldIndex;
218     }
219 
220     /**
221      * Gets the Map that contains collection indexing information. The Map key gives the binding
222      * path to the collection, and the Map value givens the <code>CollectionGroup</code> instance
223      *
224      * @return collection index map
225      */
226     public Map<String, CollectionGroup> getCollectionsIndex() {
227         return this.collectionsIndex;
228     }
229 
230     /**
231      * Retrieves a <code>CollectionGroup</code> instance from the index
232      *
233      * @param collectionPath full path of the collection (from the form)
234      * @return CollectionGroup instance for the collection path or Null if not found
235      */
236     public CollectionGroup getCollectionGroupByPath(String collectionPath) {
237         return collectionsIndex.get(collectionPath);
238     }
239 
240     /**
241      * Returns a clone of the view index.
242      *
243      * @return ViewIndex clone
244      */
245     public ViewIndex copy() {
246         ViewIndex viewIndexCopy = new ViewIndex();
247 
248         if (this.index != null) {
249             Map<String, Component> indexCopy = new HashMap<String, Component>();
250             for (Map.Entry<String, Component> indexEntry : this.index.entrySet()) {
251                 if (indexEntry.getValue() instanceof View) {
252                     LOG.warn("View reference at " + indexEntry);
253                 } else {
254                     indexCopy.put(indexEntry.getKey(), (Component) CopyUtils.copy(indexEntry.getValue()));
255                 }
256             }
257 
258             viewIndexCopy.index = indexCopy;
259         }
260 
261         if (this.dataFieldIndex != null) {
262             Map<String, DataField> dataFieldIndexCopy = new HashMap<String, DataField>();
263             for (Map.Entry<String, DataField> indexEntry : this.dataFieldIndex.entrySet()) {
264                 dataFieldIndexCopy.put(indexEntry.getKey(), (DataField) CopyUtils.copy(indexEntry.getValue()));
265             }
266 
267             viewIndexCopy.dataFieldIndex = dataFieldIndexCopy;
268         }
269 
270         if (this.collectionsIndex != null) {
271             Map<String, CollectionGroup> collectionsIndexCopy = new HashMap<String, CollectionGroup>();
272             for (Map.Entry<String, CollectionGroup> indexEntry : this.collectionsIndex.entrySet()) {
273                 collectionsIndexCopy.put(indexEntry.getKey(), (CollectionGroup) CopyUtils.copy(indexEntry.getValue()));
274             }
275 
276             viewIndexCopy.collectionsIndex = collectionsIndexCopy;
277         }
278 
279         return viewIndexCopy;
280     }
281 
282 }