org.o3project.optsdn.don.frame.NeFrame.java Source code

Java tutorial

Introduction

Here is the source code for org.o3project.optsdn.don.frame.NeFrame.java

Source

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

package org.o3project.optsdn.don.frame;

import org.apache.commons.lang3.math.NumberUtils;
import org.jboss.netty.channel.ChannelFuture;
import org.o3project.optsdn.don.nwc.Port;
import org.o3project.optsdn.don.openflow.ConnectorToOpenFlowController;
import org.o3project.optsdn.don.util.AntiAliasingPanel;
import org.o3project.optsdn.don.util.Config;
import org.o3project.optsdn.don.util.Constants;
import org.projectfloodlight.openflow.protocol.OFVersion;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Stroke;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLayeredPane;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextArea;
import javax.swing.SwingConstants;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableColumn;

/**
 * NE frame.
 */
public class NeFrame extends JFrame {
    public static Logger logger = LoggerFactory.getLogger(NeFrame.class);

    private static final int TABLE_HEIGHT = 100;
    private static final String OFPORT_NOTFOUND = "E";

    private String neTitle;
    private List<Port> portList;
    private Map<Integer, Port> portMap;
    private List<List<Port>> linkList;
    private List<List<Port>> omsConnectionInfoList;
    private String ptId;
    private long dpid;
    private JButton displayNeStatusButton;
    private JButton connectButton;

    /* Flowmod status label. */
    private JTextArea flowmodStatusLabel;

    /*
     * The List for new links.
     * (Remove a link from the List after 10 seconds.)
     */
    private List<List<Integer>> linkListNew;

    /*
     * The Set for old links.
     * (Removed link from the Set of new links)
     */
    private Set<List<Integer>> linkSetOld;

    private final Color ptColor;
    private final Color neOduColor;
    private final Color neOchColor;
    private final Color oduXcColor;
    private final Color lambdaSwColor;
    private final Color xcColor;
    private final Color flowmodLinkOldColor;
    private final Color flowmodLinkNewColor;

    /* Information Model ID Columns. */
    private enum InformationModelIdCols {
        OF_PORT("Port"), INFORMATION_MODEL_ID("Information Model ID");

        private String name;

        private InformationModelIdCols(String name) {
            this.name = name;
        }

        public String getName() {
            return name;
        }
    }

    /* Information Model ID Column names. */
    private static List<String> INFORMATION_MODEL_ID_COLNAME_LIST;

    static {
        INFORMATION_MODEL_ID_COLNAME_LIST = new ArrayList<String>();
        for (InformationModelIdCols col : InformationModelIdCols.values()) {
            INFORMATION_MODEL_ID_COLNAME_LIST.add(col.getName());
        }
    }

    /* OMS connection informations Columns. */
    private enum OmsConnectionInfoCols {
        OF_PORT("Port"), CONNECTED_TO("Connected to");

        private String name;

        private OmsConnectionInfoCols(String name) {
            this.name = name;
        }

        public String getName() {
            return name;
        }
    }

    /* Information Model ID Column names. */
    private static List<String> OMS_CONNECTION_INFO_COLNAME_LIST;

    static {
        OMS_CONNECTION_INFO_COLNAME_LIST = new ArrayList<String>();
        for (OmsConnectionInfoCols col : OmsConnectionInfoCols.values()) {
            OMS_CONNECTION_INFO_COLNAME_LIST.add(col.getName());
        }
    }

    /**
     * Constructor.
     * 
     * @param neTitle                NE frame title
     * @param portList               Ports
     * @param linkList               Links
     * @param omsConnectionInfoList  OMS connections
     * @param ptId                   PT ID
     * @param dpid                   Datapath ID
     * @param displayNeStatusButton  NE button
     */
    public NeFrame(String neTitle, List<Port> portList, List<List<Port>> linkList,
            List<List<Port>> omsConnectionInfoList, String ptId, long dpid, JButton displayNeStatusButton) {

        this.neTitle = neTitle;

        this.portList = portList;
        portMap = new HashMap<Integer, Port>();
        for (Port port : portList) {
            Integer openFlowPortId = port.getOpenFlowPortId();
            if (openFlowPortId == null) {
                continue;
            }
            portMap.put(openFlowPortId, port);
        }

        this.flowmodStatusLabel = new JTextArea();
        this.linkListNew = new ArrayList<List<Integer>>();
        this.linkSetOld = new HashSet<List<Integer>>();

        this.linkList = linkList;
        this.omsConnectionInfoList = omsConnectionInfoList;
        this.ptId = ptId;
        this.dpid = dpid;
        this.displayNeStatusButton = displayNeStatusButton;

        ptColor = getPropertyColor("ptColorR", "ptColorG", "ptColorB", "ptColorA", new Color(204, 236, 255, 255));

        neOduColor = getPropertyColor("neOduColorR", "neOduColorG", "neOduColorB", "neOduColorA",
                new Color(204, 255, 204, 255));

        neOchColor = getPropertyColor("neOchColorR", "neOchColorG", "neOchColorB", "neOchColorA",
                new Color(255, 230, 153, 255));

        oduXcColor = getPropertyColor("oduXcColorR", "oduXcColorG", "oduXcColorB", "oduXcColorA",
                new Color(255, 255, 255, 255));

        lambdaSwColor = getPropertyColor("lambdaSwColorR", "lambdaSwColorG", "lambdaSwColorB", "lambdaSwColorA",
                new Color(255, 255, 255, 255));

        xcColor = getPropertyColor("xcColorR", "xcColorG", "xcColorB", "xcColorA", new Color(0, 0, 255, 255));

        flowmodLinkOldColor = getPropertyColor("flowmodLinkOldColorR", "flowmodLinkOldColorG",
                "flowmodLinkOldColorB", "flowmodLinkOldColorA", new Color(128, 128, 128, 126));

        flowmodLinkNewColor = getPropertyColor("flowmodLinkNewColorR", "flowmodLinkNewColorG",
                "flowmodLinkNewColorB", "flowmodLinkNewColorA", new Color(255, 0, 0, 255));

        createNeFrame();
    }

    /**
     * Get Flowmod status label.
     * 
     * @return the Flowmod status label
     */
    public JTextArea getFlowmodStatusLabel() {
        return flowmodStatusLabel;
    }

    /**
     * Get the List for new links.
     * 
     * @return The List for new links
     */
    public List<List<Integer>> getLinkListNew() {
        return linkListNew;
    }

    /**
     * Get the Set for old links.
     * 
     * @return The Set for old links
     */
    public Set<List<Integer>> getLinkSetOld() {
        return linkSetOld;
    }

    /**
     * Get property color.
     * 
     * @param keyR         R Property Key
     * @param keyG         G Property Key
     * @param keyB         B Property Key
     * @param keyA         A Property Key
     * @param defaultColor The Default Color
     * @return The Property Color
     */
    private Color getPropertyColor(String keyR, String keyG, String keyB, String keyA, Color defaultColor) {

        String propertyR = Config.getProperty(keyR);
        String propertyG = Config.getProperty(keyG);
        String propertyB = Config.getProperty(keyB);
        String propertyA = Config.getProperty(keyA);

        if (NumberUtils.isNumber(propertyR) && NumberUtils.isNumber(propertyG) && NumberUtils.isNumber(propertyB)
                && NumberUtils.isNumber(propertyA)) {

            return new Color(Integer.valueOf(propertyR), Integer.valueOf(propertyG), Integer.valueOf(propertyB),
                    Integer.valueOf(propertyA));

        } else {
            return defaultColor;
        }
    }

    /**
     * Create a NE frame.
     */
    private void createNeFrame() {
        setTitle(neTitle);
        add(createNePanel());
        pack();
    }

    /**
     * Create a NE frame panel.
     * 
     * @return The NE frame panel
     */
    private JPanel createNePanel() {
        JPanel nePanel = new JPanel();
        nePanel.setBackground(Color.WHITE);
        nePanel.setLayout(new BorderLayout());

        JPanel centerPanel = new JPanel();
        centerPanel.setLayout(new BoxLayout(centerPanel, BoxLayout.Y_AXIS));
        centerPanel.add(createDrawingNePane());
        centerPanel.add(createOmsConnectionInfoPane());
        centerPanel.add(createInformationModelIdListPane(portList));

        nePanel.add(centerPanel, BorderLayout.CENTER);
        nePanel.add(createConnectButtonPanel(), BorderLayout.SOUTH);

        return nePanel;
    }

    /**
     * Create a pane that draws initial NE display.
     * 
     * @return The pane that draws initial NE display
     */
    private JScrollPane createDrawingNePane() {
        JPanel panel = new JPanel();
        panel.setLayout(new BorderLayout());
        panel.add(createFlowmodStatusPanel(), BorderLayout.NORTH);
        panel.add(new DrawingNePanel(), BorderLayout.CENTER);

        JScrollPane pane = new JScrollPane();
        pane.getViewport().setView(panel);
        return pane;
    }

    /**
     * Create a panel that displays Flowmod message.
     * 
     * @return The panel that displays Flowmod message
     */
    private JPanel createFlowmodStatusPanel() {
        JPanel flowmodStatusPanel = new JPanel();
        flowmodStatusPanel.setBackground(Color.WHITE);

        // Set maximum text size for the Flowmod message
        flowmodStatusLabel
                .setText("[match] in_port=65509, odu_sigtype=11, odu_sigid={TPN=1, tslen=8, tsmap=11111111}\n"
                        + "[actions] output=65509, odu_sigtype=11, odu_sigid={TPN=1, tslen=8, tsmap=11111111}");
        flowmodStatusPanel.add(flowmodStatusLabel);
        flowmodStatusPanel.setPreferredSize(flowmodStatusPanel.getPreferredSize());

        // Set default text for the Flowmod message
        flowmodStatusLabel.setText(Constants.FLOWMOD_INFO_TEXT_DEFAULT);
        return flowmodStatusPanel;
    }

    /**
     * Create a pane that displays OMS connection information.
     * 
     * @return The pane that displays OMS connection information
     */
    private JScrollPane createOmsConnectionInfoPane() {
        Object[] header = OMS_CONNECTION_INFO_COLNAME_LIST.toArray();

        String[][] rowDataList = new String[omsConnectionInfoList.size()][header.length];
        for (int i = 0; i < rowDataList.length; i++) {
            List<Port> omsConnectionInfo = omsConnectionInfoList.get(i);

            Port port = omsConnectionInfo.get(0);
            int ofPortIndex = OMS_CONNECTION_INFO_COLNAME_LIST.indexOf(OmsConnectionInfoCols.OF_PORT.getName());
            Integer openFlowPortId1 = port.getOpenFlowPortId();
            String ofPortValue1;
            if (openFlowPortId1 == null) {
                ofPortValue1 = OFPORT_NOTFOUND;
            } else {
                ofPortValue1 = String.valueOf(openFlowPortId1);
            }
            rowDataList[i][ofPortIndex] = ofPortValue1;

            Port port2 = omsConnectionInfo.get(1);
            int connectedToIndex = OMS_CONNECTION_INFO_COLNAME_LIST
                    .indexOf(OmsConnectionInfoCols.CONNECTED_TO.getName());
            Integer openFlowPortId2 = port2.getOpenFlowPortId();
            String ofPortValue2;
            if (openFlowPortId2 == null) {
                ofPortValue2 = OFPORT_NOTFOUND;
            } else {
                ofPortValue2 = String.valueOf(openFlowPortId2);
            }
            rowDataList[i][connectedToIndex] = port2.getNeId() + ", " + ofPortValue2;
        }

        JTable connectedTable = new JTable(rowDataList, header);

        connectedTable.getTableHeader().setBackground(Color.WHITE);

        DefaultTableCellRenderer renderer = new DefaultTableCellRenderer();
        renderer.setHorizontalAlignment(SwingConstants.CENTER);
        for (int i = 0; i < header.length; i++) {
            TableColumn column = connectedTable.getColumnModel().getColumn(i);
            column.setCellRenderer(renderer);
        }

        JScrollPane connectedPane = new JScrollPane();
        connectedPane.getViewport().setView(connectedTable);
        connectedPane.setPreferredSize(new Dimension(connectedPane.getPreferredSize().width, TABLE_HEIGHT));
        return connectedPane;
    }

    /**
     * Create a pane that displays Information Model ID list.
     * 
     * @param portList Ports
     * @return The pane that displays Information Model ID list
     */
    private JScrollPane createInformationModelIdListPane(List<Port> portList) {
        Collections.sort(portList, new PortComparator());

        Object[] columnNames = INFORMATION_MODEL_ID_COLNAME_LIST.toArray();
        DefaultTableModel tableModel = new DefaultTableModel(columnNames, 0);
        for (Port port : portList) {
            Port[] column = { port, port };
            tableModel.addRow(column);
        }

        JTable informationModeldTable = new JTable(tableModel);
        informationModeldTable.getTableHeader().setBackground(Color.WHITE);

        DefaultTableCellRenderer renderer = new InformationModelIdRenderer();
        renderer.setHorizontalAlignment(SwingConstants.CENTER);
        informationModeldTable.setDefaultRenderer(Object.class, renderer);

        JScrollPane informationModelIdPane = new JScrollPane();
        informationModelIdPane.getViewport().setView(informationModeldTable);
        informationModelIdPane
                .setPreferredSize(new Dimension(informationModelIdPane.getPreferredSize().width, TABLE_HEIGHT));

        return informationModelIdPane;
    }

    /**
     * The comparator for sorting layer, port type, port ID.
     */
    private class PortComparator implements Comparator<Port> {
        @Override
        public int compare(Port port1, Port port2) {
            if (port1.getLayerPriority() < port2.getLayerPriority()) {
                return 1;
            } else if (port1.getLayerPriority() > port2.getLayerPriority()) {
                return -1;
            }
            if (port1.getPortPriority() < port2.getPortPriority()) {
                return 1;
            } else if (port1.getPortPriority() > port2.getPortPriority()) {
                return -1;
            }
            if (port1.getPortId() > port2.getPortId()) {
                return 1;
            } else if (port1.getPortId() < port2.getPortId()) {
                return -1;
            }
            return 0;
        }
    }

    /**
     * Create a panel that displays Connect button.
     * 
     * @return The panel that displays Connect button
     */
    private JPanel createConnectButtonPanel() {
        connectButton = new JButton("Connect");
        connectButton.addActionListener(new ConnectToOpenFlowControllerListener(this));
        connectButton.setMaximumSize(new Dimension(Short.MAX_VALUE, connectButton.getMaximumSize().height));

        JPanel buttonPanel = new JPanel();
        buttonPanel.setLayout(new BoxLayout(buttonPanel, BoxLayout.Y_AXIS));
        buttonPanel.add(connectButton);
        return buttonPanel;
    }

    /**
     * The class that draws NE panel with the received Flowmod message.
     */
    private class DrawingNePanel extends JPanel {
        private static final int PT_HEIGHT = 30;
        private static final int GAP = 20;
        private static final int LEGEND_GAP = 15;
        private static final int TOP_X = 0;
        private static final int TOP_Y = 0;
        private static final int NE_TOP_Y = TOP_Y + PT_HEIGHT + GAP;
        private static final int PORT_D = 16;
        private static final int PORT_R = PORT_D / 2;
        private static final int XC_HEIGHT = 24;
        private static final int NE_ODU_HEIGHT = XC_HEIGHT + PORT_D * 2 + GAP * 3 + GAP / 2;
        private static final int NE_OCH_HEIGHT = XC_HEIGHT + PORT_D + PORT_R + GAP * 2 + GAP / 2;
        private static final int NE_HEIGHT = NE_ODU_HEIGHT + NE_OCH_HEIGHT;
        private static final int LEGEND_STRING_WIDTH = 22;
        private static final int LEGEND_WIDTH = LEGEND_GAP + PORT_D + LEGEND_GAP + LEGEND_STRING_WIDTH + LEGEND_GAP;
        private static final int LEGEND_HEIGHT = PORT_D * 2 + LEGEND_GAP * 3;
        private static final int ODU_XC_STRING_WIDTH = 46;
        private static final int LAMBDA_XC_STRING_WIDTH = 70;
        private static final int CHAR_WIDTH = 4;
        private static final int CHAR_HEIGHT = 10;
        private static final int OCH_CTP_TO_XC_LENGTH = PORT_R + GAP + XC_HEIGHT / 2;
        private static final float LINK_WIDTH = 2.0f;
        private static final String X_LABEL = "X";

        private static final int PT_TTP_Y = TOP_Y + PT_HEIGHT - PORT_R;
        private static final int ODU_TTP_Y = NE_TOP_Y + GAP;
        private static final int ODU_XC_Y = ODU_TTP_Y + PORT_D + GAP;
        private static final int ODU_CTP_Y = ODU_TTP_Y + PORT_D + GAP + XC_HEIGHT + GAP;
        private static final int OCH_TTP_Y = ODU_CTP_Y + PORT_D + GAP;
        private static final int LAMBDA_SW_Y = OCH_TTP_Y + PORT_D + GAP;
        private static final int OCH_CTP_Y = LAMBDA_SW_Y + XC_HEIGHT + GAP;
        private static final int NE_Y = TOP_Y + PT_HEIGHT + GAP;
        private static final int XC_X = TOP_X + GAP;
        private static final int LEGEND_TTP_Y = TOP_Y + LEGEND_GAP;
        private static final int LEGEND_CTP_Y = LEGEND_TTP_Y + PORT_D + LEGEND_GAP;

        private final int neWidth;
        private final int xcWidth;
        private Map<Port, Pos> portPosMap;

        /**
         * Constructor.
         */
        public DrawingNePanel() {
            super();

            xcWidth = calcNeWidth();
            neWidth = xcWidth + GAP * 2;

            initPortsPos();

            Dimension dimension = new Dimension(GAP + neWidth + GAP + LEGEND_WIDTH + GAP,
                    GAP + PT_HEIGHT + GAP + NE_HEIGHT + GAP);

            JPanel portPanel = new PortPanel();
            portPanel.setSize(dimension);
            portPanel.setOpaque(false);

            BasePanel basePanel = new BasePanel();
            basePanel.setSize(dimension);
            basePanel.setOpaque(false);

            JLayeredPane pane = new JLayeredPane();
            pane.add(portPanel);
            pane.add(basePanel);
            pane.setPreferredSize(dimension);
            this.add(pane);

            this.setPreferredSize(dimension);
            this.setBackground(Color.WHITE);
        }

        /**
         * Calculate the width of NE display.
         * The width is increased if the number of ports is more than 10.
         * 
         * @return The width of NE display
         */
        private int calcNeWidth() {
            int[] portCounts = { Port.searchPorts(portList, null, Constants.ETHER, Constants.TTP).size(),
                    Port.searchPorts(portList, null, Constants.ODU, Constants.TTP).size(),
                    Port.searchPorts(portList, null, Constants.ODU, Constants.CTP).size(),
                    Port.searchPorts(portList, null, Constants.OCH, Constants.TTP).size(),
                    Port.searchPorts(portList, null, Constants.OCH, Constants.CTP).size(), };

            Arrays.sort(portCounts);
            int maxPortCount = portCounts[portCounts.length - 1];
            final int minimumPortCount = 10;
            int portCount;
            if (maxPortCount > minimumPortCount) {
                portCount = maxPortCount;
            } else {
                portCount = minimumPortCount;
            }
            return PORT_D * portCount;
        }

        /**
        * Initialize the positions of ports in NE display.
        */
        private void initPortsPos() {
            portPosMap = new HashMap<Port, Pos>();
            calculatePortsPos(Constants.ETHER, Constants.TTP, TOP_X, PT_TTP_Y);
            calculatePortsPos(Constants.ODU, Constants.TTP, TOP_X, ODU_TTP_Y);
            calculatePortsPos(Constants.ODU, Constants.CTP, TOP_X, ODU_CTP_Y);
            calculatePortsPos(Constants.OCH, Constants.TTP, TOP_X, OCH_TTP_Y);
            calculatePortsPos(Constants.OCH, Constants.CTP, TOP_X, OCH_CTP_Y);
        }

        /**
         * Initialize the ports' positions of the layer by port type.
         * Notes:
         *   - Ports' positions are centering.
         *   - Sort ascending by port ID.
         *   - Connected ports are sort adjacently.
         * 
         * @param layer The Layer
         * @param portType The Port Type
         * @param srcX The X-coordinate of the layer in NE panel
         * @param srcY The Y-coordinate of the layer in NE panel
         */
        private void calculatePortsPos(String layer, String portType, int srcX, int srcY) {
            List<Port> targetPortList = Port.searchPorts(portList, null, layer, portType);

            // Sort ascending by OpenFlow port ID
            Collections.sort(targetPortList, new PortIdComparator());
            // Connected ports are sort adjacently
            targetPortList = createUncrossedConnectingPorts(targetPortList);

            srcX += calculatePortStartX(targetPortList.size());
            for (Port port : targetPortList) {
                portPosMap.put(port, new Pos(srcX, srcY));
                srcX += PORT_D;
            }
        }

        /**
         * The Comparator for sorting port ID.
         */
        private class PortIdComparator implements Comparator<Port> {
            @Override
            public int compare(Port port1, Port port2) {
                Integer portId1 = port1.getOpenFlowPortId();
                Integer portId2 = port2.getOpenFlowPortId();
                if (portId1 == null && portId2 == null) {
                    return 0;
                } else if (portId1 != null && portId2 == null) {
                    return 1;
                } else if (portId1 == null && portId2 != null) {
                    return -1;
                } else if (portId1 > portId2) {
                    return 1;
                } else if (portId2 > portId1) {
                    return -1;
                } else {
                    return 0;
                }
            }
        }

        /**
         * Create a port list that sorts connected ports adjacently (uncrossed connecting).
         * 
         * @param portList The Port List
         * @return The port list that sorts connected ports adjacently
         */
        private List<Port> createUncrossedConnectingPorts(List<Port> portList) {
            List<Port> sortingList = new ArrayList<Port>();
            // Sorts connected ports adjacently
            for (Port port : portList) {
                // Skip if already exists
                if (sortingList.contains(port)) {
                    continue;
                }
                // add self
                sortingList.add(port);

                // add the connected port
                // if it is the same port type in the same layer
                for (List<Port> link : linkList) {
                    Port port1 = link.get(0);
                    Port port2 = link.get(1);

                    if (!port1.getLayer().equals(port2.getLayer())
                            || !port1.getPortType().equals(port2.getPortType())) {
                        continue;
                    }

                    if (port.equals(port1)) {
                        sortingList.add(port2);
                    } else if (port.equals(port2)) {
                        sortingList.add(port1);
                    }
                }
            }
            return sortingList;
        }

        /**
         * The class that draws BasePanel (without ports) under the PortPanel.
         * (NE panel is constructed with a BasePanel and a PortPanel)
         */
        private class BasePanel extends AntiAliasingPanel {
            @Override
            protected void paintComponent(Graphics graphics) {
                super.paintComponent(graphics);

                // PT
                if (ptId != null) {
                    drawfillRect(graphics, neWidth, PT_HEIGHT, ptColor, TOP_X, TOP_Y);
                    graphics.drawString(ptId, TOP_X + neWidth / 2 - CHAR_WIDTH * ptId.length(),
                            TOP_Y + PT_HEIGHT / 2 + CHAR_HEIGHT / 2);
                }

                // NE
                drawfillRect(graphics, neWidth, NE_ODU_HEIGHT, neOduColor, TOP_X, NE_Y);
                drawfillRect(graphics, neWidth, NE_OCH_HEIGHT, neOchColor, TOP_X, NE_Y + NE_ODU_HEIGHT);

                // ODU XC
                drawfillRoundRect(graphics, xcWidth, XC_HEIGHT, oduXcColor, XC_X, ODU_XC_Y);

                String oduXcLabel = Config.getProperty("oduXcLabel");
                if (oduXcLabel == null) {
                    oduXcLabel = "";
                }
                graphics.drawString(oduXcLabel, XC_X + xcWidth / 2 - ODU_XC_STRING_WIDTH / 2,
                        ODU_XC_Y + XC_HEIGHT / 2 + CHAR_HEIGHT / 2);

                // Lambda SW
                drawfillRoundRect(graphics, xcWidth, XC_HEIGHT, lambdaSwColor, XC_X, LAMBDA_SW_Y);

                String lambdaSwLabel = Config.getProperty("lambdaSwLabel");
                if (lambdaSwLabel == null) {
                    lambdaSwLabel = "";
                }
                graphics.drawString(lambdaSwLabel, XC_X + xcWidth / 2 - LAMBDA_XC_STRING_WIDTH / 2,
                        LAMBDA_SW_Y + XC_HEIGHT / 2 + CHAR_HEIGHT / 2);

                int legendX = TOP_X + neWidth + 10;

                // Legend
                graphics.drawRect(legendX, TOP_Y, LEGEND_WIDTH, LEGEND_HEIGHT);

                // Legend - TTP
                legendX += LEGEND_GAP;
                drawBlackPort(graphics, X_LABEL, legendX, LEGEND_TTP_Y);
                graphics.drawString(Constants.TTP, legendX + PORT_D + LEGEND_GAP,
                        LEGEND_TTP_Y + PORT_R + CHAR_HEIGHT / 2);

                // Legend - CTP
                drawWhitePort(graphics, X_LABEL, legendX, LEGEND_CTP_Y);
                graphics.drawString(Constants.CTP, legendX + PORT_D + LEGEND_GAP,
                        LEGEND_CTP_Y + PORT_R + CHAR_HEIGHT / 2);

                // Link
                for (List<Port> link : linkList) {
                    drawLink(graphics, link, xcColor);
                }

                // draws old links
                for (List<Integer> link : linkSetOld) {
                    drawLinkByPortNumber(graphics, link, flowmodLinkOldColor);
                }

                // draws new links
                for (List<Integer> link : linkListNew) {
                    drawLinkByPortNumber(graphics, link, flowmodLinkNewColor);
                }
            }
        }

        /**
         * The class that draws PortPanel upon the BasePanel.
         * (NE panel is constructed with a BasePanel and a PortPanel)
         */
        private class PortPanel extends AntiAliasingPanel {
            @Override
            protected void paintComponent(Graphics graphics) {
                drawPortsToLayer(graphics, Constants.ETHER, Constants.TTP);
                drawPortsToLayer(graphics, Constants.ODU, Constants.TTP);
                drawPortsToLayer(graphics, Constants.ODU, Constants.CTP);
                drawPortsToLayer(graphics, Constants.OCH, Constants.TTP);
                drawPortsToLayer(graphics, Constants.OCH, Constants.CTP);
            }
        }

        /**
         * Draws a black port.
         * 
         * @param graphics  The Graphics
         * @param portId    The Port ID
         * @param posX      The X-coordinate
         * @param posY      The Y-coordinate
         */
        private void drawBlackPort(Graphics graphics, String portId, int posX, int posY) {
            graphics.setColor(Color.BLACK);
            graphics.fillRoundRect(posX, posY, PORT_D, PORT_D, PORT_D, PORT_D);
            graphics.setColor(Color.WHITE);
            drawStringOnPort(graphics, portId, posX, posY);
            graphics.setColor(Color.BLACK);
        }

        /**
         * Draws a white port.
         * 
         * @param graphics  The Graphics
         * @param portId    The Port ID
         * @param posX      The X-coordinate
         * @param posY      The Y-coordinate
         */
        private void drawWhitePort(Graphics graphics, String portId, int posX, int posY) {
            graphics.setColor(Color.WHITE);
            graphics.fillRoundRect(posX, posY, PORT_D, PORT_D, PORT_D, PORT_D);
            graphics.setColor(Color.BLACK);
            graphics.drawRoundRect(posX, posY, PORT_D, PORT_D, PORT_D, PORT_D);
            drawStringOnPort(graphics, portId, posX, posY);
        }

        /**
         * Draw a port ID on the port.
         * 
         * @param graphics  The Graphics
         * @param portId    The Port ID
         * @param posX      The X-coordinate
         * @param posY      The Y-coordinate
         */
        private void drawStringOnPort(Graphics graphics, String portId, int posX, int posY) {
            graphics.drawString(portId, posX + PORT_R - CHAR_WIDTH * portId.length(),
                    posY + PORT_R + CHAR_HEIGHT / 2);
        }

        /**
         * Draw a rectangle with filled color.
         * 
         * @param graphics   The Graphics
         * @param width      The Width
         * @param height     The Height
         * @param fillColor  The Color
         * @param srcX       The X-coordinate
         * @param srcY       The Y-coordinate
         */
        private void drawfillRect(Graphics graphics, int width, int height, Color fillColor, int srcX, int srcY) {
            graphics.setColor(fillColor);
            graphics.fillRect(srcX, srcY, width, height);
            graphics.setColor(Color.BLACK);
            graphics.drawRect(srcX, srcY, width, height);
        }

        /**
         * Draw a round corner rectangle with filled color.
         * 
         * @param graphics   The Graphics
         * @param width      The Width
         * @param height     The Height
         * @param fillColor  The Color
         * @param srcX       The X-coordinate
         * @param srcY       The Y-coordinate
         */
        private void drawfillRoundRect(Graphics graphics, int width, int height, Color fillColor, int srcX,
                int srcY) {
            graphics.setColor(fillColor);
            graphics.fillRoundRect(srcX, srcY, width, height, width / 5, height / 5);
            graphics.setColor(Color.BLACK);
            graphics.drawRoundRect(srcX, srcY, width, height, width / 5, height / 5);
        }

        /**
         * Draw a color link by port ID.
         * 
         * @param graphics The Graphics
         * @param link     The list with paired port IDs
         * @param color    The Color
         */
        private void drawLinkByPortNumber(Graphics graphics, List<Integer> link, Color color) {
            Port port1 = portMap.get(link.get(0));
            Port port2 = portMap.get(link.get(1));
            drawLink(graphics, port1, port2, color);
        }

        /**
         * Draw a color link by port.
         * 
         * @param graphics The Graphics
         * @param link     The list with paired ports
         * @param color    The Color
         */
        private void drawLink(Graphics graphics, List<Port> link, Color color) {
            Port port1 = link.get(0);
            Port port2 = link.get(1);
            drawLink(graphics, port1, port2, color);
        }

        /**
         * Draw a color link by specified ports.
         * 
         * @param graphics The Graphics
         * @param port1    The Port1
         * @param port2    The Port2
         * @param color    The Color
         */
        private void drawLink(Graphics graphics, Port port1, Port port2, Color color) {
            Pos port1Pos = portPosMap.get(port1);
            Pos port2Pos = portPosMap.get(port2);

            if (port1Pos == null) {
                logger.warn("Port is not found (" + port1 + ").");
                return;
            }

            if (port2Pos == null) {
                logger.warn("Port is not found (" + port2 + ").");
                return;
            }

            graphics.setColor(color);

            int x1 = port1Pos.posX + PORT_R;
            int y1 = port1Pos.posY + PORT_R;
            int x2 = port2Pos.posX + PORT_R;
            int y2 = port2Pos.posY + PORT_R;

            BasicStroke stroke = new BasicStroke(LINK_WIDTH);
            Graphics2D graphics2d = (Graphics2D) graphics;
            Stroke currentStroke = graphics2d.getStroke();
            graphics2d.setStroke(stroke);

            if (port1.getPortType().equals(Constants.CTP) && port2.getPortType().equals(Constants.CTP)) {
                drawLinkCutThrough(graphics, x1, y1, x2, y2);
            } else {
                graphics.drawLine(x1, y1, x2, y2);
            }

            graphics2d.setStroke(currentStroke);
            graphics.setColor(Color.BLACK);
        }

        /**
         * Draw a cut through link.
         * 
         * @param graphics The Graphics
         * @param x1       The X-coordinate of port1
         * @param y1       The Y-coordinate of port1
         * @param x2       The X-coordinate of port2
         * @param y2       The Y-coordinate of port2
         */
        private void drawLinkCutThrough(Graphics graphics, int x1, int y1, int x2, int y2) {
            graphics.drawLine(x1, y1, x1, y1 - OCH_CTP_TO_XC_LENGTH);
            graphics.drawLine(x1, y1 - OCH_CTP_TO_XC_LENGTH, x2, y2 - OCH_CTP_TO_XC_LENGTH);
            graphics.drawLine(x2, y2 - OCH_CTP_TO_XC_LENGTH, x2, y2);
        }

        /**
         * Draw ports of the layer by port type.
         *   TTP: black
         *   CTP: white
         * 
         * @param graphics  The Graphics
         * @param layer     The Layer
         * @param portType  The Port Type
         */
        private void drawPortsToLayer(Graphics graphics, String layer, String portType) {
            List<Port> searchedPortList = Port.searchPorts(portList, null, layer, portType);

            for (Port port : searchedPortList) {
                Pos pos = portPosMap.get(port);
                if (pos == null) {
                    logger.warn("Port is not found (" + port + ")");
                    continue;
                }

                String portId;
                if (port.getLayer().equals(Constants.ETHER)) {
                    portId = "";
                } else {
                    Integer ofPortId = port.getOpenFlowPortId();
                    if (ofPortId != null) {
                        portId = String.valueOf(ofPortId);
                    } else {
                        portId = OFPORT_NOTFOUND;
                    }
                }

                if (portType.equals(Constants.TTP)) {
                    drawBlackPort(graphics, portId, pos.posX, pos.posY);
                } else {
                    drawWhitePort(graphics, portId, pos.posX, pos.posY);
                }
            }
        }

        /**
         * Calculate X-coordinate for centering ports.
         * 
         * @param portNum The Port Number
         * @return The X-coordinate for centering ports
         */
        private int calculatePortStartX(int portNum) {
            return neWidth / 2 - PORT_D * portNum / 2;
        }

        /**
         * The coordinate class.
         */
        private class Pos {
            private final int posX;
            private final int posY;

            /**
             * Constructor.
             * 
             * @param posX The X-coordinate
             * @param posY The Y-coordinate
             */
            private Pos(int posX, int posY) {
                this.posX = posX;
                this.posY = posY;
            }
        }
    }

    /**
     * The class for rendering the Information Model ID table.
     */
    private class InformationModelIdRenderer extends DefaultTableCellRenderer {
        @Override
        public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected,
                boolean hasFocus, int row, int column) {
            super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);

            Port port = (Port) value;

            if (column == INFORMATION_MODEL_ID_COLNAME_LIST.indexOf(InformationModelIdCols.OF_PORT.getName())) {
                if (port.getLayer().equals(Constants.ETHER)) {
                    setText(String.valueOf("-"));
                } else {
                    Integer openFlowPortId = port.getOpenFlowPortId();
                    if (openFlowPortId == null) {
                        setText(String.valueOf(OFPORT_NOTFOUND));
                    } else {
                        setText(String.valueOf(openFlowPortId));
                    }
                }
            } else if (column == INFORMATION_MODEL_ID_COLNAME_LIST
                    .indexOf(InformationModelIdCols.INFORMATION_MODEL_ID.getName())) {
                setText(port.getInformationModelId());
            }

            switch (port.getLayer()) {
            case Constants.ETHER:
                setBackground(ptColor);
                break;
            case Constants.ODU:
                setBackground(neOduColor);
                break;
            case Constants.OCH:
                setBackground(neOchColor);
                break;
            default:
                break;
            }
            if (isSelected) {
                setBackground(table.getSelectionBackground());
            }

            return this;
        }
    }

    /**
     * The class for Connect button Action Listener.
     */
    private class ConnectToOpenFlowControllerListener implements ActionListener {
        private NeFrame neFrame;

        /**
         * Constructor.
         * 
         * @param neFrame The NE frame
         */
        public ConnectToOpenFlowControllerListener(NeFrame neFrame) {
            super();
            this.neFrame = neFrame;
        }

        /**
         * Connect to OpenFlow controller if connect button is clicked.
         * If succeeded, show success message and start to confirm living state.
         * If failed, show error message.
         * 
         * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
         */
        @Override
        public void actionPerformed(ActionEvent event) {
            Thread thread = new Thread(new Runnable() {
                @Override
                public void run() {
                    OFVersion of13 = OFVersion.OF_13;
                    ChannelFuture channelFuture = null;
                    String orgText = displayNeStatusButton.getText();

                    connectButton.setEnabled(false);
                    connectButton.setText("Connecting...");

                    String ofcHostname = Config.getProperty("ofcHostname");
                    if (ofcHostname == null) {
                        JOptionPane.showMessageDialog(neFrame,
                                "ofcHostname[" + ofcHostname + "] setting is invalid.", "Failed",
                                JOptionPane.ERROR_MESSAGE);
                        return;
                    }

                    String ofcPortNumberString = Config.getProperty("ofcPortNumber");
                    Integer ofcPortNumber;
                    try {
                        ofcPortNumber = Integer.valueOf(ofcPortNumberString);
                    } catch (NumberFormatException e) {
                        logger.error("", e);
                        JOptionPane.showMessageDialog(neFrame,
                                "ofcPortNumber[" + ofcPortNumberString + "] setting is invalid.", "Failed",
                                JOptionPane.ERROR_MESSAGE);
                        return;
                    }

                    ConnectorToOpenFlowController connector = new ConnectorToOpenFlowController();
                    try {
                        channelFuture = connector.connectToOpenFlowController(ofcHostname, ofcPortNumber, of13,
                                dpid, neFrame);
                    } catch (Exception e) {
                        logger.error("", e);

                        JOptionPane.showMessageDialog(neFrame,
                                orgText + " failed to connect to OpenFlow controller.", "Failed",
                                JOptionPane.ERROR_MESSAGE);

                        setComponentsDefaultStatus(orgText);

                        return;
                    }

                    JOptionPane.showMessageDialog(neFrame, orgText + " is connected to OpenFlow controller.",
                            "Succeeded", JOptionPane.INFORMATION_MESSAGE);

                    connectButton.setText("Connected");
                    displayNeStatusButton.setText(orgText + " - Connected -");
                    flowmodStatusLabel.setText(Constants.FLOWMOD_INFO_TEXT_WAITING);

                    try {
                        connector.comfirmOpenFlowControllerLiving(channelFuture, of13);
                    } catch (Exception e) {
                        logger.error("", e);

                        JOptionPane.showMessageDialog(neFrame, orgText + " is disconnected to OpenFlow controller.",
                                "Error", JOptionPane.ERROR_MESSAGE);
                        setComponentsDefaultStatus(orgText);

                        return;
                    }
                }

                /**
                 * Set NE frame to initial status.
                 * 
                 * @param neDefaultText The initial text of NE button.
                 */
                private void setComponentsDefaultStatus(String neDefaultText) {
                    connectButton.setText("Connect");
                    connectButton.setEnabled(true);
                    displayNeStatusButton.setText(neDefaultText);
                    flowmodStatusLabel.setText(Constants.FLOWMOD_INFO_TEXT_DEFAULT);
                }

            });
            thread.setName("Connection Thread");
            thread.start();
        }
    }
}