001/*
002 *    Copyright 2024-2025, Warm-Flow (290631660@qq.com).
003 *
004 *    Licensed under the Apache License, Version 2.0 (the "License");
005 *    you may not use this file except in compliance with the License.
006 *    You may obtain a copy of the License at
007 *
008 *       https://www.apache.org/licenses/LICENSE-2.0
009 *
010 *    Unless required by applicable law or agreed to in writing, software
011 *    distributed under the License is distributed on an "AS IS" BASIS,
012 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 *    See the License for the specific language governing permissions and
014 *    limitations under the License.
015 */
016package org.dromara.warm.flow.core.utils;
017
018import java.util.*;
019
020/**
021 * 字符串工具类
022 *
023 * @author warm
024 */
025public class StringUtils {
026    /**
027     * 空字符串
028     */
029    private static final String NULLSTR = "";
030
031    /**
032     * 下划线
033     */
034    private static final char SEPARATOR = '_';
035
036    /**
037     * 获取参数不为空值
038     *
039     * @param value defaultValue 要判断的value
040     * @return value 返回值
041     */
042    public static <T> T nvl(T value, T defaultValue) {
043        return value != null ? value : defaultValue;
044    }
045
046    /**
047     * * 判断一个字符串是否为空串
048     *
049     * @param str String
050     * @return true:为空 false:非空
051     */
052    public static boolean isEmpty(String str) {
053        return ObjectUtil.isNull(str) || NULLSTR.equals(str.trim());
054    }
055
056    /**
057     * * 判断一个字符串是否为非空串
058     *
059     * @param str String
060     * @return true:非空串 false:空串
061     */
062    public static boolean isNotEmpty(String str) {
063        return !isEmpty(str);
064    }
065
066    /**
067     * 如果字符串是空,则返回默认值
068     *
069     * @param str        字符串
070     * @param defaultStr 默认值
071     * @return 结果
072     */
073    public static String emptyDefault(String str, String defaultStr) {
074        return isEmpty(str) ? defaultStr : str;
075    }
076
077    /**
078     * 指定字符串数组中,是否包含空字符串
079     *
080     * @param strs
081     * @return
082     */
083    public static boolean hasEmpty(String... strs) {
084        if (ArrayUtil.isEmpty(strs)) {
085            return true;
086        }
087
088        for (String str : strs) {
089            if (isEmpty(str)) {
090                return true;
091            }
092        }
093        return false;
094    }
095
096    /**
097     * 是否存都不为null或空对象或空白符的对象
098     *
099     * @param args
100     * @return
101     */
102    public static boolean isAllNotEmpty(String... args) {
103        return false == hasEmpty(args);
104    }
105
106    /**
107     * 去空格
108     */
109    public static String trim(String str) {
110        return (str == null ? "" : str.trim());
111    }
112
113    /**
114     * 判断给定的collection列表中是否包含数组array 判断给定的数组array中是否包含给定的元素value
115     *
116     * @param collection 给定的集合
117     * @param array      给定的数组
118     * @return boolean 结果
119     */
120    public static boolean containsAny(Collection<String> collection, String... array) {
121        if (CollUtil.isEmpty(collection) || ArrayUtil.isEmpty(array)) {
122            return false;
123        } else {
124            for (String str : array) {
125                if (collection.contains(str)) {
126                    return true;
127                }
128            }
129            return false;
130        }
131    }
132
133    /**
134     * 截取字符串
135     *
136     * @param str   字符串
137     * @param start 开始
138     * @return 结果
139     */
140    public static String substring(final String str, int start) {
141        if (str == null) {
142            return NULLSTR;
143        }
144
145        if (start < 0) {
146            start = str.length() + start;
147        }
148
149        if (start < 0) {
150            start = 0;
151        }
152        if (start > str.length()) {
153            return NULLSTR;
154        }
155
156        return str.substring(start);
157    }
158
159    /**
160     * 截取字符串
161     *
162     * @param str   字符串
163     * @param start 开始
164     * @param end   结束
165     * @return 结果
166     */
167    public static String substring(final String str, int start, int end) {
168        if (str == null) {
169            return NULLSTR;
170        }
171
172        if (end < 0) {
173            end = str.length() + end;
174        }
175        if (start < 0) {
176            start = str.length() + start;
177        }
178
179        if (end > str.length()) {
180            end = str.length();
181        }
182
183        if (start > end) {
184            return NULLSTR;
185        }
186
187        if (start < 0) {
188            start = 0;
189        }
190        if (end < 0) {
191            end = 0;
192        }
193
194        return str.substring(start, end);
195    }
196
197    /**
198     * 字符串转set
199     *
200     * @param str 字符串
201     * @param sep 分隔符
202     * @return set集合
203     */
204    public static Set<String> str2Set(String str, String sep) {
205        return new HashSet<>(str2List(str, sep, true, false));
206    }
207
208    /**
209     * 字符串转list
210     *
211     * @param str 字符串
212     * @param sep 分隔符
213     * @return list集合
214     */
215    public static List<String> str2List(String str, String sep) {
216        return str2List(str, sep, true, true);
217    }
218
219    /**
220     * 字符串转list
221     *
222     * @param str         字符串
223     * @param sep         分隔符
224     * @param filterBlank 过滤纯空白
225     * @param trim        去掉首尾空白
226     * @return list集合
227     */
228    public static List<String> str2List(String str, String sep, boolean filterBlank, boolean trim) {
229        List<String> list = new ArrayList<String>();
230        if (isEmpty(str)) {
231            return list;
232        }
233
234        // 过滤空白字符串
235        if (filterBlank && isEmpty(str)) {
236            return list;
237        }
238        String[] split = str.split(sep);
239        for (String string : split) {
240            if (filterBlank && isEmpty(string)) {
241                continue;
242            }
243            if (trim) {
244                string = string.trim();
245            }
246            list.add(string);
247        }
248
249        return list;
250    }
251
252    /**
253     * 驼峰转下划线命名
254     */
255    public static String toUnderScoreCase(String str) {
256        if (str == null) {
257            return null;
258        }
259        StringBuilder sb = new StringBuilder();
260        // 前置字符是否大写
261        boolean preCharIsUpperCase = true;
262        // 当前字符是否大写
263        boolean curreCharIsUpperCase = true;
264        // 下一字符是否大写
265        boolean nexteCharIsUpperCase = true;
266        for (int i = 0; i < str.length(); i++) {
267            char c = str.charAt(i);
268            if (i > 0) {
269                preCharIsUpperCase = Character.isUpperCase(str.charAt(i - 1));
270            } else {
271                preCharIsUpperCase = false;
272            }
273
274            curreCharIsUpperCase = Character.isUpperCase(c);
275
276            if (i < (str.length() - 1)) {
277                nexteCharIsUpperCase = Character.isUpperCase(str.charAt(i + 1));
278            }
279
280            if (preCharIsUpperCase && curreCharIsUpperCase && !nexteCharIsUpperCase) {
281                sb.append(SEPARATOR);
282            } else if ((i != 0 && !preCharIsUpperCase) && curreCharIsUpperCase) {
283                sb.append(SEPARATOR);
284            }
285            sb.append(Character.toLowerCase(c));
286        }
287
288        return sb.toString();
289    }
290
291    /**
292     * 是否包含字符串
293     *
294     * @param str  验证字符串
295     * @param strs 字符串组
296     * @return 包含返回true
297     */
298    public static boolean inStringIgnoreCase(String str, String... strs) {
299        if (str != null && strs != null) {
300            for (String s : strs) {
301                if (str.equalsIgnoreCase(trim(s))) {
302                    return true;
303                }
304            }
305        }
306        return false;
307    }
308
309    /**
310     * 将下划线大写方式命名的字符串转换为驼峰式。如果转换前的下划线大写方式命名的字符串为空,则返回空字符串。 例如:HELLO_WORLD->HelloWorld
311     *
312     * @param name 转换前的下划线大写方式命名的字符串
313     * @return 转换后的驼峰式命名的字符串
314     */
315    public static String convertToCamelCase(String name) {
316        StringBuilder result = new StringBuilder();
317        // 快速检查
318        if (name == null || name.isEmpty()) {
319            // 没必要转换
320            return "";
321        } else if (!name.contains("_")) {
322            // 不含下划线,仅将首字母大写
323            return name.substring(0, 1).toUpperCase() + name.substring(1);
324        }
325        // 用下划线将原始字符串分割
326        String[] camels = name.split("_");
327        for (String camel : camels) {
328            // 跳过原始字符串中开头、结尾的下换线或双重下划线
329            if (camel.isEmpty()) {
330                continue;
331            }
332            // 首字母大写
333            result.append(camel.substring(0, 1).toUpperCase());
334            result.append(camel.substring(1).toLowerCase());
335        }
336        return result.toString();
337    }
338
339    /**
340     * 驼峰式命名法
341     * 例如:user_name->userName
342     */
343    public static String toCamelCase(String s) {
344        if (s == null) {
345            return null;
346        }
347        if (s.indexOf(SEPARATOR) == -1) {
348            return s;
349        }
350        s = s.toLowerCase();
351        StringBuilder sb = new StringBuilder(s.length());
352        boolean upperCase = false;
353        for (int i = 0; i < s.length(); i++) {
354            char c = s.charAt(i);
355
356            if (c == SEPARATOR) {
357                upperCase = true;
358            } else if (upperCase) {
359                sb.append(Character.toUpperCase(c));
360                upperCase = false;
361            } else {
362                sb.append(c);
363            }
364        }
365        return sb.toString();
366    }
367
368    /**
369     * 数字左边补齐0,使之达到指定长度。注意,如果数字转换为字符串后,长度大于size,则只保留 最后size个字符。
370     *
371     * @param num  数字对象
372     * @param size 字符串指定长度
373     * @return 返回数字的字符串格式,该字符串为指定长度。
374     */
375    public static final String padl(final Number num, final int size) {
376        return padl(num.toString(), size, '0');
377    }
378
379    /**
380     * 字符串左补齐。如果原始字符串s长度大于size,则只保留最后size个字符。
381     *
382     * @param s    原始字符串
383     * @param size 字符串指定长度
384     * @param c    用于补齐的字符
385     * @return 返回指定长度的字符串,由原字符串左补齐或截取得到。
386     */
387    public static final String padl(final String s, final int size, final char c) {
388        final StringBuilder sb = new StringBuilder(size);
389        if (s != null) {
390            final int len = s.length();
391            if (s.length() <= size) {
392                for (int i = size - len; i > 0; i--) {
393                    sb.append(c);
394                }
395                sb.append(s);
396            } else {
397                return s.substring(len - size, len);
398            }
399        } else {
400            for (int i = size; i > 0; i--) {
401                sb.append(c);
402            }
403        }
404        return sb.toString();
405    }
406}