| /* |
| * Copyright 2000-2014 JetBrains s.r.o. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package com.intellij.formatting.alignment; |
| |
| import com.intellij.formatting.Alignment; |
| import com.intellij.psi.tree.IElementType; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| |
| import java.util.*; |
| |
| import static java.util.Arrays.asList; |
| |
| /** <code>GoF 'Strategy'</code> for {@link Alignment} retrieval. */ |
| public abstract class AlignmentStrategy { |
| |
| private static final AlignmentStrategy NULL_STRATEGY = wrap(null); |
| |
| /** @return shared strategy instance that returns <code>null</code> all the time */ |
| public static AlignmentStrategy getNullStrategy() { |
| return NULL_STRATEGY; |
| } |
| |
| /** |
| * Delegates the processing to {@link #wrap(Alignment, boolean, IElementType...)} with <code>'true'</code> as the second argument |
| * |
| * @param alignment target alignment to wrap |
| * @param filterTypes types to use as a filter |
| * @return alignment strategy for the given parameters |
| */ |
| public static AlignmentStrategy wrap(@Nullable Alignment alignment, IElementType... filterTypes) { |
| return new SharedAlignmentStrategy(alignment, true, filterTypes); |
| } |
| |
| /** |
| * Constructs strategy that returns given alignment for all elements which types pass through the target filter. |
| * |
| * @param alignment target alignment to wrap |
| * @param ignoreFilterTypes flag that defines if given alignment should be returned for all elements with given types or |
| * all elements except those with the given types |
| * @param filterTypes element types that should be used for filtering on subsequent calls |
| * to {@link #getAlignment(IElementType)} |
| * @return strategy that returns given alignment all the time for elements which types pass through the target |
| * filter; <code>null</code> otherwise |
| */ |
| public static AlignmentStrategy wrap(Alignment alignment, boolean ignoreFilterTypes, IElementType... filterTypes) { |
| return new SharedAlignmentStrategy(alignment, ignoreFilterTypes, filterTypes); |
| } |
| |
| /** |
| * Delegates to {@link #createAlignmentPerTypeStrategy(Collection, IElementType, boolean, Alignment.Anchor)} with no parent type |
| * check (<code>null</code> is delivered as a parent type) and {@link Alignment.Anchor#LEFT left anchor}. |
| * |
| * @param targetTypes target child types |
| * @param allowBackwardShift flag that defines if backward alignment shift is allowed |
| * @return alignment strategy for the given arguments |
| */ |
| public static AlignmentPerTypeStrategy createAlignmentPerTypeStrategy(@NotNull Collection<IElementType> targetTypes, |
| boolean allowBackwardShift) { |
| return new AlignmentPerTypeStrategy(targetTypes, null, allowBackwardShift, Alignment.Anchor.LEFT); |
| } |
| |
| /** |
| * Delegates the processing to {@link #createAlignmentPerTypeStrategy(Collection, IElementType, boolean, Alignment.Anchor)} |
| * with the given arguments and {@link Alignment.Anchor#LEFT left anchor}. |
| * |
| * @param targetTypes target types for which cached alignment should be returned |
| * @param parentType target parent type |
| * @param allowBackwardShift flag that specifies if former aligned element may be shifted to right in order to align |
| * to subsequent element |
| * @return alignment retrieval strategy that follows the rules described above |
| */ |
| public static AlignmentPerTypeStrategy createAlignmentPerTypeStrategy( |
| @NotNull Collection<IElementType> targetTypes, @Nullable IElementType parentType, boolean allowBackwardShift) { |
| return createAlignmentPerTypeStrategy(targetTypes, parentType, allowBackwardShift, Alignment.Anchor.LEFT); |
| } |
| |
| /** |
| * Creates strategy that creates and caches one alignment per given type internally and returns it on subsequent calls |
| * to {@link #getAlignment(IElementType, IElementType)} for elements which type is listed at the given collection and parent type |
| * (if defined) is the same as the given one; <code>null</code> is returned from {@link #getAlignment(IElementType, IElementType)} for all |
| * other elements. |
| * <p/> |
| * This strategy is assumed to be used at following situations - suppose we want to align code blocks that doesn't belong |
| * to the same parent but have similar structure, e.g. variable declaration assignments like the one below: |
| * <pre> |
| * int start = 1; |
| * int finish = 2; |
| * </pre> |
| * We can provide parent blocks of that target blocks with the same instance of this alignment strategy and let them eventually |
| * reuse the same alignment objects for target sub-blocks of the same type. |
| * |
| * @param targetTypes target types for which cached alignment should be returned |
| * @param parentType target parent type |
| * @param allowBackwardShift flag that specifies if former aligned element may be shifted to right in order to align |
| * to subsequent element (e.g. <code>'='</code> block of <code>'int start = 1'</code> statement |
| * below is shifted one symbol right in order to align to the <code>'='</code> block |
| * of <code>'int finish = 1'</code> statement) |
| * @return alignment retrieval strategy that follows the rules described above |
| */ |
| public static AlignmentPerTypeStrategy createAlignmentPerTypeStrategy( |
| @NotNull Collection<IElementType> targetTypes, @Nullable IElementType parentType, boolean allowBackwardShift, |
| @NotNull Alignment.Anchor anchor) { |
| return new AlignmentPerTypeStrategy(targetTypes, parentType, allowBackwardShift, anchor); |
| } |
| |
| /** |
| * Delegates the processing to {@link #getAlignment(IElementType, IElementType)} without parent element type |
| * filtering (<code>null</code> is used as parent element type). |
| * |
| * @param childType target child type |
| * @return alignment to use |
| */ |
| @Nullable |
| public Alignment getAlignment(@Nullable IElementType childType) { |
| return getAlignment(null, childType); |
| } |
| |
| /** |
| * Requests current strategy for alignment to use for the child of the given type assuming that parent node has the given type. |
| * |
| * @param parentType parent type to use for filtering (if not <code>null</code>) |
| * @param childType child type to use for filtering (if not <code>null</code>) |
| * @return alignment to use for the given arguments |
| */ |
| @Nullable |
| public abstract Alignment getAlignment(@Nullable IElementType parentType, @Nullable IElementType childType); |
| |
| /** |
| * Stands for {@link AlignmentStrategy} implementation that is configured to return single pre-configured {@link Alignment} object |
| * or <code>null</code> for all calls to {@link #getAlignment(IElementType)}. |
| */ |
| private static class SharedAlignmentStrategy extends AlignmentStrategy { |
| |
| private final Set<IElementType> myFilterElementTypes = new HashSet<IElementType>(); |
| |
| private final Alignment myAlignment; |
| private final boolean myIgnoreFilterTypes; |
| |
| private SharedAlignmentStrategy(Alignment alignment, boolean ignoreFilterTypes, IElementType... disabledElementTypes) { |
| myAlignment = alignment; |
| myIgnoreFilterTypes = ignoreFilterTypes; |
| myFilterElementTypes.addAll(asList(disabledElementTypes)); |
| } |
| |
| @Override |
| @Nullable |
| public Alignment getAlignment(@Nullable IElementType parentType, @Nullable IElementType childType) { |
| return myFilterElementTypes.contains(childType) ^ myIgnoreFilterTypes ? myAlignment : null; |
| } |
| } |
| |
| /** |
| * Alignment strategy that creates and caches alignments for target element types and returns them for elements with the |
| * same types. |
| */ |
| public static class AlignmentPerTypeStrategy extends AlignmentStrategy { |
| private final Map<IElementType, Alignment> myAlignments = new HashMap<IElementType, Alignment>(); |
| |
| private final IElementType myParentType; |
| private final boolean myAllowBackwardShift; |
| |
| AlignmentPerTypeStrategy(Collection<IElementType> targetElementTypes, |
| IElementType parentType, |
| boolean allowBackwardShift, |
| Alignment.Anchor anchor) { |
| myParentType = parentType; |
| myAllowBackwardShift = allowBackwardShift; |
| for (IElementType elementType : targetElementTypes) { |
| myAlignments.put(elementType, Alignment.createAlignment(myAllowBackwardShift, anchor)); |
| } |
| } |
| |
| @Override |
| public Alignment getAlignment(@Nullable IElementType parentType, @Nullable IElementType childType) { |
| if (myParentType != null && parentType != null && myParentType != parentType) { |
| return null; |
| } |
| return myAlignments.get(childType); |
| } |
| |
| public void renewAlignment(IElementType elementType) { |
| myAlignments.put(elementType, Alignment.createAlignment(myAllowBackwardShift)); |
| } |
| } |
| } |