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