/*
 * @author Ranides Atterwim <ranides@gmail.com>
 * @copyright Ranides Atterwim
 * @license WTFPL
 * @url http://ranides.net/projects/assira
 */
package net.ranides.assira.trace;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.List;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
import net.ranides.assira.collection.iterators.ForwardSpliterator;

/**
 *
 * @author ranides
 */
public final class ExceptionUtils {

    private ExceptionUtils() { }
	
	public static Stream<Throwable> getCauseStream(Throwable cause) {
		return StreamSupport.stream(new CauseSpliterator(cause), false);
	}
	
	public static List<Throwable> getCauseList(Throwable cause) {
		return getCauseStream(cause).collect(Collectors.toList());
    }

    public static String getStackTrace(Throwable cause) {
        StringWriter result = new StringWriter();
        PrintWriter writer = new PrintWriter(result);
        cause.printStackTrace(writer);
        writer.flush();
        return result.toString();
    }

    public static RuntimeException rethrow(Throwable cause) {
        new ExceptionErasure<RuntimeException>().rethrow(cause);
        
        // not reachable, wyjątek bez wrappowania leci linię wyżej
        throw new RuntimeException(cause);  // NOPMD
    }
    
    private static class ExceptionErasure<T extends Throwable> { // NOPMD
        
        @SuppressWarnings("unchecked")
        private void rethrow(Throwable exception) throws T {
            throw (T) exception;
        }

    }
	
	private static final class CauseSpliterator extends ForwardSpliterator<Throwable> {
		
		private Throwable current;
		
		public CauseSpliterator(Throwable current) {
			this.current = current;
		}

		@Override
		public boolean tryAdvance(Consumer<? super Throwable> action) {
            if(null == current) {
                return false;
            }
            action.accept(current);
            current = current.getCause();
            return true;
		}

	}
    
}
