| /* |
| * Copyright 2016 Google Inc. |
| * |
| * 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.google.googlejavaformat.java; |
| |
| import com.google.common.collect.ImmutableList; |
| import com.sun.source.tree.AnnotatedTypeTree; |
| import com.sun.source.tree.AnnotationTree; |
| import com.sun.source.tree.ArrayTypeTree; |
| import com.sun.source.tree.Tree; |
| import com.sun.tools.javac.tree.JCTree; |
| import java.util.ArrayDeque; |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.Deque; |
| import java.util.List; |
| |
| /** |
| * Utilities for working with array dimensions. |
| * |
| * <p>javac's parser does not preserve concrete syntax for mixed-notation arrays, so we have to |
| * re-lex the input to extra it. |
| * |
| * <p>For example, {@code int [] a;} cannot be distinguished from {@code int [] a [];} in the AST. |
| */ |
| class DimensionHelpers { |
| |
| /** The array dimension specifiers (including any type annotations) associated with a type. */ |
| static class TypeWithDims { |
| final Tree node; |
| final ImmutableList<List<AnnotationTree>> dims; |
| |
| public TypeWithDims(Tree node, ImmutableList<List<AnnotationTree>> dims) { |
| this.node = node; |
| this.dims = dims; |
| } |
| } |
| |
| enum SortedDims { |
| YES, |
| NO |
| } |
| |
| /** Returns a (possibly re-ordered) {@link TypeWithDims} for the given type. */ |
| static TypeWithDims extractDims(Tree node, SortedDims sorted) { |
| Deque<List<AnnotationTree>> builder = new ArrayDeque<>(); |
| node = extractDims(builder, node); |
| Iterable<List<AnnotationTree>> dims; |
| if (sorted == SortedDims.YES) { |
| dims = reorderBySourcePosition(builder); |
| } else { |
| dims = builder; |
| } |
| return new TypeWithDims(node, ImmutableList.copyOf(dims)); |
| } |
| |
| /** |
| * Rotate the list of dimension specifiers until all dimensions with type annotations appear in |
| * source order. |
| * |
| * <p>javac reorders dimension specifiers in method declarations with mixed-array notation, which |
| * means that any type annotations don't appear in source order. |
| * |
| * <p>For example, the type of {@code int @A [] f() @B [] {}} is parsed as {@code @B [] @A []}. |
| * |
| * <p>This doesn't handle cases with un-annotated dimension specifiers, so the formatting logic |
| * checks the token stream to figure out which side of the method name they appear on. |
| */ |
| private static Iterable<List<AnnotationTree>> reorderBySourcePosition( |
| Deque<List<AnnotationTree>> dims) { |
| int lastAnnotation = -1; |
| int lastPos = -1; |
| int idx = 0; |
| for (List<AnnotationTree> dim : dims) { |
| if (!dim.isEmpty()) { |
| int pos = ((JCTree) dim.get(0)).getStartPosition(); |
| if (pos < lastPos) { |
| List<List<AnnotationTree>> list = new ArrayList<>(dims); |
| Collections.rotate(list, -(lastAnnotation + 1)); |
| return list; |
| } |
| lastPos = pos; |
| lastAnnotation = idx; |
| } |
| idx++; |
| } |
| return dims; |
| } |
| |
| /** |
| * Accumulates a flattened list of array dimensions specifiers with type annotations, and returns |
| * the base type. |
| * |
| * <p>Given {@code int @A @B [][] @C []}, adds {@code [[@A, @B], [@C]]} to dims and returns {@code |
| * int}. |
| */ |
| private static Tree extractDims(Deque<List<AnnotationTree>> dims, Tree node) { |
| switch (node.getKind()) { |
| case ARRAY_TYPE: |
| return extractDims(dims, ((ArrayTypeTree) node).getType()); |
| case ANNOTATED_TYPE: |
| AnnotatedTypeTree annotatedTypeTree = (AnnotatedTypeTree) node; |
| if (annotatedTypeTree.getUnderlyingType().getKind() != Tree.Kind.ARRAY_TYPE) { |
| return node; |
| } |
| node = extractDims(dims, annotatedTypeTree.getUnderlyingType()); |
| dims.addFirst(ImmutableList.copyOf(annotatedTypeTree.getAnnotations())); |
| return node; |
| default: |
| return node; |
| } |
| } |
| } |