You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@polygene.apache.org by pa...@apache.org on 2016/12/03 14:52:26 UTC
[1/3] zest-java git commit: refine composite creation performance
tests
Repository: zest-java
Updated Branches:
refs/heads/develop 07f569983 -> 96080d0ac
refine composite creation performance tests
warmup runs, more iterations, pauses to let GC & JIT do their job
unsurprisingly, the results are now more accurate, realistic
measured plain object instantiation is now down to 1 nanosec
our instanciations are much slower than that
Project: http://git-wip-us.apache.org/repos/asf/zest-java/repo
Commit: http://git-wip-us.apache.org/repos/asf/zest-java/commit/e1dccf2b
Tree: http://git-wip-us.apache.org/repos/asf/zest-java/tree/e1dccf2b
Diff: http://git-wip-us.apache.org/repos/asf/zest-java/diff/e1dccf2b
Branch: refs/heads/develop
Commit: e1dccf2b6324e9541e74ffced9c3c0449fa9fbff
Parents: 07f5699
Author: Paul Merlin <pa...@apache.org>
Authored: Sat Dec 3 14:05:21 2016 +0100
Committer: Paul Merlin <pa...@apache.org>
Committed: Sat Dec 3 14:05:21 2016 +0100
----------------------------------------------------------------------
.../CompositeCreationPerformanceTest.java | 174 +++++++++++++------
1 file changed, 118 insertions(+), 56 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/zest-java/blob/e1dccf2b/tests/performance/src/perf/java/org/apache/zest/test/performance/runtime/composite/CompositeCreationPerformanceTest.java
----------------------------------------------------------------------
diff --git a/tests/performance/src/perf/java/org/apache/zest/test/performance/runtime/composite/CompositeCreationPerformanceTest.java b/tests/performance/src/perf/java/org/apache/zest/test/performance/runtime/composite/CompositeCreationPerformanceTest.java
index ad6c93a..e77a00f 100644
--- a/tests/performance/src/perf/java/org/apache/zest/test/performance/runtime/composite/CompositeCreationPerformanceTest.java
+++ b/tests/performance/src/perf/java/org/apache/zest/test/performance/runtime/composite/CompositeCreationPerformanceTest.java
@@ -17,21 +17,16 @@
*/
package org.apache.zest.test.performance.runtime.composite;
-import java.time.Duration;
-import java.time.Instant;
-import org.apache.zest.api.time.SystemTime;
-import org.junit.Test;
import org.apache.zest.api.activation.ActivationException;
-import org.apache.zest.api.composite.TransientBuilder;
import org.apache.zest.api.composite.TransientBuilderFactory;
import org.apache.zest.api.composite.TransientComposite;
import org.apache.zest.api.object.ObjectFactory;
-import org.apache.zest.api.value.ValueBuilder;
import org.apache.zest.api.value.ValueBuilderFactory;
import org.apache.zest.api.value.ValueComposite;
import org.apache.zest.bootstrap.AssemblyException;
import org.apache.zest.bootstrap.ModuleAssembly;
import org.apache.zest.bootstrap.SingletonAssembler;
+import org.junit.Test;
/**
* Tests performance of new composite creation.
@@ -40,7 +35,7 @@ public class CompositeCreationPerformanceTest
{
@Test
public void newInstanceForRegisteredCompositePerformance()
- throws ActivationException, AssemblyException
+ throws ActivationException, AssemblyException, InterruptedException
{
SingletonAssembler assembler = new SingletonAssembler()
{
@@ -53,70 +48,122 @@ public class CompositeCreationPerformanceTest
module.values( AnyValue.class );
}
};
- int loops = 2;
- long t0 = 0;
+ int warmups = 10;
+ int runs = 20;
+ long waitBeforeRun = 1000;
+ long waitBetweenRuns = 500;
+ long timeForJavaObject = 0;
{
- for( int i = 0; i < loops; i++ )
+ // Warmup
+ for( int i = 0; i < warmups; i++ )
+ {
+ testJavaObjectCreationPerformance( false );
+ }
+ Thread.sleep( waitBeforeRun );
+ // Run
+ for( int i = 0; i < runs; i++ )
{
- t0 = t0 + testJavaObjectCreationPerformance();
+ timeForJavaObject += testJavaObjectCreationPerformance( true );
+ Thread.sleep( waitBetweenRuns );
}
- t0 = t0 / loops;
+ timeForJavaObject = timeForJavaObject / runs;
}
- long t1 = 0;
+ long timeForTransientComposite = 0;
{
TransientBuilderFactory module = assembler.module();
- for( int i = 0; i < loops; i++ )
+ // Warmup
+ for( int i = 0; i < warmups; i++ )
{
- t1 = t1 + testCompositeCreationPerformance( module );
+ testCompositeCreationPerformance( module, false );
}
- t1 = t1 / loops;
+ Thread.sleep( waitBeforeRun );
+ // Run
+ for( int i = 0; i < runs; i++ )
+ {
+ timeForTransientComposite += testCompositeCreationPerformance( module, true );
+ Thread.sleep( waitBetweenRuns );
+ }
+ timeForTransientComposite = timeForTransientComposite / runs;
}
- long t2 = 0;
+ long timeForManagedObject = 0;
{
ObjectFactory objectFactory = assembler.module();
- for( int i = 0; i < loops; i++ )
+ // Warmup
+ for( int i = 0; i < warmups; i++ )
+ {
+ testObjectCreationPerformance( objectFactory, false );
+ }
+ Thread.sleep( waitBeforeRun );
+ // Run
+ for( int i = 0; i < runs; i++ )
{
- t2 = t2 + testObjectCreationPerformance( objectFactory );
+ timeForManagedObject += testObjectCreationPerformance( objectFactory, true );
+ Thread.sleep( waitBetweenRuns );
}
- t2 = t2 / loops;
+ timeForManagedObject = timeForManagedObject / runs;
}
- long t3 = 0;
+ long timeForValueComposite = 0;
{
ValueBuilderFactory valueBuilderFactory = assembler.module();
- for( int i = 0; i < loops; i++ )
+ // Warmup
+ for( int i = 0; i < warmups; i++ )
+ {
+ testValueCreationPerformance( valueBuilderFactory, false );
+ }
+ Thread.sleep( waitBeforeRun );
+ // Run
+ for( int i = 0; i < runs; i++ )
{
- t3 = t3 + testValueCreationPerformance( valueBuilderFactory );
+ timeForValueComposite += testValueCreationPerformance( valueBuilderFactory, true );
+ Thread.sleep( waitBetweenRuns );
}
- t3 = t3 / loops;
+ timeForValueComposite = timeForValueComposite / runs;
}
- long t4 = 0;
+ long timeForTransientCompositeBuilder = 0;
{
TransientBuilderFactory module = assembler.module();
- for( int i = 0; i < loops; i++ )
+ // Warmup
+ for( int i = 0; i < warmups; i++ )
{
- t4 = t4 + testCompositeCreationWithBuilderPerformance( module );
+ testCompositeCreationWithBuilderPerformance( module, false );
}
- t4 = t4 / loops;
+ Thread.sleep( waitBeforeRun );
+ // Run
+ for( int i = 0; i < runs; i++ )
+ {
+ timeForTransientCompositeBuilder += testCompositeCreationWithBuilderPerformance( module, true );
+ Thread.sleep( waitBetweenRuns );
+ }
+ timeForTransientCompositeBuilder = timeForTransientCompositeBuilder / runs;
}
- long t6 = 0;
+ long timeForValueCompositeBuilder = 0;
{
ValueBuilderFactory valueBuilderFactory = assembler.module();
- for( int i = 0; i < loops; i++ )
+ // Warmup
+ for( int i = 0; i < warmups; i++ )
+ {
+ testValueCreationWithBuilderPerformance( valueBuilderFactory, false );
+ }
+ Thread.sleep( waitBeforeRun );
+ // Run
+ for( int i = 0; i < runs; i++ )
{
- t6 = t6 + testValueCreationWithBuilderPerformance( valueBuilderFactory );
+ timeForValueCompositeBuilder += testValueCreationWithBuilderPerformance( valueBuilderFactory, true );
+ Thread.sleep( waitBetweenRuns );
}
- t6 = t6 / loops;
+ timeForValueCompositeBuilder = timeForValueCompositeBuilder / runs;
}
- System.out.println( "Transient: " + ( t1 / t0 ) + "x" );
- System.out.println( "TransientBuilder: " + ( t4 / t0 ) + "x" );
- System.out.println( "Value: " + ( t3 / t0 ) + "x" );
- System.out.println( "ValueBuilder: " + ( t6 / t0 ) + "x" );
- System.out.println( "Object: " + ( t2 / t0 ) + "x" );
+ System.out.println( "----" );
+ System.out.println( "Transient: " + ( timeForTransientComposite / timeForJavaObject ) + "x" );
+ System.out.println( "TransientBuilder: " + ( timeForTransientCompositeBuilder / timeForJavaObject ) + "x" );
+ System.out.println( "Value: " + ( timeForValueComposite / timeForJavaObject ) + "x" );
+ System.out.println( "ValueBuilder: " + ( timeForValueCompositeBuilder / timeForJavaObject ) + "x" );
+ System.out.println( "Object: " + ( timeForManagedObject / timeForJavaObject ) + "x" );
}
- private long testCompositeCreationPerformance( TransientBuilderFactory module )
+ private long testCompositeCreationPerformance( TransientBuilderFactory module, boolean run )
{
long start = System.currentTimeMillis();
int iter = 1000000;
@@ -127,27 +174,32 @@ public class CompositeCreationPerformanceTest
long end = System.currentTimeMillis();
long time = 1000000L * ( end - start ) / iter;
- System.out.println( "Minimum Composite Creation Time:" + time + " nanoseconds per composite" );
+ if( run )
+ {
+ System.out.println( "Composite Creation Time:" + time + " nanoseconds per composite" );
+ }
return time;
}
- private long testCompositeCreationWithBuilderPerformance( TransientBuilderFactory module )
+ private long testCompositeCreationWithBuilderPerformance( TransientBuilderFactory module, boolean run )
{
long start = System.currentTimeMillis();
int iter = 1000000;
for( int i = 0; i < iter; i++ )
{
- TransientBuilder<AnyComposite> builder = module.newTransientBuilder( AnyComposite.class );
- builder.newInstance();
+ module.newTransientBuilder( AnyComposite.class ).newInstance();
}
long end = System.currentTimeMillis();
long time = 1000000L * ( end - start ) / iter;
- System.out.println( "Minimum Composite (builder) Creation Time:" + time + " nanoseconds per composite" );
+ if( run )
+ {
+ System.out.println( "Composite (builder) Creation Time:" + time + " nanoseconds per composite" );
+ }
return time;
}
- private long testValueCreationPerformance( ValueBuilderFactory valueBuilderFactory )
+ private long testValueCreationPerformance( ValueBuilderFactory valueBuilderFactory, boolean run )
{
long start = System.currentTimeMillis();
int iter = 1000000;
@@ -158,27 +210,32 @@ public class CompositeCreationPerformanceTest
long end = System.currentTimeMillis();
long time = 1000000L * ( end - start ) / iter;
- System.out.println( "Minimum Value Creation Time:" + time + " nanoseconds per composite" );
+ if( run )
+ {
+ System.out.println( "Value Creation Time:" + time + " nanoseconds per composite" );
+ }
return time;
}
- private long testValueCreationWithBuilderPerformance( ValueBuilderFactory valueBuilderFactory )
+ private long testValueCreationWithBuilderPerformance( ValueBuilderFactory valueBuilderFactory, boolean run )
{
long start = System.currentTimeMillis();
int iter = 1000000;
for( int i = 0; i < iter; i++ )
{
- ValueBuilder<AnyValue> builder = valueBuilderFactory.newValueBuilder( AnyValue.class );
- builder.newInstance();
+ valueBuilderFactory.newValueBuilder( AnyValue.class ).newInstance();
}
long end = System.currentTimeMillis();
long time = 1000000L * ( end - start ) / iter;
- System.out.println( "Minimum Value (builder) Creation Time:" + time + " nanoseconds per composite" );
+ if( run )
+ {
+ System.out.println( "Value (builder) Creation Time:" + time + " nanoseconds per composite" );
+ }
return time;
}
- private long testObjectCreationPerformance( ObjectFactory objectFactory )
+ private long testObjectCreationPerformance( ObjectFactory objectFactory, boolean run )
{
long start = System.currentTimeMillis();
int iter = 1000000;
@@ -189,11 +246,14 @@ public class CompositeCreationPerformanceTest
long end = System.currentTimeMillis();
long time = 1000000L * ( end - start ) / iter;
- System.out.println( "Minimum Zest Object Creation Time:" + time + " nanoseconds per object" );
+ if( run )
+ {
+ System.out.println( "Zest Object Creation Time:" + time + " nanoseconds per object" );
+ }
return time;
}
- private long testJavaObjectCreationPerformance()
+ private long testJavaObjectCreationPerformance( boolean run )
{
long start = System.currentTimeMillis();
int iter = 1000000;
@@ -204,16 +264,19 @@ public class CompositeCreationPerformanceTest
long end = System.currentTimeMillis();
long time = 1000000L * ( end - start ) / iter;
- System.out.println( "Minimum Java Object Creation Time:" + time + " nanoseconds per object" );
+ if( run )
+ {
+ System.out.println( "Java Object Creation Time:" + time + " nanoseconds per object" );
+ }
return time;
}
- public static interface AnyComposite
+ public interface AnyComposite
extends TransientComposite
{
}
- public static interface AnyValue
+ public interface AnyValue
extends ValueComposite
{
}
@@ -221,5 +284,4 @@ public class CompositeCreationPerformanceTest
public static class AnyObject
{
}
-
}
[2/3] zest-java git commit: Improved descriptors sorting logic to
limit iterations on candidates list.
Posted by pa...@apache.org.
Improved descriptors sorting logic to limit iterations on candidates list.
Project: http://git-wip-us.apache.org/repos/asf/zest-java/repo
Commit: http://git-wip-us.apache.org/repos/asf/zest-java/commit/7ba5335c
Tree: http://git-wip-us.apache.org/repos/asf/zest-java/tree/7ba5335c
Diff: http://git-wip-us.apache.org/repos/asf/zest-java/diff/7ba5335c
Branch: refs/heads/develop
Commit: 7ba5335c3e5515821e9e6a9820843efd4a0579bb
Parents: e1dccf2
Author: Jean-Michel Tonneau <jm...@alchemytec.com>
Authored: Wed Nov 30 12:18:27 2016 +0000
Committer: Paul Merlin <pa...@apache.org>
Committed: Sat Dec 3 14:46:39 2016 +0100
----------------------------------------------------------------------
.../zest/runtime/structure/TypeLookupImpl.java | 102 ++++++++++++++++---
1 file changed, 86 insertions(+), 16 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/zest-java/blob/7ba5335c/core/runtime/src/main/java/org/apache/zest/runtime/structure/TypeLookupImpl.java
----------------------------------------------------------------------
diff --git a/core/runtime/src/main/java/org/apache/zest/runtime/structure/TypeLookupImpl.java b/core/runtime/src/main/java/org/apache/zest/runtime/structure/TypeLookupImpl.java
index cd077b0..5eac131 100644
--- a/core/runtime/src/main/java/org/apache/zest/runtime/structure/TypeLookupImpl.java
+++ b/core/runtime/src/main/java/org/apache/zest/runtime/structure/TypeLookupImpl.java
@@ -22,6 +22,7 @@ package org.apache.zest.runtime.structure;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.WildcardType;
+import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Predicate;
@@ -152,33 +153,20 @@ class TypeLookupImpl
@Override
public List<EntityDescriptor> lookupEntityModels( final Class type )
{
- return entityModels.computeIfAbsent( type, key ->
- concat(
- allEntities().filter( ref -> new ExactTypeMatching<>( key ).test( ref ) ),
- allEntities().filter( ref -> new AssignableFromTypeMatching<>( key ).test( ref ) )
- ).distinct().collect( toList() )
- );
+ return entityModels.computeIfAbsent( type, key -> new TypeMatchingDescriptors<EntityDescriptor>(key).selectedFrom(allEntities()));
}
@Override
public ModelDescriptor lookupServiceModel( Type serviceType )
{
return serviceModels.computeIfAbsent( serviceType,
- key -> lookupServiceModels( key ).stream().findFirst().orElse( null ) );
+ key -> new BestTypeMatchingDescriptors<ModelDescriptor>(key).selectedFrom(allServices()).bestMatchOrElse(null));
}
@Override
public List<? extends ModelDescriptor> lookupServiceModels( final Type type1 )
{
- return servicesReferences.computeIfAbsent( type1, type ->
- {
- // There is a requirement that "exact match" services must be returned before "assignable match"
- // services, hence the dual streams instead of a OR filter.
- return Stream.concat( allServices().filter( new ExactTypeMatching<>( type ) ),
- allServices().filter( new AssignableFromTypeMatching<>( type ) ) )
- .distinct()
- .collect( toList() );
- } );
+ return servicesReferences.computeIfAbsent( type1, type ->new TypeMatchingDescriptors<ModelDescriptor>(type).selectedFrom(allServices()));
}
@Override
@@ -450,4 +438,86 @@ class TypeLookupImpl
return value;
}
}
+
+ private static class TypeMatchingDescriptors<T extends HasTypes> extends ArrayList<T> {
+
+ /**
+ * mutable :-( But performance is an issue here.
+ */
+ private Integer lastMatchingindex;
+ private final ExactTypeMatching<T> exactMatchingPredicate;
+ private final AssignableFromTypeMatching<T> assignablePredicate;
+
+ private TypeMatchingDescriptors(Type type) {
+ this.lastMatchingindex = null;
+ this.exactMatchingPredicate = new ExactTypeMatching<>(type);
+ this.assignablePredicate = new AssignableFromTypeMatching<>(type);
+ }
+
+ TypeMatchingDescriptors<T> selectedFrom(Stream<? extends T> candidates){
+ candidates.forEach(this::smartAddition);
+ return this;
+ }
+ /**
+ * Sorts the descriptors in a common list : matching ones first,
+ * assignable ones follow. The order of arrival is important :
+ *
+ * "{assignable1, matching1, assignable2,assignable3,matching2,
+ * non-matching-or-assignable}" should result in "{ matching1,
+ * matching2, assignable1, assignable2, assignable3}"
+ */
+ private void smartAddition(T descriptor) {
+ if (contains(descriptor)) {
+ return;
+ }
+ if ( exactMatchingPredicate.test(descriptor)) {
+ Integer nextMatchingIdx = lastMatchingindex == null ? 0 : lastMatchingindex + 1;
+ add(nextMatchingIdx, descriptor);
+ lastMatchingindex = nextMatchingIdx;
+ return;
+ }
+ if (assignablePredicate.test(descriptor)) {
+ add(descriptor);
+ }
+ }
+
+ private boolean containsExactMatches() {
+ return lastMatchingindex != null;
+ }
+
+ }
+
+ private static class BestTypeMatchingDescriptors<T extends HasTypes> {
+
+ private TypeMatchingDescriptors<T> descriptors;
+
+ private BestTypeMatchingDescriptors(Type type) {
+ this(new TypeMatchingDescriptors<>(type));
+ }
+
+ private BestTypeMatchingDescriptors(TypeMatchingDescriptors<T> descriptors) {
+ this.descriptors = descriptors;
+ }
+
+ BestTypeMatchingDescriptors<T> selectedFrom(Stream<? extends T> candidates) {
+ candidates.forEach(this::smartAddition);
+ return this;
+ }
+
+ private T bestMatchOrElse(T or) {
+ return !descriptors.isEmpty() ? descriptors.get(0) : or;
+ }
+
+ /**
+ * We want the first matching if exists, the first assignable otherwise.
+ * While there is no matching descriptor, even if we found assignable
+ * ones, we keep searching in case the last element is a matching type.
+ */
+ private void smartAddition(T descriptor) {
+ if (!descriptors.containsExactMatches()) {
+ descriptors.smartAddition(descriptor);
+ }
+ }
+ }
+
}
[3/3] zest-java git commit: typelookup: refine Jean-Michel’s contribution
Posted by pa...@apache.org.
typelookup: refine Jean-Michel\u2019s contribution
formatting, naming, documentation
no behavior change
Closes #2
Project: http://git-wip-us.apache.org/repos/asf/zest-java/repo
Commit: http://git-wip-us.apache.org/repos/asf/zest-java/commit/96080d0a
Tree: http://git-wip-us.apache.org/repos/asf/zest-java/tree/96080d0a
Diff: http://git-wip-us.apache.org/repos/asf/zest-java/diff/96080d0a
Branch: refs/heads/develop
Commit: 96080d0ac4cb948c4890964fa64040586d337d9b
Parents: 7ba5335
Author: Paul Merlin <pa...@apache.org>
Authored: Sat Dec 3 14:40:49 2016 +0100
Committer: Paul Merlin <pa...@apache.org>
Committed: Sat Dec 3 15:51:53 2016 +0100
----------------------------------------------------------------------
.../zest/runtime/structure/TypeLookupImpl.java | 194 ++++++++++---------
1 file changed, 107 insertions(+), 87 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/zest-java/blob/96080d0a/core/runtime/src/main/java/org/apache/zest/runtime/structure/TypeLookupImpl.java
----------------------------------------------------------------------
diff --git a/core/runtime/src/main/java/org/apache/zest/runtime/structure/TypeLookupImpl.java b/core/runtime/src/main/java/org/apache/zest/runtime/structure/TypeLookupImpl.java
index 5eac131..a030a20 100644
--- a/core/runtime/src/main/java/org/apache/zest/runtime/structure/TypeLookupImpl.java
+++ b/core/runtime/src/main/java/org/apache/zest/runtime/structure/TypeLookupImpl.java
@@ -153,20 +153,26 @@ class TypeLookupImpl
@Override
public List<EntityDescriptor> lookupEntityModels( final Class type )
{
- return entityModels.computeIfAbsent( type, key -> new TypeMatchingDescriptors<EntityDescriptor>(key).selectedFrom(allEntities()));
+ return entityModels.computeIfAbsent(
+ type,
+ key -> new TypeMatchesSelector<EntityDescriptor>( key ).selectFrom( allEntities() ) );
}
@Override
public ModelDescriptor lookupServiceModel( Type serviceType )
{
- return serviceModels.computeIfAbsent( serviceType,
- key -> new BestTypeMatchingDescriptors<ModelDescriptor>(key).selectedFrom(allServices()).bestMatchOrElse(null));
+ return serviceModels.computeIfAbsent(
+ serviceType,
+ key -> new BestTypeMatchSelector<ModelDescriptor>( key ).selectFrom( allServices() )
+ .bestMatchOrElse( null ) );
}
@Override
- public List<? extends ModelDescriptor> lookupServiceModels( final Type type1 )
+ public List<? extends ModelDescriptor> lookupServiceModels( final Type type )
{
- return servicesReferences.computeIfAbsent( type1, type ->new TypeMatchingDescriptors<ModelDescriptor>(type).selectedFrom(allServices()));
+ return servicesReferences.computeIfAbsent(
+ type,
+ key -> new TypeMatchesSelector<ModelDescriptor>( key ).selectFrom( allServices() ) );
}
@Override
@@ -400,6 +406,102 @@ class TypeLookupImpl
}
/**
+ * Selects descriptors by combining {@link ExactTypeMatching} and {@link AssignableFromTypeMatching}.
+ *
+ * Selected descriptors are sorted, exact matches first, assignable ones second.
+ * Other than that, original order is preserved.
+ *
+ * <code>
+ * [ assignable1, matching1, assignable2, assignable3, matching2, non-matching-nor-assignable ]
+ * </code>
+ * results in
+ * <code>
+ * [ matching1, matching2, assignable1, assignable2, assignable3 ]
+ * </code>
+ *
+ * @param <T> Descriptor type
+ */
+ private static class TypeMatchesSelector<T extends HasTypes> extends ArrayList<T>
+ {
+ private final ExactTypeMatching<T> exactMatchPredicate;
+ private final AssignableFromTypeMatching<T> assignablePredicate;
+ private Integer lastMatchIndex;
+
+ private TypeMatchesSelector( Type type )
+ {
+ this.exactMatchPredicate = new ExactTypeMatching<>( type );
+ this.assignablePredicate = new AssignableFromTypeMatching<>( type );
+ }
+
+ List<T> selectFrom( Stream<? extends T> candidates )
+ {
+ candidates.forEach( this::addDescriptor );
+ return this;
+ }
+
+ private void addDescriptor( T descriptor )
+ {
+ if( contains( descriptor ) )
+ {
+ return;
+ }
+ if( exactMatchPredicate.test( descriptor ) )
+ {
+ Integer nextMatchIndex = lastMatchIndex == null ? 0 : lastMatchIndex + 1;
+ add( nextMatchIndex, descriptor );
+ lastMatchIndex = nextMatchIndex;
+ }
+ else if( assignablePredicate.test( descriptor ) )
+ {
+ add( descriptor );
+ }
+ }
+
+ boolean containsExactMatches()
+ {
+ return lastMatchIndex != null;
+ }
+ }
+
+ /**
+ * Selects the best matching descriptor by combining {@link ExactTypeMatching} and {@link AssignableFromTypeMatching}.
+ *
+ * Selected descriptor is the first exact match if it exists, the first assignable otherwise.
+ *
+ * @param <T> Descriptor type
+ */
+ private static class BestTypeMatchSelector<T extends HasTypes>
+ {
+ private TypeMatchesSelector<T> descriptors;
+
+ BestTypeMatchSelector( Type type )
+ {
+ this.descriptors = new TypeMatchesSelector<>( type );
+ }
+
+ BestTypeMatchSelector<T> selectFrom( Stream<? extends T> candidates )
+ {
+ candidates.forEach( this::addDescriptor );
+ return this;
+ }
+
+ T bestMatchOrElse( T or )
+ {
+ return !descriptors.isEmpty() ? descriptors.get( 0 ) : or;
+ }
+
+ private void addDescriptor( T descriptor )
+ {
+ // Until an exact match is found, even if we already found assignable ones,
+ // keep selecting in case the last element is an exact match.
+ if( !descriptors.containsExactMatches() )
+ {
+ descriptors.addDescriptor( descriptor );
+ }
+ }
+ }
+
+ /**
* This Predicate will filter out all Models that doesn't have the same visibility as the first one.
*/
private static class SameVisibility<T extends ModelDescriptor>
@@ -438,86 +540,4 @@ class TypeLookupImpl
return value;
}
}
-
- private static class TypeMatchingDescriptors<T extends HasTypes> extends ArrayList<T> {
-
- /**
- * mutable :-( But performance is an issue here.
- */
- private Integer lastMatchingindex;
- private final ExactTypeMatching<T> exactMatchingPredicate;
- private final AssignableFromTypeMatching<T> assignablePredicate;
-
- private TypeMatchingDescriptors(Type type) {
- this.lastMatchingindex = null;
- this.exactMatchingPredicate = new ExactTypeMatching<>(type);
- this.assignablePredicate = new AssignableFromTypeMatching<>(type);
- }
-
- TypeMatchingDescriptors<T> selectedFrom(Stream<? extends T> candidates){
- candidates.forEach(this::smartAddition);
- return this;
- }
- /**
- * Sorts the descriptors in a common list : matching ones first,
- * assignable ones follow. The order of arrival is important :
- *
- * "{assignable1, matching1, assignable2,assignable3,matching2,
- * non-matching-or-assignable}" should result in "{ matching1,
- * matching2, assignable1, assignable2, assignable3}"
- */
- private void smartAddition(T descriptor) {
- if (contains(descriptor)) {
- return;
- }
- if ( exactMatchingPredicate.test(descriptor)) {
- Integer nextMatchingIdx = lastMatchingindex == null ? 0 : lastMatchingindex + 1;
- add(nextMatchingIdx, descriptor);
- lastMatchingindex = nextMatchingIdx;
- return;
- }
- if (assignablePredicate.test(descriptor)) {
- add(descriptor);
- }
- }
-
- private boolean containsExactMatches() {
- return lastMatchingindex != null;
- }
-
- }
-
- private static class BestTypeMatchingDescriptors<T extends HasTypes> {
-
- private TypeMatchingDescriptors<T> descriptors;
-
- private BestTypeMatchingDescriptors(Type type) {
- this(new TypeMatchingDescriptors<>(type));
- }
-
- private BestTypeMatchingDescriptors(TypeMatchingDescriptors<T> descriptors) {
- this.descriptors = descriptors;
- }
-
- BestTypeMatchingDescriptors<T> selectedFrom(Stream<? extends T> candidates) {
- candidates.forEach(this::smartAddition);
- return this;
- }
-
- private T bestMatchOrElse(T or) {
- return !descriptors.isEmpty() ? descriptors.get(0) : or;
- }
-
- /**
- * We want the first matching if exists, the first assignable otherwise.
- * While there is no matching descriptor, even if we found assignable
- * ones, we keep searching in case the last element is a matching type.
- */
- private void smartAddition(T descriptor) {
- if (!descriptors.containsExactMatches()) {
- descriptors.smartAddition(descriptor);
- }
- }
- }
-
}