/**
* Copyright (C) 2015 Valkyrie RCP
*
* 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 org.valkyriercp.application.exceptionhandling;
import com.google.common.collect.Lists;
import java.util.Collections;
import java.util.List;
/**
* A purger that looks through to a throwable chain and can select one to unwrap.
*
* @author Geoffrey De Smet
* @since 0.3.0
*/
public class DefaultExceptionPurger<SELF extends DefaultExceptionPurger<SELF>> implements ExceptionPurger {
protected List<Class<? extends Throwable>> includeThrowableClassList = Collections.emptyList();
protected List<Class<? extends Throwable>> excludeThrowableClassList = Collections.emptyList();
public DefaultExceptionPurger() {}
protected final SELF self() {
return (SELF) this;
}
public DefaultExceptionPurger(Class<? extends Throwable> includeThrowableClass, Class<? extends Throwable> excludeThrowableClass) {
if (includeThrowableClass != null) {
setIncludeThrowableClasses(includeThrowableClass);
}
if (excludeThrowableClass != null) {
setExcludeThrowableClasses(excludeThrowableClass);
}
}
public DefaultExceptionPurger(List<Class<? extends Throwable>> includeThrowableClassList, List<Class<? extends Throwable>> excludeThrowableClassList) {
if (includeThrowableClassList != null) {
this.includeThrowableClassList = includeThrowableClassList;
}
if (excludeThrowableClassList != null) {
this.excludeThrowableClassList = excludeThrowableClassList;
}
}
public SELF including(Class includeThrowableClass) {
setIncludeThrowableClasses(includeThrowableClass);
return self();
}
/**
* Sets Throwables that if found, are unwrapped.
* These Throwables are ussually very specific exceptions, for example: LoginCredentialsExpiredException.
* The earliest throwable found is selected.
* </p>
* Given a chain A1->B1->C1->B2->D1:
* {A} returns A1;
* {B} returns B1;
* {D} returns D1;
* {Z) returns A1;
* {C, Z} returns C1;
* {B, D} returns B1;
* {D, B} return B1;
* </p>
* When combined, includeThrowableClassList takes priority over excludeThrowableClassList.
* @param includeThrowableClassList a list of classes
*/
public void setIncludeThrowableClasses(Class<? extends Throwable>... includeThrowableClassList) {
this.includeThrowableClassList = Lists.newArrayList(includeThrowableClassList);
}
public SELF including(Class<? extends Throwable>... throwableClasses) {
setIncludeThrowableClasses(throwableClasses);
return self();
}
/**
* See @{link {@link #setExcludeThrowableClasses(Class...)}.
* @param excludeThrowableClass used as a singleton list for excludeThrowableClassList
*/
public SELF excluding(Class<? extends Throwable> excludeThrowableClass) {
setExcludeThrowableClasses(excludeThrowableClass);
return self();
}
/**
* Sets Throwables that if found, its cause is unwrapped.
* These Throwables are ussually very general wrapper exceptions, for example: WrapperException.
* The last throwable found's cause is selected.
* If the cause is null, itself is selected.
* </p>
* Given a chain A1->B1->C1->B2->D1:
* {A} returns B1;
* {B} returns D1;
* {D} returns D1;
* {Z) returns A1;
* {C, Z} returns B2;
* {C, D} returns D1;
* {D, C} return D1;
* </p>
* When combined, includeThrowableClassList takes priority over excludeThrowableClassList.
* @param throwableClasses a list of classes
*/
public void setExcludeThrowableClasses(Class<? extends Throwable>... throwableClasses) {
this.excludeThrowableClassList = Lists.newArrayList(throwableClasses);
}
public Throwable purge(Throwable root) {
Throwable excludedPurged = root;
Throwable e = root;
while (e != null) {
if (containedIn(e, includeThrowableClassList)) {
return e;
}
boolean excludedContained = containedIn(e, excludeThrowableClassList);
if (excludedContained) {
excludedPurged = e; // in case the cause is null
}
// get cause of e (and null at end of chain)
e = (e.getCause() == e) ? null : e.getCause();
if (excludedContained && e != null) {
excludedPurged = e; // in case the cause is not null
}
}
return excludedPurged;
}
// public Throwable purge(Throwable root) {
// Throwable purged = root;
// Throwable e = root;
// while (containedIn(e, excludeThrowableClassList)) {
// // get cause of e (and null at end of chain)
// e = (e.getCause() == e) ? null : e.getCause();
// if (e == null) {
// break;
// }
// purged = e;
// }
// return purged;
// }
protected boolean containedIn(Throwable e, List<Class<? extends Throwable>> throwableClassList) {
for (Class throwableClass : throwableClassList) {
if (throwableClass.isInstance(e)) {
return true;
}
}
return false;
}
}