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    package org.apache.commons.io;
018    
019    import java.io.IOException;
020    import 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    public 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    }