diff --git a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java index af1ca45604..b6d9f9049c 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -712,8 +712,7 @@ class ConfigurationClassParser { @Override @Nullable public AnnotationMetadata getImportingClassFor(String importedClass) { - List list = this.imports.get(importedClass); - return (!CollectionUtils.isEmpty(list) ? list.get(list.size() - 1) : null); + return CollectionUtils.lastElement(this.imports.get(importedClass)); } @Override diff --git a/spring-core/src/main/java/org/springframework/core/env/JOptCommandLinePropertySource.java b/spring-core/src/main/java/org/springframework/core/env/JOptCommandLinePropertySource.java index 161802f7fa..ab549733c8 100644 --- a/spring-core/src/main/java/org/springframework/core/env/JOptCommandLinePropertySource.java +++ b/spring-core/src/main/java/org/springframework/core/env/JOptCommandLinePropertySource.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,6 +24,8 @@ import joptsimple.OptionSet; import joptsimple.OptionSpec; import org.springframework.lang.Nullable; +import org.springframework.util.CollectionUtils; +import org.springframework.util.StringUtils; /** * {@link CommandLinePropertySource} implementation backed by a JOpt {@link OptionSet}. @@ -86,13 +88,13 @@ public class JOptCommandLinePropertySource extends CommandLinePropertySource names = new ArrayList<>(); for (OptionSpec spec : this.source.specs()) { - List aliases = new ArrayList<>(spec.options()); - if (!aliases.isEmpty()) { + String lastOption = CollectionUtils.lastElement(spec.options()); + if (lastOption != null) { // Only the longest name is used for enumerating - names.add(aliases.get(aliases.size() - 1)); + names.add(lastOption); } } - return names.toArray(new String[names.size()]); + return StringUtils.toStringArray(names); } @Override diff --git a/spring-core/src/main/java/org/springframework/util/CollectionUtils.java b/spring-core/src/main/java/org/springframework/util/CollectionUtils.java index 1cdfa35b54..8c8efcdd8d 100644 --- a/spring-core/src/main/java/org/springframework/util/CollectionUtils.java +++ b/spring-core/src/main/java/org/springframework/util/CollectionUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -29,6 +29,7 @@ import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Set; +import java.util.SortedSet; import org.springframework.lang.Nullable; @@ -312,6 +313,48 @@ public abstract class CollectionUtils { return candidate; } + /** + * Retrieve the last element of the given Set, using {@link SortedSet#last()} + * or otherwise iterating over all elements (assuming a linked set). + * @param set the Set to check (may be {@code null} or empty) + * @return the last element, or {@code null} if none + * @since 5.0.3 + * @see SortedSet + * @see LinkedHashMap#keySet() + * @see java.util.LinkedHashSet + */ + @Nullable + public static T lastElement(@Nullable Set set) { + if (isEmpty(set)) { + return null; + } + if (set instanceof SortedSet) { + return ((SortedSet) set).last(); + } + + // Full iteration necessary... + Iterator it = set.iterator(); + T last = null; + while (it.hasNext()) { + last = it.next(); + } + return last; + } + + /** + * Retrieve the last element of the given List, accessing the highest index. + * @param list the List to check (may be {@code null} or empty) + * @return the last element, or {@code null} if none + * @since 5.0.3 + */ + @Nullable + public static T lastElement(@Nullable List list) { + if (isEmpty(list)) { + return null; + } + return list.get(list.size() - 1); + } + /** * Marshal the elements from the given enumeration into an array of the given type. * Enumeration elements must be assignable to the type of the given array. The array diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/CodeFlow.java b/spring-expression/src/main/java/org/springframework/expression/spel/CodeFlow.java index 88b8743b6c..e41648e7c5 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/CodeFlow.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/CodeFlow.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,6 +26,7 @@ import org.springframework.asm.ClassWriter; import org.springframework.asm.MethodVisitor; import org.springframework.asm.Opcodes; import org.springframework.lang.Nullable; +import org.springframework.util.CollectionUtils; /** * Manages the class being generated by the compilation process. @@ -153,10 +154,7 @@ public class CodeFlow implements Opcodes { */ @Nullable public String lastDescriptor() { - if (this.compilationScopes.peek().isEmpty()) { - return null; - } - return this.compilationScopes.peek().get(this.compilationScopes.peek().size() - 1); + return CollectionUtils.lastElement(this.compilationScopes.peek()); } /** diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/Selection.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/Selection.java index 3e2c9b20d5..38aeb1f562 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/Selection.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/Selection.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2016 the original author or authors. + * Copyright 2002-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -31,6 +31,7 @@ import org.springframework.expression.spel.SpelEvaluationException; import org.springframework.expression.spel.SpelMessage; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; +import org.springframework.util.CollectionUtils; import org.springframework.util.ObjectUtils; /** @@ -160,7 +161,7 @@ public class Selection extends SpelNodeImpl { } if (this.variant == LAST) { - return new ValueRef.TypedValueHolderValueRef(new TypedValue(result.get(result.size() - 1)), this); + return new ValueRef.TypedValueHolderValueRef(new TypedValue(CollectionUtils.lastElement(result)), this); } if (operand instanceof Iterable) { diff --git a/spring-web/src/main/java/org/springframework/web/method/annotation/ErrorsMethodArgumentResolver.java b/spring-web/src/main/java/org/springframework/web/method/annotation/ErrorsMethodArgumentResolver.java index cdf13c70ac..38d7d0f1b8 100644 --- a/spring-web/src/main/java/org/springframework/web/method/annotation/ErrorsMethodArgumentResolver.java +++ b/spring-web/src/main/java/org/springframework/web/method/annotation/ErrorsMethodArgumentResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2016 the original author or authors. + * Copyright 2002-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,12 +16,11 @@ package org.springframework.web.method.annotation; -import java.util.ArrayList; - import org.springframework.core.MethodParameter; import org.springframework.lang.Nullable; import org.springframework.ui.ModelMap; import org.springframework.util.Assert; +import org.springframework.util.CollectionUtils; import org.springframework.validation.BindingResult; import org.springframework.validation.Errors; import org.springframework.web.bind.support.WebDataBinderFactory; @@ -38,6 +37,7 @@ import org.springframework.web.method.support.ModelAndViewContainer; * {@link BindingResult}. * * @author Rossen Stoyanchev + * @author Juergen Hoeller * @since 3.1 */ public class ErrorsMethodArgumentResolver implements HandlerMethodArgumentResolver { @@ -58,18 +58,15 @@ public class ErrorsMethodArgumentResolver implements HandlerMethodArgumentResolv "Errors/BindingResult argument only supported on regular handler methods"); ModelMap model = mavContainer.getModel(); - if (model.size() > 0) { - int lastIndex = model.size()-1; - String lastKey = new ArrayList<>(model.keySet()).get(lastIndex); - if (lastKey.startsWith(BindingResult.MODEL_KEY_PREFIX)) { - return model.get(lastKey); - } + String lastKey = CollectionUtils.lastElement(model.keySet()); + if (lastKey != null && lastKey.startsWith(BindingResult.MODEL_KEY_PREFIX)) { + return model.get(lastKey); } throw new IllegalStateException( "An Errors/BindingResult argument is expected to be declared immediately after " + - "the model attribute, the @RequestBody or the @RequestPart arguments " + - "to which they apply: " + parameter.getMethod()); + "the model attribute, the @RequestBody or the @RequestPart arguments " + + "to which they apply: " + parameter.getMethod()); } } diff --git a/spring-web/src/test/java/org/springframework/web/method/annotation/ErrorsMethodHandlerArgumentResolverTests.java b/spring-web/src/test/java/org/springframework/web/method/annotation/ErrorsMethodArgumentResolverTests.java similarity index 93% rename from spring-web/src/test/java/org/springframework/web/method/annotation/ErrorsMethodHandlerArgumentResolverTests.java rename to spring-web/src/test/java/org/springframework/web/method/annotation/ErrorsMethodArgumentResolverTests.java index af6bcb4328..9916d0a5d4 100644 --- a/spring-web/src/test/java/org/springframework/web/method/annotation/ErrorsMethodHandlerArgumentResolverTests.java +++ b/spring-web/src/test/java/org/springframework/web/method/annotation/ErrorsMethodArgumentResolverTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -35,7 +35,7 @@ import static org.junit.Assert.*; * * @author Rossen Stoyanchev */ -public class ErrorsMethodHandlerArgumentResolverTests { +public class ErrorsMethodArgumentResolverTests { private final ErrorsMethodArgumentResolver resolver = new ErrorsMethodArgumentResolver(); @@ -45,15 +45,17 @@ public class ErrorsMethodHandlerArgumentResolverTests { private NativeWebRequest webRequest; + @Before - public void setUp() throws Exception { + public void setup() throws Exception { paramErrors = new MethodParameter(getClass().getDeclaredMethod("handle", Errors.class), 0); bindingResult = new WebDataBinder(new Object(), "attr").getBindingResult(); webRequest = new ServletWebRequest(new MockHttpServletRequest()); } + @Test - public void supports() throws Exception { + public void supports() { resolver.supportsParameter(paramErrors); } @@ -68,7 +70,6 @@ public class ErrorsMethodHandlerArgumentResolverTests { mavContainer.addAllAttributes(bindingResult.getModel()); Object actual = resolver.resolveArgument(paramErrors, mavContainer, webRequest, null); - assertSame(actual, bindingResult); } @@ -86,8 +87,9 @@ public class ErrorsMethodHandlerArgumentResolverTests { resolver.resolveArgument(paramErrors, new ModelAndViewContainer(), webRequest, null); } + @SuppressWarnings("unused") private void handle(Errors errors) { } -} \ No newline at end of file +}