All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.grails.config.NavigableMapConfig Maven / Gradle / Ivy

There is a newer version: 6.2.0
Show newest version
/*
 * Copyright 2014 original authors
 *
 * 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 org.grails.config;

import grails.config.Config;
import grails.util.GrailsStringUtils;
import org.codehaus.groovy.runtime.DefaultGroovyMethods;
import org.grails.core.exceptions.GrailsConfigurationException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.convert.ConversionException;
import org.springframework.core.convert.support.ConfigurableConversionService;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.util.ClassUtils;

import java.util.*;

/**
 * A {@link Config} implementation that operates against a {@link org.grails.config.NavigableMap}
 *
 * @author Graeme Rocher
 * @since 3.0
 */

public abstract class NavigableMapConfig implements Config {
    protected static final Logger LOG = LoggerFactory.getLogger(NavigableMapConfig.class);
    protected ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
    protected ConfigurableConversionService conversionService = new DefaultConversionService();
    protected NavigableMap configMap = new NavigableMap() {
        @Override
        protected Object mergeMapEntry(NavigableMap targetMap, String sourceKey, Object newValue) {
            if(newValue instanceof CharSequence) {
                newValue = resolvePlaceholders(newValue.toString());
            }
            return super.mergeMapEntry(targetMap, sourceKey, newValue);
        }
    };

    @Override
    public int hashCode() {
        return configMap.hashCode();
    }

    @Override
    public boolean equals(Object obj) {
        if(obj instanceof NavigableMapConfig) {
            return this.configMap.equals(((NavigableMapConfig)obj).configMap);
        }
        return false;
    }

    @Override
    public String toString() {
        return configMap.toString();
    }

    @Override
    public Object getAt(Object key) {
        return get(key);
    }

    @Override
    public void setAt(Object key, Object value) {
        configMap.put(key.toString(), value);
    }

    @Override
    public int size() {
        return configMap.size();
    }

    @Override
    public boolean isEmpty() {
        return configMap.isEmpty();
    }

    @Override
    public boolean containsKey(Object key) {
        return containsProperty(key.toString());
    }

    @Override
    public boolean containsValue(Object value) {
        return configMap.containsValue(value);
    }

    @Override
    public Object get(Object key) {
        return configMap.getProperty(key.toString());
    }

    @Override
    public Object put(String key, Object value) {
        return configMap.put(key, value);
    }

    @Override
    public Object remove(Object key) {
        return configMap.remove(key);
    }

    @Override
    public void putAll(Map m) {
        configMap.putAll(m);
    }

    @Override
    public void clear() {
        configMap.clear();
    }

    @Override
    public Set keySet() {
        return configMap.keySet();
    }

    @Override
    public Collection values() {
        return configMap.values();
    }

    @Override
    public Set> entrySet() {
        return configMap.entrySet();
    }

    @Override
    @Deprecated
    public Map flatten() {
        if(LOG.isWarnEnabled()) {
            LOG.warn("A plugin or your application called the flatten() method which can degrade startup performance");
        }
        return configMap;
    }

    @Override
    public Properties toProperties() {
        return configMap.toProperties();
    }

    @Override
    public Object navigate(String... path) {
        return configMap.navigate(path);
    }

    @Override
    public Config merge(Map toMerge) {
        configMap.merge(toMerge, true);
        return this;
    }

    public Object asType(Class c) {
        if(c==Boolean.class || c==boolean.class) return false;
        return null;
    }

    @Override
    public Iterator> iterator() {
        return DefaultGroovyMethods.iterator(configMap);
    }

    @Override
    public boolean containsProperty(String key) {
        return getProperty(key, Object.class) != null;
    }

    @Override
    public String getProperty(String key) {
        return getProperty(key, String.class);
    }

    @Override
    public String getProperty(String key, String defaultValue) {
        return getProperty(key, String.class, defaultValue);
    }

    @Override
    public  T getProperty(String key, Class targetType) {
        return getProperty(key, targetType, null);
    }

    @Override
    public  T getProperty(String key, Class targetType, T defaultValue, List allowedValues) {
        T value = getProperty(key, targetType, defaultValue);
        if(!allowedValues.contains(value)) {
            throw new GrailsConfigurationException("Invalid configuration value [$value] for key [${key}]. Possible values $allowedValues");
        }
        return value;
    }

    @Override
    public  T getProperty(String key, Class targetType, T defaultValue) {
        Object originalValue = configMap.get(key);
        if(originalValue != null) {
            if(targetType.isInstance(originalValue)) {
                return (T)originalValue;
            }
            else {
                if(!(originalValue instanceof NavigableMap)) {

                    try {
                        T value = conversionService.convert(originalValue, targetType);
                        return DefaultGroovyMethods.asBoolean(value) ? value : defaultValue;
                    } catch (ConversionException e) {
                        // ignore, return default value
                    }
                }
            }
        }
        return defaultValue;
    }

    @Override
    public  Class getPropertyAsClass(String key, Class targetType) {
        String className = getProperty(key, String.class);

        if(!GrailsStringUtils.isBlank(className)) {
            try {
                Class clazz = (Class) ClassUtils.forName((String) className, classLoader);
                if(clazz != targetType) {
                    throw new ClassConversionException(clazz, targetType);
                }
                return clazz;
            } catch (Exception e) {
                throw new ClassConversionException(className, targetType, e);
            }
        }
        else {
            throw new IllegalStateException("Value for $key cannot be resolved");
        }
    }

    @Override
    public String getRequiredProperty(String key) throws IllegalStateException {
        String value = getProperty(key);
        if(GrailsStringUtils.isBlank(value)) {
            throw new IllegalStateException("Value for key ["+key+"] cannot be resolved");
        }
        return value;
    }

    @Override
    public  T getRequiredProperty(String key, Class targetType) throws IllegalStateException {
        T value = getProperty(key, targetType);
        if(value == null) {
            throw new IllegalStateException("Value for key ["+key+"] cannot be resolved");
        }
        return value;
    }

    public static class ClassConversionException extends ConversionException {

        public ClassConversionException(Class actual, Class expected) {
            super(String.format("Actual type %s is not assignable to expected type %s", actual.getName(), expected.getName()));
        }

        public ClassConversionException(String actual, Class expected, Exception ex) {
            super(String.format("Could not find/load class %s during attempt to convert to %s", actual, expected.getName()), ex);
        }
    }
}