package com.github.ompc.greys.core.util; import java.util.ArrayList; import java.util.HashMap; import java.util.Map; import java.util.Stack; import static com.github.ompc.greys.core.util.GaCheckUtils.isEquals; import static com.github.ompc.greys.core.util.GaCheckUtils.isIn; import static org.apache.commons.lang3.StringUtils.isBlank; /** * Feature编解器(线程安全)<br/> * <p/> * 用于封装系统内部features/attribute等扩展字段的管理 * Created by dukun@taobao.com on 15/3/31. */ public class FeatureCodec { /** * KV片段分割符<br/> * KV片段定义为一个完整的KV对,例如字符串<span>;k1=v1;k2=v2;</span> * 其中<b>;</b>即为KV片段分隔符 */ private final char kvSegmentSeparator; /** * KV分割符<br/> * KV定义为一个KV对区分K和V的分割符号,例如字符串<span>k1=v1</span> * 其中<b>=</b>即为KV分隔符 */ private final char kvSeparator; /** * 转义前缀符 */ private static final char ESCAPE_PREFIX_CHAR = '\\'; /** * 使用指定的KV分割符构造FeatureParser<br/> * * @param kvSegmentSeparator KV对之间的分隔符 * @param kvSeparator K与V之间的分隔符 */ public FeatureCodec(final char kvSegmentSeparator, final char kvSeparator) { // 分隔符禁止与转义前缀符相等 if (isIn(ESCAPE_PREFIX_CHAR, kvSegmentSeparator, kvSeparator)) { throw new IllegalArgumentException("separator can not init to '" + ESCAPE_PREFIX_CHAR + "'."); } this.kvSegmentSeparator = kvSegmentSeparator; this.kvSeparator = kvSeparator; } /** * map集合转换到feature字符串 * * @param map map集合 * @return feature字符串 */ public String toString(final Map<String, String> map) { final StringBuilder featureSB = new StringBuilder().append(kvSegmentSeparator); if (null == map || map.isEmpty()) { return featureSB.toString(); } for (Map.Entry<String, String> entry : map.entrySet()) { featureSB .append(escapeEncode(entry.getKey())) .append(kvSeparator) .append(escapeEncode(entry.getValue())) .append(kvSegmentSeparator) ; } return featureSB.toString(); } /** * feature字符串转换到map集合 * * @param featureString the feature string * @return the map */ public Map<String, String> toMap(final String featureString) { final Map<String, String> map = new HashMap<String, String>(); if (isBlank(featureString)) { return map; } for (String kv : escapeSplit(featureString, kvSegmentSeparator)) { if (isBlank(kv)) { // 过滤掉为空的字符串片段 continue; } final String[] ar = escapeSplit(kv, kvSeparator); if (ar.length != 2) { // 过滤掉不符合K:V单目的情况 continue; } final String k = ar[0]; final String v = ar[1]; if (!isBlank(k) && !isBlank(v)) { map.put(escapeDecode(k), escapeDecode(v)); } } return map; } /** * 转义编码 * * @param string 原始字符串 * @return 转义编码后的字符串 */ private String escapeEncode(final String string) { final StringBuilder returnSB = new StringBuilder(); for (final char c : string.toCharArray()) { if (isIn(c, kvSegmentSeparator, kvSeparator, ESCAPE_PREFIX_CHAR)) { returnSB.append(ESCAPE_PREFIX_CHAR); } returnSB.append(c); } return returnSB.toString(); } /** * 转义解码 * * @param string 编码字符串 * @return 转义解码后的字符串 */ private String escapeDecode(String string) { final StringBuilder segmentSB = new StringBuilder(); final int stringLength = string.length(); for (int index = 0; index < stringLength; index++) { final char c = string.charAt(index); if (isEquals(c, ESCAPE_PREFIX_CHAR) && index < stringLength - 1) { final char nextChar = string.charAt(++index); // 下一个字符是转义符 if (isIn(nextChar, kvSegmentSeparator, kvSeparator, ESCAPE_PREFIX_CHAR)) { segmentSB.append(nextChar); } // 如果不是转义字符,则需要两个都放入 else { segmentSB.append(c); segmentSB.append(nextChar); } } else { segmentSB.append(c); } } return segmentSB.toString(); } /** * 编码字符串拆分 * * @param string 编码字符串 * @param splitEscapeChar 分割符 * @return 拆分后的字符串数组 */ private String[] escapeSplit(String string, char splitEscapeChar) { final ArrayList<String> segmentArrayList = new ArrayList<String>(); final Stack<Character> decodeStack = new Stack<Character>(); final int stringLength = string.length(); for (int index = 0; index < stringLength; index++) { boolean isArchive = false; final char c = string.charAt(index); // 匹配到转义前缀符 if (isEquals(c, ESCAPE_PREFIX_CHAR)) { decodeStack.push(c); if (index < stringLength - 1) { final char nextChar = string.charAt(++index); decodeStack.push(nextChar); } } // 匹配到分割符 else if (isEquals(c, splitEscapeChar)) { isArchive = true; } // 匹配到其他字符 else { decodeStack.push(c); } if (isArchive || index == stringLength - 1) { final StringBuilder segmentSB = new StringBuilder(decodeStack.size()); while (!decodeStack.isEmpty()) { segmentSB.append(decodeStack.pop()); } segmentArrayList.add( segmentSB .reverse() // 因为堆栈中是逆序的,所以需要对逆序的字符串再次逆序 .toString() // toString .trim() // 考虑到字符串片段可能会出现首尾空格的场景,这里做一个过滤 ); } } return segmentArrayList.toArray(new String[segmentArrayList.size()]); } }