Possible bug in DataColumnSpecFilterPanel

I’ve been trying to update a node which uses a DialogComponentColumnFilter2 in such a way as to be able to change the acceptable columns filter:

		final DialogComponentColumnFilter2 diaC =
				new DialogComponentColumnFilter2(colFilterMdl, 0);
		addDialogComponent(diaC);
		filterPanel = Arrays.stream(diaC.getComponentPanel().getComponents())
				.filter(c -> c instanceof DataColumnSpecFilterPanel)
				.map(c -> (DataColumnSpecFilterPanel) c).findFirst()
				.orElse(null);

		allowCollectionsMdl = createAllowCollectionsModel();
		addDialogComponent(new DialogComponentBoolean(allowCollectionsMdl,
				ALLOW_WRAPPING_COLLECTIONS));
		allowCollectionsMdl.addChangeListener(new ChangeListener() {

			@Override
			public void stateChanged(ChangeEvent e) {
				updateFilterPanel();

			}
		});

	private void updateFilterPanel() {
		filterPanel.updateWithNewConfiguration(
				new DataColumnSpecFilterConfiguration(SELECT_COLUMNS,
						allowCollectionsMdl.getBooleanValue()
								? ANY_COLUMN_FILTER
								: COLUMN_FILTER,
						FILTER_BY_DATATYPE | FILTER_BY_NAMEPATTERN));
	}

The JavaDoc for DataColumnSpecFilterPanel#updateWithNewConfiguration() says:

Updates the panel by using the given {@link DataColumnSpecFilterConfiguration} object.
This method allows for example to change the allowed column type on the fly.

which suggests I’m using it for what it was intended, but the filter panel in the node dialog does not update to reflect the new filter.

This is presumably a bug - either in my attempted usage or somewhere in the implementation?

Thanks

Steve

2 Likes

Hey Steve,

I just tried to reproduce your issue but was not able to do so. In my minimal code example the update of the panel works just fine. Could you provide a minimal code example in order that we can try to reproduce it.?

Best regards
Lars

public class TestNodeDialog extends NodeDialogPane {

    private final SettingsModelColumnFilter2 m_settingsModelColFilter =
        new SettingsModelColumnFilter2("settingsmodelcolfilter");

    private final SettingsModelBoolean m_booleanComponent = new SettingsModelBoolean("boolean", false);

    private final DialogComponentColumnFilter2 m_dialogComponentColFilter;

    private final DataColumnSpecFilterPanel m_colSpecFilterPanel;

    public TestNodeDialog() {
        m_dialogComponentColFilter = new DialogComponentColumnFilter2(m_settingsModelColFilter, 0);

        m_colSpecFilterPanel = Arrays.stream(m_dialogComponentColFilter.getComponentPanel().getComponents())
            .filter(DataColumnSpecFilterPanel.class::isInstance)//
            .map(DataColumnSpecFilterPanel.class::cast)//
            .findFirst()//
            .orElse(null);

        final var panel = new JPanel(new GridBagLayout());
        final var gbc = new GBCBuilder();
        panel.add(m_dialogComponentColFilter.getComponentPanel(), gbc.build());

        final var booleanDialogComponent = new DialogComponentBoolean(m_booleanComponent, "Remove retained columns");
        m_booleanComponent.addChangeListener(l -> updateFilterPanel());

        panel.add(booleanDialogComponent.getComponentPanel(), gbc.incY().build());

        addTab("Settings", panel);
    }

    private void updateFilterPanel() {
        m_colSpecFilterPanel.updateWithNewConfiguration(
            new DataColumnSpecFilterConfiguration("colFilter", new InputFilter<DataColumnSpec>() {
                @Override
                public boolean include(final DataColumnSpec name) {
                    return m_booleanComponent.getBooleanValue() ? name.getType().isCompatible(DataValue.class)
                        : name.getType().isCompatible(StringValue.class);
                }
            }, DataColumnSpecFilterConfiguration.FILTER_BY_DATATYPE | NameFilterConfiguration.FILTER_BY_NAMEPATTERN));
    }

    @Override
    protected void loadSettingsFrom(final NodeSettingsRO settings, final PortObjectSpec[] specs)
        throws NotConfigurableException {
        m_dialogComponentColFilter.loadSettingsFrom(settings, specs);
    }

    @Override
    protected void saveSettingsTo(final NodeSettingsWO settings) throws InvalidSettingsException {
    }
}
3 Likes

Hi @laaaarsi

thanks for getting back about this so quickly. Below is the full code body:

public class SingletonCollectionNodeDialog extends DefaultNodeSettingsPane {

	private static final int FILTER_FLAGS =
			FILTER_BY_DATATYPE | FILTER_BY_NAMEPATTERN;

	private static final String ALLOW_COLLECTIONS =
			"Allow collections";

	private static final String SELECT_COLUMNS = "Select Columns";

	/**
	 * {@link InputFilter} for non-collection columns
	 */
	static final InputFilter<DataColumnSpec> COLUMN_FILTER =
			new InputFilter<DataColumnSpec>() {

				@Override
				public boolean include(DataColumnSpec name) {
					return !name.getType().isCollectionType();
				}
			};

	/**
	 * {@link InputFilter} for any column type
	 */
	static final InputFilter<DataColumnSpec> ANY_COLUMN_FILTER =
			new InputFilter<DataColumnSpec>() {

				@Override
				public boolean include(DataColumnSpec name) {
					return true;
				}
			};

	private final DataColumnSpecFilterPanel filterPanel;

	private final SettingsModelBoolean allowCollectionsMdl;

	/**
	 * Constructor
	 */
	protected SingletonCollectionNodeDialog() {
		super();

		createNewGroup(SELECT_COLUMNS);

		final SettingsModelColumnFilter2 colFilterMdl =
				createColumnFilterModel();
		final DialogComponentColumnFilter2 diaC =
				new DialogComponentColumnFilter2(colFilterMdl, 0);
		addDialogComponent(diaC);
		filterPanel = Arrays.stream(diaC.getComponentPanel().getComponents())
				.filter(c -> c instanceof DataColumnSpecFilterPanel)
				.map(c -> (DataColumnSpecFilterPanel) c).findFirst()
				.orElse(null);

		allowCollectionsMdl = createAllowCollectionsModel();
		addDialogComponent(new DialogComponentBoolean(allowCollectionsMdl,
				ALLOW_COLLECTIONS));
		allowCollectionsMdl.addChangeListener(new ChangeListener() {

			@Override
			public void stateChanged(ChangeEvent e) {
				updateFilterPanel();

			}
		});
		// updateFilterPanel(); - Dont do here as fails - no input spec!

		setHorizontalPlacement(true);
		
	}

	/**
	 * Update the filter panel
	 */
	private void updateFilterPanel() {
		if (filterPanel != null) {
			filterPanel.updateWithNewConfiguration(
					new DataColumnSpecFilterConfiguration(SELECT_COLUMNS,
							allowCollectionsMdl.getBooleanValue()
									? ANY_COLUMN_FILTER
									: COLUMN_FILTER,
							FILTER_FLAGS));
		}
	}

	/**
	 * @return model for the Allow Wrapping Collections setting
	 */
	static SettingsModelBoolean createAllowCollectionsModel() {
		return new SettingsModelBoolean(ALLOW_COLLECTIONS, false);
	}

	/**
	 * @return The model for the column filter
	 */
	static SettingsModelColumnFilter2 createColumnFilterModel() {
		return new SettingsModelColumnFilter2(SELECT_COLUMNS, COLUMN_FILTER,
				FILTER_FLAGS);
	}

}

Apart from the fact that I am using DefaultNodeSettingsPane as my parent class and you are using the harder work NodeDialogPane option, I’m not at the moment seeing very much difference between the two, but I’ve tested mine again this morning with an input table containing one StringValue column and one List of IntValue column - regardless of the state of the boolean model I only see the StringValue column on the ‘Include’ side of the dialog, and no columns on the ‘Exclude’ side.

Steve

2 Likes

Thanks for providing your code and you are right somewhat is fishy here. Besides that I think it is intended that you dont see any not allowed column on the “Exclude” side.

I debugged a little bit but did not completely understood what exactly is the problem (need some more investigating). It seems like it depends on how you initialize your DialogComponentColumnFilter2 to be more precise your SettingsModelColumnFilter2 in your case

	/**
	 * @return The model for the column filter
	 */
	static SettingsModelColumnFilter2 createColumnFilterModel() {
		return new SettingsModelColumnFilter2(SELECT_COLUMNS, COLUMN_FILTER,
				FILTER_FLAGS);
	}

If you use the the ANY_COLUMN_FILTER here, which allows all types of columns and therefore Collection columns are a subset of those, it seems to work.

To solve the problem on loading you cold call the updateFilterPanel in the overridable onOpen() method.

Hope this helps even though its kinda strange and it needs as I said some more investigation.

Best regards
Lars

3 Likes

Filed a ticket with the internal number AP-18079 for this issue. I hope you can use the workaround for the time being.

Best regards
Lars

1 Like

Thanks Lars.

I think (It’s quite a convoluted path through the component panels and settings objects!) that the settings model is possibly storing the list of include and exclude columns from the initial filter, and so maybe then the component never “finds” the new matching columns. I can confirm though that the #onOpen() workaround works with either the model being constructed as in your reply, or with null as the InputFilter<DataColumnSpec> option (which is what looks like the single argument constructor effectively does too):

	static SettingsModelColumnFilter2 createColumnFilterModel() {
		return new SettingsModelColumnFilter2(SELECT_COLUMNS, null,
				FILTER_FLAGS);
	}

Thanks!

Steve

1 Like

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.