001/*
002 * Licensed under the Apache License, Version 2.0 (the "License");
003 * you may not use this file except in compliance with the License.
004 * You may obtain a copy of the License at
005 *
006 * http://www.apache.org/licenses/LICENSE-2.0
007 *
008 * Unless required by applicable law or agreed to in writing, software
009 * distributed under the License is distributed on an "AS IS" BASIS,
010 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
011 * See the License for the specific language governing permissions and
012 * limitations under the License.
013 */
014package org.atteo.moonshine.injection;
015
016import java.lang.reflect.Field;
017import java.util.ArrayList;
018import java.util.Collection;
019import java.util.List;
020
021import com.google.inject.Binder;
022import com.google.inject.Injector;
023import com.google.inject.Module;
024import com.google.inject.Provider;
025import com.google.inject.TypeLiteral;
026import com.google.inject.matcher.Matchers;
027import com.google.inject.spi.InjectionListener;
028import com.google.inject.spi.TypeEncounter;
029import com.google.inject.spi.TypeListener;
030
031public class InjectMembersModule implements Module, TypeListener {
032    @Override
033    public void configure(Binder binder) {
034        binder.bindListener(Matchers.any(), this);
035    }
036
037    @Override
038    public <I> void hear(TypeLiteral<I> type, TypeEncounter<I> encounter) {
039        Class<? super I> klass = type.getRawType();
040
041        List<Field> fields = new ArrayList<>();
042
043        while (klass != Object.class) {
044            for (Field field : klass.getDeclaredFields()) {
045                if (field.getAnnotation(InjectMembers.class) != null) {
046                    fields.add(field);
047                }
048            }
049
050            klass = klass.getSuperclass();
051        }
052
053        if (fields.isEmpty()) {
054            return;
055        }
056        Provider<Injector> injector = encounter.getProvider(Injector.class);
057
058        encounter.register(generateListener(fields, injector));
059    }
060
061    private <T> InjectionListener<T> generateListener(final Collection<Field> fields,
062            final Provider<Injector> injectorProvider) {
063        return new InjectionListener<T>() {
064            @Override
065            public void afterInjection(T instance) {
066                for (Field field : fields) {
067                    boolean wasAccessible = field.isAccessible();
068                    field.setAccessible(true);
069                    Injector injector = injectorProvider.get();
070
071                    try {
072                        Object object = field.get(instance);
073                        if (object == null) {
074                            continue;
075                        }
076
077                        if (object instanceof Collection) {
078                            for (Object o : (Collection<?>) object) {
079                                injector.injectMembers(o);
080                            }
081                        } else {
082                            injector.injectMembers(object);
083                        }
084                    } catch (IllegalAccessException e) {
085                        throw new RuntimeException(e);
086                    }
087                    field.setAccessible(wasAccessible);
088                }
089            }
090        };
091    }
092}