diff options
Diffstat (limited to 'javaparser-symbol-solver-logic')
10 files changed, 797 insertions, 0 deletions
diff --git a/javaparser-symbol-solver-logic/.gitignore b/javaparser-symbol-solver-logic/.gitignore new file mode 100644 index 000000000..8f22fa733 --- /dev/null +++ b/javaparser-symbol-solver-logic/.gitignore @@ -0,0 +1,4 @@ +build +/.classpath +/.project +.settings diff --git a/javaparser-symbol-solver-logic/javaparser-symbol-solver-logic.iml b/javaparser-symbol-solver-logic/javaparser-symbol-solver-logic.iml new file mode 100644 index 000000000..2a2531a25 --- /dev/null +++ b/javaparser-symbol-solver-logic/javaparser-symbol-solver-logic.iml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<module org.jetbrains.idea.maven.project.MavenProjectsManager.isMavenModule="true" type="JAVA_MODULE" version="4"> + <component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_8"> + <output url="file://$MODULE_DIR$/target/classes" /> + <output-test url="file://$MODULE_DIR$/target/test-classes" /> + <content url="file://$MODULE_DIR$"> + <sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" /> + <excludeFolder url="file://$MODULE_DIR$/target" /> + </content> + <orderEntry type="inheritedJdk" /> + <orderEntry type="sourceFolder" forTests="false" /> + <orderEntry type="library" name="Maven: org.javassist:javassist:3.22.0-GA" level="project" /> + <orderEntry type="module" module-name="javaparser-symbol-solver-model" /> + <orderEntry type="module" module-name="javaparser-core" /> + <orderEntry type="library" name="Maven: com.google.guava:guava:23.4-jre" level="project" /> + <orderEntry type="library" name="Maven: com.google.code.findbugs:jsr305:1.3.9" level="project" /> + <orderEntry type="library" name="Maven: com.google.errorprone:error_prone_annotations:2.0.18" level="project" /> + <orderEntry type="library" name="Maven: com.google.j2objc:j2objc-annotations:1.1" level="project" /> + <orderEntry type="library" name="Maven: org.codehaus.mojo:animal-sniffer-annotations:1.14" level="project" /> + </component> +</module>
\ No newline at end of file diff --git a/javaparser-symbol-solver-logic/pom.xml b/javaparser-symbol-solver-logic/pom.xml new file mode 100644 index 000000000..25b7856c1 --- /dev/null +++ b/javaparser-symbol-solver-logic/pom.xml @@ -0,0 +1,121 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <parent> + <artifactId>javaparser-parent</artifactId> + <groupId>com.github.javaparser</groupId> + <version>3.5.16-SNAPSHOT</version> + </parent> + <modelVersion>4.0.0</modelVersion> + + <artifactId>javaparser-symbol-solver-logic</artifactId> + <packaging>jar</packaging> + <description>A Symbol Solver for Java, built on top of JavaParser (logic)</description> + + <licenses> + <license> + <name>GNU Lesser General Public License</name> + <url>http://www.gnu.org/licenses/lgpl-3.0.html</url> + <distribution>repo</distribution> + </license> + <license> + <name>Apache License, Version 2.0</name> + <url>http://www.apache.org/licenses/LICENSE-2.0.txt</url> + <distribution>repo</distribution> + <comments>A business-friendly OSS license</comments> + </license> + </licenses> + + <properties> + <java.version>1.8</java.version> + <build.timestamp>${maven.build.timestamp}</build.timestamp> + </properties> + + <dependencies> + <dependency> + <groupId>org.javassist</groupId> + <artifactId>javassist</artifactId> + </dependency> + <dependency> + <groupId>com.github.javaparser</groupId> + <artifactId>javaparser-symbol-solver-model</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>com.google.guava</groupId> + <artifactId>guava</artifactId> + </dependency> + </dependencies> + + <build> + <plugins> + <plugin> + <groupId>org.codehaus.mojo</groupId> + <artifactId>animal-sniffer-maven-plugin</artifactId> + <version>1.16</version> + <configuration> + <signature> + <!-- Make sure only the API of this JDK is used --> + <groupId>org.codehaus.mojo.signature</groupId> + <artifactId>java18</artifactId> + <version>1.0</version> + </signature> + </configuration> + <executions> + <execution> + <id>animal-sniffer</id> + <phase>verify</phase> + <goals> + <goal>check</goal> + </goals> + </execution> + </executions> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-enforcer-plugin</artifactId> + <version>3.0.0-M1</version> + <executions> + <execution> + <id>enforce-versions</id> + <phase>verify</phase> + <goals> + <goal>enforce</goal> + </goals> + <configuration> + <rules> + <requireJavaVersion> + <!-- Make sure a compiler of this version is used --> + <version>${java.version}</version> + </requireJavaVersion> + <enforceBytecodeVersion> + <!-- Make sure the dependencies are compiled for our Java version --> + <maxJdkVersion>${java.version}</maxJdkVersion> + </enforceBytecodeVersion> + </rules> + </configuration> + </execution> + </executions> + <dependencies> + <dependency> + <groupId>org.codehaus.mojo</groupId> + <artifactId>extra-enforcer-rules</artifactId> + <version>1.0-beta-6</version> + </dependency> + </dependencies> + </plugin> + <!-- Set JPMS module name --> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-jar-plugin</artifactId> + <configuration> + <archive> + <manifestEntries> + <Automatic-Module-Name>com.github.javaparser.symbolsolver.logic</Automatic-Module-Name> + </manifestEntries> + </archive> + </configuration> + </plugin> + </plugins> + </build> + +</project> diff --git a/javaparser-symbol-solver-logic/src/main/java/com/github/javaparser/symbolsolver/logic/AbstractClassDeclaration.java b/javaparser-symbol-solver-logic/src/main/java/com/github/javaparser/symbolsolver/logic/AbstractClassDeclaration.java new file mode 100644 index 000000000..1f770e785 --- /dev/null +++ b/javaparser-symbol-solver-logic/src/main/java/com/github/javaparser/symbolsolver/logic/AbstractClassDeclaration.java @@ -0,0 +1,84 @@ +/* + * Copyright 2016 Federico Tomassetti + * + * 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.github.javaparser.symbolsolver.logic; + +import com.github.javaparser.resolution.declarations.ResolvedClassDeclaration; +import com.github.javaparser.resolution.types.ResolvedReferenceType; + +import java.util.ArrayList; +import java.util.List; + +/** + * A common ancestor for all ClassDeclarations. + * + * @author Federico Tomassetti + */ +public abstract class AbstractClassDeclaration extends AbstractTypeDeclaration implements ResolvedClassDeclaration { + + /// + /// Public + /// + + @Override + public boolean hasName() { + return getQualifiedName() != null; + } + + @Override + public final List<ResolvedReferenceType> getAllSuperClasses() { + List<ResolvedReferenceType> superclasses = new ArrayList<>(); + ResolvedReferenceType superClass = getSuperClass(); + if (superClass != null) { + superclasses.add(superClass); + superclasses.addAll(superClass.getAllClassesAncestors()); + } + + if (superclasses.removeIf(s -> s.getQualifiedName().equals(Object.class.getCanonicalName()))) { + superclasses.add(object()); + } + return superclasses; + } + + @Override + public final List<ResolvedReferenceType> getAllInterfaces() { + List<ResolvedReferenceType> interfaces = new ArrayList<>(); + for (ResolvedReferenceType interfaceDeclaration : getInterfaces()) { + interfaces.add(interfaceDeclaration); + interfaces.addAll(interfaceDeclaration.getAllInterfacesAncestors()); + } + ResolvedReferenceType superClass = this.getSuperClass(); + if (superClass != null) { + interfaces.addAll(superClass.getAllInterfacesAncestors()); + } + return interfaces; + } + + @Override + public final ResolvedClassDeclaration asClass() { + return this; + } + + /// + /// Protected + /// + + /** + * An implementation of the Object class. + */ + protected abstract ResolvedReferenceType object(); + +} diff --git a/javaparser-symbol-solver-logic/src/main/java/com/github/javaparser/symbolsolver/logic/AbstractTypeDeclaration.java b/javaparser-symbol-solver-logic/src/main/java/com/github/javaparser/symbolsolver/logic/AbstractTypeDeclaration.java new file mode 100644 index 000000000..b08ff1e67 --- /dev/null +++ b/javaparser-symbol-solver-logic/src/main/java/com/github/javaparser/symbolsolver/logic/AbstractTypeDeclaration.java @@ -0,0 +1,64 @@ +/* + * Copyright 2016 Federico Tomassetti + * + * 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.github.javaparser.symbolsolver.logic; + +import com.github.javaparser.resolution.MethodUsage; +import com.github.javaparser.resolution.declarations.ResolvedMethodDeclaration; +import com.github.javaparser.resolution.declarations.ResolvedReferenceTypeDeclaration; +import com.github.javaparser.resolution.types.ResolvedReferenceType; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * Common ancestor for most types. + * + * @author Federico Tomassetti + */ +public abstract class AbstractTypeDeclaration implements ResolvedReferenceTypeDeclaration { + + @Override + public final Set<MethodUsage> getAllMethods() { + Set<MethodUsage> methods = new HashSet<>(); + + Set<String> methodsSignatures = new HashSet<>(); + + for (ResolvedMethodDeclaration methodDeclaration : getDeclaredMethods()) { + methods.add(new MethodUsage(methodDeclaration)); + methodsSignatures.add(methodDeclaration.getSignature()); + } + + for (ResolvedReferenceType ancestor : getAllAncestors()) { + for (MethodUsage mu : ancestor.getDeclaredMethods()) { + String signature = mu.getDeclaration().getSignature(); + if (!methodsSignatures.contains(signature)) { + methodsSignatures.add(signature); + methods.add(mu); + } + } + } + + return methods; + } + + @Override + public final boolean isFunctionalInterface() { + return FunctionalInterfaceLogic.getFunctionalMethod(this).isPresent(); + } + +} diff --git a/javaparser-symbol-solver-logic/src/main/java/com/github/javaparser/symbolsolver/logic/ConfilictingGenericTypesException.java b/javaparser-symbol-solver-logic/src/main/java/com/github/javaparser/symbolsolver/logic/ConfilictingGenericTypesException.java new file mode 100644 index 000000000..684ffdba3 --- /dev/null +++ b/javaparser-symbol-solver-logic/src/main/java/com/github/javaparser/symbolsolver/logic/ConfilictingGenericTypesException.java @@ -0,0 +1,13 @@ +package com.github.javaparser.symbolsolver.logic; + +import com.github.javaparser.resolution.types.ResolvedType; + +/** + * @author Federico Tomassetti + */ +public class ConfilictingGenericTypesException extends RuntimeException { + + public ConfilictingGenericTypesException(ResolvedType formalType, ResolvedType actualType) { + super(String.format("No matching between %s (formal) and %s (actual)", formalType, actualType)); + } +} diff --git a/javaparser-symbol-solver-logic/src/main/java/com/github/javaparser/symbolsolver/logic/FunctionalInterfaceLogic.java b/javaparser-symbol-solver-logic/src/main/java/com/github/javaparser/symbolsolver/logic/FunctionalInterfaceLogic.java new file mode 100644 index 000000000..94ce18b77 --- /dev/null +++ b/javaparser-symbol-solver-logic/src/main/java/com/github/javaparser/symbolsolver/logic/FunctionalInterfaceLogic.java @@ -0,0 +1,89 @@ +/* + * Copyright 2016 Federico Tomassetti + * + * 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.github.javaparser.symbolsolver.logic; + +import com.github.javaparser.resolution.MethodUsage; +import com.github.javaparser.resolution.declarations.ResolvedReferenceTypeDeclaration; +import com.github.javaparser.resolution.types.ResolvedType; + +import java.lang.reflect.Method; +import java.lang.reflect.Parameter; +import java.util.Arrays; +import java.util.List; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * @author Federico Tomassetti + */ +public final class FunctionalInterfaceLogic { + + private FunctionalInterfaceLogic() { + // prevent instantiation + } + + /** + * Get the functional method defined by the type, if any. + */ + public static Optional<MethodUsage> getFunctionalMethod(ResolvedType type) { + if (type.isReferenceType() && type.asReferenceType().getTypeDeclaration().isInterface()) { + return getFunctionalMethod(type.asReferenceType().getTypeDeclaration()); + } else { + return Optional.empty(); + } + } + + /** + * Get the functional method defined by the type, if any. + */ + public static Optional<MethodUsage> getFunctionalMethod(ResolvedReferenceTypeDeclaration typeDeclaration) { + //We need to find all abstract methods + Set<MethodUsage> methods = typeDeclaration.getAllMethods().stream() + .filter(m -> m.getDeclaration().isAbstract()) + // Remove methods inherited by Object: + // Consider the case of Comparator which define equals. It would be considered a functional method. + .filter(m -> !declaredOnObject(m)) + .collect(Collectors.toSet()); + + if (methods.size() == 1) { + return Optional.of(methods.iterator().next()); + } else { + return Optional.empty(); + } + } + + public static boolean isFunctionalInterfaceType(ResolvedType type) { + return getFunctionalMethod(type).isPresent(); + } + + private static String getSignature(Method m) { + return String.format("%s(%s)", m.getName(), String.join(", ", Arrays.stream(m.getParameters()).map(p -> toSignature(p)).collect(Collectors.toList()))); + } + + private static String toSignature(Parameter p) { + return p.getType().getCanonicalName(); + } + + private static List<String> OBJECT_METHODS_SIGNATURES = Arrays.stream(Object.class.getDeclaredMethods()) + .map(method -> getSignature(method)) + .collect(Collectors.toList()); + + private static boolean declaredOnObject(MethodUsage m) { + return OBJECT_METHODS_SIGNATURES.contains(m.getDeclaration().getSignature()); + } +} diff --git a/javaparser-symbol-solver-logic/src/main/java/com/github/javaparser/symbolsolver/logic/InferenceContext.java b/javaparser-symbol-solver-logic/src/main/java/com/github/javaparser/symbolsolver/logic/InferenceContext.java new file mode 100644 index 000000000..4d3df0fcf --- /dev/null +++ b/javaparser-symbol-solver-logic/src/main/java/com/github/javaparser/symbolsolver/logic/InferenceContext.java @@ -0,0 +1,210 @@ +/* + * Copyright 2016 Federico Tomassetti + * + * 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.github.javaparser.symbolsolver.logic; + +import com.github.javaparser.resolution.declarations.ResolvedTypeParameterDeclaration; +import com.github.javaparser.resolution.types.*; +import com.github.javaparser.symbolsolver.model.typesystem.*; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * @author Federico Tomassetti + */ +public class InferenceContext { + + private int nextInferenceVariableId = 0; + private ObjectProvider objectProvider; + private List<InferenceVariableType> inferenceVariableTypes = new ArrayList<>(); + + public InferenceContext(ObjectProvider objectProvider) { + this.objectProvider = objectProvider; + } + + private Map<String, InferenceVariableType> inferenceVariableTypeMap = new HashMap<>(); + + private InferenceVariableType inferenceVariableTypeForTp(ResolvedTypeParameterDeclaration tp) { + if (!inferenceVariableTypeMap.containsKey(tp.getName())) { + InferenceVariableType inferenceVariableType = new InferenceVariableType(nextInferenceVariableId++, objectProvider); + inferenceVariableTypes.add(inferenceVariableType); + inferenceVariableType.setCorrespondingTp(tp); + inferenceVariableTypeMap.put(tp.getName(), inferenceVariableType); + } + return inferenceVariableTypeMap.get(tp.getName()); + } + + /** + * + * @return the actual with the inference variable inserted + */ + public ResolvedType addPair(ResolvedType target, ResolvedType actual) { + target = placeInferenceVariables(target); + actual = placeInferenceVariables(actual); + registerCorrespondance(target, actual); + return target; + } + + public ResolvedType addSingle(ResolvedType actual) { + return placeInferenceVariables(actual); + } + + private void registerCorrespondance(ResolvedType formalType, ResolvedType actualType) { + if (formalType.isReferenceType() && actualType.isReferenceType()) { + ResolvedReferenceType formalTypeAsReference = formalType.asReferenceType(); + ResolvedReferenceType actualTypeAsReference = actualType.asReferenceType(); + + if (!formalTypeAsReference.getQualifiedName().equals(actualTypeAsReference.getQualifiedName())) { + List<ResolvedReferenceType> ancestors = actualTypeAsReference.getAllAncestors(); + final String formalParamTypeQName = formalTypeAsReference.getQualifiedName(); + List<ResolvedType> correspondingFormalType = ancestors.stream().filter((a) -> a.getQualifiedName().equals(formalParamTypeQName)).collect(Collectors.toList()); + if (correspondingFormalType.isEmpty()) { + ancestors = formalTypeAsReference.getAllAncestors(); + final String actualParamTypeQname = actualTypeAsReference.getQualifiedName(); + List<ResolvedType> correspondingActualType = ancestors.stream().filter(a -> a.getQualifiedName().equals(actualParamTypeQname)).collect(Collectors.toList()); + if (correspondingActualType.isEmpty()){ + throw new ConfilictingGenericTypesException(formalType, actualType); + } + correspondingFormalType = correspondingActualType; + + } + actualTypeAsReference = correspondingFormalType.get(0).asReferenceType(); + } + + if (formalTypeAsReference.getQualifiedName().equals(actualTypeAsReference.getQualifiedName())) { + if (!formalTypeAsReference.typeParametersValues().isEmpty()) { + if (actualTypeAsReference.isRawType()) { + // nothing to do + } else { + int i = 0; + for (ResolvedType formalTypeParameter : formalTypeAsReference.typeParametersValues()) { + registerCorrespondance(formalTypeParameter, actualTypeAsReference.typeParametersValues().get(i)); + i++; + } + } + } + } + } else if (formalType instanceof InferenceVariableType && !actualType.isPrimitive()) { + ((InferenceVariableType) formalType).registerEquivalentType(actualType); + if (actualType instanceof InferenceVariableType) { + ((InferenceVariableType) actualType).registerEquivalentType(formalType); + } + } else if (actualType.isNull()) { + // nothing to do + } else if (actualType.equals(formalType)) { + // nothing to do + } else if (actualType.isArray() && formalType.isArray()) { + registerCorrespondance(formalType.asArrayType().getComponentType(), actualType.asArrayType().getComponentType()); + } else if (formalType.isWildcard()) { + // nothing to do + if ((actualType instanceof InferenceVariableType) && formalType.asWildcard().isBounded()) { + ((InferenceVariableType) actualType).registerEquivalentType(formalType.asWildcard().getBoundedType()); + if (formalType.asWildcard().getBoundedType() instanceof InferenceVariableType) { + ((InferenceVariableType) formalType.asWildcard().getBoundedType()).registerEquivalentType(actualType); + } + } + if (actualType.isWildcard()) { + ResolvedWildcard formalWildcard = formalType.asWildcard(); + ResolvedWildcard actualWildcard = actualType.asWildcard(); + if (formalWildcard.isBounded() && formalWildcard.getBoundedType() instanceof InferenceVariableType) { + if (formalWildcard.isSuper() && actualWildcard.isSuper()) { + ((InferenceVariableType) formalType.asWildcard().getBoundedType()).registerEquivalentType(actualWildcard.getBoundedType()); + } else if (formalWildcard.isExtends() && actualWildcard.isExtends()) { + ((InferenceVariableType) formalType.asWildcard().getBoundedType()).registerEquivalentType(actualWildcard.getBoundedType()); + } + } + } + + if (actualType.isReferenceType()){ + if (formalType.asWildcard().isBounded()){ + registerCorrespondance(formalType.asWildcard().getBoundedType(), actualType); + } + } + } else if (actualType instanceof InferenceVariableType){ + if (formalType instanceof ResolvedReferenceType){ + ((InferenceVariableType) actualType).registerEquivalentType(formalType); + } else if (formalType instanceof InferenceVariableType){ + ((InferenceVariableType) actualType).registerEquivalentType(formalType); + } + } else if (actualType.isConstraint()){ + ResolvedLambdaConstraintType constraintType = actualType.asConstraintType(); + if (constraintType.getBound() instanceof InferenceVariableType){ + ((InferenceVariableType) constraintType.getBound()).registerEquivalentType(formalType); + } + } else if (actualType.isPrimitive()) { + if (formalType.isPrimitive()) { + // nothing to do + } else { + registerCorrespondance(formalType, objectProvider.byName(actualType.asPrimitive().getBoxTypeQName())); + } + } else { + throw new UnsupportedOperationException(formalType.describe() + " " + actualType.describe()); + } + } + + private ResolvedType placeInferenceVariables(ResolvedType type) { + if (type.isWildcard()) { + if (type.asWildcard().isExtends()) { + return ResolvedWildcard.extendsBound(placeInferenceVariables(type.asWildcard().getBoundedType())); + } else if (type.asWildcard().isSuper()) { + return ResolvedWildcard.superBound(placeInferenceVariables(type.asWildcard().getBoundedType())); + } else { + return type; + } + } else if (type.isTypeVariable()) { + return inferenceVariableTypeForTp(type.asTypeParameter()); + } else if (type.isReferenceType()) { + return type.asReferenceType().transformTypeParameters(tp -> placeInferenceVariables(tp)); + } else if (type.isArray()) { + return new ResolvedArrayType(placeInferenceVariables(type.asArrayType().getComponentType())); + } else if (type.isNull() || type.isPrimitive() || type.isVoid()) { + return type; + } else if (type.isConstraint()){ + return ResolvedLambdaConstraintType.bound(placeInferenceVariables(type.asConstraintType().getBound())); + } else if (type instanceof InferenceVariableType) { + return type; + } else { + throw new UnsupportedOperationException(type.describe()); + } + } + + public ResolvedType resolve(ResolvedType type) { + if (type instanceof InferenceVariableType) { + InferenceVariableType inferenceVariableType = (InferenceVariableType) type; + return inferenceVariableType.equivalentType(); + } else if (type.isReferenceType()) { + return type.asReferenceType().transformTypeParameters(tp -> resolve(tp)); + } else if (type.isNull() || type.isPrimitive() || type.isVoid()) { + return type; + } else if (type.isArray()) { + return new ResolvedArrayType(resolve(type.asArrayType().getComponentType())); + } else if (type.isWildcard()) { + if (type.asWildcard().isExtends()) { + return ResolvedWildcard.extendsBound(resolve(type.asWildcard().getBoundedType())); + } else if (type.asWildcard().isSuper()) { + return ResolvedWildcard.superBound(resolve(type.asWildcard().getBoundedType())); + } else { + return type; + } + } else { + throw new UnsupportedOperationException(type.describe()); + } + } +} diff --git a/javaparser-symbol-solver-logic/src/main/java/com/github/javaparser/symbolsolver/logic/InferenceVariableType.java b/javaparser-symbol-solver-logic/src/main/java/com/github/javaparser/symbolsolver/logic/InferenceVariableType.java new file mode 100644 index 000000000..4d2e9840b --- /dev/null +++ b/javaparser-symbol-solver-logic/src/main/java/com/github/javaparser/symbolsolver/logic/InferenceVariableType.java @@ -0,0 +1,164 @@ +/* + * Copyright 2016 Federico Tomassetti + * + * 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.github.javaparser.symbolsolver.logic; + +import com.github.javaparser.resolution.declarations.ResolvedTypeParameterDeclaration; +import com.github.javaparser.resolution.types.ResolvedReferenceType; +import com.github.javaparser.resolution.types.ResolvedType; +import com.github.javaparser.resolution.types.ResolvedTypeVariable; +import com.github.javaparser.resolution.types.ResolvedWildcard; + +import java.util.HashSet; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * An element using during type inference. + * + * @author Federico Tomassetti + */ +public class InferenceVariableType implements ResolvedType { + @Override + public String toString() { + return "InferenceVariableType{" + + "id=" + id + + '}'; + } + + private int id; + private ResolvedTypeParameterDeclaration correspondingTp; + + public void setCorrespondingTp(ResolvedTypeParameterDeclaration correspondingTp) { + this.correspondingTp = correspondingTp; + } + + private Set<ResolvedType> equivalentTypes = new HashSet<>(); + private ObjectProvider objectProvider; + + public void registerEquivalentType(ResolvedType type) { + this.equivalentTypes.add(type); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof InferenceVariableType)) return false; + + InferenceVariableType that = (InferenceVariableType) o; + + return id == that.id; + + } + + @Override + public int hashCode() { + return id; + } + + private Set<ResolvedType> superTypes = new HashSet<>(); + + public InferenceVariableType(int id, ObjectProvider objectProvider) { + this.id = id; + this.objectProvider = objectProvider; + } + + public static InferenceVariableType fromWildcard(ResolvedWildcard wildcard, int id, ObjectProvider objectProvider) { + InferenceVariableType inferenceVariableType = new InferenceVariableType(id, objectProvider); + if (wildcard.isExtends()) { + inferenceVariableType.superTypes.add(wildcard.getBoundedType()); + } + if (wildcard.isSuper()) { + // I am not sure about this one... + inferenceVariableType.superTypes.add(wildcard.getBoundedType()); + } + return inferenceVariableType; + } + + @Override + public String describe() { + return "InferenceVariable_" + id; + } + + @Override + public boolean isAssignableBy(ResolvedType other) { + throw new UnsupportedOperationException(); + } + + private Set<ResolvedType> concreteEquivalentTypesAlsoIndirectly(Set<InferenceVariableType> considered, InferenceVariableType inferenceVariableType) { + considered.add(inferenceVariableType); + Set<ResolvedType> result = new HashSet<>(); + result.addAll(inferenceVariableType.equivalentTypes.stream().filter(t -> !t.isTypeVariable() && !(t instanceof InferenceVariableType)).collect(Collectors.toSet())); + inferenceVariableType.equivalentTypes.stream().filter(t -> t instanceof InferenceVariableType).forEach(t -> { + InferenceVariableType ivt = (InferenceVariableType)t; + if (!considered.contains(ivt)) { + result.addAll(concreteEquivalentTypesAlsoIndirectly(considered, ivt)); + } + }); + return result; + } + + public ResolvedType equivalentType() { + Set<ResolvedType> concreteEquivalent = concreteEquivalentTypesAlsoIndirectly(new HashSet<>(), this); + if (concreteEquivalent.isEmpty()) { + if (correspondingTp == null) { + return objectProvider.object(); + } else { + return new ResolvedTypeVariable(correspondingTp); + } + } + if (concreteEquivalent.size() == 1) { + return concreteEquivalent.iterator().next(); + } + Set<ResolvedType> notTypeVariables = equivalentTypes.stream() + .filter(t -> !t.isTypeVariable() && !hasInferenceVariables(t)) + .collect(Collectors.toSet()); + if (notTypeVariables.size() == 1) { + return notTypeVariables.iterator().next(); + } else if (notTypeVariables.size() == 0 && !superTypes.isEmpty()) { + if (superTypes.size() == 1) { + return superTypes.iterator().next(); + } else { + throw new IllegalStateException("Super types are: " + superTypes); + } + } else { + throw new IllegalStateException("Equivalent types are: " + equivalentTypes); + } + } + + private boolean hasInferenceVariables(ResolvedType type){ + if (type instanceof InferenceVariableType){ + return true; + } + + if (type.isReferenceType()){ + ResolvedReferenceType refType = type.asReferenceType(); + for (ResolvedType t : refType.typeParametersValues()){ + if (hasInferenceVariables(t)){ + return true; + } + } + return false; + } + + if (type.isWildcard()){ + ResolvedWildcard wildcardType = type.asWildcard(); + return hasInferenceVariables(wildcardType.getBoundedType()); + } + + return false; + } +} diff --git a/javaparser-symbol-solver-logic/src/main/java/com/github/javaparser/symbolsolver/logic/ObjectProvider.java b/javaparser-symbol-solver-logic/src/main/java/com/github/javaparser/symbolsolver/logic/ObjectProvider.java new file mode 100644 index 000000000..01b5c3ced --- /dev/null +++ b/javaparser-symbol-solver-logic/src/main/java/com/github/javaparser/symbolsolver/logic/ObjectProvider.java @@ -0,0 +1,27 @@ +/* + * Copyright 2016 Federico Tomassetti + * + * 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.github.javaparser.symbolsolver.logic; + +import com.github.javaparser.resolution.types.ResolvedReferenceType; + +/** + * @author Federico Tomassetti + */ +public interface ObjectProvider { + ResolvedReferenceType object(); + ResolvedReferenceType byName(String qname); +} |