/*
* Copyright 2012 Cedric Hauber
*
* 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.jboss.errai.mvp.rebind.ioc;
import org.jboss.errai.codegen.BlockStatement;
import org.jboss.errai.codegen.InnerClass;
import org.jboss.errai.codegen.Parameter;
import org.jboss.errai.codegen.builder.ClassDefinitionBuilderAbstractOption;
import org.jboss.errai.codegen.builder.ClassStructureBuilder;
import org.jboss.errai.codegen.builder.impl.ClassBuilder;
import org.jboss.errai.codegen.builder.impl.ObjectBuilder;
import org.jboss.errai.codegen.meta.MetaClass;
import org.jboss.errai.codegen.meta.MetaMethod;
import org.jboss.errai.codegen.meta.MetaParameter;
import org.jboss.errai.codegen.util.Refs;
import org.jboss.errai.codegen.util.Stmt;
import org.jboss.errai.config.util.ClassScanner;
import org.jboss.errai.ioc.client.api.IOCExtension;
import org.jboss.errai.ioc.rebind.ioc.bootstrapper.IOCProcessingContext;
import org.jboss.errai.ioc.rebind.ioc.bootstrapper.IOCProcessorFactory;
import org.jboss.errai.ioc.rebind.ioc.extension.IOCExtensionConfigurator;
import org.jboss.errai.ioc.rebind.ioc.injector.InjectUtil;
import org.jboss.errai.ioc.rebind.ioc.injector.api.InjectionContext;
import org.jboss.errai.mvp.client.annotations.*;
import org.jboss.errai.mvp.client.events.NotifyingAsyncCallback;
import org.jboss.errai.mvp.client.places.Gatekeeper;
import org.jboss.errai.mvp.client.proxy.ProxyImpl;
import org.jboss.errai.mvp.client.proxy.ProxyManager;
import java.util.Collection;
import static org.jboss.errai.codegen.meta.MetaClassFactory.parameterizedAs;
import static org.jboss.errai.codegen.meta.MetaClassFactory.typeParametersOf;
@SuppressWarnings("UnusedDeclaration")
@IOCExtension
public class ProxyManagerIOCExtension implements IOCExtensionConfigurator {
public ProxyManagerIOCExtension() {
}
@Override
public void configure(IOCProcessingContext context, InjectionContext injectionContext, IOCProcessorFactory procFactory) {
}
@Override
public void afterInitialization(IOCProcessingContext context, InjectionContext injectionContext, IOCProcessorFactory procFactory) {
final BlockStatement instanceInitializer = context.getBootstrapClass().getInstanceInitializer();
for (MetaClass klass : ClassScanner.getTypesAnnotatedWith(ProxyClass.class)) {
ClassDefinitionBuilderAbstractOption<? extends ClassStructureBuilder<?>> proxy = createProxy(klass);
for (MetaMethod method : klass.getMethodsAnnotatedWith(ProxyEvent.class)) {
MetaParameter event = method.getParameters()[0];
createMethod(injectionContext, getHandler(klass, method.getName(), event.getType()), klass, proxy, method.getReturnType(), method.getName(), event);
MetaMethod staticMethod = event.getType().getBestMatchingStaticMethod("getType", new Class[]{});
if (staticMethod == null)
instanceInitializer.addStatement(Stmt.invokeStatic(ProxyManager.class, "registerEvent", InjectUtil.invokePublicOrPrivateMethod(injectionContext, Stmt.newObject(event.getType()), event.getType().getBestMatchingMethod("getAssociatedType", new Class[]{})), klass));
else
instanceInitializer.addStatement(Stmt.invokeStatic(ProxyManager.class, "registerEvent", Stmt.invokeStatic(event.getType(), staticMethod.getName()), klass));
}
context.getBootstrapClass().addInnerClass(new InnerClass(proxy.body().getClassDefinition()));
instanceInitializer.addStatement(Stmt.invokeStatic(ProxyManager.class, "registerProxy", Stmt.newObject(proxy.body().getClassDefinition(), klass)));
for (MetaMethod method : klass.getMethodsAnnotatedWith(ContentSlot.class)) {
if (!method.isStatic())
continue;
instanceInitializer.addStatement(Stmt.invokeStatic(ProxyManager.class, "registerHandler", Stmt.invokeStatic(klass, method.getName()), klass));
}
}
Class<? extends Gatekeeper> defaultGateKeeper = null;
Collection<MetaClass> defaultGatekeeperClasses = ClassScanner.getTypesAnnotatedWith(DefaultGatekeeper.class);
if (defaultGatekeeperClasses.size() > 0) {
Class<? extends Gatekeeper> aClass = (Class<? extends Gatekeeper>) defaultGatekeeperClasses.iterator().next().asClass();
defaultGateKeeper = aClass;
}
for (MetaClass klass : ClassScanner.getTypesAnnotatedWith(NameToken.class)) {
boolean useGateKeeper = klass.isAnnotationPresent(UseGatekeeper.class);
if (useGateKeeper || (defaultGateKeeper != null && !klass.isAnnotationPresent(NoGatekeeper.class))) {
Class<? extends Gatekeeper> gateKeeper = defaultGateKeeper;
if (useGateKeeper) {
Class<? extends Gatekeeper> value = klass.getAnnotation(UseGatekeeper.class).value();
gateKeeper = (value != null) ? value : gateKeeper;
}
instanceInitializer.addStatement(Stmt.invokeStatic(ProxyManager.class, "registerPlace", klass.getAnnotation(NameToken.class).value(), klass, gateKeeper));
} else
instanceInitializer.addStatement(Stmt.invokeStatic(ProxyManager.class, "registerPlace", klass.getAnnotation(NameToken.class).value(), klass));
}
}
private MetaClass getHandler(MetaClass klass, String name, MetaClass parameter) {
for (MetaClass handler : klass.getInterfaces()) {
if (handler.getMethod(name, parameter) != null)
return handler;
}
return null;
}
/* new NotifyingAsyncCallback(){
@Override
protected void success(final Presenter presenter) {
Scheduler.get().scheduleDeferred( new Command() {
@Override
public void execute() {
presenter.On...(event);
}
});
}
})*/
private ClassStructureBuilder<?> createMethod(InjectionContext injectionContext, MetaClass handler, MetaClass klass, ClassDefinitionBuilderAbstractOption<? extends ClassStructureBuilder<?>> proxy, MetaClass returnType, String name, MetaParameter event) {
Parameter parameter = Parameter.of(event.getType(), "event", true);
MetaClass metaClass = parameterizedAs(NotifyingAsyncCallback.class, typeParametersOf(klass));
proxy.implementsInterface(handler);
return proxy.body().publicMethod(returnType, name, parameter).body()
.append(
InjectUtil.invokePublicOrPrivateMethod(injectionContext, Stmt.loadVariable("this"),
proxy.body().getClassDefinition().getBestMatchingMethod("getPresenter", metaClass),
createCallback(metaClass, klass, proxy, name, parameter))
).finish();
}
private ObjectBuilder createCallback(MetaClass callbackClass, MetaClass presenterKlass, ClassDefinitionBuilderAbstractOption<? extends ClassStructureBuilder<?>> proxy, String name, Parameter parameter) {
Parameter presenter = Parameter.of(presenterKlass, "presenter", true);
return Stmt.newObject(callbackClass).extend(Stmt.loadVariable("this").invoke("getEventBus")).publicOverridesMethod("success", presenter).append(Stmt.loadVariable(presenter.getName()).invoke(name, Refs.get(parameter.getName()))).finish().finish();
}
private ClassDefinitionBuilderAbstractOption<? extends ClassStructureBuilder<?>> createProxy(MetaClass presenterKlass) {
MetaClass proxyClass =
parameterizedAs(ProxyImpl.class, typeParametersOf(presenterKlass));
ClassDefinitionBuilderAbstractOption<? extends ClassStructureBuilder<?>> definitionStaticOption = ClassBuilder.define("org.jboss.errai.ioc.client.BootstrapperImpl." + presenterKlass.getName() + "Proxy", proxyClass).publicScope().staticClass();
definitionStaticOption.body().publicConstructor(Parameter.of(parameterizedAs(Class.class, typeParametersOf(presenterKlass)), "presenterClass")).callSuper(Refs.get("presenterClass")).finish();
return definitionStaticOption;
}
}