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 com.google.common.collect.ImmutableSet;
021import com.sun.codemodel.*;
022import org.androidtransfuse.TransfuseAnalysisException;
023import org.androidtransfuse.adapter.ASTMethod;
024import org.androidtransfuse.adapter.ASTParameter;
025import org.androidtransfuse.adapter.ASTType;
026import org.androidtransfuse.adapter.ASTVoidType;
027import org.androidtransfuse.adapter.element.ASTElementFactory;
028import org.androidtransfuse.analysis.astAnalyzer.ManualSuperAspect;
029import org.androidtransfuse.experiment.*;
030import org.androidtransfuse.gen.ClassGenerationUtil;
031import org.androidtransfuse.gen.UniqueVariableNamer;
032import org.androidtransfuse.model.InjectionNode;
033import org.androidtransfuse.model.MethodDescriptor;
034
035import javax.inject.Inject;
036import java.util.Set;
037
038/**
039 * @author John Ericksen
040 */
041public class ManualSuperGenerator implements Generation {
042
043    public static final String SUPER_NAME = "SUPER";
044    private static final String SUPER_CALLER_CLASS_NAME = "SuperCaller";
045
046    private final UniqueVariableNamer namer;
047    private final ClassGenerationUtil generationUtil;
048    private final ASTElementFactory astElementFactory;
049    private final ASTMethod registrationMethod;
050
051    @org.androidtransfuse.annotations.Factory
052    public interface Factory {
053
054        ManualSuperGenerator build(ASTMethod registrationMethod);
055    }
056
057    @Inject
058    public ManualSuperGenerator(UniqueVariableNamer namer, ClassGenerationUtil generationUtil, ASTElementFactory astElementFactory, ASTMethod registrationMethod) {
059        this.namer = namer;
060        this.generationUtil = generationUtil;
061        this.astElementFactory = astElementFactory;
062        this.registrationMethod = registrationMethod;
063    }
064
065    @Override
066    public void schedule(final ComponentBuilder builder, final ComponentDescriptor descriptor) {
067
068        builder.add(registrationMethod, GenerationPhase.POSTINJECTION, new ComponentMethodGenerator() {
069            @Override
070            public void generate(MethodDescriptor methodDescriptor, JBlock block) {
071                ImmutableSet<ManualSuperAspect.Method> methods = FluentIterable.from(builder.getExpressionMap().keySet())
072                        .transformAndConcat(new Function<InjectionNode, Set<ManualSuperAspect.Method>>() {
073                            @Override
074                            public Set<ManualSuperAspect.Method> apply(InjectionNode injectionNode) {
075                                if (injectionNode.containsAspect(ManualSuperAspect.class)) {
076                                    ManualSuperAspect aspect = injectionNode.getAspect(ManualSuperAspect.class);
077                                    return aspect.getMethods();
078                                }
079                                return ImmutableSet.of();
080                            }
081                        })
082                        .toSet();
083
084                try {
085                    if(!methods.isEmpty()) {
086                        JDefinedClass superClass = builder.getDefinedClass()._class(JMod.PUBLIC, SUPER_CALLER_CLASS_NAME);
087
088                        builder.getDefinedClass().field(JMod.PUBLIC, superClass, SUPER_NAME, JExpr._new(superClass));
089
090                        for (ManualSuperAspect.Method method : methods) {
091                            ASTMethod astMethod = astElementFactory.findMethod(descriptor.getType(), method.getName(), method.getParameters().toArray(new ASTType[method.getParameters().size()]));
092
093                            if (astMethod != null) {
094                                writeSuperCallingMethod(builder.getDefinedClass(), superClass, astMethod);
095                            }
096                            //todo: validation?
097                        }
098                    }
099                } catch (JClassAlreadyExistsException e) {
100                    throw new TransfuseAnalysisException("SUPER Class already exists", e);
101                }
102            }
103
104            private void writeSuperCallingMethod(JDefinedClass target, JDefinedClass superClass, ASTMethod astMethod) {
105                JMethod superCallingMethod = superClass.method(JMod.PUBLIC, generationUtil.ref(astMethod.getReturnType()), astMethod.getName());
106
107                JBlock body = superCallingMethod.body();
108                JInvocation invocation = target.staticRef("super").invoke(astMethod.getName());
109                if(astMethod.getReturnType() == ASTVoidType.VOID){
110                    body.add(invocation);
111                }
112                else{
113                    body._return(invocation);
114                }
115
116                for (ASTParameter parameter : astMethod.getParameters()) {
117                    JVar param = superCallingMethod.param(generationUtil.ref(parameter.getASTType()), namer.generateName(parameter.getASTType()));
118                    invocation.arg(param);
119                }
120            }
121        });
122
123    }
124}