class MergeNodeDuplicates implements PluginGeneralActionsManipulator {

    public static final String DELETE_MERGED_NODES_SAVED_PREFERENCES = "MergeNodeDuplicates_DeleteMergedNodes";
    public static final String CASE_SENSITIVE_SAVED_PREFERENCES = "MergeNodeDuplicates_CaseSensitive";
    /**
     * To be set by the UI
     */
    private List<List<Node>> duplicateGroups;
    private boolean deleteMergedNodes;
    private boolean caseSensitive;
    private Column[] columns;
    private AttributeRowsMergeStrategy[] mergeStrategies;

    @Override
    public void execute() {
        Graph graph = Lookup.getDefault().lookup(GraphController.class).getGraphModel().getGraph();
        GraphElementsController gec = Lookup.getDefault().lookup(GraphElementsController.class);
        for (List<Node> nodes : duplicateGroups) {
            gec.mergeNodes(graph, nodes.toArray(new Node[0]), nodes.get(0), columns, mergeStrategies,
                deleteMergedNodes);
        }
        NbPreferences.forModule(MergeNodeDuplicates.class)
            .putBoolean(DELETE_MERGED_NODES_SAVED_PREFERENCES, deleteMergedNodes);
        NbPreferences.forModule(MergeNodeDuplicates.class).putBoolean(CASE_SENSITIVE_SAVED_PREFERENCES, caseSensitive);
    }

    @Override
    public String getName() {
        return NbBundle.getMessage(MergeNodeDuplicates.class, "MergeNodeDuplicates.name");
    }

    @Override
    public String getDescription() {
        return "MergeNodeDuplicates.description";
    }

    @Override
    public boolean canExecute() {
        return Lookup.getDefault().lookup(GraphElementsController.class).getNodesCount() > 0;
    }

    @Override
    public ManipulatorUI getUI() {<FILL_FUNCTION_BODY>}

    @Override
    public int getType() {
        return 100;
    }

    @Override
    public int getPosition() {
        return 0;
    }

    @Override
    public Icon getIcon() {
        return ImageUtilities.loadImageIcon("DataLaboratoryPlugin/merge.png", false);
    }

    public Column[] getColumns() {
        return columns;
    }

    public void setColumns(Column[] columns) {
        this.columns = columns;
    }

    public boolean isDeleteMergedNodes() {
        return deleteMergedNodes;
    }

    public void setDeleteMergedNodes(boolean deleteMergedNodes) {
        this.deleteMergedNodes = deleteMergedNodes;
    }

    public boolean isCaseSensitive() {
        return caseSensitive;
    }

    public void setCaseSensitive(boolean caseSensitive) {
        this.caseSensitive = caseSensitive;
    }

    public List<List<Node>> getDuplicateGroups() {
        return duplicateGroups;
    }

    public void setDuplicateGroups(List<List<Node>> duplicateGroups) {
        this.duplicateGroups = duplicateGroups;
    }

    public AttributeRowsMergeStrategy[] getMergeStrategies() {
        return mergeStrategies;
    }

    public void setMergeStrategies(AttributeRowsMergeStrategy[] mergeStrategies) {
        this.mergeStrategies = mergeStrategies;
    }
}

        Table nodeTable = Lookup.getDefault().lookup(GraphController.class).getGraphModel().getNodeTable();
        List<Column> columnsList = new ArrayList<>();
        for (Column column : nodeTable) {
            if (!column.isReadOnly()) {
                columnsList.add(column);
            }
        }

        columns = columnsList.toArray(new Column[0]);
        mergeStrategies = new AttributeRowsMergeStrategy[columns.length];
        deleteMergedNodes =
            NbPreferences.forModule(MergeNodeDuplicates.class).getBoolean(DELETE_MERGED_NODES_SAVED_PREFERENCES, true);
        caseSensitive =
            NbPreferences.forModule(MergeNodeDuplicates.class).getBoolean(CASE_SENSITIVE_SAVED_PREFERENCES, true);
        return new MergeNodeDuplicatesUI();
    
<no_super_class>