package org.netbeans.gradle.project.properties.standard;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;
import org.jtrim.property.PropertyFactory;
import org.jtrim.property.PropertySource;
import org.netbeans.gradle.project.api.config.ConfigPath;
import org.netbeans.gradle.project.api.config.ConfigTree;
import org.netbeans.gradle.project.api.config.PropertyDef;
import org.netbeans.gradle.project.api.config.PropertyKeyEncodingDef;
import org.netbeans.gradle.project.api.config.PropertyValueDef;
import org.netbeans.gradle.project.api.config.ValueMerger;
import org.netbeans.gradle.project.api.config.ValueReference;
public final class CustomVariablesProperty {
private static final ConfigPath CONFIG_ROOT = ConfigPath.fromKeys("custom-variables");
private static final String CONFIG_KEY_VAR = "var";
private static final String CONFIG_KEY_VAR_NAME = "name";
private static final String CONFIG_KEY_VAR_VALUE = "value";
public static final PropertyDef<CustomVariables, CustomVariables> PROPERTY_DEF = createPropertyDef();
private static PropertyDef<CustomVariables, CustomVariables> createPropertyDef() {
PropertyDef.Builder<CustomVariables, CustomVariables> result
= new PropertyDef.Builder<>(CONFIG_ROOT);
result.setKeyEncodingDef(CustomVariablesEncodingDef.INSTANCE);
result.setValueDef(CustomVariablesValueDef.INSTANCE);
result.setValueMerger(CustomVariablesMerger.INSTANCE);
return result.create();
}
private enum CustomVariablesEncodingDef implements PropertyKeyEncodingDef<CustomVariables> {
INSTANCE;
@Override
public CustomVariables decode(ConfigTree config) {
List<ConfigTree> varsConfig = config.getChildTrees(CONFIG_KEY_VAR);
List<CustomVariable> vars = new ArrayList<>(varsConfig.size());
for (ConfigTree var: varsConfig) {
String name = var.getChildTree(CONFIG_KEY_VAR_NAME).getValue(null);
String value = var.getChildTree(CONFIG_KEY_VAR_VALUE).getValue(null);
if (name != null && value != null) {
vars.add(new CustomVariable(name, value));
}
}
return new MemCustomVariables(vars);
}
@Override
public ConfigTree encode(CustomVariables value) {
if (value.isEmpty()) {
return ConfigTree.EMPTY;
}
ConfigTree.Builder result = new ConfigTree.Builder();
for (CustomVariable var: value.getVariables()) {
ConfigTree.Builder varConfig = result.addChildBuilder(CONFIG_KEY_VAR);
varConfig.addChildBuilder(CONFIG_KEY_VAR_NAME).setValue(var.getName());
varConfig.addChildBuilder(CONFIG_KEY_VAR_VALUE).setValue(var.getValue());
}
return result.create();
}
}
private enum CustomVariablesMerger implements ValueMerger<CustomVariables> {
INSTANCE;
@Override
public CustomVariables mergeValues(CustomVariables child, ValueReference<CustomVariables> parent) {
if (child == null) {
return parent.getValue();
}
return new MergedCustomVariables(child, parent);
}
}
private static final class MergedCustomVariables implements CustomVariables {
private final CustomVariables child;
private final ValueReference<CustomVariables> parentRef;
private final AtomicReference<CustomVariables> parentCache;
public MergedCustomVariables(CustomVariables child, ValueReference<CustomVariables> parentRef) {
assert child != null;
assert parentRef != null;
this.child = child;
this.parentRef = parentRef;
this.parentCache = new AtomicReference<>(null);
}
private CustomVariables getParent() {
CustomVariables result = parentCache.get();
if (result == null) {
result = parentRef.getValue();
if (result == null) {
result = MemCustomVariables.EMPTY;
}
if (!parentCache.compareAndSet(null, result)) {
result = parentCache.get();
}
}
return result;
}
@Override
public String tryGetValue(String name) {
String result = child.tryGetValue(name);
return result != null
? result
: getParent().tryGetValue(name);
}
@Override
public Collection<CustomVariable> getVariables() {
Map<String, CustomVariable> vars = new HashMap<>();
for (CustomVariable var: child.getVariables()) {
vars.put(var.getName(), var);
}
for (CustomVariable var: getParent().getVariables()) {
vars.put(var.getName(), var);
}
return vars.values();
}
@Override
public boolean isEmpty() {
return child.isEmpty() && getParent().isEmpty();
}
}
private enum CustomVariablesValueDef implements PropertyValueDef<CustomVariables, CustomVariables> {
INSTANCE;
@Override
public PropertySource<CustomVariables> property(CustomVariables valueKey) {
return PropertyFactory.constSource(valueKey);
}
@Override
public CustomVariables getKeyFromValue(CustomVariables value) {
return value == null || value.isEmpty() ? null : value;
}
}
}