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

Source Code of org.cruxframework.crux.core.rebind.screen.widget.ChildrenAnnotationScanner$AllowedOccurences

/*
* 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.HashMap;
import java.util.Map;

import org.cruxframework.crux.core.client.screen.DeviceAdaptive.Device;
import org.cruxframework.crux.core.client.screen.LazyPanelWrappingType;
import org.cruxframework.crux.core.client.screen.views.ViewFactoryUtils;
import org.cruxframework.crux.core.client.utils.EscapeUtils;
import org.cruxframework.crux.core.client.utils.StringUtils;
import org.cruxframework.crux.core.declarativeui.LazyWidgets;
import org.cruxframework.crux.core.declarativeui.LazyWidgets.WidgetLazyChecker;
import org.cruxframework.crux.core.rebind.AbstractProxyCreator.SourcePrinter;
import org.cruxframework.crux.core.rebind.CruxGeneratorException;
import org.cruxframework.crux.core.rebind.screen.widget.ViewFactoryCreator.LazyCompatibleWidgetConsumer;
import org.cruxframework.crux.core.rebind.screen.widget.ViewFactoryCreator.WidgetConsumer;
import org.cruxframework.crux.core.rebind.screen.widget.WidgetCreatorAnnotationsProcessor.ChildProcessor;
import org.cruxframework.crux.core.rebind.screen.widget.WidgetCreatorAnnotationsProcessor.ChildrenProcessor;
import org.cruxframework.crux.core.rebind.screen.widget.creator.children.AllChildProcessor;
import org.cruxframework.crux.core.rebind.screen.widget.creator.children.AnyWidgetChildProcessor;
import org.cruxframework.crux.core.rebind.screen.widget.creator.children.ChoiceChildProcessor;
import org.cruxframework.crux.core.rebind.screen.widget.creator.children.HasPostProcessor;
import org.cruxframework.crux.core.rebind.screen.widget.creator.children.SequenceChildProcessor;
import org.cruxframework.crux.core.rebind.screen.widget.creator.children.TextChildProcessor;
import org.cruxframework.crux.core.rebind.screen.widget.creator.children.WidgetChildProcessor;
import org.cruxframework.crux.core.rebind.screen.widget.creator.children.WidgetChildProcessor.AnyWidget;
import org.cruxframework.crux.core.rebind.screen.widget.declarative.TagChild;
import org.cruxframework.crux.core.rebind.screen.widget.declarative.TagChildLazyConditions;
import org.cruxframework.crux.core.rebind.screen.widget.declarative.TagChildren;
import org.cruxframework.crux.core.rebind.screen.widget.declarative.TagConstraints;
import org.cruxframework.crux.core.utils.ClassUtils;
import org.json.JSONArray;
import org.json.JSONObject;

import com.google.gwt.user.client.ui.HasText;
import com.google.gwt.user.client.ui.Widget;

/**
* @author Thiago da Rosa de Bustamante
*
*/
class ChildrenAnnotationScanner
{
  private static final int UNBOUNDED = -1;

  private WidgetCreatorHelper factoryHelper;
  private LazyPanelFactory lazyFactory;
  private Map<String, ChildrenProcessor> scannedProcessors;
 
  private final WidgetCreator<?> widgetCreator;

  /**
   * @param widgetCreator
   * @param type
   */
  ChildrenAnnotationScanner(WidgetCreator<?> widgetCreator, Class<?> type)
    {
    this.factoryHelper = new WidgetCreatorHelper(type);
    this.widgetCreator = widgetCreator;
    this.lazyFactory = new LazyPanelFactory(widgetCreator.getViewFactory());
    }

  /**
   * @return
   */
  ChildrenProcessor scanChildren()
    {
    scannedProcessors = new HashMap<String, WidgetCreatorAnnotationsProcessor.ChildrenProcessor>();
    return scanChildren(factoryHelper.getFactoryClass(), false);
    }
   
  /**
   * @param isAnyWidget
   * @param widgetProperty
   * @param lazyChecker
   * @param processor
   * @return
   */
  @SuppressWarnings({ "unchecked", "rawtypes" })
  private ChildProcessor createChildProcessor(final boolean isAnyWidget, final String widgetProperty,
                                    final WidgetLazyChecker lazyChecker,  final WidgetChildProcessor processor, final Device[] supportedDevices)
    {
        final boolean isHasPostProcessor = processor instanceof HasPostProcessor;
      return new ChildProcessor()
    {
            public void processChild(SourcePrinter out, WidgetCreatorContext context)
      {
              if (widgetCreator.isCurrentDeviceSupported(supportedDevices))
              {
                if (isAnyWidget)
                {
                  processAnyWidgetChild(out, context);
                }
                else
                {
                  try
                  {
                    processor.processChildren(out, context);
                  }
                  catch (Exception e)
                  {
                    throw new CruxGeneratorException("Error invoking ChildProcessor method.",e);
                  }
                  processChildren(out, context);
                }
        }
      }

      /**
       * @param out
       * @param context
       */
      private void processAnyWidgetChild(SourcePrinter out, WidgetCreatorContext context)
            {
        String childWidget;
        WidgetConsumer consumer = widgetCreator.getViewFactory().getScreenWidgetConsumer();
        if (consumer != null && consumer instanceof LazyCompatibleWidgetConsumer && lazyChecker != null && lazyChecker.isLazy(context.getWidgetElement()))
        {
          childWidget = lazyFactory.getLazyPanel(out, context.getChildElement(), context.getWidgetId(), LazyPanelWrappingType.wrapChildren);
          String lazyPanelId = ViewFactoryUtils.getLazyPanelId(context.getWidgetId(), LazyPanelWrappingType.wrapChildren);
          consumer.consume(out, lazyPanelId, childWidget, widgetCreator.getWidgetFactoryDeclaration(), context.getWidgetElement());
          ((LazyCompatibleWidgetConsumer)consumer).handleLazyWrapChildrenCreation(out, context.getWidgetId());
        }
        else
        {
          childWidget = widgetCreator.createChildWidget(out, context.getChildElement(), context);
        }
        boolean childPartialSupport = widgetCreator.hasChildPartialSupport(context.getChildElement());
        if (childPartialSupport)
        {
          out.println("if ("+widgetCreator.getChildWidgetClassName(context.getChildElement())+".isSupported()){");
        }
        if (!Widget.class.isAssignableFrom(widgetCreator.getChildWidgetClass(context.getChildElement())))
        {
          childWidget = childWidget+".asWidget()";
        }
       
        if (StringUtils.isEmpty(widgetProperty))
        {
          out.println(context.getWidget()+".add("+childWidget+");");
        }
        else
        {
          out.println(context.getWidget()+"."+ClassUtils.getSetterMethod(widgetProperty)+"("+childWidget+");");
        }
        if (childPartialSupport)
        {
          out.println("}");
        }
            }

            @Override
            void postProcessChild(SourcePrinter out, WidgetCreatorContext context)
            {
        if (isHasPostProcessor)
              {
                ((HasPostProcessor)processor).postProcessChildren(out, context);
              }
            }
    };
    }

  /**
   * @param acceptNoChildren
   * @param childrenProcessor
   * @param childProcessorClass
   * @param isAgregator
   * @param processor
   */
  private void createChildProcessorForMultipleChildrenProcessor(boolean acceptNoChildren, ChildrenProcessor childrenProcessor,
                                  Class<?> childProcessorClass, boolean isAgregator,
                                  WidgetChildProcessor<?> processor, Device[] supportedDevices)
    {
      TagConstraints processorAttributes = this.factoryHelper.getChildtrenAttributesAnnotation(childProcessorClass);
      final String widgetProperty = (processorAttributes!=null?processorAttributes.widgetProperty():"");
      String tagName = (processorAttributes!=null?processorAttributes.tagName():"");

      final boolean isAnyWidget = (AnyWidgetChildProcessor.class.isAssignableFrom(childProcessorClass));
      final boolean isAnyWidgetType = (processorAttributes!=null && (AnyWidget.class.isAssignableFrom(processorAttributes.type()) ||
                                       WidgetCreator.class.isAssignableFrom(processorAttributes.type())));
     
      TagChildLazyConditions lazyConditions = childProcessorClass.getAnnotation(TagChildLazyConditions.class);
      final WidgetLazyChecker lazyChecker = (lazyConditions== null?null:LazyWidgets.initializeLazyChecker(lazyConditions));
     
      final String childName = getChildTagName(tagName, isAgregator, (isAnyWidget || isAnyWidgetType));

      ChildProcessor childProcessor = createChildProcessor(isAnyWidget, widgetProperty, lazyChecker, processor, supportedDevices);
      if (!isAnyWidget && !isAnyWidgetType)
      {
        childProcessor.setChildrenProcessor(scanChildren(childProcessorClass, isAgregator));
      }

      childrenProcessor.addChildProcessor(childName, childProcessor);
    }

  /**
   * @param processorClass
   * @param child
   * @param acceptNoChildren
   * @return
   */
  private ChildrenProcessor createChildProcessorForText(Class<?> processorClass, TagChild child, final boolean acceptNoChildren)
    {
    Class<?> childProcessor = child.value();
    TagConstraints processorAttributes = factoryHelper.getChildtrenAttributesAnnotation(childProcessor);
    final String widgetProperty = processorAttributes.widgetProperty();
    final boolean isHasText = HasText.class.isAssignableFrom(factoryHelper.getWidgetType());
   
      ChildrenProcessor childrenProcessor = new ChildrenProcessor()
    {
      public void processChildren(SourcePrinter out, WidgetCreatorContext context)
      {
        String child = widgetCreator.ensureTextChild(context.getChildElement(), acceptNoChildren, context.getWidgetId(), false);
        if (!StringUtils.isEmpty(child))
        {
          if (!StringUtils.isEmpty(widgetProperty))
          {
            out.println(context.getWidget()+"."+ClassUtils.getSetterMethod(widgetProperty)+"("+EscapeUtils.quote(child)+");");
          }
          else if (isHasText)
          {
            out.println(context.getWidget()+".setText("+EscapeUtils.quote(child)+");");
          }
          else
          {
            throw new CruxGeneratorException("Can not process the text property for widget ["+context.getWidgetId()+"]. The widget is not assignable to HasText and its factory does not define any property for text value.");
          }
        }
      }
    };
   
    scannedProcessors.put(processorClass.getCanonicalName(), childrenProcessor);
    return childrenProcessor;
    }
 
  /**
   * @param processorClass
   * @param children
   * @param acceptNoChildren
   * @param isAgregatorChild
   * @return
   */
  private ChildrenProcessor createChildrenProcessorForMultipleChildren(Class<?> processorClass, TagChildren children, boolean acceptNoChildren, boolean isAgregatorChild)
    {
    try
    {
      ChildrenProcessor childrenProcessor = doCreateChildrenProcessorForMultipleChildren(processorClass, acceptNoChildren, isAgregatorChild);

      boolean hasAgregator = false;
      for (TagChild child : children.value())
            {
        if (child.autoProcess())
        {
          Class<?> childProcessorClass = child.value();
          final boolean isTextProcessor = TextChildProcessor.class.isAssignableFrom(childProcessorClass);
          if (isTextProcessor)
          {
            throw new CruxGeneratorException("A TextProcessor child processor can not have any sibling processor defined.");
          }
          boolean isAgregator = isAgregatorProcessor(childProcessorClass);
          if (isAgregator)
          {
            if (hasAgregator)
            {
              throw new CruxGeneratorException("You can not define more than one agregator under the same parent processor.");
            }
            hasAgregator = true;
          }

          WidgetChildProcessor<?> processor;
          processor = child.value().newInstance();
          processor.setWidgetCreator(widgetCreator);

          createChildProcessorForMultipleChildrenProcessor(acceptNoChildren, childrenProcessor,
              childProcessorClass, isAgregator,
              processor, child.supportedDevices());
        }
            }
      return childrenProcessor;
    }
    catch (Exception e)
    {
      throw new CruxGeneratorException("Error creating ChildrenProcessor class.", e);
    }
    }

  /**
   * @param processorClass
   * @param child
   * @param acceptNoChildren
   * @return
   */
  private ChildrenProcessor createChildrenProcessorForSingleChild(Class<?> processorClass, TagChild child, final boolean acceptNoChildren)
    {
    try
    {
      if (!child.autoProcess())
      {
        return null;
      }
      Class<?> childProcessor = child.value();
      final boolean isTextProcessor = TextChildProcessor.class.isAssignableFrom(childProcessor);
      if (isTextProcessor)
      {
        return createChildProcessorForText(processorClass, child, acceptNoChildren);
      }

      WidgetChildProcessor<?> processor;
      processor = child.value().newInstance();
      processor.setWidgetCreator(widgetCreator);

      Device[] supportedDevices = child.supportedDevices();
      ChildrenProcessor childrenProcessor = doCreateChildrenProcessorForSingleChild(processorClass,
          acceptNoChildren, processor, childProcessor, supportedDevices);

      return childrenProcessor;
    }
    catch (Exception e)
    {
      throw new CruxGeneratorException("Error creating ChildrenProcessor class.", e);
    }
    } 
 
  /**
   * @param processorClass
   * @param acceptNoChildren
   * @param isAgregatorChild
   * @param processor
   * @param processorMethod
   * @param childProcessorClass
   * @return
   */
  private ChildrenProcessor doCreateChildrenProcessorForMultipleChildren(Class<?> processorClass, final boolean acceptNoChildren,
                                      final boolean isAgregatorChild)
    {
    ChildrenProcessor childrenProcessor = new ChildrenProcessor()
    {
      public void processChildren(SourcePrinter out, WidgetCreatorContext context)
      {
        String childName;
        if (isAgregatorChild)
        {
          childName = getChildName(context.getChildElement());
          processChild(out, context, childName);
        }
        else
        {
          JSONArray children = widgetCreator.ensureChildren(context.getChildElement(), acceptNoChildren, context.getWidgetId());
          if (children != null)
          {
            for (int i = 0; i < children.length(); i++)
            {
              JSONObject child = children.optJSONObject(i);

              childName = getChildName(child);
              context.setChildElement(child);
              processChild(out, context, childName);
            }
          }
        }
      }

      private String getChildName(JSONObject child)
            {
              String childName;
              if (widgetCreator.isWidget(child))
              {
                childName = "_innerWidget";
              }
              else
              {
                childName = WidgetCreator.getChildName(child);
              }
              if (!hasChildProcessor(childName))
              {
                childName = "_agregator";
              }
              return childName;
            }
    };
    scannedProcessors.put(processorClass.getCanonicalName(), childrenProcessor);

    return childrenProcessor;
    }

  /**
   * @param processorClass
   * @param acceptNoChildren
   * @param processor
   * @param childProcessorClass
   * @return
   */
  private ChildrenProcessor doCreateChildrenProcessorForSingleChild(Class<?> processorClass, final boolean acceptNoChildren,
                      WidgetChildProcessor<?> processor, Class<?> childProcessorClass, Device[] supportedDevices)
    {
    TagConstraints processorAttributes = this.factoryHelper.getChildtrenAttributesAnnotation(childProcessorClass);
    final String widgetProperty = (processorAttributes!=null?processorAttributes.widgetProperty():"");
    String tagName = (processorAttributes!=null?processorAttributes.tagName():"");

    final boolean isAgregator = isAgregatorProcessor(childProcessorClass);
    final boolean isAnyWidget = (AnyWidgetChildProcessor.class.isAssignableFrom(childProcessorClass));
      final boolean isAnyWidgetType = (processorAttributes!=null && (AnyWidget.class.isAssignableFrom(processorAttributes.type()) ||
           WidgetCreator.class.isAssignableFrom(processorAttributes.type())));

    TagChildLazyConditions lazyConditions = childProcessorClass.getAnnotation(TagChildLazyConditions.class);
    final WidgetLazyChecker lazyChecker = (lazyConditions== null?null:LazyWidgets.initializeLazyChecker(lazyConditions));
   
    final String childName = getChildTagName(tagName, isAgregator, (isAnyWidget || isAnyWidgetType));
   
    ChildrenProcessor childrenProcessor = new ChildrenProcessor()
    {
      public void processChildren(SourcePrinter out, WidgetCreatorContext context)
      {
        JSONObject child = widgetCreator.ensureFirstChild(context.getChildElement(), acceptNoChildren, context.getWidgetId());
        if (child != null)
        {
          context.setChildElement(child);
          processChild(out, context, childName);
        }
      }
    };
    scannedProcessors.put(processorClass.getCanonicalName(), childrenProcessor);
   
    ChildProcessor childProcessor = createChildProcessor(isAnyWidget, widgetProperty, lazyChecker, processor, supportedDevices);
    if (!isAnyWidget && !isAnyWidgetType)
    {
      childProcessor.setChildrenProcessor(scanChildren(childProcessorClass, isAgregator));
    }
   
    childrenProcessor.addChildProcessor(childName, childProcessor);
    return childrenProcessor;
    }

  /**
   *
   * @param children
   * @return
   */
  private AllowedOccurences getAllowedChildrenNumber(TagChildren children)
  {
    AllowedOccurences allowed = new AllowedOccurences();
   
    for (TagChild child: children.value())
    {
      if (children.value().length > 1 && TextChildProcessor.class.isAssignableFrom(child.value()))
      {
        throw new CruxGeneratorException("Error generating widget factory. An element can not contains text and other children.");
      }
      if (child.autoProcess())
      {
        AllowedOccurences allowedForChild = getAllowedOccurrencesForChild(child);
        mergeAllowedOccurrences(allowed, allowedForChild);
      }
    }
    return allowed;
  }

  /**
   *
   * @param child
   * @return
   */
  private AllowedOccurences getAllowedOccurrencesForChild(TagChild child)
  {
    AllowedOccurences allowed = new AllowedOccurences();
    try
    {
      Class<?> childProcessorType = child.value();
      TagConstraints processorAttributes = factoryHelper.getChildtrenAttributesAnnotation(childProcessorType);

      if (processorAttributes != null)
      {
        String minOccurs = processorAttributes.minOccurs();
        if (minOccurs.equals("unbounded"))
        {
          allowed.minOccurs = UNBOUNDED;
        }
        else
        {
          allowed.minOccurs = Integer.parseInt(minOccurs);
        }

        String maxOccurs = processorAttributes.maxOccurs();
        if (maxOccurs.equals("unbounded"))
        {
          allowed.maxOccurs = UNBOUNDED;
        }
        else
        {
          allowed.maxOccurs = Integer.parseInt(maxOccurs);
        }
      }
      else if (AllChildProcessor.class.isAssignableFrom(child.value()) || SequenceChildProcessor.class.isAssignableFrom(child.value()))
      {
        TagChildren tagChildren = childProcessorType.getAnnotation(TagChildren.class);
        if (tagChildren != null)
        {
          AllowedOccurences allowedChildren = getAllowedChildrenNumber(tagChildren);
          mergeAllowedOccurrences(allowed, allowedChildren);
        }
      }
      else
      {
        allowed.minOccurs = 1;
        allowed.maxOccurs = 1;
      }
      return allowed;
    }
    catch (Exception e)
    {
      throw new CruxGeneratorException(e.getMessage(), e);
    }
  }
 
  /**
   * @param tagName
   * @param isAgregator
   * @param isAnyWidget
   * @return
   */
  private String getChildTagName(String tagName, final boolean isAgregator, final boolean isAnyWidget)
    {
      final String childName;
      if (isAnyWidget)
      {
        childName = "_innerWidget";
      }
      else if (isAgregator)
      {
        childName = "_agregator";
      }
      else
      {
        childName = tagName;
      }
    if (StringUtils.isEmpty(childName))
    {
      throw new CruxGeneratorException("Invalid tagName for child processor.");
    }
      return childName;
   
 
  /**
   * @param childProcessorClass
   * @return
   */
  private boolean isAgregatorProcessor(Class<?> childProcessorClass)
    {
      return (ChoiceChildProcessor.class.isAssignableFrom(childProcessorClass) ||
        SequenceChildProcessor.class.isAssignableFrom(childProcessorClass) ||
        AllChildProcessor.class.isAssignableFrom(childProcessorClass));
    }
 
  /**
   * @param allowed
   * @param allowedForChild
   */
  private void mergeAllowedOccurrences(AllowedOccurences allowed,
            AllowedOccurences allowedForChild)
    {
      if (allowedForChild.minOccurs == UNBOUNDED)
      {
        allowed.minOccurs = UNBOUNDED;
      }
      else if (allowed.minOccurs != UNBOUNDED)
      {
        allowed.minOccurs += allowedForChild.minOccurs; 
      }
      if (allowedForChild.maxOccurs == UNBOUNDED)
      {
        allowed.maxOccurs = UNBOUNDED;
      }
      else if (allowed.maxOccurs != UNBOUNDED)
      {
        allowed.maxOccurs += allowedForChild.maxOccurs; 
      }
    }
 
  /**
   * @param children
   * @return
   */
  private boolean mustGenerateChildrenProcessMethod(TagChildren children)
  {
    if (children != null)
    {
      for (TagChild child : children.value())
      {
        if (child.autoProcess())
        {
          return true;
        }
      }
    }
    return false;
  }
 
 
  /**
   * @param processChildrenMethod
   * @return
   */
  private ChildrenProcessor scanChildren(Class<?> processorClass, boolean isAgregatorChild)
  {
    String processorName = processorClass.getCanonicalName();
    if (scannedProcessors.containsKey(processorName))
    {
      return scannedProcessors.get(processorClass.getCanonicalName());
    }
    ChildrenProcessor result = null;
   
    TagChildren children = processorClass.getAnnotation(TagChildren.class);
   
    if (children != null && mustGenerateChildrenProcessMethod(children))
    {
      AllowedOccurences allowedChildren = getAllowedChildrenNumber(children);
      boolean acceptNoChildren = (allowedChildren.minOccurs == 0);
      if (allowedChildren.maxOccurs == 1)
      {
        TagChild child = children.value()[0];
        result = createChildrenProcessorForSingleChild(processorClass, child, acceptNoChildren);
      }
      else
      {
        result = createChildrenProcessorForMultipleChildren(processorClass, children, acceptNoChildren, isAgregatorChild);
      }
    }
    return result;
 
 
  /**
   *
   * @author Thiago da Rosa de Bustamante
   *
   */
  private static class AllowedOccurences
  {
    int maxOccurs = 0;
    int minOccurs = 0;
 
}
TOP

Related Classes of org.cruxframework.crux.core.rebind.screen.widget.ChildrenAnnotationScanner$AllowedOccurences

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.