Package org.zyp.cn8583

Source Code of org.zyp.cn8583.cnMessageFactory

package org.zyp.cn8583;

import java.text.ParseException;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Arrays;

import org.apache.log4j.Logger;

import org.zyp.cn8583.parse.cnFieldParseInfo;


/**
* ����һ���й����8583��ʽ��׼���࣬��ʼ������Դ��MessageFactory�ࡣ
* This class is used to create messages, either from scratch or from an existing String or byte
* buffer. It can be configured to put default values on newly created messages, and also to know
* what to expect when reading messages from an InputStream.
* <P>
* The factory can be configured to know what values to set for newly created messages, both from
* a template (useful for fields that must be set with the same value for EVERY message created)
* and individually (for trace [field 11] and message date [field 7]).
* <P>
* It can also be configured to know what fields to expect in incoming messages (all possible values
* must be stated, indicating the date type for each). This way the messages can be parsed from
* a byte buffer.
*
* @author zyplanke
*
*/
public class cnMessageFactory  {
  protected static final Logger log = Logger.getLogger("J8583CN_LOG");

  /** This map stores the message template for each message msgtypeid. (msgtypeid, Message)*/
  private Map<String, cnMessage> typeTemplates = new HashMap<String, cnMessage>();
  /** Stores the information needed to parse messages sorted by type. (msgtypeid, (fieldID, fieldInfo))*/
  private Map<String, Map<Integer, cnFieldParseInfo>> parseMap = new HashMap<String, Map<Integer, cnFieldParseInfo>>();
  /** Stores the field numbers to be parsed, in order of appearance. (msgtypeid, fieldID)*/
  private Map<String, List<Integer>> parseOrder = new HashMap<String, List<Integer>>();

  private cnSystemTraceNumGenerator SystraceNumGen;
  /** The 8583 header to be included in each message msgtypeid. (msgtypeid, headerlength)*/
  private Map<String, Integer> msgheadersattr = new HashMap<String, Integer>();
  /** Indicates if the current date should be set on new messages (field 7). */
  private boolean usecurrentdata;
  /** Indicates if the factory should create binary messages and also parse binary messages. */
  private boolean useBinary;
  private int etx = -1;

  /** Tells the receiver to create and parse binary messages if the flag is true.
   * Default is false, that is, create and parse ASCII messages. */
  public void setUseBinary(boolean flag) {
    useBinary = flag;
  }
  /** Returns true is the factory is set to create and parse binary messages,
   * false if it uses ASCII messages. Default is false. */
  public boolean getUseBinary() {
    return useBinary;
  }

  /** Sets the ETX character to be sent at the end of the message. This is optional and the
   * default is -1, which means nothing should be sent as terminator.
   * @param value The ASCII value of the ETX character or -1 to indicate no terminator should be used. */
  public void setEtx(int value) {
    etx = value;
  }

  /** Creates a new message of the specified type id from message template. If the factory is set to use binary
   * messages, then the returned message will be written using binary coding.
   * @param msgtypeid The message type id, ӦΪ4���ֽ��ַ�*/
  public cnMessage newMessagefromTemplate(String msgtypeid) {
    cnMessage m = new cnMessage(msgtypeid, msgheadersattr.get(msgtypeid));
    m.setEtx(etx);
    m.setBinary(useBinary);

    //Copy the values from the template (ͨ������ģ��������ֵ)
    cnMessage templ = typeTemplates.get(msgtypeid);
    if (templ != null) {
      for (int i = 2; i < 128; i++) {
        if (templ.hasField(i)) {
          m.setField(i, templ.getField(i).clone());
        }
      }
    }
    if (SystraceNumGen != null) {
      m.setValue(11, SystraceNumGen.nextTrace(), cnType.NUMERIC, 6);
    }
    if (usecurrentdata) {
      m.setValue(7, new Date(), cnType.DATE10, 10);
    }
    return m;
  }

  /** Creates a message to respond to a request. <P/>
   * ����������������Ӧ���� (��Ӧ���ĵı�ʾ�ĵ���λΪ�������ĵ���λ��һ) <P/>
   * sets all fields from the template if there is one, and copies all values from the request,
   * overwriting fields from the template if they overlap.
   * @param request An 8583 message with a request type (ending in 00). */
  public cnMessage createResponse(cnMessage request) {
    String resptypeid = request.getMsgTypeID().substring(0, 2)
              + Integer.toString(Integer.parseInt(request.getMsgTypeID().substring(2,3)) + 1)
              + request.getMsgTypeID().substring(3, 4);
    cnMessage resp = new cnMessage(resptypeid, msgheadersattr.get(resptypeid));
 
    resp.setBinary(request.isBinary());
    resp.setEtx(etx);
    //Copy the values from the template
    cnMessage templ = typeTemplates.get(resp.getMsgTypeID());
    if (templ != null) {
      for (int i = 2; i < 128; i++) {
        if (templ.hasField(i)) {
          resp.setField(i, templ.getField(i).clone());
        }
      }
    }
    // copy the values from request message
    for (int i = 2; i < 128; i++) {
      if (request.hasField(i)) {
        resp.setField(i, request.getField(i).clone());
      }
    }
    return resp;
  }

  /**
   * Creates a new message instance from the buffer, which must contain a
   * valid 8583 message. If the factory is set to use binary messages then it
   * will try to parse a binary message.
   *
   * @param buf
   *            The byte buffer containing the message. Must not include the
   *            length header.
   * @param msgheaderlength
   *            The expected length of the 8583 header, after which the
   *            message type id and the rest of the message must come.
   */
  public cnMessage parseMessage(byte[] buf, int msgheaderlength)
      throws ParseException {
    cnMessage m = new cnMessage(new String(buf, msgheaderlength, 4),
        msgheaderlength);
    log.debug("parsed message typeid: [" + m.getMsgTypeID() + "]");
    // TODO it only parses ASCII messages for now

    // �õ�����ͷ
    byte[] msgheaderdata = new byte[msgheaderlength];
    System.arraycopy(buf, 0, msgheaderdata, 0, msgheaderlength);
    m.setMessageHeaderData(0, msgheaderdata);
    log.debug("parsed message hearder: [" + new String(m.getmsgHeader()) + "]");
    // Parse the bitmap (primary first)
    BitSet bs = new BitSet(64);
    int pos = 0;
    for (int i = msgheaderlength + 4; i < msgheaderlength + 12; i++) {
      int bit = 128;
      for (int b = 0; b < 8; b++) {
        bs.set(pos++, (buf[i] & bit) != 0);
        bit >>= 1;
      }
    }

    // Check for secondary bitmap and parse if necessary
    if (bs.get(0)) {
      for (int i = msgheaderlength + 12; i < msgheaderlength + 20; i++) {
        int bit = 128;
        for (int b = 0; b < 8; b++) {
          bs.set(pos++, (buf[i] & bit) != 0);
          bit >>= 1;
        }
      }
      pos = 20 + msgheaderlength;
    } else {
      pos = 12 + msgheaderlength;
    }
   
    // ��BitSet�е�λͼ��ʽ���ɶ�������ʽ����ID������ʽ
    int bit_str_length = bs.length() <= 64 ? 78 : 158;
    StringBuffer bitmap_binstr = new StringBuffer(bit_str_length); // ÿ8��������λ��֮����2���ո����
    StringBuffer bitmap_str = new StringBuffer("{");
    for(int i = 0; i < bit_str_length; i++) {
      if(bs.get(i))  {
        bitmap_binstr.append("1")
        bitmap_str.append(i+1).append(", ");
      }
      else
        bitmap_binstr.append("0");
      if( i %10 == 7 && i < bit_str_length - 1 ) {
        bitmap_binstr.append("  ");
        i += 2;
      }
    }
    if(bitmap_str.substring(bitmap_str.length() - 2).equals(", "))
      bitmap_str.delete(bitmap_str.length() - 2, bitmap_str.length());
    bitmap_str.append("}");
    log.debug("bitmap data(binary format): [" + bitmap_binstr + "]");
  //  log.debug("parsed bitmap(0-127): [" + bs.toString() + "]");
    log.debug("parsed bitmap(1-128): [" + bitmap_str + "]");
   
    //Parse each field
    Map<Integer, cnFieldParseInfo> parseGuide = parseMap.get(m.getMsgTypeID());
    List<Integer> index = parseOrder.get(m.getMsgTypeID())// �����ͱ���Ӧ�ô��ڵ���ID����
    if(index == null) {
      RuntimeException e = new RuntimeException("��XML�ļ���δ���屨������[" + m.getMsgTypeID() + "]�Ľ�������, �޷����������͵ı���!! ����������!");
      log.error(e);
      throw e;
    }
   
    // ���2��128������յ��ı���λͼ��ָʾ�д��򣬶�XML�����ļ���ȷδָ�����򱨾��棡
    for(int fieldnum = 2; fieldnum <= 128; fieldnum++){
      if(bs.get(fieldnum -1)) { 
        if(! index.contains(Integer.valueOf(fieldnum))) { // ������ʱ
          log.warn("�յ�����Ϊ["  + m.getMsgTypeID()
              + "]�ı����е�λͼָʾ���е�[" + fieldnum
              + "]��,��XML�����ļ���δ���ø���. ����ܻᵼ�½�������,�����������XML�����ļ���");
        }
      }
    }
    for (Integer i : index) {
      cnFieldParseInfo fpi = parseGuide.get(i);
      if (bs.get(i - 1)) {
        cnValue val = useBinary ? fpi.parseBinary(buf, pos) : fpi.parse(buf, pos);
        m.setField(i, val);
        log.debug("parsed: fieldid=[" + i + "] <" + m.getField(i).getType() + "> \t[" 
            + m.getField(i).toString() + "] ---> [" + m.getObjectValue(i) + "]");
        if (useBinary && !(val.getType() == cnType.ALPHA || val.getType() == cnType.LLVAR
            || val.getType() == cnType.LLLVAR)) {
          pos += (val.getLength() / 2) + (val.getLength() % 2);
        } else {
          pos += val.getLength();
        }
        if (val.getType() == cnType.LLVAR) {
          pos += useBinary ? 1 : 2;
        } else if (val.getType() == cnType.LLLVAR) {
          pos += useBinary ? 2 : 3;
        }
      }
    }
    return m;
  }

  /** Sets whether the factory should set the current date on newly created messages,
   * in field 7. Default is false. */
  public void setUseCurrentDate(boolean flag) {
    usecurrentdata = flag;
  }
  /** Returns true if the factory is assigning the current date to newly created messages
   * (field 7). Default is false. */
  public boolean getUseCurrentDate() {
    return usecurrentdata;
  }

  /** Sets the generator that this factory will get new trace numbers from. There is no
   * default generator. */
  public void setSystemTraceNumberGenerator(cnSystemTraceNumGenerator value) {
    SystraceNumGen = value;
  }
  /** Returns the generator used to assign trace numbers to new messages. */
  public cnSystemTraceNumGenerator getSystemTraceNumberGenerator() {
    return SystraceNumGen;
  }

  /** Sets the 8583 header to be used in each message type.
   * @param value A map where the keys are the message type id and the values are the message headers length.
   */
  public void setHeaders(Map<String, Integer> value) {
    msgheadersattr.clear();
    msgheadersattr.putAll(value);
  }

  /** Sets the 8583 header attr for a specific message type.
   * @param msgtypeid The message type( 4 bytes)
   * @param headerlen The message header length */
  public void setHeaderLengthAttr(String msgtypeid, Integer headerlen) {
      msgheadersattr.put(msgtypeid, headerlen);
  }

  /** Returns the 8583 header length for the specified type. */
  public Integer getHeaderLengthAttr(String msgtypeid) {
    return msgheadersattr.get(msgtypeid);
  }

  /** Adds a message template to the factory. If there was a template for the same
   * message type id as the new one, it is overwritten. */
  public void addMessageTemplate(cnMessage templ) {
    if (templ != null) {
      typeTemplates.put(templ.getMsgTypeID(), templ);
    }
  }

  /** Removes the message template for the specified message type id. */
  public void removeMessageTemplate(String msgtypeid) {
    typeTemplates.remove(msgtypeid);
  }

  /** Sets a message template for a specified message type. When new messages of that type
   * are created, they will have the same values as the template.
   * @param msgtypeid The message type id.
   * @param templ The message from which fields should be copied, or NULL to remove the
   * template for this message type.
   * @deprecated Use addMessageTemplate(cnMessage) and removeMessageTemplate(String) instead of this. */
  public void setMessageTemplate(String msgtypeid, cnMessage templ) {
    if (templ == null) {
      typeTemplates.remove(msgtypeid);
    } else {
      typeTemplates.put(msgtypeid, templ);
    }
  }

  /** Sets a map with the fields that are to be expected when parsing a certain type of
   * message.
   * @param msgtypeid The message type id.
   * @param map A map of FieldParseInfo instances, each of which define what type and length
   * of field to expect. The keys will be the field numbers. */
  public void setParseMap(String msgtypeid, Map<Integer, cnFieldParseInfo> map) {
    parseMap.put(msgtypeid, map);
    ArrayList<Integer> index = new ArrayList<Integer>();
    index.addAll(map.keySet());
    Collections.sort(index);
    log.trace("Adding parse map for type: [" + msgtypeid + "] with fields " + index);
    parseOrder.put(msgtypeid, index);
  }

}
TOP

Related Classes of org.zyp.cn8583.cnMessageFactory

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.