package org.joget.designer.jped; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Component; import java.awt.Dimension; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import javax.swing.DefaultCellEditor; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JComponent; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTable; import javax.swing.ListSelectionModel; import javax.swing.SwingConstants; import javax.swing.SwingUtilities; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import javax.swing.table.DefaultTableModel; import javax.swing.table.TableCellRenderer; import javax.swing.tree.TreePath; import org.enhydra.jawe.JaWEAction; import org.enhydra.jawe.JaWEComponent; import org.enhydra.jawe.JaWEComponentView; import org.enhydra.jawe.JaWEManager; import org.enhydra.jawe.ResourceManager; import org.enhydra.jawe.Utils; import org.enhydra.jawe.base.controller.JaWEFrame; import org.enhydra.jawe.base.editor.StandardXPDLElementEditor; import org.enhydra.jawe.base.xpdlvalidator.ValidationError; import org.enhydra.jawe.components.XPDLTreeModel; import org.enhydra.jawe.components.XPDLTreeNode; import org.enhydra.jawe.components.graph.Graph; import org.enhydra.jawe.components.graph.GraphController; import org.enhydra.jawe.components.simplenavigator.SimpleNavigator; import org.enhydra.jawe.components.simplenavigator.SimpleNavigatorPanel; import org.enhydra.shark.xpdl.XMLComplexElement; import org.enhydra.shark.xpdl.XMLElement; import org.enhydra.shark.xpdl.XMLValidationError; import org.enhydra.shark.xpdl.elements.Activity; import org.enhydra.shark.xpdl.elements.Transition; import org.enhydra.shark.xpdl.elements.WorkflowProcess; import org.joget.designer.Designer; /** * Panel to show the warnings and errors in the process, and to suggest corrective actions. */ public class CustomValidatorPanel extends JPanel implements JaWEComponentView { protected static Dimension listDimension = new Dimension(230, 250); protected CustomValidator controller; protected JLabel designLabel; protected JButton deployButton; protected JTable table; protected List errorList; public CustomValidatorPanel( CustomValidator controller) { this.controller = controller; init(); } public void configure() { } public void init() { setLayout(new BorderLayout()); // add top design label designLabel = new JLabel(ResourceManager.getLanguageDependentString("DesignOK")); designLabel.setMinimumSize(new Dimension(200, 25)); designLabel.setPreferredSize(new Dimension(200, 25)); designLabel.setHorizontalAlignment(JLabel.CENTER); designLabel.setIcon(new ImageIcon(getClass().getClassLoader().getResource("org/enhydra/jawe/images/packagecheck.gif"))); add(designLabel, BorderLayout.NORTH); // add bottom deploy button deployButton = new JButton(ResourceManager.getLanguageDependentString("DeployOK")); deployButton.setMinimumSize(new Dimension(200, 25)); deployButton.setPreferredSize(new Dimension(200, 25)); deployButton.setIcon(new ImageIcon(getClass().getClassLoader().getResource("org/enhydra/jawe/images/packagecheck.gif"))); add(deployButton, BorderLayout.SOUTH); // add scrollpane final JaWEFrame jf = JaWEManager.getInstance().getJaWEController().getJaWEFrame(); JScrollPane scrollPane = new JScrollPane(); scrollPane.setAlignmentX(Component.LEFT_ALIGNMENT); scrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); add(scrollPane, BorderLayout.CENTER); // add table table = new JTable() { @Override public boolean isCellEditable(int rowIndex, int vColIndex) { return (vColIndex != 0); } }; table.setFillsViewportHeight(true); table.setRowHeight(55); table.setShowGrid(false); table.setBackground(Color.WHITE); table.setIntercellSpacing(new Dimension(0, 0)); table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); table.getSelectionModel().addListSelectionListener(new CustomListListener()); table.setModel(new DefaultTableModel(new Object[][] {}, new Object[] { "", "" })); table.getColumnModel().getColumn(0).setCellRenderer(new CustomCellRenderer()); table.getColumnModel().getColumn(1).setCellRenderer(new CustomButtonRenderer(new JCheckBox())); table.getColumnModel().getColumn(1).setCellEditor(new CustomButtonRenderer(new JCheckBox())); table.getColumnModel().getColumn(1).setPreferredWidth(40); table.getColumnModel().getColumn(1).setMaxWidth(40); scrollPane.setViewportView(table); } public JaWEComponent getJaWEComponent() { return controller; } public JComponent getDisplay() { return this; } protected void fillListContent(List errors) { boolean hasError = false; boolean hasWarning = false; // update error list this.errorList = new ArrayList(errors); DefaultTableModel tableModel = (DefaultTableModel) table.getModel(); while (tableModel.getRowCount() > 0) { tableModel.removeRow(0); } Iterator it = errors.iterator(); while (it.hasNext()) { Object el = it.next(); if (el instanceof ValidationError) { ValidationError err = (ValidationError) el; // determine whether error or warning String color = "yellow"; String icon = "warning_icon.gif"; if (XMLValidationError.TYPE_ERROR.equals(err.getType())) { color = "red"; icon = "error_icon.gif"; hasError = true; } else if (XMLValidationError.TYPE_WARNING.equals(err.getType())) { hasWarning = true; } XMLElement element = Utils.getLocation(err.getElement()); // determine error message String errorId = err.getId(); String error = ResourceManager.getLanguageDependentString(errorId); if (error == null) { error = errorId; } // determine error type, element type and name String errorType = ResourceManager.getLanguageDependentString(err.getType() + "TypeKey"); String elementType = ResourceManager.getLanguageDependentString(element.toName() + "Key"); String name = ""; if (element instanceof XMLComplexElement) { XMLElement idEl = (XMLElement) ((XMLComplexElement) element).get("Name"); name = (idEl != null) ? idEl.toValue() : ""; if (name == null || name.trim().length() == 0) { idEl = (XMLElement) ((XMLComplexElement) element).get("Id"); name = (idEl != null) ? idEl.toValue() : ""; } } // compose label message String message = "<html>" // + "<span bgcolor=\"" + color + "\">    </span> " + "<b><font size=\"2\">" + errorType + ": " + name + "</font></b><br>" + "<font size=\"2\">" + error + "</font>" + "</html>"; JLabel label = new JLabel(message, new ImageIcon(getClass().getClassLoader().getResource("org/enhydra/jawe/images/" + icon)), SwingConstants.LEFT); // add row tableModel.addRow(new Object[] { label, getSuggestionKey(error) }); } else if (el instanceof String) { tableModel.addRow(new Object[] { el }); } } // update label and button if (hasError) { // error, don't allow deployment designLabel.setIcon(new ImageIcon(getClass().getClassLoader().getResource("org/enhydra/jawe/images/delete.gif"))); designLabel.setText(ResourceManager.getLanguageDependentString("DesignNotOK")); deployButton.setIcon(new ImageIcon(getClass().getClassLoader().getResource("org/enhydra/jawe/images/delete.gif"))); deployButton.setText(ResourceManager.getLanguageDependentString("DeployNotOK")); deployButton.setEnabled(false); } else { // no error, allow deployment designLabel.setIcon(new ImageIcon(getClass().getClassLoader().getResource("org/enhydra/jawe/images/packagecheck.gif"))); designLabel.setText(ResourceManager.getLanguageDependentString("DesignOK")); deployButton.setIcon(new ImageIcon(getClass().getClassLoader().getResource("org/enhydra/jawe/images/packagecheck.gif"))); deployButton.setText(ResourceManager.getLanguageDependentString("DeployOK")); if (true || Designer.isPackageFixed()) { deployButton.setEnabled(true); deployButton.setAction(new Deploy(JaWEManager.getInstance().getJaWEController())); } else { deployButton.setEnabled(false); } } } public void cleanup() { table = null; } protected List getErrorList() { return errorList; } /** * Retrieves the workflow element based on the selected row * @return */ protected XMLElement getSelectedElement() { XMLElement element = null; int selected = table.getSelectedRow(); if (selected >= 0 && errorList.size() > selected) { Object el = errorList.get(selected); if (el instanceof ValidationError) { // get error ValidationError err = (ValidationError) el; element = Utils.getLocation(err.getElement()); } } return element; } /** * Listener to select the affected element in the graph when an error is selected. */ class CustomListListener implements ListSelectionListener { public void valueChanged(ListSelectionEvent lse) { XMLElement element = getSelectedElement(); if (element != null) { // select element in graph GraphController gc = (GraphController) JaWEManager.getInstance().getComponentManager().getComponent("GraphComponent"); Graph g = null; WorkflowProcess wp = null; if (element instanceof Activity) { wp = (WorkflowProcess) element.getParent().getParent(); g = gc.getGraph(wp); gc.selectGraphForElement(wp); g.selectActivity((Activity) element, false); } else if (element instanceof Transition) { wp = (WorkflowProcess) element.getParent().getParent(); g = gc.getGraph(wp); gc.selectGraphForElement(wp); g.selectTransition((Transition) element, false); } // hilite affected process in tree if (wp != null) { SimpleNavigator tcon = (SimpleNavigator) JaWEManager.getInstance().getComponentManager().getComponent("SimpleNavigatorComponent"); SimpleNavigatorPanel panel = (SimpleNavigatorPanel) (tcon.getView()); panel.getTree().removeTreeSelectionListener(tcon); XPDLTreeNode n = ((XPDLTreeModel) panel.getTreeModel()).findNode(wp); TreePath tp = null; if (n != null) { tp = new TreePath(n.getPath()); panel.getTree().addSelectionPath(tp); } if (tp != null) { panel.getTree().scrollPathToVisible(tp); } panel.getTree().addTreeSelectionListener(tcon); } } } } /** * Custom cell renderer to display the error message */ class CustomCellRenderer extends JLabel implements TableCellRenderer { public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { if (value instanceof Component) { Component comp = (Component)value; if (isSelected) { JLabel label = (JLabel)comp; label.setOpaque(true); label.setBackground(table.getSelectionBackground()); } else { comp.setBackground(Color.WHITE); } return comp; } else { setText(value.toString()); setOpaque(true); if (isSelected) { setBackground(table.getSelectionBackground()); } else { setBackground(Color.WHITE); } return this; } } } /** * Custom cell renderer and editor to show a button that can trigger an action */ public class CustomButtonRenderer extends DefaultCellEditor implements TableCellRenderer { protected JPanel panel; protected JButton button; private String message; private boolean isPushed; public CustomButtonRenderer(JCheckBox checkBox) { super(checkBox); button = new JButton(); button.setBackground(Color.WHITE); button.setOpaque(false); button.setBorderPainted(false); button.setContentAreaFilled(false); button.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { fireEditingStopped(); } }); panel = new JPanel(); panel.setLayout(new BorderLayout()); panel.setBackground(Color.WHITE); panel.add(button, BorderLayout.CENTER); } public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { if (value != null && value instanceof String && value.toString().trim().length() > 0) { button.setIcon(new ImageIcon(getClass().getClassLoader().getResource("org/enhydra/jawe/images/idea.png"))); } else { button.setIcon(null); } if (isSelected) { panel.setBackground(table.getSelectionBackground()); } else { panel.setBackground(Color.WHITE); } return panel; } @Override public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) { if (value != null && value instanceof String && value.toString().trim().length() > 0) { button.setIcon(new ImageIcon(getClass().getClassLoader().getResource("org/enhydra/jawe/images/idea.png"))); } else { button.setIcon(null); } if (isSelected) { panel.setBackground(table.getSelectionBackground()); } else { panel.setBackground(Color.WHITE); } message = (value == null) ? "" : value.toString(); isPushed = true; return panel; } @Override public Object getCellEditorValue() { if (isPushed && message != null && message.trim().length() > 0) { SwingUtilities.invokeLater(new Runnable() { public void run() { handleSuggestion(button, message); } }); } isPushed = false; return message; } @Override public boolean stopCellEditing() { isPushed = false; return super.stopCellEditing(); } @Override protected void fireEditingStopped() { super.fireEditingStopped(); } } /** * Returns a non empty String if there is a suggestion for the error. * @param errorId * @return */ protected String getSuggestionKey(String error) { if (error.equals(ResourceManager.getLanguageDependentString("ERROR_WORKFLOW_PROCESS_NOT_DEFINED")) || error.equals(ResourceManager.getLanguageDependentString("ErrorIncomingTransitionOrConnectionFromStartBubbleIsMissing")) || error.equals(ResourceManager.getLanguageDependentString("ErrorOutgoingTransitionOrConnectionToEndBubbleIsMissing")) || error.equals(ResourceManager.getLanguageDependentString("WARNING_CONDITIONAL_TRANSITION_WITHOUT_EXPRESSION")) || error.equals(ResourceManager.getLanguageDependentString("WARNING_EXCEPTION_TRANSITION_WITHOUT_EXPRESSION")) || error.equals(ResourceManager.getLanguageDependentString("WARNING_UNCONDITIONAL_TRANSITION_WITH_EXPRESSION")) || error.equals(ResourceManager.getLanguageDependentString("WARNING_DEFAULT_EXCEPTION_TRANSITION_WITH_EXPRESSION")) || error.equals(ResourceManager.getLanguageDependentString("WARNING_OTHERWISE_TRANSITION_WITH_EXPRESSION")) || error.equals(ResourceManager.getLanguageDependentString("WARNING_CONDITION_EXPRESSION_POSSIBLY_INVALID")) || error.equals(ResourceManager.getLanguageDependentString("WARNING_DEADLINE_EXPRESSION_POSSIBLY_INVALID"))) { return error; } else { return ""; } } /** * Perform suggestion based on the error/warning message * @param button * @param error */ protected void handleSuggestion(JButton button, String error) { // TODO: temporarily handle everything in this method, best to implement some design pattern to make this method cleaner if (error.equals(ResourceManager.getLanguageDependentString("ERROR_WORKFLOW_PROCESS_NOT_DEFINED"))) { // show initial help message String message = getSuggestionMessage(error, ResourceManager.getLanguageDependentString("GUIDE_ERROR_WORKFLOW_PROCESS_NOT_DEFINED")); JOptionPane.showMessageDialog(this.getRootPane(), message); } else if (error.equals(ResourceManager.getLanguageDependentString("ErrorIncomingTransitionOrConnectionFromStartBubbleIsMissing")) || error.equals(ResourceManager.getLanguageDependentString("ErrorOutgoingTransitionOrConnectionToEndBubbleIsMissing"))) { // suggest to insert missing start/end String message = getSuggestionMessage(error, ResourceManager.getLanguageDependentString("GUIDE_ERROR_START_END")); int result = JOptionPane.showConfirmDialog(this.getRootPane(), message, ResourceManager.getLanguageDependentString("GUIDE_TITLE"), JOptionPane.YES_NO_OPTION); if (result == JOptionPane.YES_OPTION) { // insert missing start/end GraphController gc = (GraphController)JaWEManager.getInstance().getComponentManager().getComponent("GraphComponent"); JaWEAction action = gc.getGraphSettings().getAction("InsertMissingStartAndEndBubbles"); action.getAction().actionPerformed(null); } } else if (error.equals(ResourceManager.getLanguageDependentString("WARNING_CONDITIONAL_TRANSITION_WITHOUT_EXPRESSION")) || error.equals(ResourceManager.getLanguageDependentString("WARNING_EXCEPTION_TRANSITION_WITHOUT_EXPRESSION")) || error.equals(ResourceManager.getLanguageDependentString("WARNING_UNCONDITIONAL_TRANSITION_WITH_EXPRESSION")) || error.equals(ResourceManager.getLanguageDependentString("WARNING_DEFAULT_EXCEPTION_TRANSITION_WITH_EXPRESSION")) || error.equals(ResourceManager.getLanguageDependentString("WARNING_OTHERWISE_TRANSITION_WITH_EXPRESSION")) || error.equals(ResourceManager.getLanguageDependentString("WARNING_CONDITION_EXPRESSION_POSSIBLY_INVALID")) || error.equals(ResourceManager.getLanguageDependentString("WARNING_DEADLINE_EXPRESSION_POSSIBLY_INVALID"))) { // suggest to edit transition properties String message = getSuggestionMessage(error, ResourceManager.getLanguageDependentString("GUIDE_WARNING_TRANSITION")); int result = JOptionPane.showConfirmDialog(this.getRootPane(), message, ResourceManager.getLanguageDependentString("GUIDE_TITLE"), JOptionPane.YES_NO_OPTION); if (result == JOptionPane.YES_OPTION) { // show transition dialog XMLElement element = getSelectedElement(); StandardXPDLElementEditor ed = new StandardXPDLElementEditor(); ed.editXPDLElement(element); } } else { // show default message String message = getSuggestionMessage(error, ResourceManager.getLanguageDependentString("GUIDE_NO_SUGGESTIONS")); JOptionPane.showMessageDialog(this.getRootPane(), message, ResourceManager.getLanguageDependentString("GUIDE_TITLE"), JOptionPane.INFORMATION_MESSAGE); } } protected String getSuggestionMessage(String error, String message) { // help message String html = "<html>" + "<b>" + error + "</b>" + "<br><br><font size=\"3\">" + message + "</font><br><br>" + "</html>"; return html; } }