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 */
017package org.apache.commons.io;
018
019import java.io.IOException;
020import java.io.Serializable;
021
022/**
023 * An {@link IOException} decorator that adds a serializable tag to the
024 * wrapped exception. Both the tag and the original exception can be used
025 * to determine further processing when this exception is caught.
026 *
027 * @since 2.0
028 */
029@SuppressWarnings("deprecation") // needs to extend deprecated IOExceptionWithCause to preserve binary compatibility
030public class TaggedIOException extends IOExceptionWithCause {
031
032    /**
033     * Generated serial version UID.
034     */
035    private static final long serialVersionUID = -6994123481142850163L;
036
037    /**
038     * Checks whether the given throwable is tagged with the given tag.
039     * <p>
040     * This check can only succeed if the throwable is a
041     * {@link TaggedIOException} and the tag is {@link Serializable}, but
042     * the argument types are intentionally more generic to make it easier
043     * to use this method without type casts.
044     * <p>
045     * A typical use for this method is in a {@code catch} block to
046     * determine how a caught exception should be handled:
047     * <pre>
048     * Serializable tag = ...;
049     * try {
050     *     ...;
051     * } catch (Throwable t) {
052     *     if (TaggedIOException.isTaggedWith(t, tag)) {
053     *         // special processing for tagged exception
054     *     } else {
055     *         // handling of other kinds of exceptions
056     *     }
057     * }
058     * </pre>
059     *
060     * @param throwable The Throwable object to check
061     * @param tag tag object
062     * @return {@code true} if the throwable has the specified tag,
063     * otherwise {@code false}
064     */
065    public static boolean isTaggedWith(final Throwable throwable, final Object tag) {
066        return tag != null
067            && throwable instanceof TaggedIOException
068            && tag.equals(((TaggedIOException) throwable).tag);
069    }
070
071    /**
072     * Throws the original {@link IOException} if the given throwable is
073     * a {@link TaggedIOException} decorator the given tag. Does nothing
074     * if the given throwable is of a different type or if it is tagged
075     * with some other tag.
076     * <p>
077     * This method is typically used in a {@code catch} block to
078     * selectively rethrow tagged exceptions.
079     * <pre>
080     * Serializable tag = ...;
081     * try {
082     *     ...;
083     * } catch (Throwable t) {
084     *     TaggedIOException.throwCauseIfTagged(t, tag);
085     *     // handle other kinds of exceptions
086     * }
087     * </pre>
088     *
089     * @param throwable an exception
090     * @param tag tag object
091     * @throws IOException original exception from the tagged decorator, if any
092     */
093    public static void throwCauseIfTaggedWith(final Throwable throwable, final Object tag)
094            throws IOException {
095        if (isTaggedWith(throwable, tag)) {
096            throw ((TaggedIOException) throwable).getCause();
097        }
098    }
099
100    /**
101     * The tag of this exception.
102     */
103    private final Serializable tag;
104
105    /**
106     * Creates a tagged wrapper for the given exception.
107     *
108     * @param original the exception to be tagged
109     * @param tag tag of this exception
110     */
111    public TaggedIOException(final IOException original, final Serializable tag) {
112        super(original.getMessage(), original);
113        this.tag = tag;
114    }
115
116    /**
117     * Returns the serializable tag object.
118     *
119     * @return tag object
120     */
121    public Serializable getTag() {
122        return tag;
123    }
124
125    /**
126     * Returns the wrapped exception. The only difference to the overridden
127     * {@link Throwable#getCause()} method is the narrower return type.
128     *
129     * @return wrapped exception
130     */
131    @Override
132    public synchronized IOException getCause() {
133        return (IOException) super.getCause();
134    }
135
136}