3030import com .sun .source .tree .BinaryTree ;
3131import com .sun .source .tree .LiteralTree ;
3232import com .sun .source .tree .MemberSelectTree ;
33+ import com .sun .source .tree .MethodInvocationTree ;
3334import com .sun .source .tree .Tree ;
3435import com .sun .source .tree .Tree .Kind ;
3536import com .sun .source .util .TreePath ;
4344import java .util .ArrayDeque ;
4445import java .util .ArrayList ;
4546import java .util .Deque ;
47+ import java .util .HashMap ;
4648import java .util .Iterator ;
4749import java .util .List ;
4850import java .util .Map ;
49- import java .util .Optional ;
5051import java .util .concurrent .atomic .AtomicBoolean ;
52+ import java .util .stream .Collectors ;
5153
5254/** Wraps string literals that exceed the column limit. */
5355public final class StringWrapper {
@@ -193,6 +195,9 @@ public Void visitLiteral(LiteralTree literalTree, Void aVoid) {
193195 }
194196
195197 private void indentTextBlocks (TreeRangeMap <Integer , String > replacements , List <TreePath > textBlocks ) {
198+ // pjf specific - compute textBlock's parents & store info about the indentation\
199+ Map <TreePath , String > textBlockToIndent = computeCustomTextBlocksIndent (textBlocks );
200+
196201 for (TreePath treePath : textBlocks ) {
197202 Tree tree = treePath .getLeaf ();
198203 int startPosition = getStartPosition (tree );
@@ -212,8 +217,9 @@ private void indentTextBlocks(TreeRangeMap<Integer, String> replacements, List<T
212217 .stripIndent ();
213218 ImmutableList <String > lines = stripped .lines ().collect (ImmutableList .toImmutableList ());
214219
215- String prefix = maybeComputeCustomPrefix (treePath )
216- .orElseGet (() -> (lineStartPosition + startColumn + 4 > startPosition ? "" : " " .repeat (4 ))
220+ String prefix = textBlockToIndent .getOrDefault (
221+ treePath ,
222+ (lineStartPosition + startColumn + 4 > startPosition ? "" : " " .repeat (4 ))
217223 + " " .repeat (startColumn ));
218224 StringBuilder output = new StringBuilder (initialLines .get (0 ));
219225 for (int i = 0 ; i < lines .size (); i ++) {
@@ -244,39 +250,42 @@ private void indentTextBlocks(TreeRangeMap<Integer, String> replacements, List<T
244250 }
245251 }
246252
247- private Optional <String > maybeComputeCustomPrefix (TreePath treePath ) {
248- if (treePath .getParentPath ().getLeaf ().getKind () == Kind .PLUS ) {
249- return Optional .of (computePrefixForForTreeKind (treePath ));
250- } else if (treePath .getParentPath ().getLeaf ().getKind () == Kind .METHOD_INVOCATION ) {
251- return Optional .of (computePrefixForForMethod (treePath ));
252- }
253- return Optional .empty ();
254- }
255-
256- private String computePrefixForForMethod (TreePath path ) {
257- List <Tree > allArguments =
258- new ArrayList <>(((JCMethodInvocation ) path .getParentPath ().getLeaf ()).getArguments ());
259- return computePrefixIndentation (path .getParentPath ().getLeaf (), allArguments );
260- }
261-
262- private String computePrefixForForTreeKind (TreePath path ) {
263- while (path .getParentPath ().getLeaf ().getKind () == Kind .PLUS ) {
264- path = path .getParentPath ();
265- }
266- ArrayDeque <Tree > todo = new ArrayDeque <>();
267- todo .add (path .getLeaf ());
268- List <Tree > arguments = new ArrayList <>();
269- while (!todo .isEmpty ()) {
270- Tree first = todo .removeFirst ();
271- if (first .getKind () == Tree .Kind .PLUS ) {
272- BinaryTree bt = (BinaryTree ) first ;
273- todo .addFirst (bt .getRightOperand ());
274- todo .addFirst (bt .getLeftOperand ());
275- } else {
276- arguments .add (first );
253+ /**
254+ * Pjf specific: When the AST needs to break a concantenation expression ('ONE'+'TWO') or the parameters of a method,
255+ * the indentation will be set to 8, in which case we need to align the text blocks to the correct indentation.
256+ * In order to compute the indentation value we need to do:
257+ * 1. for each textBlock find the enclosing block/parent (only for concat expressions/method invocations)
258+ * 2. for each parent, find the arguments/concatenated expressions and find the max indentation level
259+ * 3. store the mapping between the textBlock and the indentation level computed before.
260+ */
261+ private Map <TreePath , String > computeCustomTextBlocksIndent (List <TreePath > textBlocks ) {
262+ Map <Tree , String > parentToIndent = new HashMap <>();
263+ Map <TreePath , Tree > textBlockToParent = new HashMap <>();
264+ for (TreePath textBlock : textBlocks ) {
265+ TreePath parentPath = textBlock .getParentPath ();
266+ Tree parent = parentPath .getLeaf ();
267+ if (parent instanceof MethodInvocationTree ) {
268+ textBlockToParent .put (textBlock , parent );
269+ if (parentToIndent .containsKey (parent )) {
270+ continue ;
271+ }
272+ List <Tree > allArguments = new ArrayList <>(((JCMethodInvocation ) parent ).getArguments ());
273+ parentToIndent .put (parent , computePrefixIndentation (parent , allArguments ));
274+ } else if (parent .getKind () == Kind .PLUS ) {
275+ while (parentPath .getParentPath ().getLeaf ().getKind () == Kind .PLUS ) {
276+ parentPath = parentPath .getParentPath ();
277+ }
278+ parent = parentPath .getLeaf ();
279+ textBlockToParent .put (textBlock , parent );
280+ if (parentToIndent .containsKey (parent )) {
281+ continue ;
282+ }
283+ parentToIndent .put (parent , computePrefixIndentation (parent , flattenExpressionTree (parent )));
277284 }
278285 }
279- return computePrefixIndentation (path .getParentPath ().getLeaf (), arguments );
286+
287+ return textBlockToParent .entrySet ().stream ()
288+ .collect (Collectors .toMap (Map .Entry ::getKey , e -> parentToIndent .get (e .getValue ())));
280289 }
281290
282291 private String computePrefixIndentation (Tree parentPath , List <Tree > childExpressions ) {
@@ -499,21 +508,7 @@ private static String reflow(
499508 */
500509 private static List <Tree > flatten (
501510 String input , JCTree .JCCompilationUnit unit , TreePath path , TreePath parent , AtomicBoolean firstInChain ) {
502- List <Tree > flat = new ArrayList <>();
503-
504- // flatten the expression tree with a pre-order traversal
505- ArrayDeque <Tree > todo = new ArrayDeque <>();
506- todo .add (parent .getLeaf ());
507- while (!todo .isEmpty ()) {
508- Tree first = todo .removeFirst ();
509- if (first .getKind () == Tree .Kind .PLUS ) {
510- BinaryTree bt = (BinaryTree ) first ;
511- todo .addFirst (bt .getRightOperand ());
512- todo .addFirst (bt .getLeftOperand ());
513- } else {
514- flat .add (first );
515- }
516- }
511+ List <Tree > flat = flattenExpressionTree (parent .getLeaf ());
517512
518513 int idx = flat .indexOf (path .getLeaf ());
519514 Verify .verify (idx != -1 );
@@ -536,6 +531,29 @@ && noComments(input, unit, flat.get(endIdx - 1), flat.get(endIdx))) {
536531 return ImmutableList .copyOf (flat .subList (startIdx , endIdx ));
537532 }
538533
534+ /**
535+ * Returns the flatten expression tree with a pre-order traversal
536+ * @param parent a {@link com.sun.tools.javac.tree.JCTree.JCExpression}
537+ * @return the list of concatenated parameters in order.
538+ */
539+ private static List <Tree > flattenExpressionTree (Tree parent ) {
540+ // flatten the expression tree with a pre-order traversal
541+ List <Tree > flat = new ArrayList <>();
542+ ArrayDeque <Tree > todo = new ArrayDeque <>();
543+ todo .add (parent );
544+ while (!todo .isEmpty ()) {
545+ Tree first = todo .removeFirst ();
546+ if (first .getKind () == Tree .Kind .PLUS ) {
547+ BinaryTree bt = (BinaryTree ) first ;
548+ todo .addFirst (bt .getRightOperand ());
549+ todo .addFirst (bt .getLeftOperand ());
550+ } else {
551+ flat .add (first );
552+ }
553+ }
554+ return flat ;
555+ }
556+
539557 private static boolean noComments (String input , JCTree .JCCompilationUnit unit , Tree one , Tree two ) {
540558 return STRING_CONCAT_DELIMITER .matchesAllOf (
541559 input .subSequence (getEndPosition (unit , one ), getStartPosition (two )));
0 commit comments