|
@@ -0,0 +1,643 @@
|
|
|
+package cn.iocoder.yudao.framework.common.exception.util;
|
|
|
+
|
|
|
+import java.math.BigDecimal;
|
|
|
+import java.text.SimpleDateFormat;
|
|
|
+import java.util.*;
|
|
|
+import java.util.regex.Pattern;
|
|
|
+
|
|
|
+/**
|
|
|
+ * author: zrd
|
|
|
+ *
|
|
|
+ * @author zrd
|
|
|
+ * @date 2024/09/19
|
|
|
+ */
|
|
|
+public class FrameworkStringUtils {
|
|
|
+ /**
|
|
|
+ * 前缀
|
|
|
+ */
|
|
|
+ private static final String PREFIX = "\\u";
|
|
|
+ /**
|
|
|
+ * int 模式
|
|
|
+ */
|
|
|
+ private static final Pattern INT_PATTERN = Pattern.compile("^\\d+$");
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 框架字符串 utils
|
|
|
+ */
|
|
|
+ private FrameworkStringUtils() {
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 为空
|
|
|
+ *
|
|
|
+ * @param str str
|
|
|
+ * @return boolean
|
|
|
+ */
|
|
|
+ public static boolean isEmpty(Object str) {
|
|
|
+ return str == null || "".equals(str);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 具有长度
|
|
|
+ *
|
|
|
+ * @param str str
|
|
|
+ * @return boolean
|
|
|
+ */
|
|
|
+ public static boolean hasLength(CharSequence str) {
|
|
|
+ return str != null && str.length() > 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 具有长度
|
|
|
+ *
|
|
|
+ * @param str str
|
|
|
+ * @return boolean
|
|
|
+ */
|
|
|
+ public static boolean hasLength(String str) {
|
|
|
+ return hasLength((CharSequence) str);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 有文字
|
|
|
+ *
|
|
|
+ * @param str str
|
|
|
+ * @return boolean
|
|
|
+ */
|
|
|
+ public static boolean hasText(CharSequence str) {
|
|
|
+ if (!hasLength(str)) {
|
|
|
+ return false;
|
|
|
+ } else {
|
|
|
+ int strLen = str.length();
|
|
|
+
|
|
|
+ for (int i = 0; i < strLen; ++i) {
|
|
|
+ if (!Character.isWhitespace(str.charAt(i))) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 有文字
|
|
|
+ *
|
|
|
+ * @param str str
|
|
|
+ * @return boolean
|
|
|
+ */
|
|
|
+ public static boolean hasText(String str) {
|
|
|
+ return hasText((CharSequence) str);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 是整数
|
|
|
+ *
|
|
|
+ * @param str str
|
|
|
+ * @return boolean
|
|
|
+ */
|
|
|
+ public static boolean isInteger(String str) {
|
|
|
+ return str != null && str.length() != 0 && INT_PATTERN.matcher(str).matches();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * convert bool (转换布尔值)
|
|
|
+ *
|
|
|
+ * @param value 价值
|
|
|
+ * @return {@link String }
|
|
|
+ */
|
|
|
+ public static String convertBool(String value) {
|
|
|
+ if ("true".equalsIgnoreCase(value)) {
|
|
|
+ return "1";
|
|
|
+ } else {
|
|
|
+ return "false".equalsIgnoreCase(value) ? "0" : value;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取布尔
|
|
|
+ *
|
|
|
+ * @param value 价值
|
|
|
+ * @return boolean
|
|
|
+ */
|
|
|
+ public static boolean getBool(String value) {
|
|
|
+ return value != null && !"false".equalsIgnoreCase(value) && !"0".equals(value) && !value.isEmpty();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 全部替换
|
|
|
+ *
|
|
|
+ * @param string 字符串
|
|
|
+ * @param oldString 旧字符串
|
|
|
+ * @param newString 新建字符串
|
|
|
+ * @return {@link String }
|
|
|
+ */
|
|
|
+ public static String replaceAll(String string, String oldString, String newString) {
|
|
|
+ return innerReplace(string, oldString, newString, true);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 先替换
|
|
|
+ *
|
|
|
+ * @param string 字符串
|
|
|
+ * @param oldString 旧字符串
|
|
|
+ * @param newString 新建字符串
|
|
|
+ * @return {@link String }
|
|
|
+ */
|
|
|
+ public static String replaceFirst(String string, String oldString, String newString) {
|
|
|
+ return innerReplace(string, oldString, newString, false);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 内部替换
|
|
|
+ *
|
|
|
+ * @param string 字符串
|
|
|
+ * @param oldString 旧字符串
|
|
|
+ * @param newString 新建字符串
|
|
|
+ * @param isAll 就是全部
|
|
|
+ * @return {@link String }
|
|
|
+ */
|
|
|
+ private static String innerReplace(String string, String oldString, String newString, boolean isAll) {
|
|
|
+ int index = string.indexOf(oldString);
|
|
|
+ if (index == -1) {
|
|
|
+ return string;
|
|
|
+ } else {
|
|
|
+ int start = 0;
|
|
|
+ int len = oldString.length();
|
|
|
+ if (len == 0) {
|
|
|
+ return string;
|
|
|
+ } else {
|
|
|
+ StringBuilder buffer = new StringBuilder(string.length());
|
|
|
+
|
|
|
+ do {
|
|
|
+ buffer.append(string, start, index);
|
|
|
+ buffer.append(newString);
|
|
|
+ start = index + len;
|
|
|
+ if (!isAll) {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ index = string.indexOf(oldString, start);
|
|
|
+ } while (index != -1);
|
|
|
+
|
|
|
+ buffer.append(string.substring(start));
|
|
|
+ return buffer.toString();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 连接
|
|
|
+ *
|
|
|
+ * @param string 字符串
|
|
|
+ * @return {@link String }
|
|
|
+ */
|
|
|
+ public static String concat(String... string) {
|
|
|
+ int length = 0;
|
|
|
+ String[] var2 = string;
|
|
|
+ int var3 = string.length;
|
|
|
+
|
|
|
+ int var4;
|
|
|
+ for (var4 = 0; var4 < var3; ++var4) {
|
|
|
+ String str = var2[var4];
|
|
|
+ length += str.length();
|
|
|
+ }
|
|
|
+
|
|
|
+ StringBuilder buf = new StringBuilder(length);
|
|
|
+ String[] var8 = string;
|
|
|
+ var4 = string.length;
|
|
|
+
|
|
|
+ for (int var9 = 0; var9 < var4; ++var9) {
|
|
|
+ String str = var8[var9];
|
|
|
+ buf.append(str);
|
|
|
+ }
|
|
|
+
|
|
|
+ return buf.toString();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 加入地图
|
|
|
+ *
|
|
|
+ * @param maps 地图
|
|
|
+ * @return {@link String }
|
|
|
+ */
|
|
|
+ public static String joinMap(Map<String, String> maps) {
|
|
|
+ if (maps != null && !maps.isEmpty()) {
|
|
|
+ StringBuilder builder = new StringBuilder();
|
|
|
+ Iterator var2 = maps.entrySet().iterator();
|
|
|
+
|
|
|
+ while (var2.hasNext()) {
|
|
|
+ Map.Entry<String, String> entry = (Map.Entry) var2.next();
|
|
|
+ builder.append(entry.getKey()).append("=").append(entry.getValue()).append(",");
|
|
|
+ }
|
|
|
+
|
|
|
+ if (builder.length() > 0) {
|
|
|
+ builder.setLength(builder.length() - 1);
|
|
|
+ }
|
|
|
+
|
|
|
+ return builder.toString();
|
|
|
+ } else {
|
|
|
+ return "";
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 分割到地图
|
|
|
+ *
|
|
|
+ * @param value 价值
|
|
|
+ * @return {@link Map }<{@link String }, {@link String }>
|
|
|
+ */
|
|
|
+ public static Map<String, String> splitToMap(String value) {
|
|
|
+ if (!hasLength(value)) {
|
|
|
+ return Collections.emptyMap();
|
|
|
+ } else {
|
|
|
+ Map<String, String> maps = new HashMap(16);
|
|
|
+ String[] var2 = value.split(",");
|
|
|
+ int var3 = var2.length;
|
|
|
+
|
|
|
+ for (int var4 = 0; var4 < var3; ++var4) {
|
|
|
+ String kav = var2[var4];
|
|
|
+ String[] kavv = kav.split("=");
|
|
|
+ switch (kavv.length) {
|
|
|
+ case 0:
|
|
|
+ break;
|
|
|
+ case 1:
|
|
|
+ maps.put(kavv[0], "");
|
|
|
+ break;
|
|
|
+ case 2:
|
|
|
+ maps.put(kavv[0], kavv[1]);
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ throw new IllegalArgumentException("Value (" + value + ") is Invalid.");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return maps;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 是布尔值
|
|
|
+ *
|
|
|
+ * @param str str
|
|
|
+ * @return boolean
|
|
|
+ */
|
|
|
+ public static boolean isBooleanValue(String str) {
|
|
|
+ return Boolean.TRUE.toString().equalsIgnoreCase(str) || Boolean.FALSE.toString().equalsIgnoreCase(str);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 是 int 值
|
|
|
+ *
|
|
|
+ * @param str str
|
|
|
+ * @return boolean
|
|
|
+ */
|
|
|
+ public static boolean isIntValue(String str) {
|
|
|
+ try {
|
|
|
+ Integer.parseInt(str);
|
|
|
+ return true;
|
|
|
+ } catch (NumberFormatException var2) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 是 long value
|
|
|
+ *
|
|
|
+ * @param str str
|
|
|
+ * @return boolean
|
|
|
+ */
|
|
|
+ public static boolean isLongValue(String str) {
|
|
|
+ try {
|
|
|
+ Long.parseLong(str);
|
|
|
+ return true;
|
|
|
+ } catch (NumberFormatException var2) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 是十进制数字
|
|
|
+ *
|
|
|
+ * @param decimal 十进制
|
|
|
+ * @return boolean
|
|
|
+ */
|
|
|
+ public static boolean isDecimalDigits(String decimal) {
|
|
|
+ return isDecimalDigits(decimal, 2);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 是十进制数字
|
|
|
+ *
|
|
|
+ * @param decimal 十进制
|
|
|
+ * @param scale 规模
|
|
|
+ * @return boolean
|
|
|
+ */
|
|
|
+ public static boolean isDecimalDigits(String decimal, int scale) {
|
|
|
+ BigDecimal decimalValue;
|
|
|
+ try {
|
|
|
+ decimalValue = new BigDecimal(decimal);
|
|
|
+ } catch (NumberFormatException var4) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ return decimalValue.scale() <= scale;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 加入
|
|
|
+ *
|
|
|
+ * @param arrays 阵 列
|
|
|
+ * @param delimiters 分隔符
|
|
|
+ * @return {@link String }
|
|
|
+ */
|
|
|
+ public static String join(String[] arrays, String delimiters) {
|
|
|
+ if (arrays == null) {
|
|
|
+ return "";
|
|
|
+ } else if (arrays.length == 1) {
|
|
|
+ return arrays[0];
|
|
|
+ } else {
|
|
|
+ int length = arrays.length;
|
|
|
+ StringBuilder buffer = new StringBuilder(arrays[0]);
|
|
|
+
|
|
|
+ for (int i = 1; i < length; ++i) {
|
|
|
+ buffer.append(delimiters).append(arrays[i]);
|
|
|
+ }
|
|
|
+
|
|
|
+ return buffer.toString();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 加入
|
|
|
+ *
|
|
|
+ * @param arrays 阵 列
|
|
|
+ * @param delimiters 分隔符
|
|
|
+ * @return {@link String }
|
|
|
+ */
|
|
|
+ public static String join(Collection<String> arrays, String delimiters) {
|
|
|
+ if (arrays == null) {
|
|
|
+ return "";
|
|
|
+ } else if (arrays.size() == 1) {
|
|
|
+ return arrays.iterator().next();
|
|
|
+ } else {
|
|
|
+ Iterator<String> iterator = arrays.iterator();
|
|
|
+ StringBuilder buffer = new StringBuilder(iterator.next());
|
|
|
+
|
|
|
+ while (iterator.hasNext()) {
|
|
|
+ buffer.append(delimiters).append(iterator.next());
|
|
|
+ }
|
|
|
+
|
|
|
+ return buffer.toString();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 设置正常日期格式
|
|
|
+ *
|
|
|
+ * @param date 日期
|
|
|
+ * @return {@link String }
|
|
|
+ */
|
|
|
+ public static String formatNormalDate(Date date) {
|
|
|
+ return isEmpty(date) ? "null" : (new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS")).format(date);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 等于
|
|
|
+ *
|
|
|
+ * @param input1 输入1
|
|
|
+ * @param input2 输入2
|
|
|
+ * @return boolean
|
|
|
+ */
|
|
|
+ public static boolean equals(String input1, String input2) {
|
|
|
+ if (input1 == input2) {
|
|
|
+ return true;
|
|
|
+ } else {
|
|
|
+ boolean hasLength1 = hasLength(input1);
|
|
|
+ boolean hasLength2 = hasLength(input2);
|
|
|
+ if (!hasLength1 && !hasLength2) {
|
|
|
+ return true;
|
|
|
+ } else {
|
|
|
+ return (hasLength1 && hasLength2) && input1.equals(input2);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 等于忽略大小写
|
|
|
+ *
|
|
|
+ * @param input1 输入1
|
|
|
+ * @param input2 输入2
|
|
|
+ * @return boolean
|
|
|
+ */
|
|
|
+ public static boolean equalsIgnoreCase(String input1, String input2) {
|
|
|
+ if (input1 == input2) {
|
|
|
+ return true;
|
|
|
+ } else {
|
|
|
+ boolean hasLength1 = hasLength(input1);
|
|
|
+ boolean hasLength2 = hasLength(input2);
|
|
|
+ if (!hasLength1 && !hasLength2) {
|
|
|
+ return true;
|
|
|
+ } else {
|
|
|
+ return (hasLength1 && hasLength2) && input1.equalsIgnoreCase(input2);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 为空
|
|
|
+ *
|
|
|
+ * @param input 输入
|
|
|
+ * @return boolean
|
|
|
+ */
|
|
|
+ public static boolean isEmpty(String input) {
|
|
|
+ return input == null || input.trim().length() == 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 不为空
|
|
|
+ *
|
|
|
+ * @param input 输入
|
|
|
+ * @return boolean
|
|
|
+ */
|
|
|
+ public static boolean isNotEmpty(String input) {
|
|
|
+ return !isEmpty(input);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 安全配平
|
|
|
+ *
|
|
|
+ * @param text 发短信
|
|
|
+ * @return {@link String }
|
|
|
+ */
|
|
|
+ public static String safeTrim(String text) {
|
|
|
+ return text == null ? null : text.trim();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * safe to string
|
|
|
+ *
|
|
|
+ * @param input 输入
|
|
|
+ * @return {@link String }
|
|
|
+ */
|
|
|
+ public static String safeToString(Object input) {
|
|
|
+ return input == null ? null : input.toString();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 默认值
|
|
|
+ *
|
|
|
+ * @param text 发短信
|
|
|
+ * @param defaultValue 默认值
|
|
|
+ * @return {@link String }
|
|
|
+ */
|
|
|
+ public static String defaultValue(String text, String defaultValue) {
|
|
|
+ text = safeTrim(text);
|
|
|
+ return isNotEmpty(text) ? text : defaultValue;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 默认值
|
|
|
+ *
|
|
|
+ * @param text 发短信
|
|
|
+ * @param defaultValue 默认值
|
|
|
+ * @return int
|
|
|
+ */
|
|
|
+ public static int defaultValue(String text, int defaultValue) {
|
|
|
+ text = safeTrim(text);
|
|
|
+ if (isNotEmpty(text)) {
|
|
|
+ try {
|
|
|
+ return Integer.parseInt(text);
|
|
|
+ } catch (NumberFormatException var3) {
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return defaultValue;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 默认值
|
|
|
+ *
|
|
|
+ * @param text 发短信
|
|
|
+ * @param defaultValue 默认值
|
|
|
+ * @return boolean
|
|
|
+ */
|
|
|
+ public static boolean defaultValue(String text, boolean defaultValue) {
|
|
|
+ text = safeTrim(text);
|
|
|
+ return !isNotEmpty(text) || !Boolean.TRUE.toString().equalsIgnoreCase(text) && !Boolean.FALSE.toString().equalsIgnoreCase(text) ? defaultValue : Boolean.parseBoolean(text);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 包含
|
|
|
+ *
|
|
|
+ * @param values 值
|
|
|
+ * @param value 价值
|
|
|
+ * @return boolean
|
|
|
+ */
|
|
|
+ public static boolean isContains(String[] values, String value) {
|
|
|
+ if (value != null && value.length() > 0 && values != null && values.length > 0) {
|
|
|
+ String[] var2 = values;
|
|
|
+ int var3 = values.length;
|
|
|
+
|
|
|
+ for (int var4 = 0; var4 < var3; ++var4) {
|
|
|
+ String v = var2[var4];
|
|
|
+ if (value.equals(v)) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * native2 ASCII
|
|
|
+ *
|
|
|
+ * @param str str
|
|
|
+ * @return {@link String }
|
|
|
+ */
|
|
|
+ public static String native2Ascii(String str) {
|
|
|
+ char[] charArray = str.toCharArray();
|
|
|
+ StringBuffer sb = new StringBuffer();
|
|
|
+ char[] var3 = charArray;
|
|
|
+ int var4 = charArray.length;
|
|
|
+
|
|
|
+ for (int var5 = 0; var5 < var4; ++var5) {
|
|
|
+ char c = var3[var5];
|
|
|
+ sb.append(char2Ascii(c));
|
|
|
+ }
|
|
|
+
|
|
|
+ return sb.toString();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * char2 ascii
|
|
|
+ *
|
|
|
+ * @param c c
|
|
|
+ * @return {@link String }
|
|
|
+ */
|
|
|
+ public static String char2Ascii(char c) {
|
|
|
+ if (c > 255) {
|
|
|
+ StringBuffer sb = new StringBuffer();
|
|
|
+ sb.append("\\u");
|
|
|
+ int code = c >> 8;
|
|
|
+ String tmp = Integer.toHexString(code);
|
|
|
+ if (tmp.length() == 1) {
|
|
|
+ sb.append("0");
|
|
|
+ }
|
|
|
+
|
|
|
+ sb.append(tmp);
|
|
|
+ code = c & 255;
|
|
|
+ tmp = Integer.toHexString(code);
|
|
|
+ if (tmp.length() == 1) {
|
|
|
+ sb.append("0");
|
|
|
+ }
|
|
|
+
|
|
|
+ sb.append(tmp);
|
|
|
+ return sb.toString();
|
|
|
+ } else {
|
|
|
+ return Character.toString(c);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * ACSII2 天然
|
|
|
+ *
|
|
|
+ * @param str str
|
|
|
+ * @return {@link String }
|
|
|
+ */
|
|
|
+ public static String acsii2Native(String str) {
|
|
|
+ StringBuffer sb = new StringBuffer();
|
|
|
+ int begin = 0;
|
|
|
+
|
|
|
+ for (int index = str.indexOf("\\u"); index != -1; index = str.indexOf("\\u", begin)) {
|
|
|
+ sb.append(str, begin, index);
|
|
|
+ sb.append(ascii2Char(str.substring(index, index + 6)));
|
|
|
+ begin = index + 6;
|
|
|
+ }
|
|
|
+
|
|
|
+ sb.append(str.substring(begin));
|
|
|
+ return sb.toString();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * ASCII 2 字符
|
|
|
+ *
|
|
|
+ * @param str str
|
|
|
+ * @return char
|
|
|
+ */
|
|
|
+ private static char ascii2Char(String str) {
|
|
|
+ if (str.length() != 6) {
|
|
|
+ throw new IllegalArgumentException("Ascii string of a native character must be 6 character.");
|
|
|
+ } else if (!"\\u".equals(str.substring(0, 2))) {
|
|
|
+ throw new IllegalArgumentException("Ascii string of a native character must start with \"\\u\".");
|
|
|
+ } else {
|
|
|
+ String tmp = str.substring(2, 4);
|
|
|
+ int code = Integer.parseInt(tmp, 16) << 8;
|
|
|
+ tmp = str.substring(4, 6);
|
|
|
+ code += Integer.parseInt(tmp, 16);
|
|
|
+ return (char) code;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+}
|