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 *     https://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.configuration2.io;
018
019import java.net.URL;
020import java.util.Objects;
021
022/**
023 * <p>
024 * A class describing the location of a file.
025 * </p>
026 * <p>
027 * An instance of this class provides information for locating and accessing a file. The file location can be defined
028 * </p>
029 * <ul>
030 * <li>as a URL; this identifies a file in a unique way</li>
031 * <li>as a combination of base path and file name; if this variant is used, there may be an additional location step
032 * required in order to identify the referenced file (for instance, the file name may be interpreted as the name of a
033 * resource to be loaded from class path).</li>
034 * </ul>
035 * <p>
036 * In addition, other properties are available which are also needed for loading or saving a file, like the underlying
037 * {@link FileSystem}. The encoding to be used when accessing the represented data is also part of the data contained in
038 * an instance; if no encoding is set explicitly, the platform's default encoding is used.
039 * <p>
040 * Instances of this class are immutable and thus can be safely shared between arbitrary components. {@link FileHandler}
041 * also uses an instance to reference the associated file. Instances are created using a <em>builder</em>.
042 * {@link FileLocatorUtils} offers convenience methods for obtaining such a builder.
043 * </p>
044 *
045 * @since 2.0
046 */
047public final class FileLocator {
048
049    /**
050     * A typical <em>builder</em> implementation for creating {@code FileLocator} objects. An instance of this class is
051     * returned by the {@code fileLocator()} method of {link FileLocatorUtils}. It can be used to define the various
052     * components of the {@code FileLocator} object. By calling {@code create()} the new immutable {@code FileLocator}
053     * instance is created.
054     */
055    public static final class FileLocatorBuilder {
056
057        /** The base path. */
058        private String basePath;
059
060        /** The encoding. */
061        private String encoding;
062
063        /** The file name. */
064        private String fileName;
065
066        /** The file system. */
067        private FileSystem fileSystem;
068
069        /** The location strategy. */
070        private FileLocationStrategy locationStrategy;
071
072        /** The URL. */
073        private URL sourceURL;
074
075        /** The URL connection options. */
076        private URLConnectionOptions urlConnectionOptions;
077
078        /**
079         * Creates a new instance of {@code FileLocatorBuilder} and initializes the builder's properties from the passed in
080         * {@code FileLocator} object.
081         *
082         * @param src the source {@code FileLocator} (may be <strong>null</strong>)
083         */
084        FileLocatorBuilder(final FileLocator src) {
085            if (src != null) {
086                initBuilder(src);
087            }
088        }
089
090        /**
091         * Specifies the base path of the new {@code FileLocator}.
092         *
093         * @param path the base path
094         * @return a reference to this builder for method chaining
095         */
096        public FileLocatorBuilder basePath(final String path) {
097            basePath = path;
098            return this;
099        }
100
101        /**
102         * Creates a new immutable {@code FileLocatorImpl} object based on the properties set so far for this builder.
103         *
104         * @return the newly created {@code FileLocator} object, never null.
105         */
106        public FileLocator create() {
107            return new FileLocator(this);
108        }
109
110        /**
111         * Specifies the encoding of the new {@code FileLocator}.
112         *
113         * @param enc the encoding
114         * @return a reference to this builder for method chaining
115         */
116        public FileLocatorBuilder encoding(final String enc) {
117            encoding = enc;
118            return this;
119        }
120
121        /**
122         * Specifies the file name of the new {@code FileLocator}.
123         *
124         * @param name the file name
125         * @return a reference to this builder for method chaining
126         */
127        public FileLocatorBuilder fileName(final String name) {
128            fileName = name;
129            return this;
130        }
131
132        /**
133         * Specifies the {@code FileSystem} of the new {@code FileLocator}.
134         *
135         * @param fs the {@code FileSystem}
136         * @return a reference to this builder for method chaining
137         */
138        public FileLocatorBuilder fileSystem(final FileSystem fs) {
139            fileSystem = fs;
140            return this;
141        }
142
143        /**
144         * Initializes the properties of this builder from the passed in locator object.
145         *
146         * @param src the source {@code FileLocator}
147         */
148        private void initBuilder(final FileLocator src) {
149            basePath = src.getBasePath();
150            fileName = src.getFileName();
151            sourceURL = src.getSourceURL();
152            urlConnectionOptions = src.getURLConnectionOptions();
153            encoding = src.getEncoding();
154            fileSystem = src.getFileSystem();
155            locationStrategy = src.getLocationStrategy();
156        }
157
158        /**
159         * Specifies the {@code FileLocationStrategy} to be used when the referenced file is to be located.
160         *
161         * @param strategy the {@code FileLocationStrategy}
162         * @return a reference to this builder for method chaining
163         */
164        public FileLocatorBuilder locationStrategy(final FileLocationStrategy strategy) {
165            locationStrategy = strategy;
166            return this;
167        }
168
169        /**
170         * Specifies the source URL of the new {@code FileLocator}.
171         *
172         * @param url the source URL
173         * @return a reference to this builder for method chaining
174         */
175        public FileLocatorBuilder sourceURL(final URL url) {
176            this.sourceURL = url;
177            return this;
178        }
179
180        /**
181         * Specifies the source URL connection options of the new {@code FileLocator}.
182         *
183         * @param urlConnectionOptions the source URL connection options.
184         * @return a reference to this builder for method chaining
185         */
186        public FileLocatorBuilder urlConnectionOptions(final URLConnectionOptions urlConnectionOptions) {
187            this.urlConnectionOptions = urlConnectionOptions;
188            return this;
189
190        }
191    }
192
193    /** The base path. */
194    private final String basePath;
195
196    /** The encoding. */
197    private final String encoding;
198
199    /** The file name. */
200    private final String fileName;
201
202    /** The file system. */
203    private final FileSystem fileSystem;
204
205    /** The file location strategy. */
206    private final FileLocationStrategy locationStrategy;
207
208    /** The source URL. */
209    private final URL sourceURL;
210
211    /** The source URL connection options. */
212    private final URLConnectionOptions urlConnectionOptions;
213
214    /**
215     * Creates a new instance of {@code FileLocatorImpl} and initializes it from the given builder instance
216     *
217     * @param builder the builder
218     */
219    public FileLocator(final FileLocatorBuilder builder) {
220        fileName = builder.fileName;
221        basePath = builder.basePath;
222        sourceURL = builder.sourceURL;
223        urlConnectionOptions = builder.urlConnectionOptions;
224        encoding = builder.encoding;
225        fileSystem = builder.fileSystem;
226        locationStrategy = builder.locationStrategy;
227    }
228
229    /**
230     * Compares this object with another one. Two instances of {@code FileLocatorImpl} are considered equal if all of their
231     * properties are equal.
232     *
233     * @param obj the object to compare to
234     * @return a flag whether these objects are equal
235     */
236    @Override
237    public boolean equals(final Object obj) {
238        if (this == obj) {
239            return true;
240        }
241        if (!(obj instanceof FileLocator)) {
242            return false;
243        }
244        final FileLocator other = (FileLocator) obj;
245        return Objects.equals(basePath, other.basePath) && Objects.equals(encoding, other.encoding) && Objects.equals(fileName, other.fileName)
246            && Objects.equals(fileSystem, other.fileSystem) && Objects.equals(locationStrategy, other.locationStrategy)
247            && Objects.equals(sourceURL, other.sourceURL) && Objects.equals(urlConnectionOptions, other.urlConnectionOptions);
248    }
249
250    /**
251     * Gets the base path stored in this locator or <strong>null</strong> if it is undefined.
252     *
253     * @return the base path
254     */
255    public String getBasePath() {
256        return basePath;
257    }
258
259    /**
260     * Gets the encoding stored in this locator or <strong>null</strong> if it is undefined.
261     *
262     * @return the encoding
263     */
264    public String getEncoding() {
265        return encoding;
266    }
267
268    /**
269     * Gets the file name stored in this locator or <strong>null</strong> if it is undefined.
270     *
271     * @return the file name
272     */
273    public String getFileName() {
274        return fileName;
275    }
276
277    /**
278     * Gets the {@code FileSystem} to be used for accessing the file referenced by this locator or <strong>null</strong> if it is
279     * undefined.
280     *
281     * @return the {@code FileSystem}
282     */
283    public FileSystem getFileSystem() {
284        return fileSystem;
285    }
286
287    /**
288     * Gets the {@code FileLocationStrategy} to be used for locating the referenced file. If no specific
289     * {@code FileLocationStrategy} has been set, result is <strong>null</strong>. This means that the default strategy should be
290     * used.
291     *
292     * @return the {@code FileLocationStrategy} to be used
293     */
294    public FileLocationStrategy getLocationStrategy() {
295        return locationStrategy;
296    }
297
298    /**
299     * Gets the URL pointing to the referenced source file or <strong>null</strong> if it is undefined.
300     *
301     * @return the source URL
302     */
303    public URL getSourceURL() {
304        return sourceURL;
305    }
306
307    /**
308     * Gets the URLConnectionOptions
309     *
310     * @return the URLConnectionOptions
311     */
312    public URLConnectionOptions getURLConnectionOptions() {
313        return urlConnectionOptions;
314    }
315
316    /**
317     * Returns a hash code for this object.
318     *
319     * @return a hash code for this object
320     */
321    @Override
322    public int hashCode() {
323        return Objects.hash(basePath, encoding, fileName, fileSystem, locationStrategy, sourceURL, urlConnectionOptions);
324    }
325
326    @Override
327    public String toString() {
328        return "FileLocator [basePath=" + basePath + ", encoding=" + encoding + ", fileName=" + fileName + ", fileSystem=" + fileSystem + ", locationStrategy="
329            + locationStrategy + ", sourceURL=" + sourceURL + ", urlConnectionOptions=" + urlConnectionOptions + "]";
330    }
331}