Skip to content

Commit 06e6386

Browse files
committed
CollectionUtils.lastElement for common Set/List extraction
Issue: SPR-16374
1 parent 13a8f90 commit 06e6386

File tree

7 files changed

+75
-33
lines changed

7 files changed

+75
-33
lines changed

spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2017 the original author or authors.
2+
* Copyright 2002-2018 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -712,8 +712,7 @@ public void registerImport(AnnotationMetadata importingClass, String importedCla
712712
@Override
713713
@Nullable
714714
public AnnotationMetadata getImportingClassFor(String importedClass) {
715-
List<AnnotationMetadata> list = this.imports.get(importedClass);
716-
return (!CollectionUtils.isEmpty(list) ? list.get(list.size() - 1) : null);
715+
return CollectionUtils.lastElement(this.imports.get(importedClass));
717716
}
718717

719718
@Override

spring-core/src/main/java/org/springframework/core/env/JOptCommandLinePropertySource.java

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2017 the original author or authors.
2+
* Copyright 2002-2018 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -24,6 +24,8 @@
2424
import joptsimple.OptionSpec;
2525

2626
import org.springframework.lang.Nullable;
27+
import org.springframework.util.CollectionUtils;
28+
import org.springframework.util.StringUtils;
2729

2830
/**
2931
* {@link CommandLinePropertySource} implementation backed by a JOpt {@link OptionSet}.
@@ -86,13 +88,13 @@ protected boolean containsOption(String name) {
8688
public String[] getPropertyNames() {
8789
List<String> names = new ArrayList<>();
8890
for (OptionSpec<?> spec : this.source.specs()) {
89-
List<String> aliases = new ArrayList<>(spec.options());
90-
if (!aliases.isEmpty()) {
91+
String lastOption = CollectionUtils.lastElement(spec.options());
92+
if (lastOption != null) {
9193
// Only the longest name is used for enumerating
92-
names.add(aliases.get(aliases.size() - 1));
94+
names.add(lastOption);
9395
}
9496
}
95-
return names.toArray(new String[names.size()]);
97+
return StringUtils.toStringArray(names);
9698
}
9799

98100
@Override

spring-core/src/main/java/org/springframework/util/CollectionUtils.java

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2017 the original author or authors.
2+
* Copyright 2002-2018 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -29,6 +29,7 @@
2929
import java.util.Map;
3030
import java.util.Properties;
3131
import java.util.Set;
32+
import java.util.SortedSet;
3233

3334
import org.springframework.lang.Nullable;
3435

@@ -312,6 +313,48 @@ else if (candidate != val.getClass()) {
312313
return candidate;
313314
}
314315

316+
/**
317+
* Retrieve the last element of the given Set, using {@link SortedSet#last()}
318+
* or otherwise iterating over all elements (assuming a linked set).
319+
* @param set the Set to check (may be {@code null} or empty)
320+
* @return the last element, or {@code null} if none
321+
* @since 5.0.3
322+
* @see SortedSet
323+
* @see LinkedHashMap#keySet()
324+
* @see java.util.LinkedHashSet
325+
*/
326+
@Nullable
327+
public static <T> T lastElement(@Nullable Set<T> set) {
328+
if (isEmpty(set)) {
329+
return null;
330+
}
331+
if (set instanceof SortedSet) {
332+
return ((SortedSet<T>) set).last();
333+
}
334+
335+
// Full iteration necessary...
336+
Iterator<T> it = set.iterator();
337+
T last = null;
338+
while (it.hasNext()) {
339+
last = it.next();
340+
}
341+
return last;
342+
}
343+
344+
/**
345+
* Retrieve the last element of the given List, accessing the highest index.
346+
* @param list the List to check (may be {@code null} or empty)
347+
* @return the last element, or {@code null} if none
348+
* @since 5.0.3
349+
*/
350+
@Nullable
351+
public static <T> T lastElement(@Nullable List<T> list) {
352+
if (isEmpty(list)) {
353+
return null;
354+
}
355+
return list.get(list.size() - 1);
356+
}
357+
315358
/**
316359
* Marshal the elements from the given enumeration into an array of the given type.
317360
* Enumeration elements must be assignable to the type of the given array. The array

spring-expression/src/main/java/org/springframework/expression/spel/CodeFlow.java

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2017 the original author or authors.
2+
* Copyright 2002-2018 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -26,6 +26,7 @@
2626
import org.springframework.asm.MethodVisitor;
2727
import org.springframework.asm.Opcodes;
2828
import org.springframework.lang.Nullable;
29+
import org.springframework.util.CollectionUtils;
2930

3031
/**
3132
* Manages the class being generated by the compilation process.
@@ -153,10 +154,7 @@ public void exitCompilationScope() {
153154
*/
154155
@Nullable
155156
public String lastDescriptor() {
156-
if (this.compilationScopes.peek().isEmpty()) {
157-
return null;
158-
}
159-
return this.compilationScopes.peek().get(this.compilationScopes.peek().size() - 1);
157+
return CollectionUtils.lastElement(this.compilationScopes.peek());
160158
}
161159

162160
/**

spring-expression/src/main/java/org/springframework/expression/spel/ast/Selection.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2016 the original author or authors.
2+
* Copyright 2002-2018 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -31,6 +31,7 @@
3131
import org.springframework.expression.spel.SpelMessage;
3232
import org.springframework.util.Assert;
3333
import org.springframework.util.ClassUtils;
34+
import org.springframework.util.CollectionUtils;
3435
import org.springframework.util.ObjectUtils;
3536

3637
/**
@@ -160,7 +161,7 @@ protected ValueRef getValueRef(ExpressionState state) throws EvaluationException
160161
}
161162

162163
if (this.variant == LAST) {
163-
return new ValueRef.TypedValueHolderValueRef(new TypedValue(result.get(result.size() - 1)), this);
164+
return new ValueRef.TypedValueHolderValueRef(new TypedValue(CollectionUtils.lastElement(result)), this);
164165
}
165166

166167
if (operand instanceof Iterable) {

spring-web/src/main/java/org/springframework/web/method/annotation/ErrorsMethodArgumentResolver.java

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2016 the original author or authors.
2+
* Copyright 2002-2018 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -16,12 +16,11 @@
1616

1717
package org.springframework.web.method.annotation;
1818

19-
import java.util.ArrayList;
20-
2119
import org.springframework.core.MethodParameter;
2220
import org.springframework.lang.Nullable;
2321
import org.springframework.ui.ModelMap;
2422
import org.springframework.util.Assert;
23+
import org.springframework.util.CollectionUtils;
2524
import org.springframework.validation.BindingResult;
2625
import org.springframework.validation.Errors;
2726
import org.springframework.web.bind.support.WebDataBinderFactory;
@@ -38,6 +37,7 @@
3837
* {@link BindingResult}.
3938
*
4039
* @author Rossen Stoyanchev
40+
* @author Juergen Hoeller
4141
* @since 3.1
4242
*/
4343
public class ErrorsMethodArgumentResolver implements HandlerMethodArgumentResolver {
@@ -58,18 +58,15 @@ public Object resolveArgument(MethodParameter parameter,
5858
"Errors/BindingResult argument only supported on regular handler methods");
5959

6060
ModelMap model = mavContainer.getModel();
61-
if (model.size() > 0) {
62-
int lastIndex = model.size()-1;
63-
String lastKey = new ArrayList<>(model.keySet()).get(lastIndex);
64-
if (lastKey.startsWith(BindingResult.MODEL_KEY_PREFIX)) {
65-
return model.get(lastKey);
66-
}
61+
String lastKey = CollectionUtils.lastElement(model.keySet());
62+
if (lastKey != null && lastKey.startsWith(BindingResult.MODEL_KEY_PREFIX)) {
63+
return model.get(lastKey);
6764
}
6865

6966
throw new IllegalStateException(
7067
"An Errors/BindingResult argument is expected to be declared immediately after " +
71-
"the model attribute, the @RequestBody or the @RequestPart arguments " +
72-
"to which they apply: " + parameter.getMethod());
68+
"the model attribute, the @RequestBody or the @RequestPart arguments " +
69+
"to which they apply: " + parameter.getMethod());
7370
}
7471

7572
}
Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2012 the original author or authors.
2+
* Copyright 2002-2018 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -35,7 +35,7 @@
3535
*
3636
* @author Rossen Stoyanchev
3737
*/
38-
public class ErrorsMethodHandlerArgumentResolverTests {
38+
public class ErrorsMethodArgumentResolverTests {
3939

4040
private final ErrorsMethodArgumentResolver resolver = new ErrorsMethodArgumentResolver();
4141

@@ -45,15 +45,17 @@ public class ErrorsMethodHandlerArgumentResolverTests {
4545

4646
private NativeWebRequest webRequest;
4747

48+
4849
@Before
49-
public void setUp() throws Exception {
50+
public void setup() throws Exception {
5051
paramErrors = new MethodParameter(getClass().getDeclaredMethod("handle", Errors.class), 0);
5152
bindingResult = new WebDataBinder(new Object(), "attr").getBindingResult();
5253
webRequest = new ServletWebRequest(new MockHttpServletRequest());
5354
}
5455

56+
5557
@Test
56-
public void supports() throws Exception {
58+
public void supports() {
5759
resolver.supportsParameter(paramErrors);
5860
}
5961

@@ -68,7 +70,6 @@ public void bindingResult() throws Exception {
6870
mavContainer.addAllAttributes(bindingResult.getModel());
6971

7072
Object actual = resolver.resolveArgument(paramErrors, mavContainer, webRequest, null);
71-
7273
assertSame(actual, bindingResult);
7374
}
7475

@@ -86,8 +87,9 @@ public void noBindingResult() throws Exception {
8687
resolver.resolveArgument(paramErrors, new ModelAndViewContainer(), webRequest, null);
8788
}
8889

90+
8991
@SuppressWarnings("unused")
9092
private void handle(Errors errors) {
9193
}
9294

93-
}
95+
}

0 commit comments

Comments
 (0)