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.datadictionary;
17  
18  import org.apache.commons.lang.StringUtils;
19  import org.apache.commons.logging.Log;
20  import org.apache.commons.logging.LogFactory;
21  import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
22  import org.kuali.rice.krad.uif.UifConstants;
23  import org.kuali.rice.krad.uif.component.Component;
24  import org.kuali.rice.krad.uif.util.ComponentUtils;
25  import org.kuali.rice.krad.uif.util.ViewModelUtils;
26  import org.kuali.rice.krad.uif.view.View;
27  import org.kuali.rice.krad.uif.service.ViewTypeService;
28  import org.springframework.beans.PropertyValues;
29  import org.springframework.beans.factory.config.BeanDefinition;
30  import org.springframework.beans.factory.support.DefaultListableBeanFactory;
31  import org.kuali.rice.krad.uif.UifConstants.ViewType;
32  
33  import java.util.ArrayList;
34  import java.util.HashMap;
35  import java.util.List;
36  import java.util.Map;
37  import java.util.Map.Entry;
38  
39  /**
40   * Indexes <code>View</code> bean entries for retrieval
41   *
42   * <p>
43   * This is used to retrieve a <code>View</code> instance by its unique id.
44   * Furthermore, view of certain types (that have a <code>ViewTypeService</code>
45   * are indexed by their type to support retrieval of views based on parameters.
46   * </p>
47   *
48   * @author Kuali Rice Team (rice.collab@kuali.org)
49   */
50  public class UifDictionaryIndex implements Runnable {
51      private static final Log LOG = LogFactory.getLog(UifDictionaryIndex.class);
52  
53      private DefaultListableBeanFactory ddBeans;
54  
55      // view entries keyed by view id with value the spring bean name
56      private Map<String, String> viewBeanEntriesById;
57  
58      // view entries indexed by type
59      private Map<String, ViewTypeDictionaryIndex> viewEntriesByType;
60  
61      public UifDictionaryIndex(DefaultListableBeanFactory ddBeans) {
62          this.ddBeans = ddBeans;
63      }
64  
65      public void run() {
66          LOG.info("Starting View Index Building");
67          buildViewIndicies();
68          LOG.info("Completed View Index Building");
69      }
70  
71      /**
72       * Retrieves the View instance with the given id from the bean factory.
73       * Since View instances are stateful, we need to get a new instance from
74       * Spring each time.
75       *
76       * @param viewId - the unique id for the view
77       * @return <code>View</code> instance
78       */
79      public View getViewById(String viewId) {
80          String beanName = viewBeanEntriesById.get(viewId);
81          if (StringUtils.isBlank(beanName)) {
82              throw new DataDictionaryException("Unable to find View with id: " + viewId);
83          }
84  
85          return ddBeans.getBean(beanName, View.class);
86      }
87  
88      /**
89       * Retrieves a <code>View</code> instance that is of the given type based on
90       * the index key
91       *
92       * @param viewTypeName - type name for the view
93       * @param indexKey - Map of index key parameters, these are the parameters the
94       * indexer used to index the view initially and needs to identify
95       * an unique view instance
96       * @return View instance that matches the given index or Null if one is not
97       *         found
98       */
99      public View getViewByTypeIndex(ViewType viewTypeName, Map<String, String> indexKey) {
100         String index = buildTypeIndex(indexKey);
101 
102         ViewTypeDictionaryIndex typeIndex = getTypeIndex(viewTypeName);
103 
104         String beanName = typeIndex.get(index);
105         if (StringUtils.isNotBlank(beanName)) {
106             return ddBeans.getBean(beanName, View.class);
107         }
108 
109         return null;
110     }
111 
112     /**
113      * Indicates whether a <code>View</code> exists for the given view type and index information
114      *
115      * @param viewTypeName - type name for the view
116      * @param indexKey - Map of index key parameters, these are the parameters the indexer used to index
117      * the view initially and needs to identify an unique view instance
118      * @return boolean true if view exists, false if not
119      */
120     public boolean viewByTypeExist(ViewType viewTypeName, Map<String, String> indexKey) {
121         boolean viewExist = false;
122 
123         String index = buildTypeIndex(indexKey);
124         ViewTypeDictionaryIndex typeIndex = getTypeIndex(viewTypeName);
125 
126         String beanName = typeIndex.get(index);
127         if (StringUtils.isNotBlank(beanName)) {
128             viewExist = true;
129         }
130 
131         return viewExist;
132     }
133 
134     /**
135      * Retrieves the configured property values for the view bean definition associated with the given id
136      *
137      * <p>
138      * Since constructing the View object can be expensive, when metadata only is needed this method can be used
139      * to retrieve the configured property values. Note this looks at the merged bean definition
140      * </p>
141      *
142      * @param viewId - id for the view to retrieve
143      * @return PropertyValues configured on the view bean definition, or null if view is not found
144      */
145     public PropertyValues getViewPropertiesById(String viewId) {
146         String beanName = viewBeanEntriesById.get(viewId);
147         if (StringUtils.isBlank(beanName)) {
148             BeanDefinition beanDefinition = ddBeans.getMergedBeanDefinition(beanName);
149 
150             return beanDefinition.getPropertyValues();
151         }
152 
153         return null;
154     }
155 
156     /**
157      * Retrieves the configured property values for the view bean definition associated with the given type and
158      * index
159      *
160      * <p>
161      * Since constructing the View object can be expensive, when metadata only is needed this method can be used
162      * to retrieve the configured property values. Note this looks at the merged bean definition
163      * </p>
164      *
165      * @param viewTypeName - type name for the view
166      * @param indexKey - Map of index key parameters, these are the parameters the indexer used to index
167      * the view initially and needs to identify an unique view instance
168      * @return PropertyValues configured on the view bean definition, or null if view is not found
169      */
170     public PropertyValues getViewPropertiesByType(ViewType viewTypeName, Map<String, String> indexKey) {
171         String index = buildTypeIndex(indexKey);
172 
173         ViewTypeDictionaryIndex typeIndex = getTypeIndex(viewTypeName);
174 
175         String beanName = typeIndex.get(index);
176         if (StringUtils.isNotBlank(beanName)) {
177             BeanDefinition beanDefinition = ddBeans.getMergedBeanDefinition(beanName);
178 
179             return beanDefinition.getPropertyValues();
180         }
181 
182         return null;
183     }
184 
185     /**
186      * Gets all <code>View</code> prototypes configured for the given view type
187      * name
188      *
189      * @param viewTypeName - view type name to retrieve
190      * @return List<View> view prototypes with the given type name, or empty
191      *         list
192      */
193     public List<View> getViewsForType(ViewType viewTypeName) {
194         List<View> typeViews = new ArrayList<View>();
195 
196         // get view ids for the type
197         if (viewEntriesByType.containsKey(viewTypeName.name())) {
198             ViewTypeDictionaryIndex typeIndex = viewEntriesByType.get(viewTypeName.name());
199             for (Entry<String, String> typeEntry : typeIndex.getViewIndex().entrySet()) {
200                 View typeView = ddBeans.getBean(typeEntry.getValue(), View.class);
201                 typeViews.add(typeView);
202             }
203         } else {
204             throw new DataDictionaryException("Unable to find view index for type: " + viewTypeName);
205         }
206 
207         return typeViews;
208     }
209 
210     /**
211      * Initializes the view index <code>Map</code> then iterates through all the
212      * beans in the factory that implement <code>View</code>, adding them to the
213      * index
214      */
215     protected void buildViewIndicies() {
216         viewBeanEntriesById = new HashMap<String, String>();
217         viewEntriesByType = new HashMap<String, ViewTypeDictionaryIndex>();
218 
219         String[] beanNames = ddBeans.getBeanNamesForType(View.class);
220         for (int i = 0; i < beanNames.length; i++) {
221             String beanName = beanNames[i];
222             BeanDefinition beanDefinition = ddBeans.getMergedBeanDefinition(beanName);
223             PropertyValues propertyValues = beanDefinition.getPropertyValues();
224 
225             String id = ViewModelUtils.getStringValFromPVs(propertyValues, "id");
226             if (StringUtils.isBlank(id)) {
227                 id = beanName;
228             }
229 
230             if (viewBeanEntriesById.containsKey(id)) {
231                 throw new DataDictionaryException("Two views must not share the same id. Found duplicate id: " + id);
232             }
233             viewBeanEntriesById.put(id, beanName);
234 
235             indexViewForType(propertyValues, beanName);
236         }
237     }
238 
239     /**
240      * Performs additional indexing based on the view type associated with the view instance. The
241      * <code>ViewTypeService</code> associated with the view type name on the instance is invoked to retrieve
242      * the parameter key/value pairs from the configured property values, which are then used to build up an index
243      * used to key the entry
244      *
245      * @param propertyValues - property values configured on the view bean definition
246      * @param beanName - name of the view's bean in Spring
247      */
248     protected void indexViewForType(PropertyValues propertyValues, String beanName) {
249         String viewTypeName = ViewModelUtils.getStringValFromPVs(propertyValues,  "viewTypeName");
250         if (StringUtils.isBlank(viewTypeName)) {
251             return;
252         }
253 
254         UifConstants.ViewType viewType = ViewType.valueOf(viewTypeName);
255 
256         ViewTypeService typeService = KRADServiceLocatorWeb.getViewService().getViewTypeService(viewType);
257         if (typeService == null) {
258             // don't do any further indexing
259             return;
260         }
261 
262         // invoke type service to retrieve it parameter name/value pairs
263         Map<String, String> typeParameters = typeService.getParametersFromViewConfiguration(propertyValues);
264 
265         // build the index string from the parameters
266         String index = buildTypeIndex(typeParameters);
267 
268         // get the index for the type and add the view entry
269         ViewTypeDictionaryIndex typeIndex = getTypeIndex(viewType);
270 
271         typeIndex.put(index, beanName);
272     }
273 
274     /**
275      * Retrieves the <code>ViewTypeDictionaryIndex</code> instance for the given
276      * view type name. If one does not exist yet for the given name, a new
277      * instance is created
278      *
279      * @param viewType - name of the view type to retrieve index for
280      * @return ViewTypeDictionaryIndex instance
281      */
282     protected ViewTypeDictionaryIndex getTypeIndex(UifConstants.ViewType viewType) {
283         ViewTypeDictionaryIndex typeIndex = null;
284 
285         if (viewEntriesByType.containsKey(viewType.name())) {
286             typeIndex = viewEntriesByType.get(viewType.name());
287         } else {
288             typeIndex = new ViewTypeDictionaryIndex();
289             viewEntriesByType.put(viewType.name(), typeIndex);
290         }
291 
292         return typeIndex;
293     }
294 
295     /**
296      * Builds up an index string from the given Map of parameters
297      *
298      * @param typeParameters - Map of parameters to use for index
299      * @return String index
300      */
301     protected String buildTypeIndex(Map<String, String> typeParameters) {
302         String index = "";
303 
304         for (String parameterName : typeParameters.keySet()) {
305             if (StringUtils.isNotBlank(index)) {
306                 index += "|||";
307             }
308             index += parameterName + "^^" + typeParameters.get(parameterName);
309         }
310 
311         return index;
312     }
313 
314 }