Browse Source

add blob2string type handler 2.1.8-SNAPSHOT

= 7 years ago
parent
commit
878b91089f

+ 1 - 1
build.gradle

@@ -40,7 +40,7 @@ ext {
 
 allprojects{
     group = 'com.baomidou'
-    version = '2.1.7-SNAPSHOT'
+    version = '2.1.8-SNAPSHOT'
 }
 
 

+ 67 - 0
mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/handlers/BlobTypeHandler.java

@@ -0,0 +1,67 @@
+/**
+ * Copyright (c) 2011-2020, hubin (jobob@qq.com).
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.baomidou.mybatisplus.handlers;
+
+import java.io.ByteArrayInputStream;
+import java.io.UnsupportedEncodingException;
+import java.sql.CallableStatement;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+
+import org.apache.ibatis.type.BaseTypeHandler;
+import org.apache.ibatis.type.JdbcType;
+
+import com.baomidou.mybatisplus.exceptions.MybatisPlusException;
+import com.baomidou.mybatisplus.toolkit.StringUtils;
+
+/**
+ * <p>
+ * Blob To String 转换器
+ * </p>
+ *
+ * @author hubin
+ * @Date 2017-12-27
+ */
+public class BlobTypeHandler extends BaseTypeHandler<String> {
+
+	@Override
+	public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException {
+		try {
+			ps.setBinaryStream(i, new ByteArrayInputStream(parameter.getBytes(StringUtils.UTF8)),
+					parameter.length());
+		} catch (UnsupportedEncodingException e) {
+			throw new MybatisPlusException("Blob Encoding Error!");
+		}
+	}
+
+	@Override
+	public String getNullableResult(ResultSet rs, String columnName) throws SQLException {
+		return StringUtils.blob2String(rs.getBlob(columnName));
+	}
+
+	@Override
+	public String getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
+		return StringUtils.blob2String(cs.getBlob(columnIndex));
+	}
+
+	@Override
+	public String getNullableResult(ResultSet rs, int columnIndex)
+			throws SQLException {
+		return null;
+	}
+
+}

+ 794 - 765
mybatis-plus-support/src/main/java/com/baomidou/mybatisplus/toolkit/StringUtils.java

@@ -15,6 +15,7 @@
  */
 package com.baomidou.mybatisplus.toolkit;
 
+import java.sql.Blob;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
@@ -23,6 +24,8 @@ import java.util.List;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
+import com.baomidou.mybatisplus.exceptions.MybatisPlusException;
+
 /**
  * <p>
  * String 工具类
@@ -33,769 +36,795 @@ import java.util.regex.Pattern;
  */
 public class StringUtils {
 
-    /**
-     * 空字符
-     */
-    public static final String EMPTY = "";
-    /**
-     * 字符串 is
-     */
-    public static final String IS = "is";
-
-    /**
-     * 下划线字符
-     */
-    public static final char UNDERLINE = '_';
-
-    /**
-     * 占位符
-     */
-    public static final String PLACE_HOLDER = "{%s}";
-
-    private static boolean separatorBeforeDigit = false;
-    private static boolean separatorAfterDigit = true;
-
-
-    private StringUtils() {
-    }
-
-    /**
-     * <p>
-     * 判断字符串是否为空
-     * </p>
-     *
-     * @param cs 需要判断字符串
-     * @return 判断结果
-     */
-    public static boolean isEmpty(final CharSequence cs) {
-        int strLen;
-        if (cs == null || (strLen = cs.length()) == 0) {
-            return true;
-        }
-        for (int i = 0; i < strLen; i++) {
-            if (!Character.isWhitespace(cs.charAt(i))) {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    /**
-     * <p>
-     * 判断字符串是否不为空
-     * </p>
-     *
-     * @param cs 需要判断字符串
-     * @return 判断结果
-     */
-    public static boolean isNotEmpty(final CharSequence cs) {
-        return !isEmpty(cs);
-    }
-
-    /**
-     * <p>
-     * 字符串驼峰转下划线格式
-     * </p>
-     *
-     * @param param 需要转换的字符串
-     * @return 转换好的字符串
-     */
-    public static String camelToUnderline(String param) {
-        if (isEmpty(param)) {
-            return EMPTY;
-        }
-        int len = param.length();
-        StringBuilder sb = new StringBuilder(len);
-        for (int i = 0; i < len; i++) {
-            char c = param.charAt(i);
-            if (Character.isUpperCase(c) && i > 0) {
-                sb.append(UNDERLINE);
-            }
-            sb.append(Character.toLowerCase(c));
-        }
-        return sb.toString();
-    }
-
-    /**
-     * <p>
-     * 字符串下划线转驼峰格式
-     * </p>
-     *
-     * @param param 需要转换的字符串
-     * @return 转换好的字符串
-     */
-    public static String underlineToCamel(String param) {
-        if (isEmpty(param)) {
-            return EMPTY;
-        }
-        String temp = param.toLowerCase();
-        int len = temp.length();
-        StringBuilder sb = new StringBuilder(len);
-        for (int i = 0; i < len; i++) {
-            char c = temp.charAt(i);
-            if (c == UNDERLINE) {
-                if (++i < len) {
-                    sb.append(Character.toUpperCase(temp.charAt(i)));
-                }
-            } else {
-                sb.append(c);
-            }
-        }
-        return sb.toString();
-    }
-
-    /**
-     * <p>
-     * 首字母转换小写
-     * </p>
-     *
-     * @param param 需要转换的字符串
-     * @return 转换好的字符串
-     */
-    public static String firstToLowerCase(String param) {
-        if (isEmpty(param)) {
-            return EMPTY;
-        }
-        StringBuilder sb = new StringBuilder(param.length());
-        sb.append(param.substring(0, 1).toLowerCase());
-        sb.append(param.substring(1));
-        return sb.toString();
-    }
-
-    /**
-     * <p>
-     * 判断字符串是否为纯大写字母
-     * </p>
-     *
-     * @param str 要匹配的字符串
-     * @return
-     */
-    public static boolean isUpperCase(String str) {
-        return match("^[A-Z]+$", str);
-    }
-
-    /**
-     * <p>
-     * 正则表达式匹配
-     * </p>
-     *
-     * @param regex 正则表达式字符串
-     * @param str   要匹配的字符串
-     * @return 如果str 符合 regex的正则表达式格式,返回true, 否则返回 false;
-     */
-    public static boolean match(String regex, String str) {
-        Pattern pattern = Pattern.compile(regex);
-        Matcher matcher = pattern.matcher(str);
-        return matcher.matches();
-    }
-
-    /**
-     * <p>
-     * SQL 参数填充
-     * </p>
-     *
-     * @param content 填充内容
-     * @param args    填充参数
-     * @return
-     */
-    public static String sqlArgsFill(String content, Object... args) {
-        if (StringUtils.isEmpty(content)) {
-            return null;
-        }
-        if (args != null) {
-            int length = args.length;
-            if (length >= 1) {
-                for (int i = 0; i < length; i++) {
-                    content = content.replace(String.format(PLACE_HOLDER, i), sqlParam(args[i]));
-                }
-            }
-        }
-        return content;
-    }
-
-    /**
-     * <p>
-     * 获取SQL PARAMS字符串
-     * </p>
-     *
-     * @param obj
-     * @return
-     */
-    public static String sqlParam(Object obj) {
-        String repStr;
-        if (obj instanceof Collection) {
-            repStr = StringUtils.quotaMarkList((Collection<?>) obj);
-        } else {
-            repStr = StringUtils.quotaMark(obj);
-        }
-        return repStr;
-    }
-
-    /**
-     * <p>
-     * 使用单引号包含字符串
-     * </p>
-     *
-     * @param obj 原字符串
-     * @return 单引号包含的原字符串
-     */
-    public static String quotaMark(Object obj) {
-        String srcStr = String.valueOf(obj);
-        if (obj instanceof CharSequence) {
-            // fix #79
-            return StringEscape.escapeString(srcStr);
-        }
-        return srcStr;
-    }
-
-    /**
-     * <p>
-     * 使用单引号包含字符串
-     * </p>
-     *
-     * @param coll 集合
-     * @return 单引号包含的原字符串的集合形式
-     */
-    public static String quotaMarkList(Collection<?> coll) {
-        StringBuilder sqlBuild = new StringBuilder();
-        sqlBuild.append("(");
-        int size = coll.size();
-        int i = 0;
-        Iterator<?> iterator = coll.iterator();
-        while (iterator.hasNext()) {
-            String tempVal = StringUtils.quotaMark(iterator.next());
-            sqlBuild.append(tempVal);
-            if (i + 1 < size) {
-                sqlBuild.append(",");
-            }
-            i++;
-        }
-        sqlBuild.append(")");
-        return sqlBuild.toString();
-    }
-
-    /**
-     * <p>
-     * 拼接字符串第二个字符串第一个字母大写
-     * </p>
-     *
-     * @param concatStr
-     * @param str
-     * @return
-     */
-    public static String concatCapitalize(String concatStr, final String str) {
-        if (isEmpty(concatStr)) {
-            concatStr = EMPTY;
-        }
-        int strLen;
-        if (str == null || (strLen = str.length()) == 0) {
-            return str;
-        }
-
-        final char firstChar = str.charAt(0);
-        if (Character.isTitleCase(firstChar)) {
-            // already capitalized
-            return str;
-        }
-
-        StringBuilder sb = new StringBuilder(strLen);
-        sb.append(concatStr);
-        sb.append(Character.toTitleCase(firstChar));
-        sb.append(str.substring(1));
-        return sb.toString();
-    }
-
-    /**
-     * <p>
-     * 字符串第一个字母大写
-     * </p>
-     *
-     * @param str
-     * @return
-     */
-    public static String capitalize(final String str) {
-        return concatCapitalize(null, str);
-    }
-
-    /**
-     * <p>
-     * 判断对象是否为空
-     * </p>
-     *
-     * @param object
-     * @return
-     */
-    public static boolean checkValNotNull(Object object) {
-        if (object instanceof CharSequence) {
-            return isNotEmpty((CharSequence) object);
-        }
-        return object != null;
-    }
-
-    /**
-     * <p>
-     * 判断对象是否为空
-     * </p>
-     *
-     * @param object
-     * @return
-     */
-    public static boolean checkValNull(Object object) {
-        return !checkValNotNull(object);
-    }
-
-    /**
-     * <p>
-     * 包含大写字母
-     * </p>
-     *
-     * @param word 待判断字符串
-     * @return
-     */
-    public static boolean containsUpperCase(String word) {
-        for (int i = 0; i < word.length(); i++) {
-            char c = word.charAt(i);
-            if (Character.isUpperCase(c)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    /**
-     * <p>
-     * 是否为大写命名
-     * </p>
-     *
-     * @param word 待判断字符串
-     * @return
-     */
-    public static boolean isCapitalMode(String word) {
-        return null != word && word.matches("^[0-9A-Z/_]+$");
-    }
-
-    /**
-     * <p>
-     * 是否为驼峰下划线混合命名
-     * </p>
-     *
-     * @param word 待判断字符串
-     * @return
-     */
-    public static boolean isMixedMode(String word) {
-        return Pattern.compile(".*[A-Z]+.*").matcher(word).matches() && Pattern.compile(".*[/_]+.*").matcher(word).matches();
-    }
-
-    /**
-     * <p>
-     * Check if a String ends with a specified suffix.
-     * </p>
-     * <p>
-     * <p>
-     * <code>null</code>s are handled without exceptions. Two <code>null</code>
-     * references are considered to be equal. The comparison is case sensitive.
-     * </p>
-     * <p>
-     * <pre>
-     * StringUtils.endsWith(null, null)      = true
-     * StringUtils.endsWith(null, "abcdef")  = false
-     * StringUtils.endsWith("def", null)     = false
-     * StringUtils.endsWith("def", "abcdef") = true
-     * StringUtils.endsWith("def", "ABCDEF") = false
-     * </pre>
-     *
-     * @param str    the String to check, may be null
-     * @param suffix the suffix to find, may be null
-     * @return <code>true</code> if the String ends with the suffix, case
-     * sensitive, or both <code>null</code>
-     * @see java.lang.String#endsWith(String)
-     * @since 2.4
-     */
-    public static boolean endsWith(String str, String suffix) {
-        return endsWith(str, suffix, false);
-    }
-
-    /**
-     * <p>
-     * Case insensitive check if a String ends with a specified suffix.
-     * </p>
-     * <p>
-     * <p>
-     * <code>null</code>s are handled without exceptions. Two <code>null</code>
-     * references are considered to be equal. The comparison is case
-     * insensitive.
-     * </p>
-     * <p>
-     * <pre>
-     * StringUtils.endsWithIgnoreCase(null, null)      = true
-     * StringUtils.endsWithIgnoreCase(null, "abcdef")  = false
-     * StringUtils.endsWithIgnoreCase("def", null)     = false
-     * StringUtils.endsWithIgnoreCase("def", "abcdef") = true
-     * StringUtils.endsWithIgnoreCase("def", "ABCDEF") = false
-     * </pre>
-     *
-     * @param str    the String to check, may be null
-     * @param suffix the suffix to find, may be null
-     * @return <code>true</code> if the String ends with the suffix, case
-     * insensitive, or both <code>null</code>
-     * @see java.lang.String#endsWith(String)
-     * @since 2.4
-     */
-    public static boolean endsWithIgnoreCase(String str, String suffix) {
-        return endsWith(str, suffix, true);
-    }
-
-    /**
-     * <p>
-     * Check if a String ends with a specified suffix (optionally case
-     * insensitive).
-     * </p>
-     *
-     * @param str        the String to check, may be null
-     * @param suffix     the suffix to find, may be null
-     * @param ignoreCase inidicates whether the compare should ignore case (case
-     *                   insensitive) or not.
-     * @return <code>true</code> if the String starts with the prefix or both
-     * <code>null</code>
-     * @see java.lang.String#endsWith(String)
-     */
-    private static boolean endsWith(String str, String suffix, boolean ignoreCase) {
-        if (str == null || suffix == null) {
-            return (str == null && suffix == null);
-        }
-        if (suffix.length() > str.length()) {
-            return false;
-        }
-        int strOffset = str.length() - suffix.length();
-        return str.regionMatches(ignoreCase, strOffset, suffix, 0, suffix.length());
-    }
-
-    /**
-     * <p>
-     * Splits the provided text into an array, separators specified. This is an
-     * alternative to using StringTokenizer.
-     * </p>
-     * <p>
-     * <p>
-     * The separator is not included in the returned String array. Adjacent
-     * separators are treated as one separator. For more control over the split
-     * use the StrTokenizer class.
-     * </p>
-     * <p>
-     * <p>
-     * A {@code null} input String returns {@code null}. A {@code null}
-     * separatorChars splits on whitespace.
-     * </p>
-     * <p>
-     * <pre>
-     * StringUtils.split(null, *)         = null
-     * StringUtils.split("", *)           = []
-     * StringUtils.split("abc def", null) = ["abc", "def"]
-     * StringUtils.split("abc def", " ")  = ["abc", "def"]
-     * StringUtils.split("abc  def", " ") = ["abc", "def"]
-     * StringUtils.split("ab:cd:ef", ":") = ["ab", "cd", "ef"]
-     * </pre>
-     *
-     * @param str            the String to parse, may be null
-     * @param separatorChars the characters used as the delimiters, {@code null} splits on
-     *                       whitespace
-     * @return an array of parsed Strings, {@code null} if null String input
-     */
-    public static String[] split(final String str, final String separatorChars) {
-        List<String> strings = splitWorker(str, separatorChars, -1, false);
-        return strings.toArray(new String[strings.size()]);
-    }
-
-    /**
-     * Performs the logic for the {@code split} and
-     * {@code splitPreserveAllTokens} methods that return a maximum array
-     * length.
-     *
-     * @param str               the String to parse, may be {@code null}
-     * @param separatorChars    the separate character
-     * @param max               the maximum number of elements to include in the array. A zero
-     *                          or negative value implies no limit.
-     * @param preserveAllTokens if {@code true}, adjacent separators are treated as empty
-     *                          token separators; if {@code false}, adjacent separators are
-     *                          treated as one separator.
-     * @return an array of parsed Strings, {@code null} if null String input
-     */
-    public static List<String> splitWorker(final String str, final String separatorChars, final int max,
-                                           final boolean preserveAllTokens) {
-        // Performance tuned for 2.0 (JDK1.4)
-        // Direct code is quicker than StringTokenizer.
-        // Also, StringTokenizer uses isSpace() not isWhitespace()
-
-        if (str == null) {
-            return null;
-        }
-        final int len = str.length();
-        if (len == 0) {
-            return Collections.emptyList();
-        }
-        final List<String> list = new ArrayList<>();
-        int sizePlus1 = 1;
-        int i = 0, start = 0;
-        boolean match = false;
-        boolean lastMatch = false;
-        if (separatorChars == null) {
-            // Null separator means use whitespace
-            while (i < len) {
-                if (Character.isWhitespace(str.charAt(i))) {
-                    if (match || preserveAllTokens) {
-                        lastMatch = true;
-                        if (sizePlus1++ == max) {
-                            i = len;
-                            lastMatch = false;
-                        }
-                        list.add(str.substring(start, i));
-                        match = false;
-                    }
-                    start = ++i;
-                    continue;
-                }
-                lastMatch = false;
-                match = true;
-                i++;
-            }
-        } else if (separatorChars.length() == 1) {
-            // Optimise 1 character case
-            final char sep = separatorChars.charAt(0);
-            while (i < len) {
-                if (str.charAt(i) == sep) {
-                    if (match || preserveAllTokens) {
-                        lastMatch = true;
-                        if (sizePlus1++ == max) {
-                            i = len;
-                            lastMatch = false;
-                        }
-                        list.add(str.substring(start, i));
-                        match = false;
-                    }
-                    start = ++i;
-                    continue;
-                }
-                lastMatch = false;
-                match = true;
-                i++;
-            }
-        } else {
-            // standard case
-            while (i < len) {
-                if (separatorChars.indexOf(str.charAt(i)) >= 0) {
-                    if (match || preserveAllTokens) {
-                        lastMatch = true;
-                        if (sizePlus1++ == max) {
-                            i = len;
-                            lastMatch = false;
-                        }
-                        list.add(str.substring(start, i));
-                        match = false;
-                    }
-                    start = ++i;
-                    continue;
-                }
-                lastMatch = false;
-                match = true;
-                i++;
-            }
-        }
-        if (match || preserveAllTokens && lastMatch) {
-            list.add(str.substring(start, i));
-        }
-        return list;
-    }
-
-    /**
-     * <p>
-     * 是否为CharSequence类型
-     * </p>
-     *
-     * @param cls
-     * @return
-     */
-    public static Boolean isCharSequence(Class<?> cls) {
-        return cls != null && CharSequence.class.isAssignableFrom(cls);
-    }
-
-    /**
-     * <p>
-     * 去除boolean类型is开头的字符串
-     * </p>
-     *
-     * @param propertyName 字段名
-     * @param propertyType 字段类型
-     * @return
-     */
-    public static String removeIsPrefixIfBoolean(String propertyName, Class<?> propertyType) {
-        if (isBoolean(propertyType) && propertyName.startsWith(IS)) {
-            String property = propertyName.replaceFirst(IS, EMPTY);
-            if (isEmpty(property)) {
-                return propertyName;
-            } else {
-                String firstCharToLowerStr = firstCharToLower(property);
-                return property.equals(firstCharToLowerStr) ? propertyName : firstCharToLowerStr;
-            }
-        }
-        return propertyName;
-    }
-
-    /**
-     * <p>
-     * 是否为CharSequence类型
-     * </p>
-     *
-     * @param propertyType
-     * @return
-     */
-    public static Boolean isCharSequence(String propertyType) {
-        try {
-            return isCharSequence(Class.forName(propertyType));
-        } catch (ClassNotFoundException e) {
-            return false;
-        }
-    }
-
-    /**
-     * <p>
-     * 是否为Boolean类型(包含普通类型)
-     * </p>
-     *
-     * @param propertyCls
-     * @return
-     */
-    public static Boolean isBoolean(Class<?> propertyCls) {
-        return propertyCls != null && (boolean.class.isAssignableFrom(propertyCls) || Boolean.class.isAssignableFrom(propertyCls));
-    }
-
-    /**
-     * <p>
-     * 第一个首字母小写,之后字符大小写的不变<br>
-     * StringUtils.firstCharToLower( "UserService" )     = userService
-     * StringUtils.firstCharToLower( "UserServiceImpl" ) = userServiceImpl
-     * </p>
-     *
-     * @param rawString 需要处理的字符串
-     * @return
-     */
-    public static String firstCharToLower(String rawString) {
-        return prefixToLower(rawString, 1);
-    }
-
-    /**
-     * <p>
-     * 前n个首字母小写,之后字符大小写的不变
-     * </p>
-     *
-     * @param rawString 需要处理的字符串
-     * @param index     多少个字符(从左至右)
-     * @return
-     */
-    public static String prefixToLower(String rawString, int index) {
-        String beforeChar = rawString.substring(0, index).toLowerCase();
-        String afterChar = rawString.substring(index, rawString.length());
-        return beforeChar + afterChar;
-    }
-
-    /**
-     * <p>
-     * 删除字符前缀之后,首字母小写,之后字符大小写的不变<br>
-     * StringUtils.removePrefixAfterPrefixToLower( "isUser", 2 )     = user
-     * StringUtils.removePrefixAfterPrefixToLower( "isUserInfo", 2 ) = userInfo
-     * </p>
-     *
-     * @param rawString 需要处理的字符串
-     * @param index     删除多少个字符(从左至右)
-     * @return
-     */
-    public static String removePrefixAfterPrefixToLower(String rawString, int index) {
-        return prefixToLower(rawString.substring(index, rawString.length()), 1);
-    }
-
-    /**
-     * <p>
-     * 驼峰转连字符<br>
-     * StringUtils.camelToHyphen( "managerAdminUserService" ) = manager-admin-user-service
-     * </p>
-     *
-     * @param input
-     * @return 以'-'分隔
-     * @see <a href="https://github.com/krasa/StringManipulation">document</a>
-     */
-    public static String camelToHyphen(String input) {
-        return wordsToHyphenCase(wordsAndHyphenAndCamelToConstantCase(input));
-    }
-
-    private static String wordsAndHyphenAndCamelToConstantCase(String input) {
-        boolean betweenUpperCases = false;
-        boolean containsLowerCase = containsLowerCase(input);
-
-        StringBuilder buf = new StringBuilder();
-        char previousChar = ' ';
-        char[] chars = input.toCharArray();
-        for (int i = 0; i < chars.length; i++) {
-            char c = chars[i];
-            boolean isUpperCaseAndPreviousIsUpperCase = (Character.isUpperCase(previousChar)) && (Character.isUpperCase(c));
-            boolean isUpperCaseAndPreviousIsLowerCase = (Character.isLowerCase(previousChar)) && (Character.isUpperCase(c));
-
-            boolean previousIsWhitespace = Character.isWhitespace(previousChar);
-            boolean lastOneIsNotUnderscore = (buf.length() > 0) && (buf.charAt(buf.length() - 1) != '_');
-            boolean isNotUnderscore = c != '_';
-            if ((lastOneIsNotUnderscore) && ((isUpperCaseAndPreviousIsLowerCase) || (previousIsWhitespace) || ((betweenUpperCases)
-                    && (containsLowerCase) && (isUpperCaseAndPreviousIsUpperCase)))) {
-                buf.append("_");
-            } else if (((separatorAfterDigit) && (Character.isDigit(previousChar))
-                    && (Character.isLetter(c))) || ((separatorBeforeDigit) && (Character
-                    .isDigit(c)) && (Character.isLetter(previousChar)))) {
-                buf.append('_');
-            }
-            if ((shouldReplace(c)) && (lastOneIsNotUnderscore)) {
-                buf.append('_');
-            } else if ((!Character.isWhitespace(c)) && ((isNotUnderscore) || (lastOneIsNotUnderscore))) {
-                buf.append(Character.toUpperCase(c));
-            }
-            previousChar = c;
-        }
-        if (Character.isWhitespace(previousChar)) {
-            buf.append("_");
-        }
-        return buf.toString();
-    }
-
-    public static boolean containsLowerCase(String s) {
-        for (char c : s.toCharArray()) {
-            if (Character.isLowerCase(c)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    private static boolean shouldReplace(char c) {
-        return (c == '.') || (c == '_') || (c == '-');
-    }
-
-    private static String wordsToHyphenCase(String s) {
-        StringBuilder buf = new StringBuilder();
-        char lastChar = 'a';
-        for (char c : s.toCharArray()) {
-            if ((Character.isWhitespace(lastChar)) && (!Character.isWhitespace(c))
-                    && ('-' != c) && (buf.length() > 0)
-                    && (buf.charAt(buf.length() - 1) != '-')) {
-                buf.append("-");
-            }
-            if ('_' == c) {
-                buf.append('-');
-            } else if ('.' == c) {
-                buf.append('-');
-            } else if (!Character.isWhitespace(c)) {
-                buf.append(Character.toLowerCase(c));
-            }
-            lastChar = c;
-        }
-        if (Character.isWhitespace(lastChar)) {
-            buf.append("-");
-        }
-        return buf.toString();
-    }
+	/**
+	 * UTF-8 编码格式
+	 */
+	public static final String UTF8 = "UTF-8";
+
+	/**
+	 * 空字符
+	 */
+	public static final String EMPTY = "";
+	/**
+	 * 字符串 is
+	 */
+	public static final String IS = "is";
+
+	/**
+	 * 下划线字符
+	 */
+	public static final char UNDERLINE = '_';
+
+	/**
+	 * 占位符
+	 */
+	public static final String PLACE_HOLDER = "{%s}";
+
+	private static boolean separatorBeforeDigit = false;
+	private static boolean separatorAfterDigit = true;
+
+
+	private StringUtils() {
+	}
+
+
+	/**
+	 * <p>
+	 * Blob 转为 String 格式
+	 * </p>
+	 *
+	 * @param blob Blob 对象
+	 * @return
+	 */
+	public static String blob2String(Blob blob) {
+		if (null != blob) {
+			try {
+				byte[] returnValue = blob.getBytes(1, (int) blob.length());
+				return new String(returnValue, UTF8);
+			} catch (Exception e) {
+				throw new MybatisPlusException("Blob Convert To String Error!");
+			}
+		}
+		return null;
+	}
+
+	/**
+	 * <p>
+	 * 判断字符串是否为空
+	 * </p>
+	 *
+	 * @param cs 需要判断字符串
+	 * @return 判断结果
+	 */
+	public static boolean isEmpty(final CharSequence cs) {
+		int strLen;
+		if (cs == null || (strLen = cs.length()) == 0) {
+			return true;
+		}
+		for (int i = 0; i < strLen; i++) {
+			if (!Character.isWhitespace(cs.charAt(i))) {
+				return false;
+			}
+		}
+		return true;
+	}
+
+	/**
+	 * <p>
+	 * 判断字符串是否不为空
+	 * </p>
+	 *
+	 * @param cs 需要判断字符串
+	 * @return 判断结果
+	 */
+	public static boolean isNotEmpty(final CharSequence cs) {
+		return !isEmpty(cs);
+	}
+
+	/**
+	 * <p>
+	 * 字符串驼峰转下划线格式
+	 * </p>
+	 *
+	 * @param param 需要转换的字符串
+	 * @return 转换好的字符串
+	 */
+	public static String camelToUnderline(String param) {
+		if (isEmpty(param)) {
+			return EMPTY;
+		}
+		int len = param.length();
+		StringBuilder sb = new StringBuilder(len);
+		for (int i = 0; i < len; i++) {
+			char c = param.charAt(i);
+			if (Character.isUpperCase(c) && i > 0) {
+				sb.append(UNDERLINE);
+			}
+			sb.append(Character.toLowerCase(c));
+		}
+		return sb.toString();
+	}
+
+	/**
+	 * <p>
+	 * 字符串下划线转驼峰格式
+	 * </p>
+	 *
+	 * @param param 需要转换的字符串
+	 * @return 转换好的字符串
+	 */
+	public static String underlineToCamel(String param) {
+		if (isEmpty(param)) {
+			return EMPTY;
+		}
+		String temp = param.toLowerCase();
+		int len = temp.length();
+		StringBuilder sb = new StringBuilder(len);
+		for (int i = 0; i < len; i++) {
+			char c = temp.charAt(i);
+			if (c == UNDERLINE) {
+				if (++i < len) {
+					sb.append(Character.toUpperCase(temp.charAt(i)));
+				}
+			} else {
+				sb.append(c);
+			}
+		}
+		return sb.toString();
+	}
+
+	/**
+	 * <p>
+	 * 首字母转换小写
+	 * </p>
+	 *
+	 * @param param 需要转换的字符串
+	 * @return 转换好的字符串
+	 */
+	public static String firstToLowerCase(String param) {
+		if (isEmpty(param)) {
+			return EMPTY;
+		}
+		StringBuilder sb = new StringBuilder(param.length());
+		sb.append(param.substring(0, 1).toLowerCase());
+		sb.append(param.substring(1));
+		return sb.toString();
+	}
+
+	/**
+	 * <p>
+	 * 判断字符串是否为纯大写字母
+	 * </p>
+	 *
+	 * @param str 要匹配的字符串
+	 * @return
+	 */
+	public static boolean isUpperCase(String str) {
+		return match("^[A-Z]+$", str);
+	}
+
+	/**
+	 * <p>
+	 * 正则表达式匹配
+	 * </p>
+	 *
+	 * @param regex 正则表达式字符串
+	 * @param str   要匹配的字符串
+	 * @return 如果str 符合 regex的正则表达式格式,返回true, 否则返回 false;
+	 */
+	public static boolean match(String regex, String str) {
+		Pattern pattern = Pattern.compile(regex);
+		Matcher matcher = pattern.matcher(str);
+		return matcher.matches();
+	}
+
+	/**
+	 * <p>
+	 * SQL 参数填充
+	 * </p>
+	 *
+	 * @param content 填充内容
+	 * @param args    填充参数
+	 * @return
+	 */
+	public static String sqlArgsFill(String content, Object... args) {
+		if (StringUtils.isEmpty(content)) {
+			return null;
+		}
+		if (args != null) {
+			int length = args.length;
+			if (length >= 1) {
+				for (int i = 0; i < length; i++) {
+					content = content.replace(String.format(PLACE_HOLDER, i), sqlParam(args[i]));
+				}
+			}
+		}
+		return content;
+	}
+
+	/**
+	 * <p>
+	 * 获取SQL PARAMS字符串
+	 * </p>
+	 *
+	 * @param obj
+	 * @return
+	 */
+	public static String sqlParam(Object obj) {
+		String repStr;
+		if (obj instanceof Collection) {
+			repStr = StringUtils.quotaMarkList((Collection<?>) obj);
+		} else {
+			repStr = StringUtils.quotaMark(obj);
+		}
+		return repStr;
+	}
+
+	/**
+	 * <p>
+	 * 使用单引号包含字符串
+	 * </p>
+	 *
+	 * @param obj 原字符串
+	 * @return 单引号包含的原字符串
+	 */
+	public static String quotaMark(Object obj) {
+		String srcStr = String.valueOf(obj);
+		if (obj instanceof CharSequence) {
+			// fix #79
+			return StringEscape.escapeString(srcStr);
+		}
+		return srcStr;
+	}
+
+	/**
+	 * <p>
+	 * 使用单引号包含字符串
+	 * </p>
+	 *
+	 * @param coll 集合
+	 * @return 单引号包含的原字符串的集合形式
+	 */
+	public static String quotaMarkList(Collection<?> coll) {
+		StringBuilder sqlBuild = new StringBuilder();
+		sqlBuild.append("(");
+		int size = coll.size();
+		int i = 0;
+		Iterator<?> iterator = coll.iterator();
+		while (iterator.hasNext()) {
+			String tempVal = StringUtils.quotaMark(iterator.next());
+			sqlBuild.append(tempVal);
+			if (i + 1 < size) {
+				sqlBuild.append(",");
+			}
+			i++;
+		}
+		sqlBuild.append(")");
+		return sqlBuild.toString();
+	}
+
+	/**
+	 * <p>
+	 * 拼接字符串第二个字符串第一个字母大写
+	 * </p>
+	 *
+	 * @param concatStr
+	 * @param str
+	 * @return
+	 */
+	public static String concatCapitalize(String concatStr, final String str) {
+		if (isEmpty(concatStr)) {
+			concatStr = EMPTY;
+		}
+		int strLen;
+		if (str == null || (strLen = str.length()) == 0) {
+			return str;
+		}
+
+		final char firstChar = str.charAt(0);
+		if (Character.isTitleCase(firstChar)) {
+			// already capitalized
+			return str;
+		}
+
+		StringBuilder sb = new StringBuilder(strLen);
+		sb.append(concatStr);
+		sb.append(Character.toTitleCase(firstChar));
+		sb.append(str.substring(1));
+		return sb.toString();
+	}
+
+	/**
+	 * <p>
+	 * 字符串第一个字母大写
+	 * </p>
+	 *
+	 * @param str
+	 * @return
+	 */
+	public static String capitalize(final String str) {
+		return concatCapitalize(null, str);
+	}
+
+	/**
+	 * <p>
+	 * 判断对象是否为空
+	 * </p>
+	 *
+	 * @param object
+	 * @return
+	 */
+	public static boolean checkValNotNull(Object object) {
+		if (object instanceof CharSequence) {
+			return isNotEmpty((CharSequence) object);
+		}
+		return object != null;
+	}
+
+	/**
+	 * <p>
+	 * 判断对象是否为空
+	 * </p>
+	 *
+	 * @param object
+	 * @return
+	 */
+	public static boolean checkValNull(Object object) {
+		return !checkValNotNull(object);
+	}
+
+	/**
+	 * <p>
+	 * 包含大写字母
+	 * </p>
+	 *
+	 * @param word 待判断字符串
+	 * @return
+	 */
+	public static boolean containsUpperCase(String word) {
+		for (int i = 0; i < word.length(); i++) {
+			char c = word.charAt(i);
+			if (Character.isUpperCase(c)) {
+				return true;
+			}
+		}
+		return false;
+	}
+
+	/**
+	 * <p>
+	 * 是否为大写命名
+	 * </p>
+	 *
+	 * @param word 待判断字符串
+	 * @return
+	 */
+	public static boolean isCapitalMode(String word) {
+		return null != word && word.matches("^[0-9A-Z/_]+$");
+	}
+
+	/**
+	 * <p>
+	 * 是否为驼峰下划线混合命名
+	 * </p>
+	 *
+	 * @param word 待判断字符串
+	 * @return
+	 */
+	public static boolean isMixedMode(String word) {
+		return Pattern.compile(".*[A-Z]+.*").matcher(word).matches() && Pattern.compile(".*[/_]+.*").matcher(word).matches();
+	}
+
+	/**
+	 * <p>
+	 * Check if a String ends with a specified suffix.
+	 * </p>
+	 * <p>
+	 * <p>
+	 * <code>null</code>s are handled without exceptions. Two <code>null</code>
+	 * references are considered to be equal. The comparison is case sensitive.
+	 * </p>
+	 * <p>
+	 * <pre>
+	 * StringUtils.endsWith(null, null)      = true
+	 * StringUtils.endsWith(null, "abcdef")  = false
+	 * StringUtils.endsWith("def", null)     = false
+	 * StringUtils.endsWith("def", "abcdef") = true
+	 * StringUtils.endsWith("def", "ABCDEF") = false
+	 * </pre>
+	 *
+	 * @param str    the String to check, may be null
+	 * @param suffix the suffix to find, may be null
+	 * @return <code>true</code> if the String ends with the suffix, case
+	 * sensitive, or both <code>null</code>
+	 * @see java.lang.String#endsWith(String)
+	 * @since 2.4
+	 */
+	public static boolean endsWith(String str, String suffix) {
+		return endsWith(str, suffix, false);
+	}
+
+	/**
+	 * <p>
+	 * Case insensitive check if a String ends with a specified suffix.
+	 * </p>
+	 * <p>
+	 * <p>
+	 * <code>null</code>s are handled without exceptions. Two <code>null</code>
+	 * references are considered to be equal. The comparison is case
+	 * insensitive.
+	 * </p>
+	 * <p>
+	 * <pre>
+	 * StringUtils.endsWithIgnoreCase(null, null)      = true
+	 * StringUtils.endsWithIgnoreCase(null, "abcdef")  = false
+	 * StringUtils.endsWithIgnoreCase("def", null)     = false
+	 * StringUtils.endsWithIgnoreCase("def", "abcdef") = true
+	 * StringUtils.endsWithIgnoreCase("def", "ABCDEF") = false
+	 * </pre>
+	 *
+	 * @param str    the String to check, may be null
+	 * @param suffix the suffix to find, may be null
+	 * @return <code>true</code> if the String ends with the suffix, case
+	 * insensitive, or both <code>null</code>
+	 * @see java.lang.String#endsWith(String)
+	 * @since 2.4
+	 */
+	public static boolean endsWithIgnoreCase(String str, String suffix) {
+		return endsWith(str, suffix, true);
+	}
+
+	/**
+	 * <p>
+	 * Check if a String ends with a specified suffix (optionally case
+	 * insensitive).
+	 * </p>
+	 *
+	 * @param str        the String to check, may be null
+	 * @param suffix     the suffix to find, may be null
+	 * @param ignoreCase inidicates whether the compare should ignore case (case
+	 *                   insensitive) or not.
+	 * @return <code>true</code> if the String starts with the prefix or both
+	 * <code>null</code>
+	 * @see java.lang.String#endsWith(String)
+	 */
+	private static boolean endsWith(String str, String suffix, boolean ignoreCase) {
+		if (str == null || suffix == null) {
+			return (str == null && suffix == null);
+		}
+		if (suffix.length() > str.length()) {
+			return false;
+		}
+		int strOffset = str.length() - suffix.length();
+		return str.regionMatches(ignoreCase, strOffset, suffix, 0, suffix.length());
+	}
+
+	/**
+	 * <p>
+	 * Splits the provided text into an array, separators specified. This is an
+	 * alternative to using StringTokenizer.
+	 * </p>
+	 * <p>
+	 * <p>
+	 * The separator is not included in the returned String array. Adjacent
+	 * separators are treated as one separator. For more control over the split
+	 * use the StrTokenizer class.
+	 * </p>
+	 * <p>
+	 * <p>
+	 * A {@code null} input String returns {@code null}. A {@code null}
+	 * separatorChars splits on whitespace.
+	 * </p>
+	 * <p>
+	 * <pre>
+	 * StringUtils.split(null, *)         = null
+	 * StringUtils.split("", *)           = []
+	 * StringUtils.split("abc def", null) = ["abc", "def"]
+	 * StringUtils.split("abc def", " ")  = ["abc", "def"]
+	 * StringUtils.split("abc  def", " ") = ["abc", "def"]
+	 * StringUtils.split("ab:cd:ef", ":") = ["ab", "cd", "ef"]
+	 * </pre>
+	 *
+	 * @param str            the String to parse, may be null
+	 * @param separatorChars the characters used as the delimiters, {@code null} splits on
+	 *                       whitespace
+	 * @return an array of parsed Strings, {@code null} if null String input
+	 */
+	public static String[] split(final String str, final String separatorChars) {
+		List<String> strings = splitWorker(str, separatorChars, -1, false);
+		return strings.toArray(new String[strings.size()]);
+	}
+
+	/**
+	 * Performs the logic for the {@code split} and
+	 * {@code splitPreserveAllTokens} methods that return a maximum array
+	 * length.
+	 *
+	 * @param str               the String to parse, may be {@code null}
+	 * @param separatorChars    the separate character
+	 * @param max               the maximum number of elements to include in the array. A zero
+	 *                          or negative value implies no limit.
+	 * @param preserveAllTokens if {@code true}, adjacent separators are treated as empty
+	 *                          token separators; if {@code false}, adjacent separators are
+	 *                          treated as one separator.
+	 * @return an array of parsed Strings, {@code null} if null String input
+	 */
+	public static List<String> splitWorker(final String str, final String separatorChars, final int max,
+										   final boolean preserveAllTokens) {
+		// Performance tuned for 2.0 (JDK1.4)
+		// Direct code is quicker than StringTokenizer.
+		// Also, StringTokenizer uses isSpace() not isWhitespace()
+
+		if (str == null) {
+			return null;
+		}
+		final int len = str.length();
+		if (len == 0) {
+			return Collections.emptyList();
+		}
+		final List<String> list = new ArrayList<>();
+		int sizePlus1 = 1;
+		int i = 0, start = 0;
+		boolean match = false;
+		boolean lastMatch = false;
+		if (separatorChars == null) {
+			// Null separator means use whitespace
+			while (i < len) {
+				if (Character.isWhitespace(str.charAt(i))) {
+					if (match || preserveAllTokens) {
+						lastMatch = true;
+						if (sizePlus1++ == max) {
+							i = len;
+							lastMatch = false;
+						}
+						list.add(str.substring(start, i));
+						match = false;
+					}
+					start = ++i;
+					continue;
+				}
+				lastMatch = false;
+				match = true;
+				i++;
+			}
+		} else if (separatorChars.length() == 1) {
+			// Optimise 1 character case
+			final char sep = separatorChars.charAt(0);
+			while (i < len) {
+				if (str.charAt(i) == sep) {
+					if (match || preserveAllTokens) {
+						lastMatch = true;
+						if (sizePlus1++ == max) {
+							i = len;
+							lastMatch = false;
+						}
+						list.add(str.substring(start, i));
+						match = false;
+					}
+					start = ++i;
+					continue;
+				}
+				lastMatch = false;
+				match = true;
+				i++;
+			}
+		} else {
+			// standard case
+			while (i < len) {
+				if (separatorChars.indexOf(str.charAt(i)) >= 0) {
+					if (match || preserveAllTokens) {
+						lastMatch = true;
+						if (sizePlus1++ == max) {
+							i = len;
+							lastMatch = false;
+						}
+						list.add(str.substring(start, i));
+						match = false;
+					}
+					start = ++i;
+					continue;
+				}
+				lastMatch = false;
+				match = true;
+				i++;
+			}
+		}
+		if (match || preserveAllTokens && lastMatch) {
+			list.add(str.substring(start, i));
+		}
+		return list;
+	}
+
+	/**
+	 * <p>
+	 * 是否为CharSequence类型
+	 * </p>
+	 *
+	 * @param cls
+	 * @return
+	 */
+	public static Boolean isCharSequence(Class<?> cls) {
+		return cls != null && CharSequence.class.isAssignableFrom(cls);
+	}
+
+	/**
+	 * <p>
+	 * 去除boolean类型is开头的字符串
+	 * </p>
+	 *
+	 * @param propertyName 字段名
+	 * @param propertyType 字段类型
+	 * @return
+	 */
+	public static String removeIsPrefixIfBoolean(String propertyName, Class<?> propertyType) {
+		if (isBoolean(propertyType) && propertyName.startsWith(IS)) {
+			String property = propertyName.replaceFirst(IS, EMPTY);
+			if (isEmpty(property)) {
+				return propertyName;
+			} else {
+				String firstCharToLowerStr = firstCharToLower(property);
+				return property.equals(firstCharToLowerStr) ? propertyName : firstCharToLowerStr;
+			}
+		}
+		return propertyName;
+	}
+
+	/**
+	 * <p>
+	 * 是否为CharSequence类型
+	 * </p>
+	 *
+	 * @param propertyType
+	 * @return
+	 */
+	public static Boolean isCharSequence(String propertyType) {
+		try {
+			return isCharSequence(Class.forName(propertyType));
+		} catch (ClassNotFoundException e) {
+			return false;
+		}
+	}
+
+	/**
+	 * <p>
+	 * 是否为Boolean类型(包含普通类型)
+	 * </p>
+	 *
+	 * @param propertyCls
+	 * @return
+	 */
+	public static Boolean isBoolean(Class<?> propertyCls) {
+		return propertyCls != null && (boolean.class.isAssignableFrom(propertyCls) || Boolean.class.isAssignableFrom(propertyCls));
+	}
+
+	/**
+	 * <p>
+	 * 第一个首字母小写,之后字符大小写的不变<br>
+	 * StringUtils.firstCharToLower( "UserService" )     = userService
+	 * StringUtils.firstCharToLower( "UserServiceImpl" ) = userServiceImpl
+	 * </p>
+	 *
+	 * @param rawString 需要处理的字符串
+	 * @return
+	 */
+	public static String firstCharToLower(String rawString) {
+		return prefixToLower(rawString, 1);
+	}
+
+	/**
+	 * <p>
+	 * 前n个首字母小写,之后字符大小写的不变
+	 * </p>
+	 *
+	 * @param rawString 需要处理的字符串
+	 * @param index     多少个字符(从左至右)
+	 * @return
+	 */
+	public static String prefixToLower(String rawString, int index) {
+		String beforeChar = rawString.substring(0, index).toLowerCase();
+		String afterChar = rawString.substring(index, rawString.length());
+		return beforeChar + afterChar;
+	}
+
+	/**
+	 * <p>
+	 * 删除字符前缀之后,首字母小写,之后字符大小写的不变<br>
+	 * StringUtils.removePrefixAfterPrefixToLower( "isUser", 2 )     = user
+	 * StringUtils.removePrefixAfterPrefixToLower( "isUserInfo", 2 ) = userInfo
+	 * </p>
+	 *
+	 * @param rawString 需要处理的字符串
+	 * @param index     删除多少个字符(从左至右)
+	 * @return
+	 */
+	public static String removePrefixAfterPrefixToLower(String rawString, int index) {
+		return prefixToLower(rawString.substring(index, rawString.length()), 1);
+	}
+
+	/**
+	 * <p>
+	 * 驼峰转连字符<br>
+	 * StringUtils.camelToHyphen( "managerAdminUserService" ) = manager-admin-user-service
+	 * </p>
+	 *
+	 * @param input
+	 * @return 以'-'分隔
+	 * @see <a href="https://github.com/krasa/StringManipulation">document</a>
+	 */
+	public static String camelToHyphen(String input) {
+		return wordsToHyphenCase(wordsAndHyphenAndCamelToConstantCase(input));
+	}
+
+	private static String wordsAndHyphenAndCamelToConstantCase(String input) {
+		boolean betweenUpperCases = false;
+		boolean containsLowerCase = containsLowerCase(input);
+
+		StringBuilder buf = new StringBuilder();
+		char previousChar = ' ';
+		char[] chars = input.toCharArray();
+		for (int i = 0; i < chars.length; i++) {
+			char c = chars[i];
+			boolean isUpperCaseAndPreviousIsUpperCase = (Character.isUpperCase(previousChar)) && (Character.isUpperCase(c));
+			boolean isUpperCaseAndPreviousIsLowerCase = (Character.isLowerCase(previousChar)) && (Character.isUpperCase(c));
+
+			boolean previousIsWhitespace = Character.isWhitespace(previousChar);
+			boolean lastOneIsNotUnderscore = (buf.length() > 0) && (buf.charAt(buf.length() - 1) != '_');
+			boolean isNotUnderscore = c != '_';
+			if ((lastOneIsNotUnderscore) && ((isUpperCaseAndPreviousIsLowerCase) || (previousIsWhitespace) || ((betweenUpperCases)
+					&& (containsLowerCase) && (isUpperCaseAndPreviousIsUpperCase)))) {
+				buf.append("_");
+			} else if (((separatorAfterDigit) && (Character.isDigit(previousChar))
+					&& (Character.isLetter(c))) || ((separatorBeforeDigit) && (Character
+					.isDigit(c)) && (Character.isLetter(previousChar)))) {
+				buf.append('_');
+			}
+			if ((shouldReplace(c)) && (lastOneIsNotUnderscore)) {
+				buf.append('_');
+			} else if ((!Character.isWhitespace(c)) && ((isNotUnderscore) || (lastOneIsNotUnderscore))) {
+				buf.append(Character.toUpperCase(c));
+			}
+			previousChar = c;
+		}
+		if (Character.isWhitespace(previousChar)) {
+			buf.append("_");
+		}
+		return buf.toString();
+	}
+
+	public static boolean containsLowerCase(String s) {
+		for (char c : s.toCharArray()) {
+			if (Character.isLowerCase(c)) {
+				return true;
+			}
+		}
+		return false;
+	}
+
+	private static boolean shouldReplace(char c) {
+		return (c == '.') || (c == '_') || (c == '-');
+	}
+
+	private static String wordsToHyphenCase(String s) {
+		StringBuilder buf = new StringBuilder();
+		char lastChar = 'a';
+		for (char c : s.toCharArray()) {
+			if ((Character.isWhitespace(lastChar)) && (!Character.isWhitespace(c))
+					&& ('-' != c) && (buf.length() > 0)
+					&& (buf.charAt(buf.length() - 1) != '-')) {
+				buf.append("-");
+			}
+			if ('_' == c) {
+				buf.append('-');
+			} else if ('.' == c) {
+				buf.append('-');
+			} else if (!Character.isWhitespace(c)) {
+				buf.append(Character.toLowerCase(c));
+			}
+			lastChar = c;
+		}
+		if (Character.isWhitespace(lastChar)) {
+			buf.append("-");
+		}
+		return buf.toString();
+	}
 }