001 package org.springframework.richclient.widget.table; 002 003 import org.springframework.binding.form.FormModel; 004 import org.springframework.context.support.DefaultMessageSourceResolvable; 005 import org.springframework.richclient.form.binding.BindingFactory; 006 import org.springframework.richclient.form.binding.swing.SwingBindingFactory; 007 import org.springframework.richclient.util.RcpSupport; 008 import org.springframework.richclient.widget.editor.AbstractDataEditorWidget; 009 010 import javax.swing.table.TableCellEditor; 011 import javax.swing.table.TableCellRenderer; 012 import java.util.Comparator; 013 014 /** 015 * A column descriptor to access a specific property. Minimal configuration includes the propertyName and an 016 * {@link Accessor}. Other features can be set using the normal bean setters and getters or through a number 017 * of chaining methods. The latter is particularly useful to create columns by instantiating the column with 018 * the minimum requirements and then adding all settings in one go. 019 * 020 * @author Jan Hoskens 021 * @since 0.5.0 022 */ 023 public class PropertyColumn 024 { 025 026 /** No specific width set. */ 027 public static final int UNSPECIFIED_WIDTH = -1; 028 029 /** Type of the property. */ 030 private Class<?> type; 031 032 /** Accessor for the property. */ 033 private Accessor accessor; 034 035 /** Name of the property. */ 036 private String propertyName; 037 038 /** Header keys used to fetch the column header label from the message resource. */ 039 private String[] headerKeys; 040 041 /** Maximum width of the column. */ 042 private int maxWidth = UNSPECIFIED_WIDTH; 043 044 /** Minimum width of the column. */ 045 private int minWidth = UNSPECIFIED_WIDTH; 046 047 /** Is it possible to resize column? */ 048 private boolean resizable = true; 049 050 /** A specific renderer for the column. */ 051 private TableCellRenderer renderer; 052 053 /** A specific editor for the column. */ 054 private TableCellEditor editor; 055 056 /** Comparator to use when sorting according to this column. */ 057 private Comparator<?> comparator; 058 059 /** Is this column a select column? */ 060 private boolean isSelectColumn = false; 061 062 /** Can this column be used when filtering locally? */ 063 private boolean isFilterColumn = false; 064 065 /** Is the column initially visible? */ 066 private boolean visible = true; 067 068 /** Header title fetched from header keys or manually set. */ 069 private String header = null; 070 071 public PropertyColumn(final String propertyName) 072 { 073 this.propertyName = propertyName; 074 } 075 076 /** 077 * Minimal construction requires a propertyName and an {@link Accessor}. 078 * 079 * @param propertyName 080 * name of the property. 081 * @param accessor 082 * read-access for the property. 083 */ 084 public PropertyColumn(final String propertyName, final Accessor accessor, final Class<?> propertyType) 085 { 086 this.propertyName = propertyName; 087 this.accessor = accessor; 088 this.type = propertyType; 089 } 090 091 /** 092 * Constructor taking all possible arguments. 093 * 094 * @deprecated Please use the other system (minimal constructor + adding necessary elements) 095 */ 096 public PropertyColumn(final Class<?> type, final Accessor accessor, final String propertyName, 097 final String[] headerKeys, final int minWidth, final int maxWidth, final boolean resizable, 098 TableCellRenderer renderer, TableCellEditor editor, final Comparator<?> comparator, 099 final boolean isSelectColumn, final boolean visible) 100 { 101 this.type = type; 102 this.accessor = accessor; 103 this.propertyName = propertyName; 104 this.headerKeys = headerKeys; 105 this.minWidth = minWidth; 106 this.maxWidth = maxWidth; 107 this.resizable = resizable; 108 this.editor = editor; 109 this.renderer = renderer; 110 this.comparator = comparator; 111 this.isSelectColumn = isSelectColumn; 112 this.visible = visible; 113 } 114 115 /** 116 * Set the accessor to use when retrieving the property value. 117 * 118 * @param accessor the {@link Accessor} to read the property value. 119 */ 120 public void setAccessor(Accessor accessor) 121 { 122 this.accessor = accessor; 123 } 124 125 /** 126 * The type of the property. 127 * 128 * @param type the type of the property. 129 */ 130 public void setType(Class<?> type) 131 { 132 this.type = type; 133 } 134 135 /** 136 * Returns the header to be used as column title. If no explicit header was set, the headerKeys are used 137 * to fetch a message from the available resources. 138 */ 139 public String getHeader() 140 { 141 if (this.header == null) 142 { 143 if (this.headerKeys == null) 144 { 145 this.headerKeys = new String[2]; 146 this.headerKeys[0] = getPropertyName() + ".header"; 147 this.headerKeys[1] = getPropertyName(); 148 } 149 this.header = RcpSupport.getMessage(new DefaultMessageSourceResolvable(this.headerKeys, null, 150 this.headerKeys[this.headerKeys.length - 1])); 151 } 152 // JTableHeader has a reusable defaultHeaderRenderer on which the default height must be correct. 153 // when painting, the columns headers are processed in order and height is being calculated, 154 // if label is null or empty string header height is 4 and thus leaves us with a very small 155 // table-header, fix this by returning a space (-> font-size is incorporated) 156 return "".equals(this.header) ? " " : this.header; 157 } 158 159 /** 160 * Returns the header keys that are used to fetch the column title if the header property is not set. 161 */ 162 public String[] getHeaderKeys() 163 { 164 return headerKeys == null ? null : headerKeys.clone(); 165 } 166 167 /** 168 * Chaining method to set header keys. 169 * 170 * @param headerKeys 171 * keys used to fetch the column title from the resources if no header is explicitly set. 172 * @return <code>this</code> 173 */ 174 public PropertyColumn withHeaderKeys(String[] headerKeys) 175 { 176 setHeaderKeys(headerKeys); 177 return this; 178 } 179 180 /** 181 * Set the keys used to fetch the column title from the resources if no header is explicitly set. 182 */ 183 public void setHeaderKeys(String[] headerKeys) 184 { 185 this.headerKeys = headerKeys; 186 } 187 188 /** 189 * Returns the maximum width for this column. 190 */ 191 public int getMaxWidth() 192 { 193 return maxWidth; 194 } 195 196 /** 197 * Chaining method to set the maximum width. 198 * 199 * @param maxWidth 200 * maximum width for this column 201 * @return <code>this</code> 202 */ 203 public PropertyColumn withMaxWidth(int maxWidth) 204 { 205 setMaxWidth(maxWidth); 206 return this; 207 } 208 209 /** 210 * Set the maximum width of this column. 211 */ 212 public void setMaxWidth(int maxWidth) 213 { 214 this.maxWidth = maxWidth; 215 } 216 217 /** 218 * Returns the minimum width for this column. 219 */ 220 public int getMinWidth() 221 { 222 return minWidth; 223 } 224 225 /** 226 * Chaining method to set the minimum width. 227 * 228 * @param minWidth 229 * minimum width for this column 230 * @return <code>this</code> 231 */ 232 public PropertyColumn withMinWidth(int minWidth) 233 { 234 setMinWidth(minWidth); 235 return this; 236 } 237 238 /** 239 * Set the minimum width for this column. 240 */ 241 public void setMinWidth(int minWidth) 242 { 243 this.minWidth = minWidth; 244 } 245 246 /** 247 * Chaining method to set the minimum and maximum width to the same value. This will create a fixed width 248 * column. 249 * 250 * @param width 251 * fixed width for this column 252 * @return <code>this</code> 253 */ 254 public PropertyColumn withFixedWidth(int width) 255 { 256 setMinWidth(width); 257 setMaxWidth(width); 258 return this; 259 } 260 261 public boolean isResizable() 262 { 263 return resizable; 264 } 265 266 public PropertyColumn withResizable(boolean resizable) 267 { 268 setResizable(resizable); 269 return this; 270 } 271 272 public void setResizable(boolean resizable) 273 { 274 this.resizable = resizable; 275 } 276 277 public PropertyColumn withFilterColumn(boolean isFilterColumn) 278 { 279 setFilterColumn(isFilterColumn); 280 return this; 281 } 282 283 public void setFilterColumn(boolean isFilterColumn) 284 { 285 this.isFilterColumn = isFilterColumn; 286 } 287 288 public boolean isFilterColumn() 289 { 290 return isFilterColumn; 291 } 292 293 public TableCellRenderer getRenderer() 294 { 295 return renderer; 296 } 297 298 public PropertyColumn withRenderer(TableCellRenderer renderer) 299 { 300 setRenderer(renderer); 301 return this; 302 } 303 304 public void setRenderer(TableCellRenderer renderer) 305 { 306 this.renderer = renderer; 307 } 308 309 public TableCellEditor getEditor() 310 { 311 return editor; 312 } 313 314 public PropertyColumn withEditor(TableCellEditor editor) 315 { 316 setEditor(editor); 317 return this; 318 } 319 320 public PropertyColumn withEditor(BindingFactory bindingFactory) 321 { 322 return withEditor(new ValueModelTableCellEditor(bindingFactory.getFormModel(), getPropertyName(), 323 bindingFactory.createBinding(getType(), getPropertyName()).getControl())); 324 } 325 326 public PropertyColumn withEditor(FormModel formModel) 327 { 328 return withEditor(new SwingBindingFactory(formModel)); 329 } 330 331 public PropertyColumn withEditor(AbstractDataEditorWidget dataEditor) 332 { 333 FormModel formModel = dataEditor.getDetailForm().getFormModel(); 334 BindingFactory bindingFactory = dataEditor.getDetailForm().getBindingFactory(); 335 return withEditor(new ValueModelTableCellEditor(formModel, getPropertyName(), 336 bindingFactory.createBinding(getType(), getPropertyName()).getControl(), dataEditor.getUpdateCommand())); 337 } 338 339 public void setEditor(TableCellEditor editor) 340 { 341 this.editor = editor; 342 } 343 344 public Comparator<?> getComparator() 345 { 346 return comparator; 347 } 348 349 public PropertyColumn withComparator(Comparator<?> comparator) 350 { 351 setComparator(comparator); 352 return this; 353 } 354 355 public void setComparator(Comparator<?> comparator) 356 { 357 this.comparator = comparator; 358 } 359 360 public PropertyColumn withSelectColumn(boolean isSelectColumn) 361 { 362 setSelectColumn(isSelectColumn); 363 return this; 364 } 365 366 public boolean isSelectColumn() 367 { 368 return isSelectColumn; 369 } 370 371 public void setSelectColumn(boolean isSelectColumn) 372 { 373 this.isSelectColumn = isSelectColumn; 374 } 375 376 public boolean isVisible() 377 { 378 return visible; 379 } 380 381 public PropertyColumn withVisible(boolean visible) 382 { 383 setVisible(visible); 384 return this; 385 } 386 387 public void setVisible(boolean visible) 388 { 389 this.visible = visible; 390 } 391 392 public Class<?> getType() 393 { 394 return type; 395 } 396 397 public Accessor getAccessor() 398 { 399 return accessor; 400 } 401 402 public String getPropertyName() 403 { 404 return propertyName; 405 } 406 407 public void setHeader(String header) 408 { 409 this.header = header; 410 } 411 412 413 }