diff --git a/org.springframework.test/src/main/java/org/springframework/test/context/ContextConfiguration.java b/org.springframework.test/src/main/java/org/springframework/test/context/ContextConfiguration.java index 7c0332be5e..ec3b2f859b 100644 --- a/org.springframework.test/src/main/java/org/springframework/test/context/ContextConfiguration.java +++ b/org.springframework.test/src/main/java/org/springframework/test/context/ContextConfiguration.java @@ -103,8 +103,8 @@ public @interface ContextConfiguration { * for details on the default configuration classes that will be used if none * are specified. * - *

Note that this attribute may not be used in - * conjunction with {@link #locations} or {@link #value}. + *

This attribute may not be used in conjunction with + * {@link #locations} or {@link #value}. * @since 3.1 * @see org.springframework.context.annotation.Configuration * @see org.springframework.test.context.support.AnnotationConfigContextLoader @@ -158,12 +158,12 @@ public @interface ContextConfiguration { * {@code ExtendedConfig} may therefore override those defined in * {@code BaseConfig}. *

-	 * @ContextConfiguration(classes=BaseConfig.class, loader=AnnotationConfigContextLoader.class)
+	 * @ContextConfiguration(classes=BaseConfig.class)
 	 * public class BaseTest {
 	 *     // ...
 	 * }
 	 * 
-	 * @ContextConfiguration(classes=ExtendedConfig.class, loader=AnnotationConfigContextLoader.class)
+	 * @ContextConfiguration(classes=ExtendedConfig.class)
 	 * public class ExtendedTest extends BaseTest {
 	 *     // ...
 	 * }
@@ -172,7 +172,6 @@ public @interface ContextConfiguration {
 	 */
 	boolean inheritLocations() default true;
 
-	// TODO Update regarding default --> DelegatingSmartContextLoader
 	/**
 	 * The type of {@link ContextLoader} (or {@link SmartContextLoader}) to use
 	 * for loading an {@link org.springframework.context.ApplicationContext
@@ -184,10 +183,15 @@ public @interface ContextConfiguration {
 	 * loader, a default loader will be used instead.
 	 * 
 	 * 

The default concrete implementation chosen at runtime will be - * {@link org.springframework.test.context.support.GenericXmlContextLoader - * GenericXmlContextLoader}. Also check out + * {@link org.springframework.test.context.support.DelegatingSmartContextLoader + * DelegatingSmartContextLoader}. For further details on the default behavior + * of various concrete {@code ContextLoaders}, check out the Javadoc for * {@link org.springframework.test.context.support.AbstractContextLoader - * AbstractContextLoader}'s javadoc for details on the default behavior there. + * AbstractContextLoader}, + * {@link org.springframework.test.context.support.GenericXmlContextLoader + * GenericXmlContextLoader}, and + * {@link org.springframework.test.context.support.AnnotationConfigContextLoader + * AnnotationConfigContextLoader}. * @since 2.5 */ Class loader() default ContextLoader.class; diff --git a/org.springframework.test/src/main/java/org/springframework/test/context/ContextConfigurationAttributes.java b/org.springframework.test/src/main/java/org/springframework/test/context/ContextConfigurationAttributes.java index 3c81bd5e77..bdcd5135c1 100644 --- a/org.springframework.test/src/main/java/org/springframework/test/context/ContextConfigurationAttributes.java +++ b/org.springframework.test/src/main/java/org/springframework/test/context/ContextConfigurationAttributes.java @@ -99,6 +99,8 @@ public class ContextConfigurationAttributes { * @param classes the configuration classes declared via {@code @ContextConfiguration} * @param inheritLocations the inheritLocations flag declared via {@code @ContextConfiguration} * @param contextLoaderClass the {@code ContextLoader} class declared via {@code @ContextConfiguration} + * @throws IllegalArgumentException if the {@code declaringClass} or {@code contextLoaderClass} is + * null, or if the {@code locations} and {@code classes} are both non-empty */ public ContextConfigurationAttributes(Class declaringClass, String[] locations, Class[] classes, boolean inheritLocations, Class contextLoaderClass) { @@ -106,6 +108,16 @@ public class ContextConfigurationAttributes { Assert.notNull(declaringClass, "declaringClass must not be null"); Assert.notNull(contextLoaderClass, "contextLoaderClass must not be null"); + if (!ObjectUtils.isEmpty(locations) && !ObjectUtils.isEmpty(classes)) { + String msg = String.format( + "Test class [%s] has been configured with @ContextConfiguration's 'locations' (or 'value') %s " + + "and 'classes' %s attributes. Only one declaration of resources " + + "is permitted per @ContextConfiguration annotation.", declaringClass.getName(), + ObjectUtils.nullSafeToString(locations), ObjectUtils.nullSafeToString(classes)); + logger.error(msg); + throw new IllegalArgumentException(msg); + } + this.declaringClass = declaringClass; this.locations = locations; this.classes = classes; @@ -169,16 +181,38 @@ public class ContextConfigurationAttributes { this.classes = classes; } + /** + * Determine if this {@code ContextConfigurationAttributes} instance has + * path-based resource locations. + * @return true if the {@link #getLocations() locations} array is not empty + * @see #hasResources() + * @see #hasClasses() + */ + public boolean hasLocations() { + return !ObjectUtils.isEmpty(getLocations()); + } + + /** + * Determine if this {@code ContextConfigurationAttributes} instance has + * class-based resources. + * @return true if the {@link #getClasses() classes} array is not empty + * @see #hasResources() + * @see #hasLocations() + */ + public boolean hasClasses() { + return !ObjectUtils.isEmpty(getClasses()); + } + /** * Determine if this {@code ContextConfigurationAttributes} instance has * either path-based resource locations or class-based resources. * @return true if either the {@link #getLocations() locations} * or the {@link #getClasses() classes} array is not empty - * @see #getLocations() - * @see #getClasses() + * @see #hasLocations() + * @see #hasClasses() */ public boolean hasResources() { - return !ObjectUtils.isEmpty(getLocations()) || !ObjectUtils.isEmpty(getClasses()); + return hasLocations() || hasClasses(); } /** diff --git a/org.springframework.test/src/main/java/org/springframework/test/context/SmartContextLoader.java b/org.springframework.test/src/main/java/org/springframework/test/context/SmartContextLoader.java index 15b08e1650..422bd88bb0 100644 --- a/org.springframework.test/src/main/java/org/springframework/test/context/SmartContextLoader.java +++ b/org.springframework.test/src/main/java/org/springframework/test/context/SmartContextLoader.java @@ -22,34 +22,37 @@ import org.springframework.context.ApplicationContext; *

Strategy interface for loading an {@link ApplicationContext application context} * for an integration test managed by the Spring TestContext Framework. * - *

The SmartContextLoader SPI supersedes the {@link ContextLoader} - * SPI introduced in Spring 2.5: SmartContextLoaders can process both - * resource locations and configuration classes. Furthermore, a - * SmartContextLoader can set active bean definition profiles - * in the context that it loads (see {@link MergedContextConfiguration#getActiveProfiles()} - * and {@link #loadContext(MergedContextConfiguration)}). + *

The {@code SmartContextLoader} SPI supersedes the {@link ContextLoader} + * SPI introduced in Spring 2.5: a {@code SmartContextLoader} can process both + * resource locations and configuration classes. Furthermore, a {@code SmartContextLoader} + * can set active bean definition profiles in the context that it loads (see + * {@link MergedContextConfiguration#getActiveProfiles()} and + * {@link #loadContext(MergedContextConfiguration)}). * - *

Clients of a SmartContextLoader should call - * {@link #processContextConfiguration(ContextConfigurationAttributes) processContextConfiguration()} - * prior to calling {@link #loadContext(MergedContextConfiguration) loadContext()} - * in case the SmartContextLoader provides custom support for modifying - * or generating resource locations or configuration classes. The results of - * {@link #processContextConfiguration(ContextConfigurationAttributes) processContextConfiguration()} - * should be merged for all classes in the hierarchy of the root test class and - * then supplied to {@link #loadContext(MergedContextConfiguration) loadContext()}. + *

Clients of a {@code SmartContextLoader} should call + * {@link #processContextConfiguration(ContextConfigurationAttributes) + * processContextConfiguration()} prior to calling + * {@link #loadContext(MergedContextConfiguration) loadContext()}. This gives a + * {@code SmartContextLoader} the opportunity to provide custom support for + * modifying or generating resource locations or configuration classes. The + * results of {@link #processContextConfiguration(ContextConfigurationAttributes) + * processContextConfiguration()} should be merged for all classes in the + * hierarchy of the root test class and then supplied to + * {@link #loadContext(MergedContextConfiguration) loadContext()}. * - *

Even though SmartContextLoader extends ContextLoader, - * clients should favor SmartContextLoader-specific methods over those - * defined in ContextLoader, particularly because a - * SmartContextLoader may choose not to support methods defined in - * the ContextLoader SPI. + *

Even though {@code SmartContextLoader} extends {@code ContextLoader}, + * clients should favor {@code SmartContextLoader}-specific methods over those + * defined in {@code ContextLoader}, particularly because a + * {@code SmartContextLoader} may choose not to support methods defined in + * the {@code ContextLoader} SPI. * *

Concrete implementations must provide a public no-args constructor. * *

Spring provides the following out-of-the-box implementations: *

* @@ -62,47 +65,25 @@ import org.springframework.context.ApplicationContext; */ public interface SmartContextLoader extends ContextLoader { - /** - * Determines if this SmartContextLoader generates default resource - * locations or {@link org.springframework.context.annotation.Configuration - * configuration classes} if the locations or classes - * present in the {@link ContextConfigurationAttributes} provided to - * {@link #processContextConfiguration()} are null or empty. - *

Returning a value of true signals not only that this - * SmartContextLoader generates defaults but also that it will - * preemptively verify that a generated default actually exists. - * @return true if this SmartContextLoader - * generates default configuration locations or classes - * @see #processContextConfiguration(ContextConfigurationAttributes) - */ - boolean generatesDefaults(); - /** * Processes the {@link ContextConfigurationAttributes} for a given test class. *

Concrete implementations may choose to modify the locations - * or classes in the supplied {@link ContextConfigurationAttributes} - * or generate default configuration locations or classes if the - * supplied values are null or empty. - *

Note: If {@link #generatesDefaults()} returns true, - * this method must preemptively verify that a generated default - * actually exists before setting the corresponding locations - * or classes property in the supplied + * or classes in the supplied {@link ContextConfigurationAttributes}, + * generate default configuration locations, or detect + * default configuration classes if the supplied values are null + * or empty. + *

Note: in contrast to a standard {@code ContextLoader}, a + * {@code SmartContextLoader} must preemptively verify that + * a generated or detected default actually exists before setting the corresponding + * locations or classes property in the supplied * {@link ContextConfigurationAttributes}. Consequently, leaving the * locations or classes property empty signals that - * this SmartContextLoader was not able to generate defaults. + * this {@code SmartContextLoader} was not able to generate or detect defaults. * @param configAttributes the context configuration attributes to process * @see #generatesDefaults() */ void processContextConfiguration(ContextConfigurationAttributes configAttributes); - /** - * TODO Document supports(MergedContextConfiguration). - * - * @param mergedConfig - * @return - */ - boolean supports(MergedContextConfiguration mergedConfig); - /** * Loads a new {@link ApplicationContext context} based on the supplied * {@link MergedContextConfiguration merged context configuration}, @@ -111,7 +92,7 @@ public interface SmartContextLoader extends ContextLoader { *

Concrete implementations should register annotation configuration * processors with bean factories of * {@link ApplicationContext application contexts} loaded by this - * SmartContextLoader. Beans will therefore automatically be + * {@code SmartContextLoader}. Beans will therefore automatically be * candidates for annotation-based dependency injection using * {@link org.springframework.beans.factory.annotation.Autowired @Autowired}, * {@link javax.annotation.Resource @Resource}, and @@ -119,10 +100,10 @@ public interface SmartContextLoader extends ContextLoader { * should set the active bean definition profiles in the context's * {@link org.springframework.core.env.Environment Environment}. *

Any ApplicationContext loaded by a - * SmartContextLoader must register a JVM + * {@code SmartContextLoader} must register a JVM * shutdown hook for itself. Unless the context gets closed early, all context * instances will be automatically closed on JVM shutdown. This allows for - * freeing external resources held by beans within the context (e.g., + * freeing of external resources held by beans within the context (e.g., * temporary files). * @param mergedConfig the merged context configuration to use to load the * application context diff --git a/org.springframework.test/src/main/java/org/springframework/test/context/TestContext.java b/org.springframework.test/src/main/java/org/springframework/test/context/TestContext.java index 5e734bba8e..7bf23876f1 100644 --- a/org.springframework.test/src/main/java/org/springframework/test/context/TestContext.java +++ b/org.springframework.test/src/main/java/org/springframework/test/context/TestContext.java @@ -54,32 +54,30 @@ public class TestContext extends AttributeAccessorSupport { /** * Delegates to {@link #TestContext(Class, ContextCache, String)} with a - * value of null for the default ContextLoader - * class name. + * value of null for the default {@code ContextLoader} class name. */ TestContext(Class testClass, ContextCache contextCache) { this(testClass, contextCache, null); } - // TODO Update regarding default --> DelegatingSmartContextLoader /** * Construct a new test context for the supplied {@link Class test class} * and {@link ContextCache context cache} and parse the corresponding * {@link ContextConfiguration @ContextConfiguration} annotation, if * present. - *

If the supplied class name for the default ContextLoader - * is null or empty and no ContextLoader - * class is explicitly supplied via the - * @ContextConfiguration annotation, a - * {@link org.springframework.test.context.support.GenericXmlContextLoader - * GenericXmlContextLoader} will be used instead. + *

If the supplied class name for the default {@code ContextLoader} + * is null or empty and no concrete {@code ContextLoader} + * class is explicitly supplied via the {@code @ContextConfiguration} + * annotation, a + * {@link org.springframework.test.context.support.DelegatingSmartContextLoader + * DelegatingSmartContextLoader} will be used instead. * @param testClass the test class for which the test context should be * constructed (must not be null) * @param contextCache the context cache from which the constructed test * context should retrieve application contexts (must not be * null) * @param defaultContextLoaderClassName the name of the default - * ContextLoader class to use (may be null) + * {@code ContextLoader} class to use (may be null) */ TestContext(Class testClass, ContextCache contextCache, String defaultContextLoaderClassName) { Assert.notNull(testClass, "Test class must not be null"); @@ -110,7 +108,7 @@ public class TestContext extends AttributeAccessorSupport { /** * Load an ApplicationContext for this test context using the - * configured ContextLoader and configuration attributes. + * configured {@code ContextLoader} and configuration attributes. * @throws Exception if an error occurs while loading the application context */ private ApplicationContext loadApplicationContext() throws Exception { diff --git a/org.springframework.test/src/main/java/org/springframework/test/context/support/AbstractContextLoader.java b/org.springframework.test/src/main/java/org/springframework/test/context/support/AbstractContextLoader.java index e1d4433000..77ce7fbb79 100644 --- a/org.springframework.test/src/main/java/org/springframework/test/context/support/AbstractContextLoader.java +++ b/org.springframework.test/src/main/java/org/springframework/test/context/support/AbstractContextLoader.java @@ -23,7 +23,6 @@ import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.support.ResourcePatternUtils; import org.springframework.test.context.ContextConfigurationAttributes; import org.springframework.test.context.ContextLoader; -import org.springframework.test.context.MergedContextConfiguration; import org.springframework.test.context.SmartContextLoader; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; @@ -40,9 +39,7 @@ import org.springframework.util.StringUtils; *

As of Spring 3.1, AbstractContextLoader also provides a basis * for all concrete implementations of the {@link SmartContextLoader} SPI. For * backwards compatibility with the {@code ContextLoader} SPI, - * {@link #processContextConfiguration()} delegates to - * {@link #processLocations()}, and {@link #generatesDefaults()} delegates to - * {@link #isGenerateDefaultLocations()}. + * {@link #processContextConfiguration()} delegates to {@link #processLocations()}. * * @author Sam Brannen * @author Juergen Hoeller @@ -54,23 +51,12 @@ public abstract class AbstractContextLoader implements SmartContextLoader { private static final Log logger = LogFactory.getLog(AbstractContextLoader.class); - private static final String[] EMPTY_STRING_ARRAY = new String[] {}; + private static final String[] EMPTY_STRING_ARRAY = new String[0]; private static final String SLASH = "/"; // --- SmartContextLoader ----------------------------------------------- - /** - * For backwards compatibility with the {@link ContextLoader} SPI, the - * default implementation simply delegates to - * {@link #isGenerateDefaultLocations()}. - * @see org.springframework.test.context.SmartContextLoader#generatesDefaults() - * @see #isGenerateDefaultLocations() - */ - public boolean generatesDefaults() { - return isGenerateDefaultLocations(); - } - /** * For backwards compatibility with the {@link ContextLoader} SPI, the * default implementation simply delegates to {@link #processLocations()}, @@ -83,6 +69,7 @@ public abstract class AbstractContextLoader implements SmartContextLoader { * the supplied configuration attributes. *

Can be overridden in subclasses — for example, to process * configuration classes instead of resource locations. + * @since 3.1 * @see #processLocations() */ public void processContextConfiguration(ContextConfigurationAttributes configAttributes) { @@ -91,13 +78,6 @@ public abstract class AbstractContextLoader implements SmartContextLoader { configAttributes.setLocations(processedLocations); } - /** - * TODO Document default supports(MergedContextConfiguration) implementation. - */ - public boolean supports(MergedContextConfiguration mergedConfig) { - return !ObjectUtils.isEmpty(mergedConfig.getLocations()) && ObjectUtils.isEmpty(mergedConfig.getClasses()); - } - // --- ContextLoader ------------------------------------------------------- /** @@ -114,6 +94,7 @@ public abstract class AbstractContextLoader implements SmartContextLoader { * @param locations the unmodified locations to use for loading the * application context (can be null or empty) * @return a processed array of application context resource locations + * @since 2.5 * @see #isGenerateDefaultLocations() * @see #generateDefaultLocations() * @see #modifyLocations() @@ -142,6 +123,7 @@ public abstract class AbstractContextLoader implements SmartContextLoader { * default location generation strategy. * @param clazz the class for which the default locations are to be generated * @return an array of default application context resource locations + * @since 2.5 * @see #getResourceSuffix() */ protected String[] generateDefaultLocations(Class clazz) { @@ -178,6 +160,7 @@ public abstract class AbstractContextLoader implements SmartContextLoader { * @param clazz the class with which the locations are associated * @param locations the resource locations to be modified * @return an array of modified application context resource locations + * @since 2.5 */ protected String[] modifyLocations(Class clazz, String... locations) { String[] modifiedLocations = new String[locations.length]; @@ -203,6 +186,7 @@ public abstract class AbstractContextLoader implements SmartContextLoader { * {@link #processLocations()} are null or empty. *

Can be overridden by subclasses to change the default behavior. * @return always true by default + * @since 2.5 */ protected boolean isGenerateDefaultLocations() { return true; @@ -213,6 +197,7 @@ public abstract class AbstractContextLoader implements SmartContextLoader { * locations when generating default locations. *

Must be implemented by subclasses. * @return the resource suffix; should not be null or empty + * @since 2.5 * @see #generateDefaultLocations() */ protected abstract String getResourceSuffix(); diff --git a/org.springframework.test/src/main/java/org/springframework/test/context/support/AbstractGenericContextLoader.java b/org.springframework.test/src/main/java/org/springframework/test/context/support/AbstractGenericContextLoader.java index 55b392c426..935fc85a3b 100644 --- a/org.springframework.test/src/main/java/org/springframework/test/context/support/AbstractGenericContextLoader.java +++ b/org.springframework.test/src/main/java/org/springframework/test/context/support/AbstractGenericContextLoader.java @@ -86,6 +86,7 @@ public abstract class AbstractGenericContextLoader extends AbstractContextLoader * @return a new application context * @see org.springframework.test.context.SmartContextLoader#loadContext(MergedContextConfiguration) * @see GenericApplicationContext + * @since 3.1 */ public final ConfigurableApplicationContext loadContext(MergedContextConfiguration mergedConfig) throws Exception { if (logger.isDebugEnabled()) { @@ -131,6 +132,7 @@ public abstract class AbstractGenericContextLoader extends AbstractContextLoader * @see org.springframework.test.context.ContextLoader#loadContext * @see GenericApplicationContext * @see #loadContext(MergedContextConfiguration) + * @since 2.5 */ public final ConfigurableApplicationContext loadContext(String... locations) throws Exception { if (logger.isDebugEnabled()) { @@ -159,6 +161,7 @@ public abstract class AbstractGenericContextLoader extends AbstractContextLoader * @see GenericApplicationContext#setAllowBeanDefinitionOverriding * @see GenericApplicationContext#setResourceLoader * @see GenericApplicationContext#setId + * @since 2.5 */ protected void prepareContext(GenericApplicationContext context) { } @@ -175,6 +178,7 @@ public abstract class AbstractGenericContextLoader extends AbstractContextLoader * @see DefaultListableBeanFactory#setAllowEagerClassLoading * @see DefaultListableBeanFactory#setAllowCircularReferences * @see DefaultListableBeanFactory#setAllowRawInjectionDespiteWrapping + * @since 2.5 */ protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) { } @@ -194,8 +198,8 @@ public abstract class AbstractGenericContextLoader extends AbstractContextLoader * registering bean definitions. * @param context the context into which the bean definitions should be loaded * @param mergedConfig the merged context configuration - * @since 3.1 * @see #loadContext(MergedContextConfiguration) + * @since 3.1 */ protected void loadBeanDefinitions(GenericApplicationContext context, MergedContextConfiguration mergedConfig) { createBeanDefinitionReader(context).loadBeanDefinitions(mergedConfig.getLocations()); @@ -210,6 +214,7 @@ public abstract class AbstractGenericContextLoader extends AbstractContextLoader * @see #loadContext(String...) * @see #loadBeanDefinitions * @see BeanDefinitionReader + * @since 2.5 */ protected abstract BeanDefinitionReader createBeanDefinitionReader(GenericApplicationContext context); @@ -222,6 +227,7 @@ public abstract class AbstractGenericContextLoader extends AbstractContextLoader * @param context the newly created application context * @see #loadContext(MergedContextConfiguration) * @see #loadContext(String...) + * @since 2.5 */ protected void customizeContext(GenericApplicationContext context) { } diff --git a/org.springframework.test/src/main/java/org/springframework/test/context/support/AnnotationConfigContextLoader.java b/org.springframework.test/src/main/java/org/springframework/test/context/support/AnnotationConfigContextLoader.java index 167c0012d6..2c1b6fd222 100644 --- a/org.springframework.test/src/main/java/org/springframework/test/context/support/AnnotationConfigContextLoader.java +++ b/org.springframework.test/src/main/java/org/springframework/test/context/support/AnnotationConfigContextLoader.java @@ -64,31 +64,23 @@ public class AnnotationConfigContextLoader extends AbstractGenericContextLoader /** * Process configuration classes in the supplied {@link ContextConfigurationAttributes}. *

If the configuration classes are null or empty and - * {@link #generatesDefaults()} returns true, this + * {@link #isGenerateDefaultLocations()} returns true, this * SmartContextLoader will attempt to * {@link #generateDefaultConfigurationClasses generate default configuration classes}. * Otherwise, properties in the supplied configuration attributes will not * be modified. * @param configAttributes the context configuration attributes to process * @see org.springframework.test.context.SmartContextLoader#processContextConfiguration() - * @see #generatesDefaults() + * @see #isGenerateDefaultLocations() * @see #generateDefaultConfigurationClasses() */ public void processContextConfiguration(ContextConfigurationAttributes configAttributes) { - if (ObjectUtils.isEmpty(configAttributes.getClasses()) && generatesDefaults()) { + if (ObjectUtils.isEmpty(configAttributes.getClasses()) && isGenerateDefaultLocations()) { Class[] defaultConfigClasses = generateDefaultConfigurationClasses(configAttributes.getDeclaringClass()); configAttributes.setClasses(defaultConfigClasses); } } - /** - * TODO Document overridden supports(MergedContextConfiguration) implementation. - */ - @Override - public boolean supports(MergedContextConfiguration mergedConfig) { - return ObjectUtils.isEmpty(mergedConfig.getLocations()) && !ObjectUtils.isEmpty(mergedConfig.getClasses()); - } - // --- AnnotationConfigContextLoader --------------------------------------- private boolean isStaticNonPrivateAndNonFinal(Class clazz) { @@ -99,7 +91,7 @@ public class AnnotationConfigContextLoader extends AbstractGenericContextLoader /** * Determine if the supplied {@link Class} meets the criteria for being - * considered as a default configuration class candidate. + * considered a default configuration class candidate. *

Specifically, such candidates: *