/*
* 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.screen.widget;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
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.screen.DeviceAdaptive.Device;
import org.cruxframework.crux.core.client.screen.DeviceAdaptive.Size;
import org.cruxframework.crux.core.client.screen.DisplayHandler;
import org.cruxframework.crux.core.client.screen.InterfaceConfigException;
import org.cruxframework.crux.core.client.screen.views.ViewFactory;
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.screen.Screen;
import org.cruxframework.crux.core.rebind.screen.ScreenConfigException;
import org.cruxframework.crux.core.rebind.screen.ScreenFactory;
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.CachedGeneratorResult;
import com.google.gwt.core.ext.TreeLogger;
/**
*
* @author Thiago da Rosa de Bustamante
*
*/
public class ViewFactoriesProxyCreator extends AbstractInterfaceWrapperProxyCreator
{
private Set<String> changedViews;
private Map<String, Set<View>> fragmentedViews = new HashMap<String, Set<View>>();
private long lastCompilationTime;
private CachedGeneratorResult lastResult;
private ScreenFactory screenFactory;
private List<View> views;
private static Map<String, ViewFactoryCreator> viewFactoryCache = new HashMap<String, ViewFactoryCreator>();
/**
* @param logger
* @param context
*/
public ViewFactoriesProxyCreator(RebindContext context)
{
super(context, context.getGeneratorContext().getTypeOracle().findType(ViewFactory.class.getCanonicalName()), false);
this.screenFactory = new ScreenFactory(context);
}
@Override
public String create() throws CruxGeneratorException
{
initializeViews();
String className = getProxyQualifiedName();
if (!hasChangedView())
{
if (findCacheableImplementationAndMarkForReuseIfAvailable())
{
return className;
}
}
if (isAlreadyGenerated(className))
{
return className;
}
SourcePrinter printer = getSourcePrinter();
if (printer == null)
{
return className;
}
generateSubTypes(printer);
generateProxyContructor(printer);
generateProxyMethods(printer);
generateProxyFields(printer);
generateProxyResources();
printer.commit();
return className;
}
@Override
public String getProxySimpleName()
{
return super.getProxySimpleName()+"_"+this.getDeviceFeatures();
}
/**
*
* @param sourceWriter
*/
protected void generateCreateViewMethod(SourcePrinter sourceWriter)
{
sourceWriter.println("public void createView(String name, CreateCallback callback) throws InterfaceConfigException{ ");
sourceWriter.println("createView(name, name, callback);");
sourceWriter.println("}");
sourceWriter.println();
sourceWriter.println("public void createView(String name, String id, CreateCallback callback) throws InterfaceConfigException{ ");
sourceWriter.println("if (callback == null){");
sourceWriter.println("callback = CreateCallback.EMPTY_CALLBACK;");
sourceWriter.println("}");
generateViewCreation(sourceWriter, views);
sourceWriter.println("}");
generateFragmentedViewFactoryCreation(sourceWriter);
}
/**
* @param sourceWriter
* @param controllerClassNames
* @param controller
* @param controllerAnnot
*/
protected void generateFragmentedViewFactoryCreation(SourcePrinter sourceWriter)
{
for (String viewFragment : fragmentedViews.keySet())
{
String fragment = viewFragment.replaceAll("\\W", "");
sourceWriter.println("public void __load"+fragment+"(final String name, final String id, final CreateCallback callback){");
sourceWriter.println("GWT.runAsync(new "+RunAsyncCallback.class.getCanonicalName()+"(){");
sourceWriter.println("public void onFailure(Throwable reason){");
sourceWriter.println("Crux.getErrorHandler().handleError(Crux.getMessages().viewFactoryCanNotBeLoaded(\""+fragment+"\"));");
sourceWriter.println("}");
sourceWriter.println("public void onSuccess(){");
Set<View> views = fragmentedViews.get(viewFragment);
boolean first = true;
for (View view : views)
{
if (!first)
{
sourceWriter.print("else ");
}
first = false;
sourceWriter.println("if (StringUtils.unsafeEquals(name, "+EscapeUtils.quote(view.getId())+")){");
generateViewCreator(sourceWriter, view);
sourceWriter.println("}");
}
sourceWriter.println("}");
sourceWriter.println("});");
sourceWriter.println("}");
}
}
/**
*
* @param sourceWriter
*/
protected void generateGetCurrentDeviceMethod(SourcePrinter sourceWriter)
{
sourceWriter.println("public "+Device.class.getCanonicalName()+" getCurrentDevice(){ ");
sourceWriter.println("return "+Device.class.getCanonicalName()+"."+getDeviceFeatures()+";");
sourceWriter.println("}");
}
@Override
protected void generateProxyMethods(SourcePrinter sourceWriter) throws CruxGeneratorException
{
generateCreateViewMethod(sourceWriter);
generateGetCurrentDeviceMethod(sourceWriter);
}
@Override
protected void generateProxyResources()
{
try
{
screenFactory.generateHostPages(getDeviceFeatures());
}
catch (ScreenConfigException e)
{
throw new CruxGeneratorException("Error generating host pages for crux screens.", e);
}
}
/**
* @param sourceWriter
* @param views
*/
protected void generateViewCreation(SourcePrinter sourceWriter, List<View> views)
{
boolean first = true;
for (View view : views)
{
if (!first)
{
sourceWriter.print("else ");
}
first = false;
sourceWriter.println("if (StringUtils.unsafeEquals(name, "+EscapeUtils.quote(view.getId())+")){");
if (!StringUtils.isEmpty(view.getFragment()))
{
Set<View> fragment = fragmentedViews.get(view.getFragment());
if (fragment == null)
{
fragment = new HashSet<View>();
fragmentedViews.put(view.getFragment(), fragment);
}
fragment.add(view);
String fragmentName = view.getFragment().replaceAll("\\W", "");
sourceWriter.println("__load"+fragmentName+"(name, id, callback);");
}
else
{
generateViewCreator(sourceWriter, view);
}
if (view.isRootView())
{
Device currentDevice = Device.valueOf(getDeviceFeatures());
if (currentDevice.getSize().equals(Size.small))
{
String smallViewport = view.getSmallViewport();
if (smallViewport != null && smallViewport.length() > 0)
{
sourceWriter.println(DisplayHandler.class.getCanonicalName()+".configureViewport("+EscapeUtils.quote(smallViewport, false)+");");
}
}
else
{
String largeViewport = view.getLargeViewport();
if (largeViewport != null && largeViewport.length() > 0)
{
sourceWriter.println(DisplayHandler.class.getCanonicalName()+".configureViewport("+EscapeUtils.quote(largeViewport, false)+");");
}
}
if (view.isDisableRefresh())
{
sourceWriter.println(org.cruxframework.crux.core.client.screen.Screen.class.getCanonicalName()+".setRefreshEnabled(false);");
}
}
sourceWriter.println("}");
}
if (!first)
{
sourceWriter.println("else ");
}
sourceWriter.println("throw new InterfaceConfigException(\"View [\"+name+\"] was not found. Check if you import it using useView attribute.\");");
}
@Override
protected String[] getImports()
{
String[] imports = new String[] {
GWT.class.getCanonicalName(),
Crux.class.getCanonicalName(),
FastMap.class.getCanonicalName(),
ViewFactory.class.getCanonicalName(),
StringUtils.class.getCanonicalName(),
com.google.gwt.user.client.ui.Widget.class.getCanonicalName(),
WidgetCreatorContext.class.getCanonicalName(),
InterfaceConfigException.class.getCanonicalName()
};
return imports;
}
/**
*
* @param logger
* @return
* @throws CruxGeneratorException
*/
protected List<Screen> getScreens() throws CruxGeneratorException
{
try
{
List<Screen> screens = new ArrayList<Screen>();
Set<String> screenIDs = screenFactory.getScreens();
for (String screenID : screenIDs)
{
Screen screen = screenFactory.getScreen(screenID, getDeviceFeatures());
if(screen != null)
{
screens.add(screen);
}
}
return screens;
}
catch (ScreenConfigException e)
{
context.getLogger().log(TreeLogger.ERROR, "Error Generating registered element. Can not retrieve module's list of screens.",e);
throw new CruxGeneratorException();
}
}
/**
*
* @return
*/
protected void initializeViews()
{
initializeLastCompilationVariables();
views = new ArrayList<View>();
changedViews = new HashSet<String>();
try
{
for (Screen screen : getScreens())
{
View rootView = screen.getRootView();
views.add(rootView);
if (screen.getLastModified() >= lastCompilationTime)
{
changedViews.add(rootView.getId());
}
}
List<String> viewList = screenFactory.getViewFactory().getViews();
for (String viewName : viewList)
{
View innerView = screenFactory.getViewFactory().getView(viewName, getDeviceFeatures());
if (innerView != null)
{
views.add(innerView);
if (innerView.getLastModified() >= lastCompilationTime)
{
changedViews.add(innerView.getId());
}
}
}
}
catch (ScreenConfigException e)
{
context.getLogger().log(TreeLogger.ERROR, "Error Generating registered element. Can not retrieve list of views.",e);
throw new CruxGeneratorException();
}
}
/**
* @param sourceWriter
* @param view
*/
private void generateViewCreator(SourcePrinter sourceWriter, View view)
{
ViewFactoryCreator factoryCreator = getViewFactoryCreator(view);
try
{
sourceWriter.println("callback.onViewCreated(new "+ factoryCreator.create()+"(id));");
}
finally
{
factoryCreator.prepare(null, true, null, null);
}
}
/**
* @param view
* @return
*/
private ViewFactoryCreator getViewFactoryCreator(View view)
{
ViewFactoryCreator factory = viewFactoryCache.get(view.getId());
if (factory == null)
{
factory = new ViewFactoryCreator(context, view, isChanged(view), getDeviceFeatures());
viewFactoryCache.put(view.getId(), factory);
}
else
{
factory.prepare(context, isChanged(view), view, getDeviceFeatures());
}
return factory;
}
private boolean hasChangedView()
{
return changedViews.size() > 0;
}
private void initializeLastCompilationVariables()
{
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 changedViews.contains(view.getId());
}
}