Add explicit aliases for exclude and excludeName on SpringBootApplication
Previously, SpringBootApplication relied on implicity aliasing of exclude and excludeName that worked because the two attributes have the same names as the equivalent attributes on EnableAutoConfiguration. This commit updates SpringBootApplication to make the aliases explicit and also adds tests to EnableAutoConfigurationImportSelectorTests to verify that the aliasing is working as intended. Closes gh-7951
This commit is contained in:
parent
c41ff17dd7
commit
f93d4a0cbf
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2012-2016 the original author or authors.
|
* Copyright 2012-2017 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -58,6 +58,7 @@ public @interface SpringBootApplication {
|
||||||
* Exclude specific auto-configuration classes such that they will never be applied.
|
* Exclude specific auto-configuration classes such that they will never be applied.
|
||||||
* @return the classes to exclude
|
* @return the classes to exclude
|
||||||
*/
|
*/
|
||||||
|
@AliasFor(annotation = EnableAutoConfiguration.class, attribute = "exclude")
|
||||||
Class<?>[] exclude() default {};
|
Class<?>[] exclude() default {};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -66,6 +67,7 @@ public @interface SpringBootApplication {
|
||||||
* @return the class names to exclude
|
* @return the class names to exclude
|
||||||
* @since 1.3.0
|
* @since 1.3.0
|
||||||
*/
|
*/
|
||||||
|
@AliasFor(annotation = EnableAutoConfiguration.class, attribute = "excludeName")
|
||||||
String[] excludeName() default {};
|
String[] excludeName() default {};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -22,7 +22,6 @@ import org.junit.Before;
|
||||||
import org.junit.Rule;
|
import org.junit.Rule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.rules.ExpectedException;
|
import org.junit.rules.ExpectedException;
|
||||||
import org.mockito.Mock;
|
|
||||||
import org.mockito.MockitoAnnotations;
|
import org.mockito.MockitoAnnotations;
|
||||||
|
|
||||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||||
|
@ -32,15 +31,12 @@ import org.springframework.boot.autoconfigure.freemarker.FreeMarkerAutoConfigura
|
||||||
import org.springframework.boot.autoconfigure.mustache.MustacheAutoConfiguration;
|
import org.springframework.boot.autoconfigure.mustache.MustacheAutoConfiguration;
|
||||||
import org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration;
|
import org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.core.annotation.AnnotationAttributes;
|
|
||||||
import org.springframework.core.io.DefaultResourceLoader;
|
import org.springframework.core.io.DefaultResourceLoader;
|
||||||
import org.springframework.core.io.support.SpringFactoriesLoader;
|
import org.springframework.core.io.support.SpringFactoriesLoader;
|
||||||
import org.springframework.core.type.AnnotationMetadata;
|
import org.springframework.core.type.StandardAnnotationMetadata;
|
||||||
import org.springframework.mock.env.MockEnvironment;
|
import org.springframework.mock.env.MockEnvironment;
|
||||||
import org.springframework.util.StringUtils;
|
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
import static org.mockito.BDDMockito.given;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests for {@link EnableAutoConfigurationImportSelector}
|
* Tests for {@link EnableAutoConfigurationImportSelector}
|
||||||
|
@ -57,12 +53,6 @@ public class EnableAutoConfigurationImportSelectorTests {
|
||||||
|
|
||||||
private final MockEnvironment environment = new MockEnvironment();
|
private final MockEnvironment environment = new MockEnvironment();
|
||||||
|
|
||||||
@Mock
|
|
||||||
private AnnotationMetadata annotationMetadata;
|
|
||||||
|
|
||||||
@Mock
|
|
||||||
private AnnotationAttributes annotationAttributes;
|
|
||||||
|
|
||||||
@Rule
|
@Rule
|
||||||
public ExpectedException expected = ExpectedException.none();
|
public ExpectedException expected = ExpectedException.none();
|
||||||
|
|
||||||
|
@ -75,9 +65,8 @@ public class EnableAutoConfigurationImportSelectorTests {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void importsAreSelected() {
|
public void importsAreSelectedWhenUsingEnableAutoConfiguration() {
|
||||||
configureExclusions(new String[0], new String[0], new String[0]);
|
String[] imports = selectImports(BasicEnableAutoConfiguration.class);
|
||||||
String[] imports = this.importSelector.selectImports(this.annotationMetadata);
|
|
||||||
assertThat(imports).hasSameSizeAs(SpringFactoriesLoader.loadFactoryNames(
|
assertThat(imports).hasSameSizeAs(SpringFactoriesLoader.loadFactoryNames(
|
||||||
EnableAutoConfiguration.class, getClass().getClassLoader()));
|
EnableAutoConfiguration.class, getClass().getClassLoader()));
|
||||||
assertThat(ConditionEvaluationReport.get(this.beanFactory).getExclusions())
|
assertThat(ConditionEvaluationReport.get(this.beanFactory).getExclusions())
|
||||||
|
@ -86,9 +75,16 @@ public class EnableAutoConfigurationImportSelectorTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void classExclusionsAreApplied() {
|
public void classExclusionsAreApplied() {
|
||||||
configureExclusions(new String[] { FreeMarkerAutoConfiguration.class.getName() },
|
String[] imports = selectImports(
|
||||||
new String[0], new String[0]);
|
EnableAutoConfigurationWithClassExclusions.class);
|
||||||
String[] imports = this.importSelector.selectImports(this.annotationMetadata);
|
assertThat(imports).hasSize(getAutoConfigurationClassNames().size() - 1);
|
||||||
|
assertThat(ConditionEvaluationReport.get(this.beanFactory).getExclusions())
|
||||||
|
.contains(FreeMarkerAutoConfiguration.class.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void classExclusionsAreAppliedWhenUsingSpringBootApplication() {
|
||||||
|
String[] imports = selectImports(SpringBootApplicationWithClassExclusions.class);
|
||||||
assertThat(imports).hasSize(getAutoConfigurationClassNames().size() - 1);
|
assertThat(imports).hasSize(getAutoConfigurationClassNames().size() - 1);
|
||||||
assertThat(ConditionEvaluationReport.get(this.beanFactory).getExclusions())
|
assertThat(ConditionEvaluationReport.get(this.beanFactory).getExclusions())
|
||||||
.contains(FreeMarkerAutoConfiguration.class.getName());
|
.contains(FreeMarkerAutoConfiguration.class.getName());
|
||||||
|
@ -96,10 +92,17 @@ public class EnableAutoConfigurationImportSelectorTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void classNamesExclusionsAreApplied() {
|
public void classNamesExclusionsAreApplied() {
|
||||||
configureExclusions(new String[0],
|
String[] imports = selectImports(
|
||||||
new String[] { MustacheAutoConfiguration.class.getName() },
|
EnableAutoConfigurationWithClassNameExclusions.class);
|
||||||
new String[0]);
|
assertThat(imports).hasSize(getAutoConfigurationClassNames().size() - 1);
|
||||||
String[] imports = this.importSelector.selectImports(this.annotationMetadata);
|
assertThat(ConditionEvaluationReport.get(this.beanFactory).getExclusions())
|
||||||
|
.contains(MustacheAutoConfiguration.class.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void classNamesExclusionsAreAppliedWhenUsingSpringBootApplication() {
|
||||||
|
String[] imports = selectImports(
|
||||||
|
SpringBootApplicationWithClassNameExclusions.class);
|
||||||
assertThat(imports).hasSize(getAutoConfigurationClassNames().size() - 1);
|
assertThat(imports).hasSize(getAutoConfigurationClassNames().size() - 1);
|
||||||
assertThat(ConditionEvaluationReport.get(this.beanFactory).getExclusions())
|
assertThat(ConditionEvaluationReport.get(this.beanFactory).getExclusions())
|
||||||
.contains(MustacheAutoConfiguration.class.getName());
|
.contains(MustacheAutoConfiguration.class.getName());
|
||||||
|
@ -107,9 +110,9 @@ public class EnableAutoConfigurationImportSelectorTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void propertyExclusionsAreApplied() {
|
public void propertyExclusionsAreApplied() {
|
||||||
configureExclusions(new String[0], new String[0],
|
this.environment.setProperty("spring.autoconfigure.exclude",
|
||||||
new String[] { FreeMarkerAutoConfiguration.class.getName() });
|
FreeMarkerAutoConfiguration.class.getName());
|
||||||
String[] imports = this.importSelector.selectImports(this.annotationMetadata);
|
String[] imports = selectImports(BasicEnableAutoConfiguration.class);
|
||||||
assertThat(imports).hasSize(getAutoConfigurationClassNames().size() - 1);
|
assertThat(imports).hasSize(getAutoConfigurationClassNames().size() - 1);
|
||||||
assertThat(ConditionEvaluationReport.get(this.beanFactory).getExclusions())
|
assertThat(ConditionEvaluationReport.get(this.beanFactory).getExclusions())
|
||||||
.contains(FreeMarkerAutoConfiguration.class.getName());
|
.contains(FreeMarkerAutoConfiguration.class.getName());
|
||||||
|
@ -117,10 +120,10 @@ public class EnableAutoConfigurationImportSelectorTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void severalPropertyExclusionsAreApplied() {
|
public void severalPropertyExclusionsAreApplied() {
|
||||||
configureExclusions(new String[0], new String[0],
|
this.environment.setProperty("spring.autoconfigure.exclude",
|
||||||
new String[] { FreeMarkerAutoConfiguration.class.getName(),
|
FreeMarkerAutoConfiguration.class.getName() + ","
|
||||||
MustacheAutoConfiguration.class.getName() });
|
+ MustacheAutoConfiguration.class.getName());
|
||||||
String[] imports = this.importSelector.selectImports(this.annotationMetadata);
|
String[] imports = selectImports(BasicEnableAutoConfiguration.class);
|
||||||
assertThat(imports).hasSize(getAutoConfigurationClassNames().size() - 2);
|
assertThat(imports).hasSize(getAutoConfigurationClassNames().size() - 2);
|
||||||
assertThat(ConditionEvaluationReport.get(this.beanFactory).getExclusions())
|
assertThat(ConditionEvaluationReport.get(this.beanFactory).getExclusions())
|
||||||
.contains(FreeMarkerAutoConfiguration.class.getName(),
|
.contains(FreeMarkerAutoConfiguration.class.getName(),
|
||||||
|
@ -129,12 +132,11 @@ public class EnableAutoConfigurationImportSelectorTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void severalPropertyYamlExclusionsAreApplied() {
|
public void severalPropertyYamlExclusionsAreApplied() {
|
||||||
configureExclusions(new String[0], new String[0], new String[0]);
|
|
||||||
this.environment.setProperty("spring.autoconfigure.exclude[0]",
|
this.environment.setProperty("spring.autoconfigure.exclude[0]",
|
||||||
FreeMarkerAutoConfiguration.class.getName());
|
FreeMarkerAutoConfiguration.class.getName());
|
||||||
this.environment.setProperty("spring.autoconfigure.exclude[1]",
|
this.environment.setProperty("spring.autoconfigure.exclude[1]",
|
||||||
MustacheAutoConfiguration.class.getName());
|
MustacheAutoConfiguration.class.getName());
|
||||||
String[] imports = this.importSelector.selectImports(this.annotationMetadata);
|
String[] imports = selectImports(BasicEnableAutoConfiguration.class);
|
||||||
assertThat(imports).hasSize(getAutoConfigurationClassNames().size() - 2);
|
assertThat(imports).hasSize(getAutoConfigurationClassNames().size() - 2);
|
||||||
assertThat(ConditionEvaluationReport.get(this.beanFactory).getExclusions())
|
assertThat(ConditionEvaluationReport.get(this.beanFactory).getExclusions())
|
||||||
.contains(FreeMarkerAutoConfiguration.class.getName(),
|
.contains(FreeMarkerAutoConfiguration.class.getName(),
|
||||||
|
@ -143,10 +145,10 @@ public class EnableAutoConfigurationImportSelectorTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void combinedExclusionsAreApplied() {
|
public void combinedExclusionsAreApplied() {
|
||||||
configureExclusions(new String[] { MustacheAutoConfiguration.class.getName() },
|
this.environment.setProperty("spring.autoconfigure.exclude",
|
||||||
new String[] { FreeMarkerAutoConfiguration.class.getName() },
|
ThymeleafAutoConfiguration.class.getName());
|
||||||
new String[] { ThymeleafAutoConfiguration.class.getName() });
|
String[] imports = selectImports(
|
||||||
String[] imports = this.importSelector.selectImports(this.annotationMetadata);
|
EnableAutoConfigurationWithClassAndClassNameExclusions.class);
|
||||||
assertThat(imports).hasSize(getAutoConfigurationClassNames().size() - 3);
|
assertThat(imports).hasSize(getAutoConfigurationClassNames().size() - 3);
|
||||||
assertThat(ConditionEvaluationReport.get(this.beanFactory).getExclusions())
|
assertThat(ConditionEvaluationReport.get(this.beanFactory).getExclusions())
|
||||||
.contains(FreeMarkerAutoConfiguration.class.getName(),
|
.contains(FreeMarkerAutoConfiguration.class.getName(),
|
||||||
|
@ -156,79 +158,58 @@ public class EnableAutoConfigurationImportSelectorTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void propertyOverrideSetToTrue() throws Exception {
|
public void propertyOverrideSetToTrue() throws Exception {
|
||||||
configureExclusions(new String[0], new String[0], new String[0]);
|
|
||||||
this.environment.setProperty(EnableAutoConfiguration.ENABLED_OVERRIDE_PROPERTY,
|
this.environment.setProperty(EnableAutoConfiguration.ENABLED_OVERRIDE_PROPERTY,
|
||||||
"true");
|
"true");
|
||||||
String[] imports = this.importSelector.selectImports(this.annotationMetadata);
|
String[] imports = selectImports(BasicEnableAutoConfiguration.class);
|
||||||
assertThat(imports).isNotEmpty();
|
assertThat(imports).isNotEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void propertyOverrideSetToFalse() throws Exception {
|
public void propertyOverrideSetToFalse() throws Exception {
|
||||||
configureExclusions(new String[0], new String[0], new String[0]);
|
|
||||||
this.environment.setProperty(EnableAutoConfiguration.ENABLED_OVERRIDE_PROPERTY,
|
this.environment.setProperty(EnableAutoConfiguration.ENABLED_OVERRIDE_PROPERTY,
|
||||||
"false");
|
"false");
|
||||||
String[] imports = this.importSelector.selectImports(this.annotationMetadata);
|
String[] imports = selectImports(BasicEnableAutoConfiguration.class);
|
||||||
assertThat(imports).isEmpty();
|
assertThat(imports).isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void nonAutoConfigurationClassExclusionsShouldThrowException()
|
public void nonAutoConfigurationClassExclusionsShouldThrowException()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
configureExclusions(new String[] { TestConfiguration.class.getName() },
|
|
||||||
new String[0], new String[0]);
|
|
||||||
this.expected.expect(IllegalStateException.class);
|
this.expected.expect(IllegalStateException.class);
|
||||||
this.importSelector.selectImports(this.annotationMetadata);
|
selectImports(EnableAutoConfigurationWithFaultyClassExclude.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void nonAutoConfigurationClassNameExclusionsWhenPresentOnClassPathShouldThrowException()
|
public void nonAutoConfigurationClassNameExclusionsWhenPresentOnClassPathShouldThrowException()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
configureExclusions(new String[0],
|
|
||||||
new String[] { "org.springframework.boot.autoconfigure."
|
|
||||||
+ "EnableAutoConfigurationImportSelectorTests.TestConfiguration" },
|
|
||||||
new String[0]);
|
|
||||||
this.expected.expect(IllegalStateException.class);
|
this.expected.expect(IllegalStateException.class);
|
||||||
this.importSelector.selectImports(this.annotationMetadata);
|
selectImports(EnableAutoConfigurationWithFaultyClassNameExclude.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void nonAutoConfigurationPropertyExclusionsWhenPresentOnClassPathShouldThrowException()
|
public void nonAutoConfigurationPropertyExclusionsWhenPresentOnClassPathShouldThrowException()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
configureExclusions(new String[0], new String[0],
|
this.environment.setProperty("spring.autoconfigure.exclude",
|
||||||
new String[] { "org.springframework.boot.autoconfigure."
|
"org.springframework.boot.autoconfigure."
|
||||||
+ "EnableAutoConfigurationImportSelectorTests.TestConfiguration" });
|
+ "EnableAutoConfigurationImportSelectorTests.TestConfiguration");
|
||||||
this.expected.expect(IllegalStateException.class);
|
this.expected.expect(IllegalStateException.class);
|
||||||
this.importSelector.selectImports(this.annotationMetadata);
|
selectImports(BasicEnableAutoConfiguration.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void nameAndPropertyExclusionsWhenNotPresentOnClasspathShouldNotThrowException()
|
public void nameAndPropertyExclusionsWhenNotPresentOnClasspathShouldNotThrowException()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
configureExclusions(new String[0],
|
this.environment.setProperty("spring.autoconfigure.exclude",
|
||||||
new String[] { "org.springframework.boot.autoconfigure.DoesNotExist1" },
|
"org.springframework.boot.autoconfigure.DoesNotExist2");
|
||||||
new String[] { "org.springframework.boot.autoconfigure.DoesNotExist2" });
|
selectImports(EnableAutoConfigurationWithAbsentClassNameExclude.class);
|
||||||
this.importSelector.selectImports(this.annotationMetadata);
|
|
||||||
assertThat(ConditionEvaluationReport.get(this.beanFactory).getExclusions())
|
assertThat(ConditionEvaluationReport.get(this.beanFactory).getExclusions())
|
||||||
.contains("org.springframework.boot.autoconfigure.DoesNotExist1");
|
.containsExactlyInAnyOrder(
|
||||||
assertThat(ConditionEvaluationReport.get(this.beanFactory).getExclusions())
|
"org.springframework.boot.autoconfigure.DoesNotExist1",
|
||||||
.contains("org.springframework.boot.autoconfigure.DoesNotExist2");
|
"org.springframework.boot.autoconfigure.DoesNotExist2");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void configureExclusions(String[] classExclusion, String[] nameExclusion,
|
private String[] selectImports(Class<?> source) {
|
||||||
String[] propertyExclusion) {
|
return this.importSelector.selectImports(new StandardAnnotationMetadata(source));
|
||||||
String annotationName = EnableAutoConfiguration.class.getName();
|
|
||||||
given(this.annotationMetadata.isAnnotated(annotationName)).willReturn(true);
|
|
||||||
given(this.annotationMetadata.getAnnotationAttributes(annotationName, true))
|
|
||||||
.willReturn(this.annotationAttributes);
|
|
||||||
given(this.annotationAttributes.getStringArray("exclude"))
|
|
||||||
.willReturn(classExclusion);
|
|
||||||
given(this.annotationAttributes.getStringArray("excludeName"))
|
|
||||||
.willReturn(nameExclusion);
|
|
||||||
if (propertyExclusion.length > 0) {
|
|
||||||
String value = StringUtils.arrayToCommaDelimitedString(propertyExclusion);
|
|
||||||
this.environment.setProperty("spring.autoconfigure.exclude", value);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<String> getAutoConfigurationClassNames() {
|
private List<String> getAutoConfigurationClassNames() {
|
||||||
|
@ -241,4 +222,49 @@ public class EnableAutoConfigurationImportSelectorTests {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@EnableAutoConfiguration
|
||||||
|
private class BasicEnableAutoConfiguration {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@EnableAutoConfiguration(exclude = FreeMarkerAutoConfiguration.class)
|
||||||
|
private class EnableAutoConfigurationWithClassExclusions {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@SpringBootApplication(exclude = FreeMarkerAutoConfiguration.class)
|
||||||
|
private class SpringBootApplicationWithClassExclusions {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@EnableAutoConfiguration(excludeName = "org.springframework.boot.autoconfigure.mustache.MustacheAutoConfiguration")
|
||||||
|
private class EnableAutoConfigurationWithClassNameExclusions {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@EnableAutoConfiguration(exclude = MustacheAutoConfiguration.class, excludeName = "org.springframework.boot.autoconfigure.freemarker.FreeMarkerAutoConfiguration")
|
||||||
|
private class EnableAutoConfigurationWithClassAndClassNameExclusions {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@EnableAutoConfiguration(exclude = TestConfiguration.class)
|
||||||
|
private class EnableAutoConfigurationWithFaultyClassExclude {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@EnableAutoConfiguration(excludeName = "org.springframework.boot.autoconfigure.EnableAutoConfigurationImportSelectorTests.TestConfiguration")
|
||||||
|
private class EnableAutoConfigurationWithFaultyClassNameExclude {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@EnableAutoConfiguration(excludeName = "org.springframework.boot.autoconfigure.DoesNotExist1")
|
||||||
|
private class EnableAutoConfigurationWithAbsentClassNameExclude {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@SpringBootApplication(excludeName = "org.springframework.boot.autoconfigure.mustache.MustacheAutoConfiguration")
|
||||||
|
private class SpringBootApplicationWithClassNameExclusions {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue