package restx.config;
import static com.google.common.collect.Iterables.addAll;
import com.google.common.base.Optional;
import com.google.common.collect.Iterables;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import restx.common.ConfigElement;
import restx.common.RestxConfig;
import restx.common.StdRestxConfig;
import restx.factory.BillOfMaterials;
import restx.factory.BoundlessComponentBox;
import restx.factory.Factory;
import restx.factory.FactoryMachine;
import restx.factory.Machine;
import restx.factory.MachineEngine;
import restx.factory.Name;
import restx.factory.NamedComponent;
import restx.factory.SatisfiedBOM;
import restx.factory.StdMachineEngine;
/**
* User: xavierhanin
* Date: 9/24/13
* Time: 10:16 PM
*/
@Machine
public class ConsolidatedConfigFactoryMachine implements FactoryMachine {
@Override
public boolean canBuild(Name<?> name) {
return name.getClazz() == RestxConfig.class;
}
@Override
@SuppressWarnings("unchecked")
public <T> MachineEngine<T> getEngine(Name<T> name) {
if (!canBuild(name)) {
throw new IllegalArgumentException("unsuported name " + name);
}
return (MachineEngine<T>) new StdMachineEngine<RestxConfig>((Name<RestxConfig>) name, priority(), BoundlessComponentBox.FACTORY) {
private final Factory.Query<ConfigSupplier> configSupplierQuery = Factory.Query.byClass(ConfigSupplier.class);
private final Factory.Query<String> stringsQuery = Factory.Query.byClass(String.class);
@Override
protected RestxConfig doNewComponent(SatisfiedBOM satisfiedBOM) {
List<ConfigElement> elements = new ArrayList<>();
// fetch system properties as ConfigElements, of strongest priority
/* they are also available through named strings thanks to SystemPropertyFactoryMachine
* but fetching them here ensures they get highest priority and give clear indication of their origin.
* We could get rid of SystemPropertyFactoryMachine, but it may be helpful for someone who doesn't use
* RestxConfig at all.
*/
for (Map.Entry<Object, Object> entry : System.getProperties().entrySet()) {
// System properties are a Map which is not protected against adding non String entries
// we simply ignore them
if (entry.getKey() instanceof String && entry.getValue() instanceof String) {
elements.add(ConfigElement.of("system", "", (String) entry.getKey(), (String) entry.getValue()));
}
}
// now fetch elements coming from ConfigSuppliers
for (NamedComponent<ConfigSupplier> supplier : satisfiedBOM.get(configSupplierQuery)) {
addAll(elements, supplier.getComponent().get().elements());
}
RestxConfig config = StdRestxConfig.of(elements);
// and now String components
List<ConfigElement> factoryElements = new ArrayList<>();
for (NamedComponent<String> s : satisfiedBOM.get(stringsQuery)) {
Optional<ConfigElement> element = config.getElement(s.getName().getName());
if (element.isPresent() && element.get().getValue().equals(s.getComponent())) {
// we don't add values from factory with the same value as the one found in config:
// it's probably because config elements are provided as String components too.
continue;
}
factoryElements.add(ConfigElement.of("factory", element.isPresent() ? element.get().getDoc() : "",
s.getName().getName(), s.getComponent()));
}
return StdRestxConfig.of(Iterables.concat(factoryElements, elements));
}
@Override
public BillOfMaterials getBillOfMaterial() {
return BillOfMaterials.of(configSupplierQuery, stringsQuery);
}
};
}
@Override
@SuppressWarnings("unchecked")
public <T> Set<Name<T>> nameBuildableComponents(Class<T> componentClass) {
if (componentClass == RestxConfig.class) {
return Collections.singleton(Name.of((Class<T>) RestxConfig.class));
} else {
return Collections.emptySet();
}
}
@Override
public int priority() {
return 0;
}
@Override
public String toString() {
return ConsolidatedConfigFactoryMachine.class.getSimpleName();
}
}