简体   繁体   English

在更新TreeTableView时,似乎是随机的NullPointerException

[英]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). 我只是将过滤添加到TreeTableView中,并且开始出现似乎是随机的错误(可能不是随机的)。 I can reproduce it with spamming the filter textField to update the TreeTableView until it jams. 我可以通过向过滤器textField发送垃圾邮件来重现它,以更新TreeTableView直到出现阻塞。 I get the following stack trace, the TreeTableView is not updating anymore but the program does not crash. 我得到以下堆栈跟踪,TreeTableView不再更新,但程序不会崩溃。 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 : 与我的代码相关的堆栈跟踪的唯一部分指向cellFactory,在其中我重写CheckBoxTreeTableCell的updateItem以便为顶层行实现样式:

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. 如果我删除了getTreeTableRow().getStyleClass()...行,则似乎正在删除该错误,但随后我失去了正在寻找的样式行为。 I desperately also tried putting some Platform.runlater at some places without success. 我也拼命尝试在某些地方放置一些Platform.runlater ,但没有成功。 I am unable to provide a minimal executable example for now as my filtering and TreeTableView updating are getting quite large. 由于过滤和TreeTableView更新变得越来越大,我现在无法提供一个最小的可执行示例。 Here is a function that gets called to update the root of the TreeTableView when I enter text into the filter textField : 当我在过滤器textField中输入文本时,将调用以下函数来更新TreeTableView的根:

     /**
  * 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. 这似乎是相同的这个问题已经打开的3倍和关闭没有任何评论。

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. 编辑1经过进一步测试后,它比我想象的要少随机性。 I enter one letter in the filter field and remove it 34 times before it jams, reproductible. 我在过滤器字段中输入一个字母,然后在卡纸前将其删除34次。 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. 如果我使用一系列字母(而不是每次都使用相同的字母),它似乎比34(例如29或31)早于卡纸。因此,我认为每次迭代都不会清除某些内容,但我不知道该怎么做。

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. 我想我找到了一种方法,通过在TreeTableView上使用setRowFactory而不是在setCellFactory上使用setCellFactorygetTreeTableRow()来设置特殊行的样式。 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. 由于Row是同一个对象,所以看起来很奇怪,我只是以不同的方式访问它,但是到目前为止,我还没有使用这种方式的错误。

    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");
                    }
                }
            }
        };
    });

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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