diff --git a/botmaker/src/java/com/twitter/botmaker/function/collection/First.java b/botmaker/src/java/com/twitter/botmaker/function/collection/First.java index 5831dd25..1807381e 100644 --- a/botmaker/src/java/com/twitter/botmaker/function/collection/First.java +++ b/botmaker/src/java/com/twitter/botmaker/function/collection/First.java @@ -18,7 +18,7 @@ arguments = {"the list"}, returnType = "T", deprecated = false, - description = "The First element of the list", + description = "Returns the first element of a non-empty list.", actionLevel = ActionLevel.NO_ACTION, examples = { "First(List(1))" @@ -46,6 +46,9 @@ protected CacheLevel getCacheLevel() { @Override public Object apply(Context context, List list) { + if (list.isEmpty()) { + throw new IllegalArgumentException("First() requires a non-empty list"); + } return list.get(0); } } diff --git a/botmaker/src/java/com/twitter/botmaker/function/collection/FirstN.java b/botmaker/src/java/com/twitter/botmaker/function/collection/FirstN.java index a06e9d14..d4094d91 100644 --- a/botmaker/src/java/com/twitter/botmaker/function/collection/FirstN.java +++ b/botmaker/src/java/com/twitter/botmaker/function/collection/FirstN.java @@ -24,7 +24,10 @@ }, returnType = "List", deprecated = false, - description = "Returns the first n values of the list. if n > len(list) return list.", + description = + "Returns the first n values of the list." + + " Returns an empty list if n is negative." + + " Returns the whole list if n is greater than the length of the list.", actionLevel = ActionLevel.NO_ACTION, examples = { "FirstN(List(\"asdf\", \"bas\", \"biz\"), 2)" @@ -54,6 +57,7 @@ protected CacheLevel getCacheLevel() { @Override public Object apply(Context context, List list, Long n) { - return list.subList(0, Math.min(n.intValue(), list.size())); + long itemCount = Math.max(0L, Math.min(n.longValue(), (long) list.size())); + return list.subList(0, (int) itemCount); } } diff --git a/botmaker/src/java/com/twitter/botmaker/function/collection/Slice.java b/botmaker/src/java/com/twitter/botmaker/function/collection/Slice.java index 1d6965aa..89869aae 100644 --- a/botmaker/src/java/com/twitter/botmaker/function/collection/Slice.java +++ b/botmaker/src/java/com/twitter/botmaker/function/collection/Slice.java @@ -28,7 +28,9 @@ }, name = {"Slice", "Substring"}, returnType = "String", - description = "Returns a substring or a sublist", + description = + "Returns a substring or a sublist." + + " Throws an exception if the requested range is invalid.", actionLevel = ActionLevel.NO_ACTION, examples = { "Slice(\"teststringteststring\", 1)", @@ -74,10 +76,14 @@ private static ImmutableList validateType(ImmutableList childr protected Object apply( Context context, Object input, Long beginIndex, Long endIndex) { if (input instanceof String) { - return ((String) input).substring(beginIndex.intValue(), endIndex.intValue()); + String string = (String) input; + validateRange(beginIndex, endIndex, string.length()); + return string.substring(beginIndex.intValue(), endIndex.intValue()); } else if (input instanceof List) { + List list = (List) input; + validateRange(beginIndex, endIndex, list.size()); return Collections.unmodifiableList(new ArrayList<>( - ((List) input).subList(beginIndex.intValue(), endIndex.intValue()))); + list.subList(beginIndex.intValue(), endIndex.intValue()))); } else { throw new IllegalArgumentException(mkErrorMessage(input.getClass())); } @@ -96,6 +102,16 @@ protected Object apply(Context context, Object input, Long beginIndex) } } + private static void validateRange(Long beginIndex, Long endIndex, int inputLength) { + if (beginIndex < 0 || endIndex < beginIndex || endIndex > inputLength) { + throw new IllegalArgumentException(String.format( + "invalid Slice() range [%d, %d) for input of length %d", + beginIndex, + endIndex, + inputLength)); + } + } + private static String mkErrorMessage(Class input) { return String.format( "expecting the input to be of String or List type but received: %s for %s",