ArduinoTableAttr.java

/*******************************************************************************
 * jArduino: Arduino C++ Code Generation From Java
 * Copyright 2020 Tony Washer
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 ******************************************************************************/
package net.sourceforge.jarduino.gui;

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

import javax.swing.JComponent;
import javax.swing.JTable;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel;

import net.sourceforge.jarduino.gui.ArduinoPanelMeta.ArduinoConfigMode;
import net.sourceforge.jarduino.message.ArduinoAttribute;
import net.sourceforge.jarduino.message.ArduinoAttribute.ArduinoAttrClass;
import net.sourceforge.jarduino.message.ArduinoAttribute.ArduinoAttrObject;
import net.sourceforge.jarduino.message.ArduinoMessage;
import net.sourceforge.jarduino.message.ArduinoNode;
import net.sourceforge.jarduino.message.ArduinoSignal;
import net.sourceforge.jarduino.message.ArduinoSystem;

/**
 * Table for Attributes.
 */
public class ArduinoTableAttr {
    /**
     * The attributes.
     */
    private final List<ArduinoAttribute> theAttributes;

    /**
     * The receivers.
     */
    private final Map<Integer, ArduinoNode> theReceivers;

    /**
     * The values.
     */
    private final List<Object> theValues;

    /**
     * The Attributes table.
     */
    private final JTable theTable;

    /**
     * The Attributes table model.
     */
    private final ArduinoAttrModel theModel;

    /**
     * The Parents column.
     */
    private final TableColumn theParentCol;

    /**
     * Is the parent column visible?
     */
    private boolean parentsVisible;

    /**
     * The system.
     */
    private ArduinoSystem theSystem;

    /**
     * The mode.
     */
    private ArduinoConfigMode theMode;

    /**
     * The selected Object.
     */
    private ArduinoAttrObject theSelected;

    /**
     * Constructor.
     */
    ArduinoTableAttr() {
        /* Create lists and maps */
        theAttributes = new ArrayList<>();
        theValues = new ArrayList<>();
        theReceivers = new HashMap<>();

        /* Create the table */
        theTable = new JTable();
        theTable.setAutoCreateRowSorter(true);

        /* Create the table model */
        theModel = new ArduinoAttrModel();
        theTable.setModel(theModel);

        /* Access the parent column */
        final TableColumnModel myColModel = theTable.getColumnModel();
        theParentCol = myColModel.getColumn(ArduinoAttrModel.COL_RECEIVER);
        parentsVisible = true;
    }

    /**
     * Obtain the component.
     * @return the component
     */
    JComponent getComponent() {
        return theTable;
    }

    /**
     * Configure the table.
     *
     * @param pSystem the system
     * @param pMode the mode
     * @param pSelected the selected object
     */
    void configureTable(final ArduinoSystem pSystem,
                        final ArduinoConfigMode pMode,
                        final ArduinoAttrObject pSelected) {
        /* store the details */
        theSystem = pSystem;
        theMode = pMode;
        theSelected = pSelected;

        /* Refresh the table model */
        theModel.refresh();

        /* Determine whether parents column should be visible */
        final boolean bParentsVisible = !theReceivers.isEmpty();

        /* If we should modify the columns */
        if (bParentsVisible != parentsVisible) {
            /* Access the table column model */
            final TableColumnModel myColModel = theTable.getColumnModel();

            /* If we need to remove the column */
            if (parentsVisible) {
                myColModel.removeColumn(theParentCol);

                /* else restore the column and move to correct position */
            } else {
                myColModel.addColumn(theParentCol);
                myColModel.moveColumn(ArduinoAttrModel.COL_VALUE, ArduinoAttrModel.COL_RECEIVER);
            }

            /* Flip the flag */
            parentsVisible = !parentsVisible;
        }
    }

    /**
     * Table Model.
     */
    private class ArduinoAttrModel
            extends AbstractTableModel {
        /**
         * Serial Id.
         */
        private static final long serialVersionUID = -7093822082474013905L;

        /**
         * The attribute column.
         */
        private static final int COL_ATTR = 0;

        /**
         * The receiver column.
         */
        private static final int COL_RECEIVER = COL_ATTR + 1;

        /**
         * The value column.
         */
        private static final int COL_VALUE = COL_RECEIVER + 1;

        /**
         * Constructor.
         */
        ArduinoAttrModel() {
        }

        /**
         * Refresh the model.
         */
        void refresh() {
            /* Clear the lists/maps */
            theAttributes.clear();
            theValues.clear();
            theReceivers.clear();

            /* Loop through the attributes */
            for (ArduinoAttribute myAttr : theSystem.getAttributes()) {
                /* If the attribute is relevant */
                if (isRelevant(myAttr)) {
                    /* If this is a relation attribute */
                    if (myAttr.getAttrClass().isRelation()) {
                        /* Load values associated with it */
                        loadRelationAttribute(myAttr);
                    } else {
                        /* Load values associated with it */
                        loadAttribute(myAttr);
                    }
                }
            }

            /* Refresh the table */
            fireTableDataChanged();
        }

        /**
         * loadAttribute.
         * @param pAttr the attribute to load
         */
        private void loadAttribute(final ArduinoAttribute pAttr) {
            /* Add the item and its value to the list */
            theAttributes.add(pAttr);
            theValues.add(theSelected.getAttrValue(pAttr));
        }

        /**
         * loadAttribute.
         * @param pAttr the attribute to load
         */
        private void loadRelationAttribute(final ArduinoAttribute pAttr) {
            /* The index */
            int myIndex = theAttributes.size();

            /* Loop through the nodes */
            for (ArduinoNode myNode : theSystem.getNodes()) {
                /* If the node receives the signal/message */
                if (nodeReceivesObject(myNode)) {
                    /* Access the value and store into map */
                    theAttributes.add(pAttr);
                    theReceivers.put(myIndex++, myNode);
                    theValues.add(myNode.getRelationValue(pAttr, theSelected));
                }
            }
        }

        /**
         * is the attribute relevant in this mode?
         * @param pAttr the attribute to load
         * @return true/false
         */
        private boolean isRelevant(final ArduinoAttribute pAttr) {
            final ArduinoAttrClass myClass = pAttr.getAttrClass();
            switch (theMode) {
                case SYSTEM:
                    return myClass == ArduinoAttrClass.SYSTEM;
                case NODE:
                    return myClass == ArduinoAttrClass.NODE;
                case MESSAGE:
                    return myClass == ArduinoAttrClass.MESSAGE
                            || myClass == ArduinoAttrClass.NODE2MSG;
                case SIGNAL:
                    return myClass == ArduinoAttrClass.SIGNAL
                            || myClass == ArduinoAttrClass.NODE2SIGNAL;
                default:
                    return false;
            }
        }

        /**
         * is the node a receiver of the selected object.
         * @param pNode the node
         * @return true/false
         */
        private boolean nodeReceivesObject(final ArduinoNode pNode) {
            /* If the object is a message */
            if (theSelected instanceof ArduinoMessage) {
                return pNode.receivesMessage((ArduinoMessage) theSelected);
            } else if (theSelected instanceof ArduinoSignal) {
                return pNode.receivesSignal((ArduinoSignal) theSelected);
            }
            return false;
        }

        @Override
        public int getRowCount() {
            return theAttributes.size();
        }

        @Override
        public String getColumnName(final int pColIndex) {
            switch (pColIndex) {
                case COL_ATTR:
                    return "Attribute";
                case COL_RECEIVER:
                    return "Receiver";
                case COL_VALUE:
                    return "Value";
                default:
                    return null;
            }
        }

        @Override
        public int getColumnCount() {
            return COL_VALUE + 1;
        }

        @Override
        public boolean isCellEditable(final int pRowIndex,
                                      final int pColIndex) {
            return false;
        }

        @Override
        public Object getValueAt(final int pRowIndex,
                                 final int pColIndex) {
            switch (pColIndex) {
                case COL_ATTR:
                    return theAttributes.get(pRowIndex).getName();
                case COL_RECEIVER:
                    return theReceivers.get(pRowIndex);
                case COL_VALUE:
                    return theValues.get(pRowIndex);
                default:
                    return null;
            }
        }
    }
}