org.codeseed.common.config.PropertySources.java Source code

Java tutorial

Introduction

Here is the source code for org.codeseed.common.config.PropertySources.java

Source

/*
 * Copyright 2013 Jeremy Gustie
 *
 * 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.codeseed.common.config;

import static com.google.common.base.CaseFormat.LOWER_CAMEL;
import static com.google.common.base.CaseFormat.UPPER_UNDERSCORE;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Strings.emptyToNull;
import static com.google.common.base.Strings.isNullOrEmpty;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.nio.charset.Charset;
import java.util.Arrays;
import java.util.Locale;
import java.util.Objects;
import java.util.TimeZone;
import java.util.prefs.Preferences;
import javax.annotation.Nullable;
import com.google.common.base.Defaults;
import com.google.common.base.Joiner;
import com.google.common.base.Predicates;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.Iterables;
import com.google.common.primitives.Primitives;

/**
 * Static utility methods pertaining to {@code PropertySource} instances.
 *
 * @author Jeremy Gustie
 */
public final class PropertySources {

    private enum ConstantPropertySource implements PropertySource {
        /**
         * @see PropertySources#empty()
         */
        EMPTY {
            @Override
            public String getProperty(Method method) {
                checkNotNull(method);
                return null;
            }
        },

        /**
         * @see PropertySources#defaultValues()
         */
        DEFAULT_VALUES {
            @Override
            public String getProperty(Method method) {
                final Class<?> returnType = Primitives.unwrap(method.getReturnType());
                if (returnType.equals(Locale.class)) {
                    return Locale.getDefault().toLanguageTag();
                } else if (returnType.equals(TimeZone.class)) {
                    return TimeZone.getDefault().getID();
                } else if (returnType.equals(Charset.class)) {
                    return Charset.defaultCharset().name();
                } else {
                    return Objects.toString(Defaults.defaultValue(method.getReturnType()), null);
                }
            }
        }
    }

    /**
     * @see PropertySources#environmentVariables()
     */
    private static final class EnvironmentVariableSource extends AnnotationPropertySource<EnvironmentVariable>
            implements Serializable {
        private static final EnvironmentVariableSource INSTANCE = new EnvironmentVariableSource();
        private static final long serialVersionUID = 1L;

        private EnvironmentVariableSource() {
        }

        @Override
        protected String getProperty(Method method, EnvironmentVariable annotation) {
            String varName = annotation.value();
            if (isNullOrEmpty(varName)) {
                varName = LOWER_CAMEL.to(UPPER_UNDERSCORE, method.getName());
            }
            return System.getenv(varName);
        }
    }

    /**
     * @see PropertySources#systemProperties()
     */
    private static final class SystemPropertySource extends AnnotationPropertySource<SystemProperty>
            implements Serializable {
        private static final SystemPropertySource INSTANCE = new SystemPropertySource();
        private static final long serialVersionUID = 1L;

        private SystemPropertySource() {
        }

        @Override
        protected String getProperty(Method method, SystemProperty annotation) {
            String key = annotation.value();
            if (isNullOrEmpty(key)) {
                key = Joiner.on('.').skipNulls()
                        .join(Arrays.asList(emptyToNull(method.getDeclaringClass().getPackage().getName()),
                                emptyToNull(method.getName())));
            }
            return System.getProperty(key);
        }
    }

    /**
     * @see PropertySources#systemPreferences()
     */
    private static final class SystemPreferenceSource extends AnnotationPropertySource<SystemPreference>
            implements Serializable {
        private static final SystemPreferenceSource INSTANCE = new SystemPreferenceSource();
        private static final long serialVersionUID = 1L;

        private SystemPreferenceSource() {
        }

        @Override
        protected String getProperty(Method method, SystemPreference annotation) {
            String key = annotation.value();
            if (isNullOrEmpty(key)) {
                key = method.getName();
            }

            Preferences preferences;
            if (isNullOrEmpty(annotation.pathName())) {
                preferences = Preferences.systemNodeForPackage(method.getDeclaringClass());
            } else {
                preferences = Preferences.systemRoot().node(annotation.pathName());
            }
            return preferences.get(key, null);
        }
    }

    /**
     * @see PropertySources#userPreferences()
     */
    private static final class UserPreferenceSource extends AnnotationPropertySource<UserPreference>
            implements Serializable {
        private static final UserPreferenceSource INSTANCE = new UserPreferenceSource();
        private static final long serialVersionUID = 1L;

        private UserPreferenceSource() {
        }

        @Override
        protected String getProperty(Method method, UserPreference annotation) {
            String key = annotation.value();
            if (isNullOrEmpty(key)) {
                key = method.getName();
            }

            Preferences preferences;
            if (isNullOrEmpty(annotation.pathName())) {
                preferences = Preferences.userNodeForPackage(method.getDeclaringClass());
            } else {
                preferences = Preferences.userRoot().node(annotation.pathName());
            }
            return preferences.get(key, null);
        }
    }

    /**
     * @see PropertySources#compose(PropertySource...)
     */
    private static final class CompositionSource implements PropertySource {
        private final Iterable<PropertySource> sources;

        CompositionSource(Iterable<PropertySource> sources) {
            this.sources = checkNotNull(sources);
        }

        @Override
        public String getProperty(Method method) {
            checkNotNull(method);
            for (PropertySource source : sources) {
                String value = source.getProperty(method);
                if (value != null) {
                    return value;
                }
            }
            return null;
        }

        @Override
        public int hashCode() {
            int ordinal = 0;
            int hashCode = getClass().hashCode() * (++ordinal);
            for (PropertySource source : sources) {
                hashCode += source.hashCode() * (++ordinal);
            }
            return hashCode;
        }

        @Override
        public boolean equals(@Nullable Object obj) {
            if (obj instanceof CompositionSource) {
                return Iterables.elementsEqual(sources, ((CompositionSource) obj).sources);
            }
            return false;
        }

        @Override
        public String toString() {
            return "Compose(" + Iterables.toString(sources) + ")";
        }
    }

    /**
     * @see PropertySources#ofInstance(Object)
     */
    private static final class InstanceSource implements PropertySource {
        private final Object target;

        InstanceSource(Object target) {
            this.target = checkNotNull(target);
        }

        @Override
        public String getProperty(Method method) {
            try {
                return String.valueOf(method.invoke(target));
            } catch (ReflectiveOperationException e) {
                return null;
            }
        }

        @Override
        public int hashCode() {
            return target.hashCode() + getClass().hashCode();
        }

        @Override
        public boolean equals(@Nullable Object obj) {
            if (obj instanceof InstanceSource) {
                return target.equals(((InstanceSource) obj).target);
            }
            return false;
        }

        @Override
        public String toString() {
            return "Instance(" + target + ")";
        }
    }

    /**
     * Returns a source based on environment variables.
     */
    static AnnotationPropertySource<EnvironmentVariable> environmentVariables() {
        return EnvironmentVariableSource.INSTANCE;
    }

    /**
     * Returns a source based on system properties.
     */
    static AnnotationPropertySource<SystemProperty> systemProperties() {
        return SystemPropertySource.INSTANCE;
    }

    /**
     * Returns a source based on the system preferences.
     */
    static AnnotationPropertySource<SystemPreference> systemPreferences() {
        return SystemPreferenceSource.INSTANCE;
    }

    /**
     * Returns a source based on the user preferences.
     */
    static AnnotationPropertySource<UserPreference> userPreferences() {
        return UserPreferenceSource.INSTANCE;
    }

    /**
     * Returns a property source that always returns the default value a
     * property type. For example, if the return type of the configuration
     * method is {@code int}, this property source will return the string
     * {@code "0"}.
     * <p>
     * For non-primitive types, this property source will almost always return
     * {@code null}; this makes sense if you want your configuration methods to
     * have similar default initialization values to Java code.
     * <p>
     * Some non-primitive types have special handling where a sensible default
     * values exist. This list is currently limited to:
     * <ul>
     * <li>{@code java.util.Locale} - returns the language tag for the default
     * locale</li>
     * <li>{@code java.util.TimeZone} - returns the identifier of the the
     * default time zone</li>
     * <li>{@code java.nio.charset.Charset} - returns the name of the default
     * character set</li>
     * </ul>
     * <p>
     * If that is not what you want, you are probably thinking of the
     * {@link DefaultValuePropertySource} which allows you to configure the
     * value using an annotation.
     *
     * @return property source containing Java language default values
     */
    static PropertySource defaultValues() {
        return ConstantPropertySource.DEFAULT_VALUES;
    }

    /**
     * Returns an empty source.
     *
     * @return empty property source
     */
    public static PropertySource empty() {
        return ConstantPropertySource.EMPTY;
    }

    /**
     * Returns a source which returns the first non-{@code null} value from a
     * series of sources.
     *
     * @param sources
     *            the ordered sources to combine
     * @return property source that considers multiple sources
     */
    public static PropertySource compose(PropertySource... sources) {
        return compose(Arrays.asList(sources));
    }

    /**
     * Returns a source which returns the first non-{@code null} value from a
     * series of sources.
     *
     * @param sources
     *            the ordered sources to combine
     * @return property source that considers multiple sources
     */
    public static PropertySource compose(Iterable<PropertySource> sources) {
        FluentIterable<PropertySource> s = FluentIterable.from(sources)
                .filter(Predicates.not(Predicates.equalTo(empty())));
        if (s.isEmpty()) {
            return empty();
        } else if (s.size() == 1) {
            return s.first().get();
        } else {
            return new CompositionSource(s.toList());
        }
    }

    /**
     * Returns a source which forwards property requests to the specified
     * instance. Non-string values will be converted using
     * {@code String.valueOf} so be wary of returning {@code null} values (which
     * will result in the string "null").
     *
     * @param target
     *            the instance to wrap in a property source
     * @return property source wrapper around the supplied target
     */
    // TODO Should target be an instance of Configuration?
    public static PropertySource ofInstance(Object target) {
        return new InstanceSource(target);
    }

    private PropertySources() {
        assert false;
    }
}