    001/*
    002 * Licensed to the Apache Software Foundation (ASF) under one or more
    003 * contributor license agreements.  See the NOTICE file distributed with
    004 * this work for additional information regarding copyright ownership.
    005 * The ASF licenses this file to You under the Apache License, Version 2.0
    006 * (the "License"); you may not use this file except in compliance with
    007 * the License.  You may obtain a copy of the License at
    008 *
    009 *      https://www.apache.org/licenses/LICENSE-2.0
    010 *
    011 * Unless required by applicable law or agreed to in writing, software
    012 * distributed under the License is distributed on an "AS IS" BASIS,
    013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    014 * See the License for the specific language governing permissions and
    015 * limitations under the License.
    016 */
    017
    018package org.apache.commons.io;
    019
    020import java.io.IOException;
    021import java.util.ArrayList;
    022import java.util.Collections;
    023import java.util.Iterator;
    024import java.util.List;
    025import java.util.Objects;
    026
    027/**
    028 * An IOException based on a list of Throwable causes.
    029 * <p>
    030 * The first exception in the list is used as this exception's cause and is accessible with the usual
    031 * {@link #getCause()} while the complete list is accessible with {@link #getCauseList()}.
    032 * </p>
    033 *
    034 * @since 2.7
    035 */
    036public class IOExceptionList extends IOException implements Iterable<Throwable> {
    037
    038    private static final long serialVersionUID = 1L;
    039
    040    /**
    041     * Throws this exception if the list is not null or empty.
    042     *
    043     * @param causeList The list to test.
    044     * @param message The detail message, see {@link #getMessage()}.
    045     * @throws IOExceptionList if the list is not null or empty.
    046     * @since 2.12.0
    047     */
    048    public static void checkEmpty(final List<? extends Throwable> causeList, final Object message) throws IOExceptionList {
    049        if (!isEmpty(causeList)) {
    050            throw new IOExceptionList(Objects.toString(message, null), causeList);
    051        }
    052    }
    053
    054    private static boolean isEmpty(final List<? extends Throwable> causeList) {
    055        return size(causeList) == 0;
    056    }
    057
    058    private static int size(final List<? extends Throwable> causeList) {
    059        return causeList != null ? causeList.size() : 0;
    060    }
    061
    062    private static String toMessage(final List<? extends Throwable> causeList) {
    063        return String.format("%,d exception(s): %s", size(causeList), causeList);
    064    }
    065
    066    /**
    067     * List of causes.
    068     */
    069    private final List<? extends Throwable> causeList;
    070
    071    /**
    072     * Constructs a new exception caused by a list of exceptions.
    073     *
    074     * @param causeList a list of cause exceptions.
    075     */
    076    public IOExceptionList(final List<? extends Throwable> causeList) {
    077        this(toMessage(causeList), causeList);
    078    }
    079
    080    /**
    081     * Constructs a new exception caused by a list of exceptions.
    082     *
    083     * @param message The detail message, see {@link #getMessage()}.
    084     * @param causeList a list of cause exceptions.
    085     * @since 2.9.0
    086     */
    087    public IOExceptionList(final String message, final List<? extends Throwable> causeList) {
    088        super(message != null ? message : toMessage(causeList), isEmpty(causeList) ? null : causeList.get(0));
    089        this.causeList = causeList == null ? Collections.emptyList() : causeList;
    090    }
    091
    092    /**
    093     * Gets the cause exception at the given index.
    094     *
    095     * @param <T> type of exception to return.
    096     * @param index index in the cause list.
    097     * @return The list of causes.
    098     */
    099    public <T extends Throwable> T getCause(final int index) {
    100        return (T) causeList.get(index);
    101    }
    102
    103    /**
    104     * Gets the cause exception at the given index.
    105     *
    106     * @param <T> type of exception to return.
    107     * @param index index in the cause list.
    108     * @param clazz type of exception to return.
    109     * @return The list of causes.
    110     */
    111    public <T extends Throwable> T getCause(final int index, final Class<T> clazz) {
    112        return clazz.cast(getCause(index));
    113    }
    114
    115    /**
    116     * Gets the cause list.
    117     *
    118     * @param <T> type of exception to return.
    119     * @return The list of causes.
    120     */
    121    public <T extends Throwable> List<T> getCauseList() {
    122        return (List<T>) new ArrayList<>(causeList);
    123    }
    124
    125    /**
    126     * Works around Throwable and Generics, may fail at runtime depending on the argument value.
    127     *
    128     * @param <T> type of exception to return.
    129     * @param clazz the target type
    130     * @return The list of causes.
    131     */
    132    public <T extends Throwable> List<T> getCauseList(final Class<T> clazz) {
    133        return (List<T>) new ArrayList<>(causeList);
    134    }
    135
    136    @Override
    137    public Iterator<Throwable> iterator() {
    138        return getCauseList().iterator();
    139    }
    140
    141}