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