package com.yammer.dropwizard.config;

import com.google.common.collect.ImmutableList;
import com.yammer.dropwizard.json.Json;
import com.yammer.dropwizard.validation.Validator;
import org.codehaus.jackson.map.DeserializationConfig;
import org.codehaus.jackson.map.Module;

import java.io.File;
import java.io.IOException;

public class ConfigurationFactory<T> {
    public static <T> ConfigurationFactory<T> forClass(Class<T> klass, Validator validator, Iterable<Module> modules) {
        return new ConfigurationFactory<T>(klass, validator, modules);
    }

    public static <T> ConfigurationFactory<T> forClass(Class<T> klass, Validator validator) {
        return new ConfigurationFactory<T>(klass, validator, ImmutableList.<Module>of());
    }

    private final Class<T> klass;
    private final Json json;
    private final Validator validator;

    private ConfigurationFactory(Class<T> klass, Validator validator, Iterable<Module> modules) {
        this.klass = klass;
        this.json = new Json();
        json.enable(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES);
        for (Module module : modules) {
            json.registerModule(module);
        }
        this.validator = validator;
    }
    
    public T build(File file) throws IOException, ConfigurationException {
        final T config = parse(file);
        validate(file, config);
        return config;
    }

    private T parse(File file) throws IOException {
        if (file.getName().endsWith(".yaml") || file.getName().endsWith(".yml")) {
            return json.readYamlValue(file, klass);
        }
        return json.readValue(file, klass);
    }

    private void validate(File file, T config) throws ConfigurationException {
        final ImmutableList<String> errors = validator.validate(config);
        if (!errors.isEmpty()) {
            throw new ConfigurationException(file, errors);
        }
    }
}

@@ -32,16 +32,56 @@
 onfig;%0A%0A
+import com.google.common.base.Splitter;%0A
 import c
@@ -211,16 +211,54 @@
 idator;%0A
+import org.codehaus.jackson.JsonNode;%0A
 import o
@@ -343,16 +343,61 @@
 .Module;
+%0Aimport org.codehaus.jackson.node.ObjectNode;
 %0A%0Aimport
@@ -443,47 +443,155 @@
 on;%0A
-%0Apublic class ConfigurationFactory%3CT%3E %7B
+import java.util.Iterator;%0Aimport java.util.Map;%0A%0Apublic class ConfigurationFactory%3CT%3E %7B%0A%0A    private static final String PROPERTY_PREFIX = %22dw.%22;%0A
 %0A   
@@ -1547,107 +1547,1405 @@
 nal 
-T config = parse(file);%0A        validate(file, config);%0A        return config;%0A    %7D%0A%0A    private T
+JsonNode node = parse(file);%0A        for (Map.Entry%3CObject, Object%3E pref : System.getProperties().entrySet()) %7B%0A            final String prefName = (String) pref.getKey();%0A            if (prefName.startsWith(PROPERTY_PREFIX)) %7B%0A                final String configName = prefName.substring(PROPERTY_PREFIX.length());%0A                addOverride(node, configName, System.getProperty(prefName));%0A            %7D%0A        %7D%0A        final T config = json.readValue(node, klass);%0A        validate(file, config);%0A        return config;%0A    %7D%0A%0A    private void addOverride(JsonNode root, String name, String value) %7B%0A        JsonNode node = root;%0A        final Iterator%3CString%3E keys = Splitter.on('.').trimResults().split(name).iterator();%0A        while (keys.hasNext()) %7B%0A            final String key = keys.next();%0A            if (!(node instanceof ObjectNode)) %7B%0A                throw new IllegalArgumentException(%22Unable to override %22 + name + %22; it's not a valid path.%22);%0A            %7D%0A%0A            final ObjectNode obj = (ObjectNode) node;%0A            if (keys.hasNext()) %7B%0A                JsonNode child = obj.get(key);%0A                if (child == null) %7B%0A                    child = obj.objectNode();%0A                    obj.put(key, child);%0A                %7D%0A                node = child;%0A            %7D else %7B%0A                obj.put(key, value);%0A            %7D%0A        %7D%0A    %7D%0A%0A    private JsonNode
  par
@@ -3098,33 +3098,42 @@
 YamlValue(file, 
-k
+JsonNode.c
 lass);%0A        %7D
@@ -3169,17 +3169,26 @@
 e(file, 
-k
+JsonNode.c
 lass);%0A 

Allow command-line overrides of configuration parameters.