/*
* SonarQube Java
* Copyright (C) 2012-2016 SonarSource SA
* mailto:contact AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package org.sonar.java.resolve;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import org.junit.Test;
import org.sonar.java.ast.JavaAstScanner;
import org.sonar.java.ast.visitors.SubscriptionVisitor;
import org.sonar.java.model.JavaTree;
import org.sonar.java.model.VisitorsBridge;
import org.sonar.plugins.java.api.tree.ClassTree;
import org.sonar.plugins.java.api.tree.Tree;
import java.io.File;
import java.util.Collections;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
public class JavaTypeTest {
private Symbols symbols = new Symbols(new BytecodeCompleter(Lists.<File>newArrayList(), new ParametrizedTypeCache()));
@Test
public void test_order_of_tags() {
assertThat(JavaType.BYTE).isLessThan(JavaType.CHAR);
assertThat(JavaType.CHAR).isLessThan(JavaType.SHORT);
assertThat(JavaType.SHORT).isLessThan(JavaType.INT);
assertThat(JavaType.INT).isLessThan(JavaType.LONG);
assertThat(JavaType.LONG).isLessThan(JavaType.FLOAT);
assertThat(JavaType.FLOAT).isLessThan(JavaType.DOUBLE);
assertThat(JavaType.DOUBLE).isLessThan(JavaType.BOOLEAN);
assertThat(JavaType.BOOLEAN).isLessThan(JavaType.VOID);
assertThat(JavaType.VOID).isLessThan(JavaType.CLASS);
assertThat(JavaType.CLASS).isLessThan(JavaType.ARRAY);
}
@Test
public void checkTagging() {
assertThat(new JavaType(JavaType.VOID, null).isTagged(JavaType.VOID)).isTrue();
}
@Test
public void isNumerical_should_return_true_for_numerical_types() {
assertThat(new JavaType(JavaType.BYTE, null).isNumerical()).isTrue();
assertThat(new JavaType(JavaType.CHAR, null).isNumerical()).isTrue();
assertThat(new JavaType(JavaType.SHORT, null).isNumerical()).isTrue();
assertThat(new JavaType(JavaType.INT, null).isNumerical()).isTrue();
assertThat(new JavaType(JavaType.LONG, null).isNumerical()).isTrue();
assertThat(new JavaType(JavaType.FLOAT, null).isNumerical()).isTrue();
assertThat(new JavaType(JavaType.DOUBLE, null).isNumerical()).isTrue();
assertThat(new JavaType(JavaType.BOOLEAN, null).isNumerical()).isFalse();
assertThat(new JavaType(JavaType.VOID, null).isNumerical()).isFalse();
assertThat(new JavaType(JavaType.CLASS, null).isNumerical()).isFalse();
}
@Test
public void to_string_on_type() throws Exception {
assertThat(new JavaType(JavaType.VOID, null).toString()).isEmpty();
String methodToString = new MethodJavaType(ImmutableList.<JavaType>of(), new Symbols(new BytecodeCompleter(Lists.<File>newArrayList(), new ParametrizedTypeCache())).intType,
ImmutableList.<JavaType>of(), null).toString();
assertThat(methodToString).isEqualTo("returns int");
String constructorToString = new MethodJavaType(ImmutableList.<JavaType>of(), null, ImmutableList.<JavaType>of(), null).toString();
assertThat(constructorToString).isEqualTo("constructor");
}
@Test
public void type_is_fully_qualified_name() {
JavaSymbol.PackageJavaSymbol packageSymbol = new JavaSymbol.PackageJavaSymbol("org.foo.bar", null);
JavaSymbol.TypeJavaSymbol typeSymbol = new JavaSymbol.TypeJavaSymbol(Flags.PUBLIC, "MyType", packageSymbol);
JavaSymbol.TypeJavaSymbol typeSymbol2 = new JavaSymbol.TypeJavaSymbol(Flags.PUBLIC, "MyType", symbols.rootPackage);
ArrayJavaType arrayType = new ArrayJavaType(typeSymbol.type, symbols.arrayClass);
ClassJavaType classType = (ClassJavaType) typeSymbol.type;
classType.interfaces = Lists.newArrayList();
assertThat(symbols.byteType.is("byte")).isTrue();
assertThat(symbols.byteType.is("int")).isFalse();
assertThat(classType.is("org.foo.bar.MyType")).isTrue();
assertThat(typeSymbol2.type.is("MyType")).isTrue();
assertThat(classType.is("org.foo.bar.SomeClass")).isFalse();
assertThat(arrayType.is("org.foo.bar.MyType[]")).isTrue();
assertThat(arrayType.is("org.foo.bar.MyType")).isFalse();
assertThat(arrayType.is("org.foo.bar.SomeClass[]")).isFalse();
assertThat(symbols.nullType.is("org.foo.bar.SomeClass")).isTrue();
assertThat(symbols.unknownType.is("org.foo.bar.SomeClass")).isFalse();
}
@Test
public void isPrimitive() {
assertThat(new JavaType(JavaType.BYTE, null).isPrimitive()).isTrue();
assertThat(new JavaType(JavaType.CHAR, null).isPrimitive()).isTrue();
assertThat(new JavaType(JavaType.SHORT, null).isPrimitive()).isTrue();
assertThat(new JavaType(JavaType.INT, null).isPrimitive()).isTrue();
assertThat(new JavaType(JavaType.LONG, null).isPrimitive()).isTrue();
assertThat(new JavaType(JavaType.FLOAT, null).isPrimitive()).isTrue();
assertThat(new JavaType(JavaType.DOUBLE, null).isPrimitive()).isTrue();
assertThat(new JavaType(JavaType.BOOLEAN, null).isPrimitive()).isTrue();
assertThat(new JavaType(JavaType.VOID, null).isPrimitive()).isFalse();
assertThat(new JavaType(JavaType.ARRAY, null).isPrimitive()).isFalse();
assertThat(new JavaType(JavaType.CLASS, null).isPrimitive()).isFalse();
//Test primitive type
for (org.sonar.plugins.java.api.semantic.Type.Primitives primitive : org.sonar.plugins.java.api.semantic.Type.Primitives.values()) {
assertThat(symbols.charType.isPrimitive(primitive)).isEqualTo(primitive.equals(org.sonar.plugins.java.api.semantic.Type.Primitives.CHAR));
}
}
@Test
public void erasure_of_type_variable() {
JavaSymbol.TypeJavaSymbol typeSymbol = new JavaSymbol.TypeJavaSymbol(Flags.PUBLIC, "MyType", new JavaSymbol.PackageJavaSymbol("org.foo.bar", null));
TypeSubstitution typeSubstitution = new TypeSubstitution();
typeSubstitution.add((TypeVariableJavaType) new JavaSymbol.TypeVariableJavaSymbol("T", typeSymbol).type, typeSymbol.type);
ParametrizedTypeJavaType parametrizedType = new ParametrizedTypeJavaType(typeSymbol, typeSubstitution);
TypeVariableJavaType typeVariableType = (TypeVariableJavaType) new JavaSymbol.TypeVariableJavaSymbol("X", typeSymbol).type;
typeVariableType.bounds = ImmutableList.<JavaType>of(parametrizedType);
assertThat(typeVariableType.erasure()).isNotEqualTo(parametrizedType);
assertThat(typeVariableType.erasure()).isEqualTo(parametrizedType.erasure());
}
@Test
public void isSubtypeOf() throws Exception {
JavaSymbol.PackageJavaSymbol packageSymbol = new JavaSymbol.PackageJavaSymbol("org.foo.bar", null);
JavaSymbol.TypeJavaSymbol typeSymbol = new JavaSymbol.TypeJavaSymbol(Flags.PUBLIC, "MyType", packageSymbol);
JavaSymbol.TypeVariableJavaSymbol typeVariableSymbol = new JavaSymbol.TypeVariableJavaSymbol("T", typeSymbol);
ClassJavaType classType = (ClassJavaType) typeSymbol.type;
TypeVariableJavaType typeVariableType = (TypeVariableJavaType) typeVariableSymbol.type;
ArrayJavaType arrayType = new ArrayJavaType(typeSymbol.type, symbols.arrayClass);
typeVariableType.bounds = Lists.newArrayList(symbols.objectType);
classType.supertype = symbols.objectType;
classType.interfaces = Lists.newArrayList(symbols.cloneableType);
assertThat(classType.isSubtypeOf("java.lang.Object")).isTrue();
assertThat(classType.isSubtypeOf(symbols.objectType)).isTrue();
assertThat(classType.isSubtypeOf("org.foo.bar.MyType")).isTrue();
assertThat(classType.isSubtypeOf(typeSymbol.type)).isTrue();
assertThat(classType.isSubtypeOf("java.lang.CharSequence")).isFalse();
assertThat(classType.isSubtypeOf(symbols.stringType)).isFalse();
assertThat(classType.isSubtypeOf("java.lang.Cloneable")).isTrue();
assertThat(classType.isSubtypeOf(symbols.cloneableType)).isTrue();
assertThat(new JavaType(JavaType.BYTE, null).isSubtypeOf("java.lang.Object")).isFalse();
assertThat(arrayType.isSubtypeOf("org.foo.bar.MyType[]")).isTrue();
assertThat(arrayType.isSubtypeOf(new ArrayJavaType(typeSymbol.type, symbols.arrayClass))).isTrue();
assertThat(arrayType.isSubtypeOf("org.foo.bar.MyType")).isFalse();
assertThat(arrayType.isSubtypeOf(typeSymbol.type)).isFalse();
assertThat(arrayType.isSubtypeOf("java.lang.Object[]")).isTrue();
assertThat(arrayType.isSubtypeOf(new ArrayJavaType(symbols.objectType, symbols.arrayClass))).isTrue();
assertThat(arrayType.isSubtypeOf("java.lang.Object")).isTrue();
assertThat(arrayType.isSubtypeOf(symbols.objectType)).isTrue();
assertThat(symbols.nullType.isSubtypeOf(symbols.objectType)).isTrue();
assertThat(symbols.nullType.isSubtypeOf("java.lang.Object")).isTrue();
assertThat(symbols.objectType.isSubtypeOf(symbols.nullType)).isFalse();
assertThat(symbols.nullType.isSubtypeOf(arrayType)).isTrue();
assertThat(arrayType.isSubtypeOf(symbols.nullType)).isFalse();
assertThat(symbols.nullType.isSubtypeOf(symbols.nullType)).isTrue();
assertThat(arrayType.isSubtypeOf("org.foo.bar.SomeClass[]")).isFalse();
assertThat(typeVariableType.isSubtypeOf("java.lang.Object")).isTrue();
assertThat(typeVariableType.is("java.lang.Object")).isFalse();
assertThat(typeVariableType.isSubtypeOf("java.lang.CharSequence")).isFalse();
assertThat(Symbols.unknownType.is("java.lang.Object")).isFalse();
assertThat(Symbols.unknownType.isSubtypeOf("java.lang.CharSequence")).isFalse();
assertThat(Symbols.unknownType.isSubtypeOf(symbols.objectType)).isFalse();
}
@Test
public void is_primitive_wrapper() {
for (JavaType wrapper : symbols.boxedTypes.values()) {
assertThat(wrapper.isPrimitiveWrapper()).isTrue();
}
assertThat(symbols.objectType.isPrimitiveWrapper()).isFalse();
assertThat(symbols.intType.isPrimitiveWrapper()).isFalse();
}
@Test
public void mapping_wrapper_primitive() {
for (JavaType wrapper : symbols.boxedTypes.values()) {
assertThat(wrapper.primitiveType()).isNotNull();
assertThat(wrapper.primitiveWrapperType()).isNull();
}
for (JavaType primitive : symbols.boxedTypes.keySet()) {
assertThat(primitive.primitiveType()).isNull();
assertThat(primitive.primitiveWrapperType()).isNotNull();
}
assertThat(symbols.objectType.primitiveType()).isNull();
assertThat(symbols.objectType.primitiveWrapperType()).isNull();
}
@Test
public void parametrizedTypeType_methods_tests() {
JavaSymbol.PackageJavaSymbol packageSymbol = new JavaSymbol.PackageJavaSymbol("org.foo.bar", null);
JavaSymbol.TypeJavaSymbol typeSymbol = new JavaSymbol.TypeJavaSymbol(Flags.PUBLIC, "MyType", packageSymbol);
JavaSymbol.TypeVariableJavaSymbol typeVariableSymbol = new JavaSymbol.TypeVariableJavaSymbol("E", typeSymbol);
ClassJavaType classType = (ClassJavaType) typeSymbol.type;
TypeVariableJavaType typeVariableType = (TypeVariableJavaType) typeVariableSymbol.type;
TypeSubstitution typeSubstitution = new TypeSubstitution();
typeSubstitution.add(typeVariableType, classType);
ParametrizedTypeJavaType ptt = new ParametrizedTypeJavaType(typeSymbol, typeSubstitution);
assertThat(ptt.substitution(typeVariableType)).isEqualTo(classType);
assertThat(ptt.substitution(new TypeVariableJavaType(new JavaSymbol.TypeVariableJavaSymbol("F", typeSymbol)))).isNull();
assertThat(ptt.typeParameters()).hasSize(1);
assertThat(ptt.typeParameters()).contains(typeVariableType);
ptt = new ParametrizedTypeJavaType(typeSymbol, null);
assertThat(ptt.substitution(typeVariableType)).isNull();
assertThat(ptt.typeParameters()).isEmpty();
assertThat(ptt.isClass()).isTrue();
assertThat(ptt.isParameterized()).isTrue();
assertThat(ptt.rawType.isClass()).isTrue();
assertThat(ptt.rawType.isParameterized()).isFalse();
}
@Test
public void methodJavaType_return_type() {
JavaType intType = new Symbols(new BytecodeCompleter(Lists.<File>newArrayList(), new ParametrizedTypeCache())).intType;
MethodJavaType methodJavaType = new MethodJavaType(ImmutableList.<JavaType>of(), intType, ImmutableList.<JavaType>of(), null);
assertThat(methodJavaType.resultType()).isSameAs(intType);
MethodJavaType constructor = new MethodJavaType(ImmutableList.<JavaType>of(), null, ImmutableList.<JavaType>of(), null);
assertThat(constructor.resultType()).isNull();
}
@Test
public void fully_qualified_name() throws Exception {
JavaSymbol.PackageJavaSymbol packageSymbol = new JavaSymbol.PackageJavaSymbol("org.foo.bar", null);
JavaSymbol.TypeJavaSymbol typeSymbol = new JavaSymbol.TypeJavaSymbol(Flags.PUBLIC, "MyType", packageSymbol);
JavaSymbol.TypeJavaSymbol rootPackageTypeSymbol = new JavaSymbol.TypeJavaSymbol(Flags.PUBLIC, "MyType2", symbols.defaultPackage);
assertThat(typeSymbol.type.fullyQualifiedName()).isEqualTo("org.foo.bar.MyType");
assertThat(rootPackageTypeSymbol.type.fullyQualifiedName()).isEqualTo("MyType2");
JavaSymbol.TypeJavaSymbol typeSymbolNested = new JavaSymbol.TypeJavaSymbol(Flags.PUBLIC, "Nested", typeSymbol);
assertThat(typeSymbolNested.type.fullyQualifiedName()).isEqualTo("org.foo.bar.MyType$Nested");
JavaSymbol.TypeJavaSymbol typeSymbolAnonymous = new JavaSymbol.TypeJavaSymbol(Flags.PUBLIC, "", typeSymbol);
assertThat(typeSymbolAnonymous.type.fullyQualifiedName()).isEqualTo("org.foo.bar.MyType$1");
JavaSymbol.MethodJavaSymbol methodSymbol = new JavaSymbol.MethodJavaSymbol(Flags.PUBLIC, "<init>", typeSymbolNested);
JavaSymbol.TypeJavaSymbol typeSymbolAnonymous2 = new JavaSymbol.TypeJavaSymbol(Flags.PUBLIC, "", methodSymbol);
assertThat(typeSymbolAnonymous2.type.fullyQualifiedName()).isEqualTo("org.foo.bar.MyType$Nested$1");
}
@Test
public void test_fully_qualified_name() {
File bytecodeDir = new File("target/test-classes");
ClassFullQualifiedNameVerifierVisitor visitor = new ClassFullQualifiedNameVerifierVisitor(bytecodeDir);
JavaAstScanner.scanSingleFileForTests(
new File("src/test/java/org/sonar/java/resolve/targets/FullyQualifiedName.java"), new VisitorsBridge(Collections.singletonList(visitor), Lists.newArrayList(bytecodeDir), null));
}
private static class ClassFullQualifiedNameVerifierVisitor extends SubscriptionVisitor {
private final File bytecodeDir;
public ClassFullQualifiedNameVerifierVisitor(File bytecodeDir) {
this.bytecodeDir = bytecodeDir;
}
@Override
public List<Tree.Kind> nodesToVisit() {
return ImmutableList.of(Tree.Kind.CLASS);
}
@Override
public void visitNode(Tree tree) {
int line = ((JavaTree) tree).getLine();
String expectedFullyQualifiedName = ((ClassTree) tree).openBraceToken().trivias().get(0).comment();
expectedFullyQualifiedName = expectedFullyQualifiedName.substring(3, expectedFullyQualifiedName.length() - 3);
String classFullyQualifiedName = ((JavaSymbol.TypeJavaSymbol) ((ClassTree) tree).symbol()).getFullyQualifiedName();
assertThat(classFullyQualifiedName).as("Symbol mismatch for class at line " + line).isEqualTo(expectedFullyQualifiedName);
// check for .class file to make sure we match the compiler naming
File expectedDotClassFile = new File(bytecodeDir, classFullyQualifiedName.replace('.', File.separatorChar) + ".class");
assertThat(expectedDotClassFile).as("Bytecode file not found for class at line " + line).exists();
}
}
@Test
public void is_class_is_array() throws Exception {
JavaSymbol.PackageJavaSymbol packageSymbol = new JavaSymbol.PackageJavaSymbol("org.foo.bar", null);
JavaSymbol.TypeJavaSymbol typeSymbol = new JavaSymbol.TypeJavaSymbol(Flags.PUBLIC, "MyType", packageSymbol);
ArrayJavaType arrayType = new ArrayJavaType(typeSymbol.type, symbols.arrayClass);
assertThat(typeSymbol.type.isClass()).isTrue();
assertThat(typeSymbol.type.isArray()).isFalse();
assertThat(arrayType.isClass()).isFalse();
assertThat(arrayType.isArray()).isTrue();
}
}