Search in sources :

Example 1 with InputDataType

use of CCDD.CcddConstants.InputDataType in project CCDD by nasa.

the class CcddKeyboardHandler method setKeyboardHandler.

/**
 ********************************************************************************************
 * Adjust the handling of Enter and space key inputs in order to activate dialog controls, and
 * keyboard focus changes in order to use the arrow keys like Tab keys
 ********************************************************************************************
 */
private void setKeyboardHandler() {
    // Get the keyboard focus manager
    focusManager = KeyboardFocusManager.getCurrentKeyboardFocusManager();
    // Listen for key presses
    focusManager.addKeyEventDispatcher(new KeyEventDispatcher() {

        boolean isShowMacros = false;

        Timer releaseTimer = null;

        /**
         ************************************************************************************
         * Alter the response to the Enter key to act like the Space key to activate button and
         * check box controls, and the arrow keys so as to mimic the Tab and Shift+Tab keys,
         * unless the key press originates within a table or combo box. For a tabbed pane the
         * left/right arrows are left unchanged so that these are used for traversing the
         * tabbed panes, but the down and up arrows act like Tab and Shift+Tab respectively
         ************************************************************************************
         */
        @Override
        public boolean dispatchKeyEvent(final KeyEvent ke) {
            // Flag that indicates if the key event has been handled by this method (true) or
            // that it still needs to be processed (false)
            boolean handled = false;
            // Get a reference to the component with the focus in order to shorten the
            // subsequent calls
            Component comp = ke.getComponent();
            // pressed
            if (ke.getID() == KeyEvent.KEY_PRESSED && !ke.isControlDown() && !ke.isShiftDown() && !ke.isAltDown()) {
                // Check if the Enter key is pressed
                if (ke.getKeyCode() == KeyEvent.VK_ENTER) {
                    // Check if this is a button
                    if (comp instanceof JButton) {
                        // Activate the control (same as if Space key is pressed)
                        ((AbstractButton) comp).doClick();
                        handled = true;
                    } else // Check if this is a table
                    if (comp instanceof CcddJTableHandler || (comp.getParent() instanceof CcddJTableHandler && !(comp instanceof JComboBox))) {
                        // Handle the Enter key in the table
                        handled = tableEditCellHandler(comp);
                    }
                } else // Check if the space key is pressed
                if (ke.getKeyCode() == KeyEvent.VK_SPACE) {
                    // Check if this is a table
                    if (comp instanceof CcddJTableHandler) {
                        // Handle the space key in the table
                        handled = tableEditCellHandler(comp);
                    } else // Check this is a combo box in a table
                    if (comp.getParent() instanceof CcddJTableHandler && comp instanceof JComboBox) {
                        // Ignore the key press
                        handled = true;
                    }
                } else // produce a character and is not a modifier; this covers the arrow keys)
                if (ke.isActionKey()) {
                    // Assume that the default handling will be used with an arrow key
                    ArrowFocusOption arrowResponse = USE_DEFAULT_HANDLER;
                    // Check if the focus is on a tabbed pane's tab or on a slider
                    if (comp instanceof JTabbedPane || comp instanceof JSlider) {
                        // The left and right arrows traverse the tabs, and the up and down
                        // arrows behave like (Shift+)Tab
                        arrowResponse = HANDLE_UP_AND_DOWN_ARROWS;
                    } else // Check if the focus is in a text field within a table
                    if ((comp instanceof JTextField || comp instanceof JTextArea) && comp.getParent() instanceof CcddJTableHandler) {
                        // The up and down arrows are ignored. This prevents accidently exiting
                        // edit mode on a table cell and losing any changes
                        arrowResponse = IGNORE_UP_AND_DOWN_ARROWS;
                    } else // button, or check box
                    if (comp instanceof JButton || comp instanceof JRadioButton || comp instanceof JCheckBox) {
                        // The up and left arrow keys behave as Shift+Tab, and the down and
                        // right arrow keys behave as Tab
                        arrowResponse = HANDLE_ALL_ARROWS;
                    } else // Check if the focus is within a table
                    if (comp instanceof CcddJTableHandler) {
                        // Get the reference to the table with the focus
                        CcddJTableHandler table = (CcddJTableHandler) comp;
                        // Get the row containing the cell with the focus in order to shorten
                        // the subsequent calls
                        int row = table.getSelectedRow();
                        // Check if the table has no rows
                        if (row == -1) {
                            // Treat the table as if it wasn't there; i.e., the left and right
                            // arrows behave like the (Shift+)Tab key
                            arrowResponse = HANDLE_ALL_ARROWS;
                        } else // Check if the table has only a single row
                        if (table.getRowCount() == 1) {
                            // The up and down arrows behave like the (Shift+)Tab key
                            arrowResponse = HANDLE_UP_AND_DOWN_ARROWS;
                        } else // Check if the top row is selected
                        if (row == 0) {
                            // The up arrow behaves like the Shift+Tab key
                            arrowResponse = HANDLE_UP_ARROW;
                        } else // Check if the bottom row is selected
                        if (row == table.getRowCount() - 1) {
                            // The down arrow behaves like the Tab key
                            arrowResponse = HANDLE_DOWN_ARROW;
                        }
                    }
                    // Check if the key pressed is an arrow key and if so adjust its behavior
                    switch(ke.getKeyCode()) {
                        case KeyEvent.VK_LEFT:
                        case KeyEvent.VK_KP_LEFT:
                            // Check if the left arrow key should be handled
                            if (arrowResponse == HANDLE_ALL_ARROWS) {
                                // Treat the left arrow as a Shift+Tab key and indicate that
                                // the key has been handled
                                focusManager.focusPreviousComponent();
                                handled = true;
                            }
                            break;
                        case KeyEvent.VK_UP:
                        case KeyEvent.VK_KP_UP:
                            // Check if the up arrow key should be handled
                            if (arrowResponse == HANDLE_ALL_ARROWS || arrowResponse == HANDLE_UP_ARROW || arrowResponse == HANDLE_UP_AND_DOWN_ARROWS) {
                                // Treat the up arrow as a Shift+Tab key and indicate that the
                                // key has been handled
                                focusManager.focusPreviousComponent();
                                handled = true;
                            } else // Check if the up arrow should be ignored
                            if (arrowResponse == IGNORE_UP_AND_DOWN_ARROWS) {
                                // Indicate that the key has been handled
                                handled = true;
                            }
                            break;
                        case KeyEvent.VK_RIGHT:
                        case KeyEvent.VK_KP_RIGHT:
                            // Check if the right arrow key should be handled
                            if (arrowResponse == HANDLE_ALL_ARROWS) {
                                // Treat the right arrow as a Tab key and indicate that the key
                                // has been handled
                                focusManager.focusNextComponent();
                                handled = true;
                            }
                            break;
                        case KeyEvent.VK_DOWN:
                        case KeyEvent.VK_KP_DOWN:
                            // Check if the down arrow key should be handled
                            if (arrowResponse == HANDLE_ALL_ARROWS || arrowResponse == HANDLE_DOWN_ARROW || arrowResponse == HANDLE_UP_AND_DOWN_ARROWS) {
                                // Treat the down arrow as a Tab key and indicate that the key
                                // has been handled
                                focusManager.focusNextComponent();
                                handled = true;
                            } else // Check if the down arrow should be ignored
                            if (arrowResponse == IGNORE_UP_AND_DOWN_ARROWS) {
                                // Indicate that the key has been handled
                                handled = true;
                            }
                            break;
                    }
                }
            } else // Check if the Ctrl key is pressed
            if (ke.getID() == KeyEvent.KEY_PRESSED && ke.isControlDown() && !ke.isAltDown()) {
                // Check if the Ctrl-Z key is pressed
                if (ke.getKeyCode() == KeyEvent.VK_Z) {
                    // Get the currently active undo manager
                    final CcddUndoManager undoManager = getActiveUndoManager();
                    // Check if an active undo manager was found
                    if (undoManager != null) {
                        // Create a runnable object to be executed
                        SwingUtilities.invokeLater(new Runnable() {

                            /**
                             ****************************************************************
                             * Execute after all pending Swing events are finished so that the
                             * events occur in the desired order
                             ****************************************************************
                             */
                            @Override
                            public void run() {
                                // Undo the previous edit action
                                undoManager.undo();
                                // associated with the component
                                if (editPnlHandler != null) {
                                    // Update the data field background colors
                                    editPnlHandler.setFieldBackgound();
                                }
                                // Force the component to repaint so that the change is visible
                                ke.getComponent().repaint();
                            }
                        });
                        // Set the flag to indicate this key press was handled
                        handled = true;
                    }
                } else // Check if the Ctrl-Y key is pressed
                if (ke.getKeyCode() == KeyEvent.VK_Y) {
                    // Get the currently active undo manager
                    final CcddUndoManager undoManager = getActiveUndoManager();
                    // Check if an active undo manager was found
                    if (undoManager != null) {
                        // Create a runnable object to be executed
                        SwingUtilities.invokeLater(new Runnable() {

                            /**
                             ****************************************************************
                             * Execute after all pending Swing events are finished so that the
                             * events occur in the desired order
                             ****************************************************************
                             */
                            @Override
                            public void run() {
                                // Redo the previous undo action
                                undoManager.redo();
                                // associated with the component
                                if (editPnlHandler != null) {
                                    // Update the data field background colors
                                    editPnlHandler.setFieldBackgound();
                                }
                                // Force the component to repaint so that the change is visible
                                ke.getComponent().repaint();
                            }
                        });
                        // Set the flag to indicate this key press was handled
                        handled = true;
                    }
                } else // focus
                if (ke.getKeyCode() == KeyEvent.VK_F && ccddMain.getMainFrame().isFocused()) {
                    // Open the event log search dialog
                    ccddMain.showSearchDialog(SearchDialogType.LOG, null, ccddMain.getSessionEventLog(), ccddMain.getMainFrame());
                    // Set the flag to indicate this key press was handled
                    handled = true;
                } else // insertion
                if (ke.getKeyCode() == KeyEvent.VK_S) {
                    // Get the table editor dialog with the focus
                    CcddTableEditorDialog editorDialog = getFocusedTableEditorDialog();
                    // contain a combo box
                    if (editorDialog != null && !(comp instanceof JComboBox)) {
                        // Check if a cell in the table is being edited
                        if (editorDialog.getTableEditor().getTable().isEditing()) {
                            // Insert the structure name chosen by the user into the text field
                            // at the current text insertion point
                            dataTypeHandler.insertDataTypeName((JFrame) editorDialog, (JTextArea) comp, true, editorDialog.getTableEditor().getValidDataTypes().toArray(new String[0]));
                        }
                    } else // Check if the data type editor editing is active
                    if (SwingUtilities.getWindowAncestor(comp) instanceof CcddDataTypeEditorDialog && modalTable.isEditing()) {
                        // Get the row and column being edited in the table, and the contents
                        // of the edited row's base data type
                        int row = modalTable.getEditingRow();
                        int column = modalTable.convertColumnIndexToModel(modalTable.getEditingColumn());
                        String baseType = modalTable.getValueAt(row, DataTypeEditorColumnInfo.BASE_TYPE.ordinal()).toString();
                        // base data type is empty or a pointer
                        if ((column == DataTypeEditorColumnInfo.USER_NAME.ordinal() || column == DataTypeEditorColumnInfo.C_NAME.ordinal()) && (baseType.isEmpty() || baseType.equals(BaseDataTypeInfo.POINTER.getName()))) {
                            // Insert the structure name chosen by the user into the text field
                            // at the current text insertion point
                            dataTypeHandler.insertDataTypeName((JDialog) SwingUtilities.getWindowAncestor(comp), (JTextArea) comp, false, null);
                        }
                    } else // Check if the macro editor editing is active
                    if (SwingUtilities.getWindowAncestor(comp) instanceof CcddMacroEditorDialog && modalTable.isEditing()) {
                        // Insert the structure name chosen by the user into the text field at
                        // the current text insertion point
                        dataTypeHandler.insertDataTypeName((JDialog) SwingUtilities.getWindowAncestor(comp), (JTextArea) comp, true, null);
                    }
                    // Set the flag to indicate this key press was handled
                    handled = true;
                } else // and expansion
                if (ke.getKeyCode() == KeyEvent.VK_M) {
                    // Check if the shift key is also pressed (Ctrl-Shift-M)
                    if (ke.isShiftDown()) {
                        // Check if the macros aren't already expanded
                        if (!isShowMacros) {
                            // Get the table editor dialog with the focus
                            CcddTableEditorDialog editorDialog = getFocusedTableEditorDialog();
                            // Check if a table editor dialog has the focus
                            if (editorDialog != null) {
                                // Replace the macro names with their corresponding values in
                                // the currently selected table in this editor
                                editorDialog.getTableEditor().expandMacros(true, true);
                                isShowMacros = true;
                            } else // Check if this is the macro editor
                            if (SwingUtilities.getWindowAncestor(comp) instanceof CcddMacroEditorDialog) {
                                // Expand the macros in the macro value column
                                ((CcddMacroEditorDialog) SwingUtilities.getWindowAncestor(comp)).expandMacros(true);
                                isShowMacros = true;
                            }
                        }
                    } else // The shift key isn't pressed (Ctrl-M only). Check if this is a table cell
                    if (comp.getParent() instanceof CcddJTableHandler) {
                        // Get the table editor dialog with the focus
                        CcddTableEditorDialog editorDialog = getFocusedTableEditorDialog();
                        // doesn't contain a combo box
                        if (editorDialog != null && !(comp instanceof JComboBox)) {
                            // Get references to shorten subsequent calls
                            CcddTableEditorHandler editor = editorDialog.getTableEditor();
                            CcddJTableHandler table = editor.getTable();
                            // Check if a cell in the table is being edited
                            if (table.isEditing()) {
                                // Get the index of the column being edited in model
                                // coordinates
                                int column = table.convertColumnIndexToModel(table.getEditingColumn());
                                // Get the input type for the column being edited
                                InputDataType inputType = editor.getTableTypeDefinition().getInputTypes()[column];
                                // Insert the macro name chosen by the user into the text
                                // component at the current text insertion point
                                macroHandler.insertMacroName(editorDialog, (JTextComponent) comp, inputType, editor.getValidDataTypes());
                            }
                        } else // column is being edited
                        if (SwingUtilities.getWindowAncestor(comp) instanceof CcddMacroEditorDialog && modalTable.isEditing() && modalTable.convertColumnIndexToModel(modalTable.getEditingColumn()) == MacroEditorColumnInfo.VALUE.ordinal()) {
                            // Insert the macro name chosen by the user into the text component
                            // at the current text insertion point
                            macroHandler.insertMacroName((JDialog) SwingUtilities.getWindowAncestor(comp), (JTextComponent) comp, InputDataType.TEXT, null);
                        }
                    }
                    // Set the flag to indicate this key press was handled
                    handled = true;
                } else // Check if the Ctrl-E key is pressed while the focus is on a tree
                if (ke.getKeyCode() == KeyEvent.VK_E && comp instanceof CcddCommonTreeHandler) {
                    // Expand/collapse the selected node(s)
                    ((CcddCommonTreeHandler) comp).expandCollapseSelectedNodes();
                }
            } else // text
            if (ke.getID() == KeyEvent.KEY_PRESSED && ke.isAltDown() && !ke.isControlDown() && ke.getKeyCode() == KeyEvent.VK_ENTER && comp.getParent() instanceof CcddJTableHandler && comp instanceof JTextArea) {
                // Get the table editor dialog with the focus
                CcddTableEditorDialog editorDialog = getFocusedTableEditorDialog();
                // Check if a table editor dialog has the focus
                if (editorDialog != null) {
                    // Check if a cell in the table is being edited
                    if (editorDialog.getTableEditor().getTable().isEditing()) {
                        JTextComponent textComp = (JTextComponent) comp;
                        // Get the cell's current value
                        String cellValue = textComp.getText();
                        // Get the starting position of the selected text
                        int caretPosn = textComp.getSelectionStart();
                        // Replace the currently selected text with a line feed
                        textComp.setText(cellValue.substring(0, caretPosn) + "\n" + cellValue.substring(textComp.getSelectionEnd()));
                        // Position the cursor after the newly inserted line feed
                        textComp.setCaretPosition(caretPosn + 1);
                    }
                }
            }
            // key sequence is no longer active
            if (isShowMacros && ke.getID() == KeyEvent.KEY_RELEASED && ke.getKeyCode() == KeyEvent.VK_M) {
                // Check if the key release action timer doesn't exist
                if (releaseTimer == null) {
                    // Create the key release action timer. In Linux if a key is held it
                    // generates continuous key release events. This timer is used to ignore
                    // the key release events that are close together time-wise
                    releaseTimer = new Timer(75, new ActionListener() {

                        /**
                         ********************************************************************
                         * Handle the key release action
                         ********************************************************************
                         */
                        @Override
                        public void actionPerformed(ActionEvent ae) {
                            // Get the table editor dialog with the focus
                            CcddTableEditorDialog editorDialog = getFocusedTableEditorDialog();
                            // Check if a table editor dialog has the focus
                            if (editorDialog != null) {
                                // Restore the macro names in the currently selected table in
                                // this editor
                                editorDialog.getTableEditor().expandMacros(false, true);
                                isShowMacros = false;
                            } else // Check if this is the macro editor
                            if (SwingUtilities.getWindowAncestor(modalTable) instanceof CcddMacroEditorDialog) {
                                // Expand the macros in the macro value column
                                ((CcddMacroEditorDialog) SwingUtilities.getWindowAncestor(modalTable)).expandMacros(false);
                                isShowMacros = false;
                            }
                        }
                    });
                    // Allow the timer to send only a single expiration event
                    releaseTimer.setRepeats(false);
                }
                // (Re)start the key release action timer
                releaseTimer.restart();
                // Set the flag to indicate this key press was handled
                handled = true;
            }
            return handled;
        }
    });
}
Also used : JTextArea(javax.swing.JTextArea) JRadioButton(javax.swing.JRadioButton) ActionEvent(java.awt.event.ActionEvent) ArrowFocusOption(CCDD.CcddConstants.ArrowFocusOption) JTabbedPane(javax.swing.JTabbedPane) JButton(javax.swing.JButton) JTextComponent(javax.swing.text.JTextComponent) JTextField(javax.swing.JTextField) KeyEvent(java.awt.event.KeyEvent) JFrame(javax.swing.JFrame) JSlider(javax.swing.JSlider) Component(java.awt.Component) JTextComponent(javax.swing.text.JTextComponent) InputDataType(CCDD.CcddConstants.InputDataType) JComboBox(javax.swing.JComboBox) KeyEventDispatcher(java.awt.KeyEventDispatcher) JCheckBox(javax.swing.JCheckBox) Timer(javax.swing.Timer) ActionListener(java.awt.event.ActionListener) JDialog(javax.swing.JDialog)

Example 2 with InputDataType

use of CCDD.CcddConstants.InputDataType in project CCDD by nasa.

the class CcddMacroEditorDialog method createMacroTable.

/**
 ********************************************************************************************
 * Create the macro table
 *
 * @return Reference to the scroll pane in which the table is placed
 ********************************************************************************************
 */
private JScrollPane createMacroTable() {
    // Define the macro editor JTable
    macroTable = new CcddJTableHandler() {

        /**
         ************************************************************************************
         * Highlight any macros in the macro values column
         *
         * @param component
         *            reference to the table cell renderer component
         *
         * @param text
         *            cell text
         *
         * @param isSelected
         *            true if the cell is to be rendered with the selection highlighted
         *
         * @param int
         *            row cell row, view coordinates
         *
         * @param column
         *            cell column, view coordinates
         ************************************************************************************
         */
        @Override
        protected void doSpecialRendering(Component component, String text, boolean isSelected, int row, int column) {
            // Check if this is the macro values column
            if (column == MacroEditorColumnInfo.VALUE.ordinal()) {
                // Highlight any macro names in the table cell. Adjust the highlight color to
                // account for the cell selection highlighting so that the macro is easily
                // readable
                macroHandler.highlightMacro(component, text, isSelected ? ModifiableColorInfo.INPUT_TEXT.getColor() : ModifiableColorInfo.TEXT_HIGHLIGHT.getColor());
                // Highlight 'sizeof(data type)' instances
                CcddDataTypeHandler.highlightSizeof(component, text, isSelected ? ModifiableColorInfo.INPUT_TEXT.getColor() : ModifiableColorInfo.TEXT_HIGHLIGHT.getColor());
            }
        }

        /**
         ************************************************************************************
         * Get the tool tip text for a table cell, showing any macro name replaced with its
         * corresponding macro value
         ************************************************************************************
         */
        @Override
        public String getToolTipText(MouseEvent me) {
            String toolTipText = null;
            // Get the row and column of the cell over which the mouse pointer is hovering
            Point point = me.getPoint();
            int row = rowAtPoint(point);
            int column = columnAtPoint(point);
            // Check if a cell is beneath the mouse pointer
            if (row != -1 && column != -1) {
                // Expand any macros in the cell text and display this as the cell's tool tip
                // text
                toolTipText = macroHandler.getMacroToolTipText(getValueAt(row, column).toString());
            }
            return toolTipText;
        }

        /**
         ************************************************************************************
         * Allow multiple line display in all columns
         ************************************************************************************
         */
        @Override
        protected boolean isColumnMultiLine(int column) {
            return true;
        }

        /**
         ************************************************************************************
         * Hide the the specified columns
         ************************************************************************************
         */
        @Override
        protected boolean isColumnHidden(int column) {
            return column == MacroEditorColumnInfo.OID.ordinal();
        }

        /**
         ************************************************************************************
         * Override isCellEditable so that all columns can be edited
         ************************************************************************************
         */
        @Override
        public boolean isCellEditable(int row, int column) {
            return true;
        }

        /**
         ************************************************************************************
         * Allow pasting data into the macro cells
         ************************************************************************************
         */
        @Override
        protected boolean isDataAlterable(Object[] rowData, int row, int column) {
            return isCellEditable(convertRowIndexToView(row), convertColumnIndexToView(column));
        }

        /**
         ************************************************************************************
         * Validate changes to the editable cells
         *
         * @param tableData
         *            list containing the table data row arrays
         *
         * @param row
         *            table model row number
         *
         * @param column
         *            table model column number
         *
         * @param oldValue
         *            original cell contents
         *
         * @param newValue
         *            new cell contents
         *
         * @param showMessage
         *            true to display the invalid input dialog, if applicable
         *
         * @param isMultiple
         *            true if this is one of multiple cells to be entered and checked; false if
         *            only a single input is being entered
         *
         * @return Always returns false
         ************************************************************************************
         */
        @Override
        protected Boolean validateCellContent(List<Object[]> tableData, int row, int column, Object oldValue, Object newValue, Boolean showMessage, boolean isMultiple) {
            // Reset the flag that indicates the last edited cell's content is invalid
            setLastCellValid(true);
            // Create a string version of the new value
            String newValueS = newValue.toString();
            try {
                // Check if the value isn't blank
                if (!newValueS.isEmpty()) {
                    // Check if the macro name has been changed and if the name isn't blank
                    if (column == MacroEditorColumnInfo.NAME.ordinal()) {
                        // Check if the macro name does not match the alphanumeric input type
                        if (!newValueS.matches(InputDataType.ALPHANUMERIC.getInputMatch())) {
                            throw new CCDDException("Illegal character(s) in macro name");
                        }
                        // creating a duplicate
                        for (int otherRow = 0; otherRow < getRowCount(); otherRow++) {
                            // name matches the one being added (case insensitive)
                            if (otherRow != row && newValueS.equalsIgnoreCase(tableData.get(otherRow)[column].toString())) {
                                throw new CCDDException("Macro name already in use");
                            }
                        }
                    } else // Check if the macro value changed
                    if (column == MacroEditorColumnInfo.VALUE.ordinal()) {
                        // Create a macro handler using the values currently displayed in the
                        // macro editor
                        CcddMacroHandler newMacroHandler = new CcddMacroHandler(ccddMain, getUpdatedData());
                        newMacroHandler.setHandlers(ccddMain.getVariableHandler());
                        // Get the macro's index and name
                        String index = tableData.get(row)[MacroEditorColumnInfo.OID.ordinal()].toString();
                        String macroName = tableData.get(row)[MacroEditorColumnInfo.NAME.ordinal()].toString();
                        // (doesn't cause a recursive reference)
                        if (!macroName.isEmpty() && newMacroHandler.isMacroRecursive(macroName)) {
                            throw new CCDDException("Macro '</b>" + macroName + "<b>' contains a recursive reference");
                        }
                        // Step through the committed macros
                        for (int commRow = 0; commRow < committedData.length; commRow++) {
                            // Check if the index matches that for the committed macro
                            if (index.equals(committedData[commRow][MacroEditorColumnInfo.OID.ordinal()])) {
                                List<String> tableNames = new ArrayList<String>();
                                // Get the macro name. Use the committed name (in place of the
                                // current name in the editor, in case it's been changed) since
                                // this is how the macro is referenced in the data tables
                                macroName = committedData[commRow][MacroEditorColumnInfo.NAME.ordinal()];
                                MacroReference macroRefs = null;
                                // loaded
                                for (MacroReference loadedRef : loadedReferences) {
                                    // searched macro
                                    if (macroName.equals(loadedRef.getMacroName())) {
                                        // Store the macro search reference and stop searching
                                        macroRefs = loadedRef;
                                        break;
                                    }
                                }
                                // Check if the macro references haven't already been loaded
                                if (macroRefs == null) {
                                    // Search for references to this macro
                                    macroRefs = new MacroReference(macroName);
                                    // Add the search results to the list so that this search
                                    // doesn't get performed again
                                    loadedReferences.add(macroRefs);
                                }
                                // Step through each reference to the macro in the tables
                                for (String macroRef : macroRefs.getReferences()) {
                                    // Split the reference into table name, column name, table
                                    // type, and context
                                    String[] tblColDescAndCntxt = macroRef.split(TABLE_DESCRIPTION_SEPARATOR, 4);
                                    String refComment = tblColDescAndCntxt[SearchResultsQueryColumn.COMMENT.ordinal()];
                                    // table
                                    if (!refComment.isEmpty()) {
                                        // Extract the viewable name and type of the table and
                                        // the name of the column containing the data type, and
                                        // separate the column string into the individual
                                        // column values
                                        String[] refNameAndType = refComment.split(",");
                                        // contain a type mismatch
                                        if (!tableNames.contains(refNameAndType[0])) {
                                            String refColumn = tblColDescAndCntxt[SearchResultsQueryColumn.COLUMN.ordinal()];
                                            String[] refContext = CcddUtilities.splitAndRemoveQuotes(tblColDescAndCntxt[SearchResultsQueryColumn.CONTEXT.ordinal()]);
                                            // Use the type and column to get the column's
                                            // input data type
                                            TypeDefinition typeDefn = ccddMain.getTableTypeHandler().getTypeDefinition(refNameAndType[1]);
                                            int columnIndex = typeDefn.getColumnIndexByDbName(refColumn);
                                            InputDataType inputType = typeDefn.getInputTypes()[columnIndex];
                                            // column value
                                            for (String oldName : macroHandler.getReferencedMacros(refContext[columnIndex])) {
                                                String newName = oldName;
                                                String oid = "";
                                                // Step through the updated macro definitions
                                                for (String[] oldMacro : macroHandler.getMacroData()) {
                                                    // Check if the macro names match
                                                    if (oldName.equals(oldMacro[MacrosColumn.MACRO_NAME.ordinal()])) {
                                                        // Store the OID value for the macro
                                                        // and stop searching
                                                        oid = oldMacro[MacrosColumn.OID.ordinal()];
                                                        break;
                                                    }
                                                }
                                                // Step through the updated macro definitions
                                                for (String[] newMacro : newMacroHandler.getMacroData()) {
                                                    // the target one
                                                    if (oid.equals(newMacro[MacrosColumn.OID.ordinal()])) {
                                                        // Since the OIDs match these are the
                                                        // same macro. Store the macro's new
                                                        // name (in case it changed) and stop
                                                        // searching
                                                        newName = newMacro[MacrosColumn.MACRO_NAME.ordinal()];
                                                        break;
                                                    }
                                                }
                                                // Replace all instances of the macro's old
                                                // name with its new name
                                                refContext[columnIndex] = macroHandler.replaceMacroName(CcddMacroHandler.getFullMacroName(oldName), CcddMacroHandler.getFullMacroName(newName), refContext[columnIndex]);
                                            }
                                            // macro's user
                                            if (!newMacroHandler.getMacroExpansion(refContext[columnIndex]).matches(inputType.getInputMatch())) {
                                                // Add the affected table name to the list
                                                tableNames.add(refNameAndType[0]);
                                            }
                                        }
                                    }
                                }
                                // were found
                                if (!tableNames.isEmpty()) {
                                    throw new CCDDException("Macro value is not consistent with macro usage in table(s) '</b>" + dbTable.getShortenedTableNames(tableNames.toArray(new String[0])) + "<b>'");
                                }
                                break;
                            }
                        }
                    }
                }
            } catch (CCDDException ce) {
                // Set the flag that indicates the last edited cell's content is invalid
                setLastCellValid(false);
                // Check if the input error dialog should be displayed
                if (showMessage) {
                    // Inform the user that the input value is invalid
                    new CcddDialogHandler().showMessageDialog(CcddMacroEditorDialog.this, "<html><b>" + ce.getMessage(), "Invalid Input", JOptionPane.WARNING_MESSAGE, DialogOption.OK_OPTION);
                }
                // Restore the cell contents to its original value and pop the edit from the
                // stack
                tableData.get(row)[column] = oldValue;
                macroTable.getUndoManager().undoRemoveEdit();
            }
            return false;
        }

        /**
         ************************************************************************************
         * Load the table macro definition values into the table and format the table cells
         ************************************************************************************
         */
        @Override
        protected void loadAndFormatData() {
            // Place the data into the table model along with the column names, set up the
            // editors and renderers for the table cells, set up the table grid lines, and
            // calculate the minimum width required to display the table information
            setUpdatableCharacteristics(committedData, MacroEditorColumnInfo.getColumnNames(), null, MacroEditorColumnInfo.getToolTips(), true, true, true);
        }

        /**
         ************************************************************************************
         * Override prepareRenderer to allow adjusting the background colors of table cells
         ************************************************************************************
         */
        @Override
        public Component prepareRenderer(TableCellRenderer renderer, int row, int column) {
            JComponent comp = (JComponent) super.prepareRenderer(renderer, row, column);
            // invalid highlighting, if applicable)
            if (!(isFocusOwner() && isRowSelected(row) && (isColumnSelected(column) || !getColumnSelectionAllowed()))) {
                boolean found = true;
                // Check if the cell is required and is empty
                if (MacroEditorColumnInfo.values()[macroTable.convertColumnIndexToModel(column)].isRequired() && macroTable.getValueAt(row, column).toString().isEmpty()) {
                    // Set the flag indicating that the cell value is invalid
                    found = false;
                }
                // Check if the cell value is invalid
                if (!found) {
                    // Change the cell's background color
                    comp.setBackground(ModifiableColorInfo.REQUIRED_BACK.getColor());
                }
            }
            return comp;
        }

        /**
         ************************************************************************************
         * Override the CcddJTableHandler method to produce an array containing empty values
         * for a new row in this table
         *
         * @return Array containing blank cell values for a new row
         ************************************************************************************
         */
        @Override
        protected Object[] getEmptyRow() {
            return MacroEditorColumnInfo.getEmptyRow();
        }

        /**
         ************************************************************************************
         * Handle a change to the table's content
         ************************************************************************************
         */
        @Override
        protected void processTableContentChange() {
            // Add or remove the change indicator based on whether or not any unstored changes
            // exist
            setTitle(DIALOG_TITLE + (macroTable.isTableChanged(committedData) ? "*" : ""));
            // Force the table to redraw so that changes to the cells are displayed
            repaint();
        }
    };
    // Place the table into a scroll pane
    JScrollPane scrollPane = new JScrollPane(macroTable);
    // Disable storage of edit operations during table creation
    macroTable.getUndoHandler().setAllowUndo(false);
    // Set common table parameters and characteristics
    macroTable.setFixedCharacteristics(scrollPane, true, ListSelectionModel.MULTIPLE_INTERVAL_SELECTION, TableSelectionMode.SELECT_BY_CELL, false, ModifiableColorInfo.TABLE_BACK.getColor(), true, true, ModifiableFontInfo.DATA_TABLE_CELL.getFont(), true);
    // Re-enable storage of edit operations
    macroTable.getUndoHandler().setAllowUndo(true);
    return scrollPane;
}
Also used : JScrollPane(javax.swing.JScrollPane) TableCellRenderer(javax.swing.table.TableCellRenderer) MouseEvent(java.awt.event.MouseEvent) CCDDException(CCDD.CcddClassesDataTable.CCDDException) ArrayList(java.util.ArrayList) JComponent(javax.swing.JComponent) Point(java.awt.Point) Point(java.awt.Point) TypeDefinition(CCDD.CcddTableTypeHandler.TypeDefinition) JComponent(javax.swing.JComponent) Component(java.awt.Component) InputDataType(CCDD.CcddConstants.InputDataType)

Example 3 with InputDataType

use of CCDD.CcddConstants.InputDataType in project CCDD by nasa.

the class CcddDbTableCommandHandler method modifyTableType.

/**
 ********************************************************************************************
 * Add, rename, or delete table type column names in the table definitions table and update the
 * existing tables of the specified type
 *
 * @param typeName
 *            table type name to modify
 *
 * @param fieldInformation
 *            data field information list
 *
 * @param overwriteFields
 *            true if the content of existing fields should be overwritten by the default
 *            values; false to not overwrite existing values
 *
 * @param additions
 *            list of new columns to add to the tables. Each list item is an array containing:
 *            [0] column name (user), [1] column input type
 *
 * @param modifications
 *            list of name changes of existing columns in the tables. Each list item is an
 *            array containing: [0] original column name (user), [1] new column name (user),
 *            [2] column input type
 *
 * @param deletions
 *            list of columns to remove from the tables. Each list item is an array containing:
 *            [0] column name (user), [1] column input type
 *
 * @param columnOrderChange
 *            true if the table type's column order changed
 *
 * @param originalDefn
 *            reference to the table type definition prior to making any changes; null if this
 *            is a new table type
 *
 * @param editorDialog
 *            reference to the table type editor dialog
 *
 * @param editor
 *            reference to the table type editor where the change(s) occurred
 ********************************************************************************************
 */
protected void modifyTableType(String typeName, List<FieldInformation> fieldInformation, boolean overwriteFields, List<String[]> additions, List<String[]> modifications, List<String[]> deletions, boolean columnOrderChange, TypeDefinition originalDefn, CcddTableTypeEditorDialog editorDialog, CcddTableTypeEditorHandler editor) {
    try {
        boolean errorFlag = false;
        String[] tableNames = null;
        String names = "";
        // Get the type definition based on the table type name
        TypeDefinition typeDefn = tableTypeHandler.getTypeDefinition(typeName);
        // Set the flags that indicates if the table type definition represents a structure
        // prior to and after making the updates
        boolean wasStructure = originalDefn != null ? originalDefn.isStructure() : false;
        boolean isStructure = typeDefn.isStructure();
        // Create the command to update the table definitions table
        StringBuilder command = new StringBuilder(storeTableTypesInfoTableCommand());
        // Check if this isn't a new table type
        if (originalDefn != null) {
            // Get an array containing all of the prototype tables of the specified type
            String[] protoTableNames = queryTablesOfTypeList(typeName, editorDialog);
            String columnOrder = "";
            // Check if the column order changed or if any columns were added or deleted
            if (columnOrderChange || !additions.isEmpty() || !deletions.isEmpty()) {
                // Step through each column in the table type
                for (int index = 0; index < typeDefn.getColumnNamesDatabase().length; index++) {
                    // Add the column index and a separator
                    columnOrder += index + ":";
                }
                // Remove the trailing separator
                columnOrder = CcddUtilities.removeTrailer(columnOrder, ":");
            }
            // Create a list to store the names of all tables of the specified type
            List<String> tableNamesList = new ArrayList<String>();
            // Build a table tree with all prototype and instance tables
            CcddTableTreeHandler tableTree = new CcddTableTreeHandler(ccddMain, TableTreeType.TABLES, editorDialog);
            // Step through each prototype table of the specified type
            for (String protoName : protoTableNames) {
                // Create a list of table path arrays for instances of this prototype table
                // name
                tableNamesList.addAll(tableTree.getTableTreePathList(protoName));
                // Get the database form of the table name
                String dbTable = protoName.toLowerCase();
                // Step through each addition
                for (String[] add : additions) {
                    // Get the input data type, and the column name in database form
                    InputDataType inputDataType = InputDataType.getInputTypeByName(add[1]);
                    String dbName = DefaultColumn.convertVisibleToDatabase(add[0], inputDataType, isStructure);
                    // Append the add command
                    command.append("ALTER TABLE " + dbTable + " ADD COLUMN " + dbName + " text DEFAULT ''; ");
                }
                // Step through each modification
                for (String[] mod : modifications) {
                    // Get the old and new column names in database form
                    String oldDbName = DefaultColumn.convertVisibleToDatabase(mod[0], InputDataType.getInputTypeByName(mod[2]), isStructure);
                    String newDbName = DefaultColumn.convertVisibleToDatabase(mod[1], InputDataType.getInputTypeByName(mod[3]), isStructure);
                    // Check if the database form of the name changed
                    if (!oldDbName.equals(newDbName)) {
                        // Append the modify command
                        command.append("ALTER TABLE " + dbTable + " RENAME COLUMN " + oldDbName + " TO " + newDbName + "; ");
                    }
                }
                // Step through each deletion
                for (String[] del : deletions) {
                    // Get the input data type, and the column name in database form
                    InputDataType inputDataType = InputDataType.getInputTypeByName(del[1]);
                    String dbName = DefaultColumn.convertVisibleToDatabase(del[0], inputDataType, isStructure);
                    // Append the delete command
                    command.append("ALTER TABLE " + dbTable + " DROP COLUMN " + dbName + "; ");
                }
                // Check if the column order changed
                if (columnOrder.length() != 0) {
                    // Replace the column order for all tables matching this table name. This
                    // resets the column order for all users
                    command.append("UPDATE " + InternalTable.ORDERS.getTableName() + " SET " + OrdersColumn.COLUMN_ORDER.getColumnName() + " = '" + columnOrder + "' WHERE " + OrdersColumn.TABLE_PATH.getColumnName() + " ~ E'^" + protoName + "(,|$)'; ");
                }
            }
            // exist
            if ((!modifications.isEmpty() || !deletions.isEmpty()) && protoTableNames.length != 0) {
                // ////////////////////////////////////////////////////////////////////////////
                // Build the sub-command required to detect references to tables of the
                // affected type in the custom values, links, and telemetry scheduler tables
                // ////////////////////////////////////////////////////////////////////////////
                StringBuilder valuesCmd = new StringBuilder("");
                StringBuilder linksCmd = new StringBuilder("");
                StringBuilder tlmSchCmd = new StringBuilder("");
                // Step through each prototype table of the specified type
                for (String protoName : protoTableNames) {
                    // Build the table name comparison command for the custom values table
                    valuesCmd.append(ValuesColumn.TABLE_PATH.getColumnName() + " ~ E'[^,]+," + protoName + "\\.[^,]+,[^,]+$' OR ");
                }
                // Remove the trailing 'OR' and prepend an 'AND' to complete the custom values
                // command
                valuesCmd = CcddUtilities.removeTrailer(valuesCmd, " OR ");
                valuesCmd.insert(0, " AND (");
                valuesCmd.append(")");
                // Check if the table type represents a structure
                if (isStructure && wasStructure) {
                    // Step through each prototype table of the specified type
                    for (String protoName : protoTableNames) {
                        // Build the table name comparison command for the links and telemetry
                        // scheduler tables
                        linksCmd.append(LinksColumn.MEMBER.getColumnName() + " ~ E'(?:" + protoName + ",|[^,]+," + protoName + "\\.[^,]+,[^,]+$)' OR ");
                        tlmSchCmd.append(TlmSchedulerColumn.MEMBER.getColumnName() + " ~ E'(?:" + tlmSchSeparator + protoName + ",|[^,]+," + protoName + "\\.[^,]+,[^,]+$)' OR ");
                    }
                    // Remove the trailing 'OR' and prepend an 'AND' to complete the links and
                    // telemetry scheduler table commands
                    linksCmd = CcddUtilities.removeTrailer(linksCmd, " OR ");
                    linksCmd.insert(0, " AND (");
                    linksCmd.append(")");
                    tlmSchCmd = CcddUtilities.removeTrailer(tlmSchCmd, " OR ");
                    tlmSchCmd.insert(0, " AND (");
                    tlmSchCmd.append(")");
                }
                // doing so
                if (!isStructure && wasStructure) {
                    boolean hasSharedRate = false;
                    // Step through each rate column in the table type definition
                    for (int column : originalDefn.getColumnIndicesByInputType(InputDataType.RATE)) {
                        // Get the rate column name (as seen by the user)
                        String rateName = originalDefn.getColumnNamesUser()[column];
                        // Check if this is the only table type using this rate column name
                        if (rateHandler.getRateInformationByRateName(rateName).getNumSharedTableTypes() == 1) {
                            // Delete all entries in the links and telemetry scheduler tables
                            // that reference this rate name
                            command.append(deleteAllLinkAndTlmRateRefs(rateName));
                        } else // The rate is shared with another table type
                        {
                            // Set the flag indicating the table type has a shared rate
                            hasSharedRate = true;
                        }
                    }
                    // the specific table references in these internal tables
                    if (hasSharedRate) {
                        // Remove all references to tables of the changed type in the links and
                        // telemetry scheduler tables
                        command.append(deleteLinkAndTlmRateRef(".+", linksCmd, tlmSchCmd));
                    }
                }
                // Step through each modification
                for (String[] mod : modifications) {
                    // Check if the column name changed
                    if (!mod[0].equals(mod[1])) {
                        // Append the modify command for the custom values table
                        command.append("UPDATE " + InternalTable.VALUES.getTableName() + " SET " + ValuesColumn.COLUMN_NAME.getColumnName() + " = '" + mod[1] + "' WHERE " + ValuesColumn.COLUMN_NAME.getColumnName() + " = '" + mod[0] + "'" + valuesCmd + "; ");
                    }
                    // was a rate
                    if (isStructure && wasStructure && mod[2].equals(InputDataType.RATE.getInputName())) {
                        // column
                        if (!mod[3].equals(InputDataType.RATE.getInputName())) {
                            // Check if the rate name is used by another structure table type
                            if (rateHandler.getRateInformationByRateName(mod[0]).getNumSharedTableTypes() > 1) {
                                // Remove all references to tables of the changed type in the
                                // links and telemetry scheduler tables
                                command.append(deleteLinkAndTlmRateRef(mod[0], linksCmd, tlmSchCmd));
                            } else // The rate name is unique to this table type
                            {
                                // Remove all references to the original rate column name in
                                // the links and telemetry scheduler tables
                                command.append(deleteAllLinkAndTlmRateRefs(mod[0]));
                            }
                        } else // Check if the rate column name changed
                        if (!mod[0].equals(mod[1])) {
                            // table type
                            if (rateHandler.getRateInformationByRateName(mod[0]).getNumSharedTableTypes() > 1) {
                                // Remove all references to tables of the changed type in the
                                // links and telemetry scheduler tables
                                command.append(deleteLinkAndTlmRateRef(mod[0], linksCmd, tlmSchCmd));
                            } else // type (i.e., the rate name is unique to this table type)
                            if (rateHandler.getRateInformationByRateName(mod[1]) == null) {
                                // Append the modify command for the links and telemetry
                                // scheduler table
                                command.append("UPDATE " + InternalTable.LINKS.getTableName() + " SET " + LinksColumn.RATE_NAME.getColumnName() + " = '" + mod[1] + "' WHERE " + LinksColumn.RATE_NAME.getColumnName() + " = '" + mod[0] + "'; UPDATE " + InternalTable.TLM_SCHEDULER.getTableName() + " SET " + TlmSchedulerColumn.RATE_NAME.getColumnName() + " = '" + mod[1] + "' WHERE " + TlmSchedulerColumn.RATE_NAME.getColumnName() + " = '" + mod[0] + "'; ");
                            } else // The new rate name is already in use
                            {
                                // Remove all references to the original rate column name in
                                // the links and telemetry scheduler tables
                                command.append(deleteAllLinkAndTlmRateRefs(mod[0]));
                            }
                        }
                    }
                }
                // Step through each deletion
                for (String[] del : deletions) {
                    // Append the delete command for the custom values table
                    command.append("DELETE FROM " + InternalTable.VALUES.getTableName() + " WHERE " + ValuesColumn.COLUMN_NAME.getColumnName() + " = '" + del[0] + "'" + valuesCmd + "; ");
                    // deleted
                    if (isStructure && wasStructure && del[1].equals(InputDataType.RATE.getInputName())) {
                        // Check if the rate name is used by another structure table type
                        if (rateHandler.getRateInformationByRateName(del[0]).getNumSharedTableTypes() > 1) {
                            // Remove all references to tables of the changed type in the links
                            // and telemetry scheduler tables
                            command.append(deleteLinkAndTlmRateRef(del[0], linksCmd, tlmSchCmd));
                        } else // The rate name is unique to this table type
                        {
                            // Remove all references to the original rate column name in the
                            // links and telemetry/scheduler tables
                            command.append(deleteAllLinkAndTlmRateRefs(del[0]));
                        }
                    }
                }
            }
            // Create a field handler to store the data field information
            CcddFieldHandler fieldHandler = new CcddFieldHandler(ccddMain, null, editorDialog);
            // Step through each table of the specified type
            for (String tableName : tableNamesList) {
                // Set the flag to indicate if the table is a root structure
                boolean isRootStruct = isStructure && rootStructures.contains(tableName);
                // Get the existing data fields for this table
                fieldHandler.buildFieldInformation(tableName);
                // Get the number of separator and line break fields
                int numSep = fieldHandler.getFieldTypeCount(InputDataType.SEPARATOR);
                int numBrk = fieldHandler.getFieldTypeCount(InputDataType.BREAK);
                int sepCount = 0;
                int brkCount = 0;
                boolean isChanges = false;
                // Step through the default data fields for this table type
                for (FieldInformation fieldInfo : fieldInformation) {
                    // Check if this is a separator
                    if (fieldInfo.getInputType().equals(InputDataType.SEPARATOR)) {
                        // Increment the separator counter
                        sepCount++;
                    } else // Check if this is a line break
                    if (fieldInfo.getInputType().equals(InputDataType.BREAK)) {
                        // Increment the line break counter
                        brkCount++;
                    }
                    // Check if the data field meets the criteria of a new field for this table
                    if ((// The table doesn't have this data field
                    (fieldHandler.getFieldInformationByName(tableName, fieldInfo.getFieldName()) == null)) && // this table
                    (!tableName.contains(".") || fieldHandler.isFieldApplicable(tableName, fieldInfo.getApplicabilityType().getApplicabilityName(), isRootStruct)) || // separator in the type editor exceeds the number already in the table
                    (fieldInfo.getInputType().equals(InputDataType.SEPARATOR) && sepCount > numSep) || (fieldInfo.getInputType().equals(InputDataType.BREAK) && brkCount > numBrk)) {
                        // Add the data field to the table and set the flag indicating a change
                        // has been made
                        fieldHandler.addField(tableName, fieldInfo.getFieldName(), fieldInfo.getDescription(), fieldInfo.getSize(), fieldInfo.getInputType().getInputName(), fieldInfo.isRequired(), fieldInfo.getApplicabilityType().getApplicabilityName(), fieldInfo.getValue());
                        isChanges = true;
                    } else // overwritten, and if the field value(s) changed
                    if (overwriteFields && fieldHandler.updateField(new FieldInformation(tableName, fieldInfo.getFieldName(), fieldInfo.getDescription(), fieldInfo.getSize(), fieldInfo.getInputType().getInputName(), fieldInfo.isRequired(), fieldInfo.getApplicabilityType().getApplicabilityName(), fieldInfo.getValue()))) {
                        // Set the flag indicating a change has been made
                        isChanges = true;
                    }
                }
                // Check if any fields were added
                if (isChanges) {
                    // Create the command to modify the table's data field entries
                    command.append(modifyFieldsCommand(tableName, fieldHandler.getFieldInformation()));
                }
            }
            // Convert the list of modified tables names to an array
            tableNames = tableNamesList.toArray(new String[0]);
            // Check if any table of this type exists
            if (tableNames.length != 0) {
                // Convert the array of tables names into a single string and shorten it if too
                // long
                names = " and table(s) '</b>" + getShortenedTableNames(tableNames) + "<b>'";
            }
            // Build the command to update the data fields table and the telemetry scheduler
            // table comment (rate parameters)
            command.append(modifyFieldsCommand(CcddFieldHandler.getFieldTypeName(typeName), fieldInformation));
        }
        try {
            // Execute the command to change the table type and any table's of this type
            dbCommand.executeDbCommand(command.toString(), editorDialog);
            // Check if the type changed from being a structure to not being a structure
            if (!isStructure && wasStructure) {
                // Update the list of root structure tables
                rootStructures = getRootStructures(editorDialog);
            }
            // Log that updating the table type succeeded
            eventLog.logEvent(SUCCESS_MSG, "Table type '" + typeName + "'" + CcddUtilities.removeHTMLTags(names) + " updated");
        } catch (SQLException se) {
            // Inform the user that updating the tables failed
            eventLog.logFailEvent(editorDialog, "Cannot update table type '" + typeName + "'" + CcddUtilities.removeHTMLTags(names) + "; cause '" + se.getMessage() + "'", "<html><b>Cannot update table type '</b>" + typeName + "<b>'" + names);
            errorFlag = true;
        }
        // structure
        if (!errorFlag && (isStructure || wasStructure)) {
            // Step through each column addition
            for (String[] add : additions) {
                // Check if the column is a rate column
                if (add[1].equals(InputDataType.RATE.getInputName())) {
                    // Add the rate column to the rate information
                    rateHandler.addRateInformation(add[0]);
                }
            }
            // Step through each column modification
            for (String[] mod : modifications) {
                // Check if the column changed from a rate column to not being a rate column
                if (mod[2].equals(InputDataType.RATE.getInputName()) && !mod[3].equals(InputDataType.RATE.getInputName())) {
                    // Delete the rate column from the rate information
                    rateHandler.deleteRateInformation(mod[2]);
                } else // Check if the column changed from not being a rate column to a rate column
                if (!mod[2].equals(InputDataType.RATE.getInputName()) && mod[3].equals(InputDataType.RATE.getInputName())) {
                    // Add the rate column to the rate information
                    rateHandler.addRateInformation(mod[3]);
                } else // changed)
                if (mod[3].equals(InputDataType.RATE.getInputName())) {
                    // Rename (or add if the rate column is shared with another table type) the
                    // rate column in the rate information
                    rateHandler.renameRateInformation(mod[0], mod[1]);
                }
            }
            // Step through each column deletion
            for (String[] del : deletions) {
                // Check if the column is a rate column
                if (del[1].equals(InputDataType.RATE.getInputName())) {
                    // Delete the rate column from the rate information
                    rateHandler.deleteRateInformation(del[0]);
                }
            }
            // Update the rate column information and store it in the project database
            rateHandler.setRateInformation();
            storeRateParameters(editorDialog);
            // Update the database functions that collect structure table members and
            // structure-defining column data
            dbControl.createStructureColumnFunctions();
        }
        // Perform the type modification clean-up steps
        editorDialog.doTypeModificationComplete(errorFlag, editor, tableNames);
    } catch (Exception e) {
        // Display a dialog providing details on the unanticipated error
        CcddUtilities.displayException(e, editorDialog);
    }
}
Also used : SQLException(java.sql.SQLException) ArrayList(java.util.ArrayList) CCDDException(CCDD.CcddClassesDataTable.CCDDException) SQLException(java.sql.SQLException) TypeDefinition(CCDD.CcddTableTypeHandler.TypeDefinition) InputDataType(CCDD.CcddConstants.InputDataType) FieldInformation(CCDD.CcddClassesDataTable.FieldInformation)

Example 4 with InputDataType

use of CCDD.CcddConstants.InputDataType in project CCDD by nasa.

the class CcddTableEditorHandler method initialize.

/**
 ********************************************************************************************
 * Create the table editor
 ********************************************************************************************
 */
private void initialize() {
    // Set the table type definition
    setTypeDefinition();
    // Get the model column indices for columns with special input types
    getSpecialColumnIndices();
    // Create a copy of the table information
    setCommittedInformation(tableInfo);
    // Get the array size and index column indices and create a row filter to show/hide the
    // array member rows if an array size column exists
    setUpArraySizeColumn();
    // Define the table editor JTable
    table = new CcddJTableHandler(ModifiableSizeInfo.INIT_VIEWABLE_DATA_TABLE_ROWS.getSize()) {

        /**
         ************************************************************************************
         * Highlight any macros or special flags in the table cells
         *
         * @param component
         *            reference to the table cell renderer component
         *
         * @param text
         *            cell text
         *
         * @param isSelected
         *            true if the cell is to be rendered with the selection highlighted
         *
         * @param int
         *            row cell row, view coordinates
         *
         * @param column
         *            cell column, view coordinates
         ************************************************************************************
         */
        @Override
        protected void doSpecialRendering(Component component, String text, boolean isSelected, int row, int column) {
            // Highlight any macro names in the table cell. Adjust the highlight color to
            // account for the cell selection highlighting so that the macro is easily readable
            macroHandler.highlightMacro(component, text, isSelected ? ModifiableColorInfo.INPUT_TEXT.getColor() : ModifiableColorInfo.TEXT_HIGHLIGHT.getColor());
            // Highlight 'sizeof(data type)' instances
            CcddDataTypeHandler.highlightSizeof(component, text, isSelected ? ModifiableColorInfo.INPUT_TEXT.getColor() : ModifiableColorInfo.TEXT_HIGHLIGHT.getColor());
            // Highlight the flag that indicates the custom value for this cell is to be
            // removed and the prototype's value used instead. Create a highlighter painter
            DefaultHighlightPainter painter = new DefaultHighlighter.DefaultHighlightPainter(isSelected ? ModifiableColorInfo.INPUT_TEXT.getColor() : Color.MAGENTA);
            // Create the match pattern
            Pattern pattern = Pattern.compile("^" + Pattern.quote(REPLACE_INDICATOR));
            // Create the pattern matcher from the pattern
            Matcher matcher = pattern.matcher(text);
            // Check if there is a match in the cell value
            if (matcher.find()) {
                try {
                    // Highlight the matching text. Adjust the highlight color to account for
                    // the cell selection highlighting so that the search text is easily
                    // readable
                    ((JTextComponent) component).getHighlighter().addHighlight(matcher.start(), matcher.end(), painter);
                } catch (BadLocationException ble) {
                // Ignore highlighting failure
                }
            }
        }

        /**
         ************************************************************************************
         * Get the tool tip text for a table cell, showing any macro name replaced with its
         * corresponding macro value
         ************************************************************************************
         */
        @Override
        public String getToolTipText(MouseEvent me) {
            String toolTipText = null;
            // Get the row and column of the cell over which the mouse pointer is hovering
            Point point = me.getPoint();
            int row = rowAtPoint(point);
            int column = columnAtPoint(point);
            // Check if a cell is beneath the mouse pointer
            if (row != -1 && column != -1) {
                // Expand any macros in the cell text and display this as the cell's tool tip
                // text
                toolTipText = macroHandler.getMacroToolTipText(getValueAt(row, column).toString());
            }
            return toolTipText;
        }

        /**
         ************************************************************************************
         * Return true if the table data, column order, description, or a data field changes.
         * If the table isn't open in and editor (as when a macro is changed) then the table
         * description and data fields are not applicable
         ************************************************************************************
         */
        @Override
        protected boolean isTableChanged(Object[][] previousData, List<Integer> ignoreColumns) {
            boolean isFieldChanged = false;
            // and/or value change)
            if (editorDialog != null) {
                // Update the field information with the current text field values
                updateCurrentFieldValues(tableInfo.getFieldHandler().getFieldInformation());
                // Set the flag if the number of fields, field attributes, or field contents
                // have changed
                isFieldChanged = CcddFieldHandler.isFieldChanged(tableInfo.getFieldHandler().getFieldInformation(), committedInfo.getFieldHandler().getFieldInformation(), false);
            }
            return super.isTableChanged(previousData, ignoreColumns) || isFieldChanged || !getColumnOrder().equals(committedInfo.getColumnOrder()) || (editorDialog != null && !committedInfo.getDescription().equals(getDescription()));
        }

        /**
         ************************************************************************************
         * Allow multiple line display in all columns
         ************************************************************************************
         */
        @Override
        protected boolean isColumnMultiLine(int column) {
            return true;
        }

        /**
         ************************************************************************************
         * Hide the the specified columns
         ************************************************************************************
         */
        @Override
        protected boolean isColumnHidden(int column) {
            return column == primaryKeyIndex || column == rowIndex;
        }

        /**
         ************************************************************************************
         * Override isCellEditable to determine which cells can be edited
         ************************************************************************************
         */
        @Override
        public boolean isCellEditable(int row, int column) {
            // Initialize the flag to the table edit flag (based on the table edit flag this
            // enables normal editing or disables editing any cell)
            boolean isEditable = isEditEnabled;
            // one row
            if (isEditable && (isDisplayable() || editorDialog == null) && tableModel != null && tableModel.getRowCount() != 0) {
                // Convert the view row and column indices to model coordinates
                int modelRow = convertRowIndexToModel(row);
                int modelColumn = convertColumnIndexToModel(column);
                // Check if the cell is editable
                isEditable = isDataAlterable(((List<?>) tableModel.getDataVector().elementAt(modelRow)).toArray(new String[0]), modelRow, modelColumn);
            }
            return isEditable;
        }

        /**
         ************************************************************************************
         * Override isDataAlterable to determine which table data values can be changed
         *
         * @param rowData
         *            array containing the table row data
         *
         * @param row
         *            table row index in model coordinates
         *
         * @param column
         *            table column index in model coordinates
         *
         * @return true if the data value can be changed
         ************************************************************************************
         */
        @Override
        protected boolean isDataAlterable(Object[] rowData, int row, int column) {
            boolean isAlterable = true;
            // Check if the table data has at least one row
            if (rowData != null && rowData.length != 0) {
                // Copy the row of table data. This prevents the macro expansions for array
                // size and bit length below from changing the cell contents
                Object[] rowCopy = Arrays.copyOf(rowData, rowData.length);
                // Check if the array size column is present in this table
                if (arraySizeIndex != -1) {
                    // Expand any macros in the array size column
                    rowCopy[arraySizeIndex] = newMacroHandler.getMacroExpansion(rowCopy[arraySizeIndex].toString());
                }
                // Check if the array size column is present in this table
                if (bitLengthIndex != -1) {
                    // Expand any macros in the array size column
                    rowCopy[bitLengthIndex] = newMacroHandler.getMacroExpansion(rowCopy[bitLengthIndex].toString());
                }
                // Flag that is true if the row represents an array definition
                boolean isArrayDefinition = arraySizeIndex != -1 && variableNameIndex != -1 && !rowCopy[arraySizeIndex].toString().isEmpty() && !ArrayVariable.isArrayMember(rowCopy[variableNameIndex]);
                // Check if the cell is non-alterable based on the following criteria:
                if (// this is not a prototype table
                ((column == variableNameIndex || column == dataTypeIndex || column == arraySizeIndex || column == bitLengthIndex) && !tableInfo.isPrototype()) || // displays an array member
                ((column == variableNameIndex || column == dataTypeIndex || column == arraySizeIndex) && ArrayVariable.isArrayMember(rowCopy[variableNameIndex])) || // isn't valid for structures
                (dataTypeIndex != -1 && !dataTypeHandler.isPrimitive(rowCopy[dataTypeIndex].toString()) && !typeDefn.isStructureAllowed()[column]) || // This data type is a pointer and the column isn't valid for pointers
                (dataTypeIndex != -1 && dataTypeHandler.isPointer(rowCopy[dataTypeIndex].toString()) && !typeDefn.isPointerAllowed()[column]) || // This is the enumeration or rate cell in a row displaying an array definition
                ((isArrayDefinition && enumerationIndex.contains(column) && rateIndex.contains(column))) || // type is not an integer (signed or unsigned)
                (column == bitLengthIndex && ((arraySizeIndex != -1 && !rowCopy[arraySizeIndex].toString().isEmpty()) || (dataTypeIndex != -1 && !dataTypeHandler.isInteger(rowCopy[dataTypeIndex].toString())))) || // length is present
                (column == arraySizeIndex && ((variableNameIndex != -1 && rowCopy[variableNameIndex].toString().isEmpty()) || (bitLengthIndex != -1 && !rowCopy[bitLengthIndex].toString().isEmpty()))) || // This is a rate cell, and a data type exists that is not a primitive
                (rateIndex.contains(column) && dataTypeIndex != -1 && !rowCopy[dataTypeIndex].toString().isEmpty() && !dataTypeHandler.isPrimitive(rowCopy[dataTypeIndex].toString())) || // first array member
                (variableNameIndex != -1 && dataTypeIndex != -1 && dataTypeHandler.isString(rowCopy[dataTypeIndex].toString()) && ArrayVariable.isArrayMember(rowCopy[variableNameIndex]) && !rowCopy[variableNameIndex].toString().endsWith("[0]")) || // the variable path
                (isArrayDefinition && (typeDefn.getInputTypes()[column].equals(InputDataType.MESSAGE_ID) || column == variablePathIndex)) || // variables in a non-root prototype structure
                (column == variablePathIndex && rowCopy[variablePathIndex].toString().isEmpty())) {
                    // Set the flag to prevent altering the data value
                    isAlterable = false;
                } else // Check the column groupings
                {
                    // Step through each column grouping
                    for (AssociatedColumns colGrp : associatedColumns) {
                        // Check if the cell is non-alterable based on the following criteria:
                        if ((// isn't an integer type (signed or unsigned)
                        column == colGrp.getEnumeration() && !dataTypeHandler.isInteger(rowCopy[colGrp.getDataType()].toString())) || // defined
                        ((column == colGrp.getMinimum() || column == colGrp.getMaximum()) && rowCopy[colGrp.getDataType()].toString().isEmpty())) {
                            // Set the flag to prevent altering the data value and stop
                            // searching
                            isAlterable = false;
                            break;
                        }
                    }
                    // Check if no command argument pairing reset the flag
                    if (isAlterable) {
                        // Step through each non-command argument minimum/maximum pairing
                        for (MinMaxPair minMax : minMaxPair) {
                            // is missing, empty, or isn't a primitive type
                            if (dataTypeIndex != -1 && (rowCopy[dataTypeIndex].toString().isEmpty() || !dataTypeHandler.isPrimitive(rowCopy[dataTypeIndex].toString())) && (column == minMax.getMinimum() || column == minMax.getMaximum())) {
                                // Set the flag to prevent altering the data value and stop
                                // searching
                                isAlterable = false;
                                break;
                            }
                        }
                    }
                }
            }
            return isAlterable;
        }

        /**
         ************************************************************************************
         * Override getCellEditor so that for a data type column cell in a row where the
         * enumeration cell isn't empty the combo box editor that displays only integer data
         * types (signed and unsigned) is returned; for all other cells return the normal cell
         * editor
         *
         * @param row
         *            table view row number
         *
         * @param column
         *            table view column number
         *
         * @return The cell editor for the specified row and column
         ************************************************************************************
         */
        @Override
        public TableCellEditor getCellEditor(int row, int column) {
            // Get the editor for this cell
            TableCellEditor cellEditor = super.getCellEditor(row, column);
            // Convert the row and column indices to the model coordinates
            int modelRow = convertRowIndexToModel(row);
            int modelColumn = convertColumnIndexToModel(column);
            // column and the bit length cell is not empty
            if (modelColumn == dataTypeIndex && bitLengthIndex != -1 && !getExpandedValueAt(modelRow, bitLengthIndex).isEmpty()) {
                // Select the combo box cell editor that displays only integer data types
                // (signed and unsigned)
                cellEditor = enumDataTypeCellEditor;
            } else // Check if this is a data type and enumeration pairing or a command argument
            // column grouping
            {
                // Step through each column grouping
                for (AssociatedColumns colGrp : associatedColumns) {
                    // type column, and that the associated enumeration cell isn't blank
                    if (modelColumn == colGrp.getDataType() && colGrp.getEnumeration() != -1 && !getExpandedValueAt(modelRow, colGrp.getEnumeration()).isEmpty()) {
                        // Select the combo box cell editor that displays only integer data
                        // types (signed and unsigned) and stop searching
                        cellEditor = enumDataTypeCellEditor;
                        break;
                    }
                }
            }
            return cellEditor;
        }

        /**
         ************************************************************************************
         * Validate changes to the editable cells; e.g., verify cell content and, if found
         * invalid, revert to the original value. Update array members if needed
         *
         * @param tableData
         *            list containing the table data row arrays
         *
         * @param row
         *            table model row index
         *
         * @param column
         *            table model column index
         *
         * @param oldValue
         *            original cell contents
         *
         * @param newValue
         *            new cell contents
         *
         * @param showMessage
         *            true to display the invalid input dialog, if applicable
         *
         * @param isMultiple
         *            true if this is one of multiple cells to be entered and checked; false if
         *            only a single input is being entered
         *
         * @return true to indicate that subsequent errors should be displayed; false if
         *         subsequent errors should not be displayed; null if the operation should be
         *         canceled
         ************************************************************************************
         */
        @Override
        protected Boolean validateCellContent(List<Object[]> tableData, int row, int column, Object oldValue, Object newValue, Boolean showMessage, boolean isMultiple) {
            // Reset the flag that indicates the last edited cell's content is invalid
            setLastCellValid(true);
            try {
                // Set the parameters that govern recalculating packed variables to begin with
                // the first row in the table and to use the first variable in the pack to set
                // the rates for other variables in the same pack
                int startRow = 0;
                boolean useRowRate = false;
                // Create a string version of the new value, replacing any macro in the text
                // with its corresponding value
                String newValueS = newMacroHandler.getMacroExpansion(newValue.toString(), validDataTypes);
                // to this structure's prototype or the prototype of one of its children
                if (variableHandler.isInvalidReference()) {
                    throw new CCDDException("Invalid input value in column '</b>" + typeDefn.getColumnNamesUser()[column] + "<b>'; data type invalid or unknown in sizeof() call");
                }
                // Check if the cell is flagged for replacement by the prototype value
                if (newValueS.startsWith(REPLACE_INDICATOR)) {
                    // Remove the flag so that the updated value is stored as a custom value
                    newValueS = newValueS.replaceFirst("^" + REPLACE_INDICATOR, "");
                }
                // Check that the new value isn't blank
                if (!newValueS.isEmpty()) {
                    // Check if the values in this column must not be duplicated
                    if (typeDefn.isRowValueUnique()[column]) {
                        // Step through each row in the table
                        for (int otherRow = 0; otherRow < tableData.size(); otherRow++) {
                            // matches the one being added (case insensitive)
                            if (otherRow != row && newValueS.equalsIgnoreCase(getExpandedValueAt(tableData, otherRow, column))) {
                                throw new CCDDException("Invalid input value for column '</b>" + typeDefn.getColumnNamesUser()[column] + "<b>'; value must be unique");
                            }
                        }
                    }
                    // Step through each column grouping
                    for (AssociatedColumns colGrp : associatedColumns) {
                        // Check if this is a name column
                        if (column == colGrp.getName()) {
                            // Step through the column groupings
                            for (AssociatedColumns otherColGrp : associatedColumns) {
                                // name matches the name of another command argument
                                if (!colGrp.equals(otherColGrp) && newValueS.equals(getExpandedValueAt(tableData, row, otherColGrp.getName()))) {
                                    throw new CCDDException("Invalid input value for column '</b>" + typeDefn.getColumnNamesUser()[column] + "<b>'; command argument names must be unique for a command");
                                }
                            }
                        } else // Check if this is the minimum or maximum value columns
                        if (column == colGrp.getMinimum() || column == colGrp.getMaximum()) {
                            // Verify that the minimum/maximum value is valid for the
                            // argument's data type, and stop searching
                            validateMinMaxContent(tableData, row, column, newValueS, colGrp.getDataType(), colGrp.getMinimum(), colGrp.getMaximum());
                            break;
                        }
                    }
                    // Step through each minimum/maximum pairing
                    for (MinMaxPair minMax : minMaxPair) {
                        // Check if this is the minimum or maximum value columns
                        if (column == minMax.getMinimum() || column == minMax.getMaximum()) {
                            // Verify that the minimum/maximum value is valid for the
                            // argument's data type, and stop searching
                            validateMinMaxContent(tableData, row, column, newValueS, dataTypeIndex, minMax.getMinimum(), minMax.getMaximum());
                            break;
                        }
                    }
                    // Check if the value doesn't match the expected input type
                    if (!newValueS.matches(typeDefn.getInputTypes()[column].getInputMatch())) {
                        throw new CCDDException("Invalid characters in column '</b>" + typeDefn.getColumnNamesUser()[column] + "<b>'; characters consistent with input type '" + typeDefn.getInputTypes()[column].getInputName() + "' expected");
                    }
                    // Check if this is a message ID name column
                    if (msgIDNameIndex.contains(column)) {
                        // The message ID, which is included with the ID name in the combo box
                        // list, doesn't appear when the item is selected from the list, so
                        // remove the ID
                        newValueS = newValueS.replaceFirst(" \\(.*", "");
                    }
                }
                // Flag that indicates that the new cell value contains a macro and/or a
                // sizeof() call
                boolean hasMacroSizeof = CcddMacroHandler.hasMacro(newValue.toString()) || CcddVariableSizeAndConversionHandler.hasSizeof(newValue.toString());
                // prevents the macro reference from being lost
                if (!hasMacroSizeof) {
                    // Store the new value in the table data array after formatting the cell
                    // value per its input type. This is needed primarily to clean up numeric
                    // formatting
                    newValueS = typeDefn.getInputTypes()[column].formatInput(newValueS);
                    newValue = newValueS;
                    tableData.get(row)[column] = newValueS;
                }
                // Replace any macro in the original text with its corresponding value
                String oldValueS = macroHandler.getMacroExpansion(oldValue.toString());
                // original text with its corresponding value
                if (!newValueS.equals(oldValueS)) {
                    String variableName = null;
                    String dataType = null;
                    String arraySize = null;
                    String bitLength = null;
                    // Check if the variable name column exists
                    if (variableNameIndex != -1) {
                        // Get the variable name for the current row, expanding macros in the
                        // name (if present)
                        variableName = getExpandedValueAt(tableData, row, variableNameIndex);
                    }
                    // Check if the data type column exists
                    if (dataTypeIndex != -1) {
                        // Get the data type for the current row
                        dataType = tableData.get(row)[dataTypeIndex].toString();
                    }
                    // Check if the array size column exists
                    if (arraySizeIndex != -1) {
                        // Get the array size for the current row, expanding macros in the
                        // value (if present)
                        arraySize = getExpandedValueAt(tableData, row, arraySizeIndex);
                    }
                    // Check if the bit length column exists
                    if (bitLengthIndex != -1) {
                        // Get the bit length for the current row, expanding macros in the
                        // value (if present)
                        bitLength = getExpandedValueAt(tableData, row, bitLengthIndex);
                    }
                    // Check if the variable name or data type has been changed
                    if (column == variableNameIndex || column == dataTypeIndex) {
                        // if a data type is pasted into the cell
                        if (invalidDataTypes != null && invalidDataTypes.contains(dataType)) {
                            throw new CCDDException("Data type '</b>" + dataType + "<b>' invalid; structure cannot reference itself or an ancestor");
                        }
                        // Check if the variable is an array
                        if (arraySize != null) {
                            // and the bit length exceeds the size of the data type in bits
                            if (column == dataTypeIndex && !newValueS.isEmpty() && bitLength != null && !bitLength.isEmpty() && Integer.valueOf(bitLength) > newDataTypeHandler.getSizeInBits(dataType)) {
                                throw new CCDDException("Bit length exceeds the size of the data type");
                            }
                            // Get the array index values from the array size column and update
                            // array members if this is an array definition
                            int[] arrayDims = ArrayVariable.getArrayIndexFromSize(arraySize);
                            adjustArrayMember(tableData, arrayDims, arrayDims, row, column);
                        }
                        // manually set
                        if (variablePathIndex != -1 && tableData.get(row)[variablePathIndex].toString().equals(getVariablePath((column == variableNameIndex ? oldValueS : variableName), (column == dataTypeIndex ? oldValueS : dataType), true))) {
                            // Update the variable path with the new variable name and/or data
                            // type
                            tableData.get(row)[variablePathIndex] = getVariablePath(variableName, dataType, true);
                        }
                    } else // Check if this is the array size column
                    if (column == arraySizeIndex) {
                        // Get the original and updated array index values
                        int[] arraySizeOld = ArrayVariable.getArrayIndexFromSize(oldValueS);
                        int[] arraySizeNew = ArrayVariable.getArrayIndexFromSize(newValueS);
                        // Set the flag that indicates the array index values changed based on
                        // the number of index values changing
                        boolean isDifferent = arraySizeOld.length != arraySizeNew.length;
                        // individual array index values must be compared
                        if (!isDifferent) {
                            // Step through each index value
                            for (int index = 0; index < arraySizeOld.length; index++) {
                                // Check if the original and updated values differ
                                if (arraySizeOld[index] != arraySizeNew[index]) {
                                    // Set the flag to indicate an index value changed and stop
                                    // searching
                                    isDifferent = true;
                                    break;
                                }
                            }
                        }
                        // Check if the original and updated values differ
                        if (isDifferent) {
                            // Add or remove array members to match the new array size
                            adjustArrayMember(tableData, arraySizeOld, arraySizeNew, row, column);
                        }
                    } else // Check if this is the rate column and the row is an array definition
                    if (rateIndex.contains(column) && arraySize != null && variableName != null && !arraySize.isEmpty() && !ArrayVariable.isArrayMember(variableName)) {
                        // Get the array index value(s)
                        int[] arrayDims = ArrayVariable.getArrayIndexFromSize(arraySize);
                        // Update the array members with the new rate
                        adjustArrayMember(tableData, arrayDims, arrayDims, row, column);
                    } else // Check if this is the rate column and the variable has a bit length value
                    if (rateIndex.contains(column) && bitLength != null && dataType != null && !bitLength.isEmpty()) {
                        // Adjust the rates of any other bit-wise variables that are packed
                        // together with this variable, using this row's rate
                        startRow = row;
                        useRowRate = true;
                    } else // Check if this is the bit length column
                    if (column == bitLengthIndex) {
                        // data type
                        if (bitLength != null && !bitLength.isEmpty() && dataType != null && Integer.valueOf(bitLength) > newDataTypeHandler.getSizeInBits(dataType)) {
                            throw new CCDDException("Bit length exceeds the size of the data type");
                        }
                        // Adjust the rates of any other bit-wise variables that are packed
                        // together with this variable, using the first packed variable's rate
                        startRow = row;
                    } else // Check if this is the variable path column
                    if (column == variablePathIndex && variableName != null && !variableName.isEmpty() && dataType != null && !dataType.isEmpty()) {
                        // entered
                        if (!newValueS.isEmpty()) {
                            // another structure table
                            if (variableHandler.isVariablePathInUse(tableInfo.getTablePath() + "," + dataType + "." + variableName, newValueS)) {
                                throw new CCDDException("Variable path already in use in another structure");
                            }
                        } else // The cell has been blanked
                        {
                            // Build the variable path from the variable name and data type
                            tableData.get(row)[variablePathIndex] = getVariablePath(variableName, dataType, false);
                        }
                    } else // array
                    if (variableName != null && dataType != null && arraySize != null && column != variableNameIndex && column != dataTypeIndex && column != arraySizeIndex && column != variablePathIndex && !arraySize.isEmpty() && (!ArrayVariable.isArrayMember(variableName) || newDataTypeHandler.isString(dataType))) {
                        // Propagate the value to all members of this array/string
                        propagateArrayValues(tableData, row, column);
                    }
                    // Clear the contents of any cells that are no longer valid in this row
                    clearInvalidCells(tableData.get(row), row);
                    // Adjust the rates of the bit-wise variables that are packed together,
                    // beginning at the indicated row
                    setAllPackedVariableRates(tableData, startRow, useRowRate);
                    // Check if the new value contains any macros
                    if (hasMacroSizeof) {
                        // Store the new value, with the macro(s) restored, into the table data
                        // array
                        tableData.get(row)[column] = newValue;
                    }
                } else // vice versa)
                if (!newValue.equals(oldValue)) {
                    // Store the new value in the table data array
                    tableData.get(row)[column] = newValue;
                    // Check if the column is the array size
                    if (column == arraySizeIndex) {
                        // Propagate the value to all members of this array/string
                        propagateArrayValues(tableData, row, column);
                    }
                } else // The cell value didn't change
                {
                    // Pop the edit from the stack
                    table.getUndoManager().undoRemoveEdit();
                }
            } catch (CCDDException ce) {
                // Set the flag that indicates the last edited cell's content is invalid
                setLastCellValid(false);
                // Check if the error message dialog should be displayed
                if (showMessage) {
                    // Check if this is a single cell insert
                    if (!isMultiple) {
                        // Inform the user that the input value is invalid
                        new CcddDialogHandler().showMessageDialog(editorDialog, "<html><b>" + ce.getMessage(), "Invalid Input", JOptionPane.WARNING_MESSAGE, DialogOption.OK_OPTION);
                    } else // This is one of multiple cells being inserted
                    {
                        // Inform the user that the input value is invalid
                        CcddDialogHandler validityDlg = new CcddDialogHandler();
                        int buttonSelected = validityDlg.showIgnoreCancelDialog(editorDialog, "<html><b>" + ce.getMessage(), "Invalid Input", "Ignore this invalid input", "Ignore this and any remaining invalid inputs for this table", "Cease inputting values");
                        // Check if the Ignore All button was pressed
                        if (buttonSelected == IGNORE_BUTTON) {
                            // Set the flag to ignore subsequent input errors
                            showMessage = false;
                        } else // Check if the Cancel button was pressed
                        if (buttonSelected == CANCEL_BUTTON) {
                            // Set the flag to cancel updating the cells
                            showMessage = null;
                        }
                    }
                }
                // Restore the cell contents to its original value and pop the edit from the
                // stack
                tableData.get(row)[column] = oldValue;
                table.getUndoManager().undoRemoveEdit();
            }
            return showMessage;
        }

        /**
         ************************************************************************************
         * Clear the contents of cells in the specified row that are no longer valid due to the
         * contents of other cells
         *
         * @param tableData
         *            list containing the table data row arrays
         *
         * @param row
         *            table model row index
         ************************************************************************************
         */
        private void clearInvalidCells(Object[] rowData, int row) {
            // Step through each visible column
            for (int column = 0; column < getColumnCount(); column++) {
                // Get the column index in model coordinates
                int modelColumn = convertColumnIndexToModel(column);
                if (// rate, or variable path column, and that the cell is not alterable
                (modelColumn != variableNameIndex && modelColumn != dataTypeIndex && modelColumn != arraySizeIndex && modelColumn != bitLengthIndex && modelColumn != variablePathIndex && !rateIndex.contains(modelColumn) && !isDataAlterable(rowData, row, modelColumn)) || // (i.e., it's a structure), and structures are not allowed for this column
                (dataTypeIndex != -1 && !newDataTypeHandler.isPrimitive(rowData[dataTypeIndex].toString()) && !typeDefn.isStructureAllowed()[modelColumn]) || // pointers are not allowed for this column
                (dataTypeIndex != -1 && newDataTypeHandler.isPointer(rowData[dataTypeIndex].toString()) && !typeDefn.isPointerAllowed()[modelColumn]) || // data type isn't a primitive
                (dataTypeIndex != -1 && modelColumn == bitLengthIndex && !newDataTypeHandler.isPrimitive(rowData[dataTypeIndex].toString()))) {
                    // Clear the contents of the cell
                    rowData[modelColumn] = "";
                }
            }
        }

        /**
         ************************************************************************************
         * Load the database values into the table and format the table cells
         ************************************************************************************
         */
        @Override
        protected void loadAndFormatData() {
            // Place the data into the table model along with the column names, set up the
            // editors and renderers for the table cells, set up the table grid lines, and
            // calculate the minimum width required to display the table information
            int totalWidth = setUpdatableCharacteristics(committedInfo.getData(), typeDefn.getColumnNamesUser(), committedInfo.getColumnOrder(), toolTips, true, true, true);
            // editor dialog
            if (editorDialog != null) {
                // Get the minimum width needed to display all columns, but no wider than the
                // display
                int width = Math.min(totalWidth + LAF_SCROLL_BAR_WIDTH, GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDisplayMode().getWidth());
                // Check if the editor's width is less than the minimum
                if (editorDialog.getTableWidth() < width) {
                    // Set the initial and preferred editor size
                    editorDialog.setTableWidth(width);
                    editorDialog.setPreferredSize(new Dimension(width, editorDialog.getPreferredSize().height));
                }
            }
            // Create the drop-down combo box for the column with the name 'data type' that
            // displays the available data types, including primitive types and the names of
            // tables that represent structures, and add a mouse listener to handle mouse click
            // events. Set up any command argument data type, argument name, enumeration,
            // minimum, and maximum groupings
            setUpDataTypeColumns(null, null);
            // Set up any minimum and maximum pairings (excluding those associated with command
            // argument groupings)
            setUpMinMaxColumns();
            // Create drop-down combo boxes that display the available sample rates for the
            // "Rate" column
            setUpSampleRateColumn();
            // Create drop-down combo boxes that display the available message ID names and
            // values
            setUpMsgNamesAndIDsColumn(null);
            // Create the mouse listener for the data type column
            createDataTypeColumnMouseListener();
        }

        /**
         ************************************************************************************
         * Override prepareRenderer to allow adjusting the background colors of table cells
         ************************************************************************************
         */
        @Override
        public Component prepareRenderer(TableCellRenderer renderer, int row, int column) {
            JComponent comp = (JComponent) super.prepareRenderer(renderer, row, column);
            // highlight colors override the invalid highlight color
            if (comp.getBackground() != ModifiableColorInfo.FOCUS_BACK.getColor() && comp.getBackground() != ModifiableColorInfo.SELECTED_BACK.getColor()) {
                // contain an item in the list
                if (!isCellValueFound(row, column)) {
                    // Change the cell's background color
                    comp.setBackground(ModifiableColorInfo.REQUIRED_BACK.getColor());
                } else // Check if this cell is protected from changes
                if (!isCellEditable(row, column)) {
                    // Change the cell's text and background colors
                    comp.setForeground(ModifiableColorInfo.PROTECTED_TEXT.getColor());
                    comp.setBackground(ModifiableColorInfo.PROTECTED_BACK.getColor());
                } else // variable
                if (variableNameIndex != -1 && getExpandedValueAt(table.convertRowIndexToModel(row), variableNameIndex).toString().matches(PAD_VARIABLE + "[0-9]+(?:\\[[0-9]+\\])?$")) {
                    // Change the cell's background color
                    comp.setBackground(ModifiableColorInfo.PADDING_BACK.getColor());
                }
            }
            return comp;
        }

        /**
         ************************************************************************************
         * Override the CcddJTableHandler method to handle right double click events on the
         * array size cells in order to show/hide the array index column and array member rows,
         * and to handle sorting of columns based on their input type
         ************************************************************************************
         */
        @Override
        protected void setTableSortable() {
            super.setTableSortable();
            // Get the table's row sorter
            TableRowSorter<?> sorter = (TableRowSorter<?>) getRowSorter();
            // Create a runnable object to be executed
            SwingUtilities.invokeLater(new Runnable() {

                /**
                 ****************************************************************************
                 * Execute after all pending Swing events are finished. This allows the number
                 * of viewable columns to catch up with the column model when a column is
                 * removed
                 ****************************************************************************
                 */
                @Override
                public void run() {
                    // Issue a table change event so that the new row is displayed properly
                    // when the array view is collapsed. Can't use tableModel here since it
                    // isn't set when the first call to this method is made
                    ((UndoableTableModel) table.getModel()).fireTableStructureChanged();
                }
            });
            // Check if the table has a sorter (i.e., has at least one row)
            if (sorter != null) {
                // filter
                if (sorter.getRowFilter() == null && rowFilter != null) {
                    // Apply the row filter that shows/hides the array members
                    sorter.setRowFilter(rowFilter);
                }
                // Step through each table column
                for (int column = 0; column < table.getModel().getColumnCount(); column++) {
                    // Get the input type format for this column
                    final InputTypeFormat inputFormat = typeDefn.getInputTypes()[column].getInputFormat();
                    // Add a column sort comparator
                    sorter.setComparator(column, new Comparator<String>() {

                        /**
                         ********************************************************************
                         * Override the comparison when sorting columns to account for the
                         * column's input type format. Note that macros aren't expanded when
                         * sorting. Though expansion provides an accurate sort, visually it's
                         * confusing since the macro values aren't readily apparent. For
                         * columns with a numeric input type that contain macros then initial
                         * numeric portion (if any) if used when sorting
                         ********************************************************************
                         */
                        @Override
                        public int compare(String cell1, String cell2) {
                            Integer result = 0;
                            // Check if either cell is empty
                            if (cell1.isEmpty() || cell2.isEmpty()) {
                                // Compare as text (alphabetically)
                                result = cell1.compareTo(cell2);
                            } else // Neither cell is empty
                            {
                                // type
                                switch(inputFormat) {
                                    case TEXT:
                                    case DATA_TYPE:
                                    case ENUMERATION:
                                    case PAGE_FORMAT:
                                    case VARIABLE_PATH:
                                    case BOOLEAN:
                                        // Compare as text (alphabetically)
                                        result = cell1.compareTo(cell2);
                                        break;
                                    default:
                                        // number
                                        if (cell1.matches(InputDataType.INTEGER.getInputMatch() + ".*") && cell2.matches(InputDataType.INTEGER.getInputMatch() + ".*")) {
                                            switch(inputFormat) {
                                                case INTEGER:
                                                    // Compare the two cell values as integers
                                                    result = Integer.compare(Integer.valueOf(cell1.replaceAll("(" + InputDataType.INTEGER.getInputMatch() + ").*", "$1")), Integer.valueOf(cell2.replaceAll("(" + InputDataType.INTEGER.getInputMatch() + ").*", "$1")));
                                                    break;
                                                case HEXADECIMAL:
                                                    // Compare the two cell values as integers
                                                    result = Integer.compare(Integer.decode(cell1.replaceAll("(" + InputDataType.HEXADECIMAL.getInputMatch() + ").*", "$1")), Integer.decode(cell2.replaceAll("(" + InputDataType.HEXADECIMAL.getInputMatch() + ").*", "$1")));
                                                    break;
                                                case FLOAT:
                                                case MINIMUM:
                                                case MAXIMUM:
                                                    // Compare the two cell values as floating
                                                    // points
                                                    result = Double.compare(Double.valueOf(cell1.replaceAll("(" + InputDataType.FLOAT.getInputMatch() + ").*", "$1")), Double.valueOf(cell2.replaceAll("(" + InputDataType.FLOAT.getInputMatch() + ").*", "$1")));
                                                    break;
                                                case RATE:
                                                    // Calculate the value of the cells'
                                                    // expressions, then compare the results as
                                                    // floating point values
                                                    result = Double.compare(CcddMathExpressionHandler.evaluateExpression(cell1), CcddMathExpressionHandler.evaluateExpression(cell2));
                                                    break;
                                                case ARRAY:
                                                    // Array sizes are in the format #<,#<...>.
                                                    // Each cell's array dimensions are first
                                                    // separated, then the first dimension is
                                                    // compared between the two cells, then the
                                                    // second, and so on until a mismatch is
                                                    // found; the sort is performed based on
                                                    // the mismatch (e.g., '1, 2' follows '1'
                                                    // when sorted in ascending order)
                                                    String[] dim1 = cell1.split("\\s*,\\s*");
                                                    String[] dim2 = cell2.split("\\s*,\\s*");
                                                    // same dimensions than the second
                                                    if (dim1.length == dim2.length) {
                                                        // long as there's no mismatch
                                                        for (int index = 0; index < dim1.length && result == 0; index++) {
                                                            // sizeof() call)
                                                            if (dim1[index].matches(InputDataType.INTEGER.getInputMatch()) && dim2[index].matches(InputDataType.INTEGER.getInputMatch())) {
                                                                // Compare the two array
                                                                // dimensions
                                                                result = Integer.compare(Integer.valueOf(dim1[index]), Integer.valueOf(dim2[index]));
                                                            } else // One or both dimension values
                                                            // isn't a number
                                                            {
                                                                // Compare as text
                                                                // (alphabetically)
                                                                result = dim1[index].compareTo(dim2[index]);
                                                            }
                                                        }
                                                    } else // fewer dimensions than the second
                                                    if (dim1.length < dim2.length) {
                                                        // long as there's no mismatch
                                                        for (int index = 0; index < dim1.length && result == 0; index++) {
                                                            // sizeof() call)
                                                            if (dim1[index].matches(InputDataType.INTEGER.getInputMatch()) && dim2[index].matches(InputDataType.INTEGER.getInputMatch())) {
                                                                // Compare the two array
                                                                // dimensions
                                                                result = Integer.compare(Integer.valueOf(dim1[index]), Integer.valueOf(dim2[index]));
                                                            } else // One or both dimension values
                                                            // isn't a number
                                                            {
                                                                // Compare as text
                                                                // (alphabetically)
                                                                result = dim1[index].compareTo(dim2[index]);
                                                            }
                                                        }
                                                        // dimension values are identical
                                                        if (result == 0) {
                                                            // Set the result to indicate the
                                                            // first cell comes before the
                                                            // second since the second has more
                                                            // dimensions
                                                            result = -1;
                                                        }
                                                    } else // The first array size has the more
                                                    // dimensions than the second
                                                    {
                                                        // long as there's no mismatch
                                                        for (int index = 0; index < dim2.length && result == 0; index++) {
                                                            // sizeof() call)
                                                            if (dim1[index].matches(InputDataType.INTEGER.getInputMatch()) && dim2[index].matches(InputDataType.INTEGER.getInputMatch())) {
                                                                // Compare the two array
                                                                // dimensions
                                                                result = Integer.compare(Integer.valueOf(dim1[index]), Integer.valueOf(dim2[index]));
                                                            } else // One or both dimension values
                                                            // isn't a number
                                                            {
                                                                // Compare as text
                                                                // (alphabetically)
                                                                result = dim1[index].compareTo(dim2[index]);
                                                            }
                                                        }
                                                        // dimension values are identical
                                                        if (result == 0) {
                                                            // Set the result to indicate the
                                                            // first cell comes after the
                                                            // second since the second has
                                                            // fewer dimensions
                                                            result = 1;
                                                        }
                                                    }
                                                    break;
                                                default:
                                                    break;
                                            }
                                        } else // One or both cells doesn't begin with a number (this
                                        // is the case if the cell begins with a macro or
                                        // sizeof() call)
                                        {
                                            // Compare as text (alphabetically)
                                            result = cell1.compareTo(cell2);
                                        }
                                }
                            }
                            return result;
                        }
                    });
                }
            }
        }

        /**
         ************************************************************************************
         * Move the selected row(s) in the specified direction if possible. Account for if the
         * selection or target is an array definition or member
         *
         * @param rowDelta
         *            row move direction (-1 for up, +1 for down)
         ************************************************************************************
         */
        private void adjustAndMoveSelectedRows(int rowDelta) {
            int modelStartRow;
            int modelEndRow;
            boolean isCanMove = false;
            // Set the selected start and end rows
            MoveCellSelection selected = new MoveCellSelection();
            // Set the selected start and end rows (model coordinates), and the direction to
            // move
            modelStartRow = selected.getStartRow();
            modelEndRow = selected.getEndRow();
            // Check if the selected row(s) can be moved in the desired direction
            if ((rowDelta < 0 && modelStartRow > 0) || (rowDelta > 0 && modelEndRow < tableModel.getRowCount() - 1)) {
                // Check if the table can display arrays
                if (isCanHaveArrays()) {
                    // While the start row references an array member
                    while (ArrayVariable.isArrayMember(getExpandedValueAt(modelStartRow, variableNameIndex))) {
                        // Decrement the start index to get to the array definition row
                        modelStartRow--;
                    }
                    // Check if the selected ending row references an array definition
                    if (!getExpandedValueAt(modelEndRow, arraySizeIndex).isEmpty() && !ArrayVariable.isArrayMember(getExpandedValueAt(modelEndRow, variableNameIndex))) {
                        // Increment the end row so that the members will be included below
                        modelEndRow++;
                    }
                    boolean isIncludeMember = false;
                    // model hasn't been reached
                    while (modelEndRow < tableModel.getRowCount() && ArrayVariable.isArrayMember(getExpandedValueAt(modelEndRow, variableNameIndex))) {
                        // Increment the end index to get to the end of the array
                        modelEndRow++;
                        isIncludeMember = true;
                    }
                    // Check if the ending row was adjusted to include an array member
                    if (isIncludeMember) {
                        // Decrement the row index since the row selection is inclusive
                        modelEndRow--;
                    }
                    // Check if the selected row(s) can be moved in the desired direction
                    if ((rowDelta < 0 && modelStartRow > 0) || (rowDelta > 0 && modelEndRow < tableModel.getRowCount() - 1)) {
                        // Get the array size column value for the target row
                        String arraySize = getExpandedValueAt((rowDelta < 0 ? modelStartRow : modelEndRow) + rowDelta, arraySizeIndex);
                        // Check if the array size is present on this row
                        if (!arraySize.isEmpty()) {
                            // Get the total number of array members
                            int totalSize = ArrayVariable.getNumMembersFromArraySize(arraySize);
                            // Adjust the number of rows to move based on the number of array
                            // members
                            rowDelta += totalSize * rowDelta;
                        }
                        // Set the flag to indicate the selected row(s) can be moved
                        isCanMove = true;
                    }
                } else // The table can't have an array
                {
                    // Set the flag to indicate the selected row(s) can be moved
                    isCanMove = true;
                }
                // Calculate the row that the selected row(s) will be moved to
                int modelToRow = modelStartRow + rowDelta;
                // Check if the selected row(s) can be moved
                if (isCanMove) {
                    // Move the row(s) in the specified direction and update the cell selection
                    performRowMove(modelStartRow, modelEndRow, modelToRow, selected, rowDelta);
                }
            }
        }

        /**
         ************************************************************************************
         * Override the CcddJTableHandler method for moving the selected row(s) up one row in
         * order to prevent moving a row within an array definition and its member rows;
         * instead skip past the array
         ************************************************************************************
         */
        @Override
        protected void moveRowUp() {
            // Move the selected row(s) up if possible
            adjustAndMoveSelectedRows(-1);
        }

        /**
         ************************************************************************************
         * Override the CcddJTableHandler method for moving the selected row(s) down one row in
         * order to prevent moving a row within an array definition and its member rows;
         * instead skip past the array
         ************************************************************************************
         */
        @Override
        protected void moveRowDown() {
            // Move the selected row(s) down if possible
            adjustAndMoveSelectedRows(1);
        }

        /**
         ************************************************************************************
         * Override the CcddJTableHandler method for moving the selected row(s) so that
         * adjustments can be made to the rates for any packed variables
         *
         * @param startRow
         *            selected starting row, in model coordinates
         *
         * @param endRow
         *            selected ending row, in model coordinates
         *
         * @param toRow
         *            target row to move the selected row(s) to, in model coordinates
         *
         * @param selected
         *            cell selection class
         *
         * @param rowDelta
         *            row move direction and magnitude
         ************************************************************************************
         */
        @Override
        protected void performRowMove(int startRow, int endRow, int toRow, MoveCellSelection selected, int rowDelta) {
            // Move the row(s)
            super.performRowMove(startRow, endRow, toRow, selected, rowDelta);
            // Check if this is a parent structure table
            if (tableInfo.isRootStructure()) {
                // Load the table data into a list
                List<Object[]> tableData = getTableDataList(false);
                // Adjust the rate for any packed variables, beginning with the lowest affected
                // row index
                setAllPackedVariableRates(tableData, Math.min(startRow, toRow), false);
                // Check if a rate value changed
                if (isRateChange) {
                    // Load the array of data into the table
                    loadDataArrayIntoTable(tableData.toArray(new Object[0][0]), true);
                }
            }
        }

        /**
         ************************************************************************************
         * Override the CcddJTableHandler method for putting data into a new row inserted below
         * the specified row in order to adjust the insertion index based on the presence of
         * array members
         *
         * @param targetRow
         *            index of the row in model coordinates below which to insert the new row
         *
         * @param data
         *            data to place in the inserted row
         *
         * @return The new row's index, in model coordinates, adjusted as needed to account for
         *         array member visibility
         ************************************************************************************
         */
        @Override
        protected int insertRowData(int targetRow, Object[] data) {
            // Check if table has rows, and has variable name and array size columns
            if (targetRow != -1 && isCanHaveArrays()) {
                // Get the array size value
                String arraySize = getExpandedValueAt(targetRow, arraySizeIndex);
                // (i.e., this is the array definition row)
                if (!arraySize.isEmpty() && !ArrayVariable.isArrayMember(getExpandedValueAt(targetRow, variableNameIndex))) {
                    // Adjust the row index past the array definition and member rows
                    targetRow += ArrayVariable.getNumMembersFromArraySize(arraySize);
                } else // Check if the array members are set to be displayed
                if (isShowArrayMembers) {
                    boolean isIndex = false;
                    // While the selection row is on an array member
                    while (targetRow < tableModel.getRowCount() && ArrayVariable.isArrayMember(getExpandedValueAt(targetRow, variableNameIndex))) {
                        // Skip the array member row
                        targetRow++;
                        isIndex = true;
                    }
                    // Check if an array member was skipped
                    if (isIndex) {
                        // Decrement the row index
                        targetRow--;
                    }
                }
            }
            // Insert the supplied data below the selected row
            return super.insertRowData(targetRow, data);
        }

        /**
         ************************************************************************************
         * Override the CcddJTableHandler method for removing a row from the table. Array
         * member rows are ignored unless the array definition row is also deleted; for this
         * case the entire array is removed
         *
         * @param tableData
         *            list containing the table data row arrays
         *
         * @param modelRow
         *            row to remove (model coordinates)
         *
         * @return The index of the row prior to the last deleted row's index
         ************************************************************************************
         */
        @Override
        protected int removeRow(List<Object[]> tableData, int modelRow) {
            boolean isArray = false;
            // Check if the table has array size and variable name columns
            if (isCanHaveArrays()) {
                // Extract the array size cell value
                String arraySize = getExpandedValueAt(modelRow, arraySizeIndex);
                // Check if an array size is present
                if (!arraySize.isEmpty()) {
                    // Set the flag indicating that an array row is being removed
                    isArray = true;
                    // Perform while this row is an array member
                    while (ArrayVariable.isArrayMember(tableData.get(modelRow)[variableNameIndex])) {
                        // Move the row index up
                        modelRow--;
                    }
                    // Get the row index of the last array member
                    int arrayRow = modelRow + ArrayVariable.getNumMembersFromArraySize(arraySize);
                    // Step through each member of the array
                    while (arrayRow >= modelRow) {
                        // Delete the row
                        tableData.remove(modelRow);
                        // Go to the next array member row to remove
                        arrayRow--;
                    }
                }
            }
            // Check if the row does not represent an array definition or member
            if (!isArray) {
                // Delete the row
                super.removeRow(tableData, modelRow);
            }
            // Adjust the rate for any packed variables, beginning with this row
            setAllPackedVariableRates(tableData, convertRowIndexToView(modelRow), false);
            return modelRow - 1;
        }

        /**
         ************************************************************************************
         * Override the CcddJTableHandler method for getting the special replacement character
         * when deleting the contents of a cell. Get the corresponding cell value from the
         * table's prototype
         *
         * @param row
         *            cell row index in model coordinates
         *
         * @param column
         *            cell column index in model coordinates
         *
         * @return The corresponding cell value from the tables' prototype
         ************************************************************************************
         */
        @Override
        protected String getSpecialReplacement(int row, int column) {
            return dbTable.queryTableCellValue(tableInfo.getPrototypeName(), committedInfo.getData()[row][primaryKeyIndex], typeDefn.getColumnNamesDatabase()[column], editorDialog);
        }

        /**
         ************************************************************************************
         * Override the CcddJTableHandler method for deleting a cell. Set the special character
         * flag to false if the table is a prototype - prototypes can't have an entry in the
         * custom values table so no special handling is needed for this case
         *
         * @param isReplaceSpecial
         *            false to replace the cell value with a blank; true to replace the cell
         *            contents with the prototype's corresponding cell value
         ************************************************************************************
         */
        @Override
        protected void deleteCell(boolean isReplaceSpecial) {
            super.deleteCell(isReplaceSpecial && !tableInfo.isPrototype());
        }

        /**
         ************************************************************************************
         * Adjust the starting row index to the next row during a paste (insert) operation. If
         * the insertion point falls within an array, skip to the row immediately following the
         * array's members
         *
         * @param startRow
         *            starting row index in view coordinates
         *
         * @return Starting row index, in model coordinates, at which to insert a new row
         ************************************************************************************
         */
        protected int adjustPasteStartRow(int startRow) {
            // Check if the starting row index references a valid row
            if (startRow >= 0 && startRow < getRowCount()) {
                // Convert the row index to model coordinates and adjust the starting row index
                // to the next row
                startRow = convertRowIndexToModel(startRow) + 1;
            } else // The starting index is not a valid row
            {
                // Set the starting row index to the end of the table
                startRow = tableModel.getRowCount();
            }
            // variable size and array name columns, and an array size is present
            if (startRow < tableModel.getRowCount() && isCanHaveArrays() && !getExpandedValueAt(startRow, arraySizeIndex).isEmpty()) {
                // been reached
                while (startRow < tableModel.getRowCount() && ArrayVariable.isArrayMember(getExpandedValueAt(startRow, variableNameIndex))) {
                    // Adjust the row index to the next row
                    startRow++;
                }
            }
            return startRow;
        }

        /**
         ************************************************************************************
         * Determine if a row insertion is required during a paste operation. Array member rows
         * are inserted automatically when an array is defined, so if an array member is being
         * inserted no row needs to be inserted by the paste operation
         *
         * @param index
         *            current index into the cell data array
         *
         * @param cellData
         *            array containing the cell data being inserted
         *
         * @param startColumn
         *            data insertion starting column index
         *
         * @param endColumn
         *            data insertion ending column index
         *
         * @return true if a row should be inserted; false otherwise
         ************************************************************************************
         */
        protected boolean isInsertRowRequired(int index, Object[] cellData, int startColumn, int endColumn) {
            boolean isNotArrayMember = true;
            // Get the variable name column index in view coordinates
            int variableNameIndexView = convertColumnIndexToView(variableNameIndex);
            // Step through the row of data being inserted
            for (int column = startColumn; column <= endColumn; column++, index++) {
                // Check if the column index matches the variable name column
                if (column == variableNameIndexView) {
                    // Check if the variable name cell has a value and is an array member
                    if (cellData[index] == null || ArrayVariable.isArrayMember(cellData[index])) {
                        // Set the flag to indicate a new row doesn't need to be inserted
                        isNotArrayMember = false;
                    }
                    // Stop searching
                    break;
                }
            }
            return isNotArrayMember;
        }

        /**
         ************************************************************************************
         * Override the paste method so that hidden rows (array members) are displayed prior to
         * pasting in new data
         ************************************************************************************
         */
        @Override
        protected boolean pasteData(Object[] cellData, int numColumns, boolean isInsert, boolean isAddIfNeeded, boolean startFirstColumn, boolean combineAsSingleEdit) {
            Boolean showMessage = true;
            // Check if the pasted data should be combined into a single edit operation
            if (combineAsSingleEdit) {
                // End any active edit sequence, then disable auto-ending so that the paste
                // operation can be handled as a single edit for undo/redo purposes
                getUndoManager().endEditSequence();
                getUndoHandler().setAutoEndEditSequence(false);
            }
            // Get the table data array
            List<Object[]> tableData = getTableDataList(false);
            // Calculate the number of rows to be pasted in
            int numRows = cellData.length / numColumns;
            // Initialize the starting row to the first row, which is the default if no row is
            // selected
            int startRow = 0;
            // Check if no row is selected
            if (getSelectedRow() == -1) {
                // Clear the column selection. The column selection can remain in effect after
                // an undo action that clears the row selection. It needs to be cleared if
                // invalid so that the starting column index is correctly calculated below
                getColumnModel().getSelectionModel().clearSelection();
            } else // A row is selected
            {
                // Determine the starting row for pasting the data based on the selected row
                startRow = convertRowIndexToModel(getSelectedRow()) + getSelectedRowCount() - 1;
            }
            // Determine the starting column and ending column for pasting the data. If no
            // column is selected then default to the first column. Data pasted outside of the
            // column range is ignored
            int startColumn = startFirstColumn ? 0 : Math.max(Math.max(getSelectedColumn(), 0), getSelectedColumn() + getSelectedColumnCount() - 1);
            int endColumn = startColumn + numColumns - 1;
            int endColumnSelect = Math.min(endColumn, getColumnCount() - 1);
            // members are hidden
            if (isCanHaveArrays() && !isShowArrayMembers) {
                // Show the array members. All rows must be visible in order for the pasted
                // data to be inserted correctly. The model and view row coordinates are the
                // same after expanding the array members. Note that this clears the row and
                // column selection
                showHideArrayMembers();
            }
            // Check if the data is to be inserted versus overwriting existing cells
            if (isInsert) {
                // Adjust the starting row index to the one after the selected row, and account
                // for hidden rows, if applicable
                startRow = adjustPasteStartRow(startRow);
            } else // contains no rows
            if (startRow == -1) {
                // Set the start row to the first row
                startRow = 0;
            }
            // Determine the ending row for pasting the data
            int endRow = startRow + numRows;
            // Clear the cell selection
            clearSelection();
            // Counters for the number of array member rows added (due to pasting in an array
            // definition) and the number of rows ignored (due to the first pasted row(s) being
            // an array member)
            int arrayRowsAdded = 0;
            int totalAddedRows = 0;
            int skippedRows = 0;
            boolean isIgnoreRow = false;
            // Step through each new row
            for (int index = 0, row = startRow; row < endRow && showMessage != null; row++) {
                boolean skipRow = false;
                // Calculate the row in the table data where the values are to be pasted. This
                // must be adjusted to account for pasting in arrays
                int adjustedRow = row + totalAddedRows - skippedRows;
                // member
                for (int column = startColumn; column <= endColumn && column < getColumnCount() && showMessage != null; column++) {
                    // Get the index into the cell data for this column
                    int tempIndex = index + column - startColumn;
                    // Check if rows were removed due to an array size reduction or removal
                    if (arrayRowsAdded < 0) {
                        // Set the flag indicating that this row of data is to be skipped and
                        // increment the skipped row counter
                        skipRow = true;
                        skippedRows++;
                        // Adjust the row counters so that all array member rows are skipped
                        arrayRowsAdded++;
                        totalAddedRows++;
                    } else // and that the value is an array member
                    if (variableNameIndex == convertColumnIndexToModel(column) && tempIndex < cellData.length && cellData[tempIndex] != null && ArrayVariable.isArrayMember(cellData[tempIndex])) {
                        // the array members
                        if (arrayRowsAdded > 0) {
                            // Move the row index back so that the array member data is pasted
                            // in the proper row
                            adjustedRow -= arrayRowsAdded;
                            arrayRowsAdded--;
                            totalAddedRows--;
                        } else // No rows were added for this array member
                        {
                            // member has no definition
                            if (row == startRow) {
                                // Set the flag indicating that array member rows are ignored
                                isIgnoreRow = true;
                            }
                            // Set the flag indicating that this row of data is to be skipped
                            // and increment the skipped row counter
                            skipRow = true;
                            skippedRows++;
                            // Update the cell data index so that this row is skipped
                            index += numColumns;
                        }
                        break;
                    }
                }
                // Check that this row is not to be ignored
                if (!skipRow) {
                    // Check if inserting is in effect and the cell value is null
                    if (isInsert && index < cellData.length && cellData[index] == null) {
                        // Replace the null with a blank
                        cellData[index] = "";
                    }
                    // Check if a row needs to be inserted to contain the cell data
                    if ((isInsert || (isAddIfNeeded && adjustedRow == tableData.size())) && isInsertRowRequired(index, cellData, startColumn, endColumn)) {
                        // Insert a row at the selection point
                        tableData.add(adjustedRow, getEmptyRow());
                    }
                    // Store the index into the array of data to be pasted
                    int indexSave = index;
                    // are pasted, then the cells that are not empty are pasted
                    for (int pass = 1; pass <= 2; pass++) {
                        // Check if this is the second pass through the row's columns
                        if (pass == 2) {
                            // Reset the index into the array of data to be pasted so that the
                            // non-blank cells can be processed
                            index = indexSave;
                        }
                        // Step through the columns, beginning at the one with the focus
                        for (int column = startColumn; column <= endColumn && showMessage != null; column++) {
                            // outside the bounds or protected then discard the value
                            if (column < getColumnCount()) {
                                // Convert the column coordinate from view to model
                                int columnModel = convertColumnIndexToModel(column);
                                // Get the value to be pasted into the cell, cleaning up the
                                // value if needed. If the number of cells to be filled exceeds
                                // the stored values then insert a blank. A null paste value
                                // indicates that the current cell's value won't be overwritten
                                Object newValue = index < cellData.length ? (cellData[index] != null ? cleanUpCellValue(cellData[index], adjustedRow, columnModel) : (isInsert ? "" : null)) : "";
                                // alterable
                                if (newValue != null && ((pass == 1 && newValue.toString().isEmpty()) || (pass == 2 && !newValue.toString().isEmpty())) && isDataAlterable(tableData.get(adjustedRow), adjustedRow, columnModel)) {
                                    // Get the original cell value
                                    Object oldValue = tableData.get(adjustedRow)[columnModel];
                                    // being inserted, that the value isn't blank
                                    if (!oldValue.equals(newValue) && !(isInsert && newValue.toString().isEmpty())) {
                                        // Insert the value into the cell
                                        tableData.get(adjustedRow)[columnModel] = newValue;
                                        // Get the number of rows in the table prior to
                                        // inserting the new value
                                        int previousRows = tableData.size();
                                        // Validate the new cell contents
                                        showMessage = validateCellContent(tableData, adjustedRow, columnModel, oldValue, newValue, showMessage, cellData.length > 1);
                                        // following an invalid input
                                        if (showMessage == null) {
                                            // Stop pasting data
                                            continue;
                                        }
                                        // Get the number of rows added due to pasting in the
                                        // new value. This is non-zero if an array definition
                                        // is pasted in or if an existing array's size is
                                        // altered
                                        int deltaRows = tableData.size() - previousRows;
                                        // Check if the row count changed
                                        if (deltaRows > 0) {
                                            // Store the number of added/deleted rows and
                                            // update the total number of added/deleted rows
                                            arrayRowsAdded = deltaRows;
                                            totalAddedRows += arrayRowsAdded;
                                        }
                                    }
                                }
                            }
                            // Increment the index to the next value to paste
                            index++;
                        }
                    }
                }
            }
            // Check if the user hasn't selected the Cancel button following an invalid input
            if (showMessage != null) {
                // Load the array of data into the table
                loadDataArrayIntoTable(tableData.toArray(new Object[0][0]), true);
                // Check if automatic edit sequence ending is in effect
                if (getUndoHandler().isAutoEndEditSequence()) {
                    // Flag the end of the editing sequence for undo/redo purposes
                    getUndoManager().endEditSequence();
                }
                // Check if there are rows left to be selected
                if (endRow - 1 - skippedRows > 0) {
                    // Select all of the rows into which the data was pasted
                    setRowSelectionInterval(startRow, endRow - 1 - skippedRows);
                }
                // Select all of the columns into which the data was pasted
                setColumnSelectionInterval(startColumn, endColumnSelect);
                // Select the pasted cells and force the table to be redrawn so that the
                // changes are displayed
                setSelectedCells(startRow, endRow - 1, startColumn, endColumnSelect);
                repaint();
                // Check if any rows were ignored
                if (isIgnoreRow) {
                    // Inform the user how many rows were skipped
                    new CcddDialogHandler().showMessageDialog(editorDialog, "<html><b>" + skippedRows + " array member row(s) ignored due " + "to missing array definition(s)", "Rows Ignored", JOptionPane.WARNING_MESSAGE, DialogOption.OK_OPTION);
                }
            }
            // Set the flag that indicates the last edited cell's content is valid (if an
            // invalid input set the flag to false then it can prevent closing the editor)
            setLastCellValid(true);
            // Check if the pasted data should be combined into a single edit operation
            if (combineAsSingleEdit) {
                // Re-enable auto-ending of the edit sequence and end the sequence. The pasted
                // data can be removed with a single undo if desired
                getUndoHandler().setAutoEndEditSequence(true);
                getUndoManager().endEditSequence();
            }
            return showMessage == null;
        }

        /**
         ************************************************************************************
         * Override the method for cleaning-up of the cell value. The default is to remove any
         * leading and trailing white space characters. This method skips removal of white
         * space characters for cells having input types that allow it
         *
         * @param value
         *            new cell value
         *
         * @param row
         *            table row, model coordinates
         *
         * @param column
         *            table column, model coordinates
         *
         * @return Cell value following clean-up
         ************************************************************************************
         */
        @Override
        protected Object cleanUpCellValue(Object value, int row, int column) {
            // string (i.e., it isn't boolean, etc.)
            if (!table.isEditing() && value instanceof String) {
                // Get the input type for this column
                InputDataType inputType = typeDefn.getInputTypes()[column];
                // space characters
                if (inputType != InputDataType.TEXT_WHT_SPC && inputType != InputDataType.TEXT_MULTI_WHT_SPC) {
                    // Perform the default clean-up (remove leading and trailing white space
                    // characters)
                    value = super.cleanUpCellValue(value, row, column);
                }
            }
            return value;
        }

        /**
         ************************************************************************************
         * Handle a change to the table's content
         ************************************************************************************
         */
        @Override
        protected void processTableContentChange() {
            // and/or value change)
            if (editorDialog != null) {
                // Update the change indicator for the table
                editorDialog.updateChangeIndicator(CcddTableEditorHandler.this);
            }
        }
    };
    // Place the table into a scroll pane
    JScrollPane scrollPane = new JScrollPane(table);
    // Disable storage of edit operations during table creation
    table.getUndoHandler().setAllowUndo(false);
    // Set common table parameters and characteristics
    table.setFixedCharacteristics(scrollPane, tableInfo.isPrototype(), ListSelectionModel.MULTIPLE_INTERVAL_SELECTION, TableSelectionMode.SELECT_BY_CELL, true, ModifiableColorInfo.TABLE_BACK.getColor(), true, true, ModifiableFontInfo.DATA_TABLE_CELL.getFont(), true);
    // Get a reference to the table model to shorten later calls
    tableModel = (UndoableTableModel) table.getModel();
    // Re-enable storage of edit operations
    table.getUndoHandler().setAllowUndo(true);
    // Set the reference to the editor's data field handler in the undo handler so that data
    // field value changes can be undone/redone correctly
    table.getUndoHandler().setFieldHandler(tableInfo.getFieldHandler());
    // Set the undo/redo manager and handler for the description and data field values
    setEditPanelUndo(table.getUndoManager(), table.getUndoHandler());
    // Set the mouse listener to expand and collapse arrays
    setArrayExpansionListener();
    // Get the variable path separators and create the variable path column content, if present
    updateVariablePaths();
    // change)
    if (editorDialog != null) {
        // Create the input field panel to contain the table editor
        createDescAndDataFieldPanel(editorDialog, scrollPane, tableInfo.getProtoVariableName(), tableInfo.getDescription(), tableInfo.getFieldHandler());
        // Set the dialog name so that this dialog can be recognized as being open by the table
        // selection dialog, and the JTable name so that table change events can be identified
        // with this table
        setTableName();
        // Store the current data field information in the event an undo/redo operation occurs
        storeCurrentFieldInformation();
    }
}
Also used : CCDDException(CCDD.CcddClassesDataTable.CCDDException) Matcher(java.util.regex.Matcher) DefaultHighlighter(javax.swing.text.DefaultHighlighter) JTextComponent(javax.swing.text.JTextComponent) InputTypeFormat(CCDD.CcddConstants.InputTypeFormat) Comparator(java.util.Comparator) List(java.util.List) ArrayList(java.util.ArrayList) TableCellEditor(javax.swing.table.TableCellEditor) Component(java.awt.Component) JComponent(javax.swing.JComponent) JTextComponent(javax.swing.text.JTextComponent) DefaultHighlightPainter(javax.swing.text.DefaultHighlighter.DefaultHighlightPainter) TableRowSorter(javax.swing.table.TableRowSorter) InputDataType(CCDD.CcddConstants.InputDataType) JScrollPane(javax.swing.JScrollPane) Pattern(java.util.regex.Pattern) TableCellRenderer(javax.swing.table.TableCellRenderer) MouseEvent(java.awt.event.MouseEvent) JComponent(javax.swing.JComponent) Point(java.awt.Point) Dimension(java.awt.Dimension) Point(java.awt.Point) DefaultHighlightPainter(javax.swing.text.DefaultHighlighter.DefaultHighlightPainter) AssociatedColumns(CCDD.CcddClassesDataTable.AssociatedColumns) UndoableTableModel(CCDD.CcddUndoHandler.UndoableTableModel) MinMaxPair(CCDD.CcddClassesDataTable.MinMaxPair) BadLocationException(javax.swing.text.BadLocationException)

Example 5 with InputDataType

use of CCDD.CcddConstants.InputDataType in project CCDD by nasa.

the class CcddTableTypeHandler method createTypeDefinition.

/**
 ********************************************************************************************
 * Create a new table type definition. If the definition exists then replace it with the new
 * one
 *
 * @param typeName
 *            table type name
 *
 * @param typeData
 *            array of table type data
 *
 * @param description
 *            table type description
 *
 * @return Reference to the type definition created
 ********************************************************************************************
 */
protected TypeDefinition createTypeDefinition(String typeName, Object[][] typeData, String description) {
    // Get the reference to the type definition
    TypeDefinition typeDefn = getTypeDefinition(typeName);
    // Check if this type already exists
    if (typeDefn != null) {
        // Delete the type definition
        typeDefinitions.remove(typeDefn);
    }
    // Create a new type definition and add it to the list
    typeDefn = new TypeDefinition(typeName);
    // Add the primary key and row index column definitions
    typeDefn.addColumn(DefaultColumn.PRIMARY_KEY.ordinal(), DefaultColumn.PRIMARY_KEY.getDbName(), DefaultColumn.PRIMARY_KEY.getName(), description, DefaultColumn.PRIMARY_KEY.getInputType(), DefaultColumn.PRIMARY_KEY.isRowValueUnique(), DefaultColumn.PRIMARY_KEY.isInputRequired(), DefaultColumn.PRIMARY_KEY.isStructureAllowed(), DefaultColumn.PRIMARY_KEY.isPointerAllowed());
    typeDefn.addColumn(DefaultColumn.ROW_INDEX.ordinal(), DefaultColumn.ROW_INDEX.getDbName(), DefaultColumn.ROW_INDEX.getName(), DefaultColumn.ROW_INDEX.getDescription(), DefaultColumn.ROW_INDEX.getInputType(), DefaultColumn.ROW_INDEX.isRowValueUnique(), DefaultColumn.ROW_INDEX.isInputRequired(), DefaultColumn.PRIMARY_KEY.isStructureAllowed(), DefaultColumn.PRIMARY_KEY.isPointerAllowed());
    // Step through each row in the type definition data
    for (int row = 0; row < typeData.length; row++) {
        // Get the InputDataType for this column
        InputDataType inputType = InputDataType.getInputTypeByName(typeData[row][TableTypeEditorColumnInfo.INPUT_TYPE.ordinal()].toString());
        // Add the column names, description, input type, and flags to the type definition
        typeDefn.addColumn(row, (String) typeData[row][TableTypeEditorColumnInfo.NAME.ordinal()], (String) typeData[row][TableTypeEditorColumnInfo.NAME.ordinal()], (String) typeData[row][TableTypeEditorColumnInfo.DESCRIPTION.ordinal()], inputType, (Boolean) typeData[row][TableTypeEditorColumnInfo.UNIQUE.ordinal()], (Boolean) typeData[row][TableTypeEditorColumnInfo.REQUIRED.ordinal()], (Boolean) typeData[row][TableTypeEditorColumnInfo.STRUCTURE_ALLOWED.ordinal()], (Boolean) typeData[row][TableTypeEditorColumnInfo.POINTER_ALLOWED.ordinal()]);
    }
    // Convert the visible column names to their database equivalents
    typeDefn.setColumnNamesDatabase();
    return typeDefn;
}
Also used : InputDataType(CCDD.CcddConstants.InputDataType) TableTypeDefinition(CCDD.CcddClassesDataTable.TableTypeDefinition)

Aggregations

InputDataType (CCDD.CcddConstants.InputDataType)6 Component (java.awt.Component)4 ArrayList (java.util.ArrayList)4 CCDDException (CCDD.CcddClassesDataTable.CCDDException)3 Point (java.awt.Point)3 JComponent (javax.swing.JComponent)3 JScrollPane (javax.swing.JScrollPane)3 TableCellRenderer (javax.swing.table.TableCellRenderer)3 FieldInformation (CCDD.CcddClassesDataTable.FieldInformation)2 TypeDefinition (CCDD.CcddTableTypeHandler.TypeDefinition)2 MouseEvent (java.awt.event.MouseEvent)2 JTextComponent (javax.swing.text.JTextComponent)2 AssociatedColumns (CCDD.CcddClassesDataTable.AssociatedColumns)1 MinMaxPair (CCDD.CcddClassesDataTable.MinMaxPair)1 TableTypeDefinition (CCDD.CcddClassesDataTable.TableTypeDefinition)1 ArrowFocusOption (CCDD.CcddConstants.ArrowFocusOption)1 InputTypeFormat (CCDD.CcddConstants.InputTypeFormat)1 UndoableCellSelection (CCDD.CcddUndoHandler.UndoableCellSelection)1 UndoableTableModel (CCDD.CcddUndoHandler.UndoableTableModel)1 Dimension (java.awt.Dimension)1