<commit_before>/*
 * Copyright (c) 2014 <NAME> (Berlin, Germany) and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *    <NAME> - initial API and implementation
 *    Ericsson AB (<NAME>) - Bug 434525 - Allow prompted variables to be pre-populated
 */
package org.eclipse.oomph.setup.ui.wizards;

import org.eclipse.oomph.base.util.BaseUtil;
import org.eclipse.oomph.internal.setup.SetupPrompter;
import org.eclipse.oomph.internal.ui.AccessUtil;
import org.eclipse.oomph.preferences.util.PreferencesUtil;
import org.eclipse.oomph.setup.AnnotationConstants;
import org.eclipse.oomph.setup.Installation;
import org.eclipse.oomph.setup.SetupTaskContext;
import org.eclipse.oomph.setup.Trigger;
import org.eclipse.oomph.setup.User;
import org.eclipse.oomph.setup.VariableTask;
import org.eclipse.oomph.setup.Workspace;
import org.eclipse.oomph.setup.internal.core.SetupContext;
import org.eclipse.oomph.setup.internal.core.SetupTaskPerformer;
import org.eclipse.oomph.setup.internal.core.util.Authenticator;
import org.eclipse.oomph.setup.internal.core.util.SetupUtil;
import org.eclipse.oomph.setup.ui.PropertyField;
import org.eclipse.oomph.setup.ui.PropertyField.AuthenticatedField;
import org.eclipse.oomph.setup.ui.PropertyField.ValueListener;
import org.eclipse.oomph.setup.ui.SetupUIPlugin;
import org.eclipse.oomph.ui.UICallback;
import org.eclipse.oomph.ui.UIUtil;
import org.eclipse.oomph.util.CollectionUtil;
import org.eclipse.oomph.util.StringUtil;
import org.eclipse.oomph.util.UserCallback;

import org.eclipse.emf.common.util.BasicEList;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EAttribute;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.resource.URIConverter;
import org.eclipse.emf.ecore.util.EcoreUtil;

import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.ScrolledComposite;
import org.eclipse.swt.events.ControlAdapter;
import org.eclipse.swt.events.ControlEvent;
import org.eclipse.swt.events.FocusAdapter;
import org.eclipse.swt.events.FocusEvent;
import org.eclipse.swt.events.FocusListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Shell;

import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * @author <NAME>
 */
public class VariablePage extends SetupWizardPage implements SetupPrompter
{
  private Composite composite;

  private ScrolledComposite scrolledComposite;

  private final FieldHolderManager manager = new FieldHolderManager();

  private boolean prompted;

  private boolean fullPrompt;

  private boolean recursiveValidate;

  private boolean updating;

  private Set<SetupTaskPerformer> incompletePerformers = new LinkedHashSet<SetupTaskPerformer>();

  private Set<SetupTaskPerformer> allPromptedPerfomers = new LinkedHashSet<SetupTaskPerformer>();

  private SetupTaskPerformer performer;

  private Control focusControl;

  private SetupContext originalContext;

  private FocusListener focusListener = new FocusAdapter()
  {
    @Override
    public void focusGained(FocusEvent e)
    {
      focusControl = (Control)e.widget;
    }
  };

  protected VariablePage()
  {
    super("VariablePage");
    setTitle("Variables");
    setDescription("Enter values for the required variables.");
  }

  @Override
  protected Control createUI(Composite parent)
  {
    Composite mainComposite = new Composite(parent, SWT.NONE);
    mainComposite.setLayout(new GridLayout(1, false));
    mainComposite.setLayoutData(new GridData(GridData.FILL_BOTH));

    scrolledComposite = new ScrolledComposite(mainComposite, SWT.VERTICAL);
    scrolledComposite.setExpandHorizontal(true);
    scrolledComposite.setExpandVertical(true);
    scrolledComposite.setLayoutData(new GridData(GridData.FILL_BOTH));

    GridLayout layout = new GridLayout(3, false);
    layout.marginWidth = 0;
    layout.marginHeight = 0;
    layout.horizontalSpacing = 10;
    layout.verticalSpacing = 10;

    composite = new Composite(scrolledComposite, SWT.NONE);
    composite.setLayout(layout);
    scrolledComposite.setContent(composite);
    composite.setLayoutData(new GridData(GridData.FILL_BOTH));

    ControlAdapter resizeListener = new ControlAdapter()
    {
      @Override
      public void controlResized(ControlEvent event)
      {
        Point size = composite.computeSize(scrolledComposite.getClientArea().width, SWT.DEFAULT);
        scrolledComposite.setMinSize(size);
      }
    };

    scrolledComposite.addControlListener(resizeListener);
    composite.addControlListener(resizeListener);
    composite.notifyListeners(SWT.Resize, new Event());

    return mainComposite;
  }

  @Override
  protected void createCheckButtons()
  {
    final Button fullPromptButton = addCheckButton("Show all variables", "", false, "fullPrompt");
    fullPrompt = fullPromptButton.getSelection();
    fullPromptButton.addSelectionListener(new SelectionAdapter()
    {
      @Override
      public void widgetSelected(SelectionEvent e)
      {
        fullPrompt = fullPromptButton.getSelection();
        UIUtil.asyncExec(new Runnable()
        {
          public void run()
          {
            validate();
          }
        });
      }
    });

    AccessUtil.setKey(fullPromptButton, "showAll");
  }

  private void updateFields()
  {
    for (FieldHolder fieldHolder : manager)
    {
      fieldHolder.clear();
    }

    for (SetupTaskPerformer setupTaskPerformer : incompletePerformers.isEmpty() ? performer == null ? Collections.<SetupTaskPerformer> emptySet() : Collections
        .singleton(performer) : incompletePerformers)
    {
      List<VariableTask> variables = setupTaskPerformer.getUnresolvedVariables();
      for (VariableTask variable : variables)
      {
        VariableTask ruleVariable = setupTaskPerformer.getRuleVariable(variable);
        if (ruleVariable == null)
        {
          manager.getFieldHolder(variable, true, false);
        }
        else
        {
          FieldHolder fieldHolder = manager.getFieldHolder(ruleVariable, true, false);
          fieldHolder.add(variable);
          manager.associate(variable, fieldHolder);
        }
      }
    }

    for (FieldHolder fieldHolder : manager)
    {
      if (StringUtil.isEmpty(fieldHolder.getValue()))
      {
        String initialValue = null;
        String initialDefaultValue = null;
        for (VariableTask variable : fieldHolder.getVariables())
        {
          if (initialValue == null)
          {
            String value = variable.getValue();
            if (!StringUtil.isEmpty(value))
            {
              initialValue = value;
            }
          }

          if (initialDefaultValue == null)
          {
            String defaultValue = variable.getDefaultValue();
            if (!StringUtil.isEmpty(defaultValue))
            {
              initialDefaultValue = defaultValue;
            }
          }
        }

        if (!StringUtil.isEmpty(initialValue))
        {
          fieldHolder.setValue(initialValue);
        }
        else if (!StringUtil.isEmpty(initialDefaultValue))
        {
          fieldHolder.setValue(initialDefaultValue);
        }
      }
    }

    try
    {
      for (FieldHolder fieldHolder : manager)
      {
        updating = true;
        fieldHolder.update();
      }
    }
    finally
    {
      updating = false;
    }

    for (FieldHolder fieldHolder : manager)
    {
      fieldHolder.recordInitialValue();
    }

    // Determine the URIs of all the variables actually being used.
    Set<URI> uris = new HashSet<URI>();
    if (performer != null)
    {
      for (VariableTask variable : performer.getUnresolvedVariables())
      {
        uris.add(manager.getURI(variable));
        VariableTask ruleVariable = performer.getRuleVariable(variable);
        if (ruleVariable != null)
        {
          uris.add(manager.getURI(ruleVariable));
        }
      }
    }

    manager.cleanup(uris);

    Composite parent = composite.getParent();
    parent.setRedraw(false);

    List<SetupTaskPerformer> allPerformers = new ArrayList<SetupTaskPerformer>(allPromptedPerfomers);
    if (performer != null)
    {
      allPerformers.add(0, performer);
    }

    // Determine an appropriate field order.
    manager.reorder(allPerformers);

    FieldHolder firstField = null;
    FieldHolder firstEmptyField = null;
    for (FieldHolder fieldHolder : manager)
    {
      if (!fieldHolder.isDisposed())
      {
        if (firstField == null)
        {
          firstField = fieldHolder;
        }

        if (firstEmptyField == null && StringUtil.isEmpty(fieldHolder.getValue()))
        {
          firstEmptyField = fieldHolder;
        }
      }
    }

    if (focusControl != null && !focusControl.isDisposed())
    {
      focusControl.setFocus();
    }
    else
    {
      FieldHolder field = firstEmptyField;
      if (field == null)
      {
        field = firstField;
      }

      if (field != null)
      {
        field.setFocus();
      }
    }

    parent.pack();
    parent.getParent().layout();
    parent.setRedraw(true);

    // for (FieldHolder fieldHolder : manager)
    // {
    // Control control = fieldHolder.getControl();
    // PropertyField field = fieldHolder.field;
    // Label label = field.getLabel();
    // Control helper = field.getHelper();
    // for (VariableTask variable : fieldHolder.getVariables())
    // {
    // String name = variable.getName();
    // if (name.startsWith("@<id>"))
    // {
    // name = name.substring(name.indexOf("name: ") + 6, name.indexOf(')'));
    // }
    //
    // System.err.println("###'" + name + "'");
    //
    // AccessUtil.setKey(label, name + ".label");
    // AccessUtil.setKey(control, name + ".control");
    // AccessUtil.setKey(helper, name + ".helper");
    //
    // break;
    // }
    // }

    if (!isPageComplete() && firstEmptyField == null)
    {
      // If the page isn't complete but there are no empty fields, then the last change introduced a new field.
      // So we should validate again to be sure there really needs to be more information prompted from the user.
      if (!recursiveValidate)
      {
        recursiveValidate = true;
        validate();
      }
    }
    else
    {
      recursiveValidate = false;
    }
  }

  private void validate()
  {
    try
    {
      performer = null;
      String errorMessage = null;

      try
      {
        incompletePerformers.clear();
        allPromptedPerfomers.clear();

        User originalUser = getUser();
        URI uri = originalUser.eResource().getURI();
        final User user = EcoreUtil.copy(originalUser);
        Resource userResource = Resource.Factory.Registry.INSTANCE.getFactory(uri).createResource(uri);
        userResource.getContents().add(user);

        Trigger trigger = getTrigger();
        Installation installation = getInstallation();
        Workspace workspace = getWorkspace();

        URIConverter uriConverter = getResourceSet().getURIConverter();
        SetupContext context = SetupContext.create(installation, workspace, user);

        performer = SetupTaskPerformer.create(uriConverter, this, trigger, context, fullPrompt);
      }
      catch (OperationCanceledException ex)
      {
        //$FALL-THROUGH$
      }
      catch (IllegalArgumentException ex)
      {
        errorMessage = ex.getMessage();
      }

      setErrorMessage(errorMessage);

      UIUtil.asyncExec(new Runnable()
      {
        public void run()
        {
          updateFields();
        }
      });

      if (performer == null)
      {
        setPageComplete(false);
      }
      else
      {
        setPageComplete(true);

        if (!prompted)
        {
          prompted = true;
          advanceToNextPage();
        }
      }
    }
    catch (Exception ex)
    {
      SetupUIPlugin.INSTANCE.log(ex);
    }
  }

  @Override
  public void enterPage(boolean forward)
  {
    performer = getWizard().getPerformer();
    if (performer != null && forward)
    {
      performer.setPrompter(this);
      setPageComplete(true);
      advanceToNextPage();
    }
    else
    {
      if (!forward)
      {
        getWizard().setSetupContext(originalContext);
        originalContext = null;
      }

      setPageComplete(false);
      validate();
      if (forward && getPreviousPage() == null)
      {
        UIUtil.asyncExec(new Runnable()
        {
          public void run()
          {
            if (isPageComplete())
            {
              advanceToNextPage();
            }
          }
        });
      }
    }
  }

  @Override
  public void leavePage(boolean forward)
  {
    if (forward)
    {
      originalContext = getWizard().getSetupContext();

      final List<VariableTask> unresolvedVariables = performer.getUnresolvedVariables();
      for (FieldHolder fieldHolder : manager)
      {
        unresolvedVariables.addAll(fieldHolder.getVariables());
      }

      final ResourceSet resourceSet = SetupUtil.createResourceSet();

      User user = getUser();
      final User copiedUser = EcoreUtil.copy(user);
      URI userResourceURI = user.eResource().getURI();
      Resource userResource = resourceSet.createResource(userResourceURI);
      userResource.getContents().add(copiedUser);

      Installation installation = performer.getInstallation();
      Resource installationResource = installation.eResource();
      URI installationResourceURI = installationResource.getURI();
      installationResource.setURI(URI.createFileURI(new File(performer.getProductConfigurationLocation(), "org.eclipse.oomph.setup/installation.setup")
          .toString()));

      Workspace workspace = performer.getWorkspace();
      Resource workspaceResource = null;
      URI workspaceResourceURI = null;
      if (workspace != null)
      {
        workspaceResource = workspace.eResource();
        workspaceResourceURI = workspaceResource.getURI();
        workspaceResource.setURI(URI.createFileURI(new File(performer.getWorkspaceLocation(), ".metadata/.plugins/org.eclipse.oomph.setup/workspace.setup")
            .toString()));
      }

      Installation copiedInstallation = EcoreUtil.copy(installation);
      URI copiedInstallationResourceURI = installation.eResource().getURI();
      Resource copiedInstallationResource = resourceSet.createResource(copiedInstallationResourceURI);
      copiedInstallationResource.getContents().add(copiedInstallation);

      Workspace copiedWorkspace = EcoreUtil.copy(workspace);
      if (workspace != null)
      {
        URI copiedWorkspaceResourceURI = workspace.eResource().getURI();
        Resource copiedWorkspaceResource = resourceSet.createResource(copiedWorkspaceResourceURI);
        copiedWorkspaceResource.getContents().add(copiedWorkspace);
      }

      performer.recordVariables(copiedInstallation, copiedWorkspace, copiedUser);

      unresolvedVariables.clear();

      getWizard().setSetupContext(SetupContext.create(copiedInstallation, copiedWorkspace, copiedUser));
      setPerformer(performer);

      BaseUtil.saveEObject(copiedUser);

      performer.savePasswords();

      installationResource.setURI(installationResourceURI);
      if (workspaceResource != null)
      {
        workspaceResource.setURI(workspaceResourceURI);
      }
    }
    else
    {
      originalContext = null;

      setPerformer(null);
    }
  }

  public String getValue(VariableTask variable)
  {
    FieldHolder fieldHolder = manager.getFieldHolder(variable, false, true);
    if (fieldHolder != null && (updating || fieldHolder.isDirty()))
    {
      String value = fieldHolder.getValue();
      if (!"".equals(value))
      {
        return value;
      }
    }

    if (updating && performer != null)
    {
      Object value = performer.getMap().get(variable.getName());
      if (value != null)
      {
        return value.toString();
      }
    }

    return null;
  }

  public boolean promptVariables(List<? extends SetupTaskContext> contexts)
  {
    prompted = true;

    @SuppressWarnings("unchecked")
    List<SetupTaskPerformer> performers = (List<SetupTaskPerformer>)contexts;
    allPromptedPerfomers.addAll(performers);
    for (SetupTaskPerformer performer : performers)
    {
      boolean resolvedAll = true;
      List<VariableTask> unresolvedVariables = performer.getUnresolvedVariables();
      for (VariableTask variable : unresolvedVariables)
      {
        FieldHolder fieldHolder = manager.getFieldHolder(variable, false, true);
        if (fieldHolder != null)
        {
          String value = fieldHolder.getValue();
          if (!"".equals(value))
          {
            variable.setValue(value);
          }
          else
          {
            resolvedAll = false;
          }
        }
        else
        {
          resolvedAll = false;
        }
      }

      if (!resolvedAll)
      {
        incompletePerformers.add(performer);
      }
    }

    boolean isComplete = incompletePerformers.isEmpty();
    return isComplete;
  }

  public UserCallback getUserCallback()
  {
    Shell shell = getShell();
    return new UICallback(shell, shell.getText());
  }

  /**
   * @author <NAME>
   */
  private final class FieldHolder implements ValueListener
  {
    private final Set<VariableTask> variables = new LinkedHashSet<VariableTask>();

    private PropertyField field;<commit_msg>[444714] Author help center documentation for Oomph Setup<commit_after>/*
 * Copyright (c) 2014 <NAME> (Berlin, Germany) and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *    <NAME> - initial API and implementation
 *    Ericsson AB (<NAME>) - Bug 434525 - Allow prompted variables to be pre-populated
 */
package org.eclipse.oomph.setup.ui.wizards;

import org.eclipse.oomph.base.util.BaseUtil;
import org.eclipse.oomph.internal.setup.SetupPrompter;
import org.eclipse.oomph.internal.ui.AccessUtil;
import org.eclipse.oomph.preferences.util.PreferencesUtil;
import org.eclipse.oomph.setup.AnnotationConstants;
import org.eclipse.oomph.setup.Installation;
import org.eclipse.oomph.setup.SetupTaskContext;
import org.eclipse.oomph.setup.Trigger;
import org.eclipse.oomph.setup.User;
import org.eclipse.oomph.setup.VariableTask;
import org.eclipse.oomph.setup.Workspace;
import org.eclipse.oomph.setup.internal.core.SetupContext;
import org.eclipse.oomph.setup.internal.core.SetupTaskPerformer;
import org.eclipse.oomph.setup.internal.core.util.Authenticator;
import org.eclipse.oomph.setup.internal.core.util.SetupUtil;
import org.eclipse.oomph.setup.ui.AbstractSetupDialog;
import org.eclipse.oomph.setup.ui.PropertyField;
import org.eclipse.oomph.setup.ui.PropertyField.AuthenticatedField;
import org.eclipse.oomph.setup.ui.PropertyField.ValueListener;
import org.eclipse.oomph.setup.ui.SetupUIPlugin;
import org.eclipse.oomph.ui.UICallback;
import org.eclipse.oomph.ui.UIUtil;
import org.eclipse.oomph.util.CollectionUtil;
import org.eclipse.oomph.util.StringUtil;
import org.eclipse.oomph.util.UserCallback;

import org.eclipse.emf.common.util.BasicEList;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EAttribute;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.resource.URIConverter;
import org.eclipse.emf.ecore.util.EcoreUtil;

import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.ScrolledComposite;
import org.eclipse.swt.events.ControlAdapter;
import org.eclipse.swt.events.ControlEvent;
import org.eclipse.swt.events.FocusAdapter;
import org.eclipse.swt.events.FocusEvent;
import org.eclipse.swt.events.FocusListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Event;

import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * @author <NAME>
 */
public class VariablePage extends SetupWizardPage implements SetupPrompter
{
  private Composite composite;

  private ScrolledComposite scrolledComposite;

  private final FieldHolderManager manager = new FieldHolderManager();

  private boolean prompted;

  private boolean fullPrompt;

  private boolean recursiveValidate;

  private boolean updating;

  private Set<SetupTaskPerformer> incompletePerformers = new LinkedHashSet<SetupTaskPerformer>();

  private Set<SetupTaskPerformer> allPromptedPerfomers = new LinkedHashSet<SetupTaskPerformer>();

  private SetupTaskPerformer performer;

  private Control focusControl;

  private SetupContext originalContext;

  private FocusListener focusListener = new FocusAdapter()
  {
    @Override
    public void focusGained(FocusEvent e)
    {
      focusControl = (Control)e.widget;
    }
  };

  protected VariablePage()
  {
    super("VariablePage");
    setTitle("Variables");
    setDescription("Enter values for the required variables.");
  }

  @Override
  protected Control createUI(Composite parent)
  {
    Composite mainComposite = new Composite(parent, SWT.NONE);
    mainComposite.setLayout(new GridLayout(1, false));
    mainComposite.setLayoutData(new GridData(GridData.FILL_BOTH));

    scrolledComposite = new ScrolledComposite(mainComposite, SWT.VERTICAL);
    scrolledComposite.setExpandHorizontal(true);
    scrolledComposite.setExpandVertical(true);
    scrolledComposite.setLayoutData(new GridData(GridData.FILL_BOTH));

    GridLayout layout = new GridLayout(3, false);
    layout.marginWidth = 0;
    layout.marginHeight = 0;
    layout.horizontalSpacing = 10;
    layout.verticalSpacing = 10;

    composite = new Composite(scrolledComposite, SWT.NONE);
    composite.setLayout(layout);
    scrolledComposite.setContent(composite);
    composite.setLayoutData(new GridData(GridData.FILL_BOTH));

    ControlAdapter resizeListener = new ControlAdapter()
    {
      @Override
      public void controlResized(ControlEvent event)
      {
        Point size = composite.computeSize(scrolledComposite.getClientArea().width, SWT.DEFAULT);
        scrolledComposite.setMinSize(size);
      }
    };

    scrolledComposite.addControlListener(resizeListener);
    composite.addControlListener(resizeListener);
    composite.notifyListeners(SWT.Resize, new Event());

    return mainComposite;
  }

  @Override
  protected void createCheckButtons()
  {
    final Button fullPromptButton = addCheckButton("Show all variables", "", false, "fullPrompt");
    fullPrompt = fullPromptButton.getSelection();
    fullPromptButton.addSelectionListener(new SelectionAdapter()
    {
      @Override
      public void widgetSelected(SelectionEvent e)
      {
        fullPrompt = fullPromptButton.getSelection();
        UIUtil.asyncExec(new Runnable()
        {
          public void run()
          {
            validate();
          }
        });
      }
    });

    AccessUtil.setKey(fullPromptButton, "showAll");
  }

  private void updateFields()
  {
    for (FieldHolder fieldHolder : manager)
    {
      fieldHolder.clear();
    }

    for (SetupTaskPerformer setupTaskPerformer : incompletePerformers.isEmpty() ? performer == null ? Collections.<SetupTaskPerformer> emptySet() : Collections
        .singleton(performer) : incompletePerformers)
    {
      List<VariableTask> variables = setupTaskPerformer.getUnresolvedVariables();
      for (VariableTask variable : variables)
      {
        VariableTask ruleVariable = setupTaskPerformer.getRuleVariable(variable);
        if (ruleVariable == null)
        {
          manager.getFieldHolder(variable, true, false);
        }
        else
        {
          FieldHolder fieldHolder = manager.getFieldHolder(ruleVariable, true, false);
          fieldHolder.add(variable);
          manager.associate(variable, fieldHolder);
        }
      }
    }

    for (FieldHolder fieldHolder : manager)
    {
      if (StringUtil.isEmpty(fieldHolder.getValue()))
      {
        String initialValue = null;
        String initialDefaultValue = null;
        for (VariableTask variable : fieldHolder.getVariables())
        {
          if (initialValue == null)
          {
            String value = variable.getValue();
            if (!StringUtil.isEmpty(value))
            {
              initialValue = value;
            }
          }

          if (initialDefaultValue == null)
          {
            String defaultValue = variable.getDefaultValue();
            if (!StringUtil.isEmpty(defaultValue))
            {
              initialDefaultValue = defaultValue;
            }
          }
        }

        if (!StringUtil.isEmpty(initialValue))
        {
          fieldHolder.setValue(initialValue);
        }
        else if (!StringUtil.isEmpty(initialDefaultValue))
        {
          fieldHolder.setValue(initialDefaultValue);
        }
      }
    }

    try
    {
      for (FieldHolder fieldHolder : manager)
      {
        updating = true;
        fieldHolder.update();
      }
    }
    finally
    {
      updating = false;
    }

    for (FieldHolder fieldHolder : manager)
    {
      fieldHolder.recordInitialValue();
    }

    // Determine the URIs of all the variables actually being used.
    Set<URI> uris = new HashSet<URI>();
    if (performer != null)
    {
      for (VariableTask variable : performer.getUnresolvedVariables())
      {
        uris.add(manager.getURI(variable));
        VariableTask ruleVariable = performer.getRuleVariable(variable);
        if (ruleVariable != null)
        {
          uris.add(manager.getURI(ruleVariable));
        }
      }
    }

    manager.cleanup(uris);

    Composite parent = composite.getParent();
    parent.setRedraw(false);

    List<SetupTaskPerformer> allPerformers = new ArrayList<SetupTaskPerformer>(allPromptedPerfomers);
    if (performer != null)
    {
      allPerformers.add(0, performer);
    }

    // Determine an appropriate field order.
    manager.reorder(allPerformers);

    FieldHolder firstField = null;
    FieldHolder firstEmptyField = null;
    for (FieldHolder fieldHolder : manager)
    {
      if (!fieldHolder.isDisposed())
      {
        if (firstField == null)
        {
          firstField = fieldHolder;
        }

        if (firstEmptyField == null && StringUtil.isEmpty(fieldHolder.getValue()))
        {
          firstEmptyField = fieldHolder;
        }
      }
    }

    if (focusControl != null && !focusControl.isDisposed())
    {
      focusControl.setFocus();
    }
    else
    {
      FieldHolder field = firstEmptyField;
      if (field == null)
      {
        field = firstField;
      }

      if (field != null)
      {
        field.setFocus();
      }
    }

    parent.pack();
    parent.getParent().layout();
    parent.setRedraw(true);

    // for (FieldHolder fieldHolder : manager)
    // {
    // Control control = fieldHolder.getControl();
    // PropertyField field = fieldHolder.field;
    // Label label = field.getLabel();
    // Control helper = field.getHelper();
    // for (VariableTask variable : fieldHolder.getVariables())
    // {
    // String name = variable.getName();
    // if (name.startsWith("@<id>"))
    // {
    // name = name.substring(name.indexOf("name: ") + 6, name.indexOf(')'));
    // }
    //
    // System.err.println("###'" + name + "'");
    //
    // AccessUtil.setKey(label, name + ".label");
    // AccessUtil.setKey(control, name + ".control");
    // AccessUtil.setKey(helper, name + ".helper");
    //
    // break;
    // }
    // }

    if (!isPageComplete() && firstEmptyField == null)
    {
      // If the page isn't complete but there are no empty fields, then the last change introduced a new field.
      // So we should validate again to be sure there really needs to be more information prompted from the user.
      if (!recursiveValidate)
      {
        recursiveValidate = true;
        validate();
      }
    }
    else
    {
      recursiveValidate = false;
    }
  }

  private void validate()
  {
    try
    {
      performer = null;
      String errorMessage = null;

      try
      {
        incompletePerformers.clear();
        allPromptedPerfomers.clear();

        User originalUser = getUser();
        URI uri = originalUser.eResource().getURI();
        final User user = EcoreUtil.copy(originalUser);
        Resource userResource = Resource.Factory.Registry.INSTANCE.getFactory(uri).createResource(uri);
        userResource.getContents().add(user);

        Trigger trigger = getTrigger();
        Installation installation = getInstallation();
        Workspace workspace = getWorkspace();

        URIConverter uriConverter = getResourceSet().getURIConverter();
        SetupContext context = SetupContext.create(installation, workspace, user);

        performer = SetupTaskPerformer.create(uriConverter, this, trigger, context, fullPrompt);
      }
      catch (OperationCanceledException ex)
      {
        //$FALL-THROUGH$
      }
      catch (IllegalArgumentException ex)
      {
        errorMessage = ex.getMessage();
      }

      setErrorMessage(errorMessage);

      UIUtil.asyncExec(new Runnable()
      {
        public void run()
        {
          updateFields();
        }
      });

      if (performer == null)
      {
        setPageComplete(false);
      }
      else
      {
        setPageComplete(true);

        if (!prompted)
        {
          prompted = true;
          advanceToNextPage();
        }
      }
    }
    catch (Exception ex)
    {
      SetupUIPlugin.INSTANCE.log(ex);
    }
  }

  @Override
  public void enterPage(boolean forward)
  {
    performer = getWizard().getPerformer();
    if (performer != null && forward)
    {
      performer.setPrompter(this);
      setPageComplete(true);
      advanceToNextPage();
    }
    else
    {
      if (!forward)
      {
        getWizard().setSetupContext(originalContext);
        originalContext = null;
      }

      setPageComplete(false);
      validate();
      if (forward && getPreviousPage() == null)
      {
        UIUtil.asyncExec(new Runnable()
        {
          public void run()
          {
            if (isPageComplete())
            {
              advanceToNextPage();
            }
          }
        });
      }
    }
  }

  @Override
  public void leavePage(boolean forward)
  {
    if (forward)
    {
      originalContext = getWizard().getSetupContext();

      final List<VariableTask> unresolvedVariables = performer.getUnresolvedVariables();
      for (FieldHolder fieldHolder : manager)
      {
        unresolvedVariables.addAll(fieldHolder.getVariables());
      }

      final ResourceSet resourceSet = SetupUtil.createResourceSet();

      User user = getUser();
      final User copiedUser = EcoreUtil.copy(user);
      URI userResourceURI = user.eResource().getURI();
      Resource userResource = resourceSet.createResource(userResourceURI);
      userResource.getContents().add(copiedUser);

      Installation installation = performer.getInstallation();
      Resource installationResource = installation.eResource();
      URI installationResourceURI = installationResource.getURI();
      installationResource.setURI(URI.createFileURI(new File(performer.getProductConfigurationLocation(), "org.eclipse.oomph.setup/installation.setup")
          .toString()));

      Workspace workspace = performer.getWorkspace();
      Resource workspaceResource = null;
      URI workspaceResourceURI = null;
      if (workspace != null)
      {
        workspaceResource = workspace.eResource();
        workspaceResourceURI = workspaceResource.getURI();
        workspaceResource.setURI(URI.createFileURI(new File(performer.getWorkspaceLocation(), ".metadata/.plugins/org.eclipse.oomph.setup/workspace.setup")
            .toString()));
      }

      Installation copiedInstallation = EcoreUtil.copy(installation);
      URI copiedInstallationResourceURI = installation.eResource().getURI();
      Resource copiedInstallationResource = resourceSet.createResource(copiedInstallationResourceURI);
      copiedInstallationResource.getContents().add(copiedInstallation);

      Workspace copiedWorkspace = EcoreUtil.copy(workspace);
      if (workspace != null)
      {
        URI copiedWorkspaceResourceURI = workspace.eResource().getURI();
        Resource copiedWorkspaceResource = resourceSet.createResource(copiedWorkspaceResourceURI);
        copiedWorkspaceResource.getContents().add(copiedWorkspace);
      }

      performer.recordVariables(copiedInstallation, copiedWorkspace, copiedUser);

      unresolvedVariables.clear();

      getWizard().setSetupContext(SetupContext.create(copiedInstallation, copiedWorkspace, copiedUser));
      setPerformer(performer);

      BaseUtil.saveEObject(copiedUser);

      performer.savePasswords();

      installationResource.setURI(installationResourceURI);
      if (workspaceResource != null)
      {
        workspaceResource.setURI(workspaceResourceURI);
      }
    }
    else
    {
      originalContext = null;

      setPerformer(null);
    }
  }

  public String getValue(VariableTask variable)
  {
    FieldHolder fieldHolder = manager.getFieldHolder(variable, false, true);
    if (fieldHolder != null && (updating || fieldHolder.isDirty()))
    {
      String value = fieldHolder.getValue();
      if (!"".equals(value))
      {
        return value;
      }
    }

    if (updating && performer != null)
    {
      Object value = performer.getMap().get(variable.getName());
      if (value != null)
      {
        return value.toString();
      }
    }

    return null;
  }

  public boolean promptVariables(List<? extends SetupTaskContext> contexts)
  {
    prompted = true;

    @SuppressWarnings("unchecked")
    List<SetupTaskPerformer> performers = (List<SetupTaskPerformer>)contexts;
    allPromptedPerfomers.addAll(performers);
    for (SetupTaskPerformer performer : performers)
    {
      boolean resolvedAll = true;
      List<VariableTask> unresolvedVariables = performer.getUnresolvedVariables();
      for (VariableTask variable : unresolvedVariables)
      {
        FieldHolder fieldHolder = manager.getFieldHolder(variable, false, true);
        if (fieldHolder != null)
        {
          String value = fieldHolder.getValue();
          if (!"".equals(value))
          {
            variable.setValue(value);
          }
          else
          {
            resolvedAll = false;
          }
        }
        else
        {
          resolvedAll = false;
        }
      }

      if (!resolvedAll)
      {
        incompletePerformers.add(performer);
      }
    }

    boolean isComplete = incompletePerformers.isEmpty();
    return isComplete;
  }

  public UserCallback getUserCallback()
  {
    return new UICallback(getShell(), AbstractSetupDialog.SHELL_TEXT);
  }

  /**
   * @author <NAME>
   */
  private final class FieldHolder implements ValueListener
  {
    private final Set<VariableTask> variables = new LinkedHashSet<VariableTask>();

    private PropertyField field;
{'fraction_non_alphanumeric': 0.1049157880323155, 'fraction_numerical': 0.0014788443105573051, 'mean_word_length': 2.836923400231165, 'pattern_counts': {'":': 0, '<': 49, '<?xml version=': 0, '>': 49, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6965790', 'n_tokens_mistral': 10275, 'n_tokens_neox': 10070, 'n_words': 1961}