Skip to content

Commit

Permalink
[fix] fix to return first registered parent class if exact class not …
Browse files Browse the repository at this point in the history
…exist
  • Loading branch information
rh-id committed Feb 24, 2024
1 parent 4f94823 commit 4bed19c
Show file tree
Hide file tree
Showing 5 changed files with 128 additions and 62 deletions.
124 changes: 64 additions & 60 deletions provider/src/main/java/m/co/rh/id/aprovider/DefaultProvider.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@

import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.Set;
import java.util.concurrent.ExecutorService;

import co.rh.id.lib.concurrent_utils.concurrent.executor.WeightedThreadPool;
Expand All @@ -31,7 +31,7 @@ private static synchronized ExecutorService initExecutorService() {
}

private Context mContext;
private Map<Class, Object> mObjectMap;
private Set<ProviderRegister> mRegistry;
private List<ProviderModule> mModuleList;
private List<LazyFutureProviderRegister> mAsyncRegisterList;
private ExecutorService mExecutorService;
Expand All @@ -54,8 +54,8 @@ private static synchronized ExecutorService initExecutorService() {

DefaultProvider(Context context, ProviderModule rootModule, ExecutorService executorService, boolean autoStart) {
mContext = context;
mObjectMap = new ConcurrentHashMap<>();
mObjectMap.put(ProviderRegistry.class, this);
mRegistry = new LinkedHashSet<>();
mRegistry.add(new SingletonProviderRegister(ProviderRegistry.class, () -> this));
mModuleList = Collections.synchronizedList(new ArrayList<>());
mAsyncRegisterList = Collections.synchronizedList(new ArrayList<>());
mExecutorService = executorService;
Expand All @@ -65,17 +65,34 @@ private static synchronized ExecutorService initExecutorService() {
}
}

private Object getValue(Class clazz) {
Object val = null;
for (ProviderRegister providerRegister : mRegistry) {
Class type = providerRegister.getType();
if (type.getName().equals(clazz.getName())) {
val = providerRegister.get();
break;
}
}
return val;
}

@Override
public <I> I get(Class<I> clazz) {
Object result = mObjectMap.get(clazz);
Object result = getValue(clazz);
if (result != null) {
return processObject(result);
}
for (Map.Entry<Class, Object> entry : mObjectMap.entrySet()) {
if (clazz.isAssignableFrom(entry.getKey())) {
return processObject(entry.getValue());
} else if (clazz.isInstance(entry.getValue())) {
return processObject(entry.getValue());
for (ProviderRegister providerRegister : mRegistry) {
if (clazz.isAssignableFrom(providerRegister.getType())) {
return processObject(providerRegister.get());
} else if (
(!(providerRegister instanceof LazyFutureProviderRegister) &&
!(providerRegister instanceof LazySingletonProviderRegister)
)
&&
clazz.isInstance(providerRegister.get())) {
return processObject(providerRegister.get());
}
}
throw new ProviderNullPointerException(clazz.getName() + " not found");
Expand All @@ -96,21 +113,23 @@ public <I> I tryGet(Class<I> clazz) {
@Override
public <I> ProviderValue<I> lazyGet(Class<I> clazz) {
// check existence of the object without processing ProviderRegister
if (!mObjectMap.containsKey(clazz)) {
boolean classFound = false;
for (Map.Entry<Class, Object> entry : mObjectMap.entrySet()) {
if (clazz.isAssignableFrom(entry.getKey())) {
classFound = true;
break;
} else if (clazz.isInstance(entry.getValue())) {
classFound = true;
break;
}
}
if (!classFound) {
throw new ProviderNullPointerException(clazz.getName() + " not found");
boolean classFound = false;
for (ProviderRegister providerRegister : mRegistry) {
Class type = providerRegister.getType();
if (clazz.getName().equals(type.getName())) {
classFound = true;
break;
} else if (clazz.isAssignableFrom(type)) {
classFound = true;
break;
} else if (clazz.isInstance(providerRegister.get())) {
classFound = true;
break;
}
}
if (!classFound) {
throw new ProviderNullPointerException(clazz.getName() + " not found");
}
return new CachedProviderValue<>(() -> get(clazz));
}

Expand Down Expand Up @@ -138,15 +157,14 @@ public synchronized void dispose() {
mModuleList = null;
}
final Context disposeContext = mContext;
for (Map.Entry<Class, Object> entry : mObjectMap.entrySet()) {
Object object = entry.getValue();
if (object instanceof ProviderDisposable) {
for (ProviderRegister entry : mRegistry) {
if (entry instanceof ProviderDisposable) {
mExecutorService.execute(() ->
((ProviderDisposable) object).dispose(disposeContext));
entry.dispose(disposeContext));
}
}
mObjectMap.clear();
mObjectMap = null;
mRegistry.clear();
mRegistry = null;
mAsyncRegisterList.clear();
mAsyncRegisterList = null;
mExecutorService = null;
Expand All @@ -169,39 +187,35 @@ public void registerModule(ProviderModule providerModule) {
@Override
public <I> void register(Class<I> clazz, ProviderValue<I> providerValue) {
checkDisposed();
putValue(clazz, new SingletonProviderRegister<>(clazz, providerValue));
putValue(new SingletonProviderRegister<>(clazz, providerValue));
}

@Override
public <I> void registerLazy(Class<I> clazz, ProviderValue<I> providerValue) {
register(new LazySingletonProviderRegister<>(clazz, providerValue));
checkDisposed();
putValue(new LazySingletonProviderRegister<>(clazz, providerValue));
}

@Override
public <I> void registerAsync(Class<I> clazz, ProviderValue<I> providerValue) {
checkDisposed();
LazyFutureProviderRegister providerRegister = new LazyFutureProviderRegister(clazz, providerValue, mExecutorService);
boolean registered = register(providerRegister);
boolean registered = putValue(providerRegister);
if (registered) {
mAsyncRegisterList.add(providerRegister);
}
}

@Override
public <I> void registerFactory(Class<I> clazz, ProviderValue<I> providerValue) {
register(new FactoryProviderRegister<>(clazz, providerValue, mContext));
checkDisposed();
putValue(new FactoryProviderRegister<>(clazz, providerValue, mContext));
}

@Override
public <I> void registerPool(Class<I> clazz, ProviderValue<I> providerValue) {
register(new PoolProviderRegister<>(clazz, providerValue, mExecutorService));
}

private <I> I exactGet(Class<I> clazz) {
Object result = mObjectMap.get(clazz);
if (result != null) {
return processObject(result);
}
throw new ProviderNullPointerException(clazz.getName() + " not found");
checkDisposed();
putValue(new PoolProviderRegister<>(clazz, providerValue, mExecutorService));
}

private synchronized void checkDisposed() {
Expand All @@ -210,31 +224,21 @@ private synchronized void checkDisposed() {
}
}

private <I> boolean register(ProviderRegister<I> providerRegister) {
checkDisposed();
return putValue(providerRegister.getType(), providerRegister);
}

private <I> I processObject(Object result) {
if (result instanceof ProviderRegister) {
return (I) ((ProviderRegister) result).get();
}
return (I) result;
}

private <I> boolean putValue(Class<I> clazz, ProviderRegister<I> implementation) {
I tryGetResult = null;
try {
tryGetResult = exactGet(clazz);
} catch (ProviderNullPointerException e) {
// leave blank
private <I> boolean putValue(ProviderRegister<I> implementation) {
Class clazz = implementation.getType();
boolean added;
if (implementation instanceof SingletonProviderRegister) {
implementation.get();
}
if (tryGetResult == null) {
if (implementation instanceof SingletonProviderRegister) {
mObjectMap.put(clazz, implementation.get());
} else {
mObjectMap.put(clazz, implementation);
}
added = mRegistry.add(implementation);
if (added) {
return true;
} else {
if (skipSameType) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package m.co.rh.id.aprovider;

import java.util.Objects;

/**
* Provider member
*/
abstract class ProviderRegister<I> implements ProviderValue<I> {
abstract class ProviderRegister<I> implements ProviderValue<I>, ProviderDisposable {
private ProviderValue<I> mProviderValue;
private Class<I> mType;

Expand All @@ -19,4 +21,16 @@ public ProviderValue<I> getProviderValue() {
public Class<I> getType() {
return mType;
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof ProviderRegister<?> that)) return false;
return Objects.equals(mType.getName(), that.mType.getName());
}

@Override
public int hashCode() {
return mType.getName().hashCode();
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package m.co.rh.id.aprovider;

import android.content.Context;

/**
* Helper class to register singleton to the provider
*/
Expand All @@ -17,4 +19,12 @@ public synchronized I get() {
}
return mValue;
}

@Override
public synchronized void dispose(Context context) {
if (mValue instanceof ProviderDisposable) {
((ProviderDisposable) mValue).dispose(context);
mValue = null;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,12 @@
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import co.rh.id.lib.concurrent_utils.concurrent.executor.WeightedThreadPool;
import m.co.rh.id.aprovider.test.IServiceA;
import m.co.rh.id.aprovider.test.IServiceA1;
import m.co.rh.id.aprovider.test.IServiceB;
Expand Down Expand Up @@ -173,6 +176,22 @@ public void singleton_registerSameClass() {
});
}

@Test
public void singleton_registerWithSameParentClass_returnFirstRegistered() {
WeightedThreadPool executorService1 = new WeightedThreadPool();
ScheduledExecutorService executorService2 = Executors.newSingleThreadScheduledExecutor();
ThreadPoolExecutor executorService3 = new ThreadPoolExecutor(1,1,1,TimeUnit.SECONDS, new LinkedBlockingQueue<>());
Provider p = Provider.createProvider(mockContext, (providerRegistry, provider) -> {
providerRegistry.register(WeightedThreadPool.class, () -> executorService1);
providerRegistry.register(ScheduledExecutorService.class, () -> executorService2);
providerRegistry.register(ThreadPoolExecutor.class, () -> executorService3);
});

ExecutorService result = p.get(ExecutorService.class);
assertTrue(result instanceof ExecutorService);
assertTrue(result instanceof WeightedThreadPool);
assertSame(executorService1, result);
}
@Test
public void module_registrationAndGet() {
Provider testProvider = Provider.createNestedProvider("test",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,12 @@
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import co.rh.id.lib.concurrent_utils.concurrent.executor.WeightedThreadPool;
import m.co.rh.id.aprovider.test.IServiceA;
import m.co.rh.id.aprovider.test.IServiceA1;
import m.co.rh.id.aprovider.test.IServiceB;
Expand Down Expand Up @@ -151,7 +154,6 @@ public void singleton_registrationAndLazyGetAndTryLazyGetDifference() {
// the value is null but not throwing null pointer exception
assertNull(tryLazyGet.get());

ServiceAImpl serviceA1 = new ServiceAImpl();
testProvider.register(IServiceA.class, () -> serviceA);

// after register the get should contain value
Expand All @@ -172,6 +174,23 @@ public void singleton_registerSameClass() {
});
}

@Test
public void singleton_registerWithSameParentClass_returnFirstRegistered() {
WeightedThreadPool executorService1 = new WeightedThreadPool();
ScheduledExecutorService executorService2 = Executors.newSingleThreadScheduledExecutor();
ThreadPoolExecutor executorService3 = new ThreadPoolExecutor(1,1,1,TimeUnit.SECONDS, new LinkedBlockingQueue<>());
Provider p = Provider.createProvider(mockContext, (providerRegistry, provider) -> {
providerRegistry.register(WeightedThreadPool.class, () -> executorService1);
providerRegistry.register(ScheduledExecutorService.class, () -> executorService2);
providerRegistry.register(ThreadPoolExecutor.class, () -> executorService3);
});

ExecutorService result = p.get(ExecutorService.class);
assertTrue(result instanceof ExecutorService);
assertTrue(result instanceof WeightedThreadPool);
assertSame(executorService1, result);
}

@Test
public void module_registrationAndGet() {
Provider testProvider = Provider.createProvider(mockContext,
Expand Down

0 comments on commit 4bed19c

Please sign in to comment.