001/*
002 *  Copyright (c) 2022-2025, Mybatis-Flex (fuhai999@gmail.com).
003 *  <p>
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 *  <p>
008 *  http://www.apache.org/licenses/LICENSE-2.0
009 *  <p>
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 com.mybatisflex.core.util;
017
018
019import com.mybatisflex.core.exception.FlexExceptions;
020
021import java.util.Collection;
022import java.util.Locale;
023import java.util.function.Function;
024import java.util.regex.Pattern;
025
026public class StringUtil {
027
028    private StringUtil() {
029    }
030
031    /**
032     * @see org.apache.ibatis.reflection.property.PropertyNamer#methodToProperty(String)
033     */
034    public static String methodToProperty(String name) {
035        if (name.startsWith("is")) {
036            name = name.substring(2);
037        } else if (name.startsWith("get") || name.startsWith("set")) {
038            name = name.substring(3);
039        } else {
040            throw FlexExceptions.wrap("Error parsing property name '%s'.  Didn't start with 'is', 'get' or 'set'.", name);
041        }
042        if (!name.isEmpty()) {
043            name = name.substring(0, 1).toLowerCase(Locale.ENGLISH).concat(name.substring(1));
044        }
045        return name;
046    }
047
048
049    /**
050     * 第一个字符转换为小写
051     *
052     * @param string
053     */
054    public static String firstCharToLowerCase(String string) {
055        char firstChar = string.charAt(0);
056        if (firstChar >= 'A' && firstChar <= 'Z') {
057            char[] chars = string.toCharArray();
058            chars[0] += ('a' - 'A');
059            return new String(chars);
060        }
061        return string;
062    }
063
064
065    /**
066     * 第一个字符转换为大写
067     *
068     * @param string
069     */
070    public static String firstCharToUpperCase(String string) {
071        char firstChar = string.charAt(0);
072        if (firstChar >= 'a' && firstChar <= 'z') {
073            char[] chars = string.toCharArray();
074            chars[0] -= ('a' - 'A');
075            return new String(chars);
076        }
077        return string;
078    }
079
080
081    /**
082     * 驼峰转下划线格式
083     *
084     * @param string
085     */
086    public static String camelToUnderline(String string) {
087        if (noText(string)) {
088            return "";
089        }
090        int strLen = string.length();
091        StringBuilder sb = new StringBuilder(strLen);
092        for (int i = 0; i < strLen; i++) {
093            char c = string.charAt(i);
094            if (Character.isUpperCase(c) && i > 0) {
095                char prev = string.charAt(i - 1);
096                if (!Character.isUpperCase(prev) && prev != '_') {
097                    sb.append('_');
098                }
099            }
100            sb.append(Character.toLowerCase(c));
101        }
102        return sb.toString();
103    }
104
105    /**
106     * 下划线转驼峰格式
107     *
108     * @param string
109     */
110    public static String underlineToCamel(String string) {
111        if (noText(string)) {
112            return "";
113        }
114        if (Character.isUpperCase(string.charAt(0))) {
115            string = string.toLowerCase();
116        }
117        int strLen = string.length();
118        StringBuilder sb = new StringBuilder(strLen);
119        for (int i = 0; i < strLen; i++) {
120            char c = string.charAt(i);
121            if (c == '_') {
122                if (++i < strLen) {
123                    sb.append(Character.toUpperCase(string.charAt(i)));
124                }
125            } else {
126                sb.append(c);
127            }
128        }
129        return sb.toString();
130    }
131
132
133    /**
134     * 删除字符串中的字符
135     */
136    public static String deleteChar(String string, char deleteChar) {
137        if (noText(string)) {
138            return "";
139        }
140        char[] chars = string.toCharArray();
141        StringBuilder sb = new StringBuilder(string.length());
142        for (char aChar : chars) {
143            if (aChar != deleteChar) {
144                sb.append(aChar);
145            }
146        }
147        return sb.toString();
148    }
149
150    public static String deleteChar(String string, char deleteChar1, char deleteChar2) {
151        if (noText(string)) {
152            return "";
153        }
154        char[] chars = string.toCharArray();
155        StringBuilder sb = new StringBuilder(string.length());
156        for (char aChar : chars) {
157            if (aChar != deleteChar1 && aChar != deleteChar2) {
158                sb.append(aChar);
159            }
160        }
161        return sb.toString();
162    }
163
164    private static boolean containsText(CharSequence charSequence) {
165        for (int i = 0; i < charSequence.length(); i++) {
166            if (!Character.isWhitespace(charSequence.charAt(i))) {
167                return true;
168            }
169        }
170        return false;
171    }
172
173
174    public static boolean hasText(String string) {
175        return string != null && !string.isEmpty() && containsText(string);
176    }
177
178
179    public static boolean allHasText(String... strings) {
180        for (String string : strings) {
181            if (!hasText(string)) {
182                return false;
183            }
184        }
185        return true;
186    }
187
188    /**
189     * 字符串为 null 或者内部字符全部为 ' ', '\t', '\n', '\r' 这四类字符时返回 true
190     */
191    public static boolean noText(String string) {
192        return !hasText(string);
193    }
194
195
196    /**
197     * 这个字符串是否是全是数字
198     *
199     * @param string
200     * @return 全部数数值时返回 true,否则返回 false
201     */
202    public static boolean isNumeric(String string) {
203        if (noText(string)) {
204            return false;
205        }
206        for (int i = string.length(); --i >= 0; ) {
207            int chr = string.charAt(i);
208            if (chr < 48 || chr > 57) {
209                return false;
210            }
211        }
212        return true;
213    }
214
215
216    public static boolean startsWithAny(String string, String... prefixes) {
217        if (noText(string) || prefixes == null) {
218            return false;
219        }
220
221        for (String prefix : prefixes) {
222            if (string.startsWith(prefix)) {
223                return true;
224            }
225        }
226        return false;
227    }
228
229
230    public static boolean endsWithAny(String str, String... suffixes) {
231        if (noText(str) || suffixes == null) {
232            return false;
233        }
234
235        for (String suffix : suffixes) {
236            if (str.endsWith(suffix)) {
237                return true;
238            }
239        }
240        return false;
241    }
242
243
244    /**
245     * 正则匹配
246     *
247     * @param regex
248     * @param input
249     * @return
250     */
251    public static boolean matches(String regex, String input) {
252        if (null == regex || null == input) {
253            return false;
254        }
255        return Pattern.matches(regex, input);
256    }
257
258    /**
259     * 合并字符串,优化 String.join() 方法
260     *
261     * @param delimiter
262     * @param elements
263     * @return 新拼接好的字符串
264     * @see String#join(CharSequence, CharSequence...)
265     */
266    public static String join(String delimiter, CharSequence... elements) {
267        if (ArrayUtil.isEmpty(elements)) {
268            return "";
269        } else if (elements.length == 1) {
270            return String.valueOf(elements[0]);
271        } else {
272            return String.join(delimiter, elements);
273        }
274    }
275
276    /**
277     * 合并字符串,优化 String.join() 方法
278     *
279     * @param delimiter
280     * @param elements
281     * @return 新拼接好的字符串
282     * @see String#join(CharSequence, CharSequence...)
283     */
284    public static String join(String delimiter, Collection<? extends CharSequence> elements) {
285        if (CollectionUtil.isEmpty(elements)) {
286            return "";
287        } else if (elements.size() == 1) {
288            return String.valueOf(elements.iterator().next());
289        } else {
290            return String.join(delimiter, elements);
291        }
292    }
293
294
295    /**
296     * 合并字符串,优化 String.join() 方法
297     *
298     * @param delimiter
299     * @param objs
300     * @param function
301     * @param <T>
302     */
303    public static <T> String join(String delimiter, Collection<T> objs, Function<T, String> function) {
304        if (CollectionUtil.isEmpty(objs)) {
305            return "";
306        } else if (objs.size() == 1) {
307            T next = objs.iterator().next();
308            return String.valueOf(function.apply(next));
309        } else {
310            String[] strings = new String[objs.size()];
311            int index = 0;
312            for (T obj : objs) {
313                strings[index++] = function.apply(obj);
314            }
315            return String.join(delimiter, strings);
316        }
317    }
318
319    public static String buildSchemaWithTable(String schema, String tableName) {
320        return hasText(schema) ? schema + "." + tableName : tableName;
321    }
322
323    public static String[] getSchemaAndTableName(String tableNameWithSchema) {
324        int index = tableNameWithSchema.indexOf(".");
325        return index <= 0 ? new String[]{null, tableNameWithSchema.trim()}
326            : new String[]{tableNameWithSchema.substring(0, index).trim(), tableNameWithSchema.substring(index + 1).trim()};
327    }
328
329    public static String[] getTableNameWithAlias(String tableNameWithAlias) {
330        int index = tableNameWithAlias.indexOf(".");
331        return index <= 0 ? new String[]{tableNameWithAlias, null}
332            : new String[]{tableNameWithAlias.substring(0, index), tableNameWithAlias.substring(index + 1)};
333    }
334
335    public static String tryTrim(String string) {
336        return string != null ? string.trim() : null;
337    }
338
339    public static String substringAfterLast(String text, String prefix) {
340        if (text == null) {
341            return null;
342        }
343        if (prefix == null) {
344            return text;
345        }
346        return text.substring(text.lastIndexOf(prefix) + 1);
347    }
348
349
350}