From 2ba8d97ad07073ffc7ab8a4b722bf324d1ed6c21 Mon Sep 17 00:00:00 2001 From: Lukasz Lenart Date: Sun, 19 Jan 2025 09:36:27 +0100 Subject: [PATCH] WW-5512 Supports mixing required and optional parameters --- .../xwork2/inject/ContainerImpl.java | 16 ++++- .../xwork2/inject/ContainerImplTest.java | 59 +++++++++++++++++++ 2 files changed, 73 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/com/opensymphony/xwork2/inject/ContainerImpl.java b/core/src/main/java/com/opensymphony/xwork2/inject/ContainerImpl.java index 1596077ab..5539bbc70 100644 --- a/core/src/main/java/com/opensymphony/xwork2/inject/ContainerImpl.java +++ b/core/src/main/java/com/opensymphony/xwork2/inject/ContainerImpl.java @@ -236,7 +236,13 @@ class ContainerImpl implements Container { Inject annotation = findInject(annotationsIterator.next()); String name = annotation == null ? defaultName : annotation.value(); Key key = Key.newInstance(parameterType, name); - parameterInjectors.add(createParameterInjector(key, member)); + try { + parameterInjectors.add(createParameterInjector(key, member)); + } catch (MissingDependencyException e) { + if (annotation != null && annotation.required()) { + throw e; + } + } } return toArray(parameterInjectors); @@ -419,8 +425,14 @@ class ContainerImpl implements Container { // First time through... constructionContext.startConstruction(); try { - if (constructor.getParameterCount() > 0 && parameterInjectors == null) { + if (constructor.getParameterCount() == 0) { + t = constructor.newInstance(); + } else if (constructor.getParameterCount() > 0 && parameterInjectors == null) { t = constructor.newInstance(new Object[constructor.getParameterCount()]); + } else if (constructor.getParameterCount() > parameterInjectors.length) { + final Object[] parameters = getParameters(constructor, context, parameterInjectors); + final Object[] finalParameters = Arrays.copyOf(parameters, constructor.getParameterCount()); + t = constructor.newInstance(finalParameters); } else { final Object[] parameters = getParameters(constructor, context, parameterInjectors); t = constructor.newInstance(parameters); diff --git a/core/src/test/java/com/opensymphony/xwork2/inject/ContainerImplTest.java b/core/src/test/java/com/opensymphony/xwork2/inject/ContainerImplTest.java index d3e479d19..1136117d8 100644 --- a/core/src/test/java/com/opensymphony/xwork2/inject/ContainerImplTest.java +++ b/core/src/test/java/com/opensymphony/xwork2/inject/ContainerImplTest.java @@ -28,6 +28,7 @@ import java.util.concurrent.Callable; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotSame; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertThrows; @@ -92,6 +93,20 @@ public class ContainerImplTest { assertNull(constructorCheck.getName()); } + @Test + public void requiredOptionalConstructorInjector() { + RequiredOptionalConstructorCheck constructorCheck = c.inject(RequiredOptionalConstructorCheck.class); + assertNotNull(constructorCheck.getExistingName()); + assertNull(constructorCheck.getNonExitingName()); + } + + @Test + public void optionalRequiredConstructorInjector() { + OptionalRequiredConstructorCheck constructorCheck = c.inject(OptionalRequiredConstructorCheck.class); + assertNull(constructorCheck.getNonExitingName()); + assertNotNull(constructorCheck.getExistingName()); + } + /** * Inject values into field under SecurityManager */ @@ -244,6 +259,50 @@ public class ContainerImplTest { } } + public static class RequiredOptionalConstructorCheck { + private final String existingName; + private final String nonExitingName; + + @Inject(required = false) + public RequiredOptionalConstructorCheck( + @Inject("constructorCheck.name") String existingName, + @Inject(value = "nonExistingConstant", required = false) String nonExitingName + ) { + this.existingName = existingName; + this.nonExitingName = nonExitingName; + } + + public String getExistingName() { + return existingName; + } + + public String getNonExitingName() { + return nonExitingName; + } + } + + public static class OptionalRequiredConstructorCheck { + private final String existingName; + private final String nonExitingName; + + @Inject(required = false) + public OptionalRequiredConstructorCheck( + @Inject(value = "nonExistingConstant", required = false) String nonExitingName, + @Inject("constructorCheck.name") String existingName + ) { + this.existingName = existingName; + this.nonExitingName = nonExitingName; + } + + public String getExistingName() { + return existingName; + } + + public String getNonExitingName() { + return nonExitingName; + } + } + class InitializableCheck { private Initializable initializable;