View Javadoc

1   /***
2    * Copyright (C) 2006 Philipp Mpalampanis
3    *
4    * License: MPL 1.1/GPL 2.0/LGPL 2.1
5    *
6    * The contents of this file are subject to the Mozilla Public License Version
7    * 1.1 (the "License"); you may not use this file except in compliance with
8    * the License. You may obtain a copy of the License at
9    * http://www.mozilla.org/MPL/
10   *
11   * Software distributed under the License is distributed on an "AS IS" basis,
12   * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13   * for the specific language governing rights and limitations under the
14   * License.
15   *
16   * Alternatively, the contents of this file may be used under the terms of
17   * either the GNU General Public License Version 2 or later (the "GPL"), or
18   * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
19   * in which case the provisions of the GPL or the LGPL are applicable instead
20   * of those above. If you wish to allow use of your version of this file only
21   * under the terms of either the GPL or the LGPL, and not to allow others to
22   * use your version of this file under the terms of the MPL, indicate your
23   * decision by deleting the provisions above and replace them with the notice
24   * and other provisions required by the GPL or the LGPL. If you do not delete
25   * the provisions above, a recipient may use your version of this file under
26   * the terms of any one of the MPL, the GPL or the LGPL.
27   */
28  
29  package net.sf.echobinding.table;
30  
31  import java.beans.PropertyChangeEvent;
32  import java.beans.PropertyChangeListener;
33  import java.util.*;
34  
35  import net.sf.echobinding.binding.BindingContext;
36  import net.sf.echobinding.binding.PropertyAdapter;
37  import nextapp.echo2.app.*;
38  import nextapp.echo2.app.event.ActionEvent;
39  import nextapp.echo2.app.event.ActionListener;
40  import nextapp.echo2.app.table.TableCellRenderer;
41  import nextapp.echo2.app.table.TableModel;
42  
43  import org.apache.log4j.Logger;
44  
45  /***
46   * A data bound table. Displays a list of items in form of a table. The columns
47   * of the table are defined by the property adapters of the given
48   * <code>BindingContext</code>.
49   * 
50   * A <code>BoundTable</code> uses by default the
51   * <code>DefaultDataBoundTableCellRenderer</code> for rendering its cells.
52   * 
53   * @param <T> The type of the obejcts in the list.
54   */
55  public class BoundTable<T> extends Table implements ActionListener, PropertyChangeListener {
56  
57  	private static final long serialVersionUID = 3177515466581414146L;
58  
59  	private Logger _logger = Logger.getLogger( getClass() );
60  	
61  	/***
62  	 * The list of items.
63  	 */
64  	protected List<T> _list;
65  
66  	/***
67  	 * The binding context.
68  	 */
69  	protected BindingContext _ctx;
70  
71  	/***
72  	 * The property adapters.
73  	 */
74  	private List<PropertyAdapter> _columnAdapters = new ArrayList<PropertyAdapter>();
75  
76  	/***
77  	 * The ActionListeners.
78  	 */
79  	private ActionListener _actionListener;
80  
81  	/***
82  	 * Array of binding contexts. There will be one context for each row in the table.
83  	 */
84  	private BindingContext[] _rowContexts;
85  
86  	/***
87  	 * The header background color.
88  	 */
89  	private Color _headerBackground;
90  
91  
92  	private DetailsHandler _detailHandler;
93  	
94  	/***
95  	 * Creates a data bound table using a list. 
96  	 * 
97  	 * @param list the list of items
98  	 * @param ctx the binding context
99  	 */
100 	public BoundTable(List<T> list, BindingContext ctx) {
101 		super();
102 
103 		_list = list;
104 		_ctx = ctx;
105 
106 	}
107 
108 	@Override
109 	public void init() {
110 		super.init();
111 		initialize();
112 	}
113 	
114 	
115 	
116 	/***
117 	 * Creates a data bound table using a list adapter. 
118 	 * 
119 	 * @param listAdapterId the list adapter id
120 	 * @param ctx the binding context
121 	 */
122 	@SuppressWarnings("unchecked")
123 	public BoundTable(String listAdapterId, BindingContext ctx) {
124 		this((List<T>) ctx.getValue(listAdapterId), ctx);
125 	}
126 
127 	
128 	/***
129 	 * Adds a column for the specified adpater to the table.
130 	 * 
131 	 * @param adapterId the adapter id
132 	 * 
133 	 * @return the data bound table
134 	 */
135 	public BoundTable<T> addColumn(String adapterId) {
136 		return addColumn(null, adapterId);
137 	}
138 
139 	/***
140 	 * Removes a column from the table.
141 	 * 
142 	 * @param adapterId The adapter id of the column to be removed
143 	 * 
144 	 * @return true, if the column was successfully removed, false else
145 	 */
146 	public boolean removeColumn(String adapterId) {
147 		return getColumnAdapters().remove( _ctx.getAdapter(adapterId));
148 	}
149 
150 	/***
151 	 * Adds a column at the specified position to the table.
152 	 * 
153 	 * @param adapterId the adapter id
154 	 * @param columnIndex the column index
155 	 * 
156 	 * @return the data bound table
157 	 */
158 	public BoundTable<T> addColumn(Integer columnIndex, String adapterId ) {
159 		if(columnIndex!=null)
160 			_columnAdapters.add( columnIndex, _ctx.getAdapter(adapterId) );
161 		else
162 			_columnAdapters.add( _ctx.getAdapter(adapterId) );
163 		return this;
164 	}
165 	
166 	
167 	
168 	/***
169 	 * Initializes the table. Extracts the table headers from the binding
170 	 * context and creates the table data.
171 	 */
172 	protected void initialize() {
173 
174 		_rowContexts = new BindingContext[ _list.size() ];
175 		int i = 0;
176 		for (T row : getList() ) _rowContexts[i++] = _ctx.createChild().setModel(row);
177 		
178 		setModel(createTableModel());
179 
180 		setDefaultHeaderRenderer(getTableHeaderRenderer());
181 
182 		TableCellRenderer defaultRenderer = getTableCellRenderer();
183 		setDefaultRenderer(Integer.class, defaultRenderer);
184 		setDefaultRenderer(String.class, defaultRenderer);
185 		setDefaultRenderer(Double.class, defaultRenderer);
186 		setDefaultRenderer(Long.class, defaultRenderer);
187 		setDefaultRenderer(Boolean.class, defaultRenderer);
188 		setDefaultRenderer(Object.class, defaultRenderer);
189 		
190 		// collections rendering 
191 		BoundTableCollectionCellRenderer listTableCellRenderer = new BoundTableCollectionCellRenderer();
192 		setDefaultRenderer(HashSet.class, listTableCellRenderer);
193 		setDefaultRenderer(Collection.class, listTableCellRenderer);
194 		setDefaultRenderer(List.class, listTableCellRenderer);
195 
196 		setBorder(new Border(1, Color.DARKGRAY, Border.STYLE_SOLID));
197 		
198 		setInsets(new Insets(5,5));
199 		
200 	}
201 
202 	/***
203 	 * Gets the table header renderer.
204 	 * 
205 	 * @return the table header renderer
206 	 */
207 	protected BoundTableHeaderRenderer getTableHeaderRenderer() {
208 		return new BoundTableHeaderRenderer();
209 	}
210 
211 	/***
212 	 * Gets the table cell renderer.
213 	 * 
214 	 * @return the table cell renderer
215 	 */
216 	protected TableCellRenderer getTableCellRenderer() {
217 		return new DefaultDataBoundTableCellRenderer();
218 	}
219 
220 	/***
221 	 * Creates the table model.
222 	 * 
223 	 * @return the table model
224 	 */
225 	protected TableModel createTableModel() {
226 		return new BoundTableModel(createTableHeaderData(), createTableData());
227 	}
228 
229 	/***
230 	 * Creates the table data.
231 	 * 
232 	 * @return the object[][]
233 	 */
234 	protected Object[][] createTableData() {
235 		
236 		Object[][] data = new Object[getList().size()][getColumnAdapters().size()];
237 		int rowIndex = 0;
238 		for (Object obj : getList()) {
239 			data[rowIndex++] = createRow(obj);
240 		}
241 		 
242 		return data;
243 	}
244 
245 	/***
246 	 * Creates the table header data.
247 	 * 
248 	 * @return the object[]
249 	 */
250 	protected Object[] createTableHeaderData() {
251 		
252 		// use adapter labels as table headers
253 		ArrayList<String> tmp = new ArrayList<String>();
254 		for (PropertyAdapter adapter : getColumnAdapters()) {
255 			tmp.add( adapter.getLabel() );
256 		}
257 		Object[] tableHeaderData = tmp.toArray(new Object[0]);
258 		
259 		return tableHeaderData;
260 	}
261 
262 	/***
263 	 * Creates a row from the given bean.  
264 	 * 
265 	 * @param rowData the row data
266 	 * @return a row in form of an array
267 	 */
268 	@SuppressWarnings("unchecked")
269 	private Object[] createRow(Object rowData) {
270 
271 		ArrayList<Object> row = new ArrayList<Object>();
272 		for (PropertyAdapter binding : getColumnAdapters()) {
273 			Object value = binding.getValue(rowData);
274 			if (value instanceof Set) {
275 				row.add( new HashSet((Set)value) );
276 			}
277 			else {
278 				row.add(value);
279 			}
280 		}
281 		return row.toArray(new Object[0]);
282 	}
283 
284 	/***
285 	 * Returns the adapter id for the specified column. The adapter id can be
286 	 * used in the TableCellRenderer to create data bound widgets.
287 	 * 
288 	 * @param col the col
289 	 * @return the binding id for the given column index
290 	 */
291 	public String getAdapterId(int col) {
292 		return getColumnAdapters().get(col).getId();
293 	}
294 
295 	/***
296 	 * Returns the binding context for the specified row. The binding id can be
297 	 * used in the TableCellRenderer to create data bound widgets.
298 	 * 
299 	 * @param rowIndex the index of the row
300 	 * @return the binding context for the given row index
301 	 */
302 	public BindingContext getBindingContext(int rowIndex) {
303 		return _rowContexts[rowIndex];
304 	}
305 
306 	public void actionPerformed(ActionEvent event) {}
307 
308 	/***
309 	 * Returns the action listener.
310 	 * 
311 	 * @return the action listener
312 	 */
313 	public ActionListener getActionListener() {
314 		return _actionListener;
315 	}
316 
317 	/***
318 	 * Sets the action listener.
319 	 * 
320 	 * @param actionListener the action listener
321 	 */
322 	public void setActionListener(ActionListener actionListener) {
323 		_actionListener = actionListener;
324 	}
325 
326 	/***
327 	 * Returns the item for the specified row index.
328 	 * 
329 	 * @param row - the row index
330 	 * @return the item for the given row index
331 	 */
332 	protected T getItem(int row) {
333 		return _list.get(row);
334 	}
335 
336 	
337 	/* (non-Javadoc)
338 	 * @see java.beans.PropertyChangeListener#propertyChange(java.beans.PropertyChangeEvent)
339 	 */
340 	public void propertyChange(PropertyChangeEvent event) {
341 		
342 		System.err.println(this.getClass()+"::propertyChange::"+event.getPropertyName());
343 		// initialize();
344 	}
345 
346 	/***
347 	 * Returns the column adapters.
348 	 * 
349 	 * @return Returns the property adapters for this table.
350 	 */
351 	public List<PropertyAdapter> getColumnAdapters() {
352 		return _columnAdapters;
353 	}
354 
355 	/***
356 	 * Sets the column adapters.
357 	 * 
358 	 * @param columnAdapters The column adapters to set.
359 	 */
360 	public void setColumnAdapters(List<PropertyAdapter> columnAdapters) {
361 		_columnAdapters = columnAdapters;
362 	}
363 
364 	/***
365 	 * Returns the list.
366 	 * 
367 	 * @return Returns the list.
368 	 */
369 	public List<T> getList() {
370 		return _list;
371 	}
372 
373 	/***
374 	 * Sets the list.
375 	 * 
376 	 * @param list The list to set.
377 	 */
378 	public void setList(List<T> list) {
379 		_list = list;
380 	}
381 
382 	/***
383 	 * Returns the header background color.
384 	 * 
385 	 * @return the header background color
386 	 */
387 	public Color getHeaderBackground() {
388 		return _headerBackground;
389 	}
390 
391 	/***
392 	 * Sets the header background color.
393 	 * 
394 	 * @param headerBackground the header background
395 	 */
396 	public void setHeaderBackground(Color headerBackground) {
397 		_headerBackground = headerBackground;
398 	}
399 	
400 	/***
401 	 * @param columnIndex 
402 	 * @param rowIndex 
403 	 */
404 	@SuppressWarnings("unchecked")
405 	protected void showDetails(int columnIndex, int rowIndex) {
406 		
407 		if(getDetailHandler() == null) {
408 			_logger.warn("No detail handler defined.");
409 			return;
410 		}
411 		
412 		String adapterId = getAdapterId(columnIndex);
413 		
414 		BindingContext context = getBindingContext(rowIndex);
415 		PropertyAdapter adapter = context.getAdapter(adapterId);
416 		
417 		BindingContext oneToManyContext = adapter.getSubContext();
418 		
419 		Set set = (Set) context.getValue(adapterId);
420 		
421 		
422 		getDetailHandler().handleDetails(adapterId, new ArrayList(set), oneToManyContext);
423 	}
424 	
425 	/***
426 	 * @return Returns the detailHandler.
427 	 */
428 	public DetailsHandler getDetailHandler() {
429 		return _detailHandler;
430 	}
431 
432 	/***
433 	 * @param detailHandler The detailHandler to set.
434 	 */
435 	public void setDetailHandler(DetailsHandler detailHandler) {
436 		_detailHandler = detailHandler;
437 	}
438 
439 	/***
440 	 * @return Returns the ctx.
441 	 */
442 	public BindingContext getContext() {
443 		return _ctx;
444 	}
445 
446 	/***
447 	 * @param ctx The ctx to set.
448 	 */
449 	public void setContext(BindingContext ctx) {
450 		_ctx = ctx;
451 	}
452 		
453 }