001/**
002 * Copyright 2010-2014 The Kuali Foundation
003 *
004 * Licensed under the Educational Community License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.opensource.org/licenses/ecl2.php
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package org.kuali.common.util.env;
017
018import static org.kuali.common.util.Annotations.extractClassAnnotation;
019
020import java.lang.reflect.Field;
021import java.util.ArrayList;
022import java.util.List;
023import java.util.Set;
024
025import org.kuali.common.util.ListUtils;
026import org.kuali.common.util.ReflectionUtils;
027import org.kuali.common.util.env.adapter.EnvAdapter;
028import org.kuali.common.util.env.annotation.Env;
029import org.kuali.common.util.env.annotation.EnvAdapterClass;
030import org.kuali.common.util.env.annotation.EnvIgnore;
031import org.kuali.common.util.env.annotation.EnvKeys;
032import org.kuali.common.util.spring.SpringUtils;
033import org.kuali.common.util.spring.env.BasicEnvironmentService;
034import org.kuali.common.util.spring.env.EnvironmentService;
035
036import com.google.common.base.Optional;
037import com.google.common.base.Preconditions;
038import com.google.common.collect.ImmutableList;
039
040public final class DefaultOverrideService implements OverrideService {
041
042        public EnvironmentService getEnv() {
043                return env;
044        }
045
046        public boolean isSkip() {
047                return skip;
048        }
049
050        private final EnvironmentService env;
051        private final boolean skip;
052
053        @Override
054        public void override(Object instance) {
055                Optional<Env> optional = extractClassAnnotation(instance.getClass(), Env.class);
056                if (!optional.isPresent()) {
057                        return;
058                }
059                Env annotation = optional.get();
060                if (annotation.skip()) {
061                        return;
062                }
063                Optional<String> prefix = getPrefix(annotation);
064                Set<Field> fields = ReflectionUtils.getFields(instance.getClass(), annotation.includeInheritedFields());
065                for (Field field : fields) {
066                        override(prefix, instance, field);
067                }
068        }
069
070        private void override(Optional<String> prefix, Object instance, Field field) {
071                Optional<EnvIgnore> ignore = extractClassAnnotation(instance.getClass(), EnvIgnore.class);
072                if (ignore.isPresent()) {
073                        return;
074                }
075
076                // Get the list of environment keys to look things up by
077                List<String> keys = getKeys(prefix, field);
078
079                // Extract the adapter annotation (if there is one)
080                Optional<EnvAdapterClass> adapterAnnotation = Optional.fromNullable(field.getAnnotation(EnvAdapterClass.class));
081
082                // Extract the adapter itself (if there is one)
083                Optional<? extends EnvAdapter<?, ?>> adapter = getAdapter(adapterAnnotation);
084
085                // Target type in this context is the type we want the conversion service to convert the environment value into.
086                // The value that comes out of the environment is the "source" value for the conversion to the specific value on our
087                // domain model object
088                Class<?> targetType = adapter.isPresent() ? adapter.get().getSourceType() : field.getType();
089
090                // Extract a value from the environment
091                Optional<?> value = SpringUtils.getOptionalProperty(env, keys, targetType);
092
093                // nothing to do if there is no value
094                if (!value.isPresent()) {
095                        return;
096                }
097
098                // If there is an adapter, use it to convert the value we extracted from the environment into the value we need
099                if (adapter.isPresent()) {
100                        Object result = ReflectionUtils.invokeMethod(adapter.get(), "convert", value.get());
101                        value = Optional.fromNullable(result);
102                }
103
104                // Store the value we have on the object
105                ReflectionUtils.set(instance, field, value.orNull());
106        }
107
108        private Optional<? extends EnvAdapter<?, ?>> getAdapter(Optional<EnvAdapterClass> annotation) {
109                if (annotation.isPresent()) {
110                        Class<? extends EnvAdapter<?, ?>> adapterClass = annotation.get().value();
111                        EnvAdapter<?, ?> adapter = ReflectionUtils.newInstance(adapterClass);
112                        return Optional.of(adapter);
113                } else {
114                        return Optional.absent();
115                }
116        }
117
118        private List<String> getKeys(Optional<String> prefix, Field field) {
119                Optional<EnvKeys> optional = Optional.fromNullable(field.getAnnotation(EnvKeys.class));
120
121                List<String> list = new ArrayList<String>();
122                if (optional.isPresent() && optional.get().values().length > 0) {
123                        list.addAll(ImmutableList.copyOf(optional.get().values()));
124                } else {
125                        list.add(field.getName());
126                }
127                if (prefix.isPresent()) {
128                        return ListUtils.prefix(prefix.get(), ".", list);
129                } else {
130                        return list;
131                }
132        }
133
134        private Optional<String> getPrefix(Env annotation) {
135                String prefix = annotation.prefix();
136                if (Env.NOPREFIX.equals(prefix)) {
137                        return Optional.absent();
138                } else {
139                        return Optional.of(prefix);
140                }
141        }
142
143        private DefaultOverrideService(Builder builder) {
144                this.env = builder.env;
145                this.skip = builder.skip;
146        }
147
148        public static class Builder {
149
150                private EnvironmentService env = new BasicEnvironmentService();
151                private boolean skip = false;
152
153                public Builder withEnv(EnvironmentService env) {
154                        this.env = env;
155                        return this;
156                }
157
158                public Builder withSkip(boolean skip) {
159                        this.skip = skip;
160                        return this;
161                }
162
163                public DefaultOverrideService build() {
164                        DefaultOverrideService instance = new DefaultOverrideService(this);
165                        validate(instance);
166                        return instance;
167                }
168
169                private void validate(DefaultOverrideService instance) {
170                        Preconditions.checkNotNull(instance.env, "env may not be null");
171                }
172        }
173
174}