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.vfs2;
018
019import java.io.IOException;
020import java.util.regex.Matcher;
021import java.util.regex.Pattern;
022
023import org.apache.commons.lang3.ArrayUtils;
024import org.apache.commons.vfs2.util.Messages;
025
026/**
027 * Thrown for file system errors.
028 */
029public class FileSystemException extends IOException {
030
031    /**
032     * serialVersionUID format is YYYYMMDD for the date of the last binary change.
033     */
034    private static final long serialVersionUID = 20101208L;
035
036    /** URL pattern */
037    private static final Pattern URL_PATTERN = Pattern.compile("[a-z]+://.*");
038
039    /** Password pattern */
040    private static final Pattern PASSWORD_PATTERN = Pattern.compile(":(?:[^/]+)@");
041
042    /**
043     * Throws a FileSystemException when the given object is null.
044     *
045     * @param obj
046     *            the object reference to check for null.
047     * @param code
048     *            message used when {@code
049     *                FileSystemException} is thrown
050     * @param <T>
051     *            the type of the reference
052     * @return {@code obj} if not {@code null}
053     * @throws FileSystemException
054     *             if {@code obj} is {@code null}
055     * @since 2.3
056     */
057    public static <T> T requireNonNull(final T obj, final String code) throws FileSystemException {
058        if (obj == null) {
059            throw new FileSystemException(code);
060        }
061        return obj;
062    }
063
064    /**
065     * Throws a FileSystemException when the given object is null.
066     *
067     * @param obj
068     *            the object reference to check for null.
069     * @param code
070     *            message used when {@code
071     *                FileSystemException} is thrown
072     * @param info
073     *            one context information.
074     * @param <T>
075     *            the type of the reference
076     * @return {@code obj} if not {@code null}
077     * @throws FileSystemException
078     *             if {@code obj} is {@code null}
079     * @since 2.3
080     */
081    public static <T> T requireNonNull(final T obj, final String code, final Object... info) throws FileSystemException {
082        if (obj == null) {
083            throw new FileSystemException(code, info);
084        }
085        return obj;
086    }
087
088    /**
089     * Array of complementary info (context).
090     */
091    private final String[] info;
092
093    /**
094     * Constructs exception with the specified detail message.
095     *
096     * @param code the error code of the message.
097     */
098    public FileSystemException(final String code) {
099        this(code, null, (Object[]) null);
100    }
101
102    /**
103     * Constructs exception with the specified detail message.
104     *
105     * @param code the error code of the message.
106     * @param info one context information.
107     */
108    public FileSystemException(final String code, final Object info) {
109        this(code, null, new Object[] {info});
110    }
111
112    /**
113     * Constructs exception with the specified detail message.
114     *
115     * @param code the error code of the message.
116     * @param info array of complementary info (context).
117     */
118    public FileSystemException(final String code, final Object... info) {
119        this(code, null, info);
120    }
121
122    /**
123     * Constructs exception with the specified detail message and cause.
124     *
125     * @param code the error code of the message.
126     * @param info one context information.
127     * @param cause the cause.
128     */
129    public FileSystemException(final String code, final Object info, final Throwable cause) {
130        this(code, cause, info);
131    }
132
133    /**
134     * Constructs exception with the specified detail message.
135     *
136     * @param code the error code of the message.
137     * @param info array of complementary info (context).
138     * @param cause the cause.
139     * @deprecated Use instead {@link #FileSystemException(String, Throwable, Object[])}. Will be removed in 3.0.
140     */
141    @Deprecated
142    public FileSystemException(final String code, final Object[] info, final Throwable cause) {
143        this(code, cause, info);
144    }
145
146    /**
147     * Constructs exception with the specified detail message.
148     *
149     * @param code the error code of the message.
150     * @param cause the original cause
151     */
152    public FileSystemException(final String code, final Throwable cause) {
153        this(code, cause, (Object[]) null);
154    }
155
156    /**
157     * Constructs exception with the specified detail message.
158     *
159     * @param code the error code of the message.
160     * @param info array of complementary info (context).
161     * @param cause the cause.
162     */
163    public FileSystemException(final String code, final Throwable cause, final Object... info) {
164        super(code, cause);
165
166        if (info == null) {
167            this.info = ArrayUtils.EMPTY_STRING_ARRAY;
168        } else {
169            this.info = new String[info.length];
170            for (int i = 0; i < info.length; i++) {
171                String value = String.valueOf(info[i]);
172                // mask passwords (VFS-169)
173                final Matcher urlMatcher = URL_PATTERN.matcher(value);
174                if (urlMatcher.find()) {
175                    value = PASSWORD_PATTERN.matcher(value).replaceFirst(":***@");
176                }
177                this.info[i] = value;
178            }
179        }
180    }
181
182    /**
183     * Constructs wrapper exception.
184     *
185     * @param cause the root cause to wrap.
186     */
187    public FileSystemException(final Throwable cause) {
188        this(cause.getMessage(), cause, (Object[]) null);
189    }
190
191    /**
192     * Retrieves error code of the exception. Could be used as key for internationalization.
193     *
194     * @return the code.
195     */
196    public String getCode() {
197        return super.getMessage();
198    }
199
200    /**
201     * Retrieves array of complementary info (context). Could be used as parameter for internationalization.
202     *
203     * @return the context info.
204     */
205    public String[] getInfo() {
206        return info;
207    }
208
209    /**
210     * Retrieves message from bundle.
211     *
212     * @return The exception message.
213     */
214    @Override
215    public String getMessage() {
216        return Messages.getString(super.getMessage(), (Object[]) getInfo());
217    }
218}