Maven: dependencies with 'tests' classifier in test classpath (IDEA-54254)
[idea/community.git] / platform / lang-impl / src / com / intellij / formatting / templateLanguages / BlockUtil.java
1 /*
2  * Copyright 2000-2009 JetBrains s.r.o.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.intellij.formatting.templateLanguages;
17
18 import com.intellij.formatting.ASTBlock;
19 import com.intellij.formatting.Block;
20 import com.intellij.formatting.Indent;
21 import com.intellij.formatting.Spacing;
22 import com.intellij.lang.ASTNode;
23 import com.intellij.openapi.util.Pair;
24 import com.intellij.openapi.util.TextRange;
25 import org.jetbrains.annotations.NotNull;
26 import org.jetbrains.annotations.Nullable;
27
28 import java.util.ArrayList;
29 import java.util.Collections;
30 import java.util.List;
31
32 /**
33  * @author Alexey Chmutov
34  *         Date: Jul 3, 2009
35  *         Time: 2:47:10 PM
36  */
37 class BlockUtil {
38   private BlockUtil() {
39   }
40
41   public static List<DataLanguageBlockWrapper> buildChildWrappers(@NotNull final Block parent) {
42     assert !(parent instanceof DataLanguageBlockWrapper) : parent.getClass();
43     List<Block> children = parent.getSubBlocks();
44     if (children.size() == 0) return Collections.emptyList();
45     ArrayList<DataLanguageBlockWrapper> result = new ArrayList<DataLanguageBlockWrapper>(children.size());
46     DataLanguageBlockWrapper prevWrapper = null;
47     for (Block child : children) {
48       DataLanguageBlockWrapper currWrapper = createAndAddBlock(result, child, null);
49       if(currWrapper != null && prevWrapper != null) {
50         Spacing spacing = parent.getSpacing(prevWrapper.getOriginal(), currWrapper.getOriginal());
51         prevWrapper.setRightHandSpacing(currWrapper, spacing);
52       }
53       prevWrapper = currWrapper;
54     }
55     return result;
56   }
57
58   public static Pair<List<DataLanguageBlockWrapper>, List<DataLanguageBlockWrapper>> splitBlocksByRightBound(@NotNull Block parent, @NotNull TextRange bounds) {
59     final List<Block> subBlocks = parent.getSubBlocks();
60     if (subBlocks.size() == 0) return new Pair<List<DataLanguageBlockWrapper>, List<DataLanguageBlockWrapper>>(Collections.<DataLanguageBlockWrapper>emptyList(), Collections.<DataLanguageBlockWrapper>emptyList());
61     final ArrayList<DataLanguageBlockWrapper> before = new ArrayList<DataLanguageBlockWrapper>(subBlocks.size() / 2);
62     final ArrayList<DataLanguageBlockWrapper> after = new ArrayList<DataLanguageBlockWrapper>(subBlocks.size() / 2);
63     splitByRightBoundAndCollectBlocks(subBlocks, before, after, bounds);
64     return new Pair<List<DataLanguageBlockWrapper>, List<DataLanguageBlockWrapper>>(before, after);
65   }
66
67   private static void splitByRightBoundAndCollectBlocks(@NotNull List<Block> blocks,
68                                                         @NotNull List<DataLanguageBlockWrapper> before,
69                                                         @NotNull List<DataLanguageBlockWrapper> after,
70                                                         @NotNull TextRange bounds) {
71     for (Block block : blocks) {
72       final TextRange textRange = block.getTextRange();
73       if (bounds.contains(textRange)) {
74         createAndAddBlock(before, block, null);
75       }
76       else if (bounds.getEndOffset() < textRange.getStartOffset()) {
77         createAndAddBlock(after, block, null);
78       }
79       else {
80         splitByRightBoundAndCollectBlocks(block.getSubBlocks(), before, after, bounds);
81       }
82     }
83   }
84
85   @Nullable
86   private static DataLanguageBlockWrapper createAndAddBlock(List<DataLanguageBlockWrapper> list, Block block, @Nullable final Indent indent) {
87     DataLanguageBlockWrapper wrapper = DataLanguageBlockWrapper.create(block, indent);
88     if (wrapper != null) {
89       list.add(wrapper);
90     }
91     return wrapper;
92   }
93
94
95   public static List<Block> mergeBlocks(@NotNull List<TemplateLanguageBlock> tlBlocks, @NotNull List<DataLanguageBlockWrapper> foreignBlocks) {
96     ArrayList<Block> result = new ArrayList<Block>(tlBlocks.size() + foreignBlocks.size());
97     int vInd = 0;
98     int fInd = 0;
99     while (vInd < tlBlocks.size() && fInd < foreignBlocks.size()) {
100       final TemplateLanguageBlock v = tlBlocks.get(vInd);
101       final DataLanguageBlockWrapper f = foreignBlocks.get(fInd);
102       final TextRange vRange = v.getTextRange();
103       final TextRange fRange = f.getTextRange();
104       if (vRange.getStartOffset() >= fRange.getEndOffset()) {
105         // add leading foreign blocks
106         result.add(f);
107         fInd++;
108       }
109       else if (vRange.getEndOffset() <= fRange.getStartOffset()) {
110         // add leading TL blocks
111         result.add(v);
112         vInd++;
113       }
114       else if (vRange.getStartOffset() < fRange.getStartOffset() ||
115                vRange.getStartOffset() == fRange.getStartOffset() && vRange.getEndOffset() >= fRange.getEndOffset()) {
116         // add including TL blocks and split intersecting foreign blocks
117         result.add(v);
118         while (fInd < foreignBlocks.size() && vRange.contains(foreignBlocks.get(fInd).getTextRange())) {
119           v.addForeignChild(foreignBlocks.get(fInd++));
120         }
121         if (fInd < foreignBlocks.size()) {
122           final DataLanguageBlockWrapper notContainedF = foreignBlocks.get(fInd);
123           if (vRange.intersectsStrict(notContainedF.getTextRange())) {
124             Pair<List<DataLanguageBlockWrapper>, List<DataLanguageBlockWrapper>> splitBlocks = splitBlocksByRightBound(notContainedF.getOriginal(), vRange);
125             v.addForeignChildren(splitBlocks.getFirst());
126             foreignBlocks.remove(fInd);
127             if (splitBlocks.getSecond().size() > 0) {
128               foreignBlocks.addAll(fInd, splitBlocks.getSecond());
129             }
130           }
131         }
132         vInd++;
133       }
134       else if (vRange.getStartOffset() > fRange.getStartOffset() ||
135                vRange.getStartOffset() == fRange.getStartOffset() && vRange.getEndOffset() < fRange.getEndOffset()) {
136         // add including foreign blocks or split them if needed
137         int lastContainedTlInd = vInd;
138         while (lastContainedTlInd < tlBlocks.size() && fRange.intersectsStrict(tlBlocks.get(lastContainedTlInd).getTextRange())) {
139           lastContainedTlInd++;
140         }
141         if (fRange.contains(tlBlocks.get(lastContainedTlInd - 1).getTextRange())) {
142           result.add(f);
143           fInd++;
144           while (vInd < lastContainedTlInd) {
145             f.addTlChild(tlBlocks.get(vInd++));
146           }
147         }
148         else {
149           foreignBlocks.remove(fInd);
150           foreignBlocks.addAll(fInd, buildChildWrappers(f.getOriginal()));
151         }
152       }
153     }
154     while (vInd < tlBlocks.size()) {
155       result.add(tlBlocks.get(vInd++));
156     }
157     while (fInd < foreignBlocks.size()) {
158       result.add(foreignBlocks.get(fInd++));
159     }
160     return result;
161   }
162
163   @NotNull
164   public static List<DataLanguageBlockWrapper> filterBlocksByRange(@NotNull List<DataLanguageBlockWrapper> list, @NotNull TextRange textRange) {
165     int i = 0;
166     while (i < list.size()) {
167       final DataLanguageBlockWrapper wrapper = list.get(i);
168       final TextRange range = wrapper.getTextRange();
169       if (textRange.contains(range)) {
170         i++;
171       }
172       else if (range.intersectsStrict(textRange)) {
173         list.remove(i);
174         list.addAll(i, buildChildWrappers(wrapper.getOriginal()));
175       }
176       else {
177         list.remove(i);
178       }
179     }
180     return list;
181   }
182
183   static List<Block> splitBlockIntoFragments(@NotNull Block block, @NotNull List<TemplateLanguageBlock> subBlocks) {
184     final List<Block> children = new ArrayList<Block>(5);
185     final TextRange range = block.getTextRange();
186     int childStartOffset = range.getStartOffset();
187     TemplateLanguageBlock lastTLBlock = null;
188     for (TemplateLanguageBlock tlBlock : subBlocks) {
189       final TextRange tlTextRange = tlBlock.getTextRange();
190       if (tlTextRange.getStartOffset() > childStartOffset) {
191         TextRange dataBlockTextRange = new TextRange(childStartOffset, tlTextRange.getStartOffset());
192         if (tlBlock.isRequiredRange(dataBlockTextRange)) {
193           children.add(new DataLanguageBlockFragmentWrapper(block, dataBlockTextRange));
194         }
195       }
196       children.add(tlBlock);
197       lastTLBlock = tlBlock;
198       childStartOffset = tlTextRange.getEndOffset();
199     }
200     if (range.getEndOffset() > childStartOffset) {
201       TextRange dataBlockTextRange = new TextRange(childStartOffset, range.getEndOffset());
202       if (lastTLBlock == null || lastTLBlock.isRequiredRange(dataBlockTextRange) ) {
203         children.add(new DataLanguageBlockFragmentWrapper(block, dataBlockTextRange));
204       }
205     }
206     return children;
207   }
208
209   static void printBlocks(@Nullable TextRange textRange, @NotNull List<Block> list) {
210     StringBuilder sb = new StringBuilder(String.valueOf(textRange)).append(": ");
211     for (Block block : list) {
212       ASTNode node = block instanceof ASTBlock ? ((ASTBlock)block).getNode() : null;
213       TextRange r = block.getTextRange();
214       sb.append(" [").append(node != null ? node.getElementType() : null)//.append(" ").append(((BlockWithParent)block).getParent() != null)
215           .append(r).append(block.getIndent()).append(block.getAlignment()).append("] ");
216     }
217     System.out.println(sb);
218   }
219
220   static List<Block> setParent(List<Block> children, BlockWithParent parent) {
221     for (Block block : children) {
222       if (block instanceof BlockWithParent) ((BlockWithParent)block).setParent(parent);
223     }
224     return children;
225   }
226 }