Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: spring-projects/spring-framework
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: v4.3.25.RELEASE
Choose a base ref
...
head repository: spring-projects/spring-framework
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: v4.3.26.RELEASE
Choose a head ref

Commits on Aug 2, 2019

  1. Copy the full SHA
    18ae379 View commit details

Commits on Sep 25, 2019

  1. Copy the full SHA
    5d58676 View commit details
  2. Copy the full SHA
    ace2e62 View commit details
  3. Polishing

    jhoeller committed Sep 25, 2019
    Copy the full SHA
    d1a34b9 View commit details
  4. Copy the full SHA
    8abe714 View commit details

Commits on Oct 30, 2019

  1. Copy the full SHA
    5c741a7 View commit details
  2. Polishing

    jhoeller committed Oct 30, 2019
    Copy the full SHA
    d0506bc View commit details

Commits on Nov 13, 2019

  1. Fix schemaZip Gradle task on MS Windows

    Prior to this commit, the schemaZip Gradle task failed to find Spring
    schema files on MS Windows due to path separators hard coded to forward
    slashes that are not compatible with the Windows operating system.
    
    Consequently, a full build failed on Windows since the distZip task was
    not able to locate the zipped schema archive that the schemaZip task
    failed to create.
    
    This commit fixes this by updating the schemaZip task to search for
    schema files using backslashes as well as forward slashes.
    
    Closes gh-23933
    y987425112 authored and sbrannen committed Nov 13, 2019
    Copy the full SHA
    f694401 View commit details
  2. isIncludeHeaders() declared as protected.

    endtak authored and jhoeller committed Nov 13, 2019
    Copy the full SHA
    b3fb79e View commit details
  3. Copy the full SHA
    9cad930 View commit details
  4. Copy the full SHA
    e1f9507 View commit details

Commits on Nov 14, 2019

  1. Copy the full SHA
    f190168 View commit details

Commits on Nov 19, 2019

  1. Copy the full SHA
    ee2fe1d View commit details

Commits on Nov 20, 2019

  1. Polishing

    jhoeller committed Nov 20, 2019
    Copy the full SHA
    9404a8a View commit details

Commits on Dec 1, 2019

  1. Polishing

    jhoeller committed Dec 1, 2019
    Copy the full SHA
    dcedd29 View commit details

Commits on Dec 9, 2019

  1. Polishing

    jhoeller committed Dec 9, 2019
    Copy the full SHA
    a10f9f2 View commit details

Commits on Jan 10, 2020

  1. Copy the full SHA
    33dc3b0 View commit details
  2. Polishing

    jhoeller committed Jan 10, 2020
    Copy the full SHA
    71cd792 View commit details

Commits on Jan 13, 2020

  1. Copy the full SHA
    3cbd4a9 View commit details
  2. Disable annotation processing warning in spring-test

    This commit disables warnings for annotation processing in spring-test
    in order to get the build passing again after it mysteriously started
    failing in 2020.
    sbrannen committed Jan 13, 2020
    Copy the full SHA
    2e132bd View commit details
  3. Copy the full SHA
    58d1198 View commit details

Commits on Jan 14, 2020

  1. Copy the full SHA
    c531f8d View commit details
Showing with 356 additions and 271 deletions.
  1. +9 −5 build.gradle
  2. +1 −1 gradle.properties
  3. +7 −6 spring-aop/src/main/java/org/springframework/aop/framework/CglibAopProxy.java
  4. +2 −2 spring-aop/src/main/java/org/springframework/aop/framework/ReflectiveMethodInvocation.java
  5. +10 −7 spring-aop/src/main/java/org/springframework/aop/support/ComposablePointcut.java
  6. +13 −13 spring-aop/src/main/java/org/springframework/aop/support/DefaultIntroductionAdvisor.java
  7. +4 −3 spring-aop/src/main/java/org/springframework/aop/support/NameMatchMethodPointcut.java
  8. +3 −2 spring-aop/src/main/java/org/springframework/aop/support/Pointcuts.java
  9. +3 −3 spring-aop/src/main/java/org/springframework/aop/support/annotation/AnnotationMatchingPointcut.java
  10. +9 −10 ...ans-groovy/src/main/java/org/springframework/beans/factory/groovy/GroovyBeanDefinitionReader.java
  11. +5 −5 spring-beans/src/main/java/org/springframework/beans/ExtendedBeanInfo.java
  12. +8 −6 .../main/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessor.java
  13. +2 −2 spring-beans/src/main/java/org/springframework/beans/factory/config/ConstructorArgumentValues.java
  14. +6 −17 ...s/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java
  15. +2 −2 ...rc/main/java/org/springframework/beans/factory/support/CglibSubclassingInstantiationStrategy.java
  16. +3 −2 spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java
  17. +1 −0 ...g-beans/src/main/java/org/springframework/beans/factory/support/DefaultSingletonBeanRegistry.java
  18. +10 −2 spring-beans/src/main/java/org/springframework/beans/factory/support/GenericBeanDefinition.java
  19. +1 −2 spring-beans/src/main/java/org/springframework/beans/factory/support/MethodOverrides.java
  20. +34 −21 spring-beans/src/test/java/org/springframework/beans/factory/DefaultListableBeanFactoryTests.java
  21. +20 −1 spring-beans/src/test/java/org/springframework/beans/factory/support/BeanDefinitionTests.java
  22. +1 −0 ...ntext-support/src/main/java/org/springframework/cache/jcache/interceptor/JCacheAspectSupport.java
  23. +10 −4 spring-context/src/main/java/org/springframework/cache/interceptor/SimpleKey.java
  24. +19 −7 ...text/src/main/java/org/springframework/context/support/ReloadableResourceBundleMessageSource.java
  25. +1 −1 ...text/src/main/java/org/springframework/jmx/export/assembler/InterfaceBasedMBeanInfoAssembler.java
  26. +4 −4 spring-context/src/test/java/org/springframework/cache/interceptor/SimpleKeyGeneratorTests.java
  27. +6 −3 ...g-context/src/test/java/org/springframework/context/event/AnnotationDrivenEventListenerTests.java
  28. +6 −5 spring-core/src/main/java/org/springframework/core/ResolvableType.java
  29. +8 −6 spring-core/src/main/java/org/springframework/core/convert/TypeDescriptor.java
  30. +2 −2 spring-core/src/main/java/org/springframework/util/StringUtils.java
  31. +9 −26 spring-core/src/test/java/org/springframework/core/ResolvableTypeTests.java
  32. +70 −50 spring-expression/src/main/java/org/springframework/expression/spel/standard/SpelExpression.java
  33. +5 −5 spring-jdbc/src/main/java/org/springframework/jdbc/support/rowset/SqlRowSet.java
  34. +1 −1 spring-jdbc/src/main/resources/org/springframework/jdbc/support/sql-error-codes.xml
  35. +6 −0 spring-jdbc/src/test/java/org/springframework/jdbc/datasource/DataSourceTransactionManagerTests.java
  36. +0 −1 spring-messaging/src/main/java/org/springframework/messaging/simp/user/MultiServerUserRegistry.java
  37. +3 −2 spring-tx/src/test/java/org/springframework/transaction/interceptor/TransactionInterceptorTests.java
  38. +1 −1 spring-web/src/main/java/org/springframework/web/filter/AbstractRequestLoggingFilter.java
  39. +21 −16 spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/SseEmitter.java
  40. +5 −6 ...ain/java/org/springframework/web/socket/sockjs/transport/handler/EventSourceTransportHandler.java
  41. +7 −6 ...c/main/java/org/springframework/web/socket/sockjs/transport/handler/HtmlFileTransportHandler.java
  42. +2 −2 .../main/java/org/springframework/web/socket/sockjs/transport/handler/WebSocketTransportHandler.java
  43. +2 −3 ...main/java/org/springframework/web/socket/sockjs/transport/handler/XhrPollingTransportHandler.java
  44. +2 −3 ...in/java/org/springframework/web/socket/sockjs/transport/handler/XhrStreamingTransportHandler.java
  45. +12 −5 src/dist/license.txt
14 changes: 9 additions & 5 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -62,7 +62,7 @@ configure(allprojects) { project ->
ext.jtaVersion = "1.2"
ext.junitVersion = "4.12"
ext.log4jVersion = "1.2.17"
ext.nettyVersion = "4.1.38.Final"
ext.nettyVersion = "4.1.39.Final"
ext.okhttpVersion = "2.7.5"
ext.okhttp3Version = "3.8.1"
ext.openjpaVersion = "2.4.2"
@@ -71,11 +71,11 @@ configure(allprojects) { project ->
ext.romeVersion = "1.7.4"
ext.slf4jVersion = "1.7.25"
ext.snakeyamlVersion = "1.17"
ext.snifferVersion = "1.17"
ext.snifferVersion = "1.18"
ext.testngVersion = "6.9.10"
ext.tiles2Version = "2.2.2"
ext.tiles3Version = "3.0.8"
ext.tomcatVersion = "8.5.43"
ext.tomcatVersion = "8.5.45"
ext.tyrusVersion = "1.3.5" // constrained by WebLogic 12.1.3 support
ext.undertowVersion = "1.3.33.Final"
ext.xmlunitVersion = "1.6"
@@ -983,6 +983,10 @@ project("spring-websocket") {
project("spring-test") {
description = "Spring TestContext Framework"

// Disable warning for annotation processing in order to get the
// build passing again after it mysteriously started failing in 2020.
compileJava.options.compilerArgs -= "-Xlint:processing"

dependencies {
compile(project(":spring-core"))
optional(project(":spring-aop"))
@@ -1273,14 +1277,14 @@ configure(rootProject) {
def Properties schemas = new Properties();

subproject.sourceSets.main.resources.find {
it.path.endsWith("META-INF/spring.schemas")
(it.path.endsWith("META-INF/spring.schemas") || it.path.endsWith("META-INF\\spring.schemas"))
}?.withInputStream { schemas.load(it) }

for (def key : schemas.keySet()) {
def shortName = key.replaceAll(/http.*schema.(.*).spring-.*/, '$1')
assert shortName != key
File xsdFile = subproject.sourceSets.main.resources.find {
it.path.endsWith(schemas.get(key))
(it.path.endsWith(schemas.get(key)) || it.path.endsWith(schemas.get(key).replaceAll('\\/','\\\\')))
}
assert xsdFile != null
into (shortName) {
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
@@ -1 +1 @@
version=4.3.25.BUILD-SNAPSHOT
version=4.3.26.RELEASE
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2019 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.
@@ -328,10 +328,11 @@ private Callback[] getCallbacks(Class<?> rootClass) throws Exception {

// TODO: small memory optimization here (can skip creation for methods with no advice)
for (int x = 0; x < methods.length; x++) {
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(methods[x], rootClass);
Method method = methods[x];
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, rootClass);
fixedCallbacks[x] = new FixedChainStaticTargetInterceptor(
chain, this.advised.getTargetSource().getTarget(), this.advised.getTargetClass());
this.fixedInterceptorMap.put(methods[x].toString(), x);
this.fixedInterceptorMap.put(methods.toString(), x);
}

// Now copy both the callbacks from mainCallbacks
@@ -614,8 +615,8 @@ public FixedChainStaticTargetInterceptor(List<Object> adviceChain, Object target

@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
MethodInvocation invocation = new CglibMethodInvocation(proxy, this.target, method, args,
this.targetClass, this.adviceChain, methodProxy);
MethodInvocation invocation = new CglibMethodInvocation(
proxy, this.target, method, args, this.targetClass, this.adviceChain, methodProxy);
// If we get here, we need to create a MethodInvocation.
Object retVal = invocation.proceed();
retVal = processReturnType(proxy, this.target, method, retVal);
@@ -783,7 +784,7 @@ public ProxyCallbackFilter(AdvisedSupport advised, Map<String, Integer> fixedInt
* <dt>For advised methods:</dt>
* <dd>If the target is static and the advice chain is frozen then a
* FixedChainStaticTargetInterceptor specific to the method is used to
* invoke the advice chain. Otherwise a DyanmicAdvisedInterceptor is
* invoke the advice chain. Otherwise a DynamicAdvisedInterceptor is
* used.</dd>
* <dt>For non-advised methods:</dt>
* <dd>Where it can be determined that the method will not return {@code this}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2015 the original author or authors.
* Copyright 2002-2019 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.
@@ -152,7 +152,7 @@ public void setArguments(Object... arguments) {

@Override
public Object proceed() throws Throwable {
// We start with an index of -1 and increment early.
// We start with an index of -1 and increment early.
if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
return invokeJoinpoint();
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2019 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,12 +24,15 @@
import org.springframework.util.Assert;

/**
* Convenient class for building up pointcuts. All methods return
* ComposablePointcut, so we can use a concise idiom like:
* Convenient class for building up pointcuts.
*
* {@code
* Pointcut pc = new ComposablePointcut().union(classFilter).intersection(methodMatcher).intersection(pointcut);
* }
* <p>All methods return {@code ComposablePointcut}, so we can use concise idioms
* like in the following example.
*
* <pre class="code">Pointcut pc = new ComposablePointcut()
* .union(classFilter)
* .intersection(methodMatcher)
* .intersection(pointcut);</pre>
*
* @author Rod Johnson
* @author Juergen Hoeller
@@ -199,7 +202,7 @@ public int hashCode() {

@Override
public String toString() {
return "ComposablePointcut: " + this.classFilter + ", " +this.methodMatcher;
return "ComposablePointcut: " + this.classFilter + ", " + this.methodMatcher;
}

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2019 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.
@@ -38,7 +38,7 @@
* @author Juergen Hoeller
* @since 11.11.2003
*/
@SuppressWarnings({"serial" })
@SuppressWarnings("serial")
public class DefaultIntroductionAdvisor implements IntroductionAdvisor, ClassFilter, Ordered, Serializable {

private final Advice advice;
@@ -81,25 +81,25 @@ public DefaultIntroductionAdvisor(Advice advice, IntroductionInfo introductionIn
/**
* Create a DefaultIntroductionAdvisor for the given advice.
* @param advice the Advice to apply
* @param intf the interface to introduce
* @param ifc the interface to introduce
*/
public DefaultIntroductionAdvisor(DynamicIntroductionAdvice advice, Class<?> intf) {
public DefaultIntroductionAdvisor(DynamicIntroductionAdvice advice, Class<?> ifc) {
Assert.notNull(advice, "Advice must not be null");
this.advice = advice;
addInterface(intf);
addInterface(ifc);
}


/**
* Add the specified interface to the list of interfaces to introduce.
* @param intf the interface to introduce
* @param ifc the interface to introduce
*/
public void addInterface(Class<?> intf) {
Assert.notNull(intf, "Interface must not be null");
if (!intf.isInterface()) {
throw new IllegalArgumentException("Specified class [" + intf.getName() + "] must be an interface");
public void addInterface(Class<?> ifc) {
Assert.notNull(ifc, "Interface must not be null");
if (!ifc.isInterface()) {
throw new IllegalArgumentException("Specified class [" + ifc.getName() + "] must be an interface");
}
this.interfaces.add(intf);
this.interfaces.add(ifc);
}

@Override
@@ -112,8 +112,8 @@ public void validateInterfaces() throws IllegalArgumentException {
for (Class<?> ifc : this.interfaces) {
if (this.advice instanceof DynamicIntroductionAdvice &&
!((DynamicIntroductionAdvice) this.advice).implementsInterface(ifc)) {
throw new IllegalArgumentException("DynamicIntroductionAdvice [" + this.advice + "] " +
"does not implement interface [" + ifc.getName() + "] specified for introduction");
throw new IllegalArgumentException("DynamicIntroductionAdvice [" + this.advice + "] " +
"does not implement interface [" + ifc.getName() + "] specified for introduction");
}
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2019 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,8 +26,9 @@
import org.springframework.util.PatternMatchUtils;

/**
* Pointcut bean for simple method name matches, as alternative to regexp patterns.
* Does not handle overloaded methods: all methods with a given name will be eligible.
* Pointcut bean for simple method name matches, as an alternative to regexp patterns.
*
* <p>Does not handle overloaded methods: all methods with a given name will be eligible.
*
* @author Juergen Hoeller
* @author Rod Johnson
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2015 the original author or authors.
* Copyright 2002-2019 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,7 +26,8 @@
/**
* Pointcut constants for matching getters and setters,
* and static methods useful for manipulating and evaluating pointcuts.
* These methods are particularly useful for composing pointcuts
*
* <p>These methods are particularly useful for composing pointcuts
* using the union and intersection methods.
*
* @author Rod Johnson
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2019 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.
@@ -61,7 +61,7 @@ public AnnotationMatchingPointcut(Class<? extends Annotation> classAnnotationTyp
}

/**
* Create a new AnnotationMatchingPointcut for the given annotation type.
* Create a new AnnotationMatchingPointcut for the given annotation types.
* @param classAnnotationType the annotation type to look for at the class level
* (can be {@code null})
* @param methodAnnotationType the annotation type to look for at the method level
@@ -119,7 +119,7 @@ public int hashCode() {

@Override
public String toString() {
return "AnnotationMatchingPointcut: " + this.classFilter + ", " +this.methodMatcher;
return "AnnotationMatchingPointcut: " + this.classFilter + ", " + this.methodMatcher;
}


Original file line number Diff line number Diff line change
@@ -318,7 +318,6 @@ public AbstractBeanDefinition bean(Class<?> type, Object...args) {
callable.call(this.currentBeanDefinition);
}
return this.currentBeanDefinition.getBeanDefinition();

}
finally {
this.currentBeanDefinition = current;
@@ -371,9 +370,9 @@ public Object invokeMethod(String name, Object arg) {
}
else if ("ref".equals(name)) {
String refName;
if (args[0] == null)
if (args[0] == null) {
throw new IllegalArgumentException("Argument to ref() is not a valid bean or was not found");

}
if (args[0] instanceof RuntimeBeanReference) {
refName = ((RuntimeBeanReference) args[0]).getBeanName();
}
@@ -492,11 +491,11 @@ else if (args[0] instanceof Map) {
Map.Entry factoryBeanEntry = (Map.Entry) ((Map) args[0]).entrySet().iterator().next();
// If we have a closure body, that will be the last argument.
// In between are the constructor args
int constructorArgsTest = hasClosureArgument?2:1;
int constructorArgsTest = (hasClosureArgument ? 2 : 1);
// If we have more than this number of args, we have constructor args
if (args.length > constructorArgsTest){
// factory-method requires args
int endOfConstructArgs = (hasClosureArgument? args.length - 1 : args.length);
int endOfConstructArgs = (hasClosureArgument ? args.length - 1 : args.length);
this.currentBeanDefinition = new GroovyBeanDefinitionWrapper(beanName, null,
resolveConstructorArguments(args, 1, endOfConstructArgs));
}
@@ -514,7 +513,7 @@ else if (args[0] instanceof Closure) {
}
else {
List constructorArgs = resolveConstructorArguments(args, 0, hasClosureArgument ? args.length - 1 : args.length);
currentBeanDefinition = new GroovyBeanDefinitionWrapper(beanName, null, constructorArgs);
this.currentBeanDefinition = new GroovyBeanDefinitionWrapper(beanName, null, constructorArgs);
}

if (hasClosureArgument) {
@@ -633,7 +632,7 @@ else if (value instanceof Closure) {

/**
* This method overrides property retrieval in the scope of the
* {@code GroovyBeanDefinitionReader} to either:
* {@code GroovyBeanDefinitionReader}. A property retrieval will either:
* <ul>
* <li>Retrieve a variable from the bean builder's binding if it exists
* <li>Retrieve a RuntimeBeanReference for a specific bean if it exists
@@ -684,8 +683,8 @@ else if (this.currentBeanDefinition != null) {
}

private GroovyDynamicElementReader createDynamicElementReader(String namespace) {
XmlReaderContext readerContext = this.groovyDslXmlBeanDefinitionReader.createReaderContext(new DescriptiveResource(
"Groovy"));
XmlReaderContext readerContext = this.groovyDslXmlBeanDefinitionReader.createReaderContext(
new DescriptiveResource("Groovy"));
BeanDefinitionParserDelegate delegate = new BeanDefinitionParserDelegate(readerContext);
boolean decorating = (this.currentBeanDefinition != null);
if (!decorating) {
@@ -779,7 +778,7 @@ public void setProperty(String property, Object newValue) {


/**
* Wraps a bean definition property an ensures that any RuntimeBeanReference
* Wraps a bean definition property and ensures that any RuntimeBeanReference
* additions to it are deferred for resolution later.
*/
private class GroovyPropertyValue extends GroovyObjectSupport {
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2019 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.
@@ -41,8 +41,8 @@

/**
* Decorator for a standard {@link BeanInfo} object, e.g. as created by
* {@link Introspector#getBeanInfo(Class)}, designed to discover and register static
* and/or non-void returning setter methods. For example:
* {@link Introspector#getBeanInfo(Class)}, designed to discover and register
* static and/or non-void returning setter methods. For example:
*
* <pre class="code">
* public class Bean {
@@ -216,7 +216,7 @@ private PropertyDescriptor findExistingPropertyDescriptor(String propertyName, C
}

private String propertyNameFor(Method method) {
return Introspector.decapitalize(method.getName().substring(3, method.getName().length()));
return Introspector.decapitalize(method.getName().substring(3));
}


@@ -464,7 +464,7 @@ public void setPropertyEditorClass(Class<?> propertyEditorClass) {
}

/*
* See java.beans.IndexedPropertyDescriptor#equals(java.lang.Object)
* See java.beans.IndexedPropertyDescriptor#equals
*/
@Override
public boolean equals(Object other) {
Loading