All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.refcodes.properties.PolyglotPropertiesBuilder Maven / Gradle / Ivy

Go to download

This artifact provides means to read configuration data from various different locations such as properties from JAR files, file system files or remote HTTP addresses or GIT repositories.

The newest version!
// /////////////////////////////////////////////////////////////////////////////
// REFCODES.ORG
// =============================================================================
// This code is copyright (c) by Siegfried Steiner, Munich, Germany, distributed
// on an "AS IS" BASIS WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, and licen-
// sed under the following (see "http://en.wikipedia.org/wiki/Multi-licensing")
// licenses:
// =============================================================================
// GNU General Public License, v3.0 ("http://www.gnu.org/licenses/gpl-3.0.html")
// together with the GPL linking exception applied; as being applied by the GNU
// Classpath ("http://www.gnu.org/software/classpath/license.html")
// =============================================================================
// Apache License, v2.0 ("http://www.apache.org/licenses/TEXT-2.0")
// =============================================================================
// Please contact the copyright holding author(s) of the software artifacts in
// question for licensing issues not being covered by the above listed licenses,
// also regarding commercial licensing models or regarding the compatibility
// with other open source licenses.
// /////////////////////////////////////////////////////////////////////////////

package org.refcodes.properties;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;

import org.refcodes.data.Delimiter;
import org.refcodes.properties.JavaPropertiesBuilder.JavaPropertiesBuilderFactory;
import org.refcodes.properties.JsonPropertiesBuilder.JsonPropertiesBuilderFactory;
import org.refcodes.properties.ResourceProperties.ResourcePropertiesBuilder;
import org.refcodes.properties.ResourcePropertiesFactory.ResourcePropertiesBuilderFactory;
import org.refcodes.properties.TomlPropertiesBuilder.TomlPropertiesBuilderFactory;
import org.refcodes.properties.XmlPropertiesBuilder.XmlPropertiesBuilderFactory;
import org.refcodes.properties.YamlPropertiesBuilder.YamlPropertiesBuilderFactory;
import org.refcodes.runtime.ConfigLocator;

/**
 * Implementation of the {@link ResourcePropertiesBuilder} interface with
 * support of so called "{@link PolyglotPropertiesBuilder}" (or just
 * "properties"). For {@link PolyglotPropertiesBuilder}, see
 * "https://en.wikipedia.org/wiki/.properties".
 */
public class PolyglotPropertiesBuilder extends AbstractResourcePropertiesBuilderDecorator {

	// /////////////////////////////////////////////////////////////////////
	// VARIABLES:
	// /////////////////////////////////////////////////////////////////////

	private final DocumentMetrics _documentMetrics;

	// /////////////////////////////////////////////////////////////////////////
	// CONSTRUCTORS:
	// /////////////////////////////////////////////////////////////////////////

	/**
	 * Loads the {@link PolyglotPropertiesBuilder} from the given file's path.
	 *
	 * @param aResourceClass The class which's class loader is to take care of
	 *        loading the properties (from inside a JAR).
	 * @param aFilePath The file path of the class's resources from which to
	 *        load the properties.
	 * 
	 * @throws IOException thrown in case accessing or processing the properties
	 *         file failed.
	 * @throws ParseException Signals that an error has been reached
	 *         unexpectedly while parsing the data to be loaded.
	 */
	public PolyglotPropertiesBuilder( Class aResourceClass, String aFilePath ) throws IOException, ParseException {
		super( new PolyglotPropertiesBuilderFactory().toProperties( aResourceClass, aFilePath ) );
		_documentMetrics = DocumentNotation.DEFAULT;
	}

	/**
	 * Loads the {@link PolyglotPropertiesBuilder} from the given file's path.
	 *
	 * @param aResourceClass The class which's class loader is to take care of
	 *        loading the properties (from inside a JAR).
	 * @param aFilePath The file path of the class's resources from which to
	 *        load the properties.
	 * @param aDocumentMetrics When set to true then unmarshaling
	 *        (XML) documents will preserve the preserve the root element
	 *        (envelope). As an (XML) document requires a root element, the root
	 *        element often is provided merely as of syntactic reasons and must
	 *        be omitted as of semantic reasons. Unmarshaling functionality
	 *        therefore by default skips the root elelemt, as this is considered
	 *        merely to serve as an envelope. This behavior can be overridden by
	 *        setting this property to true.
	 * 
	 * @throws IOException thrown in case accessing or processing the properties
	 *         file failed.
	 * @throws ParseException Signals that an error has been reached
	 *         unexpectedly while parsing the data to be loaded.
	 */
	public PolyglotPropertiesBuilder( Class aResourceClass, String aFilePath, DocumentMetrics aDocumentMetrics ) throws IOException, ParseException {
		super( new PolyglotPropertiesBuilderFactory( aDocumentMetrics ).toProperties( aResourceClass, aFilePath ) );
		_documentMetrics = aDocumentMetrics;
	}

	/**
	 * Loads the {@link PolyglotPropertiesBuilder} from the given file's path. A
	 * provided {@link ConfigLocator} describes the locations to additional
	 * crawl for the desired file. Finally (if nothing else succeeds) the
	 * properties are loaded by the provided class's class loader which takes
	 * care of loading the properties (in case the file path is a relative path,
	 * also the absolute path with a prefixed path delimiter "/" is probed).
	 *
	 * @param aResourceClass The class which's class loader is to take care of
	 *        loading the properties (from inside a JAR).
	 * @param aFilePath The file path of the class's resources from which to
	 *        load the properties.
	 * @param aConfigLocator The {@link ConfigLocator} describes the locations
	 *        to additional crawl for the desired file.
	 * 
	 * @throws IOException thrown in case accessing or processing the properties
	 *         file failed.
	 * @throws ParseException Signals that an error has been reached
	 *         unexpectedly while parsing the data to be loaded.
	 */
	public PolyglotPropertiesBuilder( Class aResourceClass, String aFilePath, ConfigLocator aConfigLocator ) throws IOException, ParseException {
		super( new PolyglotPropertiesBuilderFactory().toProperties( aResourceClass, aFilePath, aConfigLocator ) );
		_documentMetrics = DocumentNotation.DEFAULT;
	}

	/**
	 * Loads the {@link PolyglotPropertiesBuilder} from the given file's path. A
	 * provided {@link ConfigLocator} describes the locations to additional
	 * crawl for the desired file. Finally (if nothing else succeeds) the
	 * properties are loaded by the provided class's class loader which takes
	 * care of loading the properties (in case the file path is a relative path,
	 * also the absolute path with a prefixed path delimiter "/" is probed).
	 *
	 * @param aResourceClass The class which's class loader is to take care of
	 *        loading the properties (from inside a JAR).
	 * @param aFilePath The file path of the class's resources from which to
	 *        load the properties.
	 * @param aConfigLocator The {@link ConfigLocator} describes the locations
	 *        to additional crawl for the desired file.
	 * @param aDocumentMetrics Provides various metrics which may be tweaked
	 *        when marshaling or unmarshaling documents of various nations (such
	 *        as INI, XML, YAML, JSON, TOML, PROPERTIES, etc.).
	 * 
	 * @throws IOException thrown in case accessing or processing the properties
	 *         file failed.
	 * @throws ParseException Signals that an error has been reached
	 *         unexpectedly while parsing the data to be loaded.
	 */
	public PolyglotPropertiesBuilder( Class aResourceClass, String aFilePath, ConfigLocator aConfigLocator, DocumentMetrics aDocumentMetrics ) throws IOException, ParseException {
		super( new PolyglotPropertiesBuilderFactory( aDocumentMetrics ).toProperties( aResourceClass, aFilePath, aConfigLocator ) );
		_documentMetrics = aDocumentMetrics;
	}

	/**
	 * Loads the {@link PolyglotPropertiesBuilder} from the given {@link File}.
	 *
	 * @param aFile The {@link File} from which to load the properties.
	 * 
	 * @throws IOException thrown in case accessing or processing the properties
	 *         file failed.
	 * @throws ParseException Signals that an error has been reached
	 *         unexpectedly while parsing the data to be loaded.
	 */
	public PolyglotPropertiesBuilder( File aFile ) throws IOException, ParseException {
		super( new PolyglotPropertiesBuilderFactory().toProperties( aFile ) );
		_documentMetrics = DocumentNotation.DEFAULT;
	}

	/**
	 * Loads the {@link PolyglotPropertiesBuilder} from the given {@link File}.
	 *
	 * @param aFile The {@link File} from which to load the properties.
	 * @param aDocumentMetrics Provides various metrics which may be tweaked
	 *        when marshaling or unmarshaling documents of various nations (such
	 *        as INI, XML, YAML, JSON, TOML, PROPERTIES, etc.).
	 * 
	 * @throws IOException thrown in case accessing or processing the properties
	 *         file failed.
	 * @throws ParseException Signals that an error has been reached
	 *         unexpectedly while parsing the data to be loaded.
	 */
	public PolyglotPropertiesBuilder( File aFile, DocumentMetrics aDocumentMetrics ) throws IOException, ParseException {
		super( new PolyglotPropertiesBuilderFactory( aDocumentMetrics ).toProperties( aFile ) );
		_documentMetrics = aDocumentMetrics;
	}

	/**
	 * Loads or seeks the {@link PolyglotPropertiesBuilder} from the given
	 * {@link File}. A provided {@link ConfigLocator} describes the locations to
	 * additional crawl for the desired file.
	 * 
	 * @param aFile The {@link File} from which to load the properties.
	 * @param aConfigLocator The {@link ConfigLocator} describes the locations
	 *        to additional crawl for the desired file.
	 * 
	 * @throws IOException thrown in case accessing or processing the properties
	 *         file failed.
	 * @throws ParseException Signals that an error has been reached
	 *         unexpectedly while parsing the data to be loaded.
	 */
	public PolyglotPropertiesBuilder( File aFile, ConfigLocator aConfigLocator ) throws IOException, ParseException {
		super( new PolyglotPropertiesBuilderFactory().toProperties( aFile, aConfigLocator ) );
		_documentMetrics = DocumentNotation.DEFAULT;
	}

	/**
	 * Loads or seeks the {@link PolyglotPropertiesBuilder} from the given
	 * {@link File}. A provided {@link ConfigLocator} describes the locations to
	 * additional crawl for the desired file.
	 *
	 * @param aFile The {@link File} from which to load the properties.
	 * @param aConfigLocator The {@link ConfigLocator} describes the locations
	 *        to additional crawl for the desired file.
	 * @param aDocumentMetrics Provides various metrics which may be tweaked
	 *        when marshaling or unmarshaling documents of various nations (such
	 *        as INI, XML, YAML, JSON, TOML, PROPERTIES, etc.).
	 * 
	 * @throws IOException thrown in case accessing or processing the properties
	 *         file failed.
	 * @throws ParseException Signals that an error has been reached
	 *         unexpectedly while parsing the data to be loaded.
	 */
	public PolyglotPropertiesBuilder( File aFile, ConfigLocator aConfigLocator, DocumentMetrics aDocumentMetrics ) throws IOException, ParseException {
		super( new PolyglotPropertiesBuilderFactory( aDocumentMetrics ).toProperties( aFile, aConfigLocator ) );
		_documentMetrics = aDocumentMetrics;
	}

	/**
	 * Reads the {@link PolyglotPropertiesBuilder} from the given
	 * {@link InputStream}.
	 *
	 * @param aInputStream The {@link InputStream} from which to read the
	 *        properties.
	 * 
	 * @throws IOException thrown in case accessing or processing the properties
	 *         file failed.
	 * @throws ParseException Signals that an error has been reached
	 *         unexpectedly while parsing the data to be loaded.
	 */
	public PolyglotPropertiesBuilder( InputStream aInputStream ) throws IOException, ParseException {
		super( new PolyglotPropertiesBuilderFactory().toProperties( aInputStream ) );
		_documentMetrics = DocumentNotation.DEFAULT;
	}

	/**
	 * Reads the {@link PolyglotPropertiesBuilder} from the given
	 * {@link InputStream}.
	 *
	 * @param aInputStream The {@link InputStream} from which to read the
	 *        properties.
	 * @param aDocumentMetrics Provides various metrics which may be tweaked
	 *        when marshaling or unmarshaling documents of various nations (such
	 *        as INI, XML, YAML, JSON, TOML, PROPERTIES, etc.).
	 * 
	 * @throws IOException thrown in case accessing or processing the properties
	 *         file failed.
	 * @throws ParseException Signals that an error has been reached
	 *         unexpectedly while parsing the data to be loaded.
	 */
	public PolyglotPropertiesBuilder( InputStream aInputStream, DocumentMetrics aDocumentMetrics ) throws IOException, ParseException {
		super( new PolyglotPropertiesBuilderFactory( aDocumentMetrics ).toProperties( aInputStream ) );
		_documentMetrics = aDocumentMetrics;
	}

	/**
	 * Create a {@link PolyglotPropertiesBuilder} instance containing the
	 * elements of the provided {@link Map} instance using the default path
	 * delimiter "/" ({@link Delimiter#PATH}) for the path declarations.
	 *
	 * @param aMap the properties to be added.
	 */
	public PolyglotPropertiesBuilder( Map aMap ) {
		super( new PolyglotPropertiesBuilderFactory().toProperties( aMap ) );
		_documentMetrics = DocumentNotation.DEFAULT;
	}

	/**
	 * Create a {@link PolyglotPropertiesBuilder} instance containing the
	 * elements of the provided {@link Map} instance using the default path
	 * delimiter "/" ({@link Delimiter#PATH}) for the path declarations.
	 *
	 * @param aMap the properties to be added.
	 * @param aDocumentMetrics Provides various metrics which may be tweaked
	 *        when marshaling or unmarshaling documents of various nations (such
	 *        as INI, XML, YAML, JSON, TOML, PROPERTIES, etc.).
	 */
	public PolyglotPropertiesBuilder( Map aMap, DocumentMetrics aDocumentMetrics ) {
		super( new PolyglotPropertiesBuilderFactory( aDocumentMetrics ).toProperties( aMap ) );
		_documentMetrics = aDocumentMetrics;
	}

	/**
	 * Create a {@link PolyglotPropertiesBuilder} instance containing the
	 * elements as of {@link MutablePathMap#insert(Object)} using the default
	 * path delimiter "/" ({@link Delimiter#PATH}) for the path declarations:
	 * "Inspects the given object and adds all elements found in the given
	 * object. Elements of type {@link Map}, {@link Collection} and arrays are
	 * identified and handled as of their type: The path for each value in a
	 * {@link Map} is appended with its according key. The path for each value
	 * in a {@link Collection} or array is appended with its according index of
	 * occurrence (in case of a {@link List} or an array, its actual index). In
	 * case of reflection, the path for each member is appended with its
	 * according mamber's name. All elements (e.g. the members and values) are
	 * inspected recursively which results in the according paths of the
	 * terminating values."
	 *
	 * @param aObj The object from which the elements are to be added.
	 */
	public PolyglotPropertiesBuilder( Object aObj ) {
		super( new PolyglotPropertiesBuilderFactory().toProperties( aObj ) );
		_documentMetrics = DocumentNotation.DEFAULT;
	}

	/**
	 * Create a {@link PolyglotPropertiesBuilder} instance containing the
	 * elements as of {@link MutablePathMap#insert(Object)} using the default
	 * path delimiter "/" ({@link Delimiter#PATH}) for the path declarations:
	 * "Inspects the given object and adds all elements found in the given
	 * object. Elements of type {@link Map}, {@link Collection} and arrays are
	 * identified and handled as of their type: The path for each value in a
	 * {@link Map} is appended with its according key. The path for each value
	 * in a {@link Collection} or array is appended with its according index of
	 * occurrence (in case of a {@link List} or an array, its actual index). In
	 * case of reflection, the path for each member is appended with its
	 * according mamber's name. All elements (e.g. the members and values) are
	 * inspected recursively which results in the according paths of the
	 * terminating values."
	 *
	 * @param aObj The object from which the elements are to be added.
	 * @param aDocumentMetrics Provides various metrics which may be tweaked
	 *        when marshaling or unmarshaling documents of various nations (such
	 *        as INI, XML, YAML, JSON, TOML, PROPERTIES, etc.).
	 */
	public PolyglotPropertiesBuilder( Object aObj, DocumentMetrics aDocumentMetrics ) {
		super( new PolyglotPropertiesBuilderFactory( aDocumentMetrics ).toProperties( aObj ) );
		_documentMetrics = aDocumentMetrics;
	}

	/**
	 * Create a {@link PolyglotPropertiesBuilder} instance containing the
	 * elements of the provided {@link Properties} instance using the default
	 * path delimiter "/" ({@link Delimiter#PATH}) for the path declarations.
	 *
	 * @param aProperties the properties to be added.
	 */
	public PolyglotPropertiesBuilder( Properties aProperties ) {
		super( new PolyglotPropertiesBuilderFactory().toProperties( aProperties ) );
		_documentMetrics = DocumentNotation.DEFAULT;
	}

	/**
	 * Create a {@link PolyglotPropertiesBuilder} instance containing the
	 * elements of the provided {@link Properties} instance using the default
	 * path delimiter "/" ({@link Delimiter#PATH}) for the path declarations.
	 *
	 * @param aProperties the properties to be added.
	 * @param aDocumentMetrics When set to true then unmarshaling
	 *        (XML) documents will preserve the preserve the root element
	 *        (envelope). As an (XML) document requires a root element, the root
	 *        element often is provided merely as of syntactic reasons and must
	 *        be omitted as of semantic reasons. Unmarshaling functionality
	 *        therefore by default skips the root elelemt, as this is considered
	 *        merely to serve as an envelope. This behavior can be overridden by
	 *        setting this property to true.
	 */
	public PolyglotPropertiesBuilder( Properties aProperties, DocumentMetrics aDocumentMetrics ) {
		super( new PolyglotPropertiesBuilderFactory( aDocumentMetrics ).toProperties( aProperties ) );
		_documentMetrics = aDocumentMetrics;
	}

	/**
	 * Create a {@link PolyglotPropertiesBuilder} instance containing the
	 * elements of the provided {@link PropertiesBuilder} instance using the
	 * default path delimiter "/" ({@link Delimiter#PATH}) for the path
	 * declarations.
	 *
	 * @param aPropertiesBuilder the properties to be added.
	 */
	public PolyglotPropertiesBuilder( PropertiesBuilder aPropertiesBuilder ) {
		super( new PolyglotPropertiesBuilderFactory().toProperties( aPropertiesBuilder ) );
		_documentMetrics = DocumentNotation.DEFAULT;
	}

	/**
	 * Create a {@link PolyglotPropertiesBuilder} instance containing the
	 * elements of the provided {@link PropertiesBuilder} instance using the
	 * default path delimiter "/" ({@link Delimiter#PATH}) for the path
	 * declarations.
	 *
	 * @param aPropertiesBuilder the properties to be added.
	 * @param aDocumentMetrics When set to true then unmarshaling
	 *        (XML) documents will preserve the preserve the root element
	 *        (envelope). As an (XML) document requires a root element, the root
	 *        element often is provided merely as of syntactic reasons and must
	 *        be omitted as of semantic reasons. Unmarshaling functionality
	 *        therefore by default skips the root elelemt, as this is considered
	 *        merely to serve as an envelope. This behavior can be overridden by
	 *        setting this property to true.
	 */
	public PolyglotPropertiesBuilder( PropertiesBuilder aPropertiesBuilder, DocumentMetrics aDocumentMetrics ) {
		super( new PolyglotPropertiesBuilderFactory( aDocumentMetrics ).toProperties( aPropertiesBuilder ) );
		_documentMetrics = aDocumentMetrics;
	}

	/**
	 * Loads the {@link PolyglotPropertiesBuilder} from the given file's path.
	 *
	 * @param aFilePath The path to the file from which to load the properties.
	 * 
	 * @throws IOException thrown in case accessing or processing the properties
	 *         file failed.
	 * @throws ParseException Signals that an error has been reached
	 *         unexpectedly while parsing the data to be loaded.
	 */
	public PolyglotPropertiesBuilder( String aFilePath ) throws IOException, ParseException {
		super( new PolyglotPropertiesBuilderFactory().toProperties( aFilePath ) );
		_documentMetrics = DocumentNotation.DEFAULT;
	}

	/**
	 * Loads the {@link PolyglotPropertiesBuilder} from the given file's path.
	 *
	 * @param aFilePath The path to the file from which to load the properties.
	 * @param aDocumentMetrics Provides various metrics which may be tweaked
	 *        when marshaling or unmarshaling documents of various nations (such
	 *        as INI, XML, YAML, JSON, TOML, PROPERTIES, etc.).
	 * 
	 * @throws IOException thrown in case accessing or processing the properties
	 *         file failed.
	 * @throws ParseException Signals that an error has been reached
	 *         unexpectedly while parsing the data to be loaded.
	 */
	public PolyglotPropertiesBuilder( String aFilePath, DocumentMetrics aDocumentMetrics ) throws IOException, ParseException {
		super( new PolyglotPropertiesBuilderFactory( aDocumentMetrics ).toProperties( aFilePath ) );
		_documentMetrics = aDocumentMetrics;
	}

	/**
	 * Loads the {@link PolyglotPropertiesBuilder} from the given file's path. A
	 * provided {@link ConfigLocator} describes the locations to additional
	 * crawl for the desired file.
	 * 
	 * @param aFilePath The path to the file from which to load the properties.
	 * @param aConfigLocator The {@link ConfigLocator} describes the locations
	 *        to additional crawl for the desired file.
	 * 
	 * @throws IOException thrown in case accessing or processing the properties
	 *         file failed.
	 * @throws ParseException Signals that an error has been reached
	 *         unexpectedly while parsing the data to be loaded.
	 */
	public PolyglotPropertiesBuilder( String aFilePath, ConfigLocator aConfigLocator ) throws IOException, ParseException {
		super( new PolyglotPropertiesBuilderFactory().toProperties( aFilePath, aConfigLocator ) );
		_documentMetrics = DocumentNotation.DEFAULT;
	}

	/**
	 * Loads the {@link PolyglotPropertiesBuilder} from the given file's path. A
	 * provided {@link ConfigLocator} describes the locations to additional
	 * crawl for the desired file.
	 *
	 * @param aFilePath The path to the file from which to load the properties.
	 * @param aConfigLocator The {@link ConfigLocator} describes the locations
	 *        to additional crawl for the desired file.
	 * @param aDocumentMetrics Provides various metrics which may be tweaked
	 *        when marshaling or unmarshaling documents of various nations (such
	 *        as INI, XML, YAML, JSON, TOML, PROPERTIES, etc.).
	 * 
	 * @throws IOException thrown in case accessing or processing the properties
	 *         file failed.
	 * @throws ParseException Signals that an error has been reached
	 *         unexpectedly while parsing the data to be loaded.
	 */
	public PolyglotPropertiesBuilder( String aFilePath, ConfigLocator aConfigLocator, DocumentMetrics aDocumentMetrics ) throws IOException, ParseException {
		super( new PolyglotPropertiesBuilderFactory( aDocumentMetrics ).toProperties( aFilePath, aConfigLocator ) );
		_documentMetrics = aDocumentMetrics;
	}

	/**
	 * Loads the {@link PolyglotPropertiesBuilder} from the given {@link URL}.
	 *
	 * @param aUrl The {@link URL} from which to read the properties.
	 * 
	 * @throws IOException thrown in case accessing or processing the properties
	 *         file failed.
	 * @throws ParseException Signals that an error has been reached
	 *         unexpectedly while parsing the data to be loaded.
	 */
	public PolyglotPropertiesBuilder( URL aUrl ) throws IOException, ParseException {
		super( new PolyglotPropertiesBuilderFactory().toProperties( aUrl ) );
		_documentMetrics = DocumentNotation.DEFAULT;
	}

	/**
	 * Loads the {@link PolyglotPropertiesBuilder} from the given {@link URL}.
	 *
	 * @param aUrl The {@link URL} from which to read the properties.
	 * @param aDocumentMetrics Provides various metrics which may be tweaked
	 *        when marshaling or unmarshaling documents of various nations (such
	 *        as INI, XML, YAML, JSON, TOML, PROPERTIES, etc.).
	 * 
	 * @throws IOException thrown in case accessing or processing the properties
	 *         file failed.
	 * @throws ParseException Signals that an error has been reached
	 *         unexpectedly while parsing the data to be loaded.
	 */
	public PolyglotPropertiesBuilder( URL aUrl, DocumentMetrics aDocumentMetrics ) throws IOException, ParseException {
		super( new PolyglotPropertiesBuilderFactory( aDocumentMetrics ).toProperties( aUrl ) );
		_documentMetrics = aDocumentMetrics;
	}

	// /////////////////////////////////////////////////////////////////////////
	// METHODS:
	// /////////////////////////////////////////////////////////////////////////

	/**
	 * {@inheritDoc}
	 */
	@Override
	public Properties loadFrom( Class aResourceClass, String aFilePath ) throws IOException, ParseException {
		final PolyglotProperties theProperties = new PolyglotProperties( aResourceClass, aFilePath, _documentMetrics );
		insert( theProperties );
		return theProperties;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public Properties loadFrom( File aFile ) throws IOException, ParseException {
		final PolyglotProperties theProperties = new PolyglotProperties( aFile, _documentMetrics );
		insert( theProperties );
		return theProperties;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public Properties loadFrom( InputStream aInputStream ) throws IOException, ParseException {
		final PolyglotProperties theProperties = new PolyglotProperties( aInputStream, _documentMetrics );
		insert( theProperties );
		return theProperties;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public Properties loadFrom( String aFilePath ) throws IOException, ParseException {
		final PolyglotProperties theProperties = new PolyglotProperties( aFilePath, _documentMetrics );
		insert( theProperties );
		return theProperties;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public Properties loadFrom( URL aUrl ) throws IOException, ParseException {
		final PolyglotProperties theProperties = new PolyglotProperties( aUrl, _documentMetrics );
		insert( theProperties );
		return theProperties;
	}

	/**
	 * {@inheritDoc}
	 * 
	 * Attention: Reloads only the initially loaded properties!
	 */
	@Override
	public Properties reload() throws IOException, ParseException {
		return super.reload();
	}

	/**
	 * {@inheritDoc}
	 * 
	 * Attention: Reloads only the initially loaded properties!
	 */
	@Override
	public Properties reload( ReloadMode aReloadMode ) throws IOException, ParseException {
		return super.reload( aReloadMode );
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public Properties seekFrom( Class aResourceClass, String aFilePath, ConfigLocator aConfigLocator ) throws IOException, ParseException {
		final PolyglotProperties theProperties = new PolyglotProperties( aResourceClass, aFilePath, aConfigLocator, _documentMetrics );
		insert( theProperties );
		return theProperties;
	}

	// /////////////////////////////////////////////////////////////////////////
	// INNER CLASSES:
	// /////////////////////////////////////////////////////////////////////////

	/**
	 * The {@link PolyglotPropertiesBuilderFactory} is a meta factory using a
	 * collection of {@link ResourcePropertiesBuilderFactory} instances to
	 * deliver {@link ResourcePropertiesBuilder} instances. In case a properties
	 * file for a filename was not found, then the according factories filename
	 * extension
	 * ({@link ResourcePropertiesBuilderFactory#getFilenameSuffixes()}) is
	 * append to the filename and probing is repeated. Any factory method such
	 * as {@link #toProperties(Map)}, {@link #toProperties(Object)},
	 * {@link #toProperties(PropertiesBuilder)} or
	 * {@link #toProperties(PropertiesBuilder)} will return
	 * {@link ResourcePropertiesBuilder} created by the first added
	 * {@link ResourcePropertiesBuilderFactory} instance.
	 */
	public static class PolyglotPropertiesBuilderFactory implements ResourcePropertiesBuilderFactory {

		// /////////////////////////////////////////////////////////////////////
		// VARIABLES:
		// /////////////////////////////////////////////////////////////////////

		private final List _factories = new ArrayList<>();

		// /////////////////////////////////////////////////////////////////////
		// CONSTRUCTORS:
		// /////////////////////////////////////////////////////////////////////

		/**
		 * Initializes the {@link PolyglotPropertiesBuilderFactory} with a
		 * predefined set of {@link ResourcePropertiesBuilderFactory} instances.
		 */
		public PolyglotPropertiesBuilderFactory() {
			this( DocumentNotation.DEFAULT );
		}

		/**
		 * Initializes the {@link PolyglotPropertiesBuilderFactory} with a
		 * predefined set of {@link ResourcePropertiesBuilderFactory} instances.
		 *
		 * @param aDocumentMetrics When set to true then
		 *        unmarshaling (XML) documents will preserve the preserve the
		 *        root element (envelope). As an (XML) document requires a root
		 *        element, the root element often is provided merely as of
		 *        syntactic reasons and must be omitted as of semantic reasons.
		 *        Unmarshaling functionality therefore by default skips the root
		 *        elelemt, as this is considered merely to serve as an envelope.
		 *        This behavior can be overridden by setting this property to
		 *        true.
		 */
		public PolyglotPropertiesBuilderFactory( DocumentMetrics aDocumentMetrics ) {
			_factories.add( new TomlPropertiesBuilderFactory( aDocumentMetrics ) );
			_factories.add( new YamlPropertiesBuilderFactory() );
			_factories.add( new XmlPropertiesBuilderFactory( aDocumentMetrics ) );
			_factories.add( new JsonPropertiesBuilderFactory() );
			_factories.add( new JavaPropertiesBuilderFactory( aDocumentMetrics ) ); // Last as them Java properties eat all notations
		}

		/**
		 * Initializes the {@link PolyglotPropertiesBuilderFactory} with the
		 * given {@link ResourcePropertiesBuilderFactory} instances. Them
		 * factories will be queried in the order being provided.
		 * 
		 * @param aFactories The factories to be added.
		 */
		public PolyglotPropertiesBuilderFactory( ResourcePropertiesBuilderFactory... aFactories ) {
			for ( ResourcePropertiesBuilderFactory eFactory : aFactories ) {
				_factories.add( eFactory );
			}
		}

		// /////////////////////////////////////////////////////////////////////
		// METHODS:
		// /////////////////////////////////////////////////////////////////////

		/**
		 * Returns the filename extensions of the ResourcePropertiesFactory in
		 * the given order. {@inheritDoc}
		 */
		@Override
		public String[] getFilenameSuffixes() {
			final List theExtensions = new ArrayList<>();
			String[] eExtensions;
			for ( ResourcePropertiesFactory.ResourcePropertiesBuilderFactory _factorie : _factories ) {
				eExtensions = _factorie.getFilenameSuffixes();
				if ( eExtensions != null ) {
					for ( String eExtension : eExtensions ) {
						theExtensions.add( eExtension );
					}
				}
			}
			return theExtensions.toArray( new String[theExtensions.size()] );
		}

		/**
		 * {@inheritDoc}
		 */
		@Override
		public ResourcePropertiesBuilder toProperties( Class aResourceClass, String aFilePath, ConfigLocator aConfigLocator ) throws IOException, ParseException {
			Exception theCause = null;

			// Keep the precedence |-->
			try {
				return toPropertiesFromFilePath( aFilePath, aConfigLocator );
			}
			catch ( Exception e ) {
				if ( theCause == null ) {
					theCause = e;
				}
			}
			// Keep the precedence <--|

			for ( ResourcePropertiesBuilderFactory eFactory : _factories ) {
				try {
					return eFactory.toProperties( aResourceClass, aFilePath, aConfigLocator );
				}
				catch ( ParseException e ) {
					if ( eFactory.hasFilenameSuffix( aFilePath ) ) {
						throw new ParseException( "Error parsing file <" + aFilePath + "> at offset <" + e.getErrorOffset() + ">!", e.getErrorOffset() );
					}
					if ( theCause == null ) {
						theCause = e;
					}
				}
				catch ( Exception e ) {
					if ( theCause == null ) {
						theCause = e;
					}
				}
			}

			// Test with the factorie's filename extension |-->
			String[] eExtensions;
			String eFilePath;
			for ( ResourcePropertiesBuilderFactory eFactory : _factories ) {
				eExtensions = eFactory.getFilenameSuffixes();
				if ( eExtensions != null ) {
					for ( String eExtension : eExtensions ) {
						eFilePath = aFilePath + eExtension;
						try {
							return eFactory.toProperties( eFilePath, aConfigLocator );
						}
						catch ( ParseException e ) {
							if ( eFactory.hasFilenameSuffix( eFilePath ) ) {
								throw new ParseException( "Error parsing file <" + eFilePath + "> at offset <" + e.getErrorOffset() + ">!", e.getErrorOffset() );
							}
							if ( theCause == null ) {
								theCause = e;
							}
						}
						catch ( Exception e ) {
							if ( theCause == null ) {
								theCause = e;
							}
						}
					}
				}
			}
			// Test with the factorie's filename extension <--|
			if ( theCause instanceof IOException ) {
				throw (IOException) theCause;
			}
			if ( theCause instanceof ParseException ) {
				throw (ParseException) theCause;
			}
			throw new IOException( "No configuration file with name scheme <" + aFilePath + ".*> found, where a suffix (if applicable) might by one of the following: " + Arrays.toString( getFilenameSuffixes() ) );
		}

		/**
		 * {@inheritDoc}
		 */
		@Override
		public ResourcePropertiesBuilder toProperties( File aFile, ConfigLocator aConfigLocator ) throws IOException, ParseException {
			Exception theCause = null;

			// Keep the precedence |-->
			try {
				return toPropertiesFromFilePath( aFile.getPath(), aConfigLocator );
			}
			catch ( Exception e ) {
				if ( theCause == null ) {
					theCause = e;
				}
			}
			// Keep the precedence <--|

			for ( ResourcePropertiesBuilderFactory eFactory : _factories ) {
				try {
					return eFactory.toProperties( aFile, aConfigLocator );
				}
				catch ( Exception e ) {
					if ( theCause == null ) {
						theCause = e;
					}
				}
			}
			// Test with the factorie's filename extension |-->
			File eFile;
			String[] eExtensions;
			for ( ResourcePropertiesBuilderFactory eFactory : _factories ) {
				eExtensions = eFactory.getFilenameSuffixes();
				if ( eExtensions != null ) {
					for ( String eExtension : eExtensions ) {
						eFile = new File( aFile.getAbsolutePath() + eExtension );
						try {
							return eFactory.toProperties( eFile, aConfigLocator );
						}
						catch ( Exception e ) {
							if ( theCause == null ) {
								theCause = e;
							}
						}
					}
				}
			}
			// Test with the factorie's filename extension <--|
			if ( theCause instanceof IOException ) {
				throw (IOException) theCause;
			}
			if ( theCause instanceof ParseException ) {
				throw (ParseException) theCause;
			}
			throw new IOException( "No configuration file with (base) file <" + aFile.getPath() + "> found!" );
		}

		/**
		 * {@inheritDoc}
		 */
		@Override
		public ResourcePropertiesBuilder toProperties( InputStream aInputStream ) throws IOException, ParseException {
			Exception theCause = null;
			final byte[] theBytes = toByteArray( aInputStream );
			for ( ResourcePropertiesBuilderFactory eFactory : _factories ) {
				try {
					return eFactory.toProperties( new ByteArrayInputStream( theBytes ) );
				}
				catch ( Exception e ) {
					if ( theCause == null ) {
						theCause = e;
					}
				}
			}
			if ( theCause instanceof IOException ) {
				throw (IOException) theCause;
			}
			if ( theCause instanceof ParseException ) {
				throw (ParseException) theCause;
			}
			throw new IOException( "No configuration file for the given InputStream found!" );
		}

		/**
		 * Will return {@link ResourcePropertiesBuilder} created by the first
		 * added {@link ResourcePropertiesBuilderFactory} instance.
		 * {@inheritDoc}
		 */
		@Override
		public ResourcePropertiesBuilder toProperties( Map aPropertiesBuilder ) {
			return _factories.get( 0 ).toProperties( aPropertiesBuilder );
		}

		/**
		 * Will return {@link ResourcePropertiesBuilder} created by the first
		 * added {@link ResourcePropertiesBuilderFactory} instance.
		 * {@inheritDoc}
		 */
		@Override
		public ResourcePropertiesBuilder toProperties( Object aObj ) {
			return _factories.get( 0 ).toProperties( aObj );
		}

		/**
		 * Will return {@link ResourcePropertiesBuilder} created by the first
		 * added {@link ResourcePropertiesBuilderFactory} instance.
		 * {@inheritDoc}
		 */
		@Override
		public ResourcePropertiesBuilder toProperties( Properties aProperties ) {
			return _factories.get( 0 ).toProperties( aProperties );
		}

		/**
		 * Will return {@link ResourcePropertiesBuilder} created by the first
		 * added {@link ResourcePropertiesBuilderFactory} instance.
		 * {@inheritDoc}
		 */
		@Override
		public ResourcePropertiesBuilder toProperties( PropertiesBuilder aPropertiesBuilder ) {
			return _factories.get( 0 ).toProperties( aPropertiesBuilder );
		}

		/**
		 * {@inheritDoc}
		 */
		@Override
		public ResourcePropertiesBuilder toProperties( String aFilePath, ConfigLocator aConfigLocator ) throws IOException, ParseException {
			return toProperties( null, aFilePath, aConfigLocator );
		}

		/**
		 * {@inheritDoc}
		 */
		@Override
		public ResourcePropertiesBuilder toProperties( URL aUrl ) throws IOException, ParseException {
			Exception theCause = null;

			// Try file extension's factory first |-->
			try {
				final ResourcePropertiesBuilderFactory theFactory = fromFilenameExtension( aUrl.toExternalForm() );
				if ( theFactory != null ) {
					return theFactory.toProperties( aUrl );
				}
			}
			catch ( Exception e ) {
				if ( theCause == null ) {
					theCause = e;
				}
			}
			// Try file extension's factory first <--|

			for ( ResourcePropertiesBuilderFactory eFactory : _factories ) {
				try {
					return eFactory.toProperties( aUrl );
				}
				catch ( Exception e ) {
					if ( theCause == null ) {
						theCause = e;
					}
				}
			}
			// Test with the factorie's filename extension |-->
			URL eUrl;
			String[] eExtensions;
			for ( ResourcePropertiesBuilderFactory eFactory : _factories ) {
				eExtensions = eFactory.getFilenameSuffixes();
				if ( eExtensions != null ) {
					for ( String eExtension : eExtensions ) {
						eUrl = new URL( aUrl.toExternalForm() + eExtension );
						try {
							return eFactory.toProperties( eUrl );
						}
						catch ( Exception e ) {
							if ( theCause == null ) {
								theCause = e;
							}
						}
					}
				}
			}
			// Test with the factorie's filename extension <--|
			if ( theCause instanceof IOException ) {
				throw (IOException) theCause;
			}
			if ( theCause instanceof ParseException ) {
				throw (ParseException) theCause;
			}
			throw new IOException( "No configuration file with (base) URL <" + aUrl.toExternalForm() + "> found!" );
		}

		// /////////////////////////////////////////////////////////////////////
		// HELPER:
		// /////////////////////////////////////////////////////////////////////

		private ResourcePropertiesBuilderFactory fromFilenameExtension( String aFilePath ) {
			String[] eExtensions;
			for ( ResourcePropertiesBuilderFactory eFactory : _factories ) {
				eExtensions = eFactory.getFilenameSuffixes();
				if ( eExtensions != null ) {
					for ( String eExtension : eExtensions ) {
						if ( aFilePath.toLowerCase().endsWith( eExtension.toLowerCase() ) ) {
							return eFactory;
						}
					}
				}
			}
			return null;
		}

		private ResourcePropertiesBuilder toPropertiesFromFilePath( String aFilePath, ConfigLocator aConfigLocator ) throws IOException, ParseException {
			Exception theCause = null;
			final File theFile = new File( aFilePath );
			File eFile;
			if ( theFile.exists() ) {
				final ResourcePropertiesBuilderFactory theFactory = fromFilenameExtension( theFile.getAbsolutePath() );
				try {
					if ( theFactory != null ) {
						return theFactory.toProperties( theFile, ConfigLocator.ABSOLUTE );
					}
				}
				catch ( ParseException e ) {
					if ( theFactory.hasFilenameSuffix( theFile ) ) {
						throw new ParseException( "Error parsing file <" + theFile.getAbsolutePath() + "> at offset <" + e.getErrorOffset() + ">!", e.getErrorOffset() );
					}
					if ( theCause == null ) {
						theCause = e;
					}
				}
				catch ( Exception e ) {
					if ( theCause == null ) {
						theCause = e;
					}
				}
			}
			else {
				for ( ResourcePropertiesBuilderFactory eFactory : _factories ) {
					for ( String eExtension : eFactory.getFilenameSuffixes() ) {
						eFile = new File( aFilePath + eExtension );
						if ( eFile.exists() ) {
							try {
								return eFactory.toProperties( eFile, ConfigLocator.ABSOLUTE );
							}
							catch ( ParseException e ) {
								if ( eFactory.hasFilenameSuffix( eFile ) ) {
									throw new ParseException( "Error parsing file <" + eFile.getAbsolutePath() + "> at offset <" + e.getErrorOffset() + ">!", e.getErrorOffset() );
								}
								if ( theCause == null ) {
									theCause = e;
								}
							}
							catch ( Exception e ) {
								if ( theCause == null ) {
									theCause = e;
								}
							}
						}
					}
				}
				if ( aConfigLocator != null && aConfigLocator != ConfigLocator.ABSOLUTE ) {
					final File[] theFolders = aConfigLocator.getFolders();
					for ( File eDir : theFolders ) {
						for ( ResourcePropertiesBuilderFactory eFactory : _factories ) {
							for ( String eExtension : eFactory.getFilenameSuffixes() ) {
								eFile = new File( eDir, aFilePath + eExtension );
								if ( eFile.exists() ) {
									try {
										return eFactory.toProperties( eFile, ConfigLocator.ABSOLUTE );
									}
									catch ( ParseException e ) {
										if ( eFactory.hasFilenameSuffix( eFile ) ) {
											throw new ParseException( "Error parsing file <" + eFile.getAbsolutePath() + "> at offset <" + e.getErrorOffset() + ">!", e.getErrorOffset() );
										}
										if ( theCause == null ) {
											theCause = e;
										}
									}
									catch ( Exception e ) {
										if ( theCause == null ) {
											theCause = e;
										}
									}
								}
							}
						}
					}
				}
			}
			if ( theCause instanceof IOException ) {
				throw (IOException) theCause;
			}
			if ( theCause instanceof ParseException ) {
				throw (ParseException) theCause;
			}
			throw new IOException( "No configuration file with name (scheme) <" + aFilePath + "> found, where a suffix (if applicable) might by one of the following: " + Arrays.toString( getFilenameSuffixes() ) );
		}

		// /////////////////////////////////////////////////////////////////////
		// HOOKS:
		// /////////////////////////////////////////////////////////////////////

		/**
		 * Converts a byte array from an {@link InputStream}.
		 * 
		 * @param aInputStream The {@link InputStream} to be converted.
		 * 
		 * @return The byte array from the {@link InputStream}.
		 * 
		 * @throws IOException Thrown in case there were problems reading the
		 *         {@link InputStream}.
		 */
		public static byte[] toByteArray( InputStream aInputStream ) throws IOException {
			final ByteArrayOutputStream theBuffer = new ByteArrayOutputStream();
			int eRead;
			final byte[] theData = new byte[16384];
			while ( ( eRead = aInputStream.read( theData, 0, theData.length ) ) != -1 ) {
				theBuffer.write( theData, 0, eRead );
			}
			theBuffer.flush();
			return theBuffer.toByteArray();
		}
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy