View Javadoc

1   /*
2    * Copyright 2012 Vincent Demeester<vincent+shortbrain@demeester.fr>.
3    * 
4    * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5    * use this file except in compliance with the License. You may obtain a copy of
6    * the License at
7    * 
8    * http://www.apache.org/licenses/LICENSE-2.0
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, WITHOUT
12   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13   * License for the specific language governing permissions and limitations under
14   * the License.
15   */
16  package org.shortbrain.vaadin.container;
17  
18  import static org.shortbrain.vaadin.container.ContainerUtils.addContainerProperty;
19  import static org.shortbrain.vaadin.container.ContainerUtils.initContainer;
20  
21  import java.lang.reflect.InvocationTargetException;
22  import java.util.Collection;
23  import java.util.List;
24  
25  import org.apache.commons.beanutils.PropertyUtils;
26  import org.shortbrain.vaadin.container.property.PropertyMetadata;
27  import org.shortbrain.vaadin.container.property.PropertyReaderAlgorithm;
28  
29  import com.vaadin.data.Container;
30  import com.vaadin.data.Container.Filterable;
31  import com.vaadin.data.Container.Hierarchical;
32  
33  /**
34   * Default abstract implementation of {@link ContainerFactory}.
35   * 
36   * @author Vincent Demeester <vincent@demeester.fr>
37   * 
38   * @param <BEAN>
39   *            type of the beans.
40   */
41  public abstract class AbstractContainerFactory<BEAN> extends
42  		ContainerFactory<BEAN> {
43  
44  	/**
45  	 * Default BEAN property name.
46  	 */
47  	private static final String DEFAULT_BEAN_PROPERTY = "bean";
48  
49  	/**
50  	 * Type of the bean.
51  	 */
52  	private Class<? extends BEAN> beanClass;
53  
54  	/**
55  	 * The property reader algorithm.
56  	 */
57  	private PropertyReaderAlgorithm propertyReaderAlgorithm;
58  
59  	/**
60  	 * The name of the property that holds the bean.
61  	 */
62  	private String beanProperty = DEFAULT_BEAN_PROPERTY;
63  
64  	/**
65  	 * Creates an AbstractContainerFactory.
66  	 * 
67  	 * @param beanClass
68  	 *            the type of BEAN.
69  	 * @param propertyReaderAlgorithm
70  	 *            the algorithm to be used to get properties.
71  	 * @throws IllegalArgumentException
72  	 *             if beanClass or propertyReaderAlgorithm are null.
73  	 */
74  	public AbstractContainerFactory(Class<? extends BEAN> beanClass,
75  			PropertyReaderAlgorithm propertyReaderAlgorithm) {
76  		if (beanClass == null || propertyReaderAlgorithm == null) {
77  			throw new IllegalArgumentException(
78  					"beanClass and propertyReaderAlgorithm cannot be null.");
79  		}
80  		this.beanClass = beanClass;
81  		this.propertyReaderAlgorithm = propertyReaderAlgorithm;
82  	}
83  
84  	/**
85  	 * {@inheritDoc}
86  	 * 
87  	 * If container is null, the default type will be {@link Filterable}
88  	 */
89  	@Override
90  	public Container getContainerFromList(Container container, List<BEAN> beans) {
91  		return getContainerFromCollection(container, beans);
92  	}
93  
94  	/**
95  	 * {@inheritDoc}
96  	 */
97  	@Override
98  	public Container getContainerFromList(List<BEAN> beans,
99  			Class<? extends Container> containerClass) {
100 		return getContainerFromCollection(beans, containerClass);
101 	}
102 
103 	/**
104 	 * {@inheritDoc}
105 	 */
106 	@Override
107 	public Container getContainerFromList(Container container,
108 			List<BEAN> beans, Class<? extends Container> containerClass) {
109 		return getContainerFromCollection(container, beans, containerClass);
110 	}
111 
112 	/**
113 	 * {@inheritDoc}
114 	 * 
115 	 * If container is null, the default type will be {@link Filterable}
116 	 */
117 	@Override
118 	public Container getContainerFromCollection(Container container,
119 			Collection<BEAN> beans) {
120 		Class<? extends Container> containerClass = (container != null) ? container
121 				.getClass() : Filterable.class;
122 		return getContainerFromCollection(container, beans, containerClass);
123 	}
124 
125 	/**
126 	 * {@inheritDoc}
127 	 */
128 	@Override
129 	public Container getContainerFromCollection(Collection<BEAN> beans,
130 			Class<? extends Container> containerClass) {
131 		return getContainerFromCollection(null, beans, containerClass);
132 	}
133 
134 	/**
135 	 * {@inheritDoc}
136 	 */
137 	@Override
138 	public Container getContainerFromCollection(Container container,
139 			Collection<BEAN> beans, Class<? extends Container> containerClass) {
140 		try {
141 			// Initialize the container if null
142 			if (container == null) {
143 				container = initContainer(containerClass);
144 			}
145 			// Property
146 			List<PropertyMetadata> properties = updateProperties(container);
147 			// Clean it
148 			if (container.removeAllItems()) {
149 				populateContainer(container, properties, beans);
150 			}
151 		} catch (InstantiationException e) {
152 			// TODO Auto-generated catch block
153 		} catch (IllegalAccessException e) {
154 			// TODO Auto-generated catch block
155 		}
156 		return container;
157 	}
158 
159 	/**
160 	 * Create or update properties of the given container.
161 	 * 
162 	 * @param container
163 	 *            the container to be updated.
164 	 */
165 	private List<PropertyMetadata> updateProperties(Container container) {
166 		List<PropertyMetadata> properties = propertyReaderAlgorithm
167 				.getProperties(beanClass);
168 		Collection<?> containerProperties = container.getContainerPropertyIds();
169 		for (PropertyMetadata property : properties) {
170 			if (!containerProperties.contains(property.getPropertyName())) {
171 				addContainerProperty(container, property);
172 			}
173 		}
174 		// Add a bean property at the end.
175 		addContainerProperty(container, getBeanProperty(), beanClass, null);
176 		return properties;
177 	}
178 
179 	/**
180 	 * Populate the given container with the list of BEAN.
181 	 * 
182 	 * There is a few different case to handle. An example is a
183 	 * {@link Hierarchical} containers, that could have children, so this method
184 	 * needs a way to identify the children, etc…
185 	 * 
186 	 * Notes : the default behavior for now is to look for an attribute
187 	 * <code>children</code> of the BEAN object. But in the future it will need
188 	 * to support more flexible way to know how to handle specific case.
189 	 * 
190 	 * @param container
191 	 *            the container to be populated.
192 	 * @param properties
193 	 *            the properties.
194 	 * @param beans
195 	 *            the list of beans.
196 	 */
197 	private void populateContainer(Container container,
198 			List<PropertyMetadata> properties, Collection<BEAN> beans) {
199 		if (beans != null) {
200 			if (container instanceof Hierarchical) {
201 				for (BEAN bean : beans) {
202 					addHierarchicalItem((Hierarchical) container, properties,
203 							bean, null);
204 				}
205 			} else {
206 				for (BEAN bean : beans) {
207 					addItem(container, properties, bean, true);
208 				}
209 			}
210 		}
211 	}
212 
213 	/**
214 	 * Add an item (using bean) to the container, and look for
215 	 * <code>children</code> if needed.
216 	 * 
217 	 * @param container
218 	 *            the container.
219 	 * @param properties
220 	 *            the properties.
221 	 * @param bean
222 	 *            the bean to add.
223 	 * @param introspect
224 	 *            introspect for children.
225 	 * @return the id of the added item
226 	 */
227 	@SuppressWarnings("unchecked")
228 	protected Object addItem(Container container,
229 			List<PropertyMetadata> properties, BEAN bean, boolean introspect) {
230 		Object itemId = container.addItem();
231 		for (PropertyMetadata metadata : properties) {
232 			String propertyId = metadata.getPropertyName();
233 			Object value = null;
234 			try {
235 				value = PropertyUtils.getProperty(bean,
236 						metadata.getPropertyAttribute());
237 			} catch (IllegalAccessException e) {
238 				// TODO Auto-generated catch block
239 				e.printStackTrace();
240 			} catch (InvocationTargetException e) {
241 				// TODO Auto-generated catch block
242 				e.printStackTrace();
243 			} catch (NoSuchMethodException e) {
244 				// TODO Auto-generated catch block
245 				e.printStackTrace();
246 			}
247 			container.getContainerProperty(itemId, propertyId).setValue(value);
248 		}
249 		// At the end, add the bean to the container (just in case)
250 		container.getContainerProperty(itemId, getBeanProperty())
251 				.setValue(bean);
252 		if (introspect) {
253 			Collection<BEAN> children;
254 			try {
255 				children = (Collection<BEAN>) PropertyUtils.getProperty(bean,
256 						"children");
257 				if (children != null && !children.isEmpty()) {
258 					for (BEAN child : children) {
259 						addItem(container, properties, child, introspect);
260 					}
261 				}
262 			} catch (IllegalAccessException e) {
263 				// TODO Auto-generated catch block
264 			} catch (InvocationTargetException e) {
265 				// TODO Auto-generated catch block
266 			} catch (NoSuchMethodException e) {
267 				// TODO Auto-generated catch block
268 			} catch (RuntimeException e) {
269 				// FIXME This is evil (but I need this temporarly for hibernate
270 				// lazyInitialisation)
271 				// e.printStackTrace();
272 			}
273 		}
274 		return itemId;
275 	}
276 
277 	/**
278 	 * Add a hierarchical item (using bean) to the container.
279 	 * 
280 	 * @param container
281 	 *            the container.
282 	 * @param properties
283 	 *            the properties.
284 	 * @param bean
285 	 *            the bean to add.
286 	 * @param parentId
287 	 *            the parent of the bean.
288 	 */
289 	@SuppressWarnings("unchecked")
290 	protected void addHierarchicalItem(Hierarchical container,
291 			List<PropertyMetadata> properties, BEAN bean, Object parentId) {
292 		Object itemId = addItem(container, properties, bean, false);
293 		if (parentId != null) {
294 			// Parent id can have children.
295 			container.setChildrenAllowed(parentId, true);
296 			// Set the parent for the current id.
297 			container.setParent(itemId, parentId);
298 			// Set no children by default
299 			container.setChildrenAllowed(itemId, false);
300 		}
301 		Collection<BEAN> children;
302 		try {
303 			children = (Collection<BEAN>) PropertyUtils.getProperty(bean,
304 					"children");
305 			if (children != null && !children.isEmpty()) {
306 				for (BEAN child : children) {
307 					addHierarchicalItem(container, properties, child, itemId);
308 				}
309 			}
310 		} catch (IllegalAccessException e) {
311 			// TODO Auto-generated catch block
312 		} catch (InvocationTargetException e) {
313 			// TODO Auto-generated catch block
314 		} catch (NoSuchMethodException e) {
315 			// TODO Auto-generated catch block
316 		} catch (RuntimeException e) {
317 			// FIXME This is evil (but I need this temporarly for hibernate
318 			// lazyInitialisation)
319 			// e.printStackTrace();
320 		}
321 	}
322 
323 	/**
324 	 * Get the name of the bean property.
325 	 * 
326 	 * @return the name of the property that holds the bean
327 	 */
328 	public String getBeanProperty() {
329 		return beanProperty;
330 	}
331 
332 	/**
333 	 * {@inheritDoc}
334 	 */
335 	public void setBeanProperty(String name) {
336 		if (beanProperty == null || beanProperty.equals("")) {
337 			this.beanProperty = DEFAULT_BEAN_PROPERTY;
338 		} else {
339 			this.beanProperty = name;
340 		}
341 	}
342 
343 }