Package org.cruxframework.crux.core.rebind.screen.widget

Source Code of org.cruxframework.crux.core.rebind.screen.widget.ViewFactoryCreator

/*
* 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.io.PrintWriter;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Map;
import java.util.Set;
import java.util.logging.Logger;

import org.cruxframework.crux.core.client.Crux;
import org.cruxframework.crux.core.client.controller.RegisteredControllers;
import org.cruxframework.crux.core.client.datasource.DataSource;
import org.cruxframework.crux.core.client.datasource.RegisteredDataSources;
import org.cruxframework.crux.core.client.ioc.IocContainer;
import org.cruxframework.crux.core.client.screen.DeviceAdaptive.Device;
import org.cruxframework.crux.core.client.screen.InterfaceConfigException;
import org.cruxframework.crux.core.client.screen.LazyPanelWrappingType;
import org.cruxframework.crux.core.client.screen.views.BindableView;
import org.cruxframework.crux.core.client.screen.views.View.RenderCallback;
import org.cruxframework.crux.core.client.screen.views.ViewActivateEvent;
import org.cruxframework.crux.core.client.screen.views.ViewActivateHandler;
import org.cruxframework.crux.core.client.screen.views.ViewDeactivateEvent;
import org.cruxframework.crux.core.client.screen.views.ViewDeactivateHandler;
import org.cruxframework.crux.core.client.screen.views.ViewFactory;
import org.cruxframework.crux.core.client.screen.views.ViewFactoryUtils;
import org.cruxframework.crux.core.client.screen.views.ViewLoadEvent;
import org.cruxframework.crux.core.client.screen.views.ViewLoadHandler;
import org.cruxframework.crux.core.client.screen.views.ViewUnloadEvent;
import org.cruxframework.crux.core.client.screen.views.ViewUnloadHandler;
import org.cruxframework.crux.core.client.utils.EscapeUtils;
import org.cruxframework.crux.core.client.utils.ScriptTagHandler;
import org.cruxframework.crux.core.client.utils.StringUtils;
import org.cruxframework.crux.core.declarativeui.ViewParser;
import org.cruxframework.crux.core.rebind.AbstractProxyCreator;
import org.cruxframework.crux.core.rebind.CruxGeneratorException;
import org.cruxframework.crux.core.rebind.controller.ClientControllers;
import org.cruxframework.crux.core.rebind.controller.ControllerProxyCreator;
import org.cruxframework.crux.core.rebind.controller.RegisteredControllersProxyCreator;
import org.cruxframework.crux.core.rebind.datasource.RegisteredDataSourcesProxyCreator;
import org.cruxframework.crux.core.rebind.dto.DataObjects;
import org.cruxframework.crux.core.rebind.i18n.MessageClasses;
import org.cruxframework.crux.core.rebind.ioc.IocContainerRebind;
import org.cruxframework.crux.core.rebind.resources.Resources;
import org.cruxframework.crux.core.rebind.screen.Event;
import org.cruxframework.crux.core.rebind.screen.View;
import org.cruxframework.crux.core.rebind.screen.resources.ResourcesHandlerProxyCreator;
import org.cruxframework.crux.core.rebind.screen.widget.declarative.DeclarativeFactory;
import org.cruxframework.crux.core.utils.JClassUtils;
import org.cruxframework.crux.core.utils.RegexpPatterns;
import org.json.JSONArray;
import org.json.JSONObject;

import com.google.gwt.core.client.GWT;
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
import com.google.gwt.core.ext.GeneratorContext;
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.typeinfo.JClassType;
import com.google.gwt.core.ext.typeinfo.JMethod;
import com.google.gwt.core.ext.typeinfo.JType;
import com.google.gwt.dev.generator.NameFactory;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.Node;
import com.google.gwt.event.logical.shared.AttachEvent;
import com.google.gwt.event.logical.shared.AttachEvent.Handler;
import com.google.gwt.event.logical.shared.CloseEvent;
import com.google.gwt.event.logical.shared.CloseHandler;
import com.google.gwt.event.logical.shared.ResizeEvent;
import com.google.gwt.event.logical.shared.ResizeHandler;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.logging.client.LogConfiguration;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.Window.ClosingEvent;
import com.google.gwt.user.client.ui.HTMLPanel;
import com.google.gwt.user.client.ui.Panel;
import com.google.gwt.user.client.ui.RootLayoutPanel;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.Widget;
import com.google.gwt.user.rebind.ClassSourceFileComposerFactory;
import com.google.gwt.user.rebind.SourceWriter;

/**
* @author Thiago da Rosa de Bustamante
*
*/
public class ViewFactoryCreator extends AbstractProxyCreator
{
  private static NameFactory nameFactory = new NameFactory();
  private static String viewVariable = "__view";

  private Map<String, Boolean> attachToDOMfactories = new HashMap<String, Boolean>();
  private Map<String, String> declaredMessages = new HashMap<String, String>();
  private Map<String, WidgetCreator<?>> creators = new HashMap<String, WidgetCreator<?>>();
  private Map<String, WidgetCreatorHelper> creatorHelpers = new HashMap<String, WidgetCreatorHelper>();
  private final LazyPanelFactory lazyFactory;
  private final Set<String> lazyPanels = new HashSet<String>();
  private final LinkedList<PostProcessingPrinter> postProcessingCode = new LinkedList<PostProcessingPrinter>();
  private String loggerVariable;
  private String viewPanelVariable;
  private String device;
  private Set<String> rootPanelChildren;
  private Set<String> resources;
  protected final String module;
  protected final View view;
  protected ControllerAccessHandler controllerAccessHandler;
  protected WidgetConsumer widgetConsumer;
  protected String iocContainerClassName;

  /**
   *
   * @author Thiago da Rosa de Bustamante
   *
   */
  public interface WidgetConsumer
  {
    public static EmptyWidgetConsumer EMPTY_WIDGET_CONSUMER = new EmptyWidgetConsumer();

    void consume(SourcePrinter out, String widgetId, String widgetVariableName, String widgetType, JSONObject metaElem);
  }

  /**
   *
   * @author Thiago da Rosa de Bustamante
   *
   */
  public interface LazyCompatibleWidgetConsumer extends WidgetConsumer
  {
    void handleLazyWholeWidgetCreation(SourcePrinter out, String widgetId);
    void handleLazyWrapChildrenCreation(SourcePrinter out, String widgetId);
  }

  /**
   *
   * @author Thiago da Rosa de Bustamante
   *
   */
  public static class EmptyWidgetConsumer implements WidgetConsumer
  {
    public void consume(SourcePrinter out, String widgetId, String widgetVariableName, String widgetType, JSONObject metaElem)
    {
    }
  }


  /**
   * Constructor
   *
   * @param context
   * @param logger
   * @param view
   * @param device
   * @param module
   */
  public ViewFactoryCreator(GeneratorContext context, TreeLogger logger, View view, String device, String module)
    {
    super(logger, context, false);
    this.view = view;
    this.device = device;
    this.module = module;
    this.lazyFactory = new LazyPanelFactory(this);
    this.loggerVariable = createVariableName("logger");
    this.viewPanelVariable = createVariableName("viewPanel");
    this.widgetConsumer = new ViewWidgetConsumer(this);
    this.rootPanelChildren = new HashSet<String>();
    this.controllerAccessHandler = new DefaultControllerAccessor(viewVariable);

    }

  /**
     * Creates a new unique name based off of{@code varName} and adds it to
     * the list of known names.
   *
   * @param varName
   * @return
   */
  public static String createVariableName(String varName)
  {
    return nameFactory.createName(varName);
  }

  /**
   * Retrieve the object responsible for print controller access expressions on client JS
   * @return
   */
  protected ControllerAccessHandler getControllerAccessHandler()
  {
    return this.controllerAccessHandler;
  }

  /**
   * Generate the View fields
   *
   * @param printer
   */
  protected void generateProxyFields(SourcePrinter printer)
    {

    printer.println("private "+RegisteredControllers.class.getCanonicalName()+" registeredControllers;");
    printer.println("private "+RegisteredDataSources.class.getCanonicalName()+" registeredDataSources;");
    printer.println("protected "+ iocContainerClassName +" iocContainer;");

    for (String messageClass: declaredMessages.keySet())
      {
        printer.println("private "+messageClass+" "+declaredMessages.get(messageClass) + " = GWT.create("+messageClass+".class);");
      }
    printer.println("private final "+getViewSuperClassName()+" "+viewVariable+" = this;");
    printer.println("private static "+Logger.class.getCanonicalName()+" "+loggerVariable+" = "+
          Logger.class.getCanonicalName()+".getLogger("+getProxySimpleName()+".class.getName());");
    printer.println("private "+HTMLPanel.class.getCanonicalName()+" "+viewPanelVariable+" = null;");
    }

  /**
   * Generate the View Constructor
   */
  @Override
  protected void generateProxyContructor(SourcePrinter printer) throws CruxGeneratorException
  {
    String regsiteredControllersClass = new RegisteredControllersProxyCreator(logger, context, view, module, iocContainerClassName, device).create();
    String regsiteredDataSourcesClass = new RegisteredDataSourcesProxyCreator(logger, context, view, iocContainerClassName, device).create();

    printer.println("protected "+getProxySimpleName()+"(String id){");
    printer.println("super(id);");
    printer.println("setTitle("+getDeclaredMessage(view.getTitle())+");");
    printer.println("this.iocContainer = new "+iocContainerClassName+"(this);");
    printer.println("this.registeredControllers = new "+regsiteredControllersClass+"(this, iocContainer);");
    printer.println("this.registeredDataSources = new "+regsiteredDataSourcesClass+"(this, iocContainer);");
    generateResources(printer);
    printer.println("}");
  }

  /**
   * Create ClientBundles for the declared resources on View
   * @param printer
   */
  protected void generateResources(SourcePrinter printer)
    {
     for (String resourceClass : resources)
      {
        printer.println(resourceClass+".init();");
      }
    }

  /**
   * Generate the View methods.
   *
     * @param printer
     */
    protected void generateProxyMethods(SourcePrinter printer)
    {
     
      if(printer != null)
      {
        generateGetRegisteredControllersMethod(printer);
        generateCreateDataSourceMethod(printer);
        generateCreateWidgetsMethod(printer);
        generateRenderMethod(printer);
        generateUpdateDimensionsMethods(printer);
        generateInitializeLazyDependenciesMethod(printer);
        generateGetIocContainerMethod(printer);

        if (isDataBindEnabled())
        {
          generateCreateDataObjectMethod(printer);
        }
      }

    }

    protected void generateCreateDataObjectMethod(SourcePrinter printer)
    {
      String dataObjectClass = DataObjects.getDataObject(view.getDataObject());
    printer.println("protected "+ dataObjectClass +" createDataObject(){");
      printer.println("return GWT.create("+dataObjectClass+".class);");
      printer.println("}");
    }

  protected void generateGetIocContainerMethod(SourcePrinter printer)
    {
      printer.println("public "+ IocContainer.class.getCanonicalName() +" getIocContainer(){");
      printer.println("return iocContainer;");
      printer.println("}");

      printer.println("public "+ iocContainerClassName +" getTypedIocContainer(){");
      printer.println("return iocContainer;");
      printer.println("}");
    }


    @Override
    protected void generateSubTypes(SourcePrinter srcWriter) throws CruxGeneratorException
    {
      iocContainerClassName = new IocContainerRebind(logger, context, view, device).create();
      resources = new HashSet<String>();
      Iterator<String> resources = view.iterateResources();
      while (resources.hasNext())
      {
        String resourceKey = resources.next();
        this.resources.add(new ResourcesHandlerProxyCreator(logger, context, resourceKey, view, device).create());
      }
    }

  /**
   * Return the type of a given crux meta tag. This type could be {@code "screen"}
   *  or another string referencing a registered {@code WidgetFactory}.
   *
   * @param cruxMetaElement
   * @return
   */
  protected String getMetaElementType(JSONObject cruxMetaElement)
  {
    return cruxMetaElement.optString("_type");
  }

  /**
   * Returns the creator of the widgets of the given type.
   * @param widgetType
   * @return the creator of the widgets of the given type.
   */
  protected WidgetCreator<?> getWidgetCreator(String widgetType)
  {
    try
        {
          if (!creators.containsKey(widgetType))
          {
            String creatorClassName = WidgetConfig.getClientClass(widgetType);
            Class<?> widgetCreator = Class.forName(creatorClassName);
            WidgetCreator<?> factory = (WidgetCreator<?>) widgetCreator.newInstance();
            factory.setViewFactory(this);
            creators.put(widgetType, factory);
            WidgetCreatorHelper creatorHelper = new WidgetCreatorHelper(widgetCreator);
        creatorHelpers.put(widgetType, creatorHelper);
          }
        }
        catch (Exception e)
        {
          throw new CruxGeneratorException("Error retrieveing widgetFactory for type ["+widgetType+"].",e);
        }
    return creators.get(widgetType);
  }

  /**
     * Returns a helper object to create the code of the widgets of the given type.
   * @param widgetType
   * @return a helper object to create the code of the widgets of the given type.
   */
  protected WidgetCreatorHelper getWidgetCreatorHelper(String widgetType)
  {
    if (!creatorHelpers.containsKey(widgetType))
    {
      if (getWidgetCreator(widgetType) == null)
      {
        return null;
      }
    }
    return creatorHelpers.get(widgetType);
  }

  /**
   * Creates a new widget based on its meta-data element.
   *
   * @param printer
   * @param metaElem
   * @param widgetId
   * @return
   * @throws CruxGeneratorException
   */
  protected String newWidget(SourcePrinter printer, JSONObject metaElem, String widgetId, String widgetType) throws CruxGeneratorException
  {
    return newWidget(printer, metaElem, widgetId, widgetType, this.widgetConsumer, true);
  }

  /**
   * Creates a new widget based on its meta-data element.
   *
   * @param printer
   * @param metaElem
   * @param widgetId
   * @param consumer
   * @param allowWrapperForCreatedWidget
   * @return
   * @throws CruxGeneratorException
   */
  protected String newWidget(SourcePrinter printer, JSONObject metaElem, String widgetId, String widgetType,
      WidgetConsumer consumer, boolean allowWrapperForCreatedWidget)
        throws CruxGeneratorException
  {
    WidgetCreator<?> widgetFactory = getWidgetCreator(widgetType);
    if (widgetFactory == null)
    {
      throw new CruxGeneratorException("Can not found widgetFactory for type: ["+widgetType+"].");
    }

    String widget;
    if (consumer != null && consumer instanceof LazyCompatibleWidgetConsumer && mustRenderLazily(widgetType, metaElem, widgetId))
    {
      String lazyPanelId = ViewFactoryUtils.getLazyPanelId(widgetId, LazyPanelWrappingType.wrapWholeWidget);
      lazyPanels.add(lazyPanelId);
      widget = lazyFactory.getLazyPanel(printer, metaElem, widgetId, LazyPanelWrappingType.wrapWholeWidget);
      consumer.consume(printer, lazyPanelId, widget, widgetType, metaElem);
      ((LazyCompatibleWidgetConsumer)consumer).handleLazyWholeWidgetCreation(printer, widgetId);
    }
    else
    {
      widget = widgetFactory.createWidget(printer, metaElem, widgetId, consumer);
    }
    if (widget == null)
    {
      throw new CruxGeneratorException("Can not create widget ["+widgetId+"]. Verify the widget type.");
    }

    if (allowWrapperForCreatedWidget)
    {
      // No wrappers
    }

    return widget;
  }

  /**
   * Close the current postProcessing scope and schedule the execution of all scope commands.
   *
   * @param printer
   */
  protected void commitPostProcessing(SourcePrinter printer)
  {
    PostProcessingPrinter postProcessingPrinter = this.postProcessingCode.removeLast();
    String postProcessingCode = postProcessingPrinter.toString();
    if (!StringUtils.isEmpty(postProcessingCode))
    {
      printer.println(Scheduler.class.getCanonicalName()+".get().scheduleDeferred(new "+
          ScheduledCommand.class.getCanonicalName()+"(){");
      printer.println("public void execute(){");
      printer.print(postProcessingCode);
      printer.println("}");
      printer.println("});");
    }
  }

  /**
   * Create a new scope for the post processing commands. All commands added by
   * {@code printlnPostProcessing} method will be added to this same scope, what means
   * that they will be fired together. When {@code commitPostProcessing} method is called,
   * the scope is closed and all scope commands are programmed for execution.
   */
  protected void createPostProcessingScope()
  {
    this.postProcessingCode.add(new PostProcessingPrinter());
  }

  /**
   * @return
   */
  protected TreeLogger getLogger()
  {
    return this.logger;
  }

  /**
   * Retrieves the view variable name
   * @return
   */
  public static String getViewVariable()
  {
    return viewVariable;
  }
 
    /**
     *
     * @return
     */
  public String getViewSuperClassName()
    {
      if (isDataBindEnabled())
      {
        return BindableView.class.getCanonicalName()+"<"+DataObjects.getDataObject(view.getDataObject())+">";
      }
      return "View";
    }

  /**
   * Retrieves the logger variable name
   * @return
   */
  protected String getLoggerVariable()
  {
    return loggerVariable;
  }

  /**
   * @return
   */
  protected View getView()
  {
    return this.view;
  }

  /**
     * Gets the code necessary to access a i18n declared property or the own property, if
     * it is not in declarative i18n format.
     *
   * @param property
   * @return
   */
  protected String getDeclaredMessage(String property)
    {
      if (isKeyReference(property))
      {
      if (isResourceReference(property))
      {
        return getResourceAccessExpression(property);
      }
      else
      {
        String[] messageParts = getKeyMessageParts(property);
        String messageClassName = MessageClasses.getMessageClass(messageParts[0]);

        // Checks if declared message is valid
        this.checkDeclaredMessage(messageClassName, messageParts[0], messageParts[1]);

        String messageVariable;

        if (!declaredMessages.containsKey(messageClassName))
        {
          messageVariable= createVariableName("mesg");
          declaredMessages.put(messageClassName, messageVariable);
        }
        else
        {
          messageVariable = declaredMessages.get(messageClassName);
        }
        return messageVariable+"."+messageParts[1]+"()";
      }
      }
      else
      {
        return property==null?null:EscapeUtils.quote(property);
      }
    }

  /**
     * Gets the code necessary to access a resource property or the own property, if
     * it is not in a resource reference format.
     *
   * @param property
   * @return
   */
  protected String getResourceAccessExpression(String property)
  {
      if (isResourceReference(property))
      {
        String[] resourceParts = getResourceParts(property);
        String resourceKey = resourceParts[0];
        String resourceProperty = resourceParts[1];
        String resourceClassName = Resources.getResource(resourceKey, Device.valueOf(device));
       
        if (!view.useResource(resourceKey))
        {
        throw new CruxGeneratorException("The resource ["+resourceKey+"] is not imported into view ["+view.getId()+"]. Use the useResource atribute of view tag to import it first.");
        }
        String resourceObject = "(("+resourceClassName+")getResource("+EscapeUtils.quote(resourceKey)+"))";
       
        StringBuilder out = new StringBuilder();
      JClassType resourceType = context.getTypeOracle().findType(resourceClassName);
      if (resourceType == null)
      {
        String message = "Resource ["+resourceKey+"] , declared on view ["+view.getId()+"], could not be loaded. "
           + "\n Possible causes:"
           + "\n\t 1. Check if any type or subtype used by resource refers to another module and if this module is inherited in the .gwt.xml file."
           + "\n\t 2. Check if your resource or its members belongs to a client package."
           + "\n\t 3. Check the versions of all your modules."
           ;
        throw new CruxGeneratorException(message);
      }
            try
            {
              JClassUtils.buildGetValueExpression(out, resourceType, resourceProperty, resourceObject, false);
            }
            catch (NoSuchFieldException e)
            {
        throw new CruxGeneratorException("Resource ["+resourceKey+"] , declared on view ["+view.getId()+"], has an invalid expression ["+resourceProperty+"]", e);
            }
      return out.toString();
      }
      else
      {
        return property==null?null:EscapeUtils.quote(property);
      }
  }
 
  /**
   * Checks if declared message is valid
   * @param messageClassName
   * @param messageKey
   * @param messageMethod
   * @throws CruxGeneratorException
   */
  private void checkDeclaredMessage(String messageClassName, String messageKey, String messageMethod) throws CruxGeneratorException
  {
    if (StringUtils.isEmpty(messageClassName))
    {
      throw new CruxGeneratorException("Message ["+messageKey+"] , declared on view ["+view.getId()+"], not found.");
    }
    else
    {
      JClassType messageClass = context.getTypeOracle().findType(messageClassName);
      if (messageClass == null)
      {
        String message = "Message class ["+messageKey+"] , declared on view ["+view.getId()+"], could not be loaded. "
                 + "\n Possible causes:"
                 + "\n\t 1. Check if any type or subtype used by message refers to another module and if this module is inherited in the .gwt.xml file."
                 + "\n\t 2. Check if your message or its members belongs to a client package."
                 + "\n\t 3. Check the versions of all your modules."
                 ;
        throw new CruxGeneratorException(message);
      }
      else
      {
        if (!hasMethod(messageClass, messageMethod, new JType[]{}))
        {
          throw new CruxGeneratorException("Method ["+messageMethod+"] of message ["+messageKey+"], declared on view ["+view.getId()+"], does not exist in message class ["+messageClassName+"].");
        }
      }
    }
  }

  /**
   * Verifies if a method exists into a interface
   * @param clazz
   * @param methodName
   * @param params
   * @return
   */
  private boolean hasMethod(JClassType clazz, String methodName, JType[] params)
  {
    if (clazz != null && methodName != null)
    {
      JMethod method = clazz.findMethod(methodName, params);
      if (method != null)
      {
        return true;
      }

      JClassType[] interfaces = clazz.getImplementedInterfaces();
      if (interfaces != null)
      {
        for (JClassType intf : interfaces)
        {
          if (hasMethod(intf, methodName, params))
          {
            return true;
          }
        }
      }
    }

    return false;
  }

    /**
     * Returns <code>true</code> if the given text is an internationalization key.
   * @param text
   * @return <code>true</code> if the given text is an internationalization key.
   */
  protected boolean isKeyReference(String text)
  {
    return text!= null && RegexpPatterns.REGEXP_CRUX_MESSAGE.matcher(text).matches();
  }

    /**
     * Returns <code>true</code> if the given text is a reference to a resource.
   * @param text
   * @return <code>true</code> if the given text is a reference to a resource.
   */
  protected boolean isResourceReference(String text)
  {
    if (text!= null &&  RegexpPatterns.REGEXP_CRUX_RESOURCE.matcher(text).matches())
    {
      String[] parts = getResourceParts(text);
      return (view.useResource(parts[0]));
    }
    return false;
  }

  /**
   * Gets all messages declared on this screen
   * @return
   */
  protected Map<String, String> getDeclaredMessages()
  {
    return declaredMessages;
  }

  /**
   * @return
   */
  protected GeneratorContext getContext()
  {
    return context;
  }

    /**
   * Gets the list of classes used by the ViewFactory.
   *
     * @return
     */
    String[] getImports()
    {
        String[] imports = new String[] {
        GWT.class.getCanonicalName(),
        org.cruxframework.crux.core.client.screen.Screen.class.getCanonicalName(),
        org.cruxframework.crux.core.client.screen.views.View.class.getCanonicalName(),
        StringUtils.class.getCanonicalName(),
        Window.class.getCanonicalName(),
        ViewFactoryUtils.class.getCanonicalName(),
        ViewFactory.CreateCallback.class.getCanonicalName(),
        RootPanel.class.getCanonicalName(),
        RootLayoutPanel.class.getCanonicalName(),
        Element.class.getCanonicalName(),
        Node.class.getCanonicalName(),
        ViewLoadEvent.class.getCanonicalName(),
        Panel.class.getCanonicalName(),
        InterfaceConfigException.class.getCanonicalName(),
        Widget.class.getCanonicalName(),
        Crux.class.getCanonicalName()
    };
      return imports;
  }

  /**
   * Create a new printer for a subType. That subType will be declared on the same package of the
   * {@code ViewFactory}.
   *
     * @param subType
     * @param superClass
     * @param interfaces
     * @param imports
     * @return
     */
    protected SourcePrinter getSubTypeWriter(String subType, String superClass, String[] interfaces, String[] imports)
    {
      return getSubTypeWriter(subType, superClass, interfaces, imports, false);
    }

    /**
   * Create a new printer for a subType. That subType will be declared on the same package of the
   * {@code ViewFactory}.
   *
   * @param packageName
     * @param subType
     * @param superClass
     * @param interfaces
     * @param imports
     * @param isInterface
     * @return
     */
    protected SourcePrinter getSubTypeWriter(String packageName, String subType, String superClass, String[] interfaces, String[] imports)
    {
      return getSubTypeWriter(packageName, subType, superClass, interfaces, imports, false);
    }
   
    /**
   * Create a new printer for a subType. That subType will be declared on the same package of the
   * {@code ViewFactory}.
   *
     * @param subType
     * @param superClass
     * @param interfaces
     * @param imports
     * @param isInterface
     * @return
     */
    protected SourcePrinter getSubTypeWriter(String subType, String superClass, String[] interfaces, String[] imports, boolean isInterface)
    {
      return getSubTypeWriter(null, subType, superClass, interfaces, imports, isInterface);
    }
   
    /**
   * Create a new printer for a subType. That subType will be declared on the package name informed in the first parameter
   * if packageName isEmpty, subType will be declared on the same package of the { @code ViewFactory }.
   *
   * @param packageName
     * @param subType
     * @param superClass
     * @param interfaces
     * @param imports
     * @param isInterface
     * @return
     */
    protected SourcePrinter getSubTypeWriter(String packageName, String subType, String superClass, String[] interfaces, String[] imports, boolean isInterface)
    {
      if(StringUtils.isEmpty(packageName))
      {
        packageName = ViewFactory.class.getPackage().getName();
      }
    PrintWriter printWriter = context.tryCreate(logger, packageName, subType);

    if (printWriter == null)
    {
      return null;
    }

    ClassSourceFileComposerFactory composerFactory = new ClassSourceFileComposerFactory(packageName, subType);
    if (isInterface)
    {
      composerFactory.makeInterface();
    }

    if (imports != null)
    {
      for (String imp : imports)
      {
        composerFactory.addImport(imp);
      }
    }

    if (superClass != null)
    {
      composerFactory.setSuperclass(superClass);
    }

    if (interfaces != null)
    {
      for (String intf : interfaces)
      {
        composerFactory.addImplementedInterface(intf);
      }
    }

    return new SourcePrinter(composerFactory.createSourceWriter(context, printWriter), logger);
  }

  /**
   * Check if the given metaElement refers to a valid widget
   *
   * @param element
   * @return
   */
    protected boolean isValidWidget(JSONObject metaElem)
  {
    String type =  metaElem.optString("_type");
    if (type != null && type.length() > 0 && !StringUtils.unsafeEquals("screen",type))
    {
      return true;
    }
    return false;
  }

  /**
   * @param context
   * @param logger
   * @param device
   */
    protected void prepare(GeneratorContext context, TreeLogger logger, String device)
  {
    this.context = context;
    this.logger = logger;
    this.lazyPanels.clear();
    this.declaredMessages.clear();
    this.postProcessingCode.clear();
    this.device = device;
    this.rootPanelChildren.clear();
  }

  /**
   *
   * @return
   */
    protected String getDevice()
  {
    return this.device;
  }

  /**
   * Print code that will be executed after the viewFactory completes the widgets construction.
   * Note that this code will be executed from inside a Command class. Any variable accessed in
   * this code and declared outside need to be declared as final.
   *
   * @param s code string
   */
    protected void printlnPostProcessing(String s)
  {
    this.postProcessingCode.getLast().println(s);
  }

  /**
   * Generate the code for the View events creation
   *
   * @param printer
   */
  protected void processViewEvents(SourcePrinter printer)
  {
    processHistoryChangedEvt(printer);
    processClosingEvt(printer);
    processCloseEvt(printer);
    processResizedEvt(printer);
    processLoadEvt(printer);
    processUnloadEvt(printer);
    processActivateEvt(printer);
    processDeactivateEvt(printer);
  }

  /**
   * Processes the close event.
   *
   * @param printer
   */
  private void processCloseEvt(SourcePrinter printer)
    {
      Event onClose = view.getEvent("onClose");
    if (onClose != null)
    {
      printer.println(viewVariable+".addWindowCloseHandler(new "+CloseHandler.class.getCanonicalName()+"<Window>(){");
      printer.println("public void onClose("+CloseEvent.class.getCanonicalName()+"<Window> event){");

      EvtProcessor.printEvtCall(printer, onClose.getController()+"."+onClose.getMethod(), "onClose",
          CloseEvent.class, "event", context, view, getControllerAccessHandler(), Device.valueOf(device));

      printer.println("}");
      printer.println("});");
    }
    }

  /**
   * Processes the closing event.
   *
   * @param printer
   */
  private void processClosingEvt(SourcePrinter printer)
    {
      Event onClosing = view.getEvent("onClosing");
    if (onClosing != null)
    {
      printer.println(viewVariable+".addWindowClosingHandler(new Window.ClosingHandler(){");
      printer.println("public void onWindowClosing("+ClosingEvent.class.getCanonicalName()+" event){");

      EvtProcessor.printEvtCall(printer, onClosing.getController()+"."+onClosing.getMethod(), "onClosing",
            ClosingEvent.class, "event", context, view, getControllerAccessHandler(), Device.valueOf(device));

      printer.println("}");
      printer.println("});");
    }
    }

  /**
   * Processes the historyChanged event.
   *
   * @param printer
   */
  private void processHistoryChangedEvt(SourcePrinter printer)
    {
      Event onHistoryChanged = view.getEvent("onHistoryChanged");
    if (onHistoryChanged != null)
    {
      printer.println(viewVariable+".addWindowHistoryChangedHandler(new "+ValueChangeHandler.class.getCanonicalName()+"<String>(){");
      printer.println("public void onValueChange("+ValueChangeEvent.class.getCanonicalName()+"<String> event){");

      EvtProcessor.printEvtCall(printer, onHistoryChanged.getController()+"."+onHistoryChanged.getMethod(),
          "onHistoryChanged", ValueChangeEvent.class, "event", context, view, getControllerAccessHandler(), Device.valueOf(device));

      printer.println("}");
      printer.println("});");
    }
    }

  /**
   * Processes the load event.
   *
   * @param printer
   */
  private void processLoadEvt(SourcePrinter printer)
  {
      Event onLoad = view.getEvent("onLoad");
    if (onLoad != null)
    {
      printer.println(viewVariable+".addViewLoadHandler(new "+ViewLoadHandler.class.getCanonicalName()+"(){");
      printer.println("public void onLoad("+ViewLoadEvent.class.getCanonicalName()+" event){");

      EvtProcessor.printEvtCall(printer, onLoad.getController()+"."+onLoad.getMethod(),
          "onLoad", ViewLoadEvent.class, "event", context, view, getControllerAccessHandler(), Device.valueOf(device));

      printer.println("}");
      printer.println("});");
    }
    }

  /**
   * Processes the unload event.
   *
   * @param printer
   */
  private void processUnloadEvt(SourcePrinter printer)
  {
      Event onUnload = view.getEvent("onUnload");
    if (onUnload != null)
    {
      printer.println(viewVariable+".addViewUnloadHandler(new "+ViewUnloadHandler.class.getCanonicalName()+"(){");
      printer.println("public void onUnload("+ViewUnloadEvent.class.getCanonicalName()+" event){");

      EvtProcessor.printEvtCall(printer, onUnload.getController()+"."+onUnload.getMethod(),
          "onUnload", ViewUnloadEvent.class, "event", context, view, getControllerAccessHandler(), Device.valueOf(device));

      printer.println("}");
      printer.println("});");
    }
    }

  /**
   * Processes the attach event.
   *
   * @param printer
   */
  private void processActivateEvt(SourcePrinter printer)
  {
      Event onActivate = view.getEvent("onActivate");
    if (onActivate != null)
    {
      printer.println(viewVariable+".addViewActivateHandler(new "+ViewActivateHandler.class.getCanonicalName()+"(){");
      printer.println("public void onActivate("+ViewActivateEvent.class.getCanonicalName()+" event){");

      EvtProcessor.printEvtCall(printer, onActivate.getController()+"."+onActivate.getMethod(),
          "onActivate", ViewActivateEvent.class, "event", context, view, getControllerAccessHandler(), Device.valueOf(device));

      printer.println("}");
      printer.println("});");
    }
    }

  /**
   * Processes the attach event.
   *
   * @param printer
   */
  private void processDeactivateEvt(SourcePrinter printer)
  {
      Event onDeactivate = view.getEvent("onDeactivate");
    if (onDeactivate != null)
    {
      printer.println(viewVariable+".addViewDeactivateHandler(new "+ViewDeactivateHandler.class.getCanonicalName()+"(){");
      printer.println("public void onDeactivate("+ViewDeactivateEvent.class.getCanonicalName()+" event){");

      EvtProcessor.printEvtCall(printer, onDeactivate.getController()+"."+onDeactivate.getMethod(),
          "onDeactivate", ViewDeactivateEvent.class, "event", context, view, getControllerAccessHandler(), Device.valueOf(device));

      printer.println("}");
      printer.println("});");
    }
    }

  /**
   * Processes the resized event.
   *
   * @param printer
   * @return
   */
  private Event processResizedEvt(SourcePrinter printer)
    {
      Event onResized = view.getEvent("onResized");
    if (onResized != null)
    {
      printer.println("screen.addResizeHandler(new "+ResizeHandler.class.getCanonicalName()+"(){");
      printer.println("public void onResize("+ResizeEvent.class.getCanonicalName()+" event){");

      EvtProcessor.printEvtCall(printer, onResized.getController()+"."+onResized.getMethod(), "onResized",
          ResizeEvent.class, "event", context, view, getControllerAccessHandler(), Device.valueOf(device));

      printer.println("}");
      printer.println("});");
    }
      return onResized;
    }

  /**
   * Generate the code for a widget creation, based on its metadata.
   *
   * @param printer
   * @param metaElem
   * @param widgetType
   * @return
   */
  private String createWidget(SourcePrinter printer, JSONObject metaElem, String widgetType)
  {
    if (!metaElem.has("id"))
    {
      throw new CruxGeneratorException("The id attribute is required for CRUX Widgets. " +
          "On page ["+view.getId()+"], there is an widget of type ["+widgetType+"] without id.");
    }
    String widget;

    String widgetId = metaElem.optString("id");
    if (widgetId == null || widgetId.length() == 0)
    {
      throw new CruxGeneratorException("The id attribute is required for CRUX Widgets. " +
          "On page ["+view.getId()+"], there is an widget of type ["+widgetType+"] without id.");
    }

    widget = newWidget(printer, metaElem, widgetId, widgetType);
    if (isAttachToDOM(widgetType))
    {
      this.rootPanelChildren.add(widgetId);
    }
    return widget;
  }

  /**
   *
   * @param printer
   */
  private void generateInitializeLazyDependenciesMethod(SourcePrinter printer)
    {
      printer.println("protected native "+org.cruxframework.crux.core.client.collection.Map.class.getCanonicalName()+"<String> initializeLazyDependencies()/*-{");
      printer.println("return "+view.getLazyDependencies().toString()+";");
      printer.println("}-*/;");
    }

  /**
   * @param printer
   */
  private void generateCreateWidgetsMethod(SourcePrinter printer)
    {
      createPostProcessingScope();
    printer.println("protected void createWidgets(){");

    JSONArray elementsMetaData = this.view.getElements();
    processViewEvents(printer);
    processViewDimensions(printer);
    for (int i = 0; i < elementsMetaData.length(); i++)
    {
      JSONObject metaElement = elementsMetaData.optJSONObject(i);

      if (!metaElement.has("_type"))
      {
        throw new CruxGeneratorException("Crux Meta Data contains an invalid meta element (without type attribute). View ID["+view.getId()+"]. "
            + "Validate your view file.");
      }
      if (isValidWidget(metaElement))
      {
        try
        {
          String type = getMetaElementType(metaElement);
          createWidget(printer, metaElement, type);
        }
        catch (Throwable e)
        {
          throw new CruxGeneratorException("Error Creating widget. See Log for more detail.", e);
        }
      }
    }

    printer.println("if ("+LogConfiguration.class.getCanonicalName()+".loggingIsEnabled()){");
    printer.println(loggerVariable+".info(Crux.getMessages().viewContainerViewCreated(getId()));");
    printer.println("}");

    printer.println("}");
    }

    private void processViewDimensions(SourcePrinter printer)
    {
      if (!StringUtils.isEmpty(this.view.getWidth()))
      {
      printer.println("setWidth("+EscapeUtils.quote(this.view.getWidth())+");");
      }
      if (!StringUtils.isEmpty(this.view.getHeight()))
      {
      printer.println("setHeight("+EscapeUtils.quote(this.view.getHeight())+");");
      }
     
    }

  private void generateUpdateDimensionsMethods(SourcePrinter printer)
    {
      printer.println("protected void updateViewHeight(String height){");
    printer.println("if (this."+viewPanelVariable+" != null){");
    printer.println("this."+viewPanelVariable+".setHeight(height);");
//    printer.println("if (this.getContainer() != null && this.getContainer().getContainerPanel(this) != null){");
//    printer.println("this.getContainer().getContainerPanel(this).setHeight(height);");
//    printer.println("}");
    printer.println("}");
    printer.println("}");

    printer.println("protected void updateViewWidth(String width){");
    printer.println("if (this."+viewPanelVariable+" != null){");
    printer.println("this."+viewPanelVariable+".setWidth(width);");
//    printer.println("if (this.getContainer() != null && this.getContainer().getContainerPanel(this) != null){");
//    printer.println("this.getContainer().getContainerPanel(this).setWidth(width);");
//    printer.println("}");
    printer.println("}");
    printer.println("}");
    }
 
 
  /**
   * @param printer
   */
  private void generateRenderMethod(SourcePrinter printer)
    {
      String rootPanelVariable = createVariableName("rootPanel");
      printer.println("protected void render("+Panel.class.getCanonicalName()+" "+rootPanelVariable+", final "+RenderCallback.class.getCanonicalName()+" renderCallback) throws InterfaceConfigException{");

    printer.println("if (this."+viewPanelVariable+" == null){");
    String viewHTML = getViewHTML();
    printer.println("this."+viewPanelVariable+" = new " +
        HTMLPanel.class.getCanonicalName() + "("+viewHTML+");");
   
    if (viewHTML.indexOf("<script") >= 0)
    {
      printer.println("this."+viewPanelVariable+".addAttachHandler(new "+Handler.class.getCanonicalName()+"(){");
      printer.println("private boolean scriptsProcessed = false;");
      printer.println("public void onAttachOrDetach("+AttachEvent.class.getCanonicalName()+" event){");
      printer.println("if (event.isAttached()){");
      printer.println("if (!scriptsProcessed){");
      printer.println(ScriptTagHandler.class.getCanonicalName()+".evaluateScripts("+getProxySimpleName()+".this."+viewPanelVariable+".getElement(), " +
                  "new " + ScriptTagHandler.ScriptLoadCallback.class.getCanonicalName()+"(){");
      printer.println("public void onLoaded(){");
      printer.println("renderCallback.onRendered();");
      printer.println("}");
      printer.println("});");
      printer.println("scriptsProcessed = true;");
      printer.println("} else {");
      printer.println("renderCallback.onRendered();");
      printer.println("}");
      printer.println("}");
      printer.println("}");
      printer.println("});");
    }
   
    printer.println(rootPanelVariable+".add(this."+viewPanelVariable+");");
      for (String widgetId : rootPanelChildren)
        {
      String lazyPanelId = ViewFactoryUtils.getLazyPanelId(widgetId, LazyPanelWrappingType.wrapWholeWidget);
      String widgetViewId;
     
      if (lazyPanels.contains(lazyPanelId))
      {
        widgetViewId = lazyPanelId;
      }
      else
      {
        widgetViewId = widgetId;
      }
       
        printer.println("this."+viewPanelVariable+".addAndReplaceElement(widgets.get("+EscapeUtils.quote(widgetViewId)+"), " +
            "ViewFactoryUtils.getEnclosingPanelId("+EscapeUtils.quote(widgetId)+", "+viewVariable+"));");
        }

    if (viewHTML.indexOf("<script") < 0)
    {
      printer.println("renderCallback.onRendered();");
    }

    commitPostProcessing(printer);
    printer.println("}");
    printer.println("else {");
    printer.println(rootPanelVariable+".add(this."+viewPanelVariable+");");
    if (viewHTML.indexOf("<script") < 0)
    {
      printer.println("renderCallback.onRendered();");
    }
    printer.println("}");

    printer.println("if(!StringUtils.isEmpty(this.width)){");
    printer.println("updateViewWidth(this.width);");
    printer.println("}");
    printer.println("if(!StringUtils.isEmpty(this.height)){");
    printer.println("updateViewHeight(this.height);");
    printer.println("}");
   
   
    printer.println("if ("+LogConfiguration.class.getCanonicalName()+".loggingIsEnabled()){");
    printer.println(loggerVariable+".info(Crux.getMessages().viewContainerViewRendered(getId()));");
    printer.println("}");

    printer.println("}");
    }

  /**
   *
   * @return
   */
  private String getViewHTML()
    {
      String html = EscapeUtils.quote(view.getHtml()).replace(ViewParser.CRUX_VIEW_PREFIX, "\"+"+getViewVariable()+".getPrefix()+\"");
    return html;
    }

  /**
   * @param printer
   */
  private void generateGetRegisteredControllersMethod(SourcePrinter printer)
    {
      printer.println("public "+RegisteredControllers.class.getCanonicalName()+" getRegisteredControllers(){");
      printer.println("return this.registeredControllers;");
      printer.println("}");
    }

  /**
   * @param printer
   */
  private void generateCreateDataSourceMethod(SourcePrinter printer)
    {
      printer.println("public "+DataSource.class.getCanonicalName()+"<?> createDataSource(String dataSource){");
      printer.println("return this.registeredDataSources.getDataSource(dataSource);");
      printer.println("}");
    }

  /**
   * Split the i18n message and separate the messageClass alias from the message method
   *
   * @param text
   * @return
   */
  protected  String[] getKeyMessageParts(String text)
  {
    text = text.substring(2, text.length()-1);
    return text.split("\\.");
  }

  /**
   * Split the resourceReference and separate the resourceClass alias from the requested property
   *
   * @param text
   * @return
   */
  protected  String[] getResourceParts(String text)
  {
    text = text.substring(2, text.length()-1);
    int index = text.indexOf('.');
    String[] result = new String[2];
    result[0] = text.substring(0, index);
    result[1] = text.substring(index+1, text.length());
    return result;
  }

  /**
   * Return the qualified name of the ViewFactory class created for the associated screen
   * @return
   */
  public String getProxyQualifiedName()
    {
      return ViewFactory.class.getPackage().getName() + "." + getProxySimpleName();
    }

  /**
   * Return the simple name of the ViewFactory class created for the associated screen
   * @return
   */
  public String getProxySimpleName()
    {
    String className = view.getId()+"_"+this.device;
    className = className.replaceAll("[\\W]", "_");
    return className;
    }

    /**
   * Creates and returns a new {@link SourceWriter}
     * @return a new {@link SourceWriter}
     */
    protected SourcePrinter getSourcePrinter()
    {
    String packageName = ViewFactory.class.getPackage().getName();
    PrintWriter printWriter = context.tryCreate(logger, packageName, getProxySimpleName());

    if (printWriter == null)
    {
      return null;
    }

    ClassSourceFileComposerFactory composerFactory = new ClassSourceFileComposerFactory(packageName, getProxySimpleName());

    if (isDataBindEnabled())
    {
      composerFactory.setSuperclass(getViewSuperClassName());
    }
    else
    {
      composerFactory.setSuperclass(org.cruxframework.crux.core.client.screen.views.View.class.getCanonicalName());
    }

    String[] imports = getImports();
    for (String imp : imports)
    {
      composerFactory.addImport(imp);
    }

    return new SourcePrinter(composerFactory.createSourceWriter(context, printWriter), logger);
  }

    /**
     * Returns <code>true</code> if widgets of the given type should be attached to DOM after instantiated.
   * @param widgetType
   * @return <code>true</code> if widgets of the given type should be attached to DOM after instantiated.
   */
  private boolean isAttachToDOM(String widgetType)
  {
    if (!attachToDOMfactories.containsKey(widgetType))
    {
      DeclarativeFactory declarativeFactory = getWidgetCreator(widgetType).getClass().getAnnotation(DeclarativeFactory.class);
      attachToDOMfactories.put(widgetType, declarativeFactory.attachToDOM());
    }
    return attachToDOMfactories.get(widgetType);
  }

  /**
   * Returns <code>true</code> if the given widget should be rendered lazily
   * @param widgetType
   * @param metaElem
   * @param widgetId
   * @return <code>true</code> if the given widget should be rendered lazily
   */
  private boolean mustRenderLazily(String widgetType, JSONObject metaElem, String widgetId)
  {
    Class<?> widgetClass = getWidgetCreatorHelper(widgetType).getWidgetType();
    if (Panel.class.isAssignableFrom(widgetClass))
    {
      if (!metaElem.optBoolean("visible", true))
      {
        String lazyPanelId = ViewFactoryUtils.getLazyPanelId(widgetId, LazyPanelWrappingType.wrapWholeWidget);
        return !lazyPanels.contains(lazyPanelId);
      }
    }
    return false;
  }

  /**
   *
   * @return
   */
  protected boolean isDataBindEnabled()
  {
    return !StringUtils.isEmpty(view.getDataObject());
  }
 
    /**
     * Printer for code that should be executed after the screen creation.
     *
     * @author Thiago da Rosa de Bustamante
     */
    private static class PostProcessingPrinter
    {
    private StringBuilder builder = new StringBuilder();
    private String indentation = "";

      /**
       * @see java.lang.Object#toString()
       */
      public String toString()
      {
        return builder.toString();
      }

      /**
       * Indents the next line to be printed
       */
      void indent()
      {
        indentation+="\t";
      }

      /**
       * Outdents the next line to be printed
       */
      void outdent()
      {
        if (indentation.length() > 0)
        {
          indentation = indentation.substring(1);
        }
      }

      /**
       * Prints a line of code into the output.
       * <li>If the line ends with <code>"{"</code>, indents the next line.</li>
       * <li>If the line ends with <code>"}"</code>, <code>"};"</code> or <code>"});"</code>, outdents the next line.</li>
       * @param s
       */
      void println(String s)
      {
        String line = s.trim();

      if (line.endsWith("}") || line.endsWith("});") || line.endsWith("};") || line.endsWith("}-*/;"))
        {
          outdent();
        }

        builder.append(indentation+s+"\n");

        if (line.endsWith("{"))
        {
          indent();
        }
      }
    }

    private static class DefaultControllerAccessor implements ControllerAccessHandler
    {
    private final String viewVariable;

    public DefaultControllerAccessor(String viewVariable)
        {
      this.viewVariable = viewVariable;
        }

    public String getControllerExpression(String controller, Device device)
        {

          return "(("+getControllerImplClassName(controller, device)+")"+viewVariable+".getController("
      +EscapeUtils.quote(controller)+"))";
        }

    public String getControllerImplClassName(String controller, Device device)
        {
      String controllerClass = ClientControllers.getController(controller, device);
          return controllerClass + ControllerProxyCreator.CONTROLLER_PROXY_SUFFIX;
        }
    }

  /**
   * @return the widgetConsumer
   */
  WidgetConsumer getScreenWidgetConsumer()
  {
    return widgetConsumer;
  }
}
TOP

Related Classes of org.cruxframework.crux.core.rebind.screen.widget.ViewFactoryCreator

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.