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()]);
}
}