com.digitalgeneralists.assurance.ui.components.ScanDefinitionPanel.java Source code

Java tutorial

Introduction

Here is the source code for com.digitalgeneralists.assurance.ui.components.ScanDefinitionPanel.java

Source

/*
 * Assurance
 * 
 * Created by Mark Johnson
 * 
 * Copyright (c) 2015 Digital Generalists, LLC.
 * 
 */
/*
 * Copyright 2015 Digital Generalists, LLC.
 *
 * 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 com.digitalgeneralists.assurance.ui.components;

import java.awt.Color;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.Dialog.ModalityType;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;

import javax.swing.BorderFactory;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.border.Border;
import javax.swing.border.EtchedBorder;
import javax.swing.border.TitledBorder;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import com.digitalgeneralists.assurance.IApplicationDelegate;
import com.digitalgeneralists.assurance.model.ModelUtils;
import com.digitalgeneralists.assurance.model.entities.ScanDefinition;
import com.digitalgeneralists.assurance.model.entities.ScanMappingDefinition;
import com.digitalgeneralists.assurance.model.enums.AssuranceMergeStrategy;
import com.digitalgeneralists.assurance.ui.components.dialogs.AssuranceDialogMode;
import com.digitalgeneralists.assurance.ui.components.dialogs.AssuranceDialogResult;
import com.digitalgeneralists.assurance.ui.components.dialogs.IDialogResponseHandler;
import com.digitalgeneralists.assurance.ui.factories.IDialogFactory;

@Component("ScanDefinitionComponent")
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class ScanDefinitionPanel extends AbstractDialogInputPanel
        implements IDialogResponseHandler, IListInputPanelDelegate<ScanMappingDefinition> {
    @Autowired
    private IApplicationDelegate applicationDelegate;

    @Autowired
    private IDialogFactory dialogFactory;

    // NOTE: This is less than ideal.  A new instance of the auto-wired app delegate 
    // spins up whenever I interact with prototype-scoped bean instances. Need to 
    // identify a better solution.
    public void setApplicationDelegate(IApplicationDelegate applicationDelegate) {
        this.applicationDelegate = applicationDelegate;
    }

    private ScanDefinition definition;

    private final JTextField nameTextField = new JTextField();

    private ListInputPanel<ScanMappingDefinition> scanMappingsList = null;

    private JComboBox<String> strategyComboBox;
    private final JCheckBox autoMergeCheckBox = new JCheckBox("Automatically Merge");
    private final JCheckBox includeNonCreationTimestampCheckBox = new JCheckBox(
            "Include Timestamps Other than Create Date");
    private final JCheckBox includeAdvancedAttributesCheckBox = new JCheckBox("Include Advanced File Attributes");

    private static final long serialVersionUID = 1L;

    public ScanDefinitionPanel() {
    }

    public void setDefinition(ScanDefinition definition) {
        this.definition = definition;

        this.initializeComponent();
    }

    @Override
    protected void initializeComponent() {
        if (!this.initialized) {
            if (this.definition == null) {
                mode = AssuranceDialogMode.ADD;
                this.dialogTitle = "Add New Scan Definition";
                this.definition = new ScanDefinition();
            } else {
                mode = AssuranceDialogMode.EDIT;
                this.dialogTitle = "Edit Scan Definition";
            }

            GridBagLayout gridbag = new GridBagLayout();
            this.setLayout(gridbag);

            final JPanel optionsPanel = new JPanel();
            optionsPanel.setLayout(new GridBagLayout());

            Border optionsBorder = BorderFactory.createEtchedBorder(EtchedBorder.LOWERED);
            optionsBorder = BorderFactory.createTitledBorder(optionsBorder, "Merge Options", TitledBorder.CENTER,
                    TitledBorder.TOP);

            GridBagConstraints nameTextFieldConstraints = new GridBagConstraints();
            nameTextFieldConstraints.anchor = GridBagConstraints.NORTH;
            nameTextFieldConstraints.fill = GridBagConstraints.HORIZONTAL;
            nameTextFieldConstraints.gridx = 0;
            nameTextFieldConstraints.gridy = 0;
            nameTextFieldConstraints.weightx = 1.0;
            nameTextFieldConstraints.weighty = 1.0;
            nameTextFieldConstraints.gridheight = 1;
            nameTextFieldConstraints.gridwidth = 2;
            nameTextFieldConstraints.insets = new Insets(10, 5, 0, 5);

            this.nameTextField.setText(this.definition.getName());
            this.nameTextField.getDocument().addDocumentListener(this.textPropertyValidationListener);
            this.add(this.nameTextField, nameTextFieldConstraints);

            Border existingScanMappingsPanelBorder = BorderFactory.createEtchedBorder(EtchedBorder.LOWERED);
            existingScanMappingsPanelBorder = BorderFactory.createTitledBorder(existingScanMappingsPanelBorder,
                    "Paths", TitledBorder.CENTER, TitledBorder.TOP);

            GridBagConstraints existingScanMappingsPanelConstraints = new GridBagConstraints();
            existingScanMappingsPanelConstraints.anchor = GridBagConstraints.WEST;
            existingScanMappingsPanelConstraints.fill = GridBagConstraints.BOTH;
            existingScanMappingsPanelConstraints.gridx = 0;
            existingScanMappingsPanelConstraints.gridy = 1;
            existingScanMappingsPanelConstraints.weightx = 1.0;
            existingScanMappingsPanelConstraints.weighty = 1.0;
            existingScanMappingsPanelConstraints.gridheight = 1;
            existingScanMappingsPanelConstraints.gridwidth = 2;
            existingScanMappingsPanelConstraints.insets = new Insets(5, 5, 0, 5);

            JPanel existingScanMappingsPanel = new JPanel();
            GridBagLayout panelGridbag = new GridBagLayout();
            existingScanMappingsPanel.setLayout(panelGridbag);
            existingScanMappingsPanel.setBorder(existingScanMappingsPanelBorder);
            this.add(existingScanMappingsPanel, existingScanMappingsPanelConstraints);

            GridBagConstraints existingScanMappingsListConstraints = new GridBagConstraints();
            existingScanMappingsListConstraints.anchor = GridBagConstraints.WEST;
            existingScanMappingsListConstraints.fill = GridBagConstraints.BOTH;
            existingScanMappingsListConstraints.gridx = 0;
            existingScanMappingsListConstraints.gridy = 0;
            existingScanMappingsListConstraints.weightx = 1.0;
            existingScanMappingsListConstraints.weighty = 0.9;
            existingScanMappingsListConstraints.gridheight = 1;
            existingScanMappingsListConstraints.gridwidth = 2;
            existingScanMappingsListConstraints.insets = new Insets(5, 5, 5, 5);

            this.definition = (ScanDefinition) ModelUtils.initializeEntity(this.definition,
                    ScanDefinition.SCAN_MAPPING_PROPERTY);
            this.scanMappingsList = new ListInputPanel<ScanMappingDefinition>(this.definition, this);
            existingScanMappingsPanel.add(this.scanMappingsList, existingScanMappingsListConstraints);

            GridBagConstraints optionsPanelConstraints = new GridBagConstraints();
            optionsPanelConstraints.anchor = GridBagConstraints.SOUTH;
            optionsPanelConstraints.fill = GridBagConstraints.HORIZONTAL;
            optionsPanelConstraints.gridx = 0;
            optionsPanelConstraints.gridy = 3;
            optionsPanelConstraints.weightx = 1.0;
            optionsPanelConstraints.weighty = 1.0;
            optionsPanelConstraints.gridheight = 1;
            optionsPanelConstraints.gridwidth = 2;
            optionsPanelConstraints.insets = new Insets(5, 5, 5, 5);

            optionsPanel.setBorder(optionsBorder);
            this.add(optionsPanel, optionsPanelConstraints);

            GridBagConstraints strategyLabelConstraints = new GridBagConstraints();
            strategyLabelConstraints.anchor = GridBagConstraints.WEST;
            strategyLabelConstraints.fill = GridBagConstraints.BOTH;
            strategyLabelConstraints.gridx = 0;
            strategyLabelConstraints.gridy = 0;
            strategyLabelConstraints.weightx = 1.0;
            strategyLabelConstraints.weighty = 1.0;
            strategyLabelConstraints.gridheight = 1;
            strategyLabelConstraints.gridwidth = 1;
            strategyLabelConstraints.insets = new Insets(5, 5, 0, 5);

            final JLabel strategyLabel = new JLabel("Strategy", SwingConstants.RIGHT);
            optionsPanel.add(strategyLabel, strategyLabelConstraints);

            GridBagConstraints strategyComboBoxConstraints = new GridBagConstraints();
            strategyComboBoxConstraints.anchor = GridBagConstraints.WEST;
            strategyComboBoxConstraints.fill = GridBagConstraints.VERTICAL;
            strategyComboBoxConstraints.gridx = 1;
            strategyComboBoxConstraints.gridy = 0;
            strategyComboBoxConstraints.weightx = 1.0;
            strategyComboBoxConstraints.weighty = 1.0;
            strategyComboBoxConstraints.gridheight = 1;
            strategyComboBoxConstraints.gridwidth = 1;
            strategyComboBoxConstraints.insets = new Insets(5, 5, 0, 5);

            String[] strategyLabels = { "Source", "Target", "Both" };
            this.strategyComboBox = new JComboBox<String>(strategyLabels);
            // NOTE: We should have better validation of the data state for these controls.
            // We could run into problems as the application versions over time.
            this.strategyComboBox.setSelectedIndex(this.definition.getMergeStrategy().ordinal());
            this.strategyComboBox.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent e) {
                    validateFormState();
                }
            });
            optionsPanel.add(this.strategyComboBox, strategyComboBoxConstraints);

            GridBagConstraints autoMergeCheckBoxConstraints = new GridBagConstraints();
            autoMergeCheckBoxConstraints.gridx = 0;
            autoMergeCheckBoxConstraints.gridy = 1;
            autoMergeCheckBoxConstraints.weightx = 1.0;
            autoMergeCheckBoxConstraints.weighty = 1.0;
            autoMergeCheckBoxConstraints.gridheight = 1;
            autoMergeCheckBoxConstraints.gridwidth = 2;
            autoMergeCheckBoxConstraints.insets = new Insets(2, 5, 5, 5);

            this.autoMergeCheckBox.setHorizontalTextPosition(SwingConstants.LEFT);
            this.autoMergeCheckBox.setSelected(this.definition.getAutoResolveConflicts());
            this.autoMergeCheckBox.addItemListener(new ItemListener() {
                public void itemStateChanged(ItemEvent e) {
                    validateFormState();
                }
            });
            optionsPanel.add(this.autoMergeCheckBox, autoMergeCheckBoxConstraints);

            GridBagConstraints includeNonCreationTimestampsCheckBoxConstraints = new GridBagConstraints();
            includeNonCreationTimestampsCheckBoxConstraints.gridx = 0;
            includeNonCreationTimestampsCheckBoxConstraints.gridy = 2;
            includeNonCreationTimestampsCheckBoxConstraints.weightx = 1.0;
            includeNonCreationTimestampsCheckBoxConstraints.weighty = 1.0;
            includeNonCreationTimestampsCheckBoxConstraints.gridheight = 1;
            includeNonCreationTimestampsCheckBoxConstraints.gridwidth = 2;
            includeNonCreationTimestampsCheckBoxConstraints.insets = new Insets(2, 5, 5, 5);

            this.includeNonCreationTimestampCheckBox.setHorizontalTextPosition(SwingConstants.LEFT);
            this.includeNonCreationTimestampCheckBox.setSelected(this.definition.getIncludeNonCreationTimestamps());
            this.includeNonCreationTimestampCheckBox.addItemListener(new ItemListener() {
                public void itemStateChanged(ItemEvent e) {
                    validateFormState();
                }
            });
            optionsPanel.add(this.includeNonCreationTimestampCheckBox,
                    includeNonCreationTimestampsCheckBoxConstraints);

            GridBagConstraints advancedAttributesCheckBoxConstraints = new GridBagConstraints();
            advancedAttributesCheckBoxConstraints.gridx = 0;
            advancedAttributesCheckBoxConstraints.gridy = 3;
            advancedAttributesCheckBoxConstraints.weightx = 1.0;
            advancedAttributesCheckBoxConstraints.weighty = 1.0;
            advancedAttributesCheckBoxConstraints.gridheight = 1;
            advancedAttributesCheckBoxConstraints.gridwidth = 2;
            advancedAttributesCheckBoxConstraints.insets = new Insets(2, 5, 5, 5);

            this.includeAdvancedAttributesCheckBox.setHorizontalTextPosition(SwingConstants.LEFT);
            this.includeAdvancedAttributesCheckBox.setSelected(this.definition.getAutoResolveConflicts());
            this.includeAdvancedAttributesCheckBox.addItemListener(new ItemListener() {
                public void itemStateChanged(ItemEvent e) {
                    validateFormState();
                }
            });
            optionsPanel.add(this.includeAdvancedAttributesCheckBox, advancedAttributesCheckBoxConstraints);

            this.scanMappingsList.loadData();

            if (this.getMode() == AssuranceDialogMode.EDIT) {
                this.validateFormState();
            }

            this.initialized = true;
        }
    }

    public void actionPerformed(ActionEvent e) {
        this.scanMappingsList.actionPerformed(e);
    }

    private void displayMappingDefinitionDialog(ScanMappingDefinition selectedItem) {
        Window parent = SwingUtilities.getWindowAncestor(this.getParent());
        JDialog scanMappingDefinitionDialog = this.dialogFactory.createScanMappingDefinitionDialogInstance(parent,
                ModalityType.APPLICATION_MODAL, this, selectedItem);
        scanMappingDefinitionDialog.setVisible(true);
    }

    private void deleteScanMappingDefinition(ScanMappingDefinition selectedItem) {
        if (selectedItem != null) {
            // NOTE:  Leaking model initialization like this into the UI is less than ideal.
            this.definition = (ScanDefinition) ModelUtils.initializeEntity(this.definition,
                    ScanDefinition.SCAN_MAPPING_PROPERTY);
            if (this.definition.getUnmodifiableScanMapping().size() > 0) {
                this.definition.removeMappingDefinition(selectedItem);
            }

            this.scanMappingsList.loadData();
        }
    }

    @Override
    protected AssuranceDialogResult processImplementationInputOnConfirm() {
        if (!this.validateFormState()) {
            return AssuranceDialogResult.VALIDATION_FAILED;
        }

        this.definition.setName(this.nameTextField.getText());
        // NOTE: We should have better validation of the data state for these controls.
        // We could run into problems as the application versions over time.
        this.definition.setMergeStrategy(AssuranceMergeStrategy.values()[strategyComboBox.getSelectedIndex()]);
        this.definition.setAutoResolveConflicts(this.autoMergeCheckBox.isSelected());
        this.definition.setIncludeNonCreationTimestamps(this.includeNonCreationTimestampCheckBox.isSelected());
        this.definition.setIncludeAdvancedAttributes(this.includeAdvancedAttributesCheckBox.isSelected());

        this.applicationDelegate.saveScanDefinition(this.definition);

        return AssuranceDialogResult.CONFIRM;
    }

    public boolean validateFormState() {
        Color controlInErrorBackgroundColor = Color.red;
        Color defaultControlBackgroundColor = Color.white;

        boolean result = true;

        if (StringUtils.isEmpty(this.nameTextField.getText())) {
            this.nameTextField.setBackground(controlInErrorBackgroundColor);
            result = false;
        } else {
            this.nameTextField.setBackground(defaultControlBackgroundColor);
        }

        if (!this.scanMappingsList.validateFormState()) {
            result = false;
        }

        return result;
    }

    public Object getResultObject() {
        return this.definition;
    }

    public void dialogClosed(AssuranceDialogResult result, Object resultObject) {
        if (result == AssuranceDialogResult.CONFIRM) {
            if ((resultObject != null) && (resultObject instanceof ScanMappingDefinition)) {
                boolean bypass = false;
                for (ScanMappingDefinition mapping : this.definition.getUnmodifiableScanMapping()) {
                    if ((mapping.getId() != null)
                            && (mapping.getId().equals(((ScanMappingDefinition) resultObject).getId()))) {
                        // NOTE:  I worry I'm bypassing the point of Hibernate here.
                        // Only need this because the resultObject ends up as a dis-associated
                        // instance from the definition through the child property initialization
                        // process when values are loaded to the UI.  Should identify a way
                        // to keep those in sync better.  This is just a bridge patch to get
                        // over that challenge.
                        mapping.mergeMappingDefinition((ScanMappingDefinition) resultObject);
                        bypass = true;
                        break;
                    }
                }

                if (bypass != true) {
                    if (!this.definition.getUnmodifiableScanMapping().contains(resultObject)) {
                        this.definition.addMappingDefinition((ScanMappingDefinition) resultObject);
                    }
                }
            }

            this.scanMappingsList.loadData();

            this.validateFormState();
        }
    }

    public void handlePrimaryButtonClick() {
        this.handlePrimaryButtonClick(null);
    }

    public void handlePrimaryButtonClick(ScanMappingDefinition item) {
        this.displayMappingDefinitionDialog(item);
    }

    public void handleSecondaryButtonClick() {
        this.handleSecondaryButtonClick(null);
    }

    public void handleSecondaryButtonClick(ScanMappingDefinition item) {
        this.deleteScanMappingDefinition(item);
        ;
    }

    public boolean listRequiresRecord() {
        return true;
    }

    public String getPrimaryButtonAction() {
        return AssuranceActions.newScanMappingDefinitonAction;
    }

    public String getPrimaryButtonLabel() {
        return "New";
    }

    public String getSecondaryButtonAction() {
        return AssuranceActions.deleteScanMappingDefinitonAction;
    }

    public String getSecondaryButtonLabel() {
        return "Delete";
    }

    public void listValueChanged(boolean itemIsSelected) {
    }
}