diff --git a/feilong-context/src/main/java/com/feilong/context/converter/StringToBeanConverter.java b/feilong-core/src/main/java/com/feilong/context/converter/StringToBeanConverter.java old mode 100755 new mode 100644 similarity index 95% rename from feilong-context/src/main/java/com/feilong/context/converter/StringToBeanConverter.java rename to feilong-core/src/main/java/com/feilong/context/converter/StringToBeanConverter.java index d49d37ef7..9748e2b1f --- a/feilong-context/src/main/java/com/feilong/context/converter/StringToBeanConverter.java +++ b/feilong-core/src/main/java/com/feilong/context/converter/StringToBeanConverter.java @@ -1,45 +1,47 @@ -/* - * Copyright (C) 2008 feilong - * - * 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 - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * 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.feilong.context.converter; - -/** - * 将字符串转成 bean 的转换器. - * - *

- * 功能和{@link org.apache.commons.collections4.Transformer} 类似,但是更专业和具体 - *

- * - * @author feilong - * @param - * the generic type - * @see org.apache.commons.collections4.Transformer - * @see "org.springframework.core.convert.converter.Converter" - * @see "org.springframework.core.convert.support.StringToEnumConverterFactory" - * @since 1.8.3 - * @since 1.11.2 rename from AbstractParse - */ -public interface StringToBeanConverter { - - /** - * 转换. - * - * @param value - * the value - * @return 如果 value 是null或者empty,返回 null
- */ - T convert(String value); - -} +/* + * Copyright (C) 2008 feilong + * + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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.feilong.context.converter; + +/** + * 将字符串转成 bean 的转换器. + * + *

+ * 功能和{@link org.apache.commons.collections4.Transformer} 类似,但是更专业和具体 + *

+ * + * @author feilong + * @param + * the generic type + * @see org.apache.commons.collections4.Transformer + * @see "org.springframework.core.convert.converter.Converter" + * @see "org.springframework.core.convert.support.StringToEnumConverterFactory" + * @since 1.8.3 + * @since 1.11.2 rename from AbstractParse + * @since 4.1.2 move from feilong-context + */ +@FunctionalInterface +public interface StringToBeanConverter { + + /** + * 转换. + * + * @param value + * the value + * @return 如果 value 是null或者empty,返回 null
+ */ + T convert(String value); + +} diff --git a/feilong-core/src/main/java/com/feilong/core/lang/StringUtil.java b/feilong-core/src/main/java/com/feilong/core/lang/StringUtil.java index 7549acbfc..79fd44e55 100644 --- a/feilong-core/src/main/java/com/feilong/core/lang/StringUtil.java +++ b/feilong-core/src/main/java/com/feilong/core/lang/StringUtil.java @@ -41,6 +41,7 @@ import org.slf4j.helpers.FormattingTuple; import org.slf4j.helpers.MessageFormatter; +import com.feilong.context.converter.StringToBeanConverter; import com.feilong.core.CharsetType; import com.feilong.core.Validate; import com.feilong.core.bean.ConvertUtil; @@ -1172,6 +1173,8 @@ public static boolean tokenizeToArrayContains(String config,String delimiter return false; } + //--------------------------------------------------------------- + /** * 将 189=988;200=455;这种格式的字符串转换成map , map的key 是189这种, value 是988,455 这种等号后面的值,使用逗号分隔成list. * @@ -1195,15 +1198,15 @@ public static boolean tokenizeToArrayContains(String config,String delimiter * * * - * @param config + * @param configString * 189=988;200=455; 这种格式的配置字符串, 用分号分隔一组, 等号分隔key 和value - * @return 如果 config 是null,返回 emptyMap()
- * 如果 config 是empty,返回 emptyMap()
+ * @return 如果 configString 是null,返回 emptyMap()
+ * 如果 configString 是empty,返回 emptyMap()
* @since 3.3.4 * @apiNote 自动去除空格,忽略空 */ - public static Map toSingleValueMap(String config){ - return toSingleValueMap(config, String.class, String.class); + public static Map toSingleValueMap(String configString){ + return toSingleValueMap(configString, String.class, String.class); } /** @@ -1232,50 +1235,130 @@ public static Map toSingleValueMap(String config){ * the generic type * @param * the value type - * @param config + * @param configString * 189=988;200=455; 这种格式的配置字符串, 用分号分隔一组, 等号分隔key 和value * @param keyClass * key转换的类型,比如上面的背景中, 189 可以转换成String Long Integer * @param valueElementClass * value转换的类型,比如上面的背景中, 455 可以转换成String Long Integer - * @return 如果 config 是null,返回 emptyMap()
- * 如果 config 是empty,返回 emptyMap()
+ * @return 如果 configString 是null,返回 emptyMap()
+ * 如果 configString 是empty,返回 emptyMap()
* 如果 keyClass 是null,抛出 {@link NullPointerException}
* 如果 valueElementClass 是null,抛出 {@link NullPointerException}
* @since 3.3.4 * @apiNote 自动去除空格,忽略空 */ - public static Map toSingleValueMap(String config,Class keyClass,Class valueElementClass){ + public static Map toSingleValueMap(String configString,Class keyClass,Class valueElementClass){ + return toMap(configString, keyClass, (valueString) -> { + return convert(valueString, valueElementClass); + }); + } - if (isNullOrEmpty(config)){ + /** + * 将 73034693=0-50;11487680=0-43;51099626=0-50; 这种格式的字符串转换成map. + * + *

+ * map的key 是73034693这种, value 可以TrackQueryExtendParam对象 + * + *

+     * public class TrackQueryExtendParam{
+     * 
+     *     // 单集编号 最小. 
+     *     private Integer minEpisodeNumber;
+     * 
+     *     // 单集编号 最大. 
+     *     private Integer maxEpisodeNumber;
+     * 
+     *     //setter/getter 省略
+     * }
+     * 
+ *

+ * + *

解析代码示例:

+ * + *
+ * + *
+     * 
+     * String configString = "73034693=0-50;\n"//
+     *                 + "11487680=0-43;\n"//
+     *                 + "51099626=0-50;";
+     * 
+     * Map{@code } map = StringUtil.toMap(configString, Long.class, (valueString) -> {
+     *                                                   String[] albumQueryParamsArray = StringUtil.tokenizeToStringArray(valueString, "-");
+     *                                                   if (isNullOrEmpty(albumQueryParamsArray)){
+     *                                                       return null;
+     *                                                   }
+     *                                                   try{
+     *                                                       //只有1个 
+     *                                                       if (1 == size(albumQueryParamsArray)){
+     *                                                           //默认 0-x
+     *                                                           Integer max = toInteger(albumQueryParamsArray[0]);
+     *                                                           return new TrackQueryExtendParam(0, max);
+     *                                                       }
+     * 
+     *                                                       Integer min = toInteger(albumQueryParamsArray[0]);
+     *                                                       Integer max = toInteger(albumQueryParamsArray[1]);
+     *                                                       return new TrackQueryExtendParam(min, max);
+     * 
+     *                                                   }catch (Exception e){
+     *                                                       return null;
+     *                                                   }
+     *                                               });
+     * 
+     * 
+ * + *
+ * + * @param + * the generic type + * @param + * the value type + * @param configString + * 73034693=0-50;11487680=0-43;51099626=0-50; 这种格式的配置字符串, 用分号分隔一组, 等号分隔key 和value + * @param keyClass + * key转换的类型,比如上面的背景中, 73034693 可以转换成String Long Integer + * @param valueStringToBeanConverter + * value转换的类型 + * @return 如果 configString 是null,返回 emptyMap()
+ * 如果 configString 是empty,返回 emptyMap()
+ * 如果 keyClass 是null,抛出 {@link NullPointerException}
+ * 如果 stringToBeanConverter 是null,抛出 {@link NullPointerException}
+ * @since 4.1.2 + * @apiNote 自动去除空格,忽略空 + */ + public static Map toMap(String configString,Class keyClass,StringToBeanConverter valueStringToBeanConverter){ + if (isNullOrEmpty(configString)){ return emptyMap(); } - Validate.notNull(keyClass, "keyClass can't be null!"); - Validate.notNull(valueElementClass, "valueElementClass can't be null!"); + //entry之间的分隔符 + String entryDelimiters = ";"; + //key 和value之间的分隔符 + String keyAndValueDelimiters = "="; - //--------------------------------------------------------------- - - String[] entrys = StringUtil.tokenizeToStringArray(config, ";"); + String[] entrys = StringUtil.tokenizeToStringArray(configString, entryDelimiters); if (isNullOrEmpty(entrys)){ return emptyMap(); } + //--------------------------------------------------------------- + Validate.notNull(keyClass, "keyClass can't be null!"); + Validate.notNull(valueStringToBeanConverter, "stringToBeanConverter can't be null!"); + //--------------------------------------------------------------- Map map = newHashMap(); for (String keyAndValueString : entrys){ if (isNullOrEmpty(keyAndValueString)){ continue; } - - String[] keyAndValues = StringUtil.tokenizeToStringArray(keyAndValueString, "="); + String[] keyAndValues = StringUtil.tokenizeToStringArray(keyAndValueString, keyAndValueDelimiters); if (size(keyAndValues) != 2){ continue; } - //--------------------------------------------------------------- - map.put( - convert(keyAndValues[0], keyClass), // - convert(keyAndValues[1], valueElementClass)); + T key = ConvertUtil.convert(keyAndValues[0], keyClass); + V value = valueStringToBeanConverter.convert(keyAndValues[1]); + map.put(key, value); } return map; } @@ -1306,55 +1389,24 @@ public static Map toSingleValueMap(String config,Class keyClass, * the generic type * @param * the value type - * @param config + * @param configString * 189=988,900;200=455; 这种格式的配置字符串, 用分号分隔一组, 等号分隔key 和value * @param keyClass * key转换的类型,比如上面的背景中, 189 可以转换成String Long Integer * @param valueElementClass * value转换的类型,比如上面的背景中, 455 可以转换成String Long Integer - * @return 如果 config 是null,返回 emptyMap()
- * 如果 config 是empty,返回 emptyMap()
+ * @return 如果 configString 是null,返回 emptyMap()
+ * 如果 configString 是empty,返回 emptyMap()
* 如果 keyClass 是null,抛出 {@link NullPointerException}
* 如果 valueElementClass 是null,抛出 {@link NullPointerException}
* @since 3.3.4 * @apiNote 自动去除空格,忽略空 */ - public static Map> toMultiValueMap(String config,Class keyClass,Class valueElementClass){ - if (isNullOrEmpty(config)){ - return emptyMap(); - } - - Validate.notNull(keyClass, "keyClass can't be null!"); - Validate.notNull(valueElementClass, "valueElementClass can't be null!"); - - //--------------------------------------------------------------- - String[] entrys = tokenizeToStringArray(config, ";"); - if (isNullOrEmpty(entrys)){ - return emptyMap(); - } - - //--------------------------------------------------------------- - - Map> map = newHashMap(); - for (String entry : entrys){ - if (isNullOrEmpty(entry)){ - continue; - } - - String[] keyAndValuesMapping = tokenizeToStringArray(entry, "="); - if (size(keyAndValuesMapping) != 2){ - continue; - } - - //--------------------------------------------------------------- - T key = convert(keyAndValuesMapping[0], keyClass); - - String[] values = StringUtil.tokenizeToStringArray(keyAndValuesMapping[1], ","); - List valueList = toList(toArray(values, valueElementClass)); - - map.put(key, valueList); - } - return map; + public static Map> toMultiValueMap(String configString,Class keyClass,Class valueElementClass){ + return toMap(configString, keyClass, (valueString) -> { + String[] values = StringUtil.tokenizeToStringArray(valueString, ","); + return toList(toArray(values, valueElementClass)); + }); } // [start]format diff --git a/feilong-core/src/test/java/com/feilong/core/lang/stringutil/StringUtilSuiteTests.java b/feilong-core/src/test/java/com/feilong/core/lang/stringutil/StringUtilSuiteTests.java index a1601fafb..de9624f1d 100644 --- a/feilong-core/src/test/java/com/feilong/core/lang/stringutil/StringUtilSuiteTests.java +++ b/feilong-core/src/test/java/com/feilong/core/lang/stringutil/StringUtilSuiteTests.java @@ -61,6 +61,7 @@ ToSingleValueMapTest.class, ToSingleValueMapConvertTest.class, ToMultiValueMapTest.class, + ToMapConvertTest.class, // }) public class StringUtilSuiteTests{ diff --git a/feilong-core/src/test/java/com/feilong/core/lang/stringutil/ToMapConvertTest.java b/feilong-core/src/test/java/com/feilong/core/lang/stringutil/ToMapConvertTest.java new file mode 100644 index 000000000..f3ae81565 --- /dev/null +++ b/feilong-core/src/test/java/com/feilong/core/lang/stringutil/ToMapConvertTest.java @@ -0,0 +1,141 @@ +/* + * Copyright (C) 2008 feilong + * + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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.feilong.core.lang.stringutil; + +import static com.feilong.core.Validator.isNullOrEmpty; +import static com.feilong.core.bean.ConvertUtil.toInteger; +import static com.feilong.core.bean.ConvertUtil.toLong; +import static com.feilong.core.util.CollectionsUtil.size; +import static java.util.Collections.emptyMap; +import static org.hamcrest.Matchers.allOf; +import static org.hamcrest.Matchers.hasEntry; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThat; + +import java.util.Map; + +import org.junit.Test; + +import com.feilong.context.converter.StringToBeanConverter; +import com.feilong.core.lang.StringUtil; + +public class ToMapConvertTest{ + + StringToBeanConverter valueStringToBeanConverter = (value) -> { + return value; + }; + + @Test + public void toMap(){ + assertEquals(emptyMap(), StringUtil.toMap("", String.class, null)); + assertEquals(emptyMap(), StringUtil.toMap(" ", String.class, null)); + assertEquals(emptyMap(), StringUtil.toMap(null, String.class, null)); + } + + //--------------------------------------------------------------- + + @Test(expected = NullPointerException.class) + public void toMap1(){ + StringUtil.toMap("1=2", null, null); + } + + @Test(expected = NullPointerException.class) + public void toMap12(){ + StringUtil.toMap("1=2", String.class, (StringToBeanConverter) null); + } + + //--------------------------------------------------------------- + + @Test + public void toMap123(){ + Map map = StringUtil.toMap("1=2", String.class, valueStringToBeanConverter); + assertThat(map, allOf(hasEntry("1", "2"))); + } + + @Test + public void toMap1223(){ + Map map = StringUtil.toMap("1=2;", String.class, valueStringToBeanConverter); + assertThat(map, allOf(hasEntry("1", "2"))); + } + + @Test + public void toMap1333223(){ + Map map = StringUtil.toMap("1=2;", Long.class, (value) -> { + return toLong(value); + }); + + assertThat(map, allOf(hasEntry(1L, 2L))); + } + + @Test + public void toMap1333223Integer(){ + Map map = StringUtil.toMap("1=2;", Integer.class, (value) -> { + return toInteger(value); + }); + + assertThat(map, allOf(hasEntry(1, 2))); + } + + @Test + public void toMap1333223Integer222(){ + Map map = StringUtil.toMap("1=2;89=100;200=230", Integer.class, (value) -> { + return toInteger(value); + }); + + assertThat( + map, + allOf(// + hasEntry(1, 2), + hasEntry(89, 100), + hasEntry(200, 230))); + } + + @Test + public void toMap222(){ + String configString = "73034693=0-50;\n"// + + "11487680=0-43;\n"// + + "51099626=0-50;"; + + Map map = StringUtil.toMap(configString, Long.class, (valueString) -> { + String[] albumQueryParamsArray = StringUtil.tokenizeToStringArray(valueString, "-"); + if (isNullOrEmpty(albumQueryParamsArray)){ + return null; + } + try{ + //只有1个 + if (1 == size(albumQueryParamsArray)){ + //默认 0-x + Integer max = toInteger(albumQueryParamsArray[0]); + return new TrackQueryExtendParam(0, max); + } + + Integer min = toInteger(albumQueryParamsArray[0]); + Integer max = toInteger(albumQueryParamsArray[1]); + return new TrackQueryExtendParam(min, max); + + }catch (Exception e){ + return null; + } + }); + + assertThat( + map, + allOf(// + hasEntry(73034693L, new TrackQueryExtendParam(0, 50)), + hasEntry(11487680L, new TrackQueryExtendParam(0, 43)), + hasEntry(51099626L, new TrackQueryExtendParam(0, 50)))); + } +} \ No newline at end of file diff --git a/feilong-core/src/test/java/com/feilong/core/lang/stringutil/ToSingleValueMapConvertTest.java b/feilong-core/src/test/java/com/feilong/core/lang/stringutil/ToSingleValueMapConvertTest.java index 6c5b04deb..014ea4fab 100644 --- a/feilong-core/src/test/java/com/feilong/core/lang/stringutil/ToSingleValueMapConvertTest.java +++ b/feilong-core/src/test/java/com/feilong/core/lang/stringutil/ToSingleValueMapConvertTest.java @@ -45,7 +45,7 @@ public void toSingleValueMap1(){ @Test(expected = NullPointerException.class) public void toSingleValueMap12(){ - StringUtil.toSingleValueMap("1=2", String.class, null); + StringUtil.toSingleValueMap("1=2", String.class, (Class) null); } //--------------------------------------------------------------- diff --git a/feilong-core/src/test/java/com/feilong/core/lang/stringutil/TrackQueryExtendParam.java b/feilong-core/src/test/java/com/feilong/core/lang/stringutil/TrackQueryExtendParam.java new file mode 100644 index 000000000..a77355b30 --- /dev/null +++ b/feilong-core/src/test/java/com/feilong/core/lang/stringutil/TrackQueryExtendParam.java @@ -0,0 +1,115 @@ +package com.feilong.core.lang.stringutil; + +import com.feilong.lib.lang3.builder.EqualsBuilder; +import com.feilong.lib.lang3.builder.HashCodeBuilder; +import com.feilong.lib.lang3.builder.ToStringBuilder; +import com.feilong.lib.lang3.builder.ToStringStyle; + +/** + * 查询参数. + */ +public class TrackQueryExtendParam{ + + /** 单集编号 最小. */ + private Integer minEpisodeNumber; + + /** 单集编号 最大. */ + private Integer maxEpisodeNumber; + + //--------------------------------------------------------------- + + /** + * + */ + public TrackQueryExtendParam(){ + super(); + } + + /** + * @param minEpisodeNumber + * @param maxEpisodeNumber + */ + public TrackQueryExtendParam(Integer minEpisodeNumber, Integer maxEpisodeNumber){ + super(); + this.minEpisodeNumber = minEpisodeNumber; + this.maxEpisodeNumber = maxEpisodeNumber; + } + + //--------------------------------------------------------------- + + /** + * To string. + * + * @return the string + */ + /* + * (non-Javadoc) + * + * @see java.lang.Object#toString() + */ + @Override + public String toString(){ + return ToStringBuilder.reflectionToString(this, ToStringStyle.DEFAULT_STYLE); + } + + /** + * Gets the 单集编号 最小. + * + * @return the 单集编号 最小 + */ + public Integer getMinEpisodeNumber(){ + return minEpisodeNumber; + } + + /** + * Sets the 单集编号 最小. + * + * @param minEpisodeNumber + * the new 单集编号 最小 + */ + public void setMinEpisodeNumber(Integer minEpisodeNumber){ + this.minEpisodeNumber = minEpisodeNumber; + } + + /** + * Gets the 单集编号 最大. + * + * @return the 单集编号 最大 + */ + public Integer getMaxEpisodeNumber(){ + return maxEpisodeNumber; + } + + /** + * Sets the 单集编号 最大. + * + * @param maxEpisodeNumber + * the new 单集编号 最大 + */ + public void setMaxEpisodeNumber(Integer maxEpisodeNumber){ + this.maxEpisodeNumber = maxEpisodeNumber; + } + + //--------------------------------------------------------------- + + /* + * (non-Javadoc) + * + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode(){ + return HashCodeBuilder.reflectionHashCode(this); + } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object obj){ + return EqualsBuilder.reflectionEquals(this, obj); + } + +} \ No newline at end of file