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 *      http://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.Collections;
022import java.util.Iterator;
023import java.util.List;
024import java.util.Objects;
025
026/**
027 * An IOException based on a list of Throwable causes.
028 * <p>
029 * The first exception in the list is used as this exception's cause and is accessible with the usual
030 * {@link #getCause()} while the complete list is accessible with {@link #getCauseList()}.
031 * </p>
032 *
033 * @since 2.7
034 */
035public class IOExceptionList extends IOException implements Iterable<Throwable> {
036
037    private static final long serialVersionUID = 1L;
038
039    /**
040     * Throws this exception if the list is not null or empty.
041     *
042     * @param causeList The list to test.
043     * @param message The detail message, see {@link #getMessage()}.
044     * @throws IOExceptionList if the list is not null or empty.
045     * @since 2.12.0
046     */
047    public static void checkEmpty(final List<? extends Throwable> causeList, final Object message) throws IOExceptionList {
048        if (!isEmpty(causeList)) {
049            throw new IOExceptionList(Objects.toString(message, null), causeList);
050        }
051    }
052
053    private static boolean isEmpty(final List<? extends Throwable> causeList) {
054        return size(causeList) == 0;
055    }
056
057    private static int size(final List<? extends Throwable> causeList) {
058        return causeList != null ? causeList.size() : 0;
059    }
060
061    private static String toMessage(final List<? extends Throwable> causeList) {
062        return String.format("%,d exception(s): %s", size(causeList), causeList);
063    }
064
065    private final List<? extends Throwable> causeList;
066
067    /**
068     * Constructs a new exception caused by a list of exceptions.
069     *
070     * @param causeList a list of cause exceptions.
071     */
072    public IOExceptionList(final List<? extends Throwable> causeList) {
073        this(toMessage(causeList), causeList);
074    }
075
076    /**
077     * Constructs a new exception caused by a list of exceptions.
078     *
079     * @param message The detail message, see {@link #getMessage()}.
080     * @param causeList a list of cause exceptions.
081     * @since 2.9.0
082     */
083    public IOExceptionList(final String message, final List<? extends Throwable> causeList) {
084        super(message != null ? message : toMessage(causeList), isEmpty(causeList) ? null : causeList.get(0));
085        this.causeList = causeList == null ? Collections.emptyList() : causeList;
086    }
087
088    /**
089     * Gets the cause exception at the given index.
090     *
091     * @param <T> type of exception to return.
092     * @param index index in the cause list.
093     * @return The list of causes.
094     */
095    public <T extends Throwable> T getCause(final int index) {
096        return (T) causeList.get(index);
097    }
098
099    /**
100     * Gets the cause exception at the given index.
101     *
102     * @param <T> type of exception to return.
103     * @param index index in the cause list.
104     * @param clazz type of exception to return.
105     * @return The list of causes.
106     */
107    public <T extends Throwable> T getCause(final int index, final Class<T> clazz) {
108        return clazz.cast(getCause(index));
109    }
110
111    /**
112     * Gets the cause list.
113     *
114     * @param <T> type of exception to return.
115     * @return The list of causes.
116     */
117    public <T extends Throwable> List<T> getCauseList() {
118        return (List<T>) causeList;
119    }
120
121    /**
122     * Works around Throwable and Generics, may fail at runtime depending on the argument value.
123     *
124     * @param <T> type of exception to return.
125     * @param clazz the target type
126     * @return The list of causes.
127     */
128    public <T extends Throwable> List<T> getCauseList(final Class<T> clazz) {
129        return (List<T>) causeList;
130    }
131
132    @Override
133    public Iterator<Throwable> iterator() {
134        return getCauseList().iterator();
135    }
136
137}