king.flow.action.DefaultMsgSendAction.java Source code

Java tutorial

Introduction

Here is the source code for king.flow.action.DefaultMsgSendAction.java

Source

/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package king.flow.action;

import com.github.jsonj.JsonArray;
import com.github.jsonj.JsonObject;
import com.github.jsonj.exceptions.JsonParseException;
import com.github.jsonj.tools.JsonParser;
import java.awt.HeadlessException;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Vector;
import java.util.concurrent.ExecutionException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.JPasswordField;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
import javax.swing.table.DefaultTableModel;
import static king.flow.common.CommonConstants.ADVANCED_TABLE_CURRENT_PAGE;
import static king.flow.common.CommonConstants.ADVANCED_TABLE_TOTAL_PAGES;
import static king.flow.common.CommonConstants.ADVANCED_TABLE_VALUE;
import king.flow.common.CommonUtil;
import king.flow.view.Component;
import king.flow.view.ComponentEnum;
import org.jdesktop.swingx.JXDatePicker;
import static king.flow.common.CommonUtil.getLogger;
import static king.flow.common.CommonUtil.createTLSMessage;
import static king.flow.common.CommonUtil.parseTLSMessage;
import static king.flow.common.CommonUtil.getResourceMsg;
import static king.flow.common.CommonUtil.sendMessage;
import static king.flow.common.CommonUtil.shapeErrPrompt;
import king.flow.data.TLSResult;
import king.flow.swing.JXMsgPanel;
import king.flow.view.Action;
import king.flow.view.MsgSendAction;
import king.flow.view.Panel;
import king.flow.view.Rules;
import org.apache.commons.lang.StringEscapeUtils;
import org.json.simple.JSONArray;
import org.json.simple.parser.JSONParser;

/**
 *
 * @author LiuJin
 */
public class DefaultMsgSendAction extends DefaultBaseAction {

    protected final String prsCode;
    protected final int cmdCode;
    protected final List<Condition> conditions = new ArrayList<>();
    protected final List<String> conditionList;
    protected final MsgSendAction.NextStep next;
    protected final MsgSendAction.Exception error;
    protected Map<Integer, String> notNullMsg = null;
    protected final List<Integer> doneDisplayList = new ArrayList<>();
    protected final List<Integer> errDisplayList = new ArrayList<>();

    public DefaultMsgSendAction(String prsCode, int cmdCode, List<String> conditionList,
            MsgSendAction.NextStep next, MsgSendAction.Exception expPage, Rules checkRules) {
        this.prsCode = prsCode;
        this.cmdCode = cmdCode;
        this.conditionList = conditionList;
        this.next = next;
        this.error = expPage;
        this.rules = checkRules;
        parseDisplayList();
    }

    protected void parseDisplayList() throws NumberFormatException {
        ArrayList<String> displayList = CommonUtil.buildListParameters(this.next.getDisplay());
        for (String id : displayList) {
            doneDisplayList.add(Integer.parseInt(id));
        }

        displayList = CommonUtil.buildListParameters(this.error.getDisplay());
        for (String id : displayList) {
            errDisplayList.add(Integer.parseInt(id));
        }
    }

    @Override
    public void initializeData() {
        for (String c : conditionList) {
            final int id = Integer.parseInt(c);
            Object blockMeta = getBlockMeta(id);
            if (blockMeta instanceof king.flow.view.Component) {
                king.flow.view.Component cm = (king.flow.view.Component) blockMeta;
                ComponentEnum cType = cm.getType();
                switch (cType) {
                case TEXT_FIELD:
                case DATE:
                case COMBO_BOX:
                case PASSWORD_FIELD:
                    conditions.add(new Condition<>(getBlock(id, JComponent.class), cm));
                    break;
                default:
                    getLogger(DefaultMsgSendAction.class.getName()).log(Level.INFO,
                            "Ignore useless component is : {0}", cType.value());
                    break;
                }
            }
        }

        notNullMsg = initialNotNullTips();
    }

    @Override
    public void installButtonAction() {
        JButton button = (JButton) this.owner;
        button.addActionListener(new ButtonSendAction());
        button.addKeyListener(new EnterKeySendAction());
    }

    private Map<Integer, String> retrieveConditionValues(boolean cleanCargo) {
        HashMap<Integer, String> contents = new HashMap<>();
        for (Condition<JComponent, Component> condition : conditions) {
            String value = null;
            int id = condition.getMeta().getId();
            switch (condition.getMeta().getType()) {
            case TEXT_FIELD:
                JTextField jtf = (JTextField) condition.getComponent();
                value = jtf.getText();
                contents.put(id, value);
                break;
            case COMBO_BOX:
                JComboBox jcb = (JComboBox) condition.getComponent();
                if (jcb.isEditable()) {
                    value = jcb.getEditor().getItem().toString();
                } else {
                    value = jcb.getSelectedItem() == null ? null : jcb.getSelectedItem().toString();
                }
                contents.put(id, value);
                break;
            case DATE:
                JXDatePicker jxdp = (JXDatePicker) condition.getComponent();
                value = CommonUtil.toStringDate(jxdp.getDate());
                contents.put(id, value);
                break;
            case PASSWORD_FIELD:
                boolean isEncryption = false;
                List<Action> action = condition.getMeta().getAction();
                for (Action actionConf : action) {
                    if (actionConf.getEncryptKeyboardAction() != null) {
                        isEncryption = true;
                        break;
                    }
                }

                if (isEncryption) {
                    getLogger(DefaultMsgSendAction.class.getName()).log(Level.INFO, "use encrypted keyboard mode");
                    value = CommonUtil.retrieveCargo(Integer.toString(id));
                    //check notnull condition calling this method, so cannot clean pinblock
                    if (cleanCargo) {
                        CommonUtil.cleanTranStation(Integer.toString(id));
                    }
                } else {
                    getLogger(DefaultMsgSendAction.class.getName()).log(Level.INFO, "use normal keyboard mode");
                    JPasswordField jpf = (JPasswordField) condition.getComponent();
                    final String unwrapped = new String(jpf.getPassword());
                    if (unwrapped.length() > 0) {
                        try {
                            value = CommonUtil.inputString(unwrapped);
                        } catch (Throwable e) {
                            getLogger(DefaultMsgSendAction.class.getName()).log(Level.WARNING,
                                    "crypto keyboard is out of work due to\n {0}", shapeErrPrompt(e));
                            value = unwrapped;
                        }
                    } else {
                        value = unwrapped;
                    }
                }

                contents.put(id, value);
                break;
            default:
                getLogger(DefaultMsgSendAction.class.getName()).log(Level.INFO, "Ignore useless component is : {0}",
                        condition.getMeta().getType().value());
                break;
            }
        }
        return contents;
    }

    protected Map<Integer, String> retrieveConditionValues() {
        return retrieveConditionValues(true);
    }

    public void showResult(TLSResult result) throws HeadlessException {
        CommonUtil.showMsg(owner.getTopLevelAncestor(), result.getOkMsg());
    }

    private void showOnComponent(Object metaNode, TLSResult result) {
        Component meta = (Component) metaNode;
        getLogger(CommunicationWorker.class.getName()).log(Level.INFO, "Display component[{0}] type : {1}",
                new Object[] { String.valueOf(meta.getId()), meta.getType().value() });
        JsonParser jsonParser;
        JsonArray arrays;
        switch (meta.getType()) {
        case TABLE:
            try {
                jsonParser = new JsonParser();
                arrays = jsonParser.parse(result.getOkMsg()).asArray();
                JTable table = getBlock(meta.getId(), JTable.class);
                DefaultTableModel model = (DefaultTableModel) table.getModel();
                model.setRowCount(0);
                for (Iterator it = arrays.iterator(); it.hasNext();) {
                    JsonArray row = (JsonArray) it.next();
                    Vector<String> rowData = new Vector<>();
                    for (Object v : row) {
                        rowData.add(v.toString());
                    }
                    model.addRow(rowData);
                }
            } catch (Exception e) {
                getLogger(CommunicationWorker.class.getName()).log(Level.WARNING,
                        "Invalid array data [ {0} ] for TABLE component[{1}], \n exception is {2}",
                        new Object[] { result.getOkMsg(), String.valueOf(meta.getId()), e });
            }
            break;
        case ADVANCED_TABLE:
            try {
                jsonParser = new JsonParser();
                JsonObject jsonObj = jsonParser.parse(result.getOkMsg()).asObject();
                Integer total = jsonObj.getInt(ADVANCED_TABLE_TOTAL_PAGES);
                Integer page = jsonObj.getInt(ADVANCED_TABLE_CURRENT_PAGE);
                arrays = jsonObj.getArray(ADVANCED_TABLE_VALUE);
                getLogger(CommunicationWorker.class.getName()).log(Level.INFO,
                        "Dump JSON DATA for ADVANCED_TABLE: \n{0} \ntotal: {1} \npage: {2}",
                        new Object[] { jsonObj.toString(), total, page });
                JXMsgPanel advanceTable = getBlock(meta.getId(), JXMsgPanel.class);
                advanceTable.refreshTotalPages(total);
                advanceTable.refreshCurrentPage(page);
                advanceTable.refreshTable(arrays);
            } catch (Exception e) {
                getLogger(CommunicationWorker.class.getName()).log(Level.WARNING,
                        "Invalid complex data [ {0} ] for ADVANCED_TABLE component[{1}] \n exception is {2}",
                        new Object[] { result.getOkMsg(), String.valueOf(meta.getId()), e });
            }
            break;
        case LABEL:
            JLabel label = getBlock(meta.getId(), JLabel.class);
            if (result.getRetCode() == 0) {
                label.setText(StringEscapeUtils.unescapeHtml(result.getOkMsg()));
            } else {
                label.setText(StringEscapeUtils.unescapeHtml(result.getErrMsg()));
            }
            break;
        case TEXT_FIELD:
            JTextField textField = getBlock(meta.getId(), JTextField.class);
            if (result.getRetCode() == 0) {
                textField.setText(result.getOkMsg());
            } else {
                CommonUtil.showErrorMsg(textField.getTopLevelAncestor(), result.getErrMsg());
            }
            break;
        default:
            getLogger(CommunicationWorker.class.getName()).log(Level.WARNING,
                    "Unsupported displayed component type : {0}", meta.getType());
        }
    }

    protected void showErrMsg(final int retCode, final String msg) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                TLSResult result = new TLSResult();
                result.setRetCode(retCode);
                result.setErrMsg(msg);

                if (errDisplayList.size() == 1) {
                    showOnComponent(getBlockMeta(errDisplayList.get(0)), result);
                } else {
                    try {
                        JSONParser jsonParser = new JSONParser();
                        Object element = jsonParser.parse(result.getErrMsg());
                        if (element instanceof JSONArray) {
                            JSONArray jsonArray = (JSONArray) element;
                            int len = Integer.min(errDisplayList.size(), jsonArray.size());
                            for (int i = 0; i < len; i++) {
                                TLSResult freshResult = new TLSResult(result.getRetCode(), result.getOkMsg(),
                                        jsonArray.get(i).toString(), result.getPrtMsg());
                                showOnComponent(getBlockMeta(errDisplayList.get(i)), freshResult);
                            }
                        } else {
                            showOnComponent(getBlockMeta(errDisplayList.get(0)), result);
                        }
                    } catch (Exception e) {
                        getLogger(DefaultMsgSendAction.class.getName()).log(Level.WARNING,
                                "Exception encounters during failed json value parsing : \n{0}", e);
                    }
                }

                panelJump(error.getNextPanel());
            }
        });
    }

    protected void showDoneMsg(final TLSResult result) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                if (doneDisplayList.size() == 1) {
                    showOnComponent(getBlockMeta(doneDisplayList.get(0)), result);
                } else {
                    try {
                        JSONParser jsonParser = new JSONParser();
                        Object element = jsonParser.parse(result.getOkMsg());
                        if (element instanceof JSONArray) {
                            JSONArray jsonArray = (JSONArray) element;
                            int len = Integer.min(doneDisplayList.size(), jsonArray.size());
                            for (int i = 0; i < len; i++) {
                                TLSResult freshResult = new TLSResult(result.getRetCode(),
                                        jsonArray.get(i).toString(), result.getErrMsg(), result.getPrtMsg());
                                showOnComponent(getBlockMeta(doneDisplayList.get(i)), freshResult);
                            }
                        } else {
                            showOnComponent(getBlockMeta(doneDisplayList.get(0)), result);
                        }
                    } catch (Exception e) {
                        getLogger(DefaultMsgSendAction.class.getName()).log(Level.WARNING,
                                "Exception encounters during successful json value parsing : \n{0}", e);
                    }
                }

                CommonUtil.cachePrintMsg(result.getPrtMsg());
                if (result.getRedirection() != null) {
                    String redirection = result.getRedirection();
                    getLogger(DefaultMsgSendAction.class.getName()).log(Level.INFO,
                            "Be forced to jump to page[{0}], ignore designated page[{1}]",
                            new Object[] { redirection, String.valueOf(next.getNextPanel()) });
                    int forwardPage = 0;
                    try {
                        forwardPage = Integer.parseInt(redirection);
                    } catch (Exception e) {
                        panelJump(next.getNextPanel());
                        return;
                    }
                    Object blockMeta = getBlockMeta(forwardPage);
                    if (blockMeta == null || !(blockMeta instanceof Panel)) {
                        getLogger(DefaultMsgSendAction.class.getName()).log(Level.INFO,
                                "Be forced to jump to page[{0}], but page[{0}] is invalid Panel type. It is type[{1}]",
                                new Object[] { redirection,
                                        (blockMeta == null ? "NULL" : blockMeta.getClass().getSimpleName()) });
                        panelJump(next.getNextPanel());
                        return;
                    }
                    panelJump(forwardPage);
                } else {
                    panelJump(next.getNextPanel());

                    //trigger the action denoted in sendMsgAction
                    Integer trigger = next.getTrigger();
                    if (trigger != null && getBlockMeta(trigger) != null) {
                        final Component blockMeta = (Component) getBlockMeta(trigger);
                        switch (blockMeta.getType()) {
                        case COMBO_BOX:
                            JComboBox comboBlock = getBlock(trigger, JComboBox.class);
                            ItemListener[] itemListeners = comboBlock.getItemListeners();
                            ItemEvent e = new ItemEvent(comboBlock, ItemEvent.ITEM_STATE_CHANGED,
                                    comboBlock.getItemAt(comboBlock.getItemCount() - 1).toString(),
                                    ItemEvent.SELECTED);
                            for (ItemListener itemListener : itemListeners) {
                                itemListener.itemStateChanged(e);//wait and hang on util progress dialog gets to dispose
                            }
                            if (comboBlock.isEditable()) {
                                String value = comboBlock.getEditor().getItem().toString();
                                if (value.length() == 0) {
                                    return;
                                }
                            }
                            break;
                        case BUTTON:
                            JButton btnBlock = getBlock(trigger, JButton.class);
                            btnBlock.doClick();
                            break;
                        default:
                            getLogger(DefaultMsgSendAction.class.getName()).log(Level.WARNING,
                                    "Invalid trigger component[{0}] as unsupported type[{1}]",
                                    new Object[] { trigger, blockMeta.getType() });
                            break;
                        }
                    } //trigger dealing 
                      //keep next cursor on correct component
                    Integer nextCursor = next.getNextCursor();
                    if (nextCursor != null && getBlockMeta(nextCursor) != null) {
                        JComponent block = getBlock(nextCursor, JComponent.class);
                        block.requestFocusInWindow();
                    }
                } // no redirection branch
            }
        });
    }

    private Map<Integer, String> initialNotNullTips() {
        HashMap<Integer, String> notNullTips = new HashMap<>();
        if (rules != null) {
            List<Rules.NotNull> notNull = rules.getNotNull();
            for (Rules.NotNull n : notNull) {
                int content = n.getContent();
                String errMsg = n.getErrMsg();
                if (errMsg != null && errMsg.length() > 0) {
                    notNullTips.put(content, errMsg);
                }
            }
        }

        return notNullTips;
    }

    @Override
    protected boolean checkNotNull() {
        Map<Integer, String> conditionValues = retrieveConditionValues(false);
        Set<Map.Entry<Integer, String>> entrySet = conditionValues.entrySet();
        for (Map.Entry<Integer, String> set : entrySet) {
            String v = set.getValue();
            if (v == null || v.length() == 0) {
                String err = notNullMsg.get(set.getKey()) == null
                        ? CommonUtil.getResourceMsg("operation.submit.inadequate.prompt")
                        : notNullMsg.get(set.getKey());
                getLogger(DefaultMsgSendAction.class.getName()).log(Level.INFO,
                        "{0} for component id {1} value {2}",
                        new Object[] { err, Integer.toString(set.getKey()), v });
                CommonUtil.showMsg(owner.getTopLevelAncestor(), err);
                return false;
            }
        }
        return true;
    }

    protected boolean validateRules() {
        if (rules != null) {
            boolean checkNotNull = checkNotNull();
            if (!checkNotNull) {
                return checkNotNull;
            }

            boolean checkTemplate = checkTemplate();
            if (!checkTemplate) {
                return checkTemplate;
            }

            boolean checkCJK = checkCJK();
            if (!checkCJK) {
                return checkCJK;
            }

            boolean checkEqualConditions = checkEqualConditions();
            if (!checkEqualConditions) {
                return checkEqualConditions;
            }

            boolean checkNotEqualConditions = checkNotEqualConditions();
            if (!checkNotEqualConditions) {
                return checkNotEqualConditions;
            }
        }
        return true;
    }

    protected void send() {
        if (!validateRules()) {
            return;
        }

        waitCommunicationTask(new CommunicationWorker());
    }

    class ButtonSendAction implements ActionListener {

        @Override
        public void actionPerformed(ActionEvent e) {
            send();
        }

    }

    class EnterKeySendAction extends KeyAdapter {

        @Override
        public void keyPressed(KeyEvent e) {
            if (e.getKeyCode() == KeyEvent.VK_ENTER) {
                getLogger(EnterKeySendAction.class.getName()).log(Level.INFO, "Enter key input and send message");
                send();
            }
        }
    }

    class Condition<C, M> {

        private final C component;
        private final M meta;

        public Condition(C component, M meta) {
            this.component = component;
            this.meta = meta;
        }

        public C getComponent() {
            return component;
        }

        public M getMeta() {
            return meta;
        }

    }

    private class CommunicationWorker extends SwingWorker<String, Integer> {

        @Override
        protected String doInBackground() throws Exception {
            Thread.sleep(1000);
            Map<Integer, String> conditionValues = retrieveConditionValues();
            String msg = createTLSMessage(prsCode, conditionValues);
            getLogger(DefaultMsgSendAction.class.getName()).log(Level.INFO, "Sending TLS Message : \n{0}", msg);
            String resp = null;
            try {
                if (cmdCode < 0) {
                    resp = sendMessage(msg);
                } else {
                    resp = sendMessage(cmdCode, msg);
                }
            } catch (Exception exception) {
                showErrMsg(Integer.MIN_VALUE, getResourceMsg("network.unaccessed.prompt"));
                throw exception;
            }
            return resp;
        }

        @Override
        protected void done() {
            try {
                String value = get();
                if (value != null) {
                    value = value.trim();
                    getLogger(CommunicationWorker.class.getName()).log(Level.FINE,
                            "Retrieve response from server : \n{0}", value);
                } else {
                    getLogger(CommunicationWorker.class.getName()).log(Level.INFO,
                            "Retrieve no response from server");
                    showErrMsg(Integer.MIN_VALUE, getResourceMsg("terminal.no.response.prompt"));
                    return;
                }

                TLSResult result = parseTLSMessage(value);
                getLogger(CommunicationWorker.class.getName()).log(Level.INFO, "Transform response to {0}", result);
                if (result == null) {
                    getLogger(CommunicationWorker.class.getName()).log(Level.INFO,
                            "Receive invalidated result {0} from server", value);
                    showErrMsg(Integer.MIN_VALUE, getResourceMsg("terminal.invalidated.response.prompt"));
                    return;
                } else if (result.getRetCode() != 0) {
                    final String errMsg = result.getErrMsg();
                    getLogger(CommunicationWorker.class.getName()).log(Level.INFO,
                            "Operation action failed with retcode {0}, root cause {1}",
                            new Object[] { result.getRetCode(), errMsg });
                    showErrMsg(Integer.MIN_VALUE,
                            (errMsg == null || errMsg.length() == 0)
                                    ? getResourceMsg("terminal.failed.operation.prompt")
                                    : errMsg);
                    return;
                }

                if (next == null) {
                    Logger.getLogger(CommunicationWorker.class.getName()).log(Level.INFO,
                            "No next display page is setup : {0}", next == null ? next : next.getDisplay());
                    showResult(result);
                } else {
                    //show msg to dedicated component
                    showDoneMsg(result);
                }
            } catch (InterruptedException | ExecutionException | JsonParseException ex) {
                getLogger(CommunicationWorker.class.getName()).log(Level.WARNING, null, ex);
            }
        }

    }

}