Skip to content

Commit

Permalink
Add an exception filter to the CombinedTypeSolver
Browse files Browse the repository at this point in the history
  • Loading branch information
HoldYourWaffle committed Dec 3, 2018
1 parent 9c68517 commit 26498c2
Show file tree
Hide file tree
Showing 2 changed files with 211 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,15 @@

package com.github.javaparser.symbolsolver.resolution.typesolvers;

import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;

import com.github.javaparser.resolution.UnsolvedSymbolException;
import com.github.javaparser.resolution.declarations.ResolvedReferenceTypeDeclaration;
import com.github.javaparser.symbolsolver.model.resolution.SymbolReference;
import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;

import java.util.ArrayList;
import java.util.List;

/**
* A container for type solvers. All solving is done by the contained type solvers.
* This helps you when an API asks for a single type solver, but you need several.
Expand All @@ -34,13 +35,29 @@ public class CombinedTypeSolver implements TypeSolver {

private TypeSolver parent;
private List<TypeSolver> elements = new ArrayList<>();
private Predicate<Exception> errorFilter;

public CombinedTypeSolver(TypeSolver... elements) {
this(ExceptionFilters.IGNORE_ALL, elements);
}

/** @see #setFilter(Predicate) */
public CombinedTypeSolver(Predicate<Exception> errorFilter, TypeSolver... elements) {
setFilter(errorFilter);

for (TypeSolver el : elements) {
add(el);
}
}

/**
* @param errorFilter A filter which determines if an exception raised while solving should be ignored.
* Should return <code>true</code> when the exception must be <b>ignored</b>.
*/
public void setFilter(Predicate<Exception> errorFilter) {
this.errorFilter = errorFilter;
}

@Override
public TypeSolver getParent() {
return parent;
Expand All @@ -59,9 +76,15 @@ public void add(TypeSolver typeSolver) {
@Override
public SymbolReference<ResolvedReferenceTypeDeclaration> tryToSolveType(String name) {
for (TypeSolver ts : elements) {
SymbolReference<ResolvedReferenceTypeDeclaration> res = ts.tryToSolveType(name);
if (res.isSolved()) {
return res;
try {
SymbolReference<ResolvedReferenceTypeDeclaration> res = ts.tryToSolveType(name);
if (res.isSolved()) {
return res;
}
} catch (Exception e) {
if (!errorFilter.test(e)) { // we shouldn't ignore this exception
throw e;
}
}
}
return SymbolReference.unsolved(ResolvedReferenceTypeDeclaration.class);
Expand All @@ -76,4 +99,81 @@ public ResolvedReferenceTypeDeclaration solveType(String name) throws UnsolvedSy
throw new UnsolvedSymbolException(name);
}
}

/** Provides some convenience filter implementations */
public static class ExceptionFilters {

/** Doesn't ignore any exceptions (default) */
public static final Predicate<Exception> IGNORE_NONE = e -> false;

/** Ignores all exceptions */
public static final Predicate<Exception> IGNORE_ALL = e -> true;

/**
* Ignores any exception that is {@link Class#isAssignableFrom(Class) assignable from}
* {@link UnsupportedOperationException}.
*
* @see #getTypeBasedWhitelist(Class...)
*/
public static final Predicate<Exception> IGNORE_UNSUPPORTED_OPERATION = getTypeBasedWhitelist(
UnsupportedOperationException.class);

/**
* Ignores any exception that is {@link Class#isAssignableFrom(Class) assignable from}
* {@link UnsolvedSymbolException}.
*
* @see #getTypeBasedWhitelist(Class...)
*/
public static final Predicate<Exception> IGNORE_UNSOLVED_SYMBOL = getTypeBasedWhitelist(
UnsolvedSymbolException.class);

/**
* Ignores any exception that is {@link Class#isAssignableFrom(Class) assignable from} either
* {@link UnsolvedSymbolException} or {@link UnsupportedOperationException}.
*
* @see #IGNORE_UNSOLVED_SYMBOL
* @see #IGNORE_UNSUPPORTED_OPERATION
* @see #getTypeBasedWhitelist(Class...)
*/
public static final Predicate<Exception> IGNORE_UNSUPPORTED_AND_UNSOLVED = getTypeBasedWhitelist(
UnsupportedOperationException.class, UnsolvedSymbolException.class);

/**
* @see CombinedTypeSolver#setFilter(Predicate)
* @see #getTypeBasedWhitelist(Class...)
*
* @return A filter that ignores an exception if <b>none</b> of the listed classes are
* {@link Class#isAssignableFrom(Class) assignable from}
* the thrown exception class.
*/
public static Predicate<Exception> getTypeBasedBlacklist(Class<? extends Exception>... blacklist) {
return e -> {
for (Class<? extends Exception> clazz : blacklist) {
if (clazz.isAssignableFrom(e.getClass())) {
return false;
}
}
return true;
};
}

/**
* @see CombinedTypeSolver#setFilter(Predicate)
* @see #getTypeBasedBlacklist(Class...)
*
* @return A filter that ignores an exception if <b>any</b> of the listed classes are
* {@link Class#isAssignableFrom(Class) assignable from}
* the thrown exception class.
*/
public static Predicate<Exception> getTypeBasedWhitelist(Class<? extends Exception>... whitelist) {
return e -> {
for (Class<? extends Exception> clazz : whitelist) {
if (clazz.isAssignableFrom(e.getClass())) {
return true;
}
}
return false;
};
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
/*
* 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.resolution.typesolvers;

import static org.junit.Assert.*;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;

import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameter;
import org.junit.runners.Parameterized.Parameters;

import com.github.javaparser.resolution.declarations.ResolvedReferenceTypeDeclaration;
import com.github.javaparser.symbolsolver.model.resolution.SymbolReference;
import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
import com.github.javaparser.symbolsolver.resolution.typesolvers.CombinedTypeSolver.ExceptionFilters;

@RunWith(Parameterized.class)
public class CombinedTypeSolverTest {

@Parameters
public static List<Object[]> parameters() {
// Why these classes? NFE is a subclass, IOOBE is a superclass and ISE is a class without children (by default)
Predicate<Exception> whitelistTestFilter = ExceptionFilters.getTypeBasedWhitelist(NumberFormatException.class,
IndexOutOfBoundsException.class, IllegalStateException.class);
Predicate<Exception> blacklistTestFilter = ExceptionFilters.getTypeBasedBlacklist(NumberFormatException.class,
IndexOutOfBoundsException.class, IllegalStateException.class);

return Arrays.asList(new Object[][] {
{ new RuntimeException(), ExceptionFilters.IGNORE_ALL, true }, // 0
{ new RuntimeException(), ExceptionFilters.IGNORE_NONE, false }, // 1

{ new RuntimeException(), whitelistTestFilter, false }, // 2
{ new IllegalStateException(), whitelistTestFilter, true }, // 3

{ new NumberFormatException(), whitelistTestFilter, true }, // 4
{ new IllegalArgumentException(), whitelistTestFilter, false }, // 5

{ new IndexOutOfBoundsException(), whitelistTestFilter, true }, // 6
{ new ArrayIndexOutOfBoundsException(), whitelistTestFilter, true }, // 7

{ new RuntimeException(), blacklistTestFilter, true }, // 8
{ new NullPointerException(), blacklistTestFilter, true }, // 9
{ new IllegalStateException(), blacklistTestFilter, false }, // 10

{ new NumberFormatException(), blacklistTestFilter, false }, // 11
{ new IllegalArgumentException(), blacklistTestFilter, true }, // 12

{ new IndexOutOfBoundsException(), blacklistTestFilter, false }, // 13
{ new ArrayIndexOutOfBoundsException(), blacklistTestFilter, false }, // 14
});
}

@Parameter(0)
public Exception toBeThrownException;

@Parameter(1)
public Predicate<Exception> filter;

@Parameter(2)
public boolean expectForward;

@Test
public void testExceptionFilter() {
TypeSolver erroringTypeSolver = mock(TypeSolver.class);
doThrow(toBeThrownException).when(erroringTypeSolver).tryToSolveType(any(String.class));

TypeSolver secondaryTypeSolver = mock(TypeSolver.class);
when(secondaryTypeSolver.tryToSolveType(any(String.class)))
.thenReturn(SymbolReference.unsolved(ResolvedReferenceTypeDeclaration.class));

try {
new CombinedTypeSolver(filter, erroringTypeSolver, secondaryTypeSolver)
.tryToSolveType("an uninteresting string");
assertTrue("Forwarded, but we expected an exception", expectForward);
} catch (Exception e) {
assertFalse("Exception, but we expected forwarding", expectForward); // if we expected the error to be
// forwarded there shouldn't be an
// exception
}

verify(secondaryTypeSolver, times(expectForward ? 1 : 0)).tryToSolveType(any(String.class));
}

}

0 comments on commit 26498c2

Please sign in to comment.