package detective.core.dsl; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.collect.ImmutableMap; import detective.core.Parameters; import detective.core.exception.ImmutableElementException; import detective.core.services.DetectiveFactory; /** * Create a tree like parameters. * always try to get/set parent parameter first. * * @author James Luo * */ public class ParametersImpl implements Parameters{ private static final Logger logger = LoggerFactory.getLogger(ParametersImpl.class); private Parameters parent; public static Parameters EMPTY_PARAMETERS = new ParametersImpl(); public Parameters getParent() { return parent; } public void setParent(Parameters parent) { this.parent = parent; } private boolean immutable = false; public ParametersImpl(){ this(null); } public ParametersImpl(Parameters parent){ map = new ConcurrentHashMap<String, Object>(); this.parent = parent; } public static ParametersImpl createFromMap(Map<String, Object> map){ ParametersImpl result = new ParametersImpl(); for (String key : map.keySet()){ result.put(key, map.get(key)); } return result; } private final Map<String, Object> map; @Override public String toString() { return "Parameters [parent=" + parent + ", current =" + map + "]"; } @Override public Object get(String key) { if (parent != null && parent.containsKey(key)){ return parent.get(key); } Object obj = map.get(key); if (obj != null && obj instanceof WrappedObject){ return getWrappedValue((WrappedObject<?>)obj); } if (obj == null) obj = DetectiveFactory.INSTANCE.getParametersConfig().getUnwrappered(key); return obj; } private Object getWrappedValue(WrappedObject<?> obj){ Object unwrapped = obj.getValue(); if (unwrapped instanceof WrappedObject) return getWrappedValue((WrappedObject<?>)unwrapped); else return unwrapped; } @SuppressWarnings({"rawtypes", "unchecked"}) @Override public Object put(String key, Object value) { if (immutable) throw new ImmutableElementException("Parameter is immutable, you may try to setup a parameter which comes from global settings. Key:" + key); if (parent != null && parent.containsKey(key)){ return parent.put(key, value); } Object oldValue = map.get(key); if (oldValue != null && oldValue instanceof SharedVariable){ if (this.isUnbind(oldValue)){ ((SharedVariable)oldValue).setValue(value); return oldValue; }else{ //throw new DslException("Shared data [" + key + "] can only setup once."); logger.info("As shared data [" + key + "] can only setup once, your value ignored."); } } return map.put(key, value); } @Override public Set<String> keySet() { if (parent == null) return map.keySet(); else{ Set<String> keys = new HashSet<String>(); keys.addAll(parent.keySet()); keys.addAll(map.keySet()); return keys; } } @Override public int size() { if (parent == null) return map.size(); else return parent.size() + map.size(); } @Override public boolean isEmpty() { if (parent == null) return map.isEmpty(); else return parent.isEmpty() && map.isEmpty(); } @Override public boolean containsKey(String key) { if (parent == null) return map.containsKey(key); else return parent.containsKey(key) || map.containsKey(key); } @Override public Parameters clone() { Parameters p = new ParametersImpl(); p.putAllUnwrappered(this); p.setImmutable(this.isImmutable()); return p; } @Override public void putAll(Parameters parameters) { for (String key : parameters.keySet()){ this.put(key, parameters.get(key)); } } @Override public void putAllUnwrappered(Parameters parameters) { if (immutable) throw new ImmutableElementException("Parameter is immutable, you may try to setup a parameter which comes from global settings."); for (String key : parameters.keySet()){ //map.put(key, parameters.getUnwrappered(key)); this.put(key, parameters.getUnwrappered(key)); } } @Override public Object remove(String key) { if (immutable) throw new ImmutableElementException("Parameter is immutable, you may try to setup a parameter which comes from global settings. Key:" + key); if (parent == null) return map.remove(key); else{ if (parent.containsKey(key)) return parent.remove(key); else return map.remove(key); } } @Override public Object getUnwrappered(String key) { Object obj = getUnwrapperedInner(key); if (obj == null){ if (DetectiveFactory.INSTANCE.getParametersConfig().containsKey(key)) obj = DetectiveFactory.INSTANCE.getParametersConfig().getUnwrappered(key); } return obj; } private Object getUnwrapperedInner(String key) { if (parent == null) return map.get(key); else{ if (parent.containsKey(key)) return parent.getUnwrappered(key); else return map.get(key); } } @Override public Set<String> getUnbindShareVarKeys() { Set<String> result = new HashSet<String>(); for (String key : this.keySet()){ Object obj = this.getUnwrappered(key); if (isUnbind(obj)) result.add(key); } return result; } private boolean isUnbind(Object value){ return value instanceof SharedVariable && !((SharedVariable<?>)value).isBound(); } @Override public boolean isInParent(String key) { if (parent == null) return false; if (map.containsKey(key)) return false; if (parent.containsKey(key)) return true; else return false; } public boolean isImmutable() { return immutable; } public void setImmutable(boolean immutable) { this.immutable = immutable; } }