content: /*******************************************************************************
   * Copyright (C) 2021, 1C-Soft LLC and others.
   *
   * This program and the accompanying materials are made
   * available under the terms of the Eclipse Public License 2.0
   * which is available at https://www.eclipse.org/legal/epl-2.0/
   *
   * SPDX-License-Identifier: EPL-2.0
   *
   * Contributors:
   *     1C-Soft LLC - initial API and implementation
   *******************************************************************************/
  package com.e1c.v8codestyle.internal.autosort;
  
  import java.util.ArrayList;
  import java.util.Collection;
  import java.util.Collections;
  import java.util.Comparator;
  import java.util.List;
  
  import org.eclipse.core.runtime.Assert;
  import org.eclipse.core.runtime.IProgressMonitor;
  import org.eclipse.emf.common.util.EList;
  import org.eclipse.emf.ecore.EObject;
  import org.eclipse.emf.ecore.EReference;
  import org.eclipse.emf.edit.command.ChangeCommand;
  
  import com._1c.g5.v8.bm.core.IBmTransaction;
  import com._1c.g5.v8.bm.integration.AbstractBmTask;
  import com._1c.g5.v8.dt.core.model.EditingMode;
  import com._1c.g5.v8.dt.core.model.IModelEditingSupport;
  import com.e1c.v8codestyle.autosort.SortItem;
  
  /**
   * The task to sort BM objects.
   *
   * @author Dmitriy Marmyshev
   *
   * @see IBmModel
   * @see SortItem
   */
  public class SortBmTask
      extends AbstractBmTask<Void>
  {
  
      private final Collection<SortItem> items;
  
      private final IModelEditingSupport modelEditingSupport;
  
      /**
       * Instantiates a new sort BM task.
       *
       * <br>
       * <b>WARNING!</b> the collection of items must be immutable or unmodifiable for callers.
       *
       * @param items the unmodifiable collection of items to sort, cannot be {@code null}.
       */
      public SortBmTask(final Collection<SortItem> items, IModelEditingSupport modelEditingSupport)
      {
          super("Sort Md objects"); //$NON-NLS-1$
          this.items = new ArrayList<>(items);
          this.modelEditingSupport = modelEditingSupport;
      }
  
      @Override
      public Void execute(IBmTransaction transaction, IProgressMonitor monitor)
      {
          for (SortItem item : items)
          {
              if (monitor.isCanceled())
              {
                  return null;
              }
  
              if (!item.getListRef().isMany())
              {
                  continue;
              }
  
              EObject parent = transaction.getTopObjectByFqn(item.getFqn());
              if (parent != null && modelEditingSupport.canEdit(parent, EditingMode.DIRECT))
              {
                  Object value = parent.eGet(item.getListRef());
                  if (!(value instanceof List))
                  {
                      continue;
                  }
  
                  List<?> elements = (List<?>)value;
                  if (elements.size() < 2)
                  {
                      continue;
                  }
  
                  SortCommand command = new SortCommand(parent, item.getListRef(), item.getSorter());
                  if (command.canExecute())
                  {
                      command.execute();
                  }
                  command.dispose();
  
              }
          }
          return null;
      }
  
      private static final class SortCommand
          extends ChangeCommand
      {
  
          private final EList<EObject> list;
  
          private final Comparator<EObject> sorter;
  
          @SuppressWarnings("unchecked")
          private SortCommand(EObject parent, EReference listFeature, Comparator<EObject> sorter)
          {
              super(parent);
              Assert.isNotNull(parent);
              Assert.isTrue(listFeature.isMany());
              this.list = (EList<EObject>)parent.eGet(listFeature);
              this.sorter = sorter;
          }
  
          @Override
          protected void doExecute()
          {
              ArrayList<EObject> sorted = new ArrayList<>(list);
              Collections.sort(sorted, this.sorter);
              for (int i = 0; i < sorted.size(); i++)
              {
                  EObject element = sorted.get(i);
                  int oldIndex = list.indexOf(element);
                  int newIndex = sorted.indexOf(element);
                  if (oldIndex != -1 && newIndex != -1 && oldIndex != newIndex)
                  {
                      list.move(newIndex, element);
                  }
              }
          }
      }
  
  }
  