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