/*
 * ARX: Powerful Data Anonymization
 * Copyright 2012 - 2017 Fabian Prasser, Florian Kohlmayer and contributors
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.deidentifier.arx.gui.view.impl.wizard;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.apache.commons.math3.util.Pair;
import org.deidentifier.arx.DataType;
import org.deidentifier.arx.DataType.DataTypeWithFormat;
import org.deidentifier.arx.gui.resources.Resources;
import org.deidentifier.arx.gui.view.SWTUtil;
import org.deidentifier.arx.io.ImportColumn;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.jface.viewers.ArrayContentProvider;
import org.eclipse.jface.viewers.CellEditor;
import org.eclipse.jface.viewers.CheckStateChangedEvent;
import org.eclipse.jface.viewers.CheckboxTableViewer;
import org.eclipse.jface.viewers.ColumnLabelProvider;
import org.eclipse.jface.viewers.ComboBoxCellEditor;
import org.eclipse.jface.viewers.ComboBoxViewerCellEditor;
import org.eclipse.jface.viewers.EditingSupport;
import org.eclipse.jface.viewers.ICheckStateListener;
import org.eclipse.jface.viewers.ICheckStateProvider;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.TableViewerColumn;
import org.eclipse.jface.viewers.TextCellEditor;
import org.eclipse.jface.wizard.WizardPage;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;

/**
 * Column overview page
 * 
 * This pages gives the user an overview of the detected columns and allows him
 * to change things around. First of all columns can be enabled or disabled on
 * an individual basis. Secondly the order of the columns can be changed around.
 * Furthermore a data type along with a format string can be defined for each
 * column.
 * 
 * A single column is represented by {@link ImportWizardModelColumn}, the list
 * of all detected columns can be accessed by
 * {@link ImportWizardModel#getWizardColumns()}.
 * 
 * @author Karol Babioch
 * @author Fabian Prasser
 */
public class ImportWizardPageColumns extends WizardPage {
    
    /**
     * Implements editing support for data type column within the column page
     * 
     * This allows to change the data type of columns. The modifications are
     * performed with a combo box {@link ComboBoxCellEditor}.
     */
    public class DatatypeEditingSupport extends EditingSupport {

        /** Reference to actual viewer. */
        private TableViewer viewer;

        /** Editors*/
        private Map<ImportWizardModelColumn, AutoDropComboBoxViewerCellEditor> editors = new
                HashMap<ImportWizardModelColumn, AutoDropComboBoxViewerCellEditor>();
        
        /** Types*/
        private Map<ImportWizardModelColumn, Map<String, DataType<?>>> types = 
                new HashMap<ImportWizardModelColumn, Map<String, DataType<?>>>();

        /**
         * Creates a new editor for the given {@link TableViewer}.
         * 
         * @param viewer The TableViewer this editor is implemented for
         * @param columns The columns
         */
        public DatatypeEditingSupport(TableViewer viewer) {
            super(viewer);
            this.viewer = viewer;
        }
        
        /**
         * Updates this editing support
         * @param columns
         */
        public void update(List<ImportWizardModelColumn> columns) {
            
            for (ImportWizardModelColumn column : columns) {
                this.types.put(column, new HashMap<String, DataType<?>>());
                
                List<Pair<DataType<?>, Double>> matchingtypes = wizardImport.getData().getMatchingDataTypes(column);
                List<String> labels = new ArrayList<String>();
                List<DataType<?>> types = new ArrayList<DataType<?>>();
                for (Pair<DataType<?>, Double> match : matchingtypes) {
                    
                    StringBuilder builder = new StringBuilder();
                    builder.append(match.getFirst().getDescription().getLabel());
                    if (match.getFirst() instanceof DataTypeWithFormat && ((DataTypeWithFormat)match.getFirst()).getFormat() != null) {
                        builder.append(" ("); //$NON-NLS-1$
                        builder.append(((DataTypeWithFormat)match.getFirst()).getFormat());
                        builder.append(")"); //$NON-NLS-1$
                    }
                    builder.append(" "); //$NON-NLS-1$
                    builder.append((int)(match.getSecond() * 100d));
                    builder.append("%"); //$NON-NLS-1$
                    
                    String label = builder.toString();
                    DataType<?> type = match.getFirst();
                    labels.add(label);
                    types.add(type);
                    this.types.get(column).put(label, type);
                }
                
                AutoDropComboBoxViewerCellEditor editor = new AutoDropComboBoxViewerCellEditor(viewer.getTable());
                editor.setContentProvider(new ArrayContentProvider());
                editor.setInput(labels.toArray(new String[labels.size()]));
                editors.put(column, editor);
            }
        }

        /**
         * Indicates that enabled cells within this column can be edited.
         *
         * @param column
         * @return
         */
        @Override
        protected boolean canEdit(Object column) {
            return ((ImportWizardModelColumn) column).isEnabled();
        }

        /**
         * Returns a reference to {@link #editor}.
         *
         * @param arg0
         * @return
         */
        @Override
        protected CellEditor getCellEditor(Object arg0) {
            return editors.get(arg0);
        }

        /**
         * Returns current index of {@link #choices} for given column datatype.
         *
         * @param element
         * @return
         */
        @Override
        protected Object getValue(Object element) {
            ImportWizardModelColumn column = (ImportWizardModelColumn) element;
            for (Entry<String, DataType<?>> entry : types.get(column).entrySet()) {
                if (entry.getValue() == column.getColumn().getDataType()) {
                    return entry.getKey();
                }
            }
            return null;
        }
        
        /**
         * Applies data type choice made by the user
         * 
         * If a data type, which requires a format string, was selected an input
         * dialog will be shown {@link actionShowFormatInputDialog}. Otherwise
         * the choice is directly applied. THe input dialog itself will make
         * sure that the format string is valid for the data type. This method on
         * the other hand will try to apply the format string to the available
         * preview data {@link ImportWizardModel#getPreviewData()} making sure
         * that it matches. In case of an error the choice is discarded.
         *
         * @param element
         * @param value
         */
        @Override
        protected void setValue(Object element, Object value) {

            // Extract
            String label = (String)value;
            ImportWizardModelColumn wizardColumn = (ImportWizardModelColumn) element;
            ImportColumn column = wizardColumn.getColumn();
            DataType<?> type = types.get(wizardColumn).get(label);
            
            /* Apply datatype */
            column.setDataType(type);
            getViewer().update(element, null);
            return;                
        }
    }

    /**
     * Implements the editing support for name column
     * 
     * This allows to change the name of columns. The modifications are
     * performed within a simple text field {@link TextCellEditor}.
     */
    public class NameEditingSupport extends EditingSupport {

        /** Reference to actual editor. */
        private TextCellEditor editor;

        /**
         * Creates a new editor for the given {@link TableViewer}.
         * 
         * @param viewer
         *            The TableViewer this editor is implemented for
         */
        public NameEditingSupport(TableViewer viewer) {

            super(viewer);
            editor = new TextCellEditor(viewer.getTable());
        }

        /**
         * Indicates that enabled cells within this column can be edited.
         *
         * @param column
         * @return
         */
        @Override
        protected boolean canEdit(Object column) {
            return ((ImportWizardModelColumn) column).isEnabled();
        }

        @Override
        protected CellEditor getCellEditor(Object arg0) {
            return editor;
        }

        /**
         * Retrieves name of column ({@link ImportColumn#getAliasName()}).
         *
         * @param arg0
         * @return
         */
        @Override
        protected Object getValue(Object arg0) {
            return ((ImportWizardModelColumn) arg0).getColumn().getAliasName();
        }

        /**
         * Sets name for given column ({@link ImportColumn#setAliasName(String)}).
         *
         * @param element
         * @param value
         */
        @Override
        protected void setValue(Object element, Object value) {

            ((ImportWizardModelColumn) element).getColumn().setAliasName((String) value);
            getViewer().update(element, null);
            check();
        }
    }

    /**
     * Works around JFace bugs. 
     * @see https://bugs.eclipse.org/bugs/show_bug.cgi?id=230398
     *
     */
    private static class AutoDropComboBoxViewerCellEditor extends ComboBoxViewerCellEditor {
        
        /**
         * 
         *
         * @param parent
         */
        protected AutoDropComboBoxViewerCellEditor(Composite parent) {
            super(parent, SWT.READ_ONLY);
            setActivationStyle(DROP_DOWN_ON_MOUSE_ACTIVATION);
        }

        @Override
        protected Control createControl(Composite parent) {
            final Control control = super.createControl(parent);
            getViewer().getCCombo().addSelectionListener(new SelectionAdapter() {
                @Override
                public void widgetSelected(SelectionEvent e) {
                    focusLost();
                }
            });
            return control;
        }
    }
    /**
     * Listener for click events of the "enabled" column
     * 
     * By clicking on the column all items can be selected and/or deselected at
     * once. The result of the action depends upon {@link #selectAll}.
     */
    private final class ColumnEnabledSelectionListener extends SelectionAdapter {

        /**
         * (Un)checks all of the items at once
         * 
         * This iterates through all of the items and invokes {@link #setChecked(int, Boolean)} for all of them. Furthermore the
         * tooltip is changed appropriately.
         *
         * @param arg0
         */
        @Override
        public void widgetSelected(SelectionEvent arg0) {

            for (int i = 0; i < table.getItems().length; i++) {
                setChecked(i, selectAll);
            }

            selectAll = !selectAll;

            if (selectAll) {
                tblclmnEnabled.setToolTipText(Resources.getMessage("ImportWizardPageColumns.4")); //$NON-NLS-1$
                tblclmnEnabled.setText(Resources.getMessage("ImportWizardPageColumns.5")); //$NON-NLS-1$
            } else {
                tblclmnEnabled.setToolTipText(Resources.getMessage("ImportWizardPageColumns.6")); //$NON-NLS-1$
                tblclmnEnabled.setText(Resources.getMessage("ImportWizardPageColumns.7")); //$NON-NLS-1$
            }
            check();
        }

        /**
         * Applies a boolean value to the given item.
         *
         * @param i Item that <code>check</code> should be applied to
         * @param check Value that should be applied to item <code>i</code>
         */
        private void setChecked(int i, Boolean check) {

            table.getItem(i).setChecked(check);
            wizardImport.getData().getWizardColumns().get(i).setEnabled(check);

            setPageComplete(false);

            for (ImportWizardModelColumn column : wizardImport.getData()
                                                              .getWizardColumns()) {

                if (column.isEnabled()) {
                    setPageComplete(true);
                    return;
                }
            }
        }
    }

    /** Reference to the wizard containing this page. */
    private ImportWizard           wizardImport;
    
    /** View */
    private Table                  table;

    /** View */
    private CheckboxTableViewer    checkboxTableViewer;

    /** View */
    private TableColumn            tblclmnName;

    /** View */
    private TableViewerColumn      tableViewerColumnName;

    /** View */
    private TableColumn            tblclmnDatatype;

    /** View */
    private TableViewerColumn      tableViewerColumnDatatype;

    /** View */
    private TableColumn            tblclmnEnabled;

    /** View */
    private TableViewerColumn      tableViewerColumnEnabled;

    /** View */
    private TableColumn            tblclmnFormat;

    /** View */
    private TableViewerColumn      tableViewerColumnFormat;

    /** View */
    private DatatypeEditingSupport tableViewerColumnDatatypeEditingSupport;

    /** View */
    private Button                 btnUp;

    /** View */
    private Button                 btnDown;

    /** View */
    private Button                 btnCleansing;

    /** Indicator for the next action of {@link ColumnEnabledSelectionListener}. */
    private boolean                selectAll = false;

    /**
     * Creates a new instance of this page and sets its title and description.
     *
     * @param wizardImport Reference to wizard containing this page
     */
    public ImportWizardPageColumns(ImportWizard wizardImport) {

        super("WizardImportCsvPage"); //$NON-NLS-1$

        this.wizardImport = wizardImport;
        
        setTitle(Resources.getMessage("ImportWizardPageColumns.9")); //$NON-NLS-1$
        setDescription(Resources.getMessage("ImportWizardPageColumns.10")); //$NON-NLS-1$

    }
    
    /**
     * Creates the design of this page along with the appropriate listeners.
     *
     * @param parent
     */
    public void createControl(Composite parent) {

        Composite container = new Composite(parent, SWT.NULL);

        setControl(container);
        container.setLayout(new GridLayout(2, false));

        /* TableViewer for the columns with a checkbox in each row */
        checkboxTableViewer = SWTUtil.createTableViewerCheckbox(container, SWT.BORDER | SWT.FULL_SELECTION);
        checkboxTableViewer.setContentProvider(new ArrayContentProvider());
        checkboxTableViewer.setCheckStateProvider(new ICheckStateProvider() {

            /** @return {@link ImportWizardModelColumn#isEnabled()} */
            @Override
            public boolean isChecked(Object column) {
                return ((ImportWizardModelColumn) column).isEnabled();
            }

            /** No column should be grayed out */
            @Override
            public boolean isGrayed(Object column) {
                return false;
            }
        });
        checkboxTableViewer.addCheckStateListener(new ICheckStateListener() {

            /**
             * Sets the enabled status for the given item
             * 
             * Using {@link ImportWizardModelColumn#setEnabled(boolean)} this
             * method will set the enabled flag for the given column.
             * Furthermore it makes sure the page is marked as complete once at
             * least one item is selected.
             */
            @Override
            public void checkStateChanged(CheckStateChangedEvent event) {
                ((ImportWizardModelColumn) event.getElement()).setEnabled(event.getChecked());
                check();
            }
        });

        /* Actual table for {@link #checkboxTableViewer} */
        table = checkboxTableViewer.getTable();
        table.setHeaderVisible(true);
        table.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 2, 1));
        table.addSelectionListener(new SelectionAdapter() {

            /**
             * Makes the buttons for column reordering (un)clickable
             * 
             * This checks the current selection and will enable and/or disable
             * the {@link #btnUp} and {@link #btnDown} if either the first or
             * last item is currently selected.
             */
            @Override
            public void widgetSelected(SelectionEvent e) {

                /* Check for first item */
                if (table.getSelectionIndex() == 0) {
                    btnUp.setEnabled(false);
                } else {
                    btnUp.setEnabled(true);
                }

                /* Check for last item */
                if (table.getSelectionIndex() == table.getItemCount() - 1) {
                    btnDown.setEnabled(false);
                } else {
                    btnDown.setEnabled(true);
                }
            }
        });

        /* Empty column to make checkboxes appear in an own cell */
        tableViewerColumnEnabled = new TableViewerColumn(checkboxTableViewer, SWT.NONE);
        tableViewerColumnEnabled.setLabelProvider(new ColumnLabelProvider() {
            /** Cells within this column should always be empty */
            @Override
            public String getText(Object element) {
                return null;
            }
        });

        /* Actual column for {@link tableViewerColumnEnabled} */
        tblclmnEnabled = tableViewerColumnEnabled.getColumn();
        tblclmnEnabled.setToolTipText(Resources.getMessage("ImportWizardPageColumns.11")); //$NON-NLS-1$
        tblclmnEnabled.setText(Resources.getMessage("ImportWizardPageColumns.12")); //$NON-NLS-1$
        tblclmnEnabled.setWidth(40);
        tblclmnEnabled.addSelectionListener(new ColumnEnabledSelectionListener());

        /* Column containing the names */
        tableViewerColumnName = new TableViewerColumn(checkboxTableViewer, SWT.NONE);
        tableViewerColumnName.setEditingSupport(new NameEditingSupport(checkboxTableViewer));
        tableViewerColumnName.setLabelProvider(new ColumnLabelProvider() {

            /**
             * Gets name of cells from {@link ImportColumn#getAliasName()}
             * 
             * This also makes sure that all column names are unique using
             * {@link #uniqueColumnNames()}. In case there are duplicates it
             * sets an error message.
             */
            @Override
            public String getText(Object element) {
                ImportWizardModelColumn column = (ImportWizardModelColumn) element;
                return column.getColumn().getAliasName();
            }
        });

        /* Actual column for {@link tableViewerColumnName} */
        tblclmnName = tableViewerColumnName.getColumn();
        tblclmnName.setToolTipText(Resources.getMessage("ImportWizardPageColumns.13")); //$NON-NLS-1$
        tblclmnName.setWidth(300);
        tblclmnName.setText(Resources.getMessage("ImportWizardPageColumns.14")); //$NON-NLS-1$

        /* Column containing the datatypes */
        tableViewerColumnDatatype = new TableViewerColumn(checkboxTableViewer, SWT.NONE);
        tableViewerColumnDatatypeEditingSupport = new DatatypeEditingSupport(checkboxTableViewer);
        tableViewerColumnDatatype.setEditingSupport(tableViewerColumnDatatypeEditingSupport);
        tableViewerColumnDatatype.setLabelProvider(new ColumnLabelProvider() {

            /**
             * Gets string representation for given datatype of column
             * 
             * Internally it makes use of {@link ImportColumn#getDataType()}.
             */
            @Override
            public String getText(Object element) {
                ImportWizardModelColumn column = (ImportWizardModelColumn) element;
                DataType<?> datatype = column.getColumn().getDataType();
                return datatype.getDescription().getLabel();
            }
        });

        /* Actual column for {@link tableViewerColumnDatatype} */
        tblclmnDatatype = tableViewerColumnDatatype.getColumn();
        tblclmnDatatype.setToolTipText(Resources.getMessage("ImportWizardPageColumns.15")); //$NON-NLS-1$
        tblclmnDatatype.setWidth(120);
        tblclmnDatatype.setText(Resources.getMessage("ImportWizardPageColumns.16")); //$NON-NLS-1$

        /* Column containing the format of the format */
        tableViewerColumnFormat = new TableViewerColumn(checkboxTableViewer,
                                                        SWT.NONE);
        tableViewerColumnFormat.setLabelProvider(new ColumnLabelProvider() {

            /**
             * Returns format string of datatype for column
             * 
             * This retrieves the used format string of the chosen datatype for
             * each column.
             * 
             * @note In case of simple datatypes without a format specifier an
             *       empty string is returned.
             * 
             * @param element
             *            Column in question
             */
            @Override
            public String getText(Object element) {

                DataType<?> column = ((ImportWizardModelColumn) element).getColumn()
                                                                        .getDataType();
                if (column instanceof DataTypeWithFormat) {
                    return ((DataTypeWithFormat) column).getFormat();
                }
                return ""; //$NON-NLS-1$
            }
        });

        /* Actual column for {@link tableViewerColumnFormat} */
        tblclmnFormat = tableViewerColumnFormat.getColumn();
        tblclmnFormat.setWidth(120);
        tblclmnFormat.setToolTipText(Resources.getMessage("ImportWizardPageColumns.18")); //$NON-NLS-1$
        tblclmnFormat.setWidth(100);
        tblclmnFormat.setText(Resources.getMessage("ImportWizardPageColumns.19")); //$NON-NLS-1$

        /* Buttons to move column up */
        btnUp = new Button(container, SWT.NONE);
        btnUp.setText(Resources.getMessage("ImportWizardPageColumns.20")); //$NON-NLS-1$
        btnUp.setImage(wizardImport.getController()
                                   .getResources()
                                   .getManagedImage("arrow_up.png")); //$NON-NLS-1$
        btnUp.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 1, 1));
        btnUp.setEnabled(false);
        btnUp.addSelectionListener(new SelectionAdapter() {

            /**
             * Swaps the current element with the one above
             * 
             * This makes also sure that the button is disabled once the top is
             * reached by notifying the appropriate selection listener.
             */
            @Override
            public void widgetSelected(SelectionEvent e) {

                int current = table.getSelectionIndex();
                if (current > 0) {
                    List<ImportWizardModelColumn> columns = wizardImport.getData()
                                                                        .getWizardColumns();
                    Collections.swap(columns, current, current - 1);
                    checkboxTableViewer.setInput(columns);
                    table.notifyListeners(SWT.Selection, null);
                }
            }
        });

        /* Buttons to move column down */
        btnDown = new Button(container, SWT.NONE);
        btnDown.setText(Resources.getMessage("ImportWizardPageColumns.22")); //$NON-NLS-1$
        btnDown.setImage(wizardImport.getController()
                                     .getResources()
                                     .getManagedImage("arrow_down.png")); //$NON-NLS-1$
        btnDown.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 1, 1));
        btnDown.setEnabled(false);
        btnDown.addSelectionListener(new SelectionAdapter() {

            /**
             * Swaps the current element with the one below
             * 
             * This makes also sure that the button is disabled once the bottom
             * is reached by notifying the appropriate selection listener.
             */
            @Override
            public void widgetSelected(SelectionEvent e) {

                int current = table.getSelectionIndex();
                if (current < table.getItemCount() - 1) {

                    List<ImportWizardModelColumn> columns = wizardImport.getData()
                                                                        .getWizardColumns();
                    Collections.swap(columns, current, current + 1);
                    checkboxTableViewer.setInput(columns);
                    table.notifyListeners(SWT.Selection, null);
                }
            }
        });
        
        btnCleansing = new Button(container, SWT.CHECK);
        btnCleansing.setLayoutData(GridDataFactory.fillDefaults().grab(true, false).span(2, 1).create());
        btnCleansing.setText(Resources.getMessage("ImportWizardPageColumns.24")); //$NON-NLS-1$
        btnCleansing.setToolTipText(Resources.getMessage("ImportWizardPageColumns.25")); //$NON-NLS-1$
        btnCleansing.setEnabled(true);
        btnCleansing.setSelection(wizardImport.getData().isPerformCleansing());
        btnCleansing.addSelectionListener(new SelectionAdapter() {
            public void widgetSelected(SelectionEvent arg0) {
                wizardImport.getData().setPerformCleansing(btnCleansing.getSelection());
            }
        });

        /* Wait for at least one column to be enabled */
        setPageComplete(false);
    }

    /**
     * Adds input to table viewer once page gets visible.
     *
     * @param visible
     */
    @Override
    public void setVisible(boolean visible) {

        super.setVisible(visible);
        if (visible) {
            
            for (ImportWizardModelColumn column : wizardImport.getData().getWizardColumns()) {
                column.getColumn().setDataType(wizardImport.getData().getMatchingDataTypes(column).iterator().next().getFirst());
            }
            
            tableViewerColumnDatatypeEditingSupport.update(wizardImport.getData()
                                                                       .getWizardColumns());
            
            checkboxTableViewer.setInput(wizardImport.getData()
                                                     .getWizardColumns());
            check();
        }
    }

    /**
     * Checks whether the current selection of columns is suited for import
     */
    private void check(){

        // Check selection
        boolean selected = false;
        for (ImportWizardModelColumn column : wizardImport.getData()
                                                          .getWizardColumns()) {
            selected |= column.isEnabled();
        }
        
        if (!selected) {
            setErrorMessage(Resources.getMessage("ImportWizardPageColumns.26")); //$NON-NLS-1$
            setPageComplete(false);
            return;
        }

        // Check names
        for (ImportWizardModelColumn c1 : wizardImport.getData().getWizardColumns()) {
            if (c1.isEnabled()) {
                String name1 = c1.getColumn().getAliasName();
                for (ImportWizardModelColumn c2 : wizardImport.getData().getWizardColumns()) {
                    if (c2.isEnabled() && c1 != c2 && name1.equals(c2.getColumn().getAliasName())) {
                        setErrorMessage(Resources.getMessage("ImportWizardPageColumns.27") + name1); //$NON-NLS-1$
                        setPageComplete(false);
                        return;
                    }
                }
            }
        }
        
        // Everything is fine
        setErrorMessage(null);
        setPageComplete(true);
    }
}