private static <E extends ArrangementEntry> void sortByName(@NotNull List<E> entries) {
   if (entries.size() < 2) {
     return;
   }
   final TObjectIntHashMap<E> weights = new TObjectIntHashMap<E>();
   int i = 0;
   for (E e : entries) {
     weights.put(e, ++i);
   }
   ContainerUtil.sort(
       entries,
       new Comparator<E>() {
         @Override
         public int compare(E e1, E e2) {
           String name1 =
               e1 instanceof NameAwareArrangementEntry
                   ? ((NameAwareArrangementEntry) e1).getName()
                   : null;
           String name2 =
               e2 instanceof NameAwareArrangementEntry
                   ? ((NameAwareArrangementEntry) e2).getName()
                   : null;
           if (name1 != null && name2 != null) {
             return name1.compareTo(name2);
           } else if (name1 == null && name2 == null) {
             return weights.get(e1) - weights.get(e2);
           } else if (name2 == null) {
             return -1;
           } else {
             return 1;
           }
         }
       });
 }
  @SuppressWarnings("unchecked")
  private static <E extends ArrangementEntry> void doArrange(
      @NotNull List<ArrangementEntryWrapper<E>> wrappers, @NotNull Context<E> context) {
    if (wrappers.isEmpty()) {
      return;
    }

    Map<E, ArrangementEntryWrapper<E>> map = ContainerUtilRt.newHashMap();
    List<E> arranged = ContainerUtilRt.newArrayList();
    List<E> toArrange = ContainerUtilRt.newArrayList();
    for (ArrangementEntryWrapper<E> wrapper : wrappers) {
      E entry = wrapper.getEntry();
      map.put(wrapper.getEntry(), wrapper);
      if (!entry.canBeMatched()) {
        // Split entries to arrange by 'can not be matched' rules.
        // See IDEA-104046 for a problem use-case example.
        if (toArrange.isEmpty()) {
          arranged.addAll(arrange(toArrange, context.rules, context.rulesByPriority));
        }
        arranged.add(entry);
        toArrange.clear();
      } else {
        toArrange.add(entry);
      }
    }
    if (!toArrange.isEmpty()) {
      arranged.addAll(arrange(toArrange, context.rules, context.rulesByPriority));
    }

    context.changer.prepare(wrappers, context);
    // We apply changes from the last position to the first position in order not to bother with
    // offsets shifts.
    for (int i = arranged.size() - 1; i >= 0; i--) {
      ArrangementEntryWrapper<E> arrangedWrapper = map.get(arranged.get(i));
      ArrangementEntryWrapper<E> initialWrapper = wrappers.get(i);
      context.changer.replace(
          arrangedWrapper, initialWrapper, i > 0 ? map.get(arranged.get(i - 1)) : null, context);
    }
  }
  /**
   * Arranges (re-orders) given entries according to the given rules.
   *
   * @param entries entries to arrange
   * @param rules rules to use for arrangement
   * @param rulesByPriority rules sorted by priority ('public static' rule will have higher priority
   *     than 'public')
   * @param <E> arrangement entry type
   * @return arranged list of the given rules
   */
  @SuppressWarnings("AssignmentToForLoopParameter")
  @NotNull
  public static <E extends ArrangementEntry> List<E> arrange(
      @NotNull Collection<E> entries,
      @NotNull List<? extends ArrangementMatchRule> rules,
      @NotNull List<? extends ArrangementMatchRule> rulesByPriority) {
    List<E> arranged = ContainerUtilRt.newArrayList();
    Set<E> unprocessed = ContainerUtilRt.newLinkedHashSet();
    List<Pair<Set<ArrangementEntry>, E>> dependent = ContainerUtilRt.newArrayList();
    for (E entry : entries) {
      List<? extends ArrangementEntry> dependencies = entry.getDependencies();
      if (dependencies == null) {
        unprocessed.add(entry);
      } else {
        if (dependencies.size() == 1 && dependencies.get(0) == entry.getParent()) {
          // Handle a situation when the entry is configured to be at the first parent's children.
          arranged.add(entry);
        } else {
          Set<ArrangementEntry> first = new HashSet<ArrangementEntry>(dependencies);
          dependent.add(Pair.create(first, entry));
        }
      }
    }

    Set<E> matched = new HashSet<E>();

    MultiMap<ArrangementMatchRule, E> elementsByRule = new MultiMap<ArrangementMatchRule, E>();
    for (ArrangementMatchRule rule : rulesByPriority) {
      matched.clear();
      for (E entry : unprocessed) {
        if (entry.canBeMatched() && rule.getMatcher().isMatched(entry)) {
          elementsByRule.putValue(rule, entry);
          matched.add(entry);
        }
      }
      unprocessed.removeAll(matched);
    }

    for (ArrangementMatchRule rule : rules) {
      if (elementsByRule.containsKey(rule)) {
        final Collection<E> arrangedEntries = elementsByRule.get(rule);

        // Sort by name if necessary.
        if (StdArrangementTokens.Order.BY_NAME.equals(rule.getOrderType())) {
          sortByName((List<E>) arrangedEntries);
        }
        arranged.addAll(arrangedEntries);
      }
    }
    arranged.addAll(unprocessed);

    for (int i = 0; i < arranged.size() && !dependent.isEmpty(); i++) {
      E e = arranged.get(i);
      for (Iterator<Pair<Set<ArrangementEntry>, E>> iterator = dependent.iterator();
          iterator.hasNext(); ) {
        Pair<Set<ArrangementEntry>, E> pair = iterator.next();
        pair.first.remove(e);
        if (pair.first.isEmpty()) {
          iterator.remove();
          arranged.add(i + 1, pair.second);
        }
      }
    }

    return arranged;
  }