Expandable indent storing additional block, which indent is used as min indent marker...
[idea/community.git] / java / java-impl / src / com / intellij / psi / formatter / java / AbstractJavaBlock.java
1 /*
2  * Copyright 2000-2014 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.psi.formatter.java;
17
18 import com.intellij.formatting.*;
19 import com.intellij.formatting.alignment.AlignmentStrategy;
20 import com.intellij.lang.ASTNode;
21 import com.intellij.openapi.diagnostic.Logger;
22 import com.intellij.openapi.util.TextRange;
23 import com.intellij.openapi.util.text.StringUtil;
24 import com.intellij.psi.*;
25 import com.intellij.psi.codeStyle.CodeStyleSettings;
26 import com.intellij.psi.codeStyle.CommonCodeStyleSettings;
27 import com.intellij.psi.codeStyle.JavaCodeStyleSettings;
28 import com.intellij.psi.formatter.FormatterUtil;
29 import com.intellij.psi.formatter.common.AbstractBlock;
30 import com.intellij.psi.formatter.java.wrap.JavaWrapManager;
31 import com.intellij.psi.formatter.java.wrap.ReservedWrapsProvider;
32 import com.intellij.psi.impl.source.SourceTreeToPsiMap;
33 import com.intellij.psi.impl.source.codeStyle.ShiftIndentInsideHelper;
34 import com.intellij.psi.impl.source.tree.*;
35 import com.intellij.psi.impl.source.tree.injected.InjectedLanguageUtil;
36 import com.intellij.psi.impl.source.tree.java.ClassElement;
37 import com.intellij.psi.jsp.JspElementType;
38 import com.intellij.psi.tree.IElementType;
39 import com.intellij.util.containers.ContainerUtil;
40 import com.intellij.util.text.CharArrayUtil;
41 import org.jetbrains.annotations.NotNull;
42 import org.jetbrains.annotations.Nullable;
43
44 import java.util.ArrayList;
45 import java.util.List;
46 import java.util.Map;
47
48 import static com.intellij.psi.formatter.java.JavaFormatterUtil.getWrapType;
49 import static com.intellij.psi.formatter.java.MultipleFieldDeclarationHelper.findLastFieldInGroup;
50
51 public abstract class AbstractJavaBlock extends AbstractBlock implements JavaBlock, ReservedWrapsProvider {
52
53   private static final Logger LOG = Logger.getInstance("#com.intellij.psi.formatter.java.AbstractJavaBlock");
54
55   @NotNull protected final CommonCodeStyleSettings mySettings;
56   @NotNull protected final JavaCodeStyleSettings myJavaSettings;
57   protected final CommonCodeStyleSettings.IndentOptions myIndentSettings;
58   private final Indent myIndent;
59   protected Indent myChildIndent;
60   protected Alignment myChildAlignment;
61   protected boolean myUseChildAttributes = false;
62   @NotNull protected final AlignmentStrategy myAlignmentStrategy;
63   private boolean myIsAfterClassKeyword = false;
64
65   protected Alignment myReservedAlignment;
66   protected Alignment myReservedAlignment2;
67
68   private final JavaWrapManager myWrapManager;
69   private Map<IElementType, Wrap> myPreferredWraps;
70   private AbstractJavaBlock myParentBlock;
71
72   private BlockFactory myBlockFactory = new BlockFactory() {
73     @Override
74     public Block createBlock(ASTNode node, Indent indent, Alignment alignment, Wrap wrap) {
75       return new SimpleJavaBlock(node, wrap, AlignmentStrategy.wrap(alignment), indent, mySettings, myJavaSettings);
76     }
77
78     @Override
79     public CommonCodeStyleSettings getSettings() {
80       return mySettings;
81     }
82
83     @Override
84     public JavaCodeStyleSettings getJavaSettings() {
85       return myJavaSettings;
86     }
87   };
88
89   protected AbstractJavaBlock(@NotNull final ASTNode node,
90                               final Wrap wrap,
91                               final Alignment alignment,
92                               final Indent indent,
93                               @NotNull final CommonCodeStyleSettings settings,
94                               @NotNull JavaCodeStyleSettings javaSettings)
95   {
96     this(node, wrap, indent, settings, javaSettings, JavaWrapManager.INSTANCE, AlignmentStrategy.wrap(alignment));
97   }
98
99   protected AbstractJavaBlock(@NotNull final ASTNode node,
100                               final Wrap wrap,
101                               @NotNull final AlignmentStrategy alignmentStrategy,
102                               final Indent indent,
103                               @NotNull final CommonCodeStyleSettings settings,
104                               @NotNull JavaCodeStyleSettings javaSettings)
105   {
106     this(node, wrap, indent, settings, javaSettings, JavaWrapManager.INSTANCE, alignmentStrategy);
107   }
108
109   private AbstractJavaBlock(@NotNull ASTNode ignored,
110                             @NotNull CommonCodeStyleSettings commonSettings,
111                             @NotNull JavaCodeStyleSettings javaSettings) {
112     super(ignored, null, null);
113     mySettings = commonSettings;
114     myJavaSettings = javaSettings;
115     myIndentSettings = commonSettings.getIndentOptions();
116     myIndent = null;
117     myWrapManager = JavaWrapManager.INSTANCE;
118     myAlignmentStrategy = AlignmentStrategy.getNullStrategy();
119   }
120
121   protected AbstractJavaBlock(@NotNull final ASTNode node,
122                               final Wrap wrap,
123                               final Indent indent,
124                               @NotNull final CommonCodeStyleSettings settings,
125                               @NotNull JavaCodeStyleSettings javaSettings,
126                               final JavaWrapManager wrapManager,
127                               @NotNull final AlignmentStrategy alignmentStrategy) {
128     super(node, wrap, createBlockAlignment(alignmentStrategy, node));
129     mySettings = settings;
130     myJavaSettings = javaSettings;
131     myIndentSettings = settings.getIndentOptions();
132     myIndent = indent;
133     myWrapManager = wrapManager;
134     myAlignmentStrategy = alignmentStrategy;
135   }
136
137   @Nullable
138   private static Alignment createBlockAlignment(@NotNull AlignmentStrategy strategy, @NotNull ASTNode node) {
139     // There is a possible case that 'implements' section is incomplete (e.g. ends with comma). We may want to align lbrace
140     // to the first implemented interface reference then.
141     if (node.getElementType() == JavaElementType.IMPLEMENTS_LIST) {
142       return null;
143     }
144     return strategy.getAlignment(node.getElementType());
145   }
146
147   @NotNull
148   public Block createJavaBlock(@NotNull ASTNode child,
149                                @NotNull CommonCodeStyleSettings settings,
150                                @NotNull JavaCodeStyleSettings javaSettings,
151                                @Nullable Indent indent,
152                                @Nullable Wrap wrap,
153                                Alignment alignment) {
154     return createJavaBlock(child, settings, javaSettings,indent, wrap, AlignmentStrategy.wrap(alignment));
155   }
156
157   @NotNull
158   public Block createJavaBlock(@NotNull ASTNode child,
159                                @NotNull CommonCodeStyleSettings settings,
160                                @NotNull JavaCodeStyleSettings javaSettings,
161                                final Indent indent,
162                                @Nullable Wrap wrap,
163                                @NotNull AlignmentStrategy alignmentStrategy) {
164     return createJavaBlock(child, settings, javaSettings, indent, wrap, alignmentStrategy, -1);
165   }
166
167   @NotNull
168   private Block createJavaBlock(@NotNull ASTNode child,
169                                 @NotNull CommonCodeStyleSettings settings,
170                                 @NotNull JavaCodeStyleSettings javaSettings,
171                                 @Nullable Indent indent,
172                                 Wrap wrap,
173                                 @NotNull AlignmentStrategy alignmentStrategy,
174                                 int startOffset) {
175     Indent actualIndent = indent == null ? getDefaultSubtreeIndent(child, getJavaIndentOptions(settings)) : indent;
176     final IElementType elementType = child.getElementType();
177     Alignment alignment = alignmentStrategy.getAlignment(elementType);
178
179     if (child.getPsi() instanceof PsiWhiteSpace) {
180       String text = child.getText();
181       int start = CharArrayUtil.shiftForward(text, 0, " \t\n");
182       int end = CharArrayUtil.shiftBackward(text, text.length() - 1, " \t\n") + 1;
183       LOG.assertTrue(start < end);
184       return new PartialWhitespaceBlock(child, new TextRange(start + child.getStartOffset(), end + child.getStartOffset()),
185                                         wrap, alignment, actualIndent, settings, javaSettings);
186     }
187     if (child.getPsi() instanceof PsiClass) {
188       return new CodeBlockBlock(child, wrap, alignment, actualIndent, settings, javaSettings);
189     }
190     if (isBlockType(elementType)) {
191       return new BlockContainingJavaBlock(child, wrap, alignment, actualIndent, settings, javaSettings);
192     }
193     if (isStatement(child, child.getTreeParent())) {
194       return new CodeBlockBlock(child, wrap, alignment, actualIndent, settings, javaSettings);
195     }
196     if (isBuildInjectedBlocks() &&
197         child instanceof PsiComment &&
198         child instanceof PsiLanguageInjectionHost &&
199         InjectedLanguageUtil.hasInjections((PsiLanguageInjectionHost)child)) {
200       return new CommentWithInjectionBlock(child, wrap, alignment, indent, settings, javaSettings);
201     }
202     if (child instanceof LeafElement) {
203       final LeafBlock block = new LeafBlock(child, wrap, alignment, actualIndent);
204       block.setStartOffset(startOffset);
205       return block;
206     }
207     else if (isLikeExtendsList(elementType)) {
208       return new ExtendsListBlock(child, wrap, alignmentStrategy, settings, javaSettings);
209     }
210     else if (elementType == JavaElementType.CODE_BLOCK) {
211       return new CodeBlockBlock(child, wrap, alignment, actualIndent, settings, javaSettings);
212     }
213     else if (elementType == JavaElementType.LABELED_STATEMENT) {
214       return new LabeledJavaBlock(child, wrap, alignment, actualIndent, settings, javaSettings);
215     }
216     else if (elementType == JavaDocElementType.DOC_COMMENT) {
217       return new DocCommentBlock(child, wrap, alignment, actualIndent, settings, javaSettings);
218     }
219     else {
220       final SimpleJavaBlock simpleJavaBlock = new SimpleJavaBlock(child, wrap, alignmentStrategy, actualIndent, settings, javaSettings);
221       simpleJavaBlock.setStartOffset(startOffset);
222       return simpleJavaBlock;
223     }
224   }
225
226   @NotNull
227   public static Block newJavaBlock(@NotNull ASTNode child,
228                                       @NotNull CommonCodeStyleSettings settings,
229                                       @NotNull JavaCodeStyleSettings javaSettings) {
230     final Indent indent = getDefaultSubtreeIndent(child, getJavaIndentOptions(settings));
231     return newJavaBlock(child, settings, javaSettings, indent, null, AlignmentStrategy.getNullStrategy());
232   }
233
234   @NotNull
235   public static Block newJavaBlock(@NotNull ASTNode child,
236                                    @NotNull CommonCodeStyleSettings settings,
237                                    @NotNull JavaCodeStyleSettings javaSettings,
238                                    @Nullable Indent indent,
239                                    @Nullable Wrap wrap,
240                                    @NotNull AlignmentStrategy strategy) {
241     return new AbstractJavaBlock(child, settings, javaSettings) {
242       @Override
243       protected List<Block> buildChildren() {
244         return null;
245       }
246     }.createJavaBlock(child, settings, javaSettings, indent, wrap, strategy);
247   }
248
249   @NotNull
250   private static CommonCodeStyleSettings.IndentOptions getJavaIndentOptions(CommonCodeStyleSettings settings) {
251     CommonCodeStyleSettings.IndentOptions indentOptions = settings.getIndentOptions();
252     assert indentOptions != null : "Java indent options are not initialized";
253     return indentOptions;
254   }
255
256   private static boolean isLikeExtendsList(final IElementType elementType) {
257     return elementType == JavaElementType.EXTENDS_LIST
258            || elementType == JavaElementType.IMPLEMENTS_LIST
259            || elementType == JavaElementType.THROWS_LIST;
260   }
261
262   private static boolean isBlockType(final IElementType elementType) {
263     return elementType == JavaElementType.SWITCH_STATEMENT
264            || elementType == JavaElementType.FOR_STATEMENT
265            || elementType == JavaElementType.WHILE_STATEMENT
266            || elementType == JavaElementType.DO_WHILE_STATEMENT
267            || elementType == JavaElementType.TRY_STATEMENT
268            || elementType == JavaElementType.CATCH_SECTION
269            || elementType == JavaElementType.IF_STATEMENT
270            || elementType == JavaElementType.METHOD
271            || elementType == JavaElementType.ARRAY_INITIALIZER_EXPRESSION
272            || elementType == JavaElementType.ANNOTATION_ARRAY_INITIALIZER
273            || elementType == JavaElementType.CLASS_INITIALIZER
274            || elementType == JavaElementType.SYNCHRONIZED_STATEMENT
275            || elementType == JavaElementType.FOREACH_STATEMENT;
276   }
277
278
279   @Nullable
280   private static Indent getDefaultSubtreeIndent(@NotNull ASTNode child, @NotNull CommonCodeStyleSettings.IndentOptions indentOptions) {
281     final ASTNode parent = child.getTreeParent();
282     final IElementType childNodeType = child.getElementType();
283     if (childNodeType == JavaElementType.ANNOTATION) {
284       if (parent.getPsi() instanceof PsiArrayInitializerMemberValue) {
285         return Indent.getNormalIndent();
286       }
287       return Indent.getNoneIndent();
288     }
289
290     final ASTNode prevElement = FormatterUtil.getPreviousNonWhitespaceSibling(child);
291     if (prevElement != null && prevElement.getElementType() == JavaElementType.MODIFIER_LIST) {
292       return Indent.getNoneIndent();
293     }
294
295     if (childNodeType == JavaDocElementType.DOC_TAG) return Indent.getNoneIndent();
296     if (childNodeType == JavaDocTokenType.DOC_COMMENT_LEADING_ASTERISKS) return Indent.getSpaceIndent(1);
297     if (child.getPsi() instanceof PsiFile) return Indent.getNoneIndent();
298     if (parent != null) {
299       final Indent defaultChildIndent = getChildIndent(parent, indentOptions);
300       if (defaultChildIndent != null) return defaultChildIndent;
301     }
302     if (child.getTreeParent() instanceof PsiLambdaExpression && child instanceof PsiCodeBlock) {
303       return Indent.getNoneIndent();
304     }
305
306     return null;
307   }
308
309   @Nullable
310   private static Indent getChildIndent(@NotNull ASTNode parent, @NotNull CommonCodeStyleSettings.IndentOptions indentOptions) {
311     final IElementType parentType = parent.getElementType();
312     if (parentType == JavaElementType.MODIFIER_LIST) return Indent.getNoneIndent();
313     if (parentType == JspElementType.JSP_CODE_BLOCK) return Indent.getNormalIndent();
314     if (parentType == JspElementType.JSP_CLASS_LEVEL_DECLARATION_STATEMENT) return Indent.getNormalIndent();
315     if (parentType == TokenType.DUMMY_HOLDER) return Indent.getNoneIndent();
316     if (parentType == JavaElementType.CLASS) return Indent.getNoneIndent();
317     if (parentType == JavaElementType.IF_STATEMENT) return Indent.getNoneIndent();
318     if (parentType == JavaElementType.TRY_STATEMENT) return Indent.getNoneIndent();
319     if (parentType == JavaElementType.CATCH_SECTION) return Indent.getNoneIndent();
320     if (parentType == JavaElementType.FOR_STATEMENT) return Indent.getNoneIndent();
321     if (parentType == JavaElementType.FOREACH_STATEMENT) return Indent.getNoneIndent();
322     if (parentType == JavaElementType.BLOCK_STATEMENT) return Indent.getNoneIndent();
323     if (parentType == JavaElementType.DO_WHILE_STATEMENT) return Indent.getNoneIndent();
324     if (parentType == JavaElementType.WHILE_STATEMENT) return Indent.getNoneIndent();
325     if (parentType == JavaElementType.SWITCH_STATEMENT) return Indent.getNoneIndent();
326     if (parentType == JavaElementType.METHOD) return Indent.getNoneIndent();
327     if (parentType == JavaDocElementType.DOC_COMMENT) return Indent.getNoneIndent();
328     if (parentType == JavaDocElementType.DOC_TAG) return Indent.getNoneIndent();
329     if (parentType == JavaDocElementType.DOC_INLINE_TAG) return Indent.getNoneIndent();
330     if (parentType == JavaElementType.IMPORT_LIST) return Indent.getNoneIndent();
331     if (parentType == JavaElementType.FIELD) return Indent.getContinuationWithoutFirstIndent(indentOptions.USE_RELATIVE_INDENTS);
332     if (parentType == JavaElementType.EXPRESSION_STATEMENT) return Indent.getNoneIndent();
333     if (SourceTreeToPsiMap.treeElementToPsi(parent) instanceof PsiFile) {
334       return Indent.getNoneIndent();
335     }
336     return null;
337   }
338
339   protected static boolean isRBrace(@NotNull final ASTNode child) {
340     return child.getElementType() == JavaTokenType.RBRACE;
341   }
342
343   @Nullable
344   @Override
345   public Spacing getSpacing(Block child1, @NotNull Block child2) {
346     return JavaSpacePropertyProcessor.getSpacing(getTreeNode(child2), mySettings, myJavaSettings);
347   }
348
349   @Override
350   public ASTNode getFirstTreeNode() {
351     return myNode;
352   }
353
354   @Override
355   public Indent getIndent() {
356     return myIndent;
357   }
358
359   protected static boolean isStatement(final ASTNode child, @Nullable final ASTNode parentNode) {
360     if (parentNode != null) {
361       final IElementType parentType = parentNode.getElementType();
362       if (parentType == JavaElementType.CODE_BLOCK) return false;
363       final int role = ((CompositeElement)parentNode).getChildRole(child);
364       if (parentType == JavaElementType.IF_STATEMENT) return role == ChildRole.THEN_BRANCH || role == ChildRole.ELSE_BRANCH;
365       if (parentType == JavaElementType.FOR_STATEMENT) return role == ChildRole.LOOP_BODY;
366       if (parentType == JavaElementType.WHILE_STATEMENT) return role == ChildRole.LOOP_BODY;
367       if (parentType == JavaElementType.DO_WHILE_STATEMENT) return role == ChildRole.LOOP_BODY;
368       if (parentType == JavaElementType.FOREACH_STATEMENT) return role == ChildRole.LOOP_BODY;
369     }
370     return false;
371   }
372
373   @Nullable
374   protected Wrap createChildWrap() {
375     if (!isBuildInjectedBlocks()) {
376       return null; //when detecting indent we do not care about wraps
377     }
378     return myWrapManager.createChildBlockWrap(this, getSettings(), this);
379   }
380
381   @Nullable
382   protected Alignment createChildAlignment() {
383     IElementType nodeType = myNode.getElementType();
384     if (nodeType == JavaElementType.POLYADIC_EXPRESSION) nodeType = JavaElementType.BINARY_EXPRESSION;
385     if (nodeType == JavaElementType.ASSIGNMENT_EXPRESSION) {
386       if (myNode.getTreeParent() != null
387           && myNode.getTreeParent().getElementType() == JavaElementType.ASSIGNMENT_EXPRESSION
388           && myAlignment != null) {
389         return myAlignment;
390       }
391       return createAlignment(mySettings.ALIGN_MULTILINE_ASSIGNMENT, null);
392     }
393     if (nodeType == JavaElementType.PARENTH_EXPRESSION) {
394       return createAlignment(mySettings.ALIGN_MULTILINE_PARENTHESIZED_EXPRESSION, null);
395     }
396     if (nodeType == JavaElementType.CONDITIONAL_EXPRESSION) {
397       return createAlignment(mySettings.ALIGN_MULTILINE_TERNARY_OPERATION, null);
398     }
399     if (nodeType == JavaElementType.FOR_STATEMENT) {
400       return createAlignment(mySettings.ALIGN_MULTILINE_FOR, null);
401     }
402     if (nodeType == JavaElementType.EXTENDS_LIST || nodeType == JavaElementType.IMPLEMENTS_LIST) {
403       return createAlignment(mySettings.ALIGN_MULTILINE_EXTENDS_LIST, null);
404     }
405     if (nodeType == JavaElementType.THROWS_LIST) {
406       return createAlignment(mySettings.ALIGN_MULTILINE_THROWS_LIST, null);
407     }
408     if (nodeType == JavaElementType.PARAMETER_LIST) {
409       return createAlignment(mySettings.ALIGN_MULTILINE_PARAMETERS, null);
410     }
411     if (nodeType == JavaElementType.RESOURCE_LIST) {
412       return createAlignment(mySettings.ALIGN_MULTILINE_RESOURCES, null);
413     }
414     if (nodeType == JavaElementType.BINARY_EXPRESSION) {
415       Alignment defaultAlignment = null;
416       if (shouldInheritAlignment()) {
417         defaultAlignment = myAlignment;
418       }
419       return createAlignment(mySettings.ALIGN_MULTILINE_BINARY_OPERATION, defaultAlignment);
420     }
421     if (nodeType == JavaElementType.CLASS || nodeType == JavaElementType.METHOD) {
422       return null;
423     }
424     return null;
425   }
426
427   @Nullable
428   protected Alignment chooseAlignment(@Nullable Alignment alignment, @Nullable Alignment alignment2, @NotNull ASTNode child) {
429     if (isTernaryOperatorToken(child)) {
430       return alignment2;
431     }
432     return alignment;
433   }
434
435   private boolean isTernaryOperatorToken(@NotNull final ASTNode child) {
436     final IElementType nodeType = myNode.getElementType();
437
438     if (nodeType == JavaElementType.CONDITIONAL_EXPRESSION) {
439       IElementType childType = child.getElementType();
440       return childType == JavaTokenType.QUEST || childType ==JavaTokenType.COLON;
441     }
442     else {
443       return false;
444     }
445   }
446
447   private boolean shouldInheritAlignment() {
448     if (myNode instanceof PsiPolyadicExpression) {
449       final ASTNode treeParent = myNode.getTreeParent();
450       if (treeParent instanceof PsiPolyadicExpression) {
451         return JavaFormatterUtil.areSamePriorityBinaryExpressions(myNode, treeParent);
452       }
453     }
454     return false;
455   }
456
457
458   @Nullable
459   protected ASTNode processChild(@NotNull final List<Block> result,
460                                  @NotNull ASTNode child,
461                                  Alignment defaultAlignment,
462                                  final Wrap defaultWrap,
463                                  final Indent childIndent) {
464     return processChild(result, child, AlignmentStrategy.wrap(defaultAlignment), defaultWrap, childIndent, -1);
465   }
466
467   @Nullable
468   protected ASTNode processChild(@NotNull final List<Block> result,
469                                  @NotNull ASTNode child,
470                                  @NotNull AlignmentStrategy alignmentStrategy,
471                                  @Nullable final Wrap defaultWrap,
472                                  final Indent childIndent) {
473     return processChild(result, child, alignmentStrategy, defaultWrap, childIndent, -1);
474   }
475
476   @Nullable
477   protected ASTNode processChild(@NotNull final List<Block> result,
478                                  @NotNull ASTNode child,
479                                  @NotNull AlignmentStrategy alignmentStrategy,
480                                  final Wrap defaultWrap,
481                                  final Indent childIndent,
482                                  int childOffset) {
483     final IElementType childType = child.getElementType();
484     if (childType == JavaTokenType.CLASS_KEYWORD || childType == JavaTokenType.INTERFACE_KEYWORD) {
485       myIsAfterClassKeyword = true;
486     }
487     if (childType == JavaElementType.METHOD_CALL_EXPRESSION) {
488       Alignment alignment = shouldAlignChild(child) ? alignmentStrategy.getAlignment(childType) : null;
489       result.add(createMethodCallExpressionBlock(child, arrangeChildWrap(child, defaultWrap), alignment, childIndent));
490     }
491     else {
492       IElementType nodeType = myNode.getElementType();
493       if (nodeType == JavaElementType.POLYADIC_EXPRESSION) nodeType = JavaElementType.BINARY_EXPRESSION;
494
495       if (childType == JavaTokenType.LBRACE && nodeType == JavaElementType.ARRAY_INITIALIZER_EXPRESSION) {
496         ArrayInitializerBlocksBuilder builder = new ArrayInitializerBlocksBuilder(myNode, myBlockFactory);
497         List<Block> newlyCreated = builder.buildBlocks();
498
499         child = myNode.getLastChildNode();
500         result.addAll(newlyCreated);
501       }
502       else if (childType == JavaTokenType.LBRACE && nodeType == JavaElementType.ANNOTATION_ARRAY_INITIALIZER) {
503         final Wrap wrap = Wrap.createWrap(getWrapType(mySettings.ARRAY_INITIALIZER_WRAP), false);
504         child = processParenthesisBlock(JavaTokenType.LBRACE, JavaTokenType.RBRACE,
505                                   result,
506                                   child,
507                                   WrappingStrategy.createDoNotWrapCommaStrategy(wrap),
508                                   mySettings.ALIGN_MULTILINE_ARRAY_INITIALIZER_EXPRESSION);
509       }
510       else if (childType == JavaTokenType.LPARENTH && nodeType == JavaElementType.EXPRESSION_LIST) {
511         final Wrap wrap = Wrap.createWrap(getWrapType(mySettings.CALL_PARAMETERS_WRAP), false);
512         if (mySettings.PREFER_PARAMETERS_WRAP && !isInsideMethodCall(myNode.getPsi())) {
513             wrap.ignoreParentWraps();
514         }
515         child = processParenthesisBlock(result, child,
516                                   WrappingStrategy.createDoNotWrapCommaStrategy(wrap),
517                                   mySettings.ALIGN_MULTILINE_PARAMETERS_IN_CALLS);
518       }
519       else if (childType == JavaTokenType.LPARENTH && nodeType == JavaElementType.PARAMETER_LIST) {
520         ASTNode parent = myNode.getTreeParent();
521         boolean isLambdaParameterList = parent != null && parent.getElementType() == JavaElementType.LAMBDA_EXPRESSION;
522         Wrap wrapToUse = isLambdaParameterList ? null : getMethodParametersWrap();
523         WrappingStrategy wrapStrategy = WrappingStrategy.createDoNotWrapCommaStrategy(wrapToUse);
524         child = processParenthesisBlock(result, child, wrapStrategy, mySettings.ALIGN_MULTILINE_PARAMETERS);
525       }
526       else if (childType == JavaTokenType.LPARENTH && nodeType == JavaElementType.RESOURCE_LIST) {
527         Wrap wrap = Wrap.createWrap(getWrapType(mySettings.RESOURCE_LIST_WRAP), false);
528         child = processParenthesisBlock(result, child,
529                                   WrappingStrategy.createDoNotWrapCommaStrategy(wrap),
530                                   mySettings.ALIGN_MULTILINE_RESOURCES);
531       }
532       else if (childType == JavaTokenType.LPARENTH && nodeType == JavaElementType.ANNOTATION_PARAMETER_LIST) {
533         AnnotationInitializerBlocksBuilder builder = new AnnotationInitializerBlocksBuilder(myNode, myBlockFactory);
534         List<Block> newlyCreated = builder.buildBlocks();
535
536         child = myNode.getLastChildNode();
537         result.addAll(newlyCreated);
538       }
539       else if (childType == JavaTokenType.LPARENTH && nodeType == JavaElementType.PARENTH_EXPRESSION) {
540         child = processParenthesisBlock(result, child,
541                                   WrappingStrategy.DO_NOT_WRAP,
542                                   mySettings.ALIGN_MULTILINE_PARENTHESIZED_EXPRESSION);
543       }
544       else if (childType == JavaElementType.ENUM_CONSTANT && myNode instanceof ClassElement) {
545         child = processEnumBlock(result, child, ((ClassElement)myNode).findEnumConstantListDelimiterPlace());
546       }
547       else if (mySettings.TERNARY_OPERATION_SIGNS_ON_NEXT_LINE && isTernaryOperationSign(child)) {
548         child = processTernaryOperationRange(result, child, defaultWrap, childIndent);
549       }
550       else if (childType == JavaElementType.FIELD) {
551         child = processField(result, child, alignmentStrategy, defaultWrap, childIndent);
552       }
553       else if (childType == JavaElementType.LOCAL_VARIABLE
554                || childType == JavaElementType.DECLARATION_STATEMENT
555                   && (nodeType == JavaElementType.METHOD || nodeType == JavaElementType.CODE_BLOCK))
556       {
557         result.add(new SimpleJavaBlock(child, defaultWrap, alignmentStrategy, childIndent, mySettings, myJavaSettings));
558       }
559       else {
560         Alignment alignment = alignmentStrategy.getAlignment(childType);
561         AlignmentStrategy alignmentStrategyToUse = shouldAlignChild(child)
562                                                    ? AlignmentStrategy.wrap(alignment)
563                                                    : AlignmentStrategy.getNullStrategy();
564
565         if (myAlignmentStrategy.getAlignment(nodeType, childType) != null &&
566             (nodeType == JavaElementType.IMPLEMENTS_LIST || nodeType == JavaElementType.CLASS)) {
567           alignmentStrategyToUse = myAlignmentStrategy;
568         }
569
570         Wrap wrap = arrangeChildWrap(child, defaultWrap);
571
572         Block block = createJavaBlock(child, mySettings, myJavaSettings, childIndent, wrap, alignmentStrategyToUse, childOffset);
573
574         if (block instanceof AbstractJavaBlock) {
575           final AbstractJavaBlock javaBlock = (AbstractJavaBlock)block;
576           if (nodeType == JavaElementType.METHOD_CALL_EXPRESSION && childType == JavaElementType.REFERENCE_EXPRESSION ||
577               nodeType == JavaElementType.REFERENCE_EXPRESSION && childType == JavaElementType.METHOD_CALL_EXPRESSION) {
578             javaBlock.setReservedWrap(getReservedWrap(nodeType), nodeType);
579             javaBlock.setReservedWrap(getReservedWrap(childType), childType);
580           }
581           else if (nodeType == JavaElementType.BINARY_EXPRESSION) {
582             javaBlock.setReservedWrap(defaultWrap, nodeType);
583           }
584         }
585
586         result.add(block);
587       }
588     }
589
590     return child;
591   }
592
593   private boolean isInsideMethodCall(@NotNull PsiElement element) {
594     PsiElement e = element.getParent();
595     int parentsVisited = 0;
596     while (e != null && !(e instanceof PsiStatement) && parentsVisited < 5) {
597       if (e instanceof PsiExpressionList) {
598         return true;
599       }
600       e = e.getParent();
601       parentsVisited++;
602     }
603     return false;
604   }
605
606   @NotNull
607   private Wrap getMethodParametersWrap() {
608     Wrap preferredWrap = getModifierListWrap();
609     if (preferredWrap == null) {
610       return Wrap.createWrap(getWrapType(mySettings.METHOD_PARAMETERS_WRAP), false);
611     } else {
612       return Wrap.createChildWrap(preferredWrap, getWrapType(mySettings.METHOD_PARAMETERS_WRAP), false);
613     }
614   }
615
616   @Nullable
617   private Wrap getModifierListWrap() {
618     AbstractJavaBlock parentBlock = getParentBlock();
619     if (parentBlock != null) {
620       return parentBlock.getReservedWrap(JavaElementType.MODIFIER_LIST);
621     }
622     return null;
623   }
624
625   private ASTNode processField(@NotNull final List<Block> result,
626                                ASTNode child,
627                                @NotNull final AlignmentStrategy alignmentStrategy,
628                                final Wrap defaultWrap,
629                                final Indent childIndent) {
630     ASTNode lastFieldInGroup = findLastFieldInGroup(child);
631     if (lastFieldInGroup == child) {
632       result.add(createJavaBlock(child, getSettings(), myJavaSettings, childIndent, arrangeChildWrap(child, defaultWrap), alignmentStrategy));
633       return child;
634     }
635     else {
636       final ArrayList<Block> localResult = new ArrayList<Block>();
637       while (child != null) {
638         if (!FormatterUtil.containsWhiteSpacesOnly(child)) {
639           localResult.add(createJavaBlock(
640               child, getSettings(), myJavaSettings,
641               Indent.getContinuationWithoutFirstIndent(myIndentSettings.USE_RELATIVE_INDENTS),
642               arrangeChildWrap(child, defaultWrap),
643               alignmentStrategy
644             )
645           );
646         }
647         if (child == lastFieldInGroup) break;
648
649         child = child.getTreeNext();
650       }
651       if (!localResult.isEmpty()) {
652         result.add(new SyntheticCodeBlock(localResult, null, getSettings(), myJavaSettings, childIndent, null));
653       }
654       return lastFieldInGroup;
655     }
656   }
657
658   @Nullable
659   private ASTNode processTernaryOperationRange(@NotNull final List<Block> result,
660                                                @NotNull final ASTNode child,
661                                                final Wrap defaultWrap,
662                                                final Indent childIndent) {
663     final ArrayList<Block> localResult = new ArrayList<Block>();
664     final Wrap wrap = arrangeChildWrap(child, defaultWrap);
665     final Alignment alignment = myReservedAlignment;
666     final Alignment alignment2 = myReservedAlignment2;
667     localResult.add(new LeafBlock(child, wrap, chooseAlignment(alignment,  alignment2, child), childIndent));
668
669     ASTNode current = child.getTreeNext();
670     while (current != null) {
671       if (!FormatterUtil.containsWhiteSpacesOnly(current) && current.getTextLength() > 0) {
672         if (isTernaryOperationSign(current)) break;
673         current = processChild(localResult, current, chooseAlignment(alignment,  alignment2, current), defaultWrap, childIndent);
674       }
675       if (current != null) {
676         current = current.getTreeNext();
677       }
678     }
679
680     result.add(new SyntheticCodeBlock(localResult,  chooseAlignment(alignment,  alignment2, child), getSettings(), myJavaSettings, null, wrap));
681
682     if (current == null) {
683       return null;
684     }
685     return current.getTreePrev();
686   }
687
688   private boolean isTernaryOperationSign(@NotNull final ASTNode child) {
689     if (myNode.getElementType() != JavaElementType.CONDITIONAL_EXPRESSION) return false;
690     final int role = ((CompositeElement)child.getTreeParent()).getChildRole(child);
691     return role == ChildRole.OPERATION_SIGN || role == ChildRole.COLON;
692   }
693
694   @NotNull
695   private Block createMethodCallExpressionBlock(@NotNull ASTNode node, Wrap blockWrap, Alignment alignment, Indent indent) {
696     final ArrayList<ASTNode> nodes = new ArrayList<ASTNode>();
697     collectNodes(nodes, node);
698     return new ChainMethodCallsBlockBuilder(alignment, blockWrap, indent, mySettings, myJavaSettings).build(nodes);
699   }
700
701   private static void collectNodes(@NotNull List<ASTNode> nodes, @NotNull ASTNode node) {
702     ASTNode child = node.getFirstChildNode();
703     while (child != null) {
704       if (!FormatterUtil.containsWhiteSpacesOnly(child)) {
705         if (child.getElementType() == JavaElementType.METHOD_CALL_EXPRESSION || child.getElementType() ==
706                                                                                 JavaElementType
707                                                                                   .REFERENCE_EXPRESSION) {
708           collectNodes(nodes, child);
709         }
710         else {
711           nodes.add(child);
712         }
713       }
714       child = child.getTreeNext();
715     }
716   }
717
718   private boolean shouldAlignChild(@NotNull final ASTNode child) {
719     int role = getChildRole(child);
720     final IElementType nodeType = myNode.getElementType();
721
722     if (nodeType == JavaElementType.FOR_STATEMENT) {
723       if (role == ChildRole.FOR_INITIALIZATION || role == ChildRole.CONDITION || role == ChildRole.FOR_UPDATE) {
724         return true;
725       }
726       return false;
727     }
728     else if (nodeType == JavaElementType.EXTENDS_LIST || nodeType == JavaElementType.IMPLEMENTS_LIST) {
729       if (role == ChildRole.REFERENCE_IN_LIST || role == ChildRole.IMPLEMENTS_KEYWORD) {
730         return true;
731       }
732       return false;
733     }
734     else if (nodeType == JavaElementType.THROWS_LIST) {
735       if (role == ChildRole.REFERENCE_IN_LIST) {
736         return true;
737       }
738       return false;
739     }
740     else if (nodeType == JavaElementType.CLASS) {
741       if (role == ChildRole.CLASS_OR_INTERFACE_KEYWORD) return true;
742       if (myIsAfterClassKeyword) return false;
743       if (role == ChildRole.MODIFIER_LIST) return true;
744       return false;
745     }
746     else if (JavaElementType.FIELD == nodeType) {
747       return shouldAlignFieldInColumns(child);
748     }
749     else if (nodeType == JavaElementType.METHOD) {
750       if (role == ChildRole.MODIFIER_LIST) return true;
751       if (role == ChildRole.TYPE_PARAMETER_LIST) return true;
752       if (role == ChildRole.TYPE) return true;
753       if (role == ChildRole.NAME) return true;
754       if (role == ChildRole.THROWS_LIST && mySettings.ALIGN_THROWS_KEYWORD) return true;
755       return false;
756     }
757
758     else if (nodeType == JavaElementType.ASSIGNMENT_EXPRESSION) {
759       if (role == ChildRole.LOPERAND) return true;
760       if (role == ChildRole.ROPERAND && child.getElementType() == JavaElementType.ASSIGNMENT_EXPRESSION) {
761         return true;
762       }
763       return false;
764     }
765
766     else if (child.getElementType() == JavaTokenType.END_OF_LINE_COMMENT) {
767       ASTNode previous = child.getTreePrev();
768       // There is a special case - comment block that is located at the very start of the line. We don't reformat such a blocks,
769       // hence, no alignment should be applied to them in order to avoid subsequent blocks aligned with the same alignment to
770       // be located at the left editor edge as well.
771       CharSequence prevChars;
772       if (previous != null && previous.getElementType() == TokenType.WHITE_SPACE && (prevChars = previous.getChars()).length() > 0
773           && prevChars.charAt(prevChars.length() - 1) == '\n') {
774         return false;
775       }
776       return true;
777     }
778
779     else if (nodeType == JavaElementType.MODIFIER_LIST) {
780       // There is a possible case that modifier list contains from more than one elements, e.g. 'private final'. It's also possible
781       // that the list is aligned. We want to apply alignment rule only to the first element then.
782       ASTNode previous = child.getTreePrev();
783       if (previous == null || previous.getTreeParent() != myNode) {
784         return true;
785       }
786       return false;
787     }
788
789     else {
790       return true;
791     }
792   }
793
794   private static int getChildRole(@NotNull ASTNode child) {
795     return ((CompositeElement)child.getTreeParent()).getChildRole(child);
796   }
797
798   /**
799    * Encapsulates alignment retrieval logic for variable declaration use-case assuming that given node is a child node
800    * of basic variable declaration node.
801    *
802    * @param child   variable declaration child node which alignment is to be defined
803    * @return        alignment to use for the given node
804    * @see CodeStyleSettings#ALIGN_GROUP_FIELD_DECLARATIONS
805    */
806   @Nullable
807   private boolean shouldAlignFieldInColumns(@NotNull ASTNode child) {
808     // The whole idea of variable declarations alignment is that complete declaration blocks which children are to be aligned hold
809     // reference to the same AlignmentStrategy object, hence, reuse the same Alignment objects. So, there is no point in checking
810     // if it's necessary to align sub-blocks if shared strategy is not defined.
811     if (!mySettings.ALIGN_GROUP_FIELD_DECLARATIONS) {
812       return false;
813     }
814
815     IElementType childType = child.getElementType();
816
817     // We don't want to align subsequent identifiers in single-line declarations like 'int i1, i2, i3'. I.e. only 'i1'
818     // should be aligned then.
819     ASTNode previousNode = FormatterUtil.getPreviousNonWhitespaceSibling(child);
820     if (childType == JavaTokenType.IDENTIFIER && (previousNode == null || previousNode.getElementType() == JavaTokenType.COMMA)) {
821       return false;
822     }
823
824     return true;
825   }
826
827   @Nullable
828   public static Alignment createAlignment(final boolean alignOption, @Nullable final Alignment defaultAlignment) {
829     return alignOption ? createAlignmentOrDefault(null, defaultAlignment) : defaultAlignment;
830   }
831
832   @Nullable
833   public static Alignment createAlignment(Alignment base, final boolean alignOption, @Nullable final Alignment defaultAlignment) {
834     return alignOption ? createAlignmentOrDefault(base, defaultAlignment) : defaultAlignment;
835   }
836
837   @Nullable
838   protected Wrap arrangeChildWrap(final ASTNode child, Wrap defaultWrap) {
839     if (!isBuildInjectedBlocks()) {
840       return null; //when detecting indent we do not care about wraps
841     }
842     return myWrapManager.arrangeChildWrap(child, myNode, mySettings, myJavaSettings, defaultWrap, this);
843   }
844
845   @NotNull
846   private ASTNode processParenthesisBlock(@NotNull List<Block> result,
847                                           @NotNull ASTNode child,
848                                           @NotNull WrappingStrategy wrappingStrategy,
849                                           final boolean doAlign) {
850     myUseChildAttributes = true;
851
852     final IElementType from = JavaTokenType.LPARENTH;
853     final IElementType to = JavaTokenType.RPARENTH;
854
855     return processParenthesisBlock(from, to, result, child, wrappingStrategy, doAlign);
856   }
857
858   @NotNull
859   private ASTNode processParenthesisBlock(@NotNull IElementType from,
860                                           @Nullable final IElementType to,
861                                           @NotNull final List<Block> result,
862                                           @NotNull ASTNode child,
863                                           @NotNull final WrappingStrategy wrappingStrategy,
864                                           final boolean doAlign)
865   {
866     Indent externalIndent = Indent.getNoneIndent();
867     Indent internalIndent = Indent.getContinuationWithoutFirstIndent(myIndentSettings.USE_RELATIVE_INDENTS);
868
869     if (isInsideMethodCallParenthesis(child)) {
870       internalIndent = Indent.getSmartIndent(Indent.Type.CONTINUATION);
871     }
872
873     AlignmentStrategy alignmentStrategy = AlignmentStrategy.wrap(createAlignment(doAlign, null), JavaTokenType.COMMA);
874     setChildIndent(internalIndent);
875     setChildAlignment(alignmentStrategy.getAlignment(null));
876     boolean methodParametersBlock = true;
877     ASTNode lBracketParent = child.getTreeParent();
878     if (lBracketParent != null) {
879       ASTNode methodCandidate = lBracketParent.getTreeParent();
880       methodParametersBlock = methodCandidate != null && (methodCandidate.getElementType() == JavaElementType.METHOD
881                                                           || methodCandidate.getElementType() == JavaElementType.METHOD_CALL_EXPRESSION);
882     }
883     Alignment bracketAlignment = methodParametersBlock && mySettings.ALIGN_MULTILINE_METHOD_BRACKETS ? Alignment.createAlignment() : null;
884     AlignmentStrategy anonymousClassStrategy = doAlign ? alignmentStrategy
885                                                        : AlignmentStrategy.wrap(Alignment.createAlignment(),
886                                                                                 false,
887                                                                                 JavaTokenType.NEW_KEYWORD,
888                                                                                 JavaElementType.NEW_EXPRESSION,
889                                                                                 JavaTokenType.RBRACE);
890     setChildIndent(internalIndent);
891     setChildAlignment(alignmentStrategy.getAlignment(null));
892
893     boolean isAfterIncomplete = false;
894
895     ASTNode prev = child;
896     boolean afterAnonymousClass = false;
897     while (child != null) {
898       isAfterIncomplete = isAfterIncomplete || child.getElementType() == TokenType.ERROR_ELEMENT ||
899                           child.getElementType() == JavaElementType.EMPTY_EXPRESSION;
900       if (!FormatterUtil.containsWhiteSpacesOnly(child) && child.getTextLength() > 0) {
901         if (child.getElementType() == from) {
902           result.add(createJavaBlock(child, mySettings, myJavaSettings, externalIndent, null, bracketAlignment));
903         }
904         else if (child.getElementType() == to) {
905           Block block = createJavaBlock(child, mySettings, myJavaSettings,
906                                         isAfterIncomplete && !afterAnonymousClass ? internalIndent : externalIndent,
907                                         null,
908                                         isAfterIncomplete ? alignmentStrategy.getAlignment(null) : bracketAlignment);
909           result.add(block);
910           if (internalIndent instanceof ExpandableIndent && to == JavaTokenType.RPARENTH) {
911             ((ExpandableIndent)internalIndent).setStrictMinOffsetBlock(block);
912           }
913           return child;
914         }
915         else {
916           final IElementType elementType = child.getElementType();
917           AlignmentStrategy alignmentStrategyToUse = canUseAnonymousClassAlignment(child) ? anonymousClassStrategy : alignmentStrategy;
918           processChild(result, child, alignmentStrategyToUse.getAlignment(elementType), wrappingStrategy.getWrap(elementType), internalIndent);
919           if (to == null) {//process only one statement
920             return child;
921           }
922         }
923         isAfterIncomplete = false;
924         if (child.getElementType() != JavaTokenType.COMMA) {
925           afterAnonymousClass = isAnonymousClass(child);
926         }
927       }
928       prev = child;
929       child = child.getTreeNext();
930     }
931
932     return prev;
933   }
934
935   private boolean isInsideMethodCallParenthesis(ASTNode child) {
936     ASTNode currentPredecessor = child.getTreeParent();
937     if (currentPredecessor != null) {
938       currentPredecessor = currentPredecessor.getTreeParent();
939       return currentPredecessor != null && currentPredecessor.getElementType() == JavaElementType.METHOD_CALL_EXPRESSION;
940     }
941     return false;
942   }
943
944   private static boolean canUseAnonymousClassAlignment(@NotNull ASTNode child) {
945     // The general idea is to handle situations like below:
946     //     test(new Runnable() {
947     //              public void run() {
948     //              }
949     //          }, new Runnable() {
950     //              public void run() {
951     //              }
952     //          }
953     //     );
954     // I.e. we want to align subsequent anonymous class argument to the previous one if it's not preceded by another argument
955     // at the same line, e.g.:
956     //      test("this is a long argument", new Runnable() {
957     //              public void run() {
958     //              }
959     //          }, new Runnable() {
960     //              public void run() {
961     //              }
962     //          }
963     //     );
964     if (!isAnonymousClass(child)) {
965       return false;
966     }
967
968     for (ASTNode node = child.getTreePrev(); node != null; node = node.getTreePrev()) {
969       if (node.getElementType() == TokenType.WHITE_SPACE) {
970         if (StringUtil.countNewLines(node.getChars()) > 0) {
971           return false;
972         }
973       }
974       else if (node.getElementType() == JavaTokenType.LPARENTH) {
975         // First method call argument.
976         return true;
977       }
978       else if (node.getElementType() != JavaTokenType.COMMA && !isAnonymousClass(node)) {
979         return false;
980       }
981     }
982     return true;
983   }
984
985   private static boolean isAnonymousClass(@Nullable ASTNode node) {
986     if (node == null || node.getElementType() != JavaElementType.NEW_EXPRESSION) {
987       return false;
988     }
989     ASTNode lastChild = node.getLastChildNode();
990     return lastChild != null && lastChild.getElementType() == JavaElementType.ANONYMOUS_CLASS;
991   }
992
993   @Nullable
994   private ASTNode processEnumBlock(@NotNull List<Block> result,
995                                    @Nullable ASTNode child,
996                                    ASTNode last)
997   {
998     final WrappingStrategy wrappingStrategy = WrappingStrategy.createDoNotWrapCommaStrategy(Wrap
999       .createWrap(getWrapType(mySettings.ENUM_CONSTANTS_WRAP), true));
1000     while (child != null) {
1001       if (!FormatterUtil.containsWhiteSpacesOnly(child) && child.getTextLength() > 0) {
1002         result.add(createJavaBlock(child, mySettings, myJavaSettings, Indent.getNormalIndent(),
1003                                    wrappingStrategy.getWrap(child.getElementType()), AlignmentStrategy.getNullStrategy()));
1004         if (child == last) return child;
1005       }
1006       child = child.getTreeNext();
1007     }
1008     return null;
1009   }
1010
1011   private void setChildAlignment(final Alignment alignment) {
1012     myChildAlignment = alignment;
1013   }
1014
1015   private void setChildIndent(final Indent internalIndent) {
1016     myChildIndent = internalIndent;
1017   }
1018
1019   @Nullable
1020   private static Alignment createAlignmentOrDefault(@Nullable Alignment base, @Nullable final Alignment defaultAlignment) {
1021     if (defaultAlignment == null) {
1022       return base == null ? Alignment.createAlignment() : Alignment.createChildAlignment(base);
1023     }
1024     return defaultAlignment;
1025   }
1026
1027   private int getBraceStyle() {
1028     final PsiElement psiNode = SourceTreeToPsiMap.treeElementToPsi(myNode);
1029     if (psiNode instanceof PsiClass) {
1030       return mySettings.CLASS_BRACE_STYLE;
1031     }
1032     if (psiNode instanceof PsiMethod
1033              || psiNode instanceof PsiCodeBlock && psiNode.getParent() != null && psiNode.getParent() instanceof PsiMethod) {
1034       return mySettings.METHOD_BRACE_STYLE;
1035     }
1036     return mySettings.BRACE_STYLE;
1037   }
1038
1039   protected Indent getCodeBlockInternalIndent(final int baseChildrenIndent) {
1040     return getCodeBlockInternalIndent(baseChildrenIndent, false);
1041   }
1042
1043   protected Indent getCodeBlockInternalIndent(final int baseChildrenIndent, boolean enforceParentIndent) {
1044     if (isTopLevelClass() && mySettings.DO_NOT_INDENT_TOP_LEVEL_CLASS_MEMBERS) {
1045       return Indent.getNoneIndent();
1046     }
1047
1048     final int braceStyle = getBraceStyle();
1049     return braceStyle == CommonCodeStyleSettings.NEXT_LINE_SHIFTED ?
1050            createNormalIndent(baseChildrenIndent - 1, enforceParentIndent)
1051            : createNormalIndent(baseChildrenIndent, enforceParentIndent);
1052   }
1053
1054   protected static Indent createNormalIndent(final int baseChildrenIndent) {
1055     return createNormalIndent(baseChildrenIndent, false);
1056   }
1057
1058   protected static Indent createNormalIndent(final int baseChildrenIndent, boolean enforceIndentToChildren) {
1059     if (baseChildrenIndent == 1) {
1060       return Indent.getIndent(Indent.Type.NORMAL, false, enforceIndentToChildren);
1061     }
1062     else if (baseChildrenIndent <= 0) {
1063       return Indent.getNoneIndent();
1064     }
1065     else {
1066       LOG.assertTrue(false);
1067       return Indent.getIndent(Indent.Type.NORMAL, false, enforceIndentToChildren);
1068     }
1069   }
1070
1071   private boolean isTopLevelClass() {
1072     return myNode.getElementType() == JavaElementType.CLASS &&
1073            SourceTreeToPsiMap.treeElementToPsi(myNode.getTreeParent()) instanceof PsiFile;
1074   }
1075
1076   protected Indent getCodeBlockExternalIndent() {
1077     final int braceStyle = getBraceStyle();
1078     if (braceStyle == CommonCodeStyleSettings.END_OF_LINE || braceStyle == CommonCodeStyleSettings.NEXT_LINE ||
1079         braceStyle == CommonCodeStyleSettings.NEXT_LINE_IF_WRAPPED) {
1080       return Indent.getNoneIndent();
1081     }
1082     return Indent.getNormalIndent();
1083   }
1084
1085   protected Indent getCodeBlockChildExternalIndent(final int newChildIndex) {
1086     final int braceStyle = getBraceStyle();
1087     if (!isAfterCodeBlock(newChildIndex)) {
1088       return Indent.getNormalIndent();
1089     }
1090     if (braceStyle == CommonCodeStyleSettings.NEXT_LINE ||
1091         braceStyle == CommonCodeStyleSettings.NEXT_LINE_IF_WRAPPED ||
1092         braceStyle == CommonCodeStyleSettings.END_OF_LINE) {
1093       return Indent.getNoneIndent();
1094     }
1095     return Indent.getNormalIndent();
1096   }
1097
1098   private boolean isAfterCodeBlock(final int newChildIndex) {
1099     if (newChildIndex == 0) return false;
1100     Block blockBefore = getSubBlocks().get(newChildIndex - 1);
1101     return blockBefore instanceof CodeBlockBlock;
1102   }
1103
1104   /**
1105    * <b>Note:</b> this method is considered to be a legacy heritage and is assumed to be removed as soon as formatting processing
1106    * is refactored
1107    *
1108    * @param elementType   target element type
1109    * @return              <code>null</code> all the time
1110    */
1111   @Nullable
1112   @Override
1113   public Wrap getReservedWrap(IElementType elementType) {
1114     return myPreferredWraps != null ? myPreferredWraps.get(elementType) : null;
1115   }
1116
1117   /**
1118    * Defines contract for associating operation type and particular wrap instance. I.e. given wrap object <b>may</b> be returned
1119    * from subsequent {@link #getReservedWrap(IElementType)} call if given operation type is used as an argument there.
1120    * <p/>
1121    * Default implementation ({@link AbstractJavaBlock#setReservedWrap(Wrap, IElementType)}) does nothing.
1122    * <p/>
1123    * <b>Note:</b> this method is considered to be a legacy heritage and is assumed to be removed as soon as formatting processing
1124    * is refactored
1125    *
1126    * @param reservedWrap    reserved wrap instance
1127    * @param operationType   target operation type to associate with the given wrap instance
1128    */
1129   public void setReservedWrap(final Wrap reservedWrap, final IElementType operationType) {
1130     if (myPreferredWraps == null) {
1131       myPreferredWraps = ContainerUtil.newHashMap();
1132     }
1133     myPreferredWraps.put(operationType, reservedWrap);
1134   }
1135
1136   @Nullable
1137   protected static ASTNode getTreeNode(final Block child2) {
1138     if (child2 instanceof JavaBlock) {
1139       return ((JavaBlock)child2).getFirstTreeNode();
1140     }
1141     if (child2 instanceof LeafBlock) {
1142       return ((LeafBlock)child2).getTreeNode();
1143     }
1144     return null;
1145   }
1146
1147   @Override
1148   @NotNull
1149   public ChildAttributes getChildAttributes(final int newChildIndex) {
1150     if (myUseChildAttributes) {
1151       return new ChildAttributes(myChildIndent, myChildAlignment);
1152     }
1153     if (isAfter(newChildIndex, new IElementType[]{JavaDocElementType.DOC_COMMENT})) {
1154       return new ChildAttributes(Indent.getNoneIndent(), myChildAlignment);
1155     }
1156     return super.getChildAttributes(newChildIndex);
1157   }
1158
1159   @Override
1160   @Nullable
1161   protected Indent getChildIndent() {
1162     return getChildIndent(myNode, myIndentSettings);
1163   }
1164
1165   @NotNull
1166   public CommonCodeStyleSettings getSettings() {
1167     return mySettings;
1168   }
1169
1170   protected boolean isAfter(final int newChildIndex, @NotNull final IElementType[] elementTypes) {
1171     if (newChildIndex == 0) return false;
1172     final Block previousBlock = getSubBlocks().get(newChildIndex - 1);
1173     if (!(previousBlock instanceof AbstractBlock)) return false;
1174     final IElementType previousElementType = ((AbstractBlock)previousBlock).getNode().getElementType();
1175     for (IElementType elementType : elementTypes) {
1176       if (previousElementType == elementType) return true;
1177     }
1178     return false;
1179   }
1180
1181   @Nullable
1182   protected Alignment getUsedAlignment(final int newChildIndex) {
1183     final List<Block> subBlocks = getSubBlocks();
1184     for (int i = 0; i < newChildIndex; i++) {
1185       if (i >= subBlocks.size()) return null;
1186       final Block block = subBlocks.get(i);
1187       final Alignment alignment = block.getAlignment();
1188       if (alignment != null) return alignment;
1189     }
1190     return null;
1191   }
1192
1193   @Override
1194   public boolean isLeaf() {
1195     return ShiftIndentInsideHelper.mayShiftIndentInside(myNode);
1196   }
1197
1198   @Nullable
1199   protected ASTNode composeCodeBlock(@NotNull final List<Block> result,
1200                                      ASTNode child,
1201                                      final Indent indent,
1202                                      final int childrenIndent,
1203                                      @Nullable final Wrap childWrap) {
1204     final ArrayList<Block> localResult = new ArrayList<Block>();
1205     processChild(localResult, child, AlignmentStrategy.getNullStrategy(), null, Indent.getNoneIndent());
1206     child = child.getTreeNext();
1207
1208     ChildAlignmentStrategyProvider alignmentStrategyProvider = getStrategyProvider();
1209
1210     while (child != null) {
1211       if (FormatterUtil.containsWhiteSpacesOnly(child)) {
1212         child = child.getTreeNext();
1213         continue;
1214       }
1215
1216       Indent childIndent = getIndentForCodeBlock(child, childrenIndent);
1217       AlignmentStrategy alignmentStrategyToUse = alignmentStrategyProvider.getNextChildStrategy(child);
1218
1219       final boolean isRBrace = isRBrace(child);
1220       child = processChild(localResult, child, alignmentStrategyToUse, childWrap, childIndent);
1221
1222       if (isRBrace) {
1223         result.add(createCodeBlockBlock(localResult, indent, childrenIndent));
1224         return child;
1225       }
1226
1227       if (child != null) {
1228         child = child.getTreeNext();
1229       }
1230     }
1231     result.add(createCodeBlockBlock(localResult, indent, childrenIndent));
1232     return null;
1233   }
1234
1235   protected ChildAlignmentStrategyProvider getStrategyProvider() {
1236     if (mySettings.ALIGN_GROUP_FIELD_DECLARATIONS && myNode.getElementType() == JavaElementType.CLASS) {
1237       return new SubsequentFieldAligner(mySettings);
1238     }
1239
1240     ASTNode parent = myNode.getTreeParent();
1241     IElementType parentType = parent != null ? parent.getElementType() : null;
1242     if (mySettings.ALIGN_CONSECUTIVE_VARIABLE_DECLARATIONS
1243         && (parentType == JavaElementType.METHOD || myNode instanceof PsiCodeBlock)) {
1244       return new SubsequentVariablesAligner();
1245     }
1246
1247     return ChildAlignmentStrategyProvider.NULL_STRATEGY_PROVIDER;
1248   }
1249
1250   private Indent getIndentForCodeBlock(ASTNode child, int childrenIndent) {
1251     if (child.getElementType() == JavaElementType.CODE_BLOCK
1252         && (getBraceStyle() == CommonCodeStyleSettings.NEXT_LINE_SHIFTED
1253             || getBraceStyle() == CommonCodeStyleSettings.NEXT_LINE_SHIFTED2))
1254     {
1255       return Indent.getNormalIndent();
1256     }
1257
1258     return isRBrace(child) ? Indent.getNoneIndent() : getCodeBlockInternalIndent(childrenIndent, false);
1259   }
1260
1261   public AbstractJavaBlock getParentBlock() {
1262     return myParentBlock;
1263   }
1264
1265   public void setParentBlock(@NotNull AbstractJavaBlock parentBlock) {
1266     myParentBlock = parentBlock;
1267   }
1268
1269   @NotNull
1270   public SyntheticCodeBlock createCodeBlockBlock(final List<Block> localResult, final Indent indent, final int childrenIndent) {
1271     final SyntheticCodeBlock result = new SyntheticCodeBlock(localResult, null, getSettings(), myJavaSettings, indent, null);
1272     result.setChildAttributes(new ChildAttributes(getCodeBlockInternalIndent(childrenIndent), null));
1273     return result;
1274   }
1275
1276
1277 }