/*
 * Licensed to Elasticsearch under one or more contributor
 * license agreements. See the NOTICE file distributed with
 * this work for additional information regarding copyright
 * ownership. Elasticsearch licenses this file to you under
 * the Apache License, Version 2.0 (the "License"); you may
 * not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

package org.elasticsearch.common.settings.loader;

import org.apache.lucene.util.IOUtils;
import org.elasticsearch.ElasticsearchParseException;
import org.elasticsearch.common.io.FastStringReader;
import org.elasticsearch.common.io.stream.StreamInput;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

/**
 * Settings loader that loads (parses) the settings in a properties format.
 */
public class PropertiesSettingsLoader implements SettingsLoader {

    @Override
    public Map<String, String> load(String source) throws IOException {
        Properties props = new NoDuplicatesProperties();
        FastStringReader reader = new FastStringReader(source);
        try {
            props.load(reader);
            Map<String, String> result = new HashMap<>();
            for (Map.Entry entry : props.entrySet()) {
                result.put((String) entry.getKey(), (String) entry.getValue());
            }
            return result;
        } finally {
            IOUtils.closeWhileHandlingException(reader);
        }
    }

    @Override
    public Map<String, String> load(byte[] source) throws IOException {
        Properties props = new NoDuplicatesProperties();
        StreamInput stream = StreamInput.wrap(source);
        try {
            props.load(stream);
            Map<String, String> result = new HashMap<>();
            for (Map.Entry entry : props.entrySet()) {
                result.put((String) entry.getKey(), (String) entry.getValue());
            }
            return result;
        } finally {
            IOUtils.closeWhileHandlingException(stream);
        }
    }

    class NoDuplicatesProperties extends Properties {
        @Override
        public synchronized Object put(Object key, Object value) {
            Object previousValue = super.put(key, value);
            if (previousValue != null) {
                throw new ElasticsearchParseException("duplicate settings key [{}] found, previous value [{}], current value [{}]", key, previousValue, value);
            }
            return previousValue;
        }
    }
}

@@ -1036,16 +1036,42 @@
 Input;%0A%0A
+import java.io.Closeable;%0A
 import j
@@ -1166,16 +1166,52 @@
 perties;
+%0Aimport java.util.function.Supplier;
 %0A%0A/**%0A *
@@ -1453,90 +1453,25 @@
     
-Properties props = new NoDuplicatesProperties();%0A        FastStringReader reader =
+return load(() -%3E
  new
@@ -1499,35 +1499,28 @@
 rce)
-;%0A        try %7B%0A           
+, (reader, props) -%3E
  pro
@@ -1538,319 +1538,420 @@
 der)
+)
 ;%0A    
+%7D%0A%0A
     
-    Map%3CString, String%3E result = new HashMap%3C%3E();%0A            for (Map.Entry entry : props.entrySet()) %7B%0A                result.put((String) entry.getKey()
+@Override%0A    public Map%3CString, String%3E load(byte%5B%5D source) throws IOException %7B%0A        return load(() -%3E StreamInput.wrap(source), (inStream, props) -%3E props.load(inStream));%0A    %7D%0A%0A    private final %3CT extends Closeable%3E Map%3CString
 , 
-(
 String
-) entry.getValue());%0A            %7D%0A            return result;%0A        %7D finally %7B%0A            IOUtils.closeWhileHandlingException(reader)
+%3E load(%0A            Supplier%3CT%3E supplier,%0A            IOExceptionThrowingBiConsumer%3CT, Properties%3E properties%0A    ) throws IOException %7B%0A        T t = null
 ;%0A  
@@ -1960,111 +1960,58 @@
     
-%7D
+try %7B
 %0A    
-%7D%0A%0A
     
-@Override%0A    public Map%3CString, String%3E load(byte%5B%5D source) throws IOException %7B%0A
+    t = supplier.get();%0A    
         
 Prop
@@ -2006,16 +2006,22 @@
         
+final 
 Properti
@@ -2073,98 +2073,38 @@
     
-StreamInput stream = StreamInput.wrap(source);%0A        try %7B%0A            props.load(stream
+    properties.accept(t, props
 );%0A 
@@ -2114,16 +2114,22 @@
         
+final 
 Map%3CStri
@@ -2414,26 +2414,163 @@
 ion(
-stream);%0A        %7D
+t);%0A        %7D%0A    %7D%0A%0A    @FunctionalInterface%0A    private interface IOExceptionThrowingBiConsumer%3CT, U%3E %7B%0A        void accept(T t, U u) throws IOException;
 %0A   

Refactor PropertiesSettingsLoader