@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); } }
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); } }
/** * 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; }