简体   繁体   中英

Seemingly random NullPointerException when updating a TreeTableView

I just added filtering to my TreeTableView and a bug started appearing that seems to be random (probably is not random). I can reproduce it with spamming the filter textField to update the TreeTableView until it jams. I get the following stack trace, the TreeTableView is not updating anymore but the program does not crash. The only part of the stack trace that is related to my code points to a cellFactory where I override the updateItem of a CheckBoxTreeTableCell in order to achieve a styling for top level rows :

colActShop.setCellFactory(column ->{
        return new CheckBoxTreeTableCell<Product, Boolean>(){
            @Override
            public void updateItem(Boolean item, boolean empty) {
                super.updateItem(item, empty);
                boolean isTopLevel = getTreeTableView().getRoot().getChildren()
                                   .contains(getTreeTableRow().getTreeItem());
                if(item == null || empty){
                    setText(null);
                    setGraphic(null);
                    //It crashes on the following line according to the stack trace
                    getTreeTableRow().getStyleClass().remove("topLevelRow");
                }else{
                    if(isTopLevel){
                        getTreeTableRow().getStyleClass().add("topLevelRow");
                    }else{
                        getTreeTableRow().getStyleClass().remove("topLevelRow");
                    }

                    setEditable(!isTopLevel);   
                }
            }
        };
    });

If I remove the getTreeTableRow().getStyleClass()... lines it seems to be removing the bug but then I lose the styling behaviour I am looking for. I desperately also tried putting some Platform.runlater at some places without success. I am unable to provide a minimal executable example for now as my filtering and TreeTableView updating are getting quite large. Here is a function that gets called to update the root of the TreeTableView when I enter text into the filter textField :

     /**
  * A method to update a TreeTableView every time backing data changes in the model,
  * without resetting the table by only adding and removing data when required.
  * Also applies filters
  * @param root : the root TreeItem of the table to update
  * @param inventory : the inventory backing this table
  * @param shop : the shop to calculate the price of products
  * @param filter : comma separated string filter
  * @param catFilter : category filter list
  */
private void updateTreeItemRoot(TreeItem<Product> root, Inventory inventory, Shop shop, String filter, ArrayList<String> catFilter){
    inventory.clean();
    shop.clean();
    inventory.calculateAverageData();
    //retrieve the product list of category items of the previous TreeTableView structure before updating
    ArrayList<Product> oldProdCategoryList= new ArrayList<Product>();
    ArrayList<TreeItem<Product>> oldTreeItemCategoryList= new ArrayList<>(root.getChildren());
    for(TreeItem<Product> treeItem : oldTreeItemCategoryList){
        oldProdCategoryList.add(treeItem.getValue());
    }
    for(Entry<String, ArrayList<Product>> entry : inventory.getData().entrySet()){
        Product product = inventory.getAverageData(entry.getKey());
        //Filtering the TreeTableView 
        boolean filterOK = false;
        if(catFilter!=null){
            if(catFilter.contains("All")||catFilter.contains("All seeds")){
                filterOK = true;
            }else{
                //else look for any matching categories
                for(String str:catFilter){
                    if(product.getCategories().stream().anyMatch(s -> s.equals(str))){
                        filterOK = true;
                        break;
                    }
                }
            }
            //turn filterOk to false if we want only mature and product is not
            if(catFilter.contains("Only mature")&&product.getMaturity()!=100) {
                filterOK = false;
            }
            //turn filterOK to false if we want only seeds and the product is not a seed
            if(catFilter.contains("Only seeds")){
                if(!(product.getCategories().stream().anyMatch(s -> s.equals("Seed")))){
                    filterOK = false;
                }
            }
        }else{
            filterOK = true;
        }
        String[] filterList = filter.split(",");
        if(filter.length()>0&&!Arrays.stream(filterList).anyMatch(s->product.getName().matches( "(?i:.*"+s+".*)"))){
            filterOK = false;
        }
        if(filterOK){
            product.updatePrice(shop);
            TreeItem<Product> averageProdDataItem; ;
            //if this product category is not yet present in the table add it.
            if(!oldProdCategoryList.contains(product)){
                averageProdDataItem = new TreeItem<Product>(product);
                averageProdDataItem.setExpanded(true);
                root.getChildren().add(averageProdDataItem);
            }else{
                //else if this product category is already in the table, retrieve the reference and remove it from the 
                //  "old" Lists to see if something needs to be removed from the table in the end
                averageProdDataItem = oldTreeItemCategoryList.get(oldProdCategoryList.indexOf(product));
                oldTreeItemCategoryList.remove(oldProdCategoryList.indexOf(product));
                oldProdCategoryList.remove(product);
            }
            //retrieve the product list for this category category of the previous TreeTableView structure before updating
            ArrayList<Product> oldProdList= new ArrayList<Product>();
            ArrayList<TreeItem<Product>> oldTreeItemList= new ArrayList<>(averageProdDataItem.getChildren());
            for(TreeItem<Product> treeItem : oldTreeItemList){
                oldProdList.add(treeItem.getValue());
            }
            for(Product prod : entry.getValue()){
                prod.updatePrice(shop);
                TreeItem<Product> treeItem;
                //if the table does not contain this prod, add it
                if(!oldProdList.contains(prod)){
                    treeItem = new TreeItem<Product>(prod);
                    averageProdDataItem.getChildren().add(treeItem);
                }else{

                    oldTreeItemList.remove(oldProdList.indexOf(prod));
                    oldProdList.remove(prod);
                }
            }
            averageProdDataItem.getChildren().removeAll(oldTreeItemList); //clean up empty products
        }// if filterOK
    }//for
    root.getChildren().removeAll(oldTreeItemCategoryList);// clean up empty categories
}

It seems to be the same as this issue that has been opened 3 times and closed without any comment.

Stack Trace:

Exception in thread "JavaFX Application Thread" java.lang.NullPointerException
    at com.sun.javafx.scene.control.skin.CellSkinBase.access$200(CellSkinBase.java:54)
    at com.sun.javafx.scene.control.skin.CellSkinBase$StyleableProperties$1.getStyleableProperty(CellSkinBase.java:149)
    at com.sun.javafx.scene.control.skin.CellSkinBase$StyleableProperties$1.getStyleableProperty(CellSkinBase.java:138)
    at javafx.scene.CssStyleHelper.resetToInitialValues(CssStyleHelper.java:447)
    at javafx.scene.CssStyleHelper.createStyleHelper(CssStyleHelper.java:180)
    at javafx.scene.Node.reapplyCss(Node.java:8983)
    at javafx.scene.Node.reapplyCss(Node.java:9012)
    at javafx.scene.Node.impl_reapplyCSS(Node.java:8946)
    at javafx.scene.Node$3.onChanged(Node.java:1023)
    at com.sun.javafx.collections.TrackableObservableList.lambda$new$29(TrackableObservableList.java:45)
    at com.sun.javafx.collections.ListListenerHelper$SingleChange.fireValueChangedEvent(ListListenerHelper.java:164)
    at com.sun.javafx.collections.ListListenerHelper.fireValueChangedEvent(ListListenerHelper.java:73)
    at javafx.collections.ObservableListBase.fireChange(ObservableListBase.java:233)
    at javafx.collections.ListChangeBuilder.commit(ListChangeBuilder.java:482)
    at javafx.collections.ListChangeBuilder.endChange(ListChangeBuilder.java:541)
    at javafx.collections.ObservableListBase.endChange(ObservableListBase.java:205)
    at javafx.collections.ModifiableObservableListBase.remove(ModifiableObservableListBase.java:183)
    at javafx.collections.ModifiableObservableListBase.remove(ModifiableObservableListBase.java:171)
    at homier.farmGame.controller.Engine$2.updateItem(Engine.java:768)
    at homier.farmGame.controller.Engine$2.updateItem(Engine.java:1)
    at javafx.scene.control.TreeTableCell.updateItem(TreeTableCell.java:630)
    at javafx.scene.control.TreeTableCell.indexChanged(TreeTableCell.java:457)
    at javafx.scene.control.IndexedCell.updateIndex(IndexedCell.java:116)
    at com.sun.javafx.scene.control.skin.TableRowSkinBase.recreateCells(TableRowSkinBase.java:671)
    at com.sun.javafx.scene.control.skin.TableRowSkinBase.updateCells(TableRowSkinBase.java:499)
    at com.sun.javafx.scene.control.skin.TreeTableRowSkin.updateCells(TreeTableRowSkin.java:220)
    at com.sun.javafx.scene.control.skin.TableRowSkinBase.checkState(TableRowSkinBase.java:631)
    at com.sun.javafx.scene.control.skin.TableRowSkinBase.computePrefHeight(TableRowSkinBase.java:571)
    at javafx.scene.control.Control.computePrefHeight(Control.java:547)
    at javafx.scene.Parent.prefHeight(Parent.java:935)
    at javafx.scene.layout.Region.prefHeight(Region.java:1435)
    at com.sun.javafx.scene.control.skin.VirtualFlow.resizeCellSize(VirtualFlow.java:1947)
    at com.sun.javafx.scene.control.skin.VirtualFlow.addLeadingCells(VirtualFlow.java:1247)
    at com.sun.javafx.scene.control.skin.VirtualFlow.layoutChildren(VirtualFlow.java:1194)
    at javafx.scene.Parent.layout(Parent.java:1087)
    at javafx.scene.Parent.layout(Parent.java:1093)
    at javafx.scene.Parent.layout(Parent.java:1093)
    at javafx.scene.Parent.layout(Parent.java:1093)
    at javafx.scene.Parent.layout(Parent.java:1093)
    at javafx.scene.Scene.doLayoutPass(Scene.java:552)
    at javafx.scene.Scene$ScenePulseListener.pulse(Scene.java:2397)
    at com.sun.javafx.tk.Toolkit.lambda$runPulse$29(Toolkit.java:398)
    at java.security.AccessController.doPrivileged(Native Method)
    at com.sun.javafx.tk.Toolkit.runPulse(Toolkit.java:397)
    at com.sun.javafx.tk.Toolkit.firePulse(Toolkit.java:424)
    at com.sun.javafx.tk.quantum.QuantumToolkit.pulse(QuantumToolkit.java:518)
    at com.sun.javafx.tk.quantum.QuantumToolkit.pulse(QuantumToolkit.java:498)
    at com.sun.javafx.tk.quantum.QuantumToolkit.pulseFromQueue(QuantumToolkit.java:491)
    at com.sun.javafx.tk.quantum.QuantumToolkit.lambda$runToolkit$403(QuantumToolkit.java:319)
    at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:95)
    at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
    at com.sun.glass.ui.win.WinApplication.lambda$null$147(WinApplication.java:177)
    at java.lang.Thread.run(Unknown Source)
Exception in thread "JavaFX Application Thread" java.lang.NullPointerException
    at com.sun.javafx.scene.control.skin.CellSkinBase.access$200(CellSkinBase.java:54)
    at com.sun.javafx.scene.control.skin.CellSkinBase$StyleableProperties$1.getStyleableProperty(CellSkinBase.java:149)
    at com.sun.javafx.scene.control.skin.CellSkinBase$StyleableProperties$1.getStyleableProperty(CellSkinBase.java:138)
    at javafx.scene.CssStyleHelper.resetToInitialValues(CssStyleHelper.java:447)
    at javafx.scene.CssStyleHelper.createStyleHelper(CssStyleHelper.java:180)
    at javafx.scene.Node.reapplyCss(Node.java:8983)
    at javafx.scene.Node.impl_reapplyCSS(Node.java:8946)
    at javafx.scene.control.Control$1.invalidated(Control.java:300)
    at javafx.beans.property.ObjectPropertyBase.markInvalid(ObjectPropertyBase.java:111)
    at javafx.beans.property.ObjectPropertyBase.set(ObjectPropertyBase.java:146)
    at javafx.css.StyleableObjectProperty.set(StyleableObjectProperty.java:82)
    at javafx.scene.control.Control$1.set(Control.java:237)
    at javafx.scene.control.Control$1.set(Control.java:220)
    at javafx.scene.control.Control.setSkin(Control.java:217)
    at com.sun.javafx.scene.control.skin.TableRowSkinBase.recreateCells(TableRowSkinBase.java:673)
    at com.sun.javafx.scene.control.skin.TableRowSkinBase.updateCells(TableRowSkinBase.java:499)
    at com.sun.javafx.scene.control.skin.TreeTableRowSkin.updateCells(TreeTableRowSkin.java:220)
    at com.sun.javafx.scene.control.skin.TableRowSkinBase.checkState(TableRowSkinBase.java:631)
    at com.sun.javafx.scene.control.skin.TableRowSkinBase.computePrefHeight(TableRowSkinBase.java:571)
    at javafx.scene.control.Control.computePrefHeight(Control.java:547)
    at javafx.scene.Parent.prefHeight(Parent.java:935)
    at javafx.scene.layout.Region.prefHeight(Region.java:1435)
    at com.sun.javafx.scene.control.skin.VirtualFlow.resizeCellSize(VirtualFlow.java:1947)
    at com.sun.javafx.scene.control.skin.VirtualFlow.addLeadingCells(VirtualFlow.java:1247)
    at com.sun.javafx.scene.control.skin.VirtualFlow.layoutChildren(VirtualFlow.java:1194)
    at javafx.scene.Parent.layout(Parent.java:1087)
    at javafx.scene.Parent.layout(Parent.java:1093)
    at javafx.scene.Parent.layout(Parent.java:1093)
    at javafx.scene.Parent.layout(Parent.java:1093)
    at javafx.scene.Parent.layout(Parent.java:1093)
    at javafx.scene.Scene.doLayoutPass(Scene.java:552)
    at javafx.scene.Scene$ScenePulseListener.pulse(Scene.java:2397)
    at com.sun.javafx.tk.Toolkit.lambda$runPulse$29(Toolkit.java:398)
    at java.security.AccessController.doPrivileged(Native Method)
    at com.sun.javafx.tk.Toolkit.runPulse(Toolkit.java:397)
    at com.sun.javafx.tk.Toolkit.firePulse(Toolkit.java:424)
    at com.sun.javafx.tk.quantum.QuantumToolkit.pulse(QuantumToolkit.java:518)
    at com.sun.javafx.tk.quantum.QuantumToolkit.pulse(QuantumToolkit.java:498)
    at com.sun.javafx.tk.quantum.QuantumToolkit.pulseFromQueue(QuantumToolkit.java:491)
    at com.sun.javafx.tk.quantum.QuantumToolkit.lambda$runToolkit$403(QuantumToolkit.java:319)
    at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:95)
    at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
    at com.sun.glass.ui.win.WinApplication.lambda$null$147(WinApplication.java:177)
    at java.lang.Thread.run(Unknown Source)

Does anyone have an idea to guide my debugging? Do I need to give up on styling certain rows?

EDIT 1 After some further testing it is less random than I thought. I enter one letter in the filter field and remove it 34 times before it jams, reproductible. If I use a sequence of letters (instead of using the same letter each time) it seems to jam earlier than 34, like 29 or 31. So it makes me think that something it not getting cleaned each iteration but I have no idea what.

The reason for the bug is still unknown to me. I think I found a way around by setting the style of the special rows by using setRowFactory on the TreeTableView instead of setCellFactory and getTreeTableRow() on the Column. It seems weird since the Row is the same object, I just access it in a different way but so far I have not had bugs with this way.

    tableShop.setRowFactory(table-> {
        return new TreeTableRow<Product>(){
            @Override
            public void updateItem(Product pers, boolean empty) {
                super.updateItem(pers, empty);      
                boolean isTopLevel = table.getRoot().getChildren().contains(treeItemProperty().get());
                if(pers==null||empty) {
                    setText(null);
                    setGraphic(null);
                    getStyleClass().remove("topLevelRow");
                }else {
                    if(isTopLevel) {
                        if(!getStyleClass().contains("topLevelRow")) {
                            getStyleClass().add("topLevelRow");
                        }
                    }else {
                        getStyleClass().remove("topLevelRow");
                    }
                }
            }
        };
    });

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM