예제 #1
0
  @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);
    }
  }
예제 #2
0
  private static class Context<E extends ArrangementEntry> {

    @NotNull public final List<ArrangementMoveInfo> moveInfos = ContainerUtilRt.newArrayList();

    @NotNull public final Rearranger<E> rearranger;
    @NotNull public final Collection<ArrangementEntryWrapper<E>> wrappers;
    @NotNull public final Document document;
    @NotNull public final List<? extends ArrangementMatchRule> rules;
    @NotNull public final List<? extends ArrangementMatchRule> rulesByPriority;
    @NotNull public final CodeStyleSettings settings;
    @NotNull public final Changer changer;

    private Context(
        @NotNull Rearranger<E> rearranger,
        @NotNull Collection<ArrangementEntryWrapper<E>> wrappers,
        @NotNull Document document,
        @NotNull List<? extends ArrangementMatchRule> rules,
        @NotNull List<? extends ArrangementMatchRule> rulesByPriority,
        @NotNull CodeStyleSettings settings,
        @NotNull Changer changer) {
      this.rearranger = rearranger;
      this.wrappers = wrappers;
      this.document = document;
      this.rules = rules;
      this.rulesByPriority = rulesByPriority;
      this.settings = settings;
      this.changer = changer;
    }

    public void addMoveInfo(int oldStart, int oldEnd, int newStart) {
      moveInfos.add(new ArrangementMoveInfo(oldStart, oldEnd, newStart));
    }

    public static <T extends ArrangementEntry> Context<T> from(
        @NotNull Rearranger<T> rearranger,
        @NotNull Document document,
        @NotNull PsiElement root,
        @NotNull Collection<TextRange> ranges,
        @NotNull ArrangementSettings arrangementSettings,
        @NotNull CodeStyleSettings codeStyleSettings) {
      Collection<T> entries = rearranger.parse(root, document, ranges, arrangementSettings);
      Collection<ArrangementEntryWrapper<T>> wrappers = new ArrayList<ArrangementEntryWrapper<T>>();
      ArrangementEntryWrapper<T> previous = null;
      for (T entry : entries) {
        ArrangementEntryWrapper<T> wrapper = new ArrangementEntryWrapper<T>(entry);
        if (previous != null) {
          previous.setNext(wrapper);
          wrapper.setPrevious(previous);
        }
        wrappers.add(wrapper);
        previous = wrapper;
      }
      Changer changer;
      if (document instanceof DocumentEx) {
        changer = new RangeMarkerAwareChanger<T>((DocumentEx) document);
      } else {
        changer = new DefaultChanger();
      }
      final List<? extends ArrangementMatchRule> rulesByPriority =
          ArrangementUtil.getRulesSortedByPriority(arrangementSettings);
      return new Context<T>(
          rearranger,
          wrappers,
          document,
          arrangementSettings.getRules(),
          rulesByPriority,
          codeStyleSettings,
          changer);
    }
  }
예제 #3
0
  /**
   * 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;
  }