/*
* Copyright 2011 cruxframework.org.
*
* 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.cruxframework.crux.core.rebind.controller;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import org.cruxframework.crux.core.client.Crux;
import org.cruxframework.crux.core.client.collection.FastMap;
import org.cruxframework.crux.core.client.controller.Controller;
import org.cruxframework.crux.core.client.controller.RegisteredControllers;
import org.cruxframework.crux.core.client.screen.DeviceAdaptive.Device;
import org.cruxframework.crux.core.client.utils.EscapeUtils;
import org.cruxframework.crux.core.client.utils.StringUtils;
import org.cruxframework.crux.core.rebind.AbstractInterfaceWrapperProxyCreator;
import org.cruxframework.crux.core.rebind.CruxGeneratorException;
import org.cruxframework.crux.core.rebind.context.RebindContext;
import org.cruxframework.crux.core.rebind.context.scanner.ResourceNotFoundException;
import org.cruxframework.crux.core.rebind.ioc.IocContainerRebind;
import org.cruxframework.crux.core.rebind.screen.View;
import com.google.gwt.core.client.GWT;
import com.google.gwt.core.client.RunAsyncCallback;
import com.google.gwt.core.ext.typeinfo.JClassType;
import com.google.gwt.core.ext.typeinfo.TypeOracle;
import com.google.gwt.dev.generator.NameFactory;
/**
* Generates a RegisteredControllers class.
*
*
* @author Thiago da Rosa de Bustamante
*
*/
public class RegisteredControllersProxyCreator extends AbstractInterfaceWrapperProxyCreator
{
private Map<String, String> controllerClassNames = new HashMap<String, String>();
private Device device;
private String iocContainerClassName;
private IocContainerRebind iocContainerRebind;
private NameFactory nameFactory;
private final View view;
/**
* Constructor
* @param logger
* @param context
*/
public RegisteredControllersProxyCreator(RebindContext context, View view, String iocContainerClassName, String device)
{
super(context, context.getGeneratorContext().getTypeOracle().findType(RegisteredControllers.class.getCanonicalName()), false);
this.view = view;
this.iocContainerClassName = iocContainerClassName;
this.device = Device.valueOf(device);
this.nameFactory = new NameFactory();
this.iocContainerRebind = new IocContainerRebind(context, view, device);
}
@Override
public String getProxySimpleName()
{
String className = view.getId() + "_" + device.toString();
className = className.replaceAll("[\\W]", "_");
return "RegisteredControllers_"+className;
}
/**
*
* @param sourceWriter
* @throws CruxGeneratorException
*/
@Override
protected void generateProxyContructor(SourcePrinter sourceWriter) throws CruxGeneratorException
{
sourceWriter.println("public "+getProxySimpleName()+"("+org.cruxframework.crux.core.client.screen.views.View.class.getCanonicalName()+" view, " +
iocContainerClassName+" iocContainer){");
sourceWriter.println("this.view = view;");
sourceWriter.println("this.iocContainer = iocContainer;");
for (String controller : controllerClassNames.keySet())
{
JClassType controllerClass = getControllerClass(controller);
if (!isControllerLazy(controllerClass))
{
String controllerVar = createController(sourceWriter, controller);
sourceWriter.println("controllers.put(\""+controller+"\", "+controllerVar+");");
}
}
sourceWriter.println("}");
}
/**
* @see org.cruxframework.crux.core.rebind.AbstractProxyCreator#generateProxyFields(com.google.gwt.user.rebind.SourceWriter)
*/
@Override
protected void generateProxyFields(SourcePrinter srcWriter) throws CruxGeneratorException
{
srcWriter.println("private FastMap<Object> controllers = new FastMap<Object>();");
srcWriter.println("private "+org.cruxframework.crux.core.client.screen.views.View.class.getCanonicalName()+" view;");
srcWriter.println("private "+iocContainerClassName+" iocContainer;");
}
/**
* @see org.cruxframework.crux.core.rebind.AbstractProxyCreator#generateProxyMethods(com.google.gwt.user.rebind.SourceWriter)
*/
@Override
protected void generateProxyMethods(SourcePrinter sourceWriter) throws CruxGeneratorException
{
generateGetControllertMethod(sourceWriter);
}
/**
* @see org.cruxframework.crux.core.rebind.AbstractProxyCreator#generateSubTypes(com.google.gwt.user.rebind.SourceWriter)
*/
@Override
protected void generateSubTypes(SourcePrinter srcWriter) throws CruxGeneratorException
{
Set<String> usedWidgets = new HashSet<String>();
generateControllersForView(view);
Iterator<org.cruxframework.crux.core.rebind.screen.Widget> screenWidgets = view.iterateWidgets();
while (screenWidgets.hasNext())
{
String widgetType = screenWidgets.next().getType();
usedWidgets.add(widgetType);
}
generateControllersForWidgets(usedWidgets);
}
/**
* @return
*/
@Override
protected String[] getImports()
{
String[] imports = new String[] {
GWT.class.getCanonicalName(),
org.cruxframework.crux.core.client.screen.Screen.class.getCanonicalName(),
RunAsyncCallback.class.getCanonicalName(),
Crux.class.getCanonicalName(),
FastMap.class.getCanonicalName(),
StringUtils.class.getCanonicalName()
};
return imports;
}
/**
*
* @param sourceWriter
* @param controller
* @return
*/
private String createController(SourcePrinter sourceWriter, String controller)
{
String controllerClassName = controllerClassNames.get(controller);
String controllerVar = nameFactory.createName("__cont");
sourceWriter.println(controllerClassName+" "+controllerVar+" = new "+controllerClassName+"(this.view);");
JClassType controllerClass = getControllerClass(controller);
iocContainerRebind.injectFieldsAndMethods(sourceWriter, controllerClass, controllerVar, "iocContainer", view, device);
return controllerVar;
}
/**
* Generate the block to include controller object.
* @param controller
* @param module
*/
private void generateControllerBlock(String controller)
{
try
{
JClassType controllerClass = getControllerClass(controller);
if (!controllerClassNames.containsKey(controller) && controllerClass!= null)
{
String genClass = new ControllerProxyCreator(context, controllerClass).create();
controllerClassNames.put(controller, genClass);
}
}
catch (Throwable e)
{
throw new CruxGeneratorException("Error for register client event handler. Controller: ["+controller+"].", e);
}
}
/**
* Generate wrapper classes for event handling.
* @param view
*/
private void generateControllersForView(View view)
{
Iterator<String> controllers = view.iterateControllers();
while (controllers.hasNext())
{
String controller = controllers.next();
generateControllerBlock(controller);
}
controllers = context.getControllers().iterateGlobalControllers();
while (controllers.hasNext())
{
String controller = controllers.next();
JClassType controllerClass = getControllerClass(controller);
if (controllerClass != null)
{
generateControllerBlock(controller);
}
}
}
/**
* @param usedWidgets
*/
private void generateControllersForWidgets(Set<String> usedWidgets)
{
Iterator<String> widgets = usedWidgets.iterator();
while (widgets.hasNext())
{
Iterator<String> controllers = context.getControllers().iterateWidgetControllers(widgets.next());
if (controllers != null)
{
while (controllers.hasNext())
{
String controller = controllers.next();
JClassType controllerClass = getControllerClass(controller);
if (controllerClass != null)
{
generateControllerBlock(controller);
}
}
}
}
}
/**
* @param sourceWriter
*/
private void generateGetControllertMethod(SourcePrinter sourceWriter)
{
sourceWriter.println("public <T> T getController(String controller){");
sourceWriter.println("T ret = (T)controllers.get(controller);");
sourceWriter.println("if (ret == null){");
boolean first = true;
for (String controller : controllerClassNames.keySet())
{
JClassType controllerClass = getControllerClass(controller);
if (isControllerLazy(controllerClass))
{
if (!first)
{
sourceWriter.print("else ");
}
first = false;
sourceWriter.println("if ("+StringUtils.class.getCanonicalName()+".unsafeEquals(controller, "+EscapeUtils.quote(controller)+")){");
String controllerVar = createController(sourceWriter, controller);
if (isControllerStatefull(controllerClass))
{
sourceWriter.println("controllers.put("+EscapeUtils.quote(controller)+", "+controllerVar+");");
}
else
{
sourceWriter.println("ret = (T) "+controllerVar+";");
}
sourceWriter.println("}");
}
}
sourceWriter.println("if (ret == null){");
sourceWriter.println("ret = (T)controllers.get(controller);");
sourceWriter.println("}");
sourceWriter.println("}");
sourceWriter.println("return ret;");
sourceWriter.println("}");
}
/**
* @param controller
* @return
*/
private JClassType getControllerClass(String controller)
{
TypeOracle typeOracle = context.getGeneratorContext().getTypeOracle();
assert (typeOracle != null);
try
{
return typeOracle.findType(context.getControllers().getController(controller, device));
}
catch (ResourceNotFoundException e)
{
throw new CruxGeneratorException("Can not found the controller ["+controller+"]. Check your classpath and the inherit modules", e);
}
}
/**
* @param controllerClass
* @return true if this controller can be loaded in lazy mode
* @throws CruxGeneratorException
*/
private boolean isControllerLazy(JClassType controllerClass) throws CruxGeneratorException
{
Controller controllerAnnot = controllerClass.getAnnotation(Controller.class);
return (controllerAnnot == null || controllerAnnot.lazy());
}
/**
* @param controllerClass
* @return true if this controller can be loaded in lazy mode
* @throws CruxGeneratorException
*/
private boolean isControllerStatefull(JClassType controllerClass) throws CruxGeneratorException
{
Controller controllerAnnot = controllerClass.getAnnotation(Controller.class);
return (controllerAnnot == null || controllerAnnot.stateful());
}
}