Java tutorial
/* * 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; } }