Newer
Older
AlgebraicDataflowArchitectureModel / AlgebraicDataflowArchitectureModel / src / application / simulator / InputEventCellEditor.java
package application.simulator;

import application.editor.Editor;
import com.mxgraph.model.mxCell;
import com.mxgraph.model.mxIGraphModel;
import com.mxgraph.swing.mxGraphComponent;
import com.mxgraph.swing.view.mxICellEditor;
import com.mxgraph.util.mxConstants;
import com.mxgraph.util.mxUtils;
import com.mxgraph.view.mxCellState;
import models.algebra.*;
import models.dataConstraintModel.Channel;
import models.dataConstraintModel.ChannelMember;
import models.dataConstraintModel.ResourcePath;
import models.dataFlowModel.DataTransferChannel;
import models.dataFlowModel.PushPullAttribute;
import models.dataFlowModel.PushPullValue;
import models.dataFlowModel.ResolvingMultipleDefinitionIsFutureWork;
import parser.Parser;
import parser.Parser.TokenStream;
import parser.exceptions.ExpectedColon;
import parser.exceptions.ExpectedDoubleQuotation;
import parser.exceptions.ExpectedRightBracket;
import parser.exceptions.WrongJsonExpression;
import simulator.Event;
import simulator.Resource;
import simulator.ResourceIdentifier;
import simulator.Simulator;

import javax.swing.*;
import java.awt.*;
import java.util.ArrayList;
import java.util.EventObject;
import java.util.List;

public class InputEventCellEditor implements mxICellEditor {
    public int DEFAULT_MIN_WIDTH = 70;
    public int DEFAULT_MIN_HEIGHT = 30;
    public double DEFAULT_MINIMUM_EDITOR_SCALE = 1;

    protected double minimumEditorScale = DEFAULT_MINIMUM_EDITOR_SCALE;
    protected int minimumWidth = DEFAULT_MIN_WIDTH;
    protected int minimumHeight = DEFAULT_MIN_HEIGHT;

    private final mxGraphComponent graphComponent;
    private final SimulatorWindow simulatorWindow;
    private final Simulator simulator;
    private final Editor editor;

    private Object editingCell;
    private EventObject trigger;
    private JComboBox<String> comboBox;

    public InputEventCellEditor(mxGraphComponent graphComponent, SimulatorWindow simulatorWindow) {
        this.graphComponent = graphComponent;
        this.simulatorWindow = simulatorWindow;

        simulator = simulatorWindow.getSimulator();
        editor = simulatorWindow.getEditor();
    }

    @Override
    public Object getEditingCell() {
        return editingCell;
    }

    @Override
    public void startEditing(Object cell, EventObject evt) {
        if (editingCell != null) {
            stopEditing(true);
        }
        if (!graphComponent.getGraph().getModel().isEdge(cell)) {
            Resource res = simulator.getCurState().getResource((String) ((mxCell) cell).getValue());
            ResourceIdentifier resId = res.getResourceIdentifier(); // Resource
            ArrayList<DataTransferChannel> eventChs = new ArrayList<>(); // IOChannel List
            ArrayList<String> messages = new ArrayList<>(); // ADL message
            ArrayList<Expression> eventMessages = new ArrayList<>(); // Message list
            ResourcePath eventResPath = null;

            for (Channel ch : simulator.getModel().getInputChannels()) { // All channel
                if (((DataTransferChannel) ch).getInputResources().isEmpty()) { // Input channel or Normal channel
                    for (ChannelMember out : ((DataTransferChannel) ch).getOutputChannelMembers()) {
                        ResourcePath resPath = out.getResource();
                        if (resId.isInstanceOf(resPath)) { // account.uid == accounts.123
                            eventResPath = resPath;
                            eventChs.add(((DataTransferChannel) ch));
                            String message = null;
                            Expression mesExp = out.getStateTransition().getMessageExpression(); // sync(a,b)
                            eventMessages.add(mesExp);
                            if (mesExp instanceof Term) {
                                message = ((Term) mesExp).getSymbol().toString(); // sync
                            } else if (mesExp instanceof Variable) {
                                message = ((Variable) mesExp).getName(); // x,y,z..
                            }
                            messages.add(message); // Pull-down
                        }
                    }
                }
            }

            if (messages.isEmpty()) {
                return;
            }
            String[] eventList = messages.toArray(new String[messages.size()]);
            JComboBox<String> event = new JComboBox<>(eventList);

            JPanel eventChoice = new JPanel();
            eventChoice.add(event); // FirstEventChoice

            int ret = JOptionPane.showConfirmDialog(simulatorWindow, eventChoice, "Event Choice", JOptionPane.OK_CANCEL_OPTION);
            if (ret == JOptionPane.OK_OPTION) {
                JPanel inputEvent = new JPanel();
                int i, eventNum;
                i = eventNum = 0;

                for (String eventString : eventList) {
                    if (eventString.equals(event.getSelectedItem().toString())) {
                        eventNum = i;
                    }
                    i++;
                }

                JTextArea textArea = new JTextArea(eventMessages.get(eventNum).toString(), 10, 30); // EventInput
                inputEvent.add(textArea);

                int approve = JOptionPane.showConfirmDialog(simulatorWindow, inputEvent, "Event Code", JOptionPane.OK_CANCEL_OPTION);
                if (approve == JOptionPane.OK_OPTION) {
                    try {
                        TokenStream stream = new Parser.TokenStream();
                        Parser parser = new Parser(stream);
                        stream.addLine(textArea.getText());
                        Expression eventMessage = parser.parseTerm(stream, simulator.getModel());

                        Event newEvent = new Event(eventChs.get(eventNum), eventMessage, eventResPath, simulator.getCurState().getResource(resId));
                        simulator.transition(newEvent);

                        // SimulationLayout layout =  new SimulationLayout(simulator.getCurState());
                        // layout.constructSimulateGraph(graph, simulator);

                        graphComponent.setCellEditor(new InputEventCellEditor(graphComponent, simulatorWindow));
                    } catch (ParameterizedIdentifierIsFutureWork | ResolvingMultipleDefinitionIsFutureWork |
                             InvalidMessage | UnificationFailed | ValueUndefined | ExpectedRightBracket |
                             WrongJsonExpression | ExpectedColon | ExpectedDoubleQuotation e) {
                        e.printStackTrace();
                    }
                }
            }
            return;
        }

        mxCellState state = graphComponent.getGraph().getView().getState(cell);
        if (state != null && state.getLabel() != null && !state.getLabel().isEmpty()) {
            editingCell = cell;
            trigger = evt;

            double scale = Math.max(minimumEditorScale, graphComponent.getGraph().getView().getScale());
            Object value = graphComponent.getGraph().getModel().getValue(cell);
            if (value instanceof PushPullAttribute) {
                PushPullAttribute attr = (PushPullAttribute) value;
                comboBox = new JComboBox<>(attr.getOptionStrings());
                comboBox.setBorder(BorderFactory.createEmptyBorder());
                comboBox.setOpaque(false);
                comboBox.setBounds(getEditorBounds(state, scale));
                comboBox.setVisible(true);
                graphComponent.getGraphControl().add(comboBox, 0);
                comboBox.updateUI();
            }
        }
    }

//	private mxGraph constructNextSimulateGraph(Set<Resource> simulateRes, DataTransferModel model,DataFlowGraph dataFlowGraph, mxGraph nextGraph) {
//		bReflectingArchitectureModel = true;
//		((mxGraphModel) nextGraph.getModel()).clear();
//		Object parent = nextGraph.getDefaultParent();
//		nextGraph.getModel().beginUpdate();
//
//		try {
//			Map<Resource, Object> resources = new HashMap<>();
//
//			// create resource vertices
//			for (Resource resNode: simulateRes) {
//				int w = 400;
//				int h = 150;
//				ResourcePath res = resNode.getResourceIdentifier();
//				Object resource = nextGraph.insertVertex(parent, null,
//						res.toString(), x, y, w, h,
//						"shape=ellipse;perimeter=ellipsePerimeter;verticalAlign=top;"); // insert a resource as a vertex
//				resources.put(resNode, resource);
//
//				createNextChildSimulateResourceVerticies(resource, resNode, resources, w, h);
//				x+=400;
//			}
//
//		} finally {
//			nextGraph.getModel().endUpdate();
//		}
//
//		bReflectingArchitectureModel = false;
//		return nextGraph;
//	}
//
//	private void createNextChildSimulateResourceVerticies(Object resource, Resource resNode, Map<Resource, Object> resources, int w, int h) { //sample
//		
//		if(resNode.getChildren() != null) {
//			for (Resource childNode: resNode.getChildren()) {
//				int i = 1;
//				ResourcePath childRes = childNode.getResourceIdentifier();
//				Object childResource = graph.insertVertex(resource, null,
//						childRes.toString(), x/i, 0, w/(i+1), h/(i+2),
//						"shape=ellipse;perimeter=ellipsePerimeter;verticalAlign=top;"); // insert a resource as a vertex
//				resources.put(childNode, childResource);
//				i++;
//				createNextChildSimulateResourceVerticies(childResource, childNode, resources, w, h);
//			}
//		}
//		if (resNode.getChildren() == null || resNode.getChildren().size() == 0) {
//			Object state = graph.insertVertex(resource, null,
//					resNode.getState().getValue().toString(), 0.5, 0.5, 0.25, 0.25, "opacity=0;verticalAlign=down;", true); // insert a state label as a vertex
//		}
//	}

    @Override
    public void stopEditing(boolean cancel) {
        if (editingCell != null) {
            comboBox.transferFocusUpCycle();
            Object cell = editingCell;
            editingCell = null;
            if (!cancel) {
                EventObject trig = trigger;
                trigger = null;
                Object value = graphComponent.getGraph().getModel().getValue(cell);
                if (value instanceof PushPullAttribute) {
                    PushPullAttribute attr = (PushPullAttribute) value;
                    List<PushPullValue> options = attr.getOptions();
                    PushPullValue selected = null;
                    for (PushPullValue option : options) {
                        if (option.toString().equals(getCurrentValue())) {
                            selected = option;
                            break;
                        }
                    }
                    if (selected != null) {
                        options.remove(selected);
                        options.add(0, selected);
                    }
                    graphComponent.labelChanged(cell, attr, trig);
                }
            } else {
                mxCellState state = graphComponent.getGraph().getView().getState(cell);
                graphComponent.redraw(state);
            }
            if (comboBox.getParent() != null) {
                comboBox.setVisible(false);
                comboBox.getParent().remove(comboBox);
            }
            graphComponent.requestFocusInWindow();
        }
    }

    public String getCurrentValue() {
        return (String) comboBox.getSelectedItem();
    }

    /**
     * Returns the bounds to be used for the editor.
     */
    public Rectangle getEditorBounds(mxCellState state, double scale) {
        mxIGraphModel model = state.getView().getGraph().getModel();
        Rectangle bounds;

        bounds = state.getLabelBounds().getRectangle();
        bounds.height += 10;

        // Applies the horizontal and vertical label positions
        if (model.isVertex(state.getCell())) {
            String horizontal = mxUtils.getString(state.getStyle(), mxConstants.STYLE_LABEL_POSITION, mxConstants.ALIGN_CENTER);

            if (horizontal.equals(mxConstants.ALIGN_LEFT)) {
                bounds.x -= (int) state.getWidth();
            } else if (horizontal.equals(mxConstants.ALIGN_RIGHT)) {
                bounds.x += (int) state.getWidth();
            }

            String vertical = mxUtils.getString(state.getStyle(), mxConstants.STYLE_VERTICAL_LABEL_POSITION, mxConstants.ALIGN_MIDDLE);

            if (vertical.equals(mxConstants.ALIGN_TOP)) {
                bounds.y -= (int) state.getHeight();
            } else if (vertical.equals(mxConstants.ALIGN_BOTTOM)) {
                bounds.y += (int) state.getHeight();
            }
        }

        bounds.setSize((int) Math.max(bounds.getWidth(), Math.round(minimumWidth * scale)), (int) Math.max(bounds.getHeight(), Math.round(minimumHeight * scale)));

        return bounds;
    }
}