View
@@ -1,7 +1,6 @@
package com.zhuinden.simplestack;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import com.zhuinden.statebundle.StateBundle;
@@ -52,44 +51,63 @@ void setScopedServices(ScopedServices scopedServices) {
this.scopedServices = scopedServices;
}
private void buildScope(Object key, String scopeTag) {
//noinspection ConstantConditions
if(scopeTag == null) {
throw new IllegalArgumentException("Scope tag provided by scope key cannot be null!");
}
if(!scopes.containsKey(scopeTag)) {
Map<String, Object> scope = new LinkedHashMap<>();
scopes.put(scopeTag, scope);
scopedServices.bindServices(new ScopedServices.ServiceBinder(this, key, scopeTag, scope));
for(Map.Entry<String, Object> serviceEntry : scope.entrySet()) {
String serviceTag = serviceEntry.getKey();
Object service = serviceEntry.getValue();
if(rootBundle.containsKey(scopeTag)) {
if(service instanceof Bundleable) {
StateBundle scopeBundle = rootBundle.getBundle(scopeTag);
if(scopeBundle != null && scopeBundle.containsKey(serviceTag)) {
((Bundleable) service).fromBundle(scopeBundle.getBundle(serviceTag));
}
}
}
if(service instanceof ScopedServices.Scoped) {
((ScopedServices.Scoped) service).onEnterScope(scopeTag);
}
}
}
}
private List<Object> latestState = null;
void buildScopes(List<Object> newState) {
this.latestState = newState;
for(Object key : newState) {
if(key instanceof ScopeKey.Child) {
ScopeKey.Child child = (ScopeKey.Child) key;
checkParentScopes(child);
for(String parent : child.getParentScopes()) {
buildScope(key, parent);
}
}
if(key instanceof ScopeKey) {
ScopeKey scopeKey = (ScopeKey) key;
String scopeTag = scopeKey.getScopeTag();
//noinspection ConstantConditions
if(scopeKey == null) {
throw new IllegalArgumentException("Scope tag provided by scope key cannot be null!");
}
if(!scopes.containsKey(scopeTag)) {
Map<String, Object> scope = new LinkedHashMap<>();
scopes.put(scopeTag, scope);
scopedServices.bindServices(new ScopedServices.ServiceBinder(this, key, scopeTag, scope));
for(Map.Entry<String, Object> serviceEntry : scope.entrySet()) {
String serviceTag = serviceEntry.getKey();
Object service = serviceEntry.getValue();
if(rootBundle.containsKey(scopeTag)) {
if(service instanceof Bundleable) {
StateBundle scopeBundle = rootBundle.getBundle(scopeTag);
if(scopeBundle != null && scopeBundle.containsKey(serviceTag)) {
((Bundleable) service).fromBundle(scopeBundle.getBundle(serviceTag));
}
}
}
if(service instanceof ScopedServices.Scoped) {
((ScopedServices.Scoped) service).onEnterScope(scopeTag);
}
}
}
buildScope(key, scopeTag);
}
}
}
void clearScopesNotIn(List<Object> newState) {
Set<String> currentScopes = new LinkedHashSet<>();
for(Object key : newState) {
if(key instanceof ScopeKey.Child) {
ScopeKey.Child child = (ScopeKey.Child) key;
checkParentScopes(child);
currentScopes.addAll(child.getParentScopes());
}
if(key instanceof ScopeKey) {
ScopeKey scopeKey = (ScopeKey) key;
currentScopes.add(scopeKey.getScopeTag());
@@ -127,8 +145,8 @@ void destroyScope(String scopeTag) {
}
}
void dispatchActivation(@Nullable String previousScopeTag, @Nullable String newScopeTag) {
if(newScopeTag != null) {
void dispatchActivation(@NonNull Set<String> scopesToDeactivate, @NonNull Set<String> scopesToActivate) {
for(String newScopeTag : scopesToActivate) {
if(!scopes.containsKey(newScopeTag)) {
throw new AssertionError(
"The new scope should exist, but it doesn't! This shouldn't happen. If you see this error, this functionality is broken.");
@@ -141,7 +159,7 @@ void dispatchActivation(@Nullable String previousScopeTag, @Nullable String newS
}
}
if(previousScopeTag != null) {
for(String previousScopeTag : scopesToDeactivate) {
if(!scopes.containsKey(previousScopeTag)) {
throw new AssertionError(
"The previous scope should exist, but it doesn't! This shouldn't happen. If you see this error, this functionality is broken.");
@@ -243,7 +261,25 @@ boolean canFindService(@NonNull String serviceTag) {
if(serviceTag == null) {
throw new IllegalArgumentException("Service tag cannot be null!");
}
List<String> activeScopes = getActiveScopesReverse();
Set<String> activeScopes = new LinkedHashSet<>();
List<Object> latestState = this.latestState;
for(int i = latestState.size() - 1; i >= 0; i--) {
Object key = latestState.get(i);
if(key instanceof ScopeKey) {
ScopeKey scopeKey = (ScopeKey) key;
activeScopes.add(scopeKey.getScopeTag());
}
if(key instanceof ScopeKey.Child) {
ScopeKey.Child child = (ScopeKey.Child) key;
checkParentScopes(child);
List<String> parentScopes = child.getParentScopes();
for(int j = parentScopes.size() - 1; j >= 0; j--) {
activeScopes.add(parentScopes.get(j));
}
}
}
for(String scopeTag : activeScopes) {
if(hasService(scopeTag, serviceTag)) {
return getService(scopeTag, serviceTag);
@@ -255,4 +291,11 @@ boolean canFindService(@NonNull String serviceTag) {
"and that you've bound and are trying to lookup the service with the correct service tag. " +
"Otherwise, it is likely that the scope you intend to inherit the service from does not exist.");
}
static void checkParentScopes(ScopeKey.Child child) {
//noinspection ConstantConditions
if(child.getParentScopes() == null) {
throw new IllegalArgumentException("Parent scopes cannot be null!");
}
}
}
View
@@ -304,6 +304,17 @@ public static boolean hasService(@NonNull Context context, @NonNull ScopeKey sco
return getManager(context).hasService(scopeKey, serviceTag);
}
/**
* Returns if a service is bound to the scope identified by the provided tag.
*
* @param scopeTag the scope tag
* @param serviceTag the service tag
* @return whether the service is bound in the given scope
*/
public static boolean hasService(@NonNull Context context, @NonNull String scopeTag, @NonNull String serviceTag) {
return getManager(context).hasService(scopeTag, serviceTag);
}
/**
* Returns the service bound to the scope of the {@link ScopeKey} by the provided tag.
*
@@ -317,6 +328,19 @@ public static boolean hasService(@NonNull Context context, @NonNull ScopeKey sco
return getManager(context).getService(scopeKey, serviceTag);
}
/**
* Returns the service bound to the scope identified by the provided tag.
*
* @param scopeTag the scope tag
* @param serviceTag the service tag
* @param <T> the type of the service
* @return the service
*/
@NonNull
public static <T> T getService(@NonNull Context context, @NonNull String scopeTag, @NonNull String serviceTag) {
return getManager(context).getService(scopeTag, serviceTag);
}
/**
* Attempts to look-up the service in all currently existing scopes, starting from the last added scope.
* Returns whether the service exists in any scopes.
View

Large diffs are not rendered by default.

Oops, something went wrong.
View
@@ -23,6 +23,6 @@
* Created by Owner on 2017. 01. 17..
*/
@RunWith(Suite.class)
@Suite.SuiteClasses({StateChangerTest.class, FlowTest.class, ReentranceTest.class, BackstackTest.class, HistoryBuilderTest.class, BackstackDelegateTest.class, BackstackManagerTest.class, HistoryTest.class, ScopingTest.class})
@Suite.SuiteClasses({StateChangerTest.class, FlowTest.class, ReentranceTest.class, BackstackTest.class, HistoryBuilderTest.class, BackstackDelegateTest.class, BackstackManagerTest.class, HistoryTest.class, ScopingTest.class, ScopingExplicitParentsTest.class})
public class TestSuite {
}