/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.commons.configuration2.io;

import java.io.Closeable;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicReference;

import org.apache.commons.configuration2.ex.ConfigurationException;
import org.apache.commons.configuration2.io.FileLocator.FileLocatorBuilder;
import org.apache.commons.configuration2.sync.LockMode;
import org.apache.commons.configuration2.sync.NoOpSynchronizer;
import org.apache.commons.configuration2.sync.Synchronizer;
import org.apache.commons.configuration2.sync.SynchronizerSupport;
import org.apache.commons.logging.LogFactory;

A class that manages persistence of an associated FileBased object.

Instances of this class can be used to load and save arbitrary objects implementing the FileBased interface in a convenient way from and to various locations. At construction time the FileBased object to manage is passed in. Basically, this object is assigned a location from which it is loaded and to which it can be saved. The following possibilities exist to specify such a location:

  • URLs: With the method setURL() a full URL to the configuration source can be specified. This is the most flexible way. Note that the save() methods support only file: URLs.
  • Files: The setFile() method allows to specify the configuration source as a file. This can be either a relative or an absolute file. In the former case the file is resolved based on the current directory.
  • As file paths in string form: With the setPath() method a full path to a configuration file can be provided as a string.
  • Separated as base path and file name: The base path is a string defining either a local directory or a URL. It can be set using the setBasePath() method. The file name, non surprisingly, defines the name of the configuration file.

An instance stores a location. The load() and save() methods that do not take an argument make use of this internal location. Alternatively, it is also possible to use overloaded variants of load() and save() which expect a location. In these cases the location specified takes precedence over the internal one; the internal location is not changed.

The actual position of the file to be loaded is determined by a FileLocationStrategy based on the location information that has been provided. By providing a custom location strategy the algorithm for searching files can be adapted. Save operations require more explicit information. They cannot rely on a location strategy because the file to be written may not yet exist. So there may be some differences in the way location information is interpreted by load and save operations. In order to avoid this, the following approach is recommended:

  • Use the desired setXXX() methods to define the location of the file to be loaded.
  • Call the locate() method. This method resolves the referenced file (if possible) and fills out all supported location information.
  • Later on, save() can be called. This method now has sufficient information to store the file at the correct location.

When loading or saving a FileBased object some additional functionality is performed if the object implements one of the following interfaces:

  • FileLocatorAware: In this case an object with the current file location is injected before the load or save operation is executed. This is useful for FileBased objects that depend on their current location, e.g. to resolve relative path names.
  • SynchronizerSupport: If this interface is implemented, load and save operations obtain a write lock on the FileBased object before they access it. (In case of a save operation, a read lock would probably be sufficient, but because of the possible injection of a FileLocator object it is not allowed to perform multiple save operations in parallel; therefore, by obtaining a write lock, we are on the safe side.)

This class is thread-safe.

Since:2.0
/** * <p> * A class that manages persistence of an associated {@link FileBased} object. * </p> * <p> * Instances of this class can be used to load and save arbitrary objects * implementing the {@code FileBased} interface in a convenient way from and to * various locations. At construction time the {@code FileBased} object to * manage is passed in. Basically, this object is assigned a location from which * it is loaded and to which it can be saved. The following possibilities exist * to specify such a location: * </p> * <ul> * <li>URLs: With the method {@code setURL()} a full URL to the configuration * source can be specified. This is the most flexible way. Note that the * {@code save()} methods support only <em>file:</em> URLs.</li> * <li>Files: The {@code setFile()} method allows to specify the configuration * source as a file. This can be either a relative or an absolute file. In the * former case the file is resolved based on the current directory.</li> * <li>As file paths in string form: With the {@code setPath()} method a full * path to a configuration file can be provided as a string.</li> * <li>Separated as base path and file name: The base path is a string defining * either a local directory or a URL. It can be set using the * {@code setBasePath()} method. The file name, non surprisingly, defines the * name of the configuration file.</li> * </ul> * <p> * An instance stores a location. The {@code load()} and {@code save()} methods * that do not take an argument make use of this internal location. * Alternatively, it is also possible to use overloaded variants of * {@code load()} and {@code save()} which expect a location. In these cases the * location specified takes precedence over the internal one; the internal * location is not changed. * </p> * <p> * The actual position of the file to be loaded is determined by a * {@link FileLocationStrategy} based on the location information that has been * provided. By providing a custom location strategy the algorithm for searching * files can be adapted. Save operations require more explicit information. They * cannot rely on a location strategy because the file to be written may not yet * exist. So there may be some differences in the way location information is * interpreted by load and save operations. In order to avoid this, the * following approach is recommended: * </p> * <ul> * <li>Use the desired {@code setXXX()} methods to define the location of the * file to be loaded.</li> * <li>Call the {@code locate()} method. This method resolves the referenced * file (if possible) and fills out all supported location information.</li> * <li>Later on, {@code save()} can be called. This method now has sufficient * information to store the file at the correct location.</li> * </ul> * <p> * When loading or saving a {@code FileBased} object some additional * functionality is performed if the object implements one of the following * interfaces: * </p> * <ul> * <li>{@code FileLocatorAware}: In this case an object with the current file * location is injected before the load or save operation is executed. This is * useful for {@code FileBased} objects that depend on their current location, * e.g. to resolve relative path names.</li> * <li>{@code SynchronizerSupport}: If this interface is implemented, load and * save operations obtain a write lock on the {@code FileBased} object before * they access it. (In case of a save operation, a read lock would probably be * sufficient, but because of the possible injection of a {@link FileLocator} * object it is not allowed to perform multiple save operations in parallel; * therefore, by obtaining a write lock, we are on the safe side.)</li> * </ul> * <p> * This class is thread-safe. * </p> * * @since 2.0 */
public class FileHandler {
Constant for the URI scheme for files.
/** Constant for the URI scheme for files. */
private static final String FILE_SCHEME = "file:";
Constant for the URI scheme for files with slashes.
/** Constant for the URI scheme for files with slashes. */
private static final String FILE_SCHEME_SLASH = FILE_SCHEME + "//";
A dummy implementation of SynchronizerSupport. This object is used when the file handler's content does not implement the SynchronizerSupport interface. All methods are just empty dummy implementations.
/** * A dummy implementation of {@code SynchronizerSupport}. This object is * used when the file handler's content does not implement the * {@code SynchronizerSupport} interface. All methods are just empty dummy * implementations. */
private static final SynchronizerSupport DUMMY_SYNC_SUPPORT = new SynchronizerSupport() { @Override public void unlock(final LockMode mode) { } @Override public void setSynchronizer(final Synchronizer sync) { } @Override public void lock(final LockMode mode) { } @Override public Synchronizer getSynchronizer() { return NoOpSynchronizer.INSTANCE; } };
The file-based object managed by this handler.
/** The file-based object managed by this handler. */
private final FileBased content;
A reference to the current FileLocator object.
/** A reference to the current {@code FileLocator} object. */
private final AtomicReference<FileLocator> fileLocator;
A collection with the registered listeners.
/** A collection with the registered listeners. */
private final List<FileHandlerListener> listeners = new CopyOnWriteArrayList<>();
Creates a new instance of FileHandler which is not associated with a FileBased object and thus does not have a content. Objects of this kind can be used to define a file location, but it is not possible to actually load or save data.
/** * Creates a new instance of {@code FileHandler} which is not associated * with a {@code FileBased} object and thus does not have a content. Objects * of this kind can be used to define a file location, but it is not * possible to actually load or save data. */
public FileHandler() { this(null); }
Creates a new instance of FileHandler and sets the managed FileBased object.
Params:
  • obj – the file-based object to manage
/** * Creates a new instance of {@code FileHandler} and sets the managed * {@code FileBased} object. * * @param obj the file-based object to manage */
public FileHandler(final FileBased obj) { this(obj, emptyFileLocator()); }
Creates a new instance of FileHandler which is associated with the given FileBased object and the location defined for the given FileHandler object. A copy of the location of the given FileHandler is created. This constructor is a possibility to associate a file location with a FileBased object.
Params:
  • obj – the FileBased object to manage
  • c – the FileHandler from which to copy the location (must not be null)
Throws:
/** * Creates a new instance of {@code FileHandler} which is associated with * the given {@code FileBased} object and the location defined for the given * {@code FileHandler} object. A copy of the location of the given * {@code FileHandler} is created. This constructor is a possibility to * associate a file location with a {@code FileBased} object. * * @param obj the {@code FileBased} object to manage * @param c the {@code FileHandler} from which to copy the location (must * not be <b>null</b>) * @throws IllegalArgumentException if the {@code FileHandler} is * <b>null</b> */
public FileHandler(final FileBased obj, final FileHandler c) { this(obj, checkSourceHandler(c).getFileLocator()); }
Creates a new instance of FileHandler based on the given FileBased and FileLocator objects.
Params:
  • obj – the FileBased object to manage
  • locator – the FileLocator
/** * Creates a new instance of {@code FileHandler} based on the given * {@code FileBased} and {@code FileLocator} objects. * * @param obj the {@code FileBased} object to manage * @param locator the {@code FileLocator} */
private FileHandler(final FileBased obj, final FileLocator locator) { content = obj; fileLocator = new AtomicReference<>(locator); }
Creates a new FileHandler instance from properties stored in a map. This method tries to extract a FileLocator from the map. A new FileHandler is created based on this FileLocator.
Params:
  • map – the map (may be null)
See Also:
Returns:the newly created FileHandler
/** * Creates a new {@code FileHandler} instance from properties stored in a * map. This method tries to extract a {@link FileLocator} from the map. A * new {@code FileHandler} is created based on this {@code FileLocator}. * * @param map the map (may be <b>null</b>) * @return the newly created {@code FileHandler} * @see FileLocatorUtils#fromMap(Map) */
public static FileHandler fromMap(final Map<String, ?> map) { return new FileHandler(null, FileLocatorUtils.fromMap(map)); }
Returns the FileBased object associated with this FileHandler.
Returns:the associated FileBased object
/** * Returns the {@code FileBased} object associated with this * {@code FileHandler}. * * @return the associated {@code FileBased} object */
public final FileBased getContent() { return content; }
Adds a listener to this FileHandler. It is notified about property changes and IO operations.
Params:
  • l – the listener to be added (must not be null)
Throws:
/** * Adds a listener to this {@code FileHandler}. It is notified about * property changes and IO operations. * * @param l the listener to be added (must not be <b>null</b>) * @throws IllegalArgumentException if the listener is <b>null</b> */
public void addFileHandlerListener(final FileHandlerListener l) { if (l == null) { throw new IllegalArgumentException("Listener must not be null!"); } listeners.add(l); }
Removes the specified listener from this object.
Params:
  • l – the listener to be removed
/** * Removes the specified listener from this object. * * @param l the listener to be removed */
public void removeFileHandlerListener(final FileHandlerListener l) { listeners.remove(l); }
Return the name of the file. If only a URL is defined, the file name is derived from there.
Returns:the file name
/** * Return the name of the file. If only a URL is defined, the file name * is derived from there. * * @return the file name */
public String getFileName() { final FileLocator locator = getFileLocator(); if (locator.getFileName() != null) { return locator.getFileName(); } if (locator.getSourceURL() != null) { return FileLocatorUtils.getFileName(locator.getSourceURL()); } return null; }
Set the name of the file. The passed in file name can contain a relative path. It must be used when referring files with relative paths from classpath. Use setPath() to set a full qualified file name. The URL is set to null as it has to be determined anew based on the file name and the base path.
Params:
  • fileName – the name of the file
/** * Set the name of the file. The passed in file name can contain a relative * path. It must be used when referring files with relative paths from * classpath. Use {@code setPath()} to set a full qualified file name. The * URL is set to <b>null</b> as it has to be determined anew based on the * file name and the base path. * * @param fileName the name of the file */
public void setFileName(final String fileName) { final String name = normalizeFileURL(fileName); new Updater() { @Override protected void updateBuilder(final FileLocatorBuilder builder) { builder.fileName(name); builder.sourceURL(null); } } .update(); }
Return the base path. If no base path is defined, but a URL, the base path is derived from there.
Returns:the base path
/** * Return the base path. If no base path is defined, but a URL, the base * path is derived from there. * * @return the base path */
public String getBasePath() { final FileLocator locator = getFileLocator(); if (locator.getBasePath() != null) { return locator.getBasePath(); } if (locator.getSourceURL() != null) { return FileLocatorUtils.getBasePath(locator.getSourceURL()); } return null; }
Sets the base path. The base path is typically either a path to a directory or a URL. Together with the value passed to the setFileName() method it defines the location of the configuration file to be loaded. The strategies for locating the file are quite tolerant. For instance if the file name is already an absolute path or a fully defined URL, the base path will be ignored. The base path can also be a URL, in which case the file name is interpreted in this URL's context. If other methods are used for determining the location of the associated file (e.g. setFile() or setURL()), the base path is automatically set. Setting the base path using this method automatically sets the URL to null because it has to be determined anew based on the file name and the base path.
Params:
  • basePath – the base path.
/** * Sets the base path. The base path is typically either a path to a * directory or a URL. Together with the value passed to the * {@code setFileName()} method it defines the location of the configuration * file to be loaded. The strategies for locating the file are quite * tolerant. For instance if the file name is already an absolute path or a * fully defined URL, the base path will be ignored. The base path can also * be a URL, in which case the file name is interpreted in this URL's * context. If other methods are used for determining the location of the * associated file (e.g. {@code setFile()} or {@code setURL()}), the base * path is automatically set. Setting the base path using this method * automatically sets the URL to <b>null</b> because it has to be * determined anew based on the file name and the base path. * * @param basePath the base path. */
public void setBasePath(final String basePath) { final String path = normalizeFileURL(basePath); new Updater() { @Override protected void updateBuilder(final FileLocatorBuilder builder) { builder.basePath(path); builder.sourceURL(null); } } .update(); }
Returns the location of the associated file as a File object. If the base path is a URL with a protocol different than "file", or the file is within a compressed archive, the return value will not point to a valid file object.
Returns:the location as File object; this can be null
/** * Returns the location of the associated file as a {@code File} object. If * the base path is a URL with a protocol different than &quot;file&quot;, * or the file is within a compressed archive, the return value will not * point to a valid file object. * * @return the location as {@code File} object; this can be <b>null</b> */
public File getFile() { return createFile(getFileLocator()); }
Sets the location of the associated file as a File object. The passed in File is made absolute if it is not yet. Then the file's path component becomes the base path and its name component becomes the file name.
Params:
  • file – the location of the associated file
/** * Sets the location of the associated file as a {@code File} object. The * passed in {@code File} is made absolute if it is not yet. Then the file's * path component becomes the base path and its name component becomes the * file name. * * @param file the location of the associated file */
public void setFile(final File file) { final String fileName = file.getName(); final String basePath = file.getParentFile() != null ? file.getParentFile() .getAbsolutePath() : null; new Updater() { @Override protected void updateBuilder(final FileLocatorBuilder builder) { builder.fileName(fileName).basePath(basePath).sourceURL(null); } } .update(); }
Returns the full path to the associated file. The return value is a valid File path only if this location is based on a file on the local disk. If the file was loaded from a packed archive, the returned value is the string form of the URL from which the file was loaded.
Returns:the full path to the associated file
/** * Returns the full path to the associated file. The return value is a valid * {@code File} path only if this location is based on a file on the local * disk. If the file was loaded from a packed archive, the returned value is * the string form of the URL from which the file was loaded. * * @return the full path to the associated file */
public String getPath() { final FileLocator locator = getFileLocator(); final File file = createFile(locator); return FileLocatorUtils.obtainFileSystem(locator).getPath(file, locator.getSourceURL(), locator.getBasePath(), locator.getFileName()); }
Sets the location of the associated file as a full or relative path name. The passed in path should represent a valid file name on the file system. It must not be used to specify relative paths for files that exist in classpath, either plain file system or compressed archive, because this method expands any relative path to an absolute one which may end in an invalid absolute path for classpath references.
Params:
  • path – the full path name of the associated file
/** * Sets the location of the associated file as a full or relative path name. * The passed in path should represent a valid file name on the file system. * It must not be used to specify relative paths for files that exist in * classpath, either plain file system or compressed archive, because this * method expands any relative path to an absolute one which may end in an * invalid absolute path for classpath references. * * @param path the full path name of the associated file */
public void setPath(final String path) { setFile(new File(path)); }
Returns the location of the associated file as a URL. If a URL is set, it is directly returned. Otherwise, an attempt to locate the referenced file is made.
Returns:a URL to the associated file; can be null if the location is unspecified
/** * Returns the location of the associated file as a URL. If a URL is set, * it is directly returned. Otherwise, an attempt to locate the referenced * file is made. * * @return a URL to the associated file; can be <b>null</b> if the location * is unspecified */
public URL getURL() { final FileLocator locator = getFileLocator(); return locator.getSourceURL() != null ? locator.getSourceURL() : FileLocatorUtils.locate(locator); }
Sets the location of the associated file as a URL. For loading this can be an arbitrary URL with a supported protocol. If the file is to be saved, too, a URL with the "file" protocol should be provided. This method sets the file name and the base path to null. They have to be determined anew based on the new URL.
Params:
  • url – the location of the file as URL
/** * Sets the location of the associated file as a URL. For loading this can * be an arbitrary URL with a supported protocol. If the file is to be * saved, too, a URL with the &quot;file&quot; protocol should be provided. * This method sets the file name and the base path to <b>null</b>. * They have to be determined anew based on the new URL. * * @param url the location of the file as URL */
public void setURL(final URL url) { new Updater() { @Override protected void updateBuilder(final FileLocatorBuilder builder) { builder.sourceURL(url); builder.basePath(null).fileName(null); } } .update(); }
Returns a FileLocator object with the specification of the file stored by this FileHandler. Note that this method returns the internal data managed by this FileHandler as it was defined. This is not necessarily the same as the data returned by the single access methods like getFileName() or getURL(): These methods try to derive missing data from other values that have been set.
Returns:a FileLocator with the referenced file
/** * Returns a {@code FileLocator} object with the specification of the file * stored by this {@code FileHandler}. Note that this method returns the * internal data managed by this {@code FileHandler} as it was defined. * This is not necessarily the same as the data returned by the single * access methods like {@code getFileName()} or {@code getURL()}: These * methods try to derive missing data from other values that have been set. * * @return a {@code FileLocator} with the referenced file */
public FileLocator getFileLocator() { return fileLocator.get(); }
Sets the file to be accessed by this FileHandler as a FileLocator object.
Params:
  • locator – the FileLocator with the definition of the file to be accessed (must not be null
Throws:
/** * Sets the file to be accessed by this {@code FileHandler} as a * {@code FileLocator} object. * * @param locator the {@code FileLocator} with the definition of the file to * be accessed (must not be <b>null</b> * @throws IllegalArgumentException if the {@code FileLocator} is * <b>null</b> */
public void setFileLocator(final FileLocator locator) { if (locator == null) { throw new IllegalArgumentException("FileLocator must not be null!"); } fileLocator.set(locator); fireLocationChangedEvent(); }
Tests whether a location is defined for this FileHandler.
Returns:true if a location is defined, false otherwise
/** * Tests whether a location is defined for this {@code FileHandler}. * * @return <b>true</b> if a location is defined, <b>false</b> otherwise */
public boolean isLocationDefined() { return FileLocatorUtils.isLocationDefined(getFileLocator()); }
Clears the location of this FileHandler. Afterwards this handler does not point to any valid file.
/** * Clears the location of this {@code FileHandler}. Afterwards this handler * does not point to any valid file. */
public void clearLocation() { new Updater() { @Override protected void updateBuilder(final FileLocatorBuilder builder) { builder.basePath(null).fileName(null).sourceURL(null); } } .update(); }
Returns the encoding of the associated file. Result can be null if no encoding has been set.
Returns:the encoding of the associated file
/** * Returns the encoding of the associated file. Result can be <b>null</b> if * no encoding has been set. * * @return the encoding of the associated file */
public String getEncoding() { return getFileLocator().getEncoding(); }
Sets the encoding of the associated file. The encoding applies if binary files are loaded. Note that in this case setting an encoding is recommended; otherwise the platform's default encoding is used.
Params:
  • encoding – the encoding of the associated file
/** * Sets the encoding of the associated file. The encoding applies if binary * files are loaded. Note that in this case setting an encoding is * recommended; otherwise the platform's default encoding is used. * * @param encoding the encoding of the associated file */
public void setEncoding(final String encoding) { new Updater() { @Override protected void updateBuilder(final FileLocatorBuilder builder) { builder.encoding(encoding); } } .update(); }
Returns the FileSystem to be used by this object when locating files. Result is never null; if no file system has been set, the default file system is returned.
Returns:the used FileSystem
/** * Returns the {@code FileSystem} to be used by this object when locating * files. Result is never <b>null</b>; if no file system has been set, the * default file system is returned. * * @return the used {@code FileSystem} */
public FileSystem getFileSystem() { return FileLocatorUtils.obtainFileSystem(getFileLocator()); }
Sets the FileSystem to be used by this object when locating files. If a null value is passed in, the file system is reset to the default file system.
Params:
  • fileSystem – the FileSystem
/** * Sets the {@code FileSystem} to be used by this object when locating * files. If a <b>null</b> value is passed in, the file system is reset to * the default file system. * * @param fileSystem the {@code FileSystem} */
public void setFileSystem(final FileSystem fileSystem) { new Updater() { @Override protected void updateBuilder(final FileLocatorBuilder builder) { builder.fileSystem(fileSystem); } } .update(); }
Resets the FileSystem used by this object. It is set to the default file system.
/** * Resets the {@code FileSystem} used by this object. It is set to the * default file system. */
public void resetFileSystem() { setFileSystem(null); }
Returns the FileLocationStrategy to be applied when accessing the associated file. This method never returns null. If a FileLocationStrategy has been set, it is returned. Otherwise, result is the default FileLocationStrategy.
Returns:the FileLocationStrategy to be used
/** * Returns the {@code FileLocationStrategy} to be applied when accessing the * associated file. This method never returns <b>null</b>. If a * {@code FileLocationStrategy} has been set, it is returned. Otherwise, * result is the default {@code FileLocationStrategy}. * * @return the {@code FileLocationStrategy} to be used */
public FileLocationStrategy getLocationStrategy() { return FileLocatorUtils.obtainLocationStrategy(getFileLocator()); }
Sets the FileLocationStrategy to be applied when accessing the associated file. The strategy is stored in the underlying FileLocator. The argument can be null; this causes the default FileLocationStrategy to be used.
Params:
  • strategy – the FileLocationStrategy
See Also:
/** * Sets the {@code FileLocationStrategy} to be applied when accessing the * associated file. The strategy is stored in the underlying * {@link FileLocator}. The argument can be <b>null</b>; this causes the * default {@code FileLocationStrategy} to be used. * * @param strategy the {@code FileLocationStrategy} * @see FileLocatorUtils#DEFAULT_LOCATION_STRATEGY */
public void setLocationStrategy(final FileLocationStrategy strategy) { new Updater() { @Override protected void updateBuilder(final FileLocatorBuilder builder) { builder.locationStrategy(strategy); } } .update(); }
Locates the referenced file if necessary and ensures that the associated FileLocator is fully initialized. When accessing the referenced file the information stored in the associated FileLocator is used. If this information is incomplete (e.g. only the file name is set), an attempt to locate the file may have to be performed on each access. By calling this method such an attempt is performed once, and the results of a successful localization are stored. Hence, later access to the referenced file can be more efficient. Also, all properties pointing to the referenced file in this object's FileLocator are set (i.e. the URL, the base path, and the file name). If the referenced file cannot be located, result is false. This means that the information in the current FileLocator is insufficient or wrong. If the FileLocator is already fully defined, it is not changed.
See Also:
Returns:a flag whether the referenced file could be located successfully
/** * Locates the referenced file if necessary and ensures that the associated * {@link FileLocator} is fully initialized. When accessing the referenced * file the information stored in the associated {@code FileLocator} is * used. If this information is incomplete (e.g. only the file name is set), * an attempt to locate the file may have to be performed on each access. By * calling this method such an attempt is performed once, and the results of * a successful localization are stored. Hence, later access to the * referenced file can be more efficient. Also, all properties pointing to * the referenced file in this object's {@code FileLocator} are set (i.e. * the URL, the base path, and the file name). If the referenced file cannot * be located, result is <b>false</b>. This means that the information in * the current {@code FileLocator} is insufficient or wrong. If the * {@code FileLocator} is already fully defined, it is not changed. * * @return a flag whether the referenced file could be located successfully * @see FileLocatorUtils#fullyInitializedLocator(FileLocator) */
public boolean locate() { boolean result; boolean done; do { final FileLocator locator = getFileLocator(); FileLocator fullLocator = FileLocatorUtils.fullyInitializedLocator(locator); if (fullLocator == null) { result = false; fullLocator = locator; } else { result = fullLocator != locator || FileLocatorUtils.isFullyInitialized(locator); } done = fileLocator.compareAndSet(locator, fullLocator); } while (!done); return result; }
Loads the associated file from the underlying location. If no location has been set, an exception is thrown.
Throws:
  • ConfigurationException – if loading of the configuration fails
/** * Loads the associated file from the underlying location. If no location * has been set, an exception is thrown. * * @throws ConfigurationException if loading of the configuration fails */
public void load() throws ConfigurationException { load(checkContentAndGetLocator()); }
Loads the associated file from the given file name. The file name is interpreted in the context of the already set location (e.g. if it is a relative file name, a base path is applied if available). The underlying location is not changed.
Params:
  • fileName – the name of the file to be loaded
Throws:
/** * Loads the associated file from the given file name. The file name is * interpreted in the context of the already set location (e.g. if it is a * relative file name, a base path is applied if available). The underlying * location is not changed. * * @param fileName the name of the file to be loaded * @throws ConfigurationException if an error occurs */
public void load(final String fileName) throws ConfigurationException { load(fileName, checkContentAndGetLocator()); }
Loads the associated file from the specified File.
Params:
  • file – the file to load
Throws:
/** * Loads the associated file from the specified {@code File}. * * @param file the file to load * @throws ConfigurationException if an error occurs */
public void load(final File file) throws ConfigurationException { URL url; try { url = FileLocatorUtils.toURL(file); } catch (final MalformedURLException e1) { throw new ConfigurationException("Cannot create URL from file " + file); } load(url); }
Loads the associated file from the specified URL. The location stored in this object is not changed.
Params:
  • url – the URL of the file to be loaded
Throws:
/** * Loads the associated file from the specified URL. The location stored in * this object is not changed. * * @param url the URL of the file to be loaded * @throws ConfigurationException if an error occurs */
public void load(final URL url) throws ConfigurationException { load(url, checkContentAndGetLocator()); }
Loads the associated file from the specified stream, using the encoding returned by getEncoding().
Params:
  • in – the input stream
Throws:
/** * Loads the associated file from the specified stream, using the encoding * returned by {@link #getEncoding()}. * * @param in the input stream * @throws ConfigurationException if an error occurs during the load * operation */
public void load(final InputStream in) throws ConfigurationException { load(in, checkContentAndGetLocator()); }
Loads the associated file from the specified stream, using the specified encoding. If the encoding is null, the default encoding is used.
Params:
  • in – the input stream
  • encoding – the encoding used, null to use the default encoding
Throws:
/** * Loads the associated file from the specified stream, using the specified * encoding. If the encoding is <b>null</b>, the default encoding is used. * * @param in the input stream * @param encoding the encoding used, {@code null} to use the default * encoding * @throws ConfigurationException if an error occurs during the load * operation */
public void load(final InputStream in, final String encoding) throws ConfigurationException { loadFromStream(in, encoding, null); }
Loads the associated file from the specified reader.
Params:
  • in – the reader
Throws:
/** * Loads the associated file from the specified reader. * * @param in the reader * @throws ConfigurationException if an error occurs during the load * operation */
public void load(final Reader in) throws ConfigurationException { checkContent(); injectNullFileLocator(); loadFromReader(in); }
Saves the associated file to the current location set for this object. Before this method can be called a valid location must have been set.
Throws:
  • ConfigurationException – if an error occurs or no location has been set yet
/** * Saves the associated file to the current location set for this object. * Before this method can be called a valid location must have been set. * * @throws ConfigurationException if an error occurs or no location has been * set yet */
public void save() throws ConfigurationException { save(checkContentAndGetLocator()); }
Saves the associated file to the specified file name. This does not change the location of this object (use setFileName(String) if you need it).
Params:
  • fileName – the file name
Throws:
/** * Saves the associated file to the specified file name. This does not * change the location of this object (use {@link #setFileName(String)} if * you need it). * * @param fileName the file name * @throws ConfigurationException if an error occurs during the save * operation */
public void save(final String fileName) throws ConfigurationException { save(fileName, checkContentAndGetLocator()); }
Saves the associated file to the specified URL. This does not change the location of this object (use setURL(URL) if you need it).
Params:
  • url – the URL
Throws:
/** * Saves the associated file to the specified URL. This does not change the * location of this object (use {@link #setURL(URL)} if you need it). * * @param url the URL * @throws ConfigurationException if an error occurs during the save * operation */
public void save(final URL url) throws ConfigurationException { save(url, checkContentAndGetLocator()); }
Saves the associated file to the specified File. The file is created automatically if it doesn't exist. This does not change the location of this object (use setFile if you need it).
Params:
  • file – the target file
Throws:
/** * Saves the associated file to the specified {@code File}. The file is * created automatically if it doesn't exist. This does not change the * location of this object (use {@link #setFile} if you need it). * * @param file the target file * @throws ConfigurationException if an error occurs during the save * operation */
public void save(final File file) throws ConfigurationException { save(file, checkContentAndGetLocator()); }
Saves the associated file to the specified stream using the encoding returned by getEncoding().
Params:
  • out – the output stream
Throws:
/** * Saves the associated file to the specified stream using the encoding * returned by {@link #getEncoding()}. * * @param out the output stream * @throws ConfigurationException if an error occurs during the save * operation */
public void save(final OutputStream out) throws ConfigurationException { save(out, checkContentAndGetLocator()); }
Saves the associated file to the specified stream using the specified encoding. If the encoding is null, the default encoding is used.
Params:
  • out – the output stream
  • encoding – the encoding to be used, null to use the default encoding
Throws:
/** * Saves the associated file to the specified stream using the specified * encoding. If the encoding is <b>null</b>, the default encoding is used. * * @param out the output stream * @param encoding the encoding to be used, {@code null} to use the default * encoding * @throws ConfigurationException if an error occurs during the save * operation */
public void save(final OutputStream out, final String encoding) throws ConfigurationException { saveToStream(out, encoding, null); }
Saves the associated file to the given Writer.
Params:
  • out – the Writer
Throws:
/** * Saves the associated file to the given {@code Writer}. * * @param out the {@code Writer} * @throws ConfigurationException if an error occurs during the save * operation */
public void save(final Writer out) throws ConfigurationException { checkContent(); injectNullFileLocator(); saveToWriter(out); }
Prepares a builder for a FileLocator which does not have a defined file location. Other properties (e.g. encoding or file system) are initialized from the FileLocator associated with this object.
Returns:the initialized builder for a FileLocator
/** * Prepares a builder for a {@code FileLocator} which does not have a * defined file location. Other properties (e.g. encoding or file system) * are initialized from the {@code FileLocator} associated with this object. * * @return the initialized builder for a {@code FileLocator} */
private FileLocatorBuilder prepareNullLocatorBuilder() { return FileLocatorUtils.fileLocator(getFileLocator()).sourceURL(null) .basePath(null).fileName(null); }
Checks whether the associated FileBased object implements the FileLocatorAware interface. If this is the case, a FileLocator instance is injected which returns only null values. This method is called if no file location is available (e.g. if data is to be loaded from a stream). The encoding of the injected locator is derived from this object.
/** * Checks whether the associated {@code FileBased} object implements the * {@code FileLocatorAware} interface. If this is the case, a * {@code FileLocator} instance is injected which returns only <b>null</b> * values. This method is called if no file location is available (e.g. if * data is to be loaded from a stream). The encoding of the injected locator * is derived from this object. */
private void injectNullFileLocator() { if (getContent() instanceof FileLocatorAware) { final FileLocator locator = prepareNullLocatorBuilder().create(); ((FileLocatorAware) getContent()).initFileLocator(locator); } }
Injects a FileLocator pointing to the specified URL if the current FileBased object implements the FileLocatorAware interface.
Params:
  • url – the URL for the locator
/** * Injects a {@code FileLocator} pointing to the specified URL if the * current {@code FileBased} object implements the {@code FileLocatorAware} * interface. * * @param url the URL for the locator */
private void injectFileLocator(final URL url) { if (url == null) { injectNullFileLocator(); } else { if (getContent() instanceof FileLocatorAware) { final FileLocator locator = prepareNullLocatorBuilder().sourceURL(url).create(); ((FileLocatorAware) getContent()).initFileLocator(locator); } } }
Obtains a SynchronizerSupport for the current content. If the content implements this interface, it is returned. Otherwise, result is a dummy object. This method is called before load and save operations. The returned object is used for synchronization.
Returns:the SynchronizerSupport for synchronization
/** * Obtains a {@code SynchronizerSupport} for the current content. If the * content implements this interface, it is returned. Otherwise, result is a * dummy object. This method is called before load and save operations. The * returned object is used for synchronization. * * @return the {@code SynchronizerSupport} for synchronization */
private SynchronizerSupport fetchSynchronizerSupport() { if (getContent() instanceof SynchronizerSupport) { return (SynchronizerSupport) getContent(); } return DUMMY_SYNC_SUPPORT; }
Internal helper method for loading the associated file from the location specified in the given FileLocator.
Params:
  • locator – the current FileLocator
Throws:
/** * Internal helper method for loading the associated file from the location * specified in the given {@code FileLocator}. * * @param locator the current {@code FileLocator} * @throws ConfigurationException if an error occurs */
private void load(final FileLocator locator) throws ConfigurationException { final URL url = FileLocatorUtils.locateOrThrow(locator); load(url, locator); }
Internal helper method for loading a file from the given URL.
Params:
  • url – the URL
  • locator – the current FileLocator
Throws:
/** * Internal helper method for loading a file from the given URL. * * @param url the URL * @param locator the current {@code FileLocator} * @throws ConfigurationException if an error occurs */
private void load(final URL url, final FileLocator locator) throws ConfigurationException { InputStream in = null; try { in = FileLocatorUtils.obtainFileSystem(locator).getInputStream(url); loadFromStream(in, locator.getEncoding(), url); } catch (final ConfigurationException e) { throw e; } catch (final Exception e) { throw new ConfigurationException( "Unable to load the configuration from the URL " + url, e); } finally { closeSilent(in); } }
Internal helper method for loading a file from a file name.
Params:
  • fileName – the file name
  • locator – the current FileLocator
Throws:
/** * Internal helper method for loading a file from a file name. * * @param fileName the file name * @param locator the current {@code FileLocator} * @throws ConfigurationException if an error occurs */
private void load(final String fileName, final FileLocator locator) throws ConfigurationException { final FileLocator locFileName = createLocatorWithFileName(fileName, locator); final URL url = FileLocatorUtils.locateOrThrow(locFileName); load(url, locator); }
Internal helper method for loading a file from the given input stream.
Params:
  • in – the input stream
  • locator – the current FileLocator
Throws:
/** * Internal helper method for loading a file from the given input stream. * * @param in the input stream * @param locator the current {@code FileLocator} * @throws ConfigurationException if an error occurs */
private void load(final InputStream in, final FileLocator locator) throws ConfigurationException { load(in, locator.getEncoding()); }
Internal helper method for loading a file from an input stream.
Params:
  • in – the input stream
  • encoding – the encoding
  • url – the URL of the file to be loaded (if known)
Throws:
/** * Internal helper method for loading a file from an input stream. * * @param in the input stream * @param encoding the encoding * @param url the URL of the file to be loaded (if known) * @throws ConfigurationException if an error occurs */
private void loadFromStream(final InputStream in, final String encoding, final URL url) throws ConfigurationException { checkContent(); final SynchronizerSupport syncSupport = fetchSynchronizerSupport(); syncSupport.lock(LockMode.WRITE); try { injectFileLocator(url); if (getContent() instanceof InputStreamSupport) { loadFromStreamDirectly(in); } else { loadFromTransformedStream(in, encoding); } } finally { syncSupport.unlock(LockMode.WRITE); } }
Loads data from an input stream if the associated FileBased object implements the InputStreamSupport interface.
Params:
  • in – the input stream
Throws:
/** * Loads data from an input stream if the associated {@code FileBased} * object implements the {@code InputStreamSupport} interface. * * @param in the input stream * @throws ConfigurationException if an error occurs */
private void loadFromStreamDirectly(final InputStream in) throws ConfigurationException { try { ((InputStreamSupport) getContent()).read(in); } catch (final IOException e) { throw new ConfigurationException(e); } }
Internal helper method for transforming an input stream to a reader and reading its content.
Params:
  • in – the input stream
  • encoding – the encoding
Throws:
/** * Internal helper method for transforming an input stream to a reader and * reading its content. * * @param in the input stream * @param encoding the encoding * @throws ConfigurationException if an error occurs */
private void loadFromTransformedStream(final InputStream in, final String encoding) throws ConfigurationException { Reader reader = null; if (encoding != null) { try { reader = new InputStreamReader(in, encoding); } catch (final UnsupportedEncodingException e) { throw new ConfigurationException( "The requested encoding is not supported, try the default encoding.", e); } } if (reader == null) { reader = new InputStreamReader(in); } loadFromReader(reader); }
Internal helper method for loading a file from the given reader.
Params:
  • in – the reader
Throws:
/** * Internal helper method for loading a file from the given reader. * * @param in the reader * @throws ConfigurationException if an error occurs */
private void loadFromReader(final Reader in) throws ConfigurationException { fireLoadingEvent(); try { getContent().read(in); } catch (final IOException ioex) { throw new ConfigurationException(ioex); } finally { fireLoadedEvent(); } }
Internal helper method for saving data to the internal location stored for this object.
Params:
  • locator – the current FileLocator
Throws:
/** * Internal helper method for saving data to the internal location stored * for this object. * * @param locator the current {@code FileLocator} * @throws ConfigurationException if an error occurs during the save * operation */
private void save(final FileLocator locator) throws ConfigurationException { if (!FileLocatorUtils.isLocationDefined(locator)) { throw new ConfigurationException("No file location has been set!"); } if (locator.getSourceURL() != null) { save(locator.getSourceURL(), locator); } else { save(locator.getFileName(), locator); } }
Internal helper method for saving data to the given file name.
Params:
  • fileName – the path to the target file
  • locator – the current FileLocator
Throws:
/** * Internal helper method for saving data to the given file name. * * @param fileName the path to the target file * @param locator the current {@code FileLocator} * @throws ConfigurationException if an error occurs during the save * operation */
private void save(final String fileName, final FileLocator locator) throws ConfigurationException { URL url; try { url = FileLocatorUtils.obtainFileSystem(locator).getURL( locator.getBasePath(), fileName); } catch (final MalformedURLException e) { throw new ConfigurationException(e); } if (url == null) { throw new ConfigurationException( "Cannot locate configuration source " + fileName); } save(url, locator); }
Internal helper method for saving data to the given URL.
Params:
  • url – the target URL
  • locator – the FileLocator
Throws:
/** * Internal helper method for saving data to the given URL. * * @param url the target URL * @param locator the {@code FileLocator} * @throws ConfigurationException if an error occurs during the save * operation */
private void save(final URL url, final FileLocator locator) throws ConfigurationException { OutputStream out = null; try { out = FileLocatorUtils.obtainFileSystem(locator).getOutputStream(url); saveToStream(out, locator.getEncoding(), url); if (out instanceof VerifiableOutputStream) { try { ((VerifiableOutputStream) out).verify(); } catch (final IOException e) { throw new ConfigurationException(e); } } } finally { closeSilent(out); } }
Internal helper method for saving data to the given File.
Params:
  • file – the target file
  • locator – the current FileLocator
Throws:
/** * Internal helper method for saving data to the given {@code File}. * * @param file the target file * @param locator the current {@code FileLocator} * @throws ConfigurationException if an error occurs during the save * operation */
private void save(final File file, final FileLocator locator) throws ConfigurationException { OutputStream out = null; try { out = FileLocatorUtils.obtainFileSystem(locator).getOutputStream(file); saveToStream(out, locator.getEncoding(), file.toURI().toURL()); } catch (final MalformedURLException muex) { throw new ConfigurationException(muex); } finally { closeSilent(out); } }
Internal helper method for saving a file to the given output stream.
Params:
  • out – the output stream
  • locator – the current FileLocator
Throws:
/** * Internal helper method for saving a file to the given output stream. * * @param out the output stream * @param locator the current {@code FileLocator} * @throws ConfigurationException if an error occurs during the save * operation */
private void save(final OutputStream out, final FileLocator locator) throws ConfigurationException { save(out, locator.getEncoding()); }
Internal helper method for saving a file to the given stream.
Params:
  • out – the output stream
  • encoding – the encoding
  • url – the URL of the output file if known
Throws:
/** * Internal helper method for saving a file to the given stream. * * @param out the output stream * @param encoding the encoding * @param url the URL of the output file if known * @throws ConfigurationException if an error occurs */
private void saveToStream(final OutputStream out, final String encoding, final URL url) throws ConfigurationException { checkContent(); final SynchronizerSupport syncSupport = fetchSynchronizerSupport(); syncSupport.lock(LockMode.WRITE); try { injectFileLocator(url); Writer writer = null; if (encoding != null) { try { writer = new OutputStreamWriter(out, encoding); } catch (final UnsupportedEncodingException e) { throw new ConfigurationException( "The requested encoding is not supported, try the default encoding.", e); } } if (writer == null) { writer = new OutputStreamWriter(out); } saveToWriter(writer); } finally { syncSupport.unlock(LockMode.WRITE); } }
Internal helper method for saving a file into the given writer.
Params:
  • out – the writer
Throws:
/** * Internal helper method for saving a file into the given writer. * * @param out the writer * @throws ConfigurationException if an error occurs */
private void saveToWriter(final Writer out) throws ConfigurationException { fireSavingEvent(); try { getContent().write(out); } catch (final IOException ioex) { throw new ConfigurationException(ioex); } finally { fireSavedEvent(); } }
Creates a FileLocator which is a copy of the passed in one, but has the given file name set to reference the target file.
Params:
  • fileName – the file name
  • locator – the FileLocator to copy
Returns:the manipulated FileLocator with the file name
/** * Creates a {@code FileLocator} which is a copy of the passed in one, but * has the given file name set to reference the target file. * * @param fileName the file name * @param locator the {@code FileLocator} to copy * @return the manipulated {@code FileLocator} with the file name */
private FileLocator createLocatorWithFileName(final String fileName, final FileLocator locator) { return FileLocatorUtils.fileLocator(locator).sourceURL(null) .fileName(fileName).create(); }
Checks whether a content object is available. If not, an exception is thrown. This method is called whenever the content object is accessed.
Throws:
  • ConfigurationException – if not content object is defined
/** * Checks whether a content object is available. If not, an exception is * thrown. This method is called whenever the content object is accessed. * * @throws ConfigurationException if not content object is defined */
private void checkContent() throws ConfigurationException { if (getContent() == null) { throw new ConfigurationException("No content available!"); } }
Checks whether a content object is available and returns the current FileLocator. If there is no content object, an exception is thrown. This is a typical operation to be performed before a load() or save() operation.
Returns:the current FileLocator to be used for the calling operation
/** * Checks whether a content object is available and returns the current * {@code FileLocator}. If there is no content object, an exception is * thrown. This is a typical operation to be performed before a load() or * save() operation. * * @return the current {@code FileLocator} to be used for the calling * operation */
private FileLocator checkContentAndGetLocator() throws ConfigurationException { checkContent(); return getFileLocator(); }
Notifies the registered listeners about the start of a load operation.
/** * Notifies the registered listeners about the start of a load operation. */
private void fireLoadingEvent() { for (final FileHandlerListener l : listeners) { l.loading(this); } }
Notifies the registered listeners about a completed load operation.
/** * Notifies the registered listeners about a completed load operation. */
private void fireLoadedEvent() { for (final FileHandlerListener l : listeners) { l.loaded(this); } }
Notifies the registered listeners about the start of a save operation.
/** * Notifies the registered listeners about the start of a save operation. */
private void fireSavingEvent() { for (final FileHandlerListener l : listeners) { l.saving(this); } }
Notifies the registered listeners about a completed save operation.
/** * Notifies the registered listeners about a completed save operation. */
private void fireSavedEvent() { for (final FileHandlerListener l : listeners) { l.saved(this); } }
Notifies the registered listeners about a property update.
/** * Notifies the registered listeners about a property update. */
private void fireLocationChangedEvent() { for (final FileHandlerListener l : listeners) { l.locationChanged(this); } }
Normalizes URLs to files. Ensures that file URLs start with the correct protocol.
Params:
  • fileName – the string to be normalized
Returns:the normalized file URL
/** * Normalizes URLs to files. Ensures that file URLs start with the correct * protocol. * * @param fileName the string to be normalized * @return the normalized file URL */
private static String normalizeFileURL(String fileName) { if (fileName != null && fileName.startsWith(FILE_SCHEME) && !fileName.startsWith(FILE_SCHEME_SLASH)) { fileName = FILE_SCHEME_SLASH + fileName.substring(FILE_SCHEME.length()); } return fileName; }
A helper method for closing a stream. Occurring exceptions will be ignored.
Params:
  • cl – the stream to be closed (may be null)
/** * A helper method for closing a stream. Occurring exceptions will be * ignored. * * @param cl the stream to be closed (may be <b>null</b>) */
private static void closeSilent(final Closeable cl) { try { if (cl != null) { cl.close(); } } catch (final IOException e) { LogFactory.getLog(FileHandler.class).warn("Exception when closing " + cl, e); } }
Creates a File object from the content of the given FileLocator object. If the locator is not defined, result is null.
Params:
  • loc – the FileLocator
Returns:a File object pointing to the associated file
/** * Creates a {@code File} object from the content of the given * {@code FileLocator} object. If the locator is not defined, result is * <b>null</b>. * * @param loc the {@code FileLocator} * @return a {@code File} object pointing to the associated file */
private static File createFile(final FileLocator loc) { if (loc.getFileName() == null && loc.getSourceURL() == null) { return null; } else if (loc.getSourceURL() != null) { return FileLocatorUtils.fileFromURL(loc.getSourceURL()); } else { return FileLocatorUtils.getFile(loc.getBasePath(), loc.getFileName()); } }
Creates an uninitialized file locator.
Returns:the locator
/** * Creates an uninitialized file locator. * * @return the locator */
private static FileLocator emptyFileLocator() { return FileLocatorUtils.fileLocator().create(); }
Helper method for checking a file handler which is to be copied. Throws an exception if the handler is null.
Params:
  • c – the FileHandler from which to copy the location
Returns:the same FileHandler
/** * Helper method for checking a file handler which is to be copied. Throws * an exception if the handler is <b>null</b>. * * @param c the {@code FileHandler} from which to copy the location * @return the same {@code FileHandler} */
private static FileHandler checkSourceHandler(final FileHandler c) { if (c == null) { throw new IllegalArgumentException( "FileHandler to assign must not be null!"); } return c; }
An internal class that performs all update operations of the handler's FileLocator in a safe way even if there is concurrent access. This class implements anon-blocking algorithm for replacing the immutable FileLocator instance stored in an atomic reference by a manipulated instance. (If we already had lambdas, this could be done without a class in a more elegant way.)
/** * An internal class that performs all update operations of the handler's * {@code FileLocator} in a safe way even if there is concurrent access. * This class implements anon-blocking algorithm for replacing the immutable * {@code FileLocator} instance stored in an atomic reference by a * manipulated instance. (If we already had lambdas, this could be done * without a class in a more elegant way.) */
private abstract class Updater {
Performs an update of the enclosing file handler's FileLocator object.
/** * Performs an update of the enclosing file handler's * {@code FileLocator} object. */
public void update() { boolean done; do { final FileLocator oldLocator = fileLocator.get(); final FileLocatorBuilder builder = FileLocatorUtils.fileLocator(oldLocator); updateBuilder(builder); done = fileLocator.compareAndSet(oldLocator, builder.create()); } while (!done); fireLocationChangedEvent(); }
Updates the passed in builder object to apply the manipulation to be performed by this Updater. The builder has been setup with the former content of the FileLocator to be manipulated.
Params:
  • builder – the builder for creating an updated FileLocator
/** * Updates the passed in builder object to apply the manipulation to be * performed by this {@code Updater}. The builder has been setup with * the former content of the {@code FileLocator} to be manipulated. * * @param builder the builder for creating an updated * {@code FileLocator} */
protected abstract void updateBuilder(FileLocatorBuilder builder); } }