001/**
002 * Copyright 2013 John Ericksen
003 *
004 * Licensed under the Apache 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.apache.org/licenses/LICENSE-2.0
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.androidtransfuse.analysis;
017
018import com.google.common.base.Function;
019import com.google.common.collect.FluentIterable;
020import org.androidtransfuse.ComponentBuilder;
021import org.androidtransfuse.ConfigurationRepository;
022import org.androidtransfuse.EventMapping;
023import org.androidtransfuse.InjectionMapping;
024import org.androidtransfuse.adapter.ASTType;
025import org.androidtransfuse.adapter.classes.ASTClassFactory;
026import org.androidtransfuse.util.matcher.Matcher;
027
028import javax.inject.Inject;
029import javax.inject.Singleton;
030import java.lang.annotation.Annotation;
031import java.util.*;
032
033/**
034 * @author John Ericksen
035 */
036@Singleton
037public class ConfigurationRepositoryImpl implements ConfigurationRepository{
038
039    private final ASTClassFactory astClassFactory;
040
041    private final Map<Matcher<AnnotatedType>, Configuration> configurations = new HashMap<Matcher<AnnotatedType>, Configuration>();
042
043    private static class Configuration{
044        private final List<EventMapping> events = new ArrayList<EventMapping>();
045        private final List<InjectionMapping> mappings = new ArrayList<InjectionMapping>();
046        private final List<SuperCallMapping> superCalls = new ArrayList<SuperCallMapping>();
047        private final Set<Class<?>> callThroughEvents = new HashSet<Class<?>>();
048        private final Map<ASTType, ListenableMethod> listeners = new HashMap<ASTType, ListenableMethod>();
049        private Registration registraion = null;
050    }
051
052    @Inject
053    public ConfigurationRepositoryImpl(ASTClassFactory astClassFactory) {
054        this.astClassFactory = astClassFactory;
055    }
056
057    private List<Configuration> findConfigurations(ASTType type, Class<? extends Annotation> annotation){
058        AnnotatedType signature = new AnnotatedType(type, astClassFactory.getType(annotation));
059        List<Configuration> matched = new ArrayList<Configuration>();
060        for (Map.Entry<Matcher<AnnotatedType>,Configuration> configurationEntry : configurations.entrySet()) {
061            if(configurationEntry.getKey().matches(signature)){
062                matched.add(configurationEntry.getValue());
063            }
064        }
065        return matched;
066    }
067
068    private Configuration getConfiguration(ASTType type, Class<? extends Annotation> annotation){
069        AnnotatedTypeMatcher annotatedTypeMatcher = new AnnotatedTypeMatcher(type, astClassFactory.getType(annotation));
070        if(!configurations.containsKey(annotatedTypeMatcher)){
071            configurations.put(annotatedTypeMatcher, new Configuration());
072        }
073        return configurations.get(annotatedTypeMatcher);
074    }
075
076    @Override
077    public void addRegistration(Class<? extends Annotation> componentAnnotation, ASTType componentType, String methodName, List<ASTType> parameters) {
078        getConfiguration(componentType, componentAnnotation).registraion = new Registration(methodName, parameters);
079    }
080
081    @Override
082    public void addSuperCall(Class<? extends Annotation> componentAnnotation, ASTType componentType, String methodName, List<ASTType> parameters) {
083        getConfiguration(componentType, componentAnnotation).superCalls.add(new SuperCallMapping(methodName, parameters));
084    }
085
086    @Override
087    public ComponentBuilder component(Class<? extends Annotation> annotation) {
088        return new ComponentBuilder(this, annotation);
089    }
090
091    @Override
092    public void addEvent(Class<? extends Annotation> componentType, ASTType type, EventMapping eventMapping) {
093        getConfiguration(type, componentType).events.add(eventMapping);
094    }
095
096    public void addMapping(Class<? extends Annotation> componentType, ASTType type, InjectionMapping injectionMapping) {
097        getConfiguration(type, componentType).mappings.add(injectionMapping);
098    }
099
100    @Override
101    public void addCallThroughEvent(Class<? extends Annotation> componentType, ASTType type, Class<?> callThroughEventClass) {
102        getConfiguration(type, componentType).callThroughEvents.add(callThroughEventClass);
103    }
104
105    @Override
106    public void addListener(Class<? extends Annotation> componentType, ASTType type, ASTType listenerType, ASTType listenable, String listenerMethod) {
107        getConfiguration(type, componentType).listeners.put(listenerType, new ListenableMethod(listenable, listenerMethod));
108    }
109
110    public List<EventMapping> getEvents(ASTType type, Class<? extends Annotation> annotation){
111        return FluentIterable.from(findConfigurations(type, annotation))
112                .transformAndConcat(new Function<Configuration, Iterable<EventMapping>>() {
113                    public Iterable<EventMapping> apply(Configuration configuration) {
114                        return configuration.events;
115                    }
116                }).toList();
117    }
118
119    public List<InjectionMapping> getMappings(ASTType type, Class<? extends Annotation> annotation) {
120        return FluentIterable.from(findConfigurations(type, annotation))
121                .transformAndConcat(new Function<Configuration, Iterable<InjectionMapping>>() {
122                    public Iterable<InjectionMapping> apply(Configuration configuration) {
123                        return configuration.mappings;
124                    }
125                }).toList();
126    }
127
128    public Map<ASTType, ListenableMethod> getListeners(ASTType type, Class<? extends Annotation> annotation) {
129        Map<ASTType, ListenableMethod> matchedListeners = new HashMap<ASTType, ListenableMethod>();
130
131        for (Configuration configuration : findConfigurations(type, annotation)) {
132            matchedListeners.putAll(configuration.listeners);
133        }
134
135        return matchedListeners;
136    }
137
138    public Set<Class<?>> getCallThroughClasses(ASTType type, Class<? extends Annotation> annotation) {
139        return FluentIterable.from(findConfigurations(type, annotation))
140                .transformAndConcat(new Function<Configuration, Iterable<Class<?>>>() {
141                    public Iterable<Class<?>> apply(Configuration configuration) {
142                        return configuration.callThroughEvents;
143                    }
144                }).toSet();
145    }
146
147    public List<SuperCallMapping> getSuperCalls(ASTType type, Class<? extends Annotation> annotation){
148        return FluentIterable.from(findConfigurations(type, annotation))
149                .transformAndConcat(new Function<Configuration, Iterable<SuperCallMapping>>() {
150                    public Iterable<SuperCallMapping> apply(Configuration configuration) {
151                        return configuration.superCalls;
152                    }
153                }).toList();
154    }
155
156    public Registration getRegistration(ASTType type, Class<? extends Annotation> annotation){
157        for(Configuration configuration : findConfigurations(type, annotation)){
158            if(configuration.registraion != null){
159                return configuration.registraion;
160            }
161        }
162        return null;
163    }
164}