Package org.jboss.forge.addon.ui.impl.controller

Source Code of org.jboss.forge.addon.ui.impl.controller.WizardCommandControllerImpl

/**
* Copyright 2013 Red Hat, Inc. and/or its affiliates.
*
* Licensed under the Eclipse Public License version 1.0, available at
* http://www.eclipse.org/legal/epl-v10.html
*/

package org.jboss.forge.addon.ui.impl.controller;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.jboss.forge.addon.ui.UIRuntime;
import org.jboss.forge.addon.ui.command.CommandExecutionListener;
import org.jboss.forge.addon.ui.command.UICommand;
import org.jboss.forge.addon.ui.context.UIContext;
import org.jboss.forge.addon.ui.context.UIExecutionContext;
import org.jboss.forge.addon.ui.controller.CommandController;
import org.jboss.forge.addon.ui.controller.WizardCommandController;
import org.jboss.forge.addon.ui.impl.context.UIExecutionContextImpl;
import org.jboss.forge.addon.ui.impl.context.UINavigationContextImpl;
import org.jboss.forge.addon.ui.input.InputComponent;
import org.jboss.forge.addon.ui.input.UIPrompt;
import org.jboss.forge.addon.ui.metadata.UICommandMetadata;
import org.jboss.forge.addon.ui.output.UIMessage;
import org.jboss.forge.addon.ui.progress.UIProgressMonitor;
import org.jboss.forge.addon.ui.result.NavigationResult;
import org.jboss.forge.addon.ui.result.NavigationResultEntry;
import org.jboss.forge.addon.ui.result.Result;
import org.jboss.forge.addon.ui.result.Results;
import org.jboss.forge.addon.ui.wizard.UIWizard;
import org.jboss.forge.addon.ui.wizard.WizardExecutionListener;
import org.jboss.forge.furnace.addons.AddonRegistry;

/**
*
* Implementation for the {@link WizardCommandController} interface
*
* @author <a href="ggastald@redhat.com">George Gastaldi</a>
*/
class WizardCommandControllerImpl extends AbstractCommandController implements WizardCommandController
{
   private final Logger logger = Logger.getLogger(getClass().getName());
   /**
    * The execution flow
    */
   private final List<WizardStepEntry> flow = new ArrayList<>();

   /**
    * If there are any subflows, store here
    */
   private final LinkedList<WizardStepEntry> subflow = new LinkedList<>();

   private final Set<Integer> usedSubflows = new HashSet<>();

   /**
    * The pointer that this flow is on. Starts with 0
    */
   private int flowPointer = 0;

   private final CommandControllerFactoryImpl controllerFactory;

   public WizardCommandControllerImpl(UIContext context, AddonRegistry addonRegistry, UIRuntime runtime,
            UIWizard initialCommand, CommandControllerFactoryImpl controllerFactory)
   {
      super(addonRegistry, runtime, initialCommand, context);
      this.controllerFactory = controllerFactory;
      flow.add(createEntry(initialCommand, false));
   }

   /**
    * Refreshes the current flow so it's possible to eagerly fetch all the steps
    */
   private void refreshFlow()
   {
      try
      {
         initialize();
      }
      catch (Exception e1)
      {
         // TODO Auto-generated catch block
         e1.printStackTrace();
      }
      int currentFlowPointer = this.flowPointer;
      this.flowPointer = 0;
      while (canMoveToNextStep())
      {
         try
         {
            next().initialize();
         }
         catch (Exception e)
         {
            break;
         }
      }
      cleanSubsequentStalePages();
      this.flowPointer = currentFlowPointer;
   }

   @Override
   public void initialize() throws Exception
   {
      getCurrentController().initialize();
   }

   @Override
   public boolean isInitialized()
   {
      return getCurrentController().isInitialized();
   }

   @Override
   public Result execute() throws Exception
   {
      assertInitialized();
      assertValid();
      UIProgressMonitor progressMonitor = runtime.createProgressMonitor(context);
      UIPrompt prompt = runtime.createPrompt(context);
      UIExecutionContextImpl executionContext = new UIExecutionContextImpl(context, progressMonitor, prompt);
      Set<CommandExecutionListener> listeners = new LinkedHashSet<>();
      listeners.addAll(context.getListeners());
      for (CommandExecutionListener listener : addonRegistry
               .getServices(CommandExecutionListener.class))
      {
         listeners.add(listener);
      }
      List<Result> results = new LinkedList<>();
      try
      {
         firePreWizardExecuted(executionContext, listeners);
         for (WizardStepEntry entry : flow)
         {
            CommandController controller = entry.controller;
            if (progressMonitor.isCancelled())
            {
               break;
            }
            UICommand command = controller.getCommand();
            try
            {
               firePreCommandExecuted(executionContext, listeners, command);
               Result currentResult = command.execute(executionContext);
               results.add(currentResult);
               firePostCommandExecuted(executionContext, listeners, command, currentResult);
            }
            catch (Exception e)
            {
               firePostCommandFailure(executionContext, listeners, command, e);
               throw e;
            }
         }
      }
      catch (Exception e)
      {
         firePostWizardFailure(executionContext, listeners, e);
         throw e;
      }
      Result result = (results.size() == 1) ? results.get(0) : Results.aggregate(results);
      firePostWizardExecuted(executionContext, listeners, result);
      return result;
   }

   protected void firePreWizardExecuted(UIExecutionContext executionContext,
            Set<CommandExecutionListener> listeners)
   {
      for (CommandExecutionListener listener : listeners)
      {
         if (listener instanceof WizardExecutionListener)
         {
            ((WizardExecutionListener) listener).preWizardExecuted((UIWizard) initialCommand, executionContext);
         }
      }
   }

   protected void firePostWizardFailure(UIExecutionContext executionContext,
            Set<CommandExecutionListener> listeners, Exception e)
   {
      for (CommandExecutionListener listener : listeners)
      {
         if (listener instanceof WizardExecutionListener)
         {
            ((WizardExecutionListener) listener).postWizardFailure((UIWizard) initialCommand, executionContext, e);
         }
      }
   }

   protected void firePostWizardExecuted(UIExecutionContext executionContext,
            Set<CommandExecutionListener> listeners, Result currentResult)
   {
      for (CommandExecutionListener listener : listeners)
      {
         if (listener instanceof WizardExecutionListener)
         {
            ((WizardExecutionListener) listener).postWizardExecuted((UIWizard) initialCommand, executionContext,
                     currentResult);
         }
      }
   }

   @Override
   public List<UIMessage> validate()
   {
      return getCurrentController().validate();
   }

   @Override
   public boolean isValid()
   {
      return getCurrentController().isValid();
   }

   @Override
   public CommandController setValueFor(String inputName, Object value) throws IllegalArgumentException
   {
      getCurrentController().setValueFor(inputName, value);
      return this;
   }

   @Override
   public Object getValueFor(String inputName) throws IllegalArgumentException
   {
      return getCurrentController().getValueFor(inputName);
   }

   @Override
   public Map<String, InputComponent<?, ?>> getInputs()
   {
      return getCurrentController().getInputs();
   }

   @Override
   public UICommandMetadata getMetadata()
   {
      return getCurrentController().getMetadata();
   }

   @Override
   public UICommandMetadata getInitialMetadata()
   {
      return flow.get(0).controller.getMetadata();
   }

   @Override
   public boolean isEnabled()
   {
      return getCurrentController().isEnabled();
   }

   @Override
   public UICommand getCommand()
   {
      return getCurrentController().getCommand();
   }

   @Override
   public void close() throws Exception
   {
      context.close();
      subflow.clear();
      flow.clear();
      usedSubflows.clear();
   }

   @Override
   public boolean canMoveToNextStep()
   {
      assertInitialized();
      if (!isValid())
      {
         return false;
      }
      NavigationResultEntry[] next = getNextFrom(getCurrentController().getCommand());
      return ((next != null || !subflow.isEmpty()) || usedSubflows.contains(flowPointer));
   }

   @Override
   public boolean canMoveToPreviousStep()
   {
      assertInitialized();
      return flowPointer > 0;
   }

   @Override
   public boolean canExecute()
   {
      assertInitialized();
      // FORGE-1466: Eager initialization so canExecute() works
      refreshFlow();
      for (WizardStepEntry entry : flow)
      {
         if (!entry.controller.canExecute())
         {
            return false;
         }
      }

      // Checking if there is any next page left
      CommandController lastController = flow.get(flow.size() - 1).controller;
      if (lastController.isInitialized())
      {
         NavigationResultEntry[] next = getNextFrom(flow.get(flow.size() - 1).controller.getCommand());
         if (next != null || !subflow.isEmpty())
         {
            return false;
         }
      }
      else
      {
         return false;
      }
      return true;
   }

   @SuppressWarnings("unchecked")
   @Override
   public WizardCommandController next() throws Exception
   {
      assertInitialized();
      assertValid();

      WizardStepEntry currentEntry = getCurrentEntry();
      WizardStepEntry nextEntry = getNextEntry();
      NavigationResultEntry[] result = getNextFrom(currentEntry.controller.getCommand());
      if (nextEntry == null)
      {
         currentEntry.next = result;
         addNextFlowStep(result);
      }
      else
      {
         // There is already a next page, did the object returned from UICommand.next() changed ?
         if (!Arrays.equals(currentEntry.next, result))
         {
            // Update current entry
            currentEntry.next = result;
            cleanSubsequentStalePages();
            addNextFlowStep(result);
         }
         else
         {
            // FORGE-1372- Test if the inputs changed.
            final UICommand command;
            if (result == null)
            {
               if (subflow.isEmpty())
               {
                  command = null;
               }
               else
               {
                  UICommandMetadata metadata = subflow.peek().controller.getCommand().getMetadata(context);
                  command = createCommand((Class<? extends UICommand>) metadata.getType());
               }
            }
            else
            {
               command = createCommand(result[0]);
            }
            if (command != null)
            {
               CommandController ctrl = controllerFactory.doCreateSingleController(context, runtime, command);
               ctrl.initialize();
               Set<String> currentInputsKeySet = nextEntry.controller.getInputs().keySet();
               Set<String> keySet = ctrl.getInputs().keySet();
               if (!(currentInputsKeySet.containsAll(keySet) && keySet.containsAll(currentInputsKeySet)))
               {
                  cleanSubsequentStalePages();
                  addNextFlowStep(result);
               }
            }
         }
      }
      flowPointer++;
      return this;
   }

   /**
    * Remove stale pages in case of navigational changes
    */
   private void cleanSubsequentStalePages()
   {
      // FIXME: Workaround until FORGE-1704 is fixed
      if (flowPointer == 0)
      {
         flow.subList(1, flow.size()).clear();
         subflow.clear();
      }
      else
      {
         // Remove subsequent pages and push the subflows back to the stack
         Iterator<WizardStepEntry> it = flow.listIterator(flowPointer + 1);
         int subflowIdx = 0;
         while (it.hasNext())
         {
            WizardStepEntry entry = it.next();
            if (entry.subflowHead && !subflow.contains(entry))
            {
               subflow.add(subflowIdx++, entry);
            }
            it.remove();
         }
      }
   }

   /**
    * @param result
    */
   private void addNextFlowStep(NavigationResultEntry[] result)
   {
      final WizardStepEntry next;
      if (result == null)
      {
         if (subflow.isEmpty())
         {
            throw new IllegalStateException("No next step found");
         }
         else
         {
            next = subflow.pop();
            usedSubflows.add(flowPointer);
         }
      }
      else
      {
         next = createEntry(result[0], false);
         for (int i = 1; i < result.length; i++)
         {
            // Save this subflow for later
            WizardStepEntry subflowEntry = createEntry(result[i], true);
            if (!subflow.contains(subflowEntry))
            {
               subflow.add(subflowEntry);
            }
         }
      }
      flow.add(next);
   }

   @Override
   public WizardCommandController previous() throws IllegalStateException
   {
      assertInitialized();
      if (!canMoveToPreviousStep())
      {
         throw new IllegalStateException("No previous step found");
      }
      flowPointer--;
      return this;
   }

   protected int getFlowPointer()
   {
      return flowPointer;
   }

   protected void setFlowPointer(int flowPointer)
   {
      this.flowPointer = flowPointer;
   }

   private WizardStepEntry getCurrentEntry()
   {
      return flow.get(flowPointer);
   }

   private WizardStepEntry getNextEntry()
   {
      int nextIdx = flowPointer + 1;
      return (nextIdx < flow.size()) ? flow.get(nextIdx) : null;
   }

   private CommandController getCurrentController()
   {
      return getCurrentEntry().controller;
   }

   private WizardStepEntry createEntry(NavigationResultEntry entry, boolean subflowHead)
   {
      UICommand command = createCommand(entry);
      return createEntry(command, subflowHead);
   }

   private UICommand createCommand(NavigationResultEntry entry)
   {
      UICommand command = entry.getCommand(addonRegistry, context);
      return command;
   }

   private UICommand createCommand(Class<? extends UICommand> commandClass)
   {
      UICommand command = addonRegistry.getServices(commandClass).get();
      return command;
   }

   private WizardStepEntry createEntry(UICommand command, boolean subflowHead)
   {
      CommandController controller = controllerFactory.doCreateSingleController(context, runtime, command);
      return new WizardStepEntry(controller, subflowHead);
   }

   private NavigationResultEntry[] getNextFrom(UICommand command)
   {
      NavigationResultEntry[] result = null;
      if (command instanceof UIWizard)
      {
         NavigationResult next;
         try
         {
            next = ((UIWizard) command).next(new UINavigationContextImpl(context));
         }
         catch (Exception e)
         {
            logger.log(Level.SEVERE, "Cannot fetch the next steps from " + command, e);
            next = null;
         }
         if (next != null)
         {
            result = next.getNext();
         }
      }
      return result;
   }

   private static class WizardStepEntry
   {
      final CommandController controller;
      NavigationResultEntry[] next;
      // If this entry starts a subflow
      final boolean subflowHead;

      public WizardStepEntry(CommandController controller, boolean subflowHead)
      {
         this.controller = controller;
         this.subflowHead = subflowHead;
      }

      @Override
      public int hashCode()
      {
         final int prime = 31;
         int result = 1;
         result = prime * result + ((controller == null) ? 0 : controller.hashCode());
         result = prime * result + Arrays.hashCode(next);
         return result;
      }

      @Override
      public boolean equals(Object obj)
      {
         if (this == obj)
            return true;
         if (obj == null)
            return false;
         if (getClass() != obj.getClass())
            return false;
         WizardStepEntry other = (WizardStepEntry) obj;
         if (controller == null)
         {
            if (other.controller != null)
               return false;
         }
         else if (!controller.equals(other.controller))
            return false;
         if (!Arrays.equals(next, other.next))
            return false;
         return true;
      }
   }
}
TOP

Related Classes of org.jboss.forge.addon.ui.impl.controller.WizardCommandControllerImpl

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.