35 import javafx.scene.control.TableView;
36 import javafx.util.Callback;
37
38 import sun.util.logging.PlatformLogger;
39 import sun.util.logging.PlatformLogger.Level;
40 import com.sun.javafx.property.PropertyReference;
41 import com.sun.javafx.scene.control.Logging;
42
43
44 /**
45 * A convenience implementation of the Callback interface, designed specifically
46 * for use within the {@link TableColumn}
47 * {@link TableColumn#cellValueFactoryProperty() cell value factory}. An example
48 * of how to use this class is:
49 *
50 * <pre><code>
51 * TableColumn<Person,String> firstNameCol = new TableColumn<Person,String>("First Name");
52 * firstNameCol.setCellValueFactory(new PropertyValueFactory<Person,String>("firstName"));
53 * </code></pre>
54 *
55 * In this example, the "firstName" string is used as a reference to an assumed
56 * <code>firstNameProperty()</code> method in the <code>Person</code> class type
57 * (which is the class type of the TableView
58 * {@link TableView#itemsProperty() items} list). Additionally, this method must
59 * return a {@link Property} instance. If a method meeting these requirements
60 * is found, then the {@link TableCell} is populated with this
61 * {@literal ObservableValue<T>}.
62 * In addition, the TableView will automatically add an observer to the
63 * returned value, such that any changes fired will be observed by the TableView,
64 * resulting in the cell immediately updating.
65 *
66 * <p>If no method matching this pattern exists, there is fall-through support
67 * for attempting to call get<property>() or is<property>() (that is,
68 * <code>getFirstName()</code> or <code>isFirstName()</code> in the example
69 * above). If a method matching this pattern exists, the value returned from this method
70 * is wrapped in a {@link ReadOnlyObjectWrapper} and returned to the TableCell.
71 * However, in this situation, this means that the TableCell will not be able
72 * to observe the ObservableValue for changes (as is the case in the first
73 * approach above).
74 *
75 * <p>For reference (and as noted in the TableColumn
76 * {@link TableColumn#cellValueFactory cell value factory} documentation), the
77 * long form of the code above would be the following:
78 *
79 * <pre><code>
80 * TableColumn<Person,String> firstNameCol = new TableColumn<Person,String>("First Name");
81 * firstNameCol.setCellValueFactory(new Callback<CellDataFeatures<Person, String>, ObservableValue<String>>() {
82 * public ObservableValue<String> call(CellDataFeatures<Person, String> p) {
83 * // p.getValue() returns the Person instance for a particular TableView row
84 * return p.getValue().firstNameProperty();
85 * }
86 * });
87 * }
88 * </code></pre>
89 *
90 * @see TableColumn
91 * @see TableView
92 * @see TableCell
93 * @see TreeItemPropertyValueFactory
94 * @see MapValueFactory
95 * @param <S> The type of the class contained within the TableView.items list.
96 * @param <T> The type of the class contained within the TableColumn cells.
97 * @since JavaFX 2.0
98 */
99 public class PropertyValueFactory<S,T> implements Callback<CellDataFeatures<S,T>, ObservableValue<T>> {
100
101 private final String property;
102
103 private Class<?> columnClass;
104 private String previousProperty;
105 private PropertyReference<T> propertyRef;
106
107 /**
108 * Creates a default PropertyValueFactory to extract the value from a given
109 * TableView row item reflectively, using the given property name.
126 */
127 public final String getProperty() { return property; }
128
129 private ObservableValue<T> getCellDataReflectively(S rowData) {
130 if (getProperty() == null || getProperty().isEmpty() || rowData == null) return null;
131
132 try {
133 // we attempt to cache the property reference here, as otherwise
134 // performance suffers when working in large data models. For
135 // a bit of reference, refer to RT-13937.
136 if (columnClass == null || previousProperty == null ||
137 ! columnClass.equals(rowData.getClass()) ||
138 ! previousProperty.equals(getProperty())) {
139
140 // create a new PropertyReference
141 this.columnClass = rowData.getClass();
142 this.previousProperty = getProperty();
143 this.propertyRef = new PropertyReference<T>(rowData.getClass(), getProperty());
144 }
145
146 if (propertyRef.hasProperty()) {
147 return propertyRef.getProperty(rowData);
148 } else {
149 T value = propertyRef.get(rowData);
150 return new ReadOnlyObjectWrapper<T>(value);
151 }
152 } catch (IllegalStateException e) {
153 // log the warning and move on
154 final PlatformLogger logger = Logging.getControlsLogger();
155 if (logger.isLoggable(Level.WARNING)) {
156 logger.finest("Can not retrieve property '" + getProperty() +
157 "' in PropertyValueFactory: " + this +
158 " with provided class type: " + rowData.getClass(), e);
159 }
160 }
161
162 return null;
163 }
164 }
|
35 import javafx.scene.control.TableView;
36 import javafx.util.Callback;
37
38 import sun.util.logging.PlatformLogger;
39 import sun.util.logging.PlatformLogger.Level;
40 import com.sun.javafx.property.PropertyReference;
41 import com.sun.javafx.scene.control.Logging;
42
43
44 /**
45 * A convenience implementation of the Callback interface, designed specifically
46 * for use within the {@link TableColumn}
47 * {@link TableColumn#cellValueFactoryProperty() cell value factory}. An example
48 * of how to use this class is:
49 *
50 * <pre><code>
51 * TableColumn<Person,String> firstNameCol = new TableColumn<Person,String>("First Name");
52 * firstNameCol.setCellValueFactory(new PropertyValueFactory<Person,String>("firstName"));
53 * </code></pre>
54 *
55 * <p>
56 * In this example, {@code Person} is the class type of the {@code TableView}
57 * {@link TableView#itemsProperty() items} list.
58 * The class {@code Person} must be declared public.
59 * {@code PropertyValueFactory} uses the constructor argument,
60 * {@code "firstName"}, to assume that {@code Person} has a public method
61 * {@code firstNameProperty} with no formal parameters and a return type of
62 * {@code ObservableValue<String>}.
63 * </p>
64 * <p>
65 * If such a method exists, then it is invoked, and additionally assumed
66 * to return an instance of {@code Property<String>}. The return value is used
67 * to populate the {@link TableCell}. In addition, the {@code TableView} adds
68 * an observer to the return value, such that any changes fired will be observed
69 * by the {@code TableView}, resulting in the cell immediately updating.
70 * </p>
71 * <p>
72 * If no such method exists, then {@code PropertyValueFactory}
73 * assumes that {@code Person} has a public method {@code getFirstName} or
74 * {@code isFirstName} with no formal parameters and a return type of
75 * {@code String}. If such a method exists, then it is invoked, and its return
76 * value is wrapped in a {@link ReadOnlyObjectWrapper}
77 * and returned to the {@code TableCell}. In this situation,
78 * the {@code TableCell} will not be able to observe changes to the property,
79 * unlike in the first approach above.
80 * </p>
81 *
82 * <p>For reference (and as noted in the TableColumn
83 * {@link TableColumn#cellValueFactory cell value factory} documentation), the
84 * long form of the code above would be the following:
85 * </p>
86 *
87 * <pre><code>
88 * TableColumn<Person,String> firstNameCol = new TableColumn<Person,String>("First Name");
89 * firstNameCol.setCellValueFactory(new Callback<CellDataFeatures<Person, String>, ObservableValue<String>>() {
90 * public ObservableValue<String> call(CellDataFeatures<Person, String> p) {
91 * // p.getValue() returns the Person instance for a particular TableView row
92 * return p.getValue().firstNameProperty();
93 * }
94 * });
95 * }
96 * </code></pre>
97 *
98 * <p><b>Deploying an Application as a Module</b></p>
99 * <p>
100 * If the referenced class is in a named module, then it must be reflectively
101 * accessible to the {@code javafx.base} module.
102 * A class is reflectively accessible if the module
103 * {@link Module#isOpen(String,Module) opens} the containing package to at
104 * least the {@code javafx.base} module.
105 * Otherwise the {@link #call call(TableColumn.CellDataFeatures)} method
106 * will log a warning and return {@code null}.
107 * </p>
108 * <p>
109 * For example, if the {@code Person} class is in the {@code com.foo} package
110 * in the {@code foo.app} module, the {@code module-info.java} might
111 * look like this:
112 * </p>
113 *
114 <pre>{@code module foo.app {
115 opens com.foo to javafx.base;
116 }}</pre>
117 *
118 * <p>
119 * Alternatively, a class is reflectively accessible if the module
120 * {@link Module#isExported(String) exports} the containing package
121 * unconditionally.
122 * </p>
123 *
124 * @see TableColumn
125 * @see TableView
126 * @see TableCell
127 * @see TreeItemPropertyValueFactory
128 * @see MapValueFactory
129 * @param <S> The type of the class contained within the TableView.items list.
130 * @param <T> The type of the class contained within the TableColumn cells.
131 * @since JavaFX 2.0
132 */
133 public class PropertyValueFactory<S,T> implements Callback<CellDataFeatures<S,T>, ObservableValue<T>> {
134
135 private final String property;
136
137 private Class<?> columnClass;
138 private String previousProperty;
139 private PropertyReference<T> propertyRef;
140
141 /**
142 * Creates a default PropertyValueFactory to extract the value from a given
143 * TableView row item reflectively, using the given property name.
160 */
161 public final String getProperty() { return property; }
162
163 private ObservableValue<T> getCellDataReflectively(S rowData) {
164 if (getProperty() == null || getProperty().isEmpty() || rowData == null) return null;
165
166 try {
167 // we attempt to cache the property reference here, as otherwise
168 // performance suffers when working in large data models. For
169 // a bit of reference, refer to RT-13937.
170 if (columnClass == null || previousProperty == null ||
171 ! columnClass.equals(rowData.getClass()) ||
172 ! previousProperty.equals(getProperty())) {
173
174 // create a new PropertyReference
175 this.columnClass = rowData.getClass();
176 this.previousProperty = getProperty();
177 this.propertyRef = new PropertyReference<T>(rowData.getClass(), getProperty());
178 }
179
180 if (propertyRef != null) {
181 if (propertyRef.hasProperty()) {
182 return propertyRef.getProperty(rowData);
183 } else {
184 T value = propertyRef.get(rowData);
185 return new ReadOnlyObjectWrapper<T>(value);
186 }
187 }
188 } catch (RuntimeException e) {
189 // log the warning and move on
190 final PlatformLogger logger = Logging.getControlsLogger();
191 if (logger.isLoggable(Level.WARNING)) {
192 logger.warning("Can not retrieve property '" + getProperty() +
193 "' in PropertyValueFactory: " + this +
194 " with provided class type: " + rowData.getClass(), e);
195 }
196 propertyRef = null;
197 }
198
199 return null;
200 }
201 }
|