• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2007-2010 Júlio Vilmar Gesser.
3  * Copyright (C) 2011, 2013-2016 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> {
getVariables()39     NodeList<VariableDeclarator> getVariables();
40 
setVariables(NodeList<VariableDeclarator> variables)41     N setVariables(NodeList<VariableDeclarator> variables);
42 
getVariable(int i)43     default VariableDeclarator getVariable(int i) {
44         return getVariables().get(i);
45     }
46 
47     @SuppressWarnings("unchecked")
setVariable(int i, VariableDeclarator variableDeclarator)48     default N setVariable(int i, VariableDeclarator variableDeclarator) {
49         getVariables().set(i, variableDeclarator);
50         return (N) this;
51     }
52 
53     @SuppressWarnings("unchecked")
addVariable(VariableDeclarator variableDeclarator)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;</code> this is int.
64      * <br/>For <code>int a,b,c,d;</code> this is also int.
65      * <br/>For <code>int a,b[],c;</code> this is an assertion error since b is an int[], not an int.
66      * <br/>For <code>int a,b;</code>, then doing setType(String) on b, this is an assertion error. It is also a situation that you don't really want.
67      */
getCommonType()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;</code> this is int.
85      * <br/>For <code>int a,b,c,d;</code> this is also int.
86      * <br/>For <code>int a,b[],c;</code> this is also int. Note: no mention of b being an array.
87      * <br/>For <code>int a,b;</code>, 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      */
getElementType()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")
setAllTypes(Type newType)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;</code> this is int.
121      * <br/>For <code>int a,b,c,d;</code> this is also int.
122      * <br/>For <code>int a,b[],c;</code> this is also int.
123      * <br/>For <code>int[] a[][],b[],c[][];</code> this is int[][].
124      */
125     @DerivedProperty
getMaximumCommonType()126     default Optional<Type> getMaximumCommonType() {
127         return calculateMaximumCommonType(getVariables().stream().map(v -> v.getType()).collect(Collectors.toList()));
128     }
129 
calculateMaximumCommonType(List<Type> types)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