/*
* 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.crossdevice;
import java.io.PrintWriter;
import java.util.logging.Logger;
import org.cruxframework.crux.core.client.controller.Controller;
import org.cruxframework.crux.core.client.controller.crossdevice.DeviceAdaptiveController;
import org.cruxframework.crux.core.client.controller.crossdevice.DeviceAdaptiveViewContainer;
import org.cruxframework.crux.core.client.screen.DeviceAdaptive;
import org.cruxframework.crux.core.client.screen.DeviceAdaptive.Device;
import org.cruxframework.crux.core.client.screen.Screen;
import org.cruxframework.crux.core.client.screen.ScreenFactory;
import org.cruxframework.crux.core.client.utils.EscapeUtils;
import org.cruxframework.crux.core.rebind.AbstractWrapperProxyCreator;
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.controller.ControllerProxyCreator;
import org.cruxframework.crux.core.rebind.ioc.IocContainerRebind;
import org.cruxframework.crux.core.rebind.screen.View;
import org.cruxframework.crux.core.rebind.screen.widget.ViewFactoryCreator;
import org.w3c.dom.Document;
import com.google.gwt.core.client.GWT;
import com.google.gwt.core.ext.CachedGeneratorResult;
import com.google.gwt.core.ext.typeinfo.JClassType;
import com.google.gwt.core.ext.typeinfo.JMethod;
import com.google.gwt.core.ext.typeinfo.JParameter;
import com.google.gwt.core.ext.typeinfo.JPrimitiveType;
import com.google.gwt.core.ext.typeinfo.JType;
import com.google.gwt.dev.resource.Resource;
import com.google.gwt.event.shared.HasHandlers;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.rebind.ClassSourceFileComposerFactory;
/**
* @author Thiago da Rosa de Bustamante
*
*/
public class DeviceAdaptiveProxyCreator extends AbstractWrapperProxyCreator
{
private JClassType controllerClass;
private String controllerName;
private Device device;
private JClassType deviceAdaptiveClass;
private JClassType deviceAdaptiveControllerClass;
private JClassType hasHandlersClass;
private IocContainerRebind iocContainerRebind;
private long lastCompilationTime;
private Document template;
private CrossDevicesTemplateParser templateParser;
private View view;
private String viewClassName;
private DeviceAdaptiveViewFactoryCreator viewFactoryCreator;
private Resource templateResource;
/**
*
* @param logger
* @param context
* @param baseIntf
*/
public DeviceAdaptiveProxyCreator(RebindContext context, JClassType baseIntf)
{
super(context, baseIntf, true);
initializeLastCompilationVariables();
initializeTemplateParser();
if ((templateResource.getLastModified() >= lastCompilationTime) ||
!findCacheableImplementationAndMarkForReuseIfAvailable())
{
template = templateParser.getDeviceAdaptiveTemplate(templateResource);
deviceAdaptiveControllerClass = context.getGeneratorContext().getTypeOracle().findType(DeviceAdaptiveController.class.getCanonicalName());
deviceAdaptiveClass = context.getGeneratorContext().getTypeOracle().findType(DeviceAdaptive.class.getCanonicalName());
hasHandlersClass = context.getGeneratorContext().getTypeOracle().findType(HasHandlers.class.getCanonicalName());
view = templateParser.getTemplateView(template, baseIntf.getQualifiedSourceName(), templateResource.getLastModified(), device);
initializeController(view);
viewFactoryCreator = new DeviceAdaptiveViewFactoryCreator(context, view, isChanged(view), getDeviceFeatures(), controllerName);
viewClassName = viewFactoryCreator.create();
iocContainerRebind = new IocContainerRebind(context, view, device.toString());
}
}
@Override
public String getProxyQualifiedName()
{
return DeviceAdaptiveController.class.getPackage().getName() + "." + getProxySimpleName();
}
@Override
public String getProxySimpleName()
{
String name = super.getProxySimpleName();
return name+"_"+getDeviceFeatures();
}
/**
*
* @param srcWriter
*/
protected void createController(SourcePrinter srcWriter, String viewVariable)
{
String genClass = new ControllerProxyCreator(context, controllerClass).create();
srcWriter.println("this._controller = new "+genClass+"("+viewVariable+");");
srcWriter.println("(("+DeviceAdaptiveController.class.getCanonicalName()+")this._controller).setBoundWidget(this);");
iocContainerRebind.injectFieldsAndMethods(srcWriter, controllerClass, "this._controller", viewVariable+".getTypedIocContainer()", view, device);
}
@Override
protected void generateProxyContructor(SourcePrinter srcWriter) throws CruxGeneratorException
{
srcWriter.println("public " + getProxySimpleName() + "(){");
String viewVariable = ViewFactoryCreator.createVariableName("view");
srcWriter.println(viewClassName + " " +
viewVariable + " = new "+viewClassName+"("+EscapeUtils.quote(baseIntf.getSimpleSourceName())+"+(_idGen++));");
createController(srcWriter, viewVariable);
srcWriter.println(viewVariable+".setController(this._controller);");
srcWriter.println("initWidget(viewContainer.asWidget());");
srcWriter.println("viewContainer.add("+viewVariable+", true, null);");
srcWriter.println("(("+DeviceAdaptiveController.class.getCanonicalName()+")this._controller).init();");
srcWriter.println("}");
}
@Override
protected void generateProxyFields(SourcePrinter srcWriter) throws CruxGeneratorException
{
srcWriter.println("private "+controllerClass.getQualifiedSourceName()+ControllerProxyCreator.CONTROLLER_PROXY_SUFFIX+" _controller;");
srcWriter.println("private "+DeviceAdaptiveViewContainer.class.getCanonicalName()+ " viewContainer = new "+DeviceAdaptiveViewContainer.class.getCanonicalName()+"();");
for (String messageClass: viewFactoryCreator.getDeclaredMessages().keySet())
{
srcWriter.println("private "+messageClass+" "+viewFactoryCreator.getDeclaredMessages().get(messageClass) + " = GWT.create("+messageClass+".class);");
}
srcWriter.println("private static "+Logger.class.getCanonicalName()+" "+viewFactoryCreator.getLoggerVariable()+" = "+
Logger.class.getCanonicalName()+".getLogger("+getProxySimpleName()+".class.getName());");
srcWriter.println("private static int _idGen = 0;");
}
/**
*
*/
@Override
protected void generateWrapperMethod(JMethod method, SourcePrinter srcWriter)
{
if (mustDelegateToController(method))
{
JType returnType = method.getReturnType().getErasedType();
srcWriter.println(method.getReadableDeclaration(false, false, false, false, true)+"{");
if (returnType != JPrimitiveType.VOID)
{
srcWriter.print("return ");
}
srcWriter.print("this._controller."+method.getName()+"(");
boolean needsComma = false;
for (JParameter parameter : method.getParameters())
{
if (needsComma)
{
srcWriter.print(", ");
}
needsComma = true;
srcWriter.print(parameter.getName());
}
srcWriter.println(");");
srcWriter.println("}");
}
}
@Override
protected String[] getImports()
{
return new String[]{
ScreenFactory.class.getCanonicalName(),
Screen.class.getCanonicalName(),
GWT.class.getCanonicalName()
};
}
/**
* @return a sourceWriter for the proxy class
*/
@Override
protected SourcePrinter getSourcePrinter()
{
String packageName = DeviceAdaptiveController.class.getPackage().getName();
PrintWriter printWriter = context.getGeneratorContext().tryCreate(context.getLogger(), packageName, getProxySimpleName());
if (printWriter == null)
{
return null;
}
ClassSourceFileComposerFactory composerFactory = new ClassSourceFileComposerFactory(packageName, getProxySimpleName());
String[] imports = getImports();
for (String imp : imports)
{
composerFactory.addImport(imp);
}
composerFactory.addImplementedInterface(baseIntf.getQualifiedSourceName());
composerFactory.setSuperclass(Composite.class.getCanonicalName());
return new SourceCodePrinter(composerFactory.createSourceWriter(context.getGeneratorContext(), printWriter), context.getLogger());
}
/**
*
* @return
*/
protected void initializeController(View view)
{
controllerName = templateParser.getTemplateController(view, baseIntf.getQualifiedSourceName(), device);
String controllerClassName;
try
{
controllerClassName = context.getControllers().getController(controllerName, device);
}
catch (ResourceNotFoundException e)
{
throw new CruxGeneratorException("Error generating invoker. Controller ["+controllerName+"] not found.");
}
controllerClass = context.getGeneratorContext().getTypeOracle().findType(controllerClassName);
if (controllerClass == null)
{
String message = "Controller class ["+controllerName+"] , declared on view ["+view.getId()+"], could not be loaded. "
+ "\n Possible causes:"
+ "\n\t 1. Check if any type or subtype used by controller refers to another module and if this module is inherited in the .gwt.xml file."
+ "\n\t 2. Check if your controller or its members belongs to a client package."
+ "\n\t 3. Check the versions of all your modules.";
throw new CruxGeneratorException(message);
}
Controller controllerAnnot = controllerClass.getAnnotation(Controller.class);
if (controllerAnnot == null)
{
throw new CruxGeneratorException("DeviceAdaptive implementation class ["+controllerClass.getQualifiedSourceName()+"] is not a valid Controller. It must be annotated with @Controller annotation.");
}
if (!controllerClass.isAssignableTo(deviceAdaptiveControllerClass))
{
throw new CruxGeneratorException("DeviceAdaptive implementation class ["+controllerClass.getQualifiedSourceName()+"] must externds the base class DeviceAdaptiveController.");
}
}
/**
*
*/
protected void initializeTemplateParser()
{
Device[] devices = Devices.getDevicesForDevice(getDeviceFeatures());
for (Device device : devices)
{
this.device = device;
templateParser = new CrossDevicesTemplateParser(context, baseIntf, device);
templateResource = templateParser.getDeviceAdaptiveTemplateResource();
if (templateResource != null)
{
break;
}
}
if (templateResource == null)
{
throw new CruxGeneratorException("DeviceAdaptive widget does not declare any valid template for device ["+getDeviceFeatures()+"].");
}
}
protected boolean mustDelegateToController(JMethod method)
{
JClassType enclosingType = method.getEnclosingType();
return (!enclosingType.equals(deviceAdaptiveClass) && !enclosingType.equals(hasHandlersClass));
}
private void initializeLastCompilationVariables()
{
CachedGeneratorResult lastResult = context.getGeneratorContext().getCachedGeneratorResult();
if (lastResult == null || !context.getGeneratorContext().isGeneratorResultCachingEnabled())
{
lastCompilationTime = -1;
}
else
{
try
{
lastCompilationTime = lastResult.getTimeGenerated();
}
catch(RuntimeException e)
{
lastCompilationTime = -1;
}
}
}
private boolean isChanged(View view)
{
return (view.getLastModified() >= lastCompilationTime);
}
}