/*
 * Copyright 2017 Crown Copyright
 *
 * Licensed 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 stroom.config.global.impl.db;


import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.stream.Collectors;

public final class BeanUtil {
    private static final Logger LOGGER = LoggerFactory.getLogger(BeanUtil.class);

    private BeanUtil() {
        // Utility class.
    }

    static Map<String, Prop> getProperties(final Object object) {
        final Class<?> clazz = object.getClass();
        final Method[] methods = clazz.getDeclaredMethods();
        final Map<String, Prop> propMap = new HashMap<>();
        for (final Method method : methods) {
            final String methodName = method.getName();
            if (methodName.length() > 3 && !methodName.equals("getClass")) {
                if (methodName.startsWith("get") && method.getParameterTypes().length == 0 && !method.getReturnType().equals(Void.TYPE)) {
                    // Getter.
                    final String name = getPropertyName(methodName);
                    final Prop prop = propMap.computeIfAbsent(name, k -> new Prop(name, object));
                    prop.getter = method;
                } else if (methodName.startsWith("set") && method.getParameterTypes().length == 1 && method.getReturnType().equals(Void.TYPE)) {
                    // Setter.
                    final String name = getPropertyName(methodName);
                    final Prop prop = propMap.computeIfAbsent(name, k -> new Prop(name, object));
                    prop.setter = method;
                }
            }
        }

        return propMap
                .entrySet()
                .stream()
                .filter(e -> {
                    if (e.getValue().getter == null || e.getValue().setter == null) {
                        LOGGER.debug("Invalid property " + e.getKey() + " on " + clazz.getName());
                        return false;
                    }
                    return true;
                })
                .collect(Collectors.toMap(Entry::getKey, Entry::getValue));
    }

    private static String getPropertyName(final String methodName) {
        final String name = methodName.substring(3);
        return name.substring(0, 1).toLowerCase() + name.substring(1);
    }

    public static class Prop {
        private final String name;
        private final Object object;
        private Method getter;
        private Method setter;

        Prop(final String name, final Object object) {
            this.name = name;
            this.object = object;
        }

        public String getName() {
            return name;
        }

        public Object getObject() {
            return object;
        }

        public Method getGetter() {
            return getter;
        }

        public Method getSetter() {
            return setter;
        }
    }
}

@@ -1345,24 +1345,112 @@
 .getName();%0A
+%0A            if (methodName.startsWith(%22is%22)) %7B%0A                // Boolean Getter.%0A%0A    
             
@@ -1479,93 +1479,489 @@
 ) %3E 
-3
+2
  && 
-!
 method
-Name.equals(%22getClass%22)) %7B%0A                if (methodName.startsWith(%22get
+.getParameterTypes().length == 0 && !method.getReturnType().equals(Void.TYPE)) %7B%0A                    final String name = getPropertyName(methodName, 2);%0A                    final Prop prop = propMap.computeIfAbsent(name, k -%3E new Prop(name, object));%0A                    prop.getter = method;%0A                %7D%0A%0A            %7D else if (methodName.startsWith(%22get%22)) %7B%0A                // Getter.%0A%0A                if (methodName.length() %3E 3 && !methodName.equals(%22getClass
 %22) &
@@ -2053,39 +2053,8 @@
 ) %7B%0A
-                    // Getter.%0A
     
@@ -2107,32 +2107,35 @@
 yName(methodName
+, 3
 );%0A             
@@ -2270,32 +2270,46 @@
                %7D
+%0A            %7D
  else if (method
@@ -2330,16 +2330,91 @@
 h(%22set%22)
+) %7B%0A                // Setter.%0A%0A                if (methodName.length() %3E 3
  && meth
@@ -2499,39 +2499,8 @@
 ) %7B%0A
-                    // Setter.%0A
     
@@ -2561,16 +2561,19 @@
 thodName
+, 3
 );%0A     
@@ -3303,16 +3303,31 @@
 thodName
+, final int len
 ) %7B%0A    
@@ -3371,17 +3371,19 @@
 bstring(
-3
+len
 );%0A     

Fix for boolean getter