1 |
| |
2 |
| |
3 |
| |
4 |
| |
5 |
| |
6 |
| |
7 |
| |
8 |
| |
9 |
| |
10 |
| |
11 |
| |
12 |
| |
13 |
| |
14 |
| |
15 |
| |
16 |
| |
17 |
| |
18 |
| |
19 |
| |
20 |
| |
21 |
| |
22 |
| |
23 |
| |
24 |
| |
25 |
| |
26 |
| |
27 |
| |
28 |
| |
29 |
| |
30 |
| |
31 |
| |
32 |
| |
33 |
| |
34 |
| |
35 |
| |
36 |
| |
37 |
| |
38 |
| package com.gargoylesoftware.base.gui; |
39 |
| |
40 |
| import com.gargoylesoftware.base.collections.NotificationList; |
41 |
| import com.gargoylesoftware.base.collections.NotificationListEvent; |
42 |
| import com.gargoylesoftware.base.collections.NotificationListListener; |
43 |
| import com.gargoylesoftware.base.trace.Trace; |
44 |
| import com.gargoylesoftware.base.trace.TraceChannel; |
45 |
| import com.gargoylesoftware.base.util.DetailedIllegalArgumentException; |
46 |
| import com.gargoylesoftware.base.util.DetailedNullPointerException; |
47 |
| import java.beans.BeanInfo; |
48 |
| import java.beans.IntrospectionException; |
49 |
| import java.beans.Introspector; |
50 |
| import java.beans.PropertyChangeEvent; |
51 |
| import java.beans.PropertyChangeListener; |
52 |
| import java.beans.PropertyDescriptor; |
53 |
| import java.lang.reflect.InvocationTargetException; |
54 |
| import java.lang.reflect.Method; |
55 |
| import java.util.ArrayList; |
56 |
| import java.util.Collections; |
57 |
| import java.util.HashMap; |
58 |
| import java.util.List; |
59 |
| import java.util.Map; |
60 |
| import javax.swing.table.AbstractTableModel; |
61 |
| import java.util.Iterator; |
62 |
| |
63 |
| |
64 |
| |
65 |
| |
66 |
| |
67 |
| |
68 |
| |
69 |
| |
70 |
| |
71 |
| |
72 |
| |
73 |
| |
74 |
| |
75 |
| |
76 |
| |
77 |
| |
78 |
| |
79 |
| |
80 |
| |
81 |
| |
82 |
| |
83 |
| |
84 |
| |
85 |
| |
86 |
| |
87 |
| |
88 |
| |
89 |
| |
90 |
| |
91 |
| |
92 |
| |
93 |
| |
94 |
| public class ReflectedTableModel extends AbstractTableModel { |
95 |
| |
96 |
| |
97 |
| |
98 |
| |
99 |
| |
100 |
| |
101 |
| private static final long serialVersionUID = -7537209244601820035L; |
102 |
| |
103 |
| |
104 |
| |
105 |
| |
106 |
| public static class ColumnInfo { |
107 |
| private final String columnName_; |
108 |
| private final String propertyName_; |
109 |
| |
110 |
| |
111 |
| |
112 |
| |
113 |
| |
114 |
| |
115 |
6
| public ColumnInfo( final String columnName, final String propertyName ) {
|
116 |
6
| if( columnName == null ) {
|
117 |
0
| throw new DetailedNullPointerException("columnName");
|
118 |
| } |
119 |
6
| if( propertyName == null ) {
|
120 |
0
| throw new DetailedNullPointerException("propertyName");
|
121 |
| } |
122 |
| |
123 |
6
| columnName_ = columnName;
|
124 |
6
| propertyName_ = propertyName;
|
125 |
| } |
126 |
| |
127 |
| |
128 |
| |
129 |
| |
130 |
| |
131 |
6
| public ColumnInfo( final String name ) {
|
132 |
6
| this( name, name );
|
133 |
| } |
134 |
| |
135 |
| |
136 |
| |
137 |
| |
138 |
| |
139 |
3
| public String getColumnName() {
|
140 |
3
| return columnName_;
|
141 |
| } |
142 |
| |
143 |
| |
144 |
| |
145 |
| |
146 |
| |
147 |
11
| public String getPropertyName() {
|
148 |
11
| return propertyName_;
|
149 |
| } |
150 |
| } |
151 |
| |
152 |
| |
153 |
| |
154 |
| |
155 |
| private NotificationListListener rowListener_ |
156 |
| = new NotificationListListener() { |
157 |
| |
158 |
4
| public void listElementsAdded( final NotificationListEvent event ) {
|
159 |
4
| addRowElements( event.getNewValues() );
|
160 |
4
| fireTableRowsInserted( event.getStartIndex(), event.getEndIndex() );
|
161 |
| } |
162 |
0
| public void listElementsRemoved( final NotificationListEvent event ) {
|
163 |
0
| removeRowElements( event.getOldValues() );
|
164 |
0
| fireTableRowsDeleted( event.getStartIndex(), event.getEndIndex() );
|
165 |
| } |
166 |
0
| public void listElementsChanged( final NotificationListEvent event ) {
|
167 |
0
| removeRowElements( event.getOldValues() );
|
168 |
0
| addRowElements( event.getNewValues() );
|
169 |
0
| fireTableRowsUpdated( event.getStartIndex(), event.getEndIndex() );
|
170 |
| } |
171 |
| }; |
172 |
| |
173 |
| |
174 |
| |
175 |
| |
176 |
| private NotificationListListener columnListener_ |
177 |
| = new NotificationListListener() { |
178 |
| |
179 |
6
| public void listElementsAdded( final NotificationListEvent event ) {
|
180 |
6
| fireTableStructureChanged();
|
181 |
| } |
182 |
0
| public void listElementsRemoved( final NotificationListEvent event ) {
|
183 |
0
| fireTableStructureChanged();
|
184 |
| } |
185 |
0
| public void listElementsChanged( final NotificationListEvent event ) {
|
186 |
0
| fireTableStructureChanged();
|
187 |
| } |
188 |
| }; |
189 |
| |
190 |
| |
191 |
| |
192 |
| |
193 |
| private PropertyChangeListener propertyChangeListener_ |
194 |
| = new PropertyChangeListener() { |
195 |
| |
196 |
0
| public void propertyChange( final PropertyChangeEvent event ) {
|
197 |
0
| final int row = rows_.indexOf( event.getSource() );
|
198 |
0
| final int columnCount = columns_.size();
|
199 |
0
| final String propertyName = event.getPropertyName();
|
200 |
0
| ColumnInfo columnInfo;
|
201 |
| |
202 |
0
| int i;
|
203 |
0
| for( i=0; i<columnCount; i++ ) {
|
204 |
0
| columnInfo = (ColumnInfo)columns_.get(i);
|
205 |
0
| if( columnInfo.getPropertyName().equals(propertyName) ) {
|
206 |
0
| fireTableCellUpdated( row, i );
|
207 |
0
| if( traceChannel_ != null ) {
|
208 |
0
| Trace.println(traceChannel_,
|
209 |
| "ReflectedTableModel property changed:" |
210 |
| + " property=" + propertyName |
211 |
| + " row=" +row |
212 |
| + " column=" + i); |
213 |
| } |
214 |
| } |
215 |
| } |
216 |
| } |
217 |
| }; |
218 |
| |
219 |
| private class RowElementControlData { |
220 |
| |
221 |
| |
222 |
| |
223 |
| public int instanceCounter_ = 0; |
224 |
| |
225 |
| |
226 |
| |
227 |
| |
228 |
| public boolean supportsPropertyChangeEvents_ = false; |
229 |
| } |
230 |
| |
231 |
| |
232 |
| private final Map rowElementControlDatas_ = new HashMap(); |
233 |
| |
234 |
| private final List rows_; |
235 |
| private final List columns_; |
236 |
| |
237 |
| private static final Object[] EMPTY_OBJECT_ARRAY = new Object[0]; |
238 |
| |
239 |
| |
240 |
| private TraceChannel traceChannel_ = null; |
241 |
| |
242 |
| |
243 |
| |
244 |
| |
245 |
2
| public ReflectedTableModel() {
|
246 |
| |
247 |
2
| final NotificationList columnList = new NotificationList( new ArrayList() );
|
248 |
2
| columnList.addNotificationListListener(columnListener_);
|
249 |
2
| columns_ = Collections.synchronizedList( columnList );
|
250 |
| |
251 |
2
| final NotificationList rowList = new NotificationList( new ArrayList() );
|
252 |
2
| rowList.addNotificationListListener(rowListener_);
|
253 |
2
| rows_ = Collections.synchronizedList( rowList );
|
254 |
| } |
255 |
| |
256 |
| |
257 |
| |
258 |
| |
259 |
| |
260 |
| |
261 |
| |
262 |
| |
263 |
| |
264 |
2
| public ReflectedTableModel( final Class clazz ) throws IntrospectionException {
|
265 |
2
| this();
|
266 |
2
| final BeanInfo beanInfo = Introspector.getBeanInfo( clazz );
|
267 |
2
| final PropertyDescriptor propertyDescriptors[]
|
268 |
| = beanInfo.getPropertyDescriptors(); |
269 |
| |
270 |
2
| String propertyName;
|
271 |
| |
272 |
2
| int i;
|
273 |
2
| for( i=0; i<propertyDescriptors.length; i++ ) {
|
274 |
6
| propertyName = propertyDescriptors[i].getName();
|
275 |
| |
276 |
6
| if( traceChannel_ != null ) {
|
277 |
0
| Trace.println( traceChannel_,
|
278 |
| "ReflectedTableModel(Class) adding column: [" |
279 |
| + propertyName +"]" ); |
280 |
| } |
281 |
6
| columns_.add( new ColumnInfo( propertyName ) );
|
282 |
| } |
283 |
| } |
284 |
| |
285 |
| |
286 |
| |
287 |
| |
288 |
| |
289 |
| |
290 |
| |
291 |
1
| public List getRows() {
|
292 |
1
| return rows_;
|
293 |
| } |
294 |
| |
295 |
| |
296 |
| |
297 |
| |
298 |
| |
299 |
| |
300 |
| |
301 |
1
| public List getColumns() {
|
302 |
1
| return columns_;
|
303 |
| } |
304 |
| |
305 |
| |
306 |
| |
307 |
| |
308 |
| |
309 |
1
| public int getColumnCount() {
|
310 |
1
| return columns_.size();
|
311 |
| } |
312 |
| |
313 |
| |
314 |
| |
315 |
| |
316 |
| |
317 |
1
| public int getRowCount() {
|
318 |
1
| return rows_.size();
|
319 |
| } |
320 |
| |
321 |
| |
322 |
| |
323 |
| |
324 |
| |
325 |
| |
326 |
| |
327 |
8
| public Object getValueAt( final int rowIndex, final int columnIndex ) {
|
328 |
8
| final Object rowObject = rows_.get(rowIndex);
|
329 |
8
| final ColumnInfo columnInfo = (ColumnInfo)columns_.get(columnIndex);
|
330 |
8
| final String propertyName = columnInfo.getPropertyName();
|
331 |
| |
332 |
8
| try {
|
333 |
8
| final BeanInfo beanInfo = Introspector.getBeanInfo( rowObject.getClass() );
|
334 |
8
| final PropertyDescriptor propertyDescriptors[]
|
335 |
| = beanInfo.getPropertyDescriptors(); |
336 |
| |
337 |
8
| int i;
|
338 |
16
| for( i=0; i<propertyDescriptors.length; i++ ) {
|
339 |
16
| if( propertyDescriptors[i].getName().equals( propertyName ) ) {
|
340 |
8
| final Method readMethod = propertyDescriptors[i].getReadMethod();
|
341 |
8
| return readMethod.invoke( rowObject, EMPTY_OBJECT_ARRAY );
|
342 |
| } |
343 |
| } |
344 |
| } |
345 |
| catch( final Exception e ) { |
346 |
0
| if( traceChannel_ != null ) {
|
347 |
0
| Trace.printStackTrace( traceChannel_, e );
|
348 |
| } |
349 |
| } |
350 |
| |
351 |
0
| return null;
|
352 |
| } |
353 |
| |
354 |
| |
355 |
| |
356 |
| |
357 |
| |
358 |
| |
359 |
3
| public String getColumnName( final int index ) {
|
360 |
3
| return((ColumnInfo)columns_.get(index)).getColumnName();
|
361 |
| } |
362 |
| |
363 |
| |
364 |
| |
365 |
| |
366 |
| |
367 |
| |
368 |
| |
369 |
0
| public void setTraceChannel( final TraceChannel channel ) {
|
370 |
0
| traceChannel_ = channel;
|
371 |
| } |
372 |
| |
373 |
| |
374 |
| |
375 |
| |
376 |
| |
377 |
| |
378 |
0
| public TraceChannel getTraceChannel() {
|
379 |
0
| return traceChannel_;
|
380 |
| } |
381 |
| |
382 |
| |
383 |
| |
384 |
| |
385 |
| |
386 |
4
| private synchronized void addRowElement( final Object object ) {
|
387 |
| |
388 |
4
| RowElementControlData data =
|
389 |
| (RowElementControlData)rowElementControlDatas_.get(object); |
390 |
| |
391 |
4
| if( data == null ) {
|
392 |
4
| data = new RowElementControlData();
|
393 |
| |
394 |
4
| final Class clazz = object.getClass();
|
395 |
4
| final Class parms[] = { PropertyChangeListener.class};
|
396 |
4
| Method method;
|
397 |
4
| try {
|
398 |
| |
399 |
4
| method = clazz.getMethod("removePropertyChangeListener", parms);
|
400 |
4
| method = clazz.getMethod("addPropertyChangeListener", parms);
|
401 |
4
| method.invoke(object, new Object[]{propertyChangeListener_} );
|
402 |
| } |
403 |
| catch( NoSuchMethodException e ) { |
404 |
0
| data.supportsPropertyChangeEvents_ = false;
|
405 |
| } |
406 |
| catch( IllegalAccessException e ) { |
407 |
0
| if( traceChannel_ != null ) {
|
408 |
0
| Trace.printStackTrace(traceChannel_, e);
|
409 |
| } |
410 |
| } |
411 |
| catch( InvocationTargetException e ) { |
412 |
0
| if( traceChannel_ != null ) {
|
413 |
0
| Trace.printStackTrace(traceChannel_, e.getTargetException());
|
414 |
| } |
415 |
| } |
416 |
| |
417 |
4
| rowElementControlDatas_.put(object, data);
|
418 |
| } |
419 |
| |
420 |
4
| data.instanceCounter_++;
|
421 |
| } |
422 |
| |
423 |
| |
424 |
| |
425 |
| |
426 |
4
| private void addRowElements( final List list ) {
|
427 |
4
| final Iterator iterator = list.iterator();
|
428 |
4
| while( iterator.hasNext() ) {
|
429 |
4
| addRowElement( iterator.next() );
|
430 |
| } |
431 |
| } |
432 |
| |
433 |
| |
434 |
| |
435 |
| |
436 |
0
| private void removeRowElements( final List list ) {
|
437 |
0
| final Iterator iterator = list.iterator();
|
438 |
0
| while( iterator.hasNext() ) {
|
439 |
0
| removeRowElement( iterator.next() );
|
440 |
| } |
441 |
| } |
442 |
| |
443 |
| |
444 |
| |
445 |
| |
446 |
| |
447 |
0
| private synchronized void removeRowElement( final Object object ) {
|
448 |
| |
449 |
0
| RowElementControlData data =
|
450 |
| (RowElementControlData)rowElementControlDatas_.get(object); |
451 |
| |
452 |
0
| if( data == null ) {
|
453 |
0
| throw new DetailedIllegalArgumentException("object", object, "Not in row list");
|
454 |
| } |
455 |
| |
456 |
0
| final Class clazz = object.getClass();
|
457 |
0
| final Class parms[] = { PropertyChangeListener.class};
|
458 |
0
| Method method;
|
459 |
0
| try {
|
460 |
0
| method = clazz.getMethod("removePropertyChangeListener", parms);
|
461 |
0
| method.invoke(object, new Object[]{propertyChangeListener_} );
|
462 |
| } |
463 |
| catch( final NoSuchMethodException e ) { |
464 |
0
| throw new IllegalStateException(
|
465 |
| "object doesn't have a removePropertyChange(): "+object); |
466 |
| } |
467 |
| catch( IllegalAccessException e ) { |
468 |
0
| if( traceChannel_ != null ) {
|
469 |
0
| Trace.printStackTrace(traceChannel_, e);
|
470 |
| } |
471 |
| } |
472 |
| catch( InvocationTargetException e ) { |
473 |
0
| if( traceChannel_ != null ) {
|
474 |
0
| Trace.printStackTrace(traceChannel_, e.getTargetException());
|
475 |
| } |
476 |
| } |
477 |
| |
478 |
0
| data.instanceCounter_--;
|
479 |
0
| if( data.instanceCounter_ == 0 ) {
|
480 |
0
| rowElementControlDatas_.remove(object);
|
481 |
| } |
482 |
| } |
483 |
| |
484 |
| |
485 |
| |
486 |
| |
487 |
| |
488 |
| |
489 |
| |
490 |
0
| protected final void assertNotNull( final String fieldName, final Object object ) {
|
491 |
0
| if( object == null ) {
|
492 |
0
| throw new DetailedNullPointerException(fieldName);
|
493 |
| } |
494 |
| } |
495 |
| } |