Spec-Zone .ru
спецификации, руководства, описания, API
001/*
002 * Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation.  Oracle designates this
008 * particular file as subject to the "Classpath" exception as provided
009 * by Oracle in the LICENSE file that accompanied this code.
010 *
011 * This code is distributed in the hope that it will be useful, but WITHOUT
012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
014 * version 2 for more details (a copy is included in the LICENSE file that
015 * accompanied this code).
016 *
017 * You should have received a copy of the GNU General Public License version
018 * 2 along with this work; if not, write to the Free Software Foundation,
019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020 *
021 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
022 * or visit www.oracle.com if you need additional information or have any
023 * questions.
024 */
025
026package javafx.scene.control.cell;
027
028import javafx.beans.property.Property;
029import javafx.beans.property.ReadOnlyObjectWrapper;
030import javafx.beans.value.ObservableValue;
031import javafx.scene.control.TableCell;
032import javafx.scene.control.TableColumn;
033import javafx.scene.control.TableColumn.CellDataFeatures;
034import javafx.scene.control.TableView;
035import javafx.util.Callback;
036
037import sun.util.logging.PlatformLogger;
038import com.sun.javafx.property.PropertyReference;
039import com.sun.javafx.scene.control.Logging;
040
041
042/**
043 * A convenience implementation of the Callback interface, designed specifically
044 * for use within the {@link TableColumn} 
045 * {@link TableColumn#cellValueFactoryProperty() cell value factory}. An example
046 * of how to use this class is:
047 * 
048 * <pre><code>
049 * TableColumn&lt;Person,String&gt; firstNameCol = new TableColumn&lt;Person,String&gt;("First Name");
050 * firstNameCol.setCellValueFactory(new PropertyValueFactory&lt;Person,String&gt;("firstName"));
051 * </code></pre>
052 * 
053 * In this example, the "firstName" string is used as a reference to an assumed 
054 * <code>firstNameProperty()</code> method in the <code>Person</code> class type
055 * (which is the class type of the TableView 
056 * {@link TableView#itemsProperty() items} list). Additionally, this method must
057 * return a {@link Property} instance. If a method meeting these requirements
058 * is found, then the {@link TableCell} is populated with this ObservableValue<T>.
059 * In addition, the TableView will automatically add an observer to the 
060 * returned value, such that any changes fired will be observed by the TableView,
061 * resulting in the cell immediately updating.
062 * 
063 * <p>If no method matching this pattern exists, there is fall-through support
064 * for attempting to call get&lt;property&gt;() or is&lt;property&gt;() (that is,
065 * <code>getFirstName()</code> or <code>isFirstName()</code> in the example
066 * above). If a  method matching this pattern exists, the value returned from this method
067 * is wrapped in a {@link ReadOnlyObjectWrapper} and returned to the TableCell.
068 * However, in this situation, this means that the TableCell will not be able
069 * to observe the ObservableValue for changes (as is the case in the first 
070 * approach above). 
071 * 
072 * <p>For reference (and as noted in the TableColumn 
073 * {@link TableColumn#cellValueFactory cell value factory} documentation), the 
074 * long form of the code above would be the following:
075 * 
076 * <pre><code>
077 * TableColumn&lt;Person,String&gt; firstNameCol = new TableColumn&lt;Person,String&gt;("First Name");
078 * firstNameCol.setCellValueFactory(new Callback&lt;CellDataFeatures&lt;Person, String&gt;, ObservableValue&lt;String&gt;&gt;() {
079 *     public ObservableValue&lt;String&gt; call(CellDataFeatures&lt;Person, String&gt; p) {
080 *         // p.getValue() returns the Person instance for a particular TableView row
081 *         return p.getValue().firstNameProperty();
082 *     }
083 *  });
084 * }
085 * </code></pre>
086 * 
087 * @see TableColumn
088 * @see TableView
089 * @see TableCell
090 * @see TreeItemPropertyValueFactory
091 * @see MapValueFactory
092 * @param <S> The type of the class contained within the TableView.items list.
093 * @param <T> The type of the class contained within the TableColumn cells.
094 */
095public class PropertyValueFactory<S,T> implements Callback<CellDataFeatures<S,T>, ObservableValue<T>> {
096
097    private final String property;
098
099    private Class columnClass;
100    private String previousProperty;
101    private PropertyReference<T> propertyRef;
102
103    /**
104     * Creates a default PropertyValueFactory to extract the value from a given
105     * TableView row item reflectively, using the given property name.
106     * 
107     * @param property The name of the property with which to attempt to 
108     *      reflectively extract a corresponding value for in a given object.
109     */
110    public PropertyValueFactory(String property) {
111        this.property = property;
112    }
113
114    /** {@inheritDoc} */
115    @Override public ObservableValue<T> call(CellDataFeatures<S,T> param) {
116        return getCellDataReflectively((T)param.getValue());
117    }
118
119    /**
120     * Returns the property name provided in the constructor.
121     */
122    public final String getProperty() { return property; }
123
124    private ObservableValue<T> getCellDataReflectively(T rowData) {
125        if (getProperty() == null || getProperty().isEmpty() || rowData == null) return null;
126
127        try {
128            // we attempt to cache the property reference here, as otherwise
129            // performance suffers when working in large data models. For
130            // a bit of reference, refer to RT-13937.
131            if (columnClass == null || previousProperty == null ||
132                    ! columnClass.equals(rowData.getClass()) || 
133                    ! previousProperty.equals(getProperty())) {
134
135                // create a new PropertyReference
136                this.columnClass = rowData.getClass();
137                this.previousProperty = getProperty();
138                this.propertyRef = new PropertyReference<T>(rowData.getClass(), getProperty());
139            }
140
141            return propertyRef.getProperty(rowData);
142        } catch (IllegalStateException e) {
143            try {
144                // attempt to just get the value
145                T value = propertyRef.get(rowData);
146                return new ReadOnlyObjectWrapper<T>(value);
147            } catch (IllegalStateException e2) {
148                // fall through to logged exception below
149            }
150            
151            // log the warning and move on
152            final PlatformLogger logger = Logging.getControlsLogger();
153            if (logger.isLoggable(PlatformLogger.WARNING)) {
154               logger.finest("Can not retrieve property '" + getProperty() + 
155                        "' in PropertyValueFactory: " + this + 
156                        " with provided class type: " + rowData.getClass(), e);
157            }
158        }
159
160        return null;
161    }
162}