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