New file |
| | |
| | | package com.zy.common.utils; |
| | | |
| | | import com.zy.common.model.annotations.*; |
| | | import org.springframework.expression.EvaluationContext; |
| | | import org.springframework.expression.Expression; |
| | | import org.springframework.expression.ExpressionParser; |
| | | import org.springframework.expression.spel.standard.SpelExpressionParser; |
| | | import org.springframework.expression.spel.support.StandardEvaluationContext; |
| | | |
| | | import java.io.ByteArrayInputStream; |
| | | import java.io.ByteArrayOutputStream; |
| | | import java.io.DataInputStream; |
| | | import java.io.DataOutputStream; |
| | | import java.lang.reflect.Array; |
| | | import java.lang.reflect.Field; |
| | | import java.util.*; |
| | | |
| | | public class Struct implements java.io.Serializable { |
| | | |
| | | private transient Map<String,byte[]> decodeByteMapping = new HashMap<>(); |
| | | private transient Map<String,byte[]> encodeByteMapping = new HashMap<>(); |
| | | private transient byte[] dataBytes = null; |
| | | private transient byte[] decodeBytes = null; |
| | | private transient byte[] encodeBytes = null; |
| | | private transient byte[] unusedBytes = null; |
| | | |
| | | public Class getClassType(String name){ |
| | | return null; |
| | | } |
| | | |
| | | public boolean isReverse() { |
| | | return this.getClass().getAnnotation(little.class)!=null; |
| | | } |
| | | |
| | | public byte[] toBytes() throws Exception { |
| | | return encode(this); |
| | | } |
| | | |
| | | public Map<String,String> toDecodeHexMapping() throws Exception{ |
| | | if(this.decodeByteMapping==null)this.decodeByteMapping=new HashMap(); |
| | | Map<String, String> mapping = getHex(this.decodeByteMapping); |
| | | if(this.decodeBytes!=null){ |
| | | mapping.put("byte[]", Struct.toHex(this.decodeBytes)); |
| | | } |
| | | return mapping; |
| | | } |
| | | public Map<String,String> toEncodeHexMapping() throws Exception{ |
| | | if(this.encodeBytes==null)this.encode(this); |
| | | if(this.encodeByteMapping==null)this.encodeByteMapping=new HashMap(); |
| | | Map<String, String> mapping = getHex(this.encodeByteMapping); |
| | | mapping.put("byte[]", Struct.toHex(this.encodeBytes)); |
| | | return mapping; |
| | | } |
| | | public Map<String,String> getHex(Map<String,byte[]> bytemapping) throws Exception { |
| | | Map<String,String> map = new LinkedHashMap(); |
| | | Field[] fields = this.getClass().getFields(); |
| | | for (Field field : fields) { |
| | | byte[] bytes = bytemapping.get(field.getName()); |
| | | if(bytes!=null){ |
| | | map.put(field.getName(), Struct.toHex(bytes)); |
| | | } |
| | | } |
| | | return map; |
| | | } |
| | | |
| | | // 转java对象到数组 |
| | | public byte[] encode(Struct entity) throws Exception { |
| | | ByteArrayOutputStream baOs = new ByteArrayOutputStream(); |
| | | write(new DataOutputStream(baOs), entity); |
| | | entity.encodeBytes = baOs.toByteArray(); |
| | | return entity.encodeBytes; |
| | | } |
| | | |
| | | /** |
| | | * 写数据到输出流 |
| | | * |
| | | * @param dos |
| | | * @param entity |
| | | * @throws Exception |
| | | */ |
| | | public void write(DataOutputStream dos, Struct entity) throws Exception { |
| | | if(entity==null)return; |
| | | if(entity.encodeByteMapping==null)entity.encodeByteMapping=new HashMap(); |
| | | Field[] fields = entity.getClass().getFields(); |
| | | EvaluationContext context = new StandardEvaluationContext(); |
| | | for (Field field : Objects.requireNonNull(ReflectUtils.removeStaticField(fields))) { |
| | | field.setAccessible(true); |
| | | |
| | | String name = field.getName(); |
| | | Class<?> type = field.getType(); |
| | | String typeName = type.getSimpleName(); |
| | | if(typeName.equals("Struct")){ |
| | | if(entity.getClassType(name)!=null){ |
| | | type = entity.getClassType(name); |
| | | typeName = type.getSimpleName(); |
| | | }else if(field.get(entity)!=null){ |
| | | type = field.get(entity).getClass(); |
| | | typeName = type.getSimpleName(); |
| | | } |
| | | } |
| | | |
| | | size size = field.getAnnotation(size.class); |
| | | bit bit = field.getAnnotation(bit.class); |
| | | expr expr = field.getAnnotation(expr.class); |
| | | flag flag = field.getAnnotation(flag.class); |
| | | if(size==null && bit==null && expr==null){ |
| | | continue; |
| | | } |
| | | |
| | | int bited = 0;//已写位数 |
| | | int length = 0;//定义的长度 |
| | | if(size!=null){ |
| | | length = size.value(); |
| | | bited+=size.value()*8; |
| | | }else if(bit!=null){ |
| | | //按位写方式未实现处理 |
| | | throw new RuntimeException("按位写方式未实现处理"); |
| | | }else if(expr!=null){ |
| | | ExpressionParser parser = new SpelExpressionParser(); |
| | | Expression exp = parser.parseExpression(expr.value()); |
| | | Integer len = null; |
| | | try{ |
| | | len = CommonUtils.parseInt(exp.getValue(context, entity)); |
| | | }catch (Exception e){ |
| | | len = 0; |
| | | } |
| | | if(len!=null){ |
| | | if(len==-1)continue; |
| | | length = len; |
| | | bited+=len*8; |
| | | } |
| | | } |
| | | context.setVariable(name, field.get(entity)); |
| | | |
| | | byte[] bytes = new byte[length]; |
| | | |
| | | if (typeName.equals("int") || typeName.equals("Integer")){ |
| | | dos.write(bytes=convert(length,field.getInt(entity))); |
| | | }else if (typeName.equals("long") || typeName.equals("Long")){ |
| | | dos.write(bytes=convert(length,field.getLong(entity))); |
| | | }else if (typeName.equals("short") || typeName.equals("Short")){ |
| | | dos.write(bytes=convert(length,field.getShort(entity))); |
| | | }else if (typeName.equals("byte") || typeName.equals("Byte")){ |
| | | dos.write(bytes=convert(length,field.getByte(entity))); |
| | | }else if (typeName.equals("String")) { |
| | | String str = (String) field.get(entity); |
| | | if (str == null) |
| | | str = ""; |
| | | str = str.replaceAll(" ", " "); |
| | | |
| | | byte[] bts = null; |
| | | if(flag!=null && flag.value().equals("ASCII")){ |
| | | bts = Struct.StrToAsc(str); |
| | | }else if(flag!=null && flag.value().equals("BCD")){ |
| | | bts = Struct.str2Bcd(str); |
| | | }else{ |
| | | bts = str.getBytes(); |
| | | } |
| | | |
| | | if(length==0){//如果长度未设置,以当前实际的字节为准 |
| | | bytes = new byte[bts.length]; |
| | | } |
| | | System.arraycopy(bts, 0, bytes, 0, bts.length); |
| | | dos.write(bytes); |
| | | } else if (typeName.equals("byte[]")) { |
| | | byte[] bts = (byte[]) field.get(entity); |
| | | if(bts!=null){ |
| | | if(length==0){//如果长度未设置,以当前实际的字节为准 |
| | | bytes = new byte[bts.length]; |
| | | } |
| | | System.arraycopy(bts, 0, bytes, 0, bts.length); |
| | | dos.write(bytes); |
| | | } |
| | | }else if (type.isArray()) { |
| | | ByteArrayOutputStream baOs1 = new ByteArrayOutputStream(); |
| | | DataOutputStream dos1 = new DataOutputStream(baOs1); |
| | | for (Object object : (Object[]) field.get(entity)){ |
| | | write(dos1, (Struct) object); |
| | | } |
| | | byte[] bts = baOs1.toByteArray(); |
| | | if(length==0){//如果长度未设置,以当前实际的字节为准 |
| | | bytes = new byte[bts.length]; |
| | | } |
| | | System.arraycopy(bts, 0, bytes, 0, bts.length); |
| | | dos.write(bytes); |
| | | } else { |
| | | byte[] bts = encode((Struct) field.get(entity)); |
| | | if(length==0){//如果长度未设置,以当前实际的字节为准 |
| | | bytes = new byte[bts.length]; |
| | | } |
| | | System.arraycopy(bts, 0, bytes, 0, bts.length); |
| | | dos.write(bytes); |
| | | } |
| | | entity.encodeByteMapping.put(name, bytes); |
| | | } |
| | | } |
| | | |
| | | |
| | | public byte[] convert(int size, long value) throws Exception{ |
| | | ByteArrayOutputStream baOs = new ByteArrayOutputStream(); |
| | | DataOutputStream dos = new DataOutputStream(baOs); |
| | | switch(size){ |
| | | case 1://byte |
| | | dos.write((byte) value); |
| | | break; |
| | | case 2://short |
| | | dos.writeShort(this.isReverse() ? Short.reverseBytes((short)value) : (short)value); |
| | | break; |
| | | case 4://int |
| | | dos.writeInt(this.isReverse() ? Integer.reverseBytes((int)value) : (int)value); |
| | | break; |
| | | case 8://long |
| | | dos.writeLong(this.isReverse() ? Long.reverseBytes((long)value) : (long)value); |
| | | break; |
| | | } |
| | | return baOs.toByteArray(); |
| | | } |
| | | |
| | | public long convertEx(long value,int size) throws Exception{ |
| | | switch(size){ |
| | | case 1://byte |
| | | return value; |
| | | case 2://short |
| | | return (this.isReverse() ? Short.reverseBytes((short)value) : (short)value); |
| | | case 4://int |
| | | return (this.isReverse() ? Integer.reverseBytes((int)value) : (int)value); |
| | | case 8://long |
| | | return (this.isReverse() ? Long.reverseBytes((long)value) : (long)value); |
| | | } |
| | | return 0; |
| | | } |
| | | |
| | | @SuppressWarnings({ "unchecked", "rawtypes" }) |
| | | public static <T extends Struct> T decode(Class<? extends Struct> cls,byte[] bytes) throws Exception{ |
| | | Struct instance = cls.newInstance(); |
| | | return instance.decode(bytes); |
| | | } |
| | | |
| | | @SuppressWarnings({ "unchecked", "rawtypes" }) |
| | | public <T extends Struct> T decode(byte[] bytes) throws Exception{ |
| | | this.dataBytes = bytes; |
| | | Struct instance = this; |
| | | Class<? extends Struct> cls = this.getClass(); |
| | | Field[] fields = cls.getFields(); |
| | | if(this.decodeByteMapping==null)instance.decodeByteMapping=new HashMap(); |
| | | EvaluationContext context = new StandardEvaluationContext(); |
| | | context.setVariable("_", bytes.length); |
| | | int bited = 0;//已读位数 |
| | | for(Field field: Objects.requireNonNull(ReflectUtils.removeStaticField(fields))){ |
| | | size size = field.getAnnotation(size.class); |
| | | bit bit = field.getAnnotation(bit.class); |
| | | expr expr = field.getAnnotation(expr.class); |
| | | flag flag = field.getAnnotation(flag.class); |
| | | if(size==null && bit==null && expr==null){ |
| | | continue; |
| | | //throw new RuntimeException(cls.getSimpleName()+"字段"+field.getName()+"未标记@size|@bit|@expr"); |
| | | } |
| | | String name = field.getName(); |
| | | Class<?> type = field.getType(); |
| | | String typeName = type.getSimpleName(); |
| | | if(typeName.equals("Struct")){ |
| | | if(instance.getClassType(name)!=null){ |
| | | type = instance.getClassType(name); |
| | | typeName = type.getSimpleName(); |
| | | } |
| | | } |
| | | context.setVariable("__", bytes.length-bited/8); |
| | | byte[] bts = null; |
| | | if(size!=null){ |
| | | bts = new byte[size.value()]; |
| | | System.arraycopy(bytes, bited/8, bts, 0, size.value()); |
| | | bited+=size.value()*8; |
| | | }else if(bit!=null){ |
| | | int val = 0; |
| | | String strBin = ""; |
| | | for(int i=bited;i<bited+bit.value();i++){//逐位扫描 |
| | | if((bytes[i/8] & (1 << (i%8))) != 0){//如果位为1 |
| | | strBin+="1"; |
| | | }else{ |
| | | strBin+="0"; |
| | | } |
| | | val = Integer.parseInt(strBin, 2); |
| | | } |
| | | byte[] result = new byte[4]; |
| | | result[3] = (byte)(val); |
| | | result[2] = (byte)(val >> 8); |
| | | result[1] = (byte)(val >> 16); |
| | | result[0] = (byte)(val >> 24); |
| | | bts = new byte[(int)Math.ceil((double)bit.value()/8.0)]; |
| | | System.arraycopy(result, result.length-bts.length, bts, 0, bts.length); |
| | | bited+=bit.value(); |
| | | }else if(expr!=null){ |
| | | ExpressionParser parser = new SpelExpressionParser(); |
| | | Expression exp = parser.parseExpression(expr.value()); |
| | | Integer len = CommonUtils.parseInt(exp.getValue(context,instance)); |
| | | if(len!=null){ |
| | | if(len==-1)continue; |
| | | bts = new byte[len]; |
| | | System.arraycopy(bytes, bited/8, bts, 0, len); |
| | | bited+=len*8; |
| | | } |
| | | } |
| | | if(bts.length==0)continue; |
| | | DataInputStream dis = new DataInputStream(new ByteArrayInputStream(bts)); |
| | | if (typeName.equals("int") || typeName.equals("Integer")){ |
| | | field.setInt(instance,(int) convertEx(dis.readInt(),4)); |
| | | }else if (typeName.equals("long") || typeName.equals("Long")){ |
| | | field.setLong(instance, convertEx(dis.readLong(),8)); |
| | | }else if (typeName.equals("short") || typeName.equals("Short")){ |
| | | field.setShort(instance, (short) convertEx(dis.readShort(),2)); |
| | | }else if (typeName.equals("byte") || typeName.equals("Byte")){ |
| | | field.setByte(instance, (byte) convertEx(dis.readByte(),1)); |
| | | }else if (typeName.equals("byte[]")){ |
| | | field.set(instance, bts); |
| | | }else if (typeName.equals("String")) { |
| | | String strStr = new String(bts,"UTF-8"); |
| | | if(flag!=null && flag.value().equals("BCD")){ |
| | | strStr = bcd2Str(bts); |
| | | }else if(flag!=null && flag.value().equals("ASCII")){ |
| | | strStr = Struct.AscToStr(bts); |
| | | } |
| | | strStr = strStr.replaceAll("[\u0000]", ""); |
| | | field.set(instance, strStr); |
| | | }else if (type.isArray()) { |
| | | byte[] arrayBytes = bts; |
| | | List list = new ArrayList(); |
| | | while(arrayBytes.length>0 && list.size()<256){ |
| | | Struct item = decode((Class<? extends Struct>) type.getComponentType(),arrayBytes); |
| | | list.add(item); |
| | | arrayBytes = item.unusedBytes; |
| | | } |
| | | Object array = Array.newInstance(type.getComponentType(), list.size()); |
| | | for(int i=0;i<list.size();i++){ |
| | | Array.set(array, i, list.get(i)); |
| | | } |
| | | field.set(instance, array); |
| | | }else if(type==Struct.class || Struct.class.isAssignableFrom(type)){ |
| | | byte[] arrayBytes = bts; |
| | | Struct item = decode((Class<? extends Struct>) type,arrayBytes); |
| | | field.set(instance, item); |
| | | }else{ |
| | | throw new RuntimeException(cls.getSimpleName()+"字段"+field.getName()+"类型未知"); |
| | | } |
| | | instance.decodeByteMapping.put(name, bts); |
| | | context.setVariable(name, field.get(instance)); |
| | | } |
| | | byte[] useBytes = new byte[bited/8]; |
| | | System.arraycopy(bytes, 0 , useBytes, 0, bited/8); |
| | | instance.decodeBytes = useBytes; |
| | | byte[] unusedBytes = new byte[bytes.length-(bited/8)]; |
| | | if(unusedBytes.length>0){ |
| | | System.arraycopy(bytes, useBytes.length , unusedBytes, 0, unusedBytes.length); |
| | | } |
| | | instance.unusedBytes = unusedBytes; |
| | | return (T) instance; |
| | | } |
| | | public static String toHex(byte[] bytes) { |
| | | StringBuilder buf = new StringBuilder(bytes.length * 2); |
| | | for(byte b : bytes) { // 使用String的format方法进行转换 |
| | | buf.append(String.format("%02x", new Integer(b & 0xff))); |
| | | } |
| | | return buf.toString().toUpperCase(); |
| | | } |
| | | public static byte[] toBytes(String str) { |
| | | if(str == null || str.trim().equals("")) return new byte[0]; |
| | | byte[] bytes = new byte[str.length() / 2]; |
| | | for(int i = 0; i < str.length() / 2; i++) { |
| | | String subStr = str.substring(i * 2, i * 2 + 2); |
| | | bytes[i] = (byte) Integer.parseInt(subStr, 16); |
| | | } |
| | | return bytes; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * @功能: BCD码转为10进制串(阿拉伯数据) |
| | | * @参数: BCD码 |
| | | * @结果: 10进制串 |
| | | */ |
| | | public static String bcd2Str(byte[] bytes) { |
| | | StringBuffer temp = new StringBuffer(bytes.length * 2); |
| | | for (int i = 0; i < bytes.length; i++) { |
| | | temp.append((byte) ((bytes[i] & 0xf0) >>> 4)); |
| | | temp.append((byte) (bytes[i] & 0x0f)); |
| | | } |
| | | return temp.toString().substring(0, 1).equalsIgnoreCase("0") ? temp |
| | | .toString().substring(1) : temp.toString(); |
| | | } |
| | | |
| | | /** |
| | | * @功能: 10进制串转为BCD码 |
| | | * @参数: 10进制串 |
| | | * @结果: BCD码 |
| | | */ |
| | | public static byte[] str2Bcd(String asc) { |
| | | int len = asc.length(); |
| | | int mod = len % 2; |
| | | if (mod != 0) { |
| | | asc = "0" + asc; |
| | | len = asc.length(); |
| | | } |
| | | byte abt[] = new byte[len]; |
| | | if (len >= 2) { |
| | | len = len / 2; |
| | | } |
| | | byte bbt[] = new byte[len]; |
| | | abt = asc.getBytes(); |
| | | int j, k; |
| | | for (int p = 0; p < asc.length() / 2; p++) { |
| | | if ((abt[2 * p] >= '0') && (abt[2 * p] <= '9')) { |
| | | j = abt[2 * p] - '0'; |
| | | } else if ((abt[2 * p] >= 'a') && (abt[2 * p] <= 'z')) { |
| | | j = abt[2 * p] - 'a' + 0x0a; |
| | | } else { |
| | | j = abt[2 * p] - 'A' + 0x0a; |
| | | } |
| | | if ((abt[2 * p + 1] >= '0') && (abt[2 * p + 1] <= '9')) { |
| | | k = abt[2 * p + 1] - '0'; |
| | | } else if ((abt[2 * p + 1] >= 'a') && (abt[2 * p + 1] <= 'z')) { |
| | | k = abt[2 * p + 1] - 'a' + 0x0a; |
| | | } else { |
| | | k = abt[2 * p + 1] - 'A' + 0x0a; |
| | | } |
| | | int a = (j << 4) + k; |
| | | byte b = (byte) a; |
| | | bbt[p] = b; |
| | | } |
| | | return bbt; |
| | | } |
| | | |
| | | |
| | | public static class TestMessage { |
| | | public @size(1) byte begin = 0x7e;//起始符 |
| | | public @size(2) short type = 0;//消息ID |
| | | public @bit(2) byte retain;//消息体属性>保留 |
| | | public @bit(1) byte subpkg;//消息体属性>是否分包,0表示不分包 |
| | | public @bit(3) byte encrypt;//消息体属性>加密方式,0表示不加密 |
| | | public @bit(10) short length;//消息体长度 |
| | | public @size(6) @flag("BCD") String number;//手机号码 |
| | | public @size(2) short serial;//消息流水号 |
| | | public @expr("#subpkg==1?2:0") short pkgs;//分包数 |
| | | public @expr("#subpkg==1?2:0") short index;//包序号 |
| | | public @expr("#length") byte[] data;//数据部分 |
| | | public @size(1) byte verify;//校验码 |
| | | public @size(1) byte end = 0x7e;//结束符 |
| | | } |
| | | |
| | | public static byte getXor(byte[] datas,int start, int end){ |
| | | byte temp=datas[start]; |
| | | for (int i = start+1; i <end; i++) { |
| | | temp ^=datas[i]; |
| | | } |
| | | return temp; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * 求校验和的算法 |
| | | * @param b 需要求校验和的字节数组 |
| | | * @return 校验和 |
| | | */ |
| | | public static byte sumCheck(byte[] b,int start, int end){ |
| | | int sum = 0; |
| | | for(int i = start; i < end; i++){ |
| | | sum = sum + b[i]; |
| | | } |
| | | return (byte) (sum & 0xff); |
| | | } |
| | | |
| | | //字符串转换为ascii |
| | | public static byte[] StrToAsc(String content){ |
| | | try { |
| | | return content.getBytes("US-ASCII"); |
| | | } catch (Exception e) { |
| | | e.printStackTrace(); |
| | | return null; |
| | | } |
| | | } |
| | | |
| | | //ascii转换为string |
| | | public static String AscToStr(byte[] bytes){ |
| | | StringBuffer sbu = new StringBuffer(); |
| | | for(int i=0;i<bytes.length;i++){ |
| | | sbu.append(Character.toString((char)bytes[i])); |
| | | } |
| | | return sbu.toString(); |
| | | } |
| | | |
| | | } |