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