ContextConc.java
/*
* @copyright defined in LICENSE.txt
*/
package hera;
import static java.util.Collections.unmodifiableSet;
import static org.slf4j.LoggerFactory.getLogger;
import hera.annotation.ApiAudience;
import hera.annotation.ApiStability;
import hera.api.model.ChainIdHash;
import hera.util.Configuration;
import hera.util.conf.InMemoryConfiguration;
import java.util.HashSet;
import java.util.Set;
import lombok.Getter;
import lombok.ToString;
import org.slf4j.Logger;
@ApiAudience.Private
@ApiStability.Unstable
@ToString(exclude = {"logger", "parent"})
public final class ContextConc implements Context {
protected final Logger logger = getLogger(getClass());
protected final Context parent;
@Getter
protected final String scope;
@Getter
protected final ChainIdHash chainIdHash;
@Getter
protected final Configuration configuration;
protected final Set<Strategy> strategies;
protected ContextConc(final Context parent, final String scope, final ChainIdHash chainIdHash,
final Configuration configuration, final Set<Strategy> strageties) {
this.parent = parent;
this.scope = scope;
this.chainIdHash = chainIdHash;
this.configuration = (configuration instanceof InMemoryConfiguration
&& ((InMemoryConfiguration) configuration).isReadOnly()) ? configuration
: new InMemoryConfiguration(true, configuration);
this.strategies = unmodifiableSet(strageties);
}
@Override
public Context withScope(final String scope) {
logger.debug("New scope: {}", scope);
Context newContext = null;
if (isScopeBase()) {
newContext =
new ContextConc(this, scope, this.chainIdHash, this.configuration, this.strategies);
} else {
final Context newScopeBase = getScopeBase().withScope(scope);
newContext = new ContextConc(newScopeBase, scope, this.chainIdHash, this.configuration,
this.strategies);
}
logger.debug("New context: {}", newContext);
return newContext;
}
@Override
public Context popScope() {
logger.debug("Pop scope: {}", getScope());
Context newContext = null;
if (isScopeBase()) {
newContext = this.parent;
} else {
final Context scopeParent = getScopeParent();
newContext = new ContextConc(scopeParent,
scopeParent.getScope(), this.chainIdHash, this.configuration, this.strategies);
}
logger.debug("New context: {}", newContext);
return newContext;
}
@Override
public Context withChainIdHash(final ChainIdHash chainIdHash) {
logger.debug("New chain id hash: {}", chainIdHash);
final ContextConc newContext =
new ContextConc(this.parent, this.scope, chainIdHash, this.configuration, this.strategies);
logger.debug("New context: {}", newContext);
return newContext;
}
protected Context getScopeBase() {
return isScopeBase() ? this : this.parent;
}
protected Context getScopeParent() {
return isScopeBase() ? this.parent : ((ContextConc) this.parent).parent;
}
protected boolean isScopeBase() {
return this.configuration.asMap().isEmpty() && this.strategies.isEmpty();
}
@Override
public Context withKeyValue(final String key, final Object value) {
logger.debug("Define key: {}, value: {}", key, value);
final Configuration newConfiguration = new InMemoryConfiguration(getConfiguration());
newConfiguration.define(key, value);
return withConfiguration(newConfiguration);
}
@Override
public Context withConfiguration(final Configuration configuration) {
logger.debug("New configuration: {}", configuration);
final ContextConc newContext =
new ContextConc(this.parent, this.scope, this.chainIdHash, configuration, this.strategies);
logger.debug("New context: {}", newContext);
return newContext;
}
@Override
public Context withoutKey(final String key) {
logger.debug("Remove key: {}", key);
if (null == getConfiguration().get(key)) {
return this;
}
final Configuration newConfiguration = new InMemoryConfiguration(getConfiguration());
newConfiguration.remove(key);;
final ContextConc newContext =
new ContextConc(this.parent, this.scope, this.chainIdHash, newConfiguration,
this.strategies);
logger.debug("New context: {}", newContext);
return newContext;
}
@Override
public <StrategyT extends Strategy> Context withStrategy(final StrategyT strategy) {
logger.debug("New strategy: {}", strategy);
final Set<Strategy> newStrategies = new HashSet<Strategy>(this.strategies);
if (newStrategies.contains(strategy)) {
newStrategies.remove(strategy);
}
newStrategies.add(strategy);
return withStrategies(newStrategies);
}
@Override
public Context withStrategies(final Set<Strategy> strategies) {
logger.debug("New strategies: {}", strategies);
final ContextConc newContext =
new ContextConc(this.parent, this.scope, this.chainIdHash, this.configuration, strategies);
logger.debug("New context: {}", newContext);
return newContext;
}
@SuppressWarnings("unchecked")
@Override
public <StrategyT extends Strategy> StrategyT getStrategy(
final Class<StrategyT> strategyClass) {
StrategyT ret = null;
for (final Strategy strategy : getStrategies()) {
if (strategyClass.isInstance(strategy)) {
ret = (StrategyT) strategy;
break;
}
}
return ret;
}
@Override
public <StrategyT extends Strategy> Context withoutStrategy(final Class<StrategyT> strategy) {
final Set<Strategy> newStrategies = new HashSet<Strategy>(this.strategies);
for (final Strategy oldStrategy : this.strategies) {
if (strategy.isInstance(oldStrategy)) {
newStrategies.remove(oldStrategy);
}
}
return new ContextConc(this.parent, this.scope, this.chainIdHash, this.configuration,
newStrategies);
}
@Override
public Set<Strategy> getStrategies() {
final Set<Strategy> strategies = new HashSet<Strategy>(this.strategies);
for (final Strategy strategy : strategies) {
if (strategy instanceof ContextAware) {
((ContextAware) strategy).setContext(this);
}
}
return strategies;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((parent == null) ? 0 : parent.hashCode());
result = prime * result + ((scope == null) ? 0 : scope.hashCode());
result = prime * result + ((chainIdHash == null) ? 0 : chainIdHash.hashCode());
result = prime * result + ((configuration == null) ? 0 : configuration.hashCode());
result = prime * result + ((strategies == null) ? 0 : strategies.hashCode());
return result;
}
@Override
public boolean equals(final Object obj) {
if (null == obj) {
return false;
}
if (!obj.getClass().equals(getClass())) {
return false;
}
final ContextConc other = (ContextConc) obj;
// don't use lombok because of it
if (this.parent != other.parent) {
return false;
}
return ((null == this.chainIdHash) ? (null == other.chainIdHash)
: (this.chainIdHash.equals(other.chainIdHash)))
&& this.scope.equals(other.scope)
&& this.configuration.equals(other.configuration)
&& this.strategies.equals(other.strategies);
}
}