diff options
author | Emmanuel Bourg <ebourg@apache.org> | 2016-12-23 09:09:27 +0100 |
---|---|---|
committer | Emmanuel Bourg <ebourg@apache.org> | 2016-12-23 09:09:27 +0100 |
commit | 7b2b01514e9875ddcb0f6cc15c6557a0417b22fc (patch) | |
tree | 9327f61a75d4bb1bcac2e8827d315d9a37c201a7 /spring-beans/src/main/java/org/springframework/beans/factory/support | |
parent | 0591d269b8b2b33af090ace1ecbd408490618428 (diff) |
New upstream version 4.3.5
Diffstat (limited to 'spring-beans/src/main/java/org/springframework/beans/factory/support')
8 files changed, 120 insertions, 78 deletions
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java index 69b3d93a..1884912e 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java @@ -74,6 +74,7 @@ import org.springframework.core.GenericTypeResolver; import org.springframework.core.MethodParameter; import org.springframework.core.ParameterNameDiscoverer; import org.springframework.core.PriorityOrdered; +import org.springframework.core.ResolvableType; import org.springframework.util.ClassUtils; import org.springframework.util.ObjectUtils; import org.springframework.util.ReflectionUtils; @@ -659,9 +660,9 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac * @see #createBean */ protected Class<?> getTypeForFactoryMethod(String beanName, RootBeanDefinition mbd, Class<?>... typesToMatch) { - Class<?> preResolved = mbd.resolvedFactoryMethodReturnType; - if (preResolved != null) { - return preResolved; + ResolvableType cachedReturnType = mbd.factoryMethodReturnType; + if (cachedReturnType != null) { + return cachedReturnType.resolve(); } Class<?> factoryClass; @@ -685,11 +686,12 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac if (factoryClass == null) { return null; } + factoryClass = ClassUtils.getUserClass(factoryClass); // If all factory methods have the same return type, return that type. // Can't clearly figure out exact method due to type converting / autowiring! Class<?> commonType = null; - boolean cache = false; + Method uniqueCandidate = null; int minNrOfArgs = mbd.getConstructorArgumentValues().getArgumentCount(); Method[] candidates = ReflectionUtils.getUniqueDeclaredMethods(factoryClass); for (Method factoryMethod : candidates) { @@ -724,8 +726,12 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac Class<?> returnType = AutowireUtils.resolveReturnTypeForFactoryMethod( factoryMethod, args, getBeanClassLoader()); if (returnType != null) { - cache = true; + uniqueCandidate = (commonType == null ? factoryMethod : null); commonType = ClassUtils.determineCommonAncestor(returnType, commonType); + if (commonType == null) { + // Ambiguous return types found: return null to indicate "not determinable". + return null; + } } } catch (Throwable ex) { @@ -735,22 +741,22 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac } } else { + uniqueCandidate = (commonType == null ? factoryMethod : null); commonType = ClassUtils.determineCommonAncestor(factoryMethod.getReturnType(), commonType); + if (commonType == null) { + // Ambiguous return types found: return null to indicate "not determinable". + return null; + } } } } if (commonType != null) { // Clear return type found: all factory methods return same type. - if (cache) { - mbd.resolvedFactoryMethodReturnType = commonType; - } - return commonType; - } - else { - // Ambiguous return types found: return null to indicate "not determinable". - return null; + mbd.factoryMethodReturnType = (uniqueCandidate != null ? + ResolvableType.forMethodReturnType(uniqueCandidate) : ResolvableType.forClass(commonType)); } + return commonType; } /** diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java index 104d91c1..326c3295 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java @@ -134,13 +134,13 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp private final Set<PropertyEditorRegistrar> propertyEditorRegistrars = new LinkedHashSet<PropertyEditorRegistrar>(4); - /** A custom TypeConverter to use, overriding the default PropertyEditor mechanism */ - private TypeConverter typeConverter; - /** Custom PropertyEditors to apply to the beans of this factory */ private final Map<Class<?>, Class<? extends PropertyEditor>> customEditors = new HashMap<Class<?>, Class<? extends PropertyEditor>>(4); + /** A custom TypeConverter to use, overriding the default PropertyEditor mechanism */ + private TypeConverter typeConverter; + /** String resolvers to apply e.g. to annotation attribute values */ private final List<StringValueResolver> embeddedValueResolvers = new LinkedList<StringValueResolver>(); @@ -517,7 +517,10 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp // Retrieve corresponding bean definition. RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName); - Class<?> classToMatch = typeToMatch.getRawClass(); + Class<?> classToMatch = typeToMatch.resolve(); + if (classToMatch == null) { + classToMatch = FactoryBean.class; + } Class<?>[] typesToMatch = (FactoryBean.class == classToMatch ? new Class<?>[] {classToMatch} : new Class<?>[] {FactoryBean.class, classToMatch}); @@ -557,6 +560,13 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp } } + ResolvableType resolvableType = mbd.targetType; + if (resolvableType == null) { + resolvableType = mbd.factoryMethodReturnType; + } + if (resolvableType != null && resolvableType.resolve() == beanType) { + return typeToMatch.isAssignableFrom(resolvableType); + } return typeToMatch.isAssignableFrom(beanType); } } @@ -921,10 +931,12 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp setBeanClassLoader(otherFactory.getBeanClassLoader()); setCacheBeanMetadata(otherFactory.isCacheBeanMetadata()); setBeanExpressionResolver(otherFactory.getBeanExpressionResolver()); + setConversionService(otherFactory.getConversionService()); if (otherFactory instanceof AbstractBeanFactory) { AbstractBeanFactory otherAbstractFactory = (AbstractBeanFactory) otherFactory; - this.customEditors.putAll(otherAbstractFactory.customEditors); this.propertyEditorRegistrars.addAll(otherAbstractFactory.propertyEditorRegistrars); + this.customEditors.putAll(otherAbstractFactory.customEditors); + this.typeConverter = otherAbstractFactory.typeConverter; this.beanPostProcessors.addAll(otherAbstractFactory.beanPostProcessors); this.hasInstantiationAwareBeanPostProcessors = this.hasInstantiationAwareBeanPostProcessors || otherAbstractFactory.hasInstantiationAwareBeanPostProcessors; @@ -935,6 +947,10 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp } else { setTypeConverter(otherFactory.getTypeConverter()); + String[] otherScopeNames = otherFactory.getRegisteredScopeNames(); + for (String scopeName : otherScopeNames) { + this.scopes.put(scopeName, otherFactory.getRegisteredScope(scopeName)); + } } } @@ -1441,6 +1457,10 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp * @return the type of the bean, or {@code null} if not predictable */ protected Class<?> predictBeanType(String beanName, RootBeanDefinition mbd, Class<?>... typesToMatch) { + Class<?> targetType = mbd.getTargetType(); + if (targetType != null) { + return targetType; + } if (mbd.getFactoryMethodName() != null) { return null; } diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/AutowireUtils.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/AutowireUtils.java index 1bf54034..0b650df8 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/AutowireUtils.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/AutowireUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 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. @@ -162,8 +162,8 @@ abstract class AutowireUtils { * Determine the target type for the generic return type of the given * <em>generic factory method</em>, where formal type variables are declared * on the given method itself. - * <p>For example, given a factory method with the following signature, - * if {@code resolveReturnTypeForFactoryMethod()} is invoked with the reflected + * <p>For example, given a factory method with the following signature, if + * {@code resolveReturnTypeForFactoryMethod()} is invoked with the reflected * method for {@code creatProxy()} and an {@code Object[]} array containing * {@code MyService.class}, {@code resolveReturnTypeForFactoryMethod()} will * infer that the target return type is {@code MyService}. @@ -184,9 +184,9 @@ abstract class AutowireUtils { * @param method the method to introspect (never {@code null}) * @param args the arguments that will be supplied to the method when it is * invoked (never {@code null}) - * @param classLoader the ClassLoader to resolve class names against, if necessary - * (never {@code null}) - * @return the resolved target return type, the standard return type, or {@code null} + * @param classLoader the ClassLoader to resolve class names against, + * if necessary (never {@code null}) + * @return the resolved target return type or the standard method return type * @since 3.2.5 */ public static Class<?> resolveReturnTypeForFactoryMethod(Method method, Object[] args, ClassLoader classLoader) { diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/BeanDefinitionReaderUtils.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/BeanDefinitionReaderUtils.java index f56f0e52..8a745dab 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/BeanDefinitionReaderUtils.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/BeanDefinitionReaderUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -70,6 +70,23 @@ public class BeanDefinitionReaderUtils { } /** + * Generate a bean name for the given top-level bean definition, + * unique within the given bean factory. + * @param beanDefinition the bean definition to generate a bean name for + * @param registry the bean factory that the definition is going to be + * registered with (to check for existing bean names) + * @return the generated bean name + * @throws BeanDefinitionStoreException if no unique name can be generated + * for the given bean definition + * @see #generateBeanName(BeanDefinition, BeanDefinitionRegistry, boolean) + */ + public static String generateBeanName(BeanDefinition beanDefinition, BeanDefinitionRegistry registry) + throws BeanDefinitionStoreException { + + return generateBeanName(beanDefinition, registry, false); + } + + /** * Generate a bean name for the given bean definition, unique within the * given bean factory. * @param definition the bean definition to generate a bean name for @@ -118,22 +135,6 @@ public class BeanDefinitionReaderUtils { } /** - * Generate a bean name for the given top-level bean definition, - * unique within the given bean factory. - * @param beanDefinition the bean definition to generate a bean name for - * @param registry the bean factory that the definition is going to be - * registered with (to check for existing bean names) - * @return the generated bean name - * @throws BeanDefinitionStoreException if no unique name can be generated - * for the given bean definition - */ - public static String generateBeanName(BeanDefinition beanDefinition, BeanDefinitionRegistry registry) - throws BeanDefinitionStoreException { - - return generateBeanName(beanDefinition, registry, false); - } - - /** * Register the given bean definition with the given bean factory. * @param definitionHolder the bean definition including name and aliases * @param registry the bean factory to register with diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java index c85ef3ca..83448499 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java @@ -43,6 +43,7 @@ import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import javax.inject.Provider; +import org.springframework.beans.BeanUtils; import org.springframework.beans.BeansException; import org.springframework.beans.TypeConverter; import org.springframework.beans.factory.BeanCreationException; @@ -266,6 +267,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto /** * Set a {@link java.util.Comparator} for dependency Lists and arrays. + * @since 4.0 * @see org.springframework.core.OrderComparator * @see org.springframework.core.annotation.AnnotationAwareOrderComparator */ @@ -275,6 +277,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto /** * Return the dependency comparator for this BeanFactory (may be {@code null}. + * @since 4.0 */ public Comparator<Object> getDependencyComparator() { return this.dependencyComparator; @@ -289,11 +292,10 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto Assert.notNull(autowireCandidateResolver, "AutowireCandidateResolver must not be null"); if (autowireCandidateResolver instanceof BeanFactoryAware) { if (System.getSecurityManager() != null) { - final BeanFactory target = this; AccessController.doPrivileged(new PrivilegedAction<Object>() { @Override public Object run() { - ((BeanFactoryAware) autowireCandidateResolver).setBeanFactory(target); + ((BeanFactoryAware) autowireCandidateResolver).setBeanFactory(DefaultListableBeanFactory.this); return null; } }, getAccessControlContext()); @@ -320,7 +322,10 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto DefaultListableBeanFactory otherListableFactory = (DefaultListableBeanFactory) otherFactory; this.allowBeanDefinitionOverriding = otherListableFactory.allowBeanDefinitionOverriding; this.allowEagerClassLoading = otherListableFactory.allowEagerClassLoading; - this.autowireCandidateResolver = otherListableFactory.autowireCandidateResolver; + this.dependencyComparator = otherListableFactory.dependencyComparator; + // A clone of the AutowireCandidateResolver since it is potentially BeanFactoryAware... + setAutowireCandidateResolver(BeanUtils.instantiateClass(getAutowireCandidateResolver().getClass())); + // Make resolvable dependencies (e.g. ResourceLoader) available here as well... this.resolvableDependencies.putAll(otherListableFactory.resolvableDependencies); } } @@ -367,7 +372,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto @Override public String[] getBeanDefinitionNames() { if (this.frozenBeanDefinitionNames != null) { - return this.frozenBeanDefinitionNames; + return this.frozenBeanDefinitionNames.clone(); } else { return StringUtils.toStringArray(this.beanDefinitionNames); @@ -1142,7 +1147,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto if (type.isArray()) { Class<?> componentType = type.getComponentType(); Map<String, Object> matchingBeans = findAutowireCandidates(beanName, componentType, - new MultiElementDependencyDescriptor(descriptor)); + new MultiElementDescriptor(descriptor)); if (matchingBeans.isEmpty()) { return null; } @@ -1162,7 +1167,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto return null; } Map<String, Object> matchingBeans = findAutowireCandidates(beanName, elementType, - new MultiElementDependencyDescriptor(descriptor)); + new MultiElementDescriptor(descriptor)); if (matchingBeans.isEmpty()) { return null; } @@ -1186,7 +1191,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto return null; } Map<String, Object> matchingBeans = findAutowireCandidates(beanName, valueType, - new MultiElementDependencyDescriptor(descriptor)); + new MultiElementDescriptor(descriptor)); if (matchingBeans.isEmpty()) { return null; } @@ -1253,24 +1258,27 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto } } } - for (String candidateName : candidateNames) { - if (!isSelfReference(beanName, candidateName) && isAutowireCandidate(candidateName, descriptor)) { - addCandidateEntry(result, candidateName, descriptor, requiredType); + for (String candidate : candidateNames) { + if (!isSelfReference(beanName, candidate) && isAutowireCandidate(candidate, descriptor)) { + addCandidateEntry(result, candidate, descriptor, requiredType); } } if (result.isEmpty() && !indicatesMultipleBeans(requiredType)) { // Consider fallback matches if the first pass failed to find anything... DependencyDescriptor fallbackDescriptor = descriptor.forFallbackMatch(); - for (String candidateName : candidateNames) { - if (!isSelfReference(beanName, candidateName) && isAutowireCandidate(candidateName, fallbackDescriptor)) { - addCandidateEntry(result, candidateName, descriptor, requiredType); + for (String candidate : candidateNames) { + if (!isSelfReference(beanName, candidate) && isAutowireCandidate(candidate, fallbackDescriptor)) { + addCandidateEntry(result, candidate, descriptor, requiredType); } } if (result.isEmpty()) { - // Consider self references before as a final pass - for (String candidateName : candidateNames) { - if (isSelfReference(beanName, candidateName) && isAutowireCandidate(candidateName, fallbackDescriptor)) { - addCandidateEntry(result, candidateName, descriptor, requiredType); + // Consider self references as a final pass... + // but in the case of a dependency collection, not the very same bean itself. + for (String candidate : candidateNames) { + if (isSelfReference(beanName, candidate) && + (!(descriptor instanceof MultiElementDescriptor) || !beanName.equals(candidate)) && + isAutowireCandidate(candidate, fallbackDescriptor)) { + addCandidateEntry(result, candidate, descriptor, requiredType); } } } @@ -1285,7 +1293,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto private void addCandidateEntry(Map<String, Object> candidates, String candidateName, DependencyDescriptor descriptor, Class<?> requiredType) { - if (descriptor instanceof MultiElementDependencyDescriptor || containsSingleton(candidateName)) { + if (descriptor instanceof MultiElementDescriptor || containsSingleton(candidateName)) { candidates.put(candidateName, descriptor.resolveCandidate(candidateName, requiredType, this)); } else { @@ -1476,10 +1484,10 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto Class<?> targetType = mbd.getTargetType(); if (targetType != null && type.isAssignableFrom(targetType) && isAutowireCandidate(beanName, mbd, descriptor, getAutowireCandidateResolver())) { - // Probably a poxy interfering with target type match -> throw meaningful exception. + // Probably a proxy interfering with target type match -> throw meaningful exception. Object beanInstance = getSingleton(beanName, false); Class<?> beanType = (beanInstance != null ? beanInstance.getClass() : predictBeanType(beanName, mbd)); - if (type != beanType) { + if (!type.isAssignableFrom((beanType))) { throw new BeanNotOfRequiredTypeException(beanName, type, beanType); } } @@ -1739,9 +1747,9 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto } - private static class MultiElementDependencyDescriptor extends NestedDependencyDescriptor { + private static class MultiElementDescriptor extends NestedDependencyDescriptor { - public MultiElementDependencyDescriptor(DependencyDescriptor original) { + public MultiElementDescriptor(DependencyDescriptor original) { super(original); } } diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/GenericTypeAwareAutowireCandidateResolver.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/GenericTypeAwareAutowireCandidateResolver.java index cba9e977..b204d551 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/GenericTypeAwareAutowireCandidateResolver.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/GenericTypeAwareAutowireCandidateResolver.java @@ -147,22 +147,23 @@ public class GenericTypeAwareAutowireCandidateResolver implements AutowireCandid protected ResolvableType getReturnTypeForFactoryMethod(RootBeanDefinition rbd, DependencyDescriptor descriptor) { // Should typically be set for any kind of factory method, since the BeanFactory // pre-resolves them before reaching out to the AutowireCandidateResolver... - Class<?> preResolved = rbd.resolvedFactoryMethodReturnType; - if (preResolved != null) { - return ResolvableType.forClass(preResolved); + ResolvableType returnType = rbd.factoryMethodReturnType; + if (returnType == null) { + Method factoryMethod = rbd.getResolvedFactoryMethod(); + if (factoryMethod != null) { + returnType = ResolvableType.forMethodReturnType(factoryMethod); + } } - else { - Method resolvedFactoryMethod = rbd.getResolvedFactoryMethod(); - if (resolvedFactoryMethod != null) { - if (descriptor.getDependencyType().isAssignableFrom(resolvedFactoryMethod.getReturnType())) { - // Only use factory method metadata if the return type is actually expressive enough - // for our dependency. Otherwise, the returned instance type may have matched instead - // in case of a singleton instance having been registered with the container already. - return ResolvableType.forMethodReturnType(resolvedFactoryMethod); - } + if (returnType != null) { + Class<?> resolvedClass = returnType.resolve(); + if (resolvedClass != null && descriptor.getDependencyType().isAssignableFrom(resolvedClass)) { + // Only use factory method metadata if the return type is actually expressive enough + // for our dependency. Otherwise, the returned instance type may have matched instead + // in case of a singleton instance having been registered with the container already. + return returnType; } - return null; } + return null; } diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/RootBeanDefinition.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/RootBeanDefinition.java index 237e672a..6f61186a 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/RootBeanDefinition.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/RootBeanDefinition.java @@ -64,7 +64,7 @@ public class RootBeanDefinition extends AbstractBeanDefinition { volatile Class<?> resolvedTargetType; /** Package-visible field for caching the return type of a generically typed factory method */ - volatile Class<?> resolvedFactoryMethodReturnType; + volatile ResolvableType factoryMethodReturnType; /** Common lock for the four constructor fields below */ final Object constructorArgumentLock = new Object(); diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/StaticListableBeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/StaticListableBeanFactory.java index 32866846..d1aa847f 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/StaticListableBeanFactory.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/StaticListableBeanFactory.java @@ -248,7 +248,13 @@ public class StaticListableBeanFactory implements ListableBeanFactory { @Override public String[] getBeanNamesForType(ResolvableType type) { - boolean isFactoryType = (type != null && FactoryBean.class.isAssignableFrom(type.getRawClass())); + boolean isFactoryType = false; + if (type != null) { + Class<?> resolved = type.resolve(); + if (resolved != null && FactoryBean.class.isAssignableFrom(resolved)) { + isFactoryType = true; + } + } List<String> matches = new ArrayList<String>(); for (Map.Entry<String, Object> entry : this.beans.entrySet()) { String name = entry.getKey(); |