• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2016 Google Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5  * in compliance with the License. You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software distributed under the License
10  * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11  * or implied. See the License for the specific language governing permissions and limitations under
12  * the License.
13  */
14 
15 package com.google.googlejavaformat.java;
16 
17 import com.google.common.collect.ImmutableList;
18 import com.sun.source.tree.AnnotatedTypeTree;
19 import com.sun.source.tree.AnnotationTree;
20 import com.sun.source.tree.ArrayTypeTree;
21 import com.sun.source.tree.Tree;
22 import com.sun.tools.javac.tree.JCTree;
23 import java.util.ArrayDeque;
24 import java.util.ArrayList;
25 import java.util.Collections;
26 import java.util.Deque;
27 import java.util.List;
28 
29 /**
30  * Utilities for working with array dimensions.
31  *
32  * <p>javac's parser does not preserve concrete syntax for mixed-notation arrays, so we have to
33  * re-lex the input to extra it.
34  *
35  * <p>For example, {@code int [] a;} cannot be distinguished from {@code int [] a [];} in the AST.
36  */
37 class DimensionHelpers {
38 
39   /** The array dimension specifiers (including any type annotations) associated with a type. */
40   static class TypeWithDims {
41     final Tree node;
42     final ImmutableList<List<AnnotationTree>> dims;
43 
TypeWithDims(Tree node, ImmutableList<List<AnnotationTree>> dims)44     public TypeWithDims(Tree node, ImmutableList<List<AnnotationTree>> dims) {
45       this.node = node;
46       this.dims = dims;
47     }
48   }
49 
50   enum SortedDims {
51     YES,
52     NO
53   }
54 
55   /** Returns a (possibly re-ordered) {@link TypeWithDims} for the given type. */
extractDims(Tree node, SortedDims sorted)56   static TypeWithDims extractDims(Tree node, SortedDims sorted) {
57     Deque<List<AnnotationTree>> builder = new ArrayDeque<>();
58     node = extractDims(builder, node);
59     Iterable<List<AnnotationTree>> dims;
60     if (sorted == SortedDims.YES) {
61       dims = reorderBySourcePosition(builder);
62     } else {
63       dims = builder;
64     }
65     return new TypeWithDims(node, ImmutableList.copyOf(dims));
66   }
67 
68   /**
69    * Rotate the list of dimension specifiers until all dimensions with type annotations appear in
70    * source order.
71    *
72    * <p>javac reorders dimension specifiers in method declarations with mixed-array notation, which
73    * means that any type annotations don't appear in source order.
74    *
75    * <p>For example, the type of {@code int @A [] f() @B [] {}} is parsed as {@code @B [] @A []}.
76    *
77    * <p>This doesn't handle cases with un-annotated dimension specifiers, so the formatting logic
78    * checks the token stream to figure out which side of the method name they appear on.
79    */
reorderBySourcePosition( Deque<List<AnnotationTree>> dims)80   private static Iterable<List<AnnotationTree>> reorderBySourcePosition(
81       Deque<List<AnnotationTree>> dims) {
82     int lastAnnotation = -1;
83     int lastPos = -1;
84     int idx = 0;
85     for (List<AnnotationTree> dim : dims) {
86       if (!dim.isEmpty()) {
87         int pos = ((JCTree) dim.get(0)).getStartPosition();
88         if (pos < lastPos) {
89           List<List<AnnotationTree>> list = new ArrayList<>(dims);
90           Collections.rotate(list, -(lastAnnotation + 1));
91           return list;
92         }
93         lastPos = pos;
94         lastAnnotation = idx;
95       }
96       idx++;
97     }
98     return dims;
99   }
100 
101   /**
102    * Accumulates a flattened list of array dimensions specifiers with type annotations, and returns
103    * the base type.
104    *
105    * <p>Given {@code int @A @B [][] @C []}, adds {@code [[@A, @B], [@C]]} to dims and returns {@code
106    * int}.
107    */
extractDims(Deque<List<AnnotationTree>> dims, Tree node)108   private static Tree extractDims(Deque<List<AnnotationTree>> dims, Tree node) {
109     switch (node.getKind()) {
110       case ARRAY_TYPE:
111         return extractDims(dims, ((ArrayTypeTree) node).getType());
112       case ANNOTATED_TYPE:
113         AnnotatedTypeTree annotatedTypeTree = (AnnotatedTypeTree) node;
114         if (annotatedTypeTree.getUnderlyingType().getKind() != Tree.Kind.ARRAY_TYPE) {
115           return node;
116         }
117         node = extractDims(dims, annotatedTypeTree.getUnderlyingType());
118         dims.addFirst(ImmutableList.copyOf(annotatedTypeTree.getAnnotations()));
119         return node;
120       default:
121         return node;
122     }
123   }
124 }
125