package pipe.gui.widgets;
import pipe.controllers.PetriNetController;
import pipe.controllers.PlaceController;
import uk.ac.imperial.pipe.models.petrinet.PetriNet;
import uk.ac.imperial.pipe.models.petrinet.Token;
import javax.swing.*;
import javax.swing.event.ChangeListener;
import java.awt.*;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Panel for editing place properties
*/
@SuppressWarnings("serial")
public class PlaceEditorPanel extends javax.swing.JPanel {
/**
* Class logger
*/
private static final Logger LOGGER = Logger.getLogger(PlaceEditorPanel.class.getName());
/**
* Petri net controller that the place belongs to
*/
private final PetriNetController netController;
/**
* Place controller
*/
private final PlaceController placeController;
/**
* Graphical root pane
*/
private final JRootPane rootPane;
/**
* Capacity label
*/
private javax.swing.JLabel capacity0Label = new javax.swing.JLabel();
/**
* Capacity spinner for changing the place capacity
*/
private javax.swing.JSpinner capacitySpinner = new javax.swing.JSpinner();
/**
* Place name text field
*/
private javax.swing.JTextField nameTextField = new javax.swing.JTextField();
/**
* OK button that will perform changes when pressed
*/
private javax.swing.JButton okButton = new javax.swing.JButton();
/**
* Place tokens
*/
private List<JSpinner> inputtedMarkings = new LinkedList<>();
/**
* Token ids
*/
private List<String> inputtedTokenClassNames = new LinkedList<>();
/**
* Creates new form PlaceEditor
* @param rootPane root pane
* @param placeController place controller
* @param petriNetController Petri net controller
*/
public PlaceEditorPanel(JRootPane rootPane, PlaceController placeController,
PetriNetController petriNetController) {
this.rootPane = rootPane;
this.rootPane.setDefaultButton(okButton);
this.placeController = placeController;
netController = petriNetController;
initComponents();
}
/**
* Initializes the editor view
*/
private void initComponents() {
setLayout(new java.awt.GridBagLayout());
JPanel placeEditorPanel = createPlaceEditorPanel();
// Now set new dimension used in for loop below
int col = 0;
int row = 2;
PetriNet net = netController.getPetriNet();
for (Token token : net.getTokens()) {
JLabel tokenClassName = new JLabel();
JSpinner tokenClassMarking = new JSpinner();
inputtedMarkings.add(tokenClassMarking);
tokenClassName.setText(token.getId() + ": ");
inputtedTokenClassNames.add(token.getId());
GridBagConstraints tokenNameConstraints = new java.awt.GridBagConstraints();
tokenNameConstraints.gridx = 0;
tokenNameConstraints.gridy = row;
tokenNameConstraints.anchor = java.awt.GridBagConstraints.EAST;
tokenNameConstraints.insets = new java.awt.Insets(3, 3, 3, 3);
placeEditorPanel.add(tokenClassName, tokenNameConstraints);
tokenClassMarking.setValue(placeController.getTokenCount(token.getId()));
tokenClassMarking.setMinimumSize(new java.awt.Dimension(50, 20));
tokenClassMarking.setPreferredSize(new java.awt.Dimension(50, 20));
tokenClassMarking.addChangeListener(new javax.swing.event.ChangeListener() {
public void stateChanged(javax.swing.event.ChangeEvent evt) {
markingSpinnerStateChanged(evt, inputtedMarkings.size() - 1);
}
});
GridBagConstraints tokenValueConstraints = new java.awt.GridBagConstraints();
tokenValueConstraints.gridx = col + 1;
tokenValueConstraints.gridy = row;
tokenValueConstraints.gridwidth = 3;
tokenValueConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
tokenValueConstraints.insets = new java.awt.Insets(3, 3, 3, 3);
placeEditorPanel.add(tokenClassMarking, tokenValueConstraints);
row++;
}
initializeCapacityLabel(placeEditorPanel, row);
initializeCapacitySpinner(placeEditorPanel, row);
initializeCapacity0Label(placeEditorPanel, row);
GridBagConstraints gridBagConstraints;
JPanel buttonPanel = new JPanel();
buttonPanel.setLayout(new java.awt.GridBagLayout());
initializeOkButton(buttonPanel);
initializeCancelButton(buttonPanel);
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 1;
gridBagConstraints.gridy = 1;
gridBagConstraints.anchor = java.awt.GridBagConstraints.EAST;
add(buttonPanel, gridBagConstraints);
}
/**
* Sets the no capacity restriction label visible if the capacity is
* zero
*
* @param capacity of the place
*/
private void setCapacityVisible(double capacity) {
if (capacity == 0) {
capacity0Label.setVisible(true);
} else {
capacity0Label.setVisible(false);
}
}
/**
* initializes the capacity label. If he capacity is set to 0 it reminds users
* that a capacity of 0 imposes no restrictions.
* @param placeEditorPanel panel
* @param row of the panel
*/
private void initializeCapacity0Label(JPanel placeEditorPanel, int row) {
capacity0Label.setText("(no capacity restriction) ");
GridBagConstraints gridBagConstraints = new GridBagConstraints();
gridBagConstraints.gridx = 2;
gridBagConstraints.gridy = row;
gridBagConstraints.insets = new Insets(3, 3, 3, 3);
placeEditorPanel.add(capacity0Label, gridBagConstraints);
gridBagConstraints = new GridBagConstraints();
gridBagConstraints.gridwidth = 2;
gridBagConstraints.anchor = GridBagConstraints.EAST;
gridBagConstraints.insets = new Insets(5, 8, 5, 8);
add(placeEditorPanel, gridBagConstraints);
setCapacityVisible(placeController.getCapacity());
}
/**
* Initializes the capacity spinner
* @param placeEditorPanel panel
* @param row of the panel
*/
private void initializeCapacitySpinner(JPanel placeEditorPanel, int row) {
capacitySpinner.setModel(new SpinnerNumberModel(placeController.getCapacity(), 0, Integer.MAX_VALUE, 1));
capacitySpinner.setMinimumSize(new Dimension(50, 20));
capacitySpinner.setPreferredSize(new Dimension(50, 20));
capacitySpinner.addChangeListener(new ChangeListener() {
public void stateChanged(javax.swing.event.ChangeEvent evt) {
capacitySpinnerStateChanged(evt);
}
});
GridBagConstraints capacityConstraints = new GridBagConstraints();
capacityConstraints.gridx = 1;
capacityConstraints.gridy = row;
capacityConstraints.fill = GridBagConstraints.HORIZONTAL;
capacityConstraints.insets = new Insets(3, 3, 3, 3);
placeEditorPanel.add(capacitySpinner, capacityConstraints);
}
/**
* Creates the editor panel
* @return editor panel
*/
private JPanel createPlaceEditorPanel() {
GridBagConstraints gridBagConstraints;
JPanel placeEditorPanel = new JPanel();
placeEditorPanel.setLayout(new GridBagLayout());
placeEditorPanel.setBorder(BorderFactory.createTitledBorder("Place Editor"));
JLabel nameLabel = new JLabel();
nameLabel.setText("NameDetails:");
gridBagConstraints = new GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 0;
gridBagConstraints.anchor = GridBagConstraints.EAST;
gridBagConstraints.insets = new Insets(3, 3, 3, 3);
placeEditorPanel.add(nameLabel, gridBagConstraints);
initializeNameTextField(placeEditorPanel);
return placeEditorPanel;
}
/**
* initializes the capacity label
* @param placeEditorPanel panel
* @param row of the panel
*/
private void initializeCapacityLabel(JPanel placeEditorPanel, int row) {
GridBagConstraints gridBagConstraints;
JLabel capacityLabel = new JLabel();
capacityLabel.setText("Capacity:");
gridBagConstraints = new GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = row;
gridBagConstraints.anchor = GridBagConstraints.EAST;
gridBagConstraints.insets = new Insets(3, 3, 3, 3);
placeEditorPanel.add(capacityLabel, gridBagConstraints);
}
/**
* Initialises the cancel button. When cancel is pressed the window exits and no changes
* are saved
* @param buttonPanel cancel button
*/
private void initializeCancelButton(JPanel buttonPanel) {
GridBagConstraints gridBagConstraints;
JButton cancelButton = new JButton();
cancelButton.setText("Cancel");
cancelButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
cancelButtonHandler(evt);
}
});
gridBagConstraints = new GridBagConstraints();
gridBagConstraints.gridx = 1;
gridBagConstraints.gridy = 0;
gridBagConstraints.anchor = GridBagConstraints.WEST;
gridBagConstraints.insets = new Insets(8, 0, 8, 10);
buttonPanel.add(cancelButton, gridBagConstraints);
}
/**
* Initializes the name text field to the place name
* @param placeEditorPanel editor panel
*/
private void initializeNameTextField(JPanel placeEditorPanel) {
nameTextField.setText(placeController.getName());
GridBagConstraints gridBagConstraints = new GridBagConstraints();
gridBagConstraints.gridwidth = 2;
gridBagConstraints.fill = GridBagConstraints.HORIZONTAL;
gridBagConstraints.insets = new Insets(3, 3, 3, 3);
placeEditorPanel.add(nameTextField, gridBagConstraints);
}
/**
* Initializes the OK button action, this will save the settings to the place
* @param buttonPanel button panel
*/
private void initializeOkButton(JPanel buttonPanel) {
okButton.setText("OK");
okButton.setMaximumSize(new Dimension(75, 25));
okButton.setMinimumSize(new Dimension(75, 25));
okButton.setPreferredSize(new Dimension(75, 25));
okButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
okButtonHandler(evt);
}
});
okButton.addKeyListener(new java.awt.event.KeyAdapter() {
public void keyPressed(java.awt.event.KeyEvent evt) {
okButtonKeyPressed(evt);
}
});
GridBagConstraints gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 0;
gridBagConstraints.gridwidth = java.awt.GridBagConstraints.RELATIVE;
gridBagConstraints.anchor = java.awt.GridBagConstraints.EAST;
gridBagConstraints.insets = new java.awt.Insets(5, 0, 5, 9);
buttonPanel.add(okButton, gridBagConstraints);
}
/**
* If the marking spinner changes then more tokens are added to the place
*
* This feature is not currently implemented
* @param evt event
* @param posInList position in list
*/
private void markingSpinnerStateChanged(javax.swing.event.ChangeEvent evt,
int posInList) {
/* Integer capacity = (Integer)capacitySpinner.getValue();
int totalMarkings = 0;
for(JSpinner inputtedMarking:inputtedMarkings){
totalMarkings += (Integer)inputtedMarking.getValue();
}
int markingOfCurrentSpinner = (Integer)inputtedMarkings.get(posInList).getValue();
if (capacity > 0) {
if (totalMarkings > capacity) {
int overMarkingLimit = totalMarkings - capacity;
inputtedMarkings.get(posInList).setValue(markingOfCurrentSpinner - overMarkingLimit);
}
}*/
}
/**
* Performs the ok action
* @param evt event
*/
private void okButtonKeyPressed(java.awt.event.KeyEvent evt) {
if (evt.getKeyCode() == java.awt.event.KeyEvent.VK_ENTER) {
doOK();
}
}
/**
* Saves the changes to the place controller
*/
private void doOK() {
Map<String, Integer> newTokenValues = getNewTokenValues();
if (canSetCapacity() && canSetNameValue()) {
placeController.startMultipleEdits();
int newCapacity = (Integer) capacitySpinner.getValue();
placeController.setCapacity(newCapacity);
String newName = nameTextField.getText();
placeController.setId(newName);
placeController.setTokenCounts(newTokenValues);
placeController.finishMultipleEdits();
exit();
}
}
/**
*
* @return true
*/
private boolean canSetCapacity() {
return true;
}
/**
*
* @return the capacity spinner value
*/
private Double getCapacitySpinnerValue() {
return (Double) capacitySpinner.getValue();
}
/**
* @return return false if could not set name
*/
private boolean canSetNameValue() {
String newName = nameTextField.getText();
if (newName.equals(placeController.getName())) {
return true;
}
if (!netController.isUniqueName(newName)) {
JOptionPane.showMessageDialog(null, "There is already a component named " + newName, "Error",
JOptionPane.WARNING_MESSAGE);
return false;
}
return true;
}
/**
* Sets the token values on the place
*
* @return new token counts, empty if could not change
*/
private Map<String, Integer> getNewTokenValues() {
Map<String, Integer> newTokenCounts = new HashMap<>();
int totalCount = calculateTokenCount();
if (placeController.hasCapacityRestriction() && totalCount > getCapacitySpinnerValue()) {
JOptionPane.showMessageDialog(null,
"Token counts exceed the capacity of place. Please alter capacity or tokens");
return newTokenCounts;
}
for (int tokenIndex = 0; tokenIndex < inputtedMarkings.size(); tokenIndex++) {
String tokenName = inputtedTokenClassNames.get(tokenIndex);
int newTokenCount = (Integer) inputtedMarkings.get(tokenIndex).getValue();
try {
if (newTokenCount < 0) {
JOptionPane.showMessageDialog(null, "Marking cannot be less than 0. Please re-enter");
newTokenCounts.clear();
return newTokenCounts;
}
if (placeController.getTokenCount(tokenName) != newTokenCount) {
newTokenCounts.put(tokenName, newTokenCount);
}
} catch (NumberFormatException ignored) {
JOptionPane.showMessageDialog(null, "Please enter a positive integer greater or equal to 0.",
"Invalid entry", JOptionPane.ERROR_MESSAGE);
newTokenCounts.clear();
return newTokenCounts;
} catch (HeadlessException e) {
LOGGER.log(Level.SEVERE, e.getMessage());
JOptionPane.showMessageDialog(null, "Please enter a positive integer greater or equal to 0.",
"Invalid entry", JOptionPane.ERROR_MESSAGE);
newTokenCounts.clear();
return newTokenCounts;
}
}
return newTokenCounts;
}
/**
* @return the total number of tokens declared for the place
*/
private int calculateTokenCount() {
int sum = 0;
for (JSpinner inputtedMarking : inputtedMarkings) {
Object value = inputtedMarking.getValue();
sum += (Integer) value;
}
return sum;
}
/**
* Performs the OK event
* @param evt OK event
*/
private void okButtonHandler(java.awt.event.ActionEvent evt) {
doOK();
}
/**
* Exits the dialog
*/
private void exit() {
rootPane.getParent().setVisible(false);
}
/**
* Exits the dialog
* @param evt cancel event
*/
private void cancelButtonHandler(java.awt.event.ActionEvent evt) {
exit();
}
/**
* Listens for changes in the capacity spinner
* @param evt change capacity event
*/
private void capacitySpinnerStateChanged(javax.swing.event.ChangeEvent evt) {
Double capacity = (Double) capacitySpinner.getValue();
setCapacityVisible(capacity);
}
}