1 | /* |
---|---|
2 | * Copyright (C) 2007-2010 JĂșlio Vilmar Gesser. |
3 | * Copyright (C) 2011, 2013-2020 The JavaParser Team. |
4 | * |
5 | * This file is part of JavaParser. |
6 | * |
7 | * JavaParser can be used either under the terms of |
8 | * a) the GNU Lesser General Public License as published by |
9 | * the Free Software Foundation, either version 3 of the License, or |
10 | * (at your option) any later version. |
11 | * b) the terms of the Apache License |
12 | * |
13 | * You should have received a copy of both licenses in LICENCE.LGPL and |
14 | * LICENCE.APACHE. Please refer to those files for details. |
15 | * |
16 | * JavaParser is distributed in the hope that it will be useful, |
17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
19 | * GNU Lesser General Public License for more details. |
20 | */ |
21 | |
22 | package com.github.javaparser.ast.nodeTypes; |
23 | |
24 | import com.github.javaparser.ast.Node; |
25 | import com.github.javaparser.ast.NodeList; |
26 | import com.github.javaparser.ast.body.VariableDeclarator; |
27 | import com.github.javaparser.ast.type.ArrayType; |
28 | import com.github.javaparser.ast.type.Type; |
29 | import com.github.javaparser.metamodel.DerivedProperty; |
30 | |
31 | import java.util.List; |
32 | import java.util.Optional; |
33 | import java.util.stream.Collectors; |
34 | |
35 | /** |
36 | * A node which has a list of variables. |
37 | */ |
38 | public interface NodeWithVariables<N extends Node> { |
39 | NodeList<VariableDeclarator> getVariables(); |
40 | |
41 | N setVariables(NodeList<VariableDeclarator> variables); |
42 | |
43 | default VariableDeclarator getVariable(int i) { |
44 | return getVariables().get(i); |
45 | } |
46 | |
47 | @SuppressWarnings("unchecked") |
48 | default N setVariable(int i, VariableDeclarator variableDeclarator) { |
49 | getVariables().set(i, variableDeclarator); |
50 | return (N) this; |
51 | } |
52 | |
53 | @SuppressWarnings("unchecked") |
54 | default N addVariable(VariableDeclarator variableDeclarator) { |
55 | getVariables().add(variableDeclarator); |
56 | return (N) this; |
57 | } |
58 | |
59 | /** |
60 | * Returns the type that is shared between all variables. |
61 | * This is a shortcut for when you are certain that all variables share one type. |
62 | * What makes this difficult is arrays, and being able to set the type. |
63 | * <br>For {@code int a;} this is int. |
64 | * <br>For {@code int a,b,c,d;} this is also int. |
65 | * <br>For {@code int a,b[],c;} this is an assertion error since b is an int[], not an int. |
66 | * <br>For {@code int a,b;}, then doing setType(String) on b, this is an assertion error. It is also a situation that you don't really want. |
67 | */ |
68 | default Type getCommonType() { |
69 | NodeList<VariableDeclarator> variables = getVariables(); |
70 | if (variables.isEmpty()) { |
71 | throw new AssertionError("There is no common type since there are no variables."); |
72 | } |
73 | Type type = variables.get(0).getType(); |
74 | for (int i = 1; i < variables.size(); i++) { |
75 | if (!variables.get(i).getType().equals(type)) { |
76 | throw new AssertionError("The variables do not have a common type."); |
77 | } |
78 | } |
79 | return type; |
80 | } |
81 | |
82 | /** |
83 | * Returns the element type. |
84 | * <br>For {@code int a;} this is int. |
85 | * <br>For {@code int a,b,c,d;} this is also int. |
86 | * <br>For {@code int a,b[],c;} this is also int. Note: no mention of b being an array. |
87 | * <br>For {@code int a,b;}, then doing setType(String) on b, then calling getElementType(). This is an assertion error. It is also a situation that you don't really want. |
88 | */ |
89 | default Type getElementType() { |
90 | NodeList<VariableDeclarator> variables = getVariables(); |
91 | if (variables.isEmpty()) { |
92 | throw new AssertionError("There is no element type since there are no variables."); |
93 | } |
94 | Type type = variables.get(0).getType().getElementType(); |
95 | for (int i = 1; i < variables.size(); i++) { |
96 | if (!variables.get(i).getType().getElementType().equals(type)) { |
97 | throw new AssertionError("The variables do not have a common type."); |
98 | } |
99 | } |
100 | return type; |
101 | } |
102 | |
103 | /** |
104 | * Sets the type of all variables. |
105 | * Erases any existing type. |
106 | * This is a shortcut for setting a type on all variable declarators separately. |
107 | */ |
108 | @SuppressWarnings("unchecked") |
109 | default N setAllTypes(Type newType) { |
110 | for (VariableDeclarator variable : getVariables()) { |
111 | variable.setType(newType); |
112 | } |
113 | return (N) this; |
114 | } |
115 | |
116 | /** |
117 | * Returns the type that maximum shared type between all variables. |
118 | * The minimum common type does never include annotations on the array level. |
119 | * <p> |
120 | * <br>For {@code int a;} this is int. |
121 | * <br>For {@code int a,b,c,d;} this is also int. |
122 | * <br>For {@code int a,b[],c;} this is also int. |
123 | * <br>For {@code int[] a[][],b[],c[][];} this is int[][]. |
124 | */ |
125 | @DerivedProperty |
126 | default Optional<Type> getMaximumCommonType() { |
127 | return calculateMaximumCommonType(getVariables().stream().map(v -> v.getType()).collect(Collectors.toList())); |
128 | } |
129 | |
130 | static Optional<Type> calculateMaximumCommonType(List<Type> types) { |
131 | // we use a local class because we cannot use an helper static method in an interface |
132 | class Helper { |
133 | // Conceptually: given a type we start from the Element Type and get as many array levels as indicated |
134 | // From the implementation point of view we start from the actual type and we remove how many array |
135 | // levels as needed to get the target level of arrays |
136 | // It returns null if the type has less array levels then the desired target |
137 | private Optional<Type> toArrayLevel(Type type, int level) { |
138 | if (level > type.getArrayLevel()) { |
139 | return Optional.empty(); |
140 | } |
141 | for (int i = type.getArrayLevel(); i > level; i--) { |
142 | if (!(type instanceof ArrayType)) { |
143 | return Optional.empty(); |
144 | } |
145 | type = ((ArrayType) type).getComponentType(); |
146 | } |
147 | return Optional.of(type); |
148 | } |
149 | } |
150 | |
151 | Helper helper = new Helper(); |
152 | int level = 0; |
153 | boolean keepGoing = true; |
154 | // In practice we want to check for how many levels of arrays all the variables have the same type, |
155 | // including also the annotations |
156 | while (keepGoing) { |
157 | final int currentLevel = level; |
158 | // Now, given that equality on nodes consider the position the simplest way is to compare |
159 | // the pretty-printed string got for a node. We just check all them are the same and if they |
160 | // are we just just is not null |
161 | Object[] values = types.stream().map(v -> { |
162 | Optional<Type> t = helper.toArrayLevel(v, currentLevel); |
163 | return t.map(Node::toString).orElse(null); |
164 | }).distinct().toArray(); |
165 | if (values.length == 1 && values[0] != null) { |
166 | level++; |
167 | } else { |
168 | keepGoing = false; |
169 | } |
170 | } |
171 | return helper.toArrayLevel(types.get(0), --level); |
172 | } |
173 | |
174 | } |
175 |
Members