title: Dealing with null values in Cell
body: <p>I'm having serious problems getting a JavaFX 2 TableView to deal with null values in my data. So much trouble in fact I've put together a simple demo of the problem which is shown below.</p>
  
  <p>Essentially the problem is that some of my data may be null and coercing the null to a value, such as an empty string, is not valid, it must be null. In the real code I'm working on I have a null date value, to keep the example simple I've shown a null string below.</p>
  
  <p>Rows 1 and 2 of the table have null values. The two columns are different, the first shows the behaviour of the TextFieldTableCell the second my implementation of an editable cell. Both show the same incorrect behaviour.</p>
  
  <p>The current behaviour is this: </p>
  
  <ol>
  <li>Click the cell to enter editing mode</li>
  <li>Enter a value in the cell</li>
  <li>Press enter to commit the edit</li>
  <li>Nothing happens</li>
  </ol>
  
  <p>At step 4 I would expect the onEditCommit handler for the column to get called but it isn't. Having a look at the source for javax.scene.control.TableCell the commit isn't happening because of the first line of commitEdit is:</p>
  
  <pre><code>if (! isEditing()) return;
  </code></pre>
  
  <p>It seems that because the cell is null the editing property never gets set to true although I admit I've not yet traced through all the code to see why it never get's switched to true.</p>
  
  <p>Thanks as always for any help.</p>
  
  <hr>
  
  <p>Example</p>
  
  <p>Main Application</p>
  
  <pre><code>package example;
  
  import javafx.application.Application;
  import javafx.collections.FXCollections;
  import javafx.collections.ObservableList;
  import javafx.event.EventHandler;
  import javafx.scene.Group;
  import javafx.scene.Scene;
  import javafx.scene.control.TableCell;
  import javafx.scene.control.TableColumn;
  import javafx.scene.control.TableView;
  import javafx.scene.control.cell.PropertyValueFactory;
  import javafx.scene.control.cell.TextFieldTableCell;
  import javafx.stage.Stage;
  import javafx.util.Callback;
  
  public class NullCellEditingExample extends Application {
  
      private TableView table = new TableView();
      private final ObservableList&lt;Person&gt; data =
              FXCollections.observableArrayList( new Person(null, "Smith"), new Person("Isabella", null), 
              new Person("Ethan", "Williams"), new Person("Emma", "Jones"), new Person("Michael", "Brown"));
  
      public static void main(String[] args) {
          launch(args);
      }
  
      @Override
      public void start(Stage stage) {
          Scene scene = new Scene(new Group());
  
          TableColumn firstNameCol = createSimpleFirstNameColumn();
          TableColumn lastNameCol = createLastNameColumn();
          table.setItems(data);
          table.getColumns().addAll(firstNameCol, lastNameCol);
          table.setEditable(true);
  
          ((Group) scene.getRoot()).getChildren().addAll(table);
          stage.setScene(scene);
          stage.show();
      }
  
      private TableColumn createSimpleFirstNameColumn() {
          TableColumn firstNameCol = new TableColumn("First Name");
          firstNameCol.setMinWidth(100);
          firstNameCol.setCellValueFactory(new PropertyValueFactory&lt;Person, String&gt;("firstName"));
          firstNameCol.setCellFactory(TextFieldTableCell.forTableColumn());
          firstNameCol.setOnEditCommit(new EventHandler&lt;TableColumn.CellEditEvent&lt;Person, String&gt;&gt;() {
              @Override
              public void handle(TableColumn.CellEditEvent&lt;Person, String&gt; t) {
                  ((Person) t.getTableView().getItems().get(t.getTablePosition().getRow())).setFirstName(t.getNewValue());
              }
          });
  
          return firstNameCol;
      }
  
      private TableColumn createLastNameColumn() {
          Callback&lt;TableColumn, TableCell&gt; editableFactory = new Callback&lt;TableColumn, TableCell&gt;() {
              @Override
              public TableCell call(TableColumn p) {
                  return new EditingCell();
              }
          };
  
          TableColumn lastNameCol = new TableColumn("Last Name");
          lastNameCol.setMinWidth(100);
          lastNameCol.setCellValueFactory(new PropertyValueFactory&lt;Person, String&gt;("lastName"));
          lastNameCol.setCellFactory(editableFactory);
          lastNameCol.setOnEditCommit(new EventHandler&lt;TableColumn.CellEditEvent&lt;Person, String&gt;&gt;() {
              @Override
              public void handle(TableColumn.CellEditEvent&lt;Person, String&gt; t) {
                  t.getRowValue().setLastName(t.getNewValue());
              }
          });
  
          return lastNameCol;
      }   
  }
  </code></pre>
  
  <p>Editing Cell</p>
  
  <pre><code>package example;
  
  import javafx.beans.value.ChangeListener;
  import javafx.beans.value.ObservableValue;
  import javafx.event.EventHandler;
  import javafx.scene.control.TableCell;
  import javafx.scene.control.TextField;
  import javafx.scene.input.KeyCode;
  import javafx.scene.input.KeyEvent;
  
  public class EditingCell extends TableCell&lt;Person, String&gt; {
  
      private TextField textField;
  
      public EditingCell() {
      }
  
      @Override
      public void startEdit() {
          super.startEdit();
          if( textField == null ) {
              createTextField();
          }
          setText(null);
          setGraphic(textField);
          textField.selectAll();
      }
  
      @Override
      public void cancelEdit() {
          super.cancelEdit();
          setText((String) getItem());
          setGraphic(null);
      }
  
      @Override
      public void updateItem(String item, boolean empty) {
          super.updateItem(item, empty);
          if (empty) {
              setText(null);
              setGraphic(null);
          } else {
              if (isEditing()) {
                  if (textField != null) {
                      textField.setText(getString());
                  }
                  setText(null);
                  setGraphic(textField);
              } else {
                  setText(getString());
                  setGraphic(null);
              }
          }
      }
  
      private void createTextField() {
          textField = new TextField(getString());
          textField.setMinWidth(this.getWidth() - this.getGraphicTextGap() * 2);
          textField.focusedProperty().addListener(new ChangeListener&lt;Boolean&gt;() {
              @Override
              public void changed(ObservableValue&lt;? extends Boolean&gt; arg0, Boolean arg1, Boolean arg2) {
                  if (!arg2) { commitEdit(textField.getText()); }
              }
          });
  
          textField.setOnKeyReleased(new EventHandler&lt;KeyEvent&gt;() {
              @Override
              public void handle(KeyEvent t) {
                  if (t.getCode() == KeyCode.ENTER) {
                      String value = textField.getText();
                      if (value != null) { commitEdit(value); } else { commitEdit(null); }
                  } else if (t.getCode() == KeyCode.ESCAPE) {
                      cancelEdit();
                  }
              }
          });
      }
  
      private String getString() {
          return getItem() == null ? "" : getItem().toString();
      }
  }
  </code></pre>
  
  <p>Person</p>
  
  <pre><code>package example;
  
  import javafx.beans.property.SimpleStringProperty;
  
  public class Person {
  
      private final SimpleStringProperty firstName;
      private final SimpleStringProperty lastName;
  
      public Person(String firstName, String lastName) {
          this.firstName = new SimpleStringProperty(firstName);
          this.lastName = new SimpleStringProperty(lastName);
      }
  
      public String getFirstName() {
          return firstName.get();
      }
  
      public void setFirstName(String firstName) {
          this.firstName.set(firstName);
      }
  
      public SimpleStringProperty firstNameProperty() {
          return firstName;
      }
  
      public String getLastName() {
          return lastName.get();
      }
  
      public void setLastName(String lastName) {
          this.lastName.set(lastName);
      }
  
      public SimpleStringProperty lastNameProperty() {
          return lastName;
      }
  }
  </code></pre>