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