diff --git a/hutool-core/src/main/java/cn/hutool/core/bean/BeanPath.java b/hutool-core/src/main/java/cn/hutool/core/bean/BeanPath.java index 026b7e2d7..3b8bf45e4 100644 --- a/hutool-core/src/main/java/cn/hutool/core/bean/BeanPath.java +++ b/hutool-core/src/main/java/cn/hutool/core/bean/BeanPath.java @@ -4,6 +4,7 @@ import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.ListUtil; import cn.hutool.core.convert.Convert; import cn.hutool.core.map.MapUtil; +import cn.hutool.core.math.NumberUtil; import cn.hutool.core.text.StrUtil; import cn.hutool.core.util.ArrayUtil; import cn.hutool.core.util.CharUtil; @@ -22,7 +23,7 @@ import java.util.Map; *
  • .表达式,可以获取Bean对象中的属性(字段)值或者Map中key对应的值
  • *
  • []表达式,可以获取集合等对象中对应index的值
  • * - * + *

    * 表达式栗子: * *

    @@ -36,11 +37,13 @@ import java.util.Map;
      * @author Looly
      * @since 4.0.6
      */
    -public class BeanPath implements Serializable{
    +public class BeanPath implements Serializable {
     	private static final long serialVersionUID = 1L;
     
    -	/** 表达式边界符号数组 */
    -	private static final char[] EXP_CHARS = { CharUtil.DOT, CharUtil.BRACKET_START, CharUtil.BRACKET_END };
    +	/**
    +	 * 表达式边界符号数组
    +	 */
    +	private static final char[] EXP_CHARS = {CharUtil.DOT, CharUtil.BRACKET_START, CharUtil.BRACKET_END};
     
     	private boolean isStartWith = false;
     	protected List patternParts;
    @@ -53,7 +56,7 @@ public class BeanPath implements Serializable{
     	 * 
  • .表达式,可以获取Bean对象中的属性(字段)值或者Map中key对应的值
  • *
  • []表达式,可以获取集合等对象中对应index的值
  • * - * + *

    * 表达式栗子: * *

    @@ -85,7 +88,7 @@ public class BeanPath implements Serializable{
     	 *
     	 * @return 表达式分段列表
     	 */
    -	public List getPatternParts(){
    +	public List getPatternParts() {
     		return this.patternParts;
     	}
     
    @@ -109,11 +112,11 @@ public class BeanPath implements Serializable{
     	 * 2. 如果为数组,如果下标不大于数组长度,则替换原有值,否则追加值
     	 * 
    * - * @param bean Bean、Map或List + * @param bean Bean、Map或List * @param value 值 */ public void set(final Object bean, final Object value) { - set(bean, this.patternParts, value); + set(bean, this.patternParts, lastIsNumber(this.patternParts), value); } @Override @@ -122,6 +125,7 @@ public class BeanPath implements Serializable{ } //region Private Methods + /** * 设置表达式指定位置(或filed对应)的值
    * 若表达式指向一个List则设置其坐标对应位置的值,若指向Map则put对应key的值,Bean则设置字段的值
    @@ -132,26 +136,47 @@ public class BeanPath implements Serializable{ * 2. 如果为数组,如果下标不大于数组长度,则替换原有值,否则追加值 *
    * - * @param bean Bean、Map或List + * @param bean Bean、Map或List * @param patternParts 表达式块列表 - * @param value 值 + * @param value 值 */ - private void set(final Object bean, final List patternParts, final Object value) { - Object subBean = get(patternParts, bean, true); - if(null == subBean) { - set(bean, patternParts.subList(0, patternParts.size() - 1), new HashMap<>()); + private void set(final Object bean, final List patternParts, final boolean nextNumberPart, final Object value) { + Object subBean = this.get(patternParts, bean, true); + if (null == subBean) { + final List parentParts = getParentParts(patternParts); + this.set(bean, parentParts, lastIsNumber(parentParts), nextNumberPart ? new ArrayList<>() : new HashMap<>()); //set中有可能做过转换,因此此处重新获取bean - subBean = get(patternParts, bean, true); + subBean = this.get(patternParts, bean, true); } BeanUtil.setFieldValue(subBean, patternParts.get(patternParts.size() - 1), value); } + /** + * 判断path列表中末尾的标记是否为数字 + * + * @param patternParts path列表 + * @return 是否为数字 + */ + private static boolean lastIsNumber(List patternParts) { + return NumberUtil.isInteger(patternParts.get(patternParts.size() - 1)); + } + + /** + * 获取父级路径列表 + * + * @param patternParts 路径列表 + * @return 父级路径列表 + */ + private static List getParentParts(List patternParts) { + return patternParts.subList(0, patternParts.size() - 1); + } + /** * 获取Bean中对应表达式的值 * * @param patternParts 表达式分段列表 - * @param bean Bean对象或Map或List等 - * @param ignoreLast 是否忽略最后一个值,忽略最后一个值则用于set,否则用于read + * @param bean Bean对象或Map或List等 + * @param ignoreLast 是否忽略最后一个值,忽略最后一个值则用于set,否则用于read * @return 值,如果对应值不存在,则返回null */ private Object get(final List patternParts, final Object bean, final boolean ignoreLast) { @@ -247,7 +272,7 @@ public class BeanPath implements Serializable{ continue; } - if('\'' == c){ + if ('\'' == c) { // 结束 isInWrap = (false == isInWrap); continue; diff --git a/hutool-core/src/main/java/cn/hutool/core/bean/BeanUtil.java b/hutool-core/src/main/java/cn/hutool/core/bean/BeanUtil.java index 459f85a1f..67cb068f7 100755 --- a/hutool-core/src/main/java/cn/hutool/core/bean/BeanUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/bean/BeanUtil.java @@ -319,7 +319,7 @@ public class BeanUtil { if (bean instanceof Map) { ((Map) bean).put(fieldNameOrIndex, value); } else if (bean instanceof List) { - ListUtil.setOrAppend((List) bean, Convert.toInt(fieldNameOrIndex), value); + ListUtil.setOrPadding((List) bean, Convert.toInt(fieldNameOrIndex), value); } else if (ArrayUtil.isArray(bean)) { ArrayUtil.setOrAppend(bean, Convert.toInt(fieldNameOrIndex), value); } else { diff --git a/hutool-core/src/main/java/cn/hutool/core/collection/ListUtil.java b/hutool-core/src/main/java/cn/hutool/core/collection/ListUtil.java index 6402dc06e..7b7223a2e 100755 --- a/hutool-core/src/main/java/cn/hutool/core/collection/ListUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/collection/ListUtil.java @@ -7,6 +7,7 @@ import cn.hutool.core.collection.partition.RandomAccessAvgPartition; import cn.hutool.core.collection.partition.RandomAccessPartition; import cn.hutool.core.comparator.PinyinComparator; import cn.hutool.core.comparator.PropertyComparator; +import cn.hutool.core.lang.Assert; import cn.hutool.core.util.ArrayUtil; import cn.hutool.core.util.ObjUtil; import cn.hutool.core.util.PageUtil; @@ -396,6 +397,45 @@ public class ListUtil { return list; } + /** + * 在指定位置设置元素。当index小于List的长度时,替换指定位置的值,否则追加{@code null}直到到达index后,设置值 + * + * @param 元素类型 + * @param list List列表 + * @param index 位置 + * @param element 新元素 + * @return 原List + * @since 5.8.4 + */ + public static List setOrPadding(final List list, final int index, final T element) { + return setOrPadding(list, index, element, null); + } + + /** + * 在指定位置设置元素。当index小于List的长度时,替换指定位置的值,否则追加{@code paddingElement}直到到达index后,设置值 + * + * @param 元素类型 + * @param list List列表 + * @param index 位置 + * @param element 新元素 + * @param paddingElement 填充的值 + * @return 原List + * @since 5。8.4 + */ + public static List setOrPadding(final List list, final int index, final T element, final T paddingElement) { + Assert.notNull(list, "List must be not null !"); + final int size = list.size(); + if (index < size) { + list.set(index, element); + } else { + for (int i = size; i < index; i++) { + list.add(paddingElement); + } + list.add(element); + } + return list; + } + /** * 截取集合的部分 * diff --git a/hutool-core/src/main/java/cn/hutool/core/date/chinese/LunarInfo.java b/hutool-core/src/main/java/cn/hutool/core/date/chinese/LunarInfo.java index a9cfe70cf..d1eb8bef7 100644 --- a/hutool-core/src/main/java/cn/hutool/core/date/chinese/LunarInfo.java +++ b/hutool-core/src/main/java/cn/hutool/core/date/chinese/LunarInfo.java @@ -20,7 +20,7 @@ public class LunarInfo { public static final long BASE_DAY = LocalDate.of(BASE_YEAR, 1, 31).toEpochDay(); /** - * 此表来自:https://github.com/jjonline/calendar.js/blob/master/calendar.js + * 此表来自:https://github.com/jjonline/calendar.js/blob/master/calendar.js * 农历表示: * 1. 表示当年有无闰年,有的话,为闰月的月份,没有的话,为0。 * 2-4.为除了闰月外的正常月份是大月还是小月,1为30天,0为29天。 @@ -61,8 +61,9 @@ public class LunarInfo { public static int yearDays(final int y) { int i, sum = 348; for (i = 0x8000; i > 0x8; i >>= 1) { - if ((getCode(y) & i) != 0) + if ((getCode(y) & i) != 0) { sum += 1; + } } return (sum + leapDays(y)); } diff --git a/hutool-core/src/test/java/cn/hutool/core/bean/BeanPathTest.java b/hutool-core/src/test/java/cn/hutool/core/bean/BeanPathTest.java index 25c1a89f6..7802f5cd0 100644 --- a/hutool-core/src/test/java/cn/hutool/core/bean/BeanPathTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/bean/BeanPathTest.java @@ -110,7 +110,7 @@ public class BeanPathTest { @Test public void getKeyWithDotTest () { - Map dataMap = new HashMap<>(16); + final Map dataMap = new HashMap<>(16); dataMap.put("aa", "value0"); dataMap.put("aa.bb.cc", "value111111");// key 是类名 格式 带 ' . ' @@ -125,4 +125,23 @@ public class BeanPathTest { Assert.assertEquals("ee", of.getPatternParts().get(1)); Assert.assertEquals("ff.", of.getPatternParts().get(2)); } + + @Test + public void issue2362Test() { + final Map map = new HashMap<>(); + + BeanPath beanPath = BeanPath.of("list[0].name"); + beanPath.set(map, "张三"); + Assert.assertEquals("{list=[{name=张三}]}", map.toString()); + + map.clear(); + beanPath = BeanPath.of("list[1].name"); + beanPath.set(map, "张三"); + Assert.assertEquals("{list=[null, {name=张三}]}", map.toString()); + + map.clear(); + beanPath = BeanPath.of("list[0].1.name"); + beanPath.set(map, "张三"); + Assert.assertEquals("{list=[[null, {name=张三}]]}", map.toString()); + } } diff --git a/hutool-core/src/test/java/cn/hutool/core/collection/ListUtilTest.java b/hutool-core/src/test/java/cn/hutool/core/collection/ListUtilTest.java index ac1c2b1fd..65c57ef62 100644 --- a/hutool-core/src/test/java/cn/hutool/core/collection/ListUtilTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/collection/ListUtilTest.java @@ -98,56 +98,56 @@ public class ListUtilTest { public void indexOfAll() { final List a = ListUtil.ofLinked("1", "2", "3", "4", "3", "2", "1"); final int[] indexArray = CollUtil.indexOfAll(a, "2"::equals); - Assert.assertArrayEquals(new int[]{1,5}, indexArray); + Assert.assertArrayEquals(new int[]{1, 5}, indexArray); final int[] indexArray2 = CollUtil.indexOfAll(a, "1"::equals); - Assert.assertArrayEquals(new int[]{0,6}, indexArray2); + Assert.assertArrayEquals(new int[]{0, 6}, indexArray2); } @Test public void pageTest() { - final List a = ListUtil.ofLinked(1, 2, 3,4,5); + final List a = ListUtil.ofLinked(1, 2, 3, 4, 5); PageUtil.setFirstPageNo(1); - final int[] a_1 = ListUtil.page(1,2,a).stream().mapToInt(Integer::valueOf).toArray(); - final int[] a1 = ListUtil.page(1,2,a).stream().mapToInt(Integer::valueOf).toArray(); - final int[] a2 = ListUtil.page(2,2,a).stream().mapToInt(Integer::valueOf).toArray(); - final int[] a3 = ListUtil.page(3,2,a).stream().mapToInt(Integer::valueOf).toArray(); - final int[] a4 = ListUtil.page(4,2,a).stream().mapToInt(Integer::valueOf).toArray(); - Assert.assertArrayEquals(new int[]{1,2},a_1); - Assert.assertArrayEquals(new int[]{1,2},a1); - Assert.assertArrayEquals(new int[]{3,4},a2); - Assert.assertArrayEquals(new int[]{5},a3); - Assert.assertArrayEquals(new int[]{},a4); + final int[] a_1 = ListUtil.page(1, 2, a).stream().mapToInt(Integer::valueOf).toArray(); + final int[] a1 = ListUtil.page(1, 2, a).stream().mapToInt(Integer::valueOf).toArray(); + final int[] a2 = ListUtil.page(2, 2, a).stream().mapToInt(Integer::valueOf).toArray(); + final int[] a3 = ListUtil.page(3, 2, a).stream().mapToInt(Integer::valueOf).toArray(); + final int[] a4 = ListUtil.page(4, 2, a).stream().mapToInt(Integer::valueOf).toArray(); + Assert.assertArrayEquals(new int[]{1, 2}, a_1); + Assert.assertArrayEquals(new int[]{1, 2}, a1); + Assert.assertArrayEquals(new int[]{3, 4}, a2); + Assert.assertArrayEquals(new int[]{5}, a3); + Assert.assertArrayEquals(new int[]{}, a4); PageUtil.setFirstPageNo(2); - final int[] b_1 = ListUtil.page(1,2,a).stream().mapToInt(Integer::valueOf).toArray(); - final int[] b1 = ListUtil.page(2,2,a).stream().mapToInt(Integer::valueOf).toArray(); - final int[] b2 = ListUtil.page(3,2,a).stream().mapToInt(Integer::valueOf).toArray(); - final int[] b3 = ListUtil.page(4,2,a).stream().mapToInt(Integer::valueOf).toArray(); - final int[] b4 = ListUtil.page(5,2,a).stream().mapToInt(Integer::valueOf).toArray(); - Assert.assertArrayEquals(new int[]{1,2},b_1); - Assert.assertArrayEquals(new int[]{1,2},b1); - Assert.assertArrayEquals(new int[]{3,4},b2); - Assert.assertArrayEquals(new int[]{5},b3); - Assert.assertArrayEquals(new int[]{},b4); + final int[] b_1 = ListUtil.page(1, 2, a).stream().mapToInt(Integer::valueOf).toArray(); + final int[] b1 = ListUtil.page(2, 2, a).stream().mapToInt(Integer::valueOf).toArray(); + final int[] b2 = ListUtil.page(3, 2, a).stream().mapToInt(Integer::valueOf).toArray(); + final int[] b3 = ListUtil.page(4, 2, a).stream().mapToInt(Integer::valueOf).toArray(); + final int[] b4 = ListUtil.page(5, 2, a).stream().mapToInt(Integer::valueOf).toArray(); + Assert.assertArrayEquals(new int[]{1, 2}, b_1); + Assert.assertArrayEquals(new int[]{1, 2}, b1); + Assert.assertArrayEquals(new int[]{3, 4}, b2); + Assert.assertArrayEquals(new int[]{5}, b3); + Assert.assertArrayEquals(new int[]{}, b4); PageUtil.setFirstPageNo(0); - final int[] c_1 = ListUtil.page(-1,2,a).stream().mapToInt(Integer::valueOf).toArray(); - final int[] c1 = ListUtil.page(0,2,a).stream().mapToInt(Integer::valueOf).toArray(); - final int[] c2 = ListUtil.page(1,2,a).stream().mapToInt(Integer::valueOf).toArray(); - final int[] c3 = ListUtil.page(2,2,a).stream().mapToInt(Integer::valueOf).toArray(); - final int[] c4 = ListUtil.page(3,2,a).stream().mapToInt(Integer::valueOf).toArray(); - Assert.assertArrayEquals(new int[]{1,2},c_1); - Assert.assertArrayEquals(new int[]{1,2},c1); - Assert.assertArrayEquals(new int[]{3,4},c2); - Assert.assertArrayEquals(new int[]{5},c3); - Assert.assertArrayEquals(new int[]{},c4); + final int[] c_1 = ListUtil.page(-1, 2, a).stream().mapToInt(Integer::valueOf).toArray(); + final int[] c1 = ListUtil.page(0, 2, a).stream().mapToInt(Integer::valueOf).toArray(); + final int[] c2 = ListUtil.page(1, 2, a).stream().mapToInt(Integer::valueOf).toArray(); + final int[] c3 = ListUtil.page(2, 2, a).stream().mapToInt(Integer::valueOf).toArray(); + final int[] c4 = ListUtil.page(3, 2, a).stream().mapToInt(Integer::valueOf).toArray(); + Assert.assertArrayEquals(new int[]{1, 2}, c_1); + Assert.assertArrayEquals(new int[]{1, 2}, c1); + Assert.assertArrayEquals(new int[]{3, 4}, c2); + Assert.assertArrayEquals(new int[]{5}, c3); + Assert.assertArrayEquals(new int[]{}, c4); PageUtil.setFirstPageNo(1); - final int[] d1 = ListUtil.page(0,8,a).stream().mapToInt(Integer::valueOf).toArray(); - Assert.assertArrayEquals(new int[]{1,2,3,4,5},d1); + final int[] d1 = ListUtil.page(0, 8, a).stream().mapToInt(Integer::valueOf).toArray(); + Assert.assertArrayEquals(new int[]{1, 2, 3, 4, 5}, d1); // page with consumer final List> pageListData = new ArrayList<>(); @@ -198,7 +198,7 @@ public class ListUtilTest { new TestBean(5, "test5"), new TestBean(4, "test4"), new TestBean(3, "test3") - ); + ); final List order = ListUtil.sortByProperty(beanList, "order"); Assert.assertEquals("test1", order.get(0).getName()); @@ -248,4 +248,22 @@ public class ListUtilTest { Assert.assertEquals("2", list1.get(1)); Assert.assertEquals("3", list1.get(2)); } + + @Test + public void setOrPaddingNullTest() { + final List list = new ArrayList<>(); + list.add("1"); + + // 替换原值 + ListUtil.setOrPadding(list, 0, "a"); + Assert.assertEquals("[a]", list.toString()); + + //append值 + ListUtil.setOrPadding(list, 1, "a"); + Assert.assertEquals("[a, a]", list.toString()); + + // padding null 后加入值 + ListUtil.setOrPadding(list, 3, "a"); + Assert.assertEquals(4, list.size()); + } }