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
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
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
338
339
340 public void propertyChange(PropertyChangeEvent event) {
341
342 System.err.println(this.getClass()+"::propertyChange::"+event.getPropertyName());
343
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 }