13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 package javafx.scene.control.cell;
27
28 import javafx.beans.NamedArg;
29 import javafx.beans.property.Property;
30 import javafx.beans.property.ReadOnlyObjectWrapper;
31 import javafx.beans.value.ObservableValue;
32 import javafx.scene.control.TreeItem;
33 import javafx.scene.control.TreeTableColumn;
34 import javafx.scene.control.TreeTableColumn.CellDataFeatures;
35 import javafx.util.Callback;
36 import com.sun.javafx.property.PropertyReference;
37 import com.sun.javafx.scene.control.Logging;
38 import sun.util.logging.PlatformLogger;
39 import sun.util.logging.PlatformLogger.Level;
40
41
42 /**
43 * A convenience implementation of the Callback interface, designed specifically
44 * for use within the {@link TreeTableColumn}
45 * {@link TreeTableColumn#cellValueFactoryProperty() cell value factory}. An example
46 * of how to use this class is:
47 *
48 * <pre><code>
49 * TreeTableColumn<Person,String> firstNameCol = new TreeTableColumn<Person,String>("First Name");
50 * firstNameCol.setCellValueFactory(new TreeItemPropertyValueFactory<Person,String>("firstName"));
51 * </code></pre>
52 *
53 * In this example, the "firstName" string is used as a reference to an assumed
54 * <code>firstNameProperty()</code> method in the <code>Person</code> class type
55 * (which is the class type of the TreeTableView). Additionally, this method must
56 * return a {@link Property} instance. If a method meeting these requirements
57 * is found, then the {@link javafx.scene.control.TreeTableCell} is populated
58 * with this {@literal ObservableValue<T>}.
59 * In addition, the TreeTableView will automatically add an observer to the
60 * returned value, such that any changes fired will be observed by the TreeTableView,
61 * resulting in the cell immediately updating.
62 *
63 * <p>If no method matching this pattern exists, there is fall-through support
64 * for attempting to call get<property>() or is<property>() (that is,
65 * <code>getFirstName()</code> or <code>isFirstName()</code> in the example
66 * above). If a method matching this pattern exists, the value returned from this method
67 * is wrapped in a {@link ReadOnlyObjectWrapper} and returned to the TreeTableCell.
68 * However, in this situation, this means that the TreeTableCell will not be able
69 * to observe the ObservableValue for changes (as is the case in the first
70 * approach above).
71 *
72 * <p>For reference (and as noted in the TreeTableColumn
73 * {@link TreeTableColumn#cellValueFactory cell value factory} documentation), the
74 * long form of the code above would be the following:
75 *
76 * <pre><code>
77 * TreeTableColumn<Person,String> firstNameCol = new TreeTableColumn<Person,String>("First Name");
78 * {@literal
79 * firstNameCol.setCellValueFactory(new Callback<CellDataFeatures<Person, String>, ObservableValue<String>>() {
80 * public ObservableValue<String> call(CellDataFeatures<Person, String> p) {
81 * // p.getValue() returns the TreeItem<Person> instance for a particular
82 * // TreeTableView row, and the second getValue() call returns the
83 * // Person instance contained within the TreeItem.
84 * return p.getValue().getValue().firstNameProperty();
85 * }
86 * });
87 * }
88 * }
89 * </code></pre>
90 *
91 * @see TreeTableColumn
92 * @see javafx.scene.control.TreeTableView
93 * @see javafx.scene.control.TreeTableCell
94 * @see PropertyValueFactory
95 * @see MapValueFactory
96 * @since JavaFX 8.0
97 */
98 public class TreeItemPropertyValueFactory<S,T> implements Callback<TreeTableColumn.CellDataFeatures<S,T>, ObservableValue<T>> {
99
100 private final String property;
101
102 private Class<?> columnClass;
103 private String previousProperty;
104 private PropertyReference<T> propertyRef;
105
106 /**
107 * Creates a default PropertyValueFactory to extract the value from a given
108 * TableView row item reflectively, using the given property name.
109 *
110 * @param property The name of the property with which to attempt to
111 * reflectively extract a corresponding value for in a given object.
112 */
113 public TreeItemPropertyValueFactory(@NamedArg("property") String property) {
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 return propertyRef.getProperty(rowData);
147 } catch (IllegalStateException e) {
148 try {
149 // attempt to just get the value
150 T value = propertyRef.get(rowData);
151 return new ReadOnlyObjectWrapper<T>(value);
152 } catch (IllegalStateException e2) {
153 // fall through to logged exception below
154 }
155
156 // log the warning and move on
157 final PlatformLogger logger = Logging.getControlsLogger();
158 if (logger.isLoggable(Level.WARNING)) {
159 logger.finest("Can not retrieve property '" + getProperty() +
160 "' in TreeItemPropertyValueFactory: " + this +
161 " with provided class type: " + rowData.getClass(), e);
162 }
163 }
164
165 return null;
166 }
167 }
|
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 package javafx.scene.control.cell;
27
28 import javafx.beans.NamedArg;
29 import javafx.beans.property.Property;
30 import javafx.beans.property.ReadOnlyObjectWrapper;
31 import javafx.beans.value.ObservableValue;
32 import javafx.scene.control.TreeItem;
33 import javafx.scene.control.TreeTableCell;
34 import javafx.scene.control.TreeTableColumn;
35 import javafx.scene.control.TreeTableColumn.CellDataFeatures;
36 import javafx.scene.control.TreeTableView;
37 import javafx.util.Callback;
38 import com.sun.javafx.property.PropertyReference;
39 import com.sun.javafx.scene.control.Logging;
40 import sun.util.logging.PlatformLogger;
41 import sun.util.logging.PlatformLogger.Level;
42
43
44 /**
45 * A convenience implementation of the Callback interface, designed specifically
46 * for use within the {@link TreeTableColumn}
47 * {@link TreeTableColumn#cellValueFactoryProperty() cell value factory}. An example
48 * of how to use this class is:
49 *
50 * <pre><code>
51 * TreeTableColumn<Person,String> firstNameCol = new TreeTableColumn<Person,String>("First Name");
52 * firstNameCol.setCellValueFactory(new TreeItemPropertyValueFactory<Person,String>("firstName"));
53 * </code></pre>
54 *
55 *
56 * <p>
57 * In this example, {@code Person} is the class type of the {@link TreeItem}
58 * instances used in the {@link TreeTableView}.
59 * {@code TreeItemPropertyValueFactory} uses the constructor argument,
60 * {@code "firstName"}, to assume that {@code Person} has a 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 TreeTableCell}. In addition, the {@code TreeTableView}
68 * adds an observer to the return value, such that any changes fired will be
69 * observed by the {@code TreeTableView}, resulting in the cell immediately
70 * updating.
71 * </p>
72 * <p>
73 * If no such method exists, then {@code TreeItemPropertyValueFactory}
74 * assumes that {@code Person} has a method {@code getFirstName} or
75 * {@code isFirstName} with no formal parameters and a return type of
76 * {@code String}. If such a method exists, then it is invoked, and its return
77 * value is wrapped in a {@link ReadOnlyObjectWrapper}
78 * and returned to the {@code TreeTableCell}. In this situation,
79 * the {@code TreeTableCell} will not be able to observe changes to the property,
80 * unlike in the first approach above.
81 * </p>
82 * <p>
83 * The class {@code Person} must be declared public. If that class is in a named
84 * module, then the module must {@link Module#isOpen(String,Module) open}
85 * the containing package to at least the {@code javafx.base} module
86 * (or {@link Module#isExported(String) export} the containing package
87 * unconditionally).
88 * Otherwise the {@link #call call(TreeTableColumn.CellDataFeatures)} method
89 * will log a warning and return {@code null}.
90 * </p>
91 *
92 * <p>For reference (and as noted in the TreeTableColumn
93 * {@link TreeTableColumn#cellValueFactory cell value factory} documentation), the
94 * long form of the code above would be the following:
95 * </p>
96 *
97 * <pre><code>
98 * TreeTableColumn<Person,String> firstNameCol = new TreeTableColumn<Person,String>("First Name");
99 * {@literal
100 * firstNameCol.setCellValueFactory(new Callback<CellDataFeatures<Person, String>, ObservableValue<String>>() {
101 * public ObservableValue<String> call(CellDataFeatures<Person, String> p) {
102 * // p.getValue() returns the TreeItem<Person> instance for a particular
103 * // TreeTableView row, and the second getValue() call returns the
104 * // Person instance contained within the TreeItem.
105 * return p.getValue().getValue().firstNameProperty();
106 * }
107 * });
108 * }
109 * }
110 * </code></pre>
111 *
112 * @see TreeTableColumn
113 * @see TreeTableView
114 * @see TreeTableCell
115 * @see PropertyValueFactory
116 * @see MapValueFactory
117 * @since JavaFX 8.0
118 */
119 public class TreeItemPropertyValueFactory<S,T> implements Callback<TreeTableColumn.CellDataFeatures<S,T>, ObservableValue<T>> {
120
121 private final String property;
122
123 private Class<?> columnClass;
124 private String previousProperty;
125 private PropertyReference<T> propertyRef;
126
127 /**
128 * Creates a default PropertyValueFactory to extract the value from a given
129 * TableView row item reflectively, using the given property name.
130 *
131 * @param property The name of the property with which to attempt to
132 * reflectively extract a corresponding value for in a given object.
133 */
134 public TreeItemPropertyValueFactory(@NamedArg("property") String property) {
147 */
148 public final String getProperty() { return property; }
149
150 private ObservableValue<T> getCellDataReflectively(S rowData) {
151 if (getProperty() == null || getProperty().isEmpty() || rowData == null) return null;
152
153 try {
154 // we attempt to cache the property reference here, as otherwise
155 // performance suffers when working in large data models. For
156 // a bit of reference, refer to RT-13937.
157 if (columnClass == null || previousProperty == null ||
158 ! columnClass.equals(rowData.getClass()) ||
159 ! previousProperty.equals(getProperty())) {
160
161 // create a new PropertyReference
162 this.columnClass = rowData.getClass();
163 this.previousProperty = getProperty();
164 this.propertyRef = new PropertyReference<T>(rowData.getClass(), getProperty());
165 }
166
167 if (propertyRef != null) {
168 return propertyRef.getProperty(rowData);
169 }
170 } catch (RuntimeException e) {
171 try {
172 // attempt to just get the value
173 T value = propertyRef.get(rowData);
174 return new ReadOnlyObjectWrapper<T>(value);
175 } catch (RuntimeException e2) {
176 // fall through to logged exception below
177 }
178
179 // log the warning and move on
180 final PlatformLogger logger = Logging.getControlsLogger();
181 if (logger.isLoggable(Level.WARNING)) {
182 logger.warning("Can not retrieve property '" + getProperty() +
183 "' in TreeItemPropertyValueFactory: " + this +
184 " with provided class type: " + rowData.getClass(), e);
185 }
186 propertyRef = null;
187 }
188
189 return null;
190 }
191 }
|