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

org.refcodes.web.Url Maven / Gradle / Ivy

Go to download

This artifact provides web (HTTP) related definitions and types being used by REFCODES.ORG web related functionality and artifacts.

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")
// -----------------------------------------------------------------------------
// 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.web;

import java.net.MalformedURLException;
import java.net.URL;

import org.refcodes.data.Delimiter;
import org.refcodes.data.Scheme;
import org.refcodes.mixin.CredentialsAccessor;
import org.refcodes.mixin.Dumpable;
import org.refcodes.mixin.PathAccessor;
import org.refcodes.mixin.PortAccessor;
import org.refcodes.net.IpAddress;
import org.refcodes.net.IpAddressAccessor;

/**
 * The {@link Url} class represents an immutable URL: An URL looks something
 * like this:
 * 
 * "scheme://[identity[:secret]@]host[:port][/path][?query][#fragment] In
 * contrast to the java.net {@link URL}, this URL also supports
 * "relative" locators with neither a scheme nor a host declaration. If the
 * relative locator starts with a "/" slash, then we assume not having a host
 * being provided: "/path?query#fragment" When it does *not* start with a "/"
 * slash, then we assume that the first element being the host:
 * "[identity[:secret]@]host[:port]/path[?query][#fragment]"
 */
public class Url implements Dumpable, SchemeAccessor, HostAccessor, IpAddressAccessor, PortAccessor, PathAccessor, QueryFieldsAccessor, FragmentAccessor, CredentialsAccessor {

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

	protected Scheme _scheme = null;
	protected String _protocol = null;
	protected String _host = null;
	protected int[] _ipAddress = null;
	protected String _path = null;
	protected int _port = -1;
	protected FormFields _queryFields = null;
	protected String _identity = null;
	protected String _secret = null;
	protected String _fragment = null;

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

	/**
	 * Default constructor. Make sure to set required attributes for a valid
	 * URL.
	 */
	protected Url() {}

	/**
	 * Creates an {@link Url} from the provided {@link Url}.
	 * 
	 * @param aUrl The {@link Url} from which to construct this instance.
	 */
	public Url( Url aUrl ) {
		_scheme = aUrl.getScheme();
		if ( _scheme == null ) {
			_protocol = aUrl.toProtocol();
		}
		_host = aUrl.getHost();
		_ipAddress = aUrl.getIpAddress();
		setPath( aUrl.getPath() );
		_port = aUrl.getPort();
		_queryFields = new FormFields( aUrl.getQueryFields() );
		_identity = aUrl.getIdentity();
		_secret = aUrl.getSecret();
		_fragment = aUrl.getFragment();
	}

	/**
	 * Constructs an {@link Url} from the provided URL {@link String}.
	 * 
	 * @param aUrl The URL {@link String} to be parsed. The URL consists of the
	 *        scheme (protocol), the identify and the secret (optional), the
	 *        host as well as an optional port and the (optional) path.
	 * 
	 * @throws MalformedURLException in case the provided URL is considered
	 *         being malformed.
	 */
	public Url( String aUrl ) throws MalformedURLException {
		fromUrl( aUrl );
	}

	/**
	 * Constructs an {@link Url} from the provided URL {@link String}.
	 * 
	 * @param aUrl The URL {@link String} to be parsed. The URL consists of the
	 *        scheme (protocol), the identify and the secret (optional), the
	 *        host as well as an optional port and the (optional) path.
	 * @param aQueryFields The Query-Fields to be used for the HTTP
	 *        Query-String.
	 * 
	 * @throws MalformedURLException in case the provided URL is considered
	 *         being malformed.
	 */
	public Url( String aUrl, FormFields aQueryFields ) throws MalformedURLException {
		fromUrl( aUrl );
		_queryFields = aQueryFields;
	}

	/**
	 * Constructs an {@link Url} from the provided URL {@link String}.
	 * 
	 * @param aUrl The URL {@link String} to be parsed. The URL consists of the
	 *        scheme (protocol), the identify and the secret (optional), the
	 *        host as well as an optional port and the (optional) path.
	 * @param aQueryFields The Query-Fields to be used for the HTTP
	 *        Query-String.
	 * @param aFragment The fragment to be set.
	 * 
	 * @throws MalformedURLException in case the provided URL is considered
	 *         being malformed.
	 */
	public Url( String aUrl, FormFields aQueryFields, String aFragment ) throws MalformedURLException {
		fromUrl( aUrl );
		_queryFields = aQueryFields;
		_fragment = aFragment;
	}

	/**
	 * Constructs an {@link Url} from the provided {@link URL} instance.
	 * 
	 * @param aURL The {@link URL} to be used.
	 */
	public Url( URL aURL ) {
		fromURL( aURL );
	}

	/**
	 * Constructs an {@link Url} with the common attributes.
	 * 
	 * @param aScheme The {@link Scheme} (e.g. HTTP or HTTPS) to be used for the
	 *        destination URL.
	 * @param aHost The host to which the destination URL is to point to.
	 */
	public Url( Scheme aScheme, String aHost ) {
		_scheme = aScheme;
		_host = aHost;
	}

	/**
	 * Constructs an {@link Url} with the common attributes.
	 * 
	 * @param aScheme The {@link Scheme} (e.g. HTTP or HTTPS) to be used for the
	 *        destination URL.
	 * @param aHost The host to which the destination URL is to point to.
	 * @param aPort The port to be used when connecting to the host.
	 */
	public Url( Scheme aScheme, String aHost, int aPort ) {
		_scheme = aScheme;
		_host = aHost;
		_port = aPort;
	}

	/**
	 * Constructs an {@link Url} with the common attributes.
	 * 
	 * @param aScheme The {@link Scheme} (e.g. HTTP or HTTPS) to be used for the
	 *        destination URL.
	 * @param aHost The host to which the destination URL is to point to.
	 * @param aPort The port to be used when connecting to the host.
	 * @param aPath The path on the host to which the base destination URL is to
	 *        point to.
	 */
	public Url( Scheme aScheme, String aHost, int aPort, String aPath ) {
		_scheme = aScheme;
		_host = aHost;
		_port = aPort;
		setPath( aPath );
	}

	/**
	 * Constructs an {@link Url} with the common attributes.
	 * 
	 * @param aScheme The {@link Scheme} (e.g. HTTP or HTTPS) to be used for the
	 *        destination URL.
	 * @param aHost The host to which the destination URL is to point to.
	 * @param aPort The port to be used when connecting to the host.
	 * @param aPath The path on the host to which the base destination URL is to
	 *        point to.
	 * @param aQueryFields The Query-Fields to be used for the HTTP
	 *        Query-String.
	 */
	public Url( Scheme aScheme, String aHost, int aPort, String aPath, FormFields aQueryFields ) {
		_scheme = aScheme;
		_host = aHost;
		_port = aPort;
		setPath( aPath );
		_queryFields = aQueryFields;
	}

	/**
	 * Constructs an {@link Url} with the common attributes.
	 * 
	 * @param aScheme The {@link Scheme} (e.g. HTTP or HTTPS) to be used for the
	 *        destination URL.
	 * @param aHost The host to which the destination URL is to point to.
	 * @param aPort The port to be used when connecting to the host.
	 * @param aPath The path on the host to which the base destination URL is to
	 *        point to.
	 * @param aFragment The fragment to be set.
	 * @param aQueryFields The Query-Fields to be used for the HTTP
	 *        Query-String.
	 */
	public Url( Scheme aScheme, String aHost, int aPort, String aPath, FormFields aQueryFields, String aFragment ) {
		_scheme = aScheme;
		_host = aHost;
		_port = aPort;
		setPath( aPath );
		_queryFields = aQueryFields;
		_fragment = aFragment;
	}

	/**
	 * Constructs an {@link Url} with the common attributes.
	 * 
	 * @param aProtocol The protocol {@link String} (e.g. "http://" or
	 *        "https://") to be used for the destination URL.
	 * @param aHost The host to which the destination URL is to point to.
	 */
	public Url( String aProtocol, String aHost ) {
		setProtocol( aProtocol );
		_host = aHost;
	}

	/**
	 * Constructs an {@link Url} with the common attributes.
	 * 
	 * @param aProtocol The protocol {@link String} (e.g. "http://" or
	 *        "https://") to be used for the destination URL.
	 * @param aHost The host to which the destination URL is to point to.
	 * @param aPort The port to be used when connecting to the host.
	 */
	public Url( String aProtocol, String aHost, int aPort ) {
		setProtocol( aProtocol );
		_host = aHost;
		_port = aPort;
	}

	/**
	 * Constructs an {@link Url} with the common attributes.
	 * 
	 * @param aProtocol The protocol {@link String} (e.g. "http://" or
	 *        "https://") to be used for the destination URL.
	 * @param aHost The host to which the destination URL is to point to.
	 * @param aPort The port to be used when connecting to the host.
	 * @param aPath The path on the host to which the base destination URL is to
	 *        point to.
	 */
	public Url( String aProtocol, String aHost, int aPort, String aPath ) {
		setProtocol( aProtocol );
		_host = aHost;
		_port = aPort;
		setPath( aPath );
	}

	/**
	 * Constructs an {@link Url} with the common attributes.
	 * 
	 * @param aProtocol The protocol {@link String} (e.g. "http://" or
	 *        "https://") to be used for the destination URL.
	 * @param aHost The host to which the destination URL is to point to.
	 * @param aPort The port to be used when connecting to the host.
	 * @param aPath The path on the host to which the base destination URL is to
	 *        point to.
	 * @param aQueryFields The Query-Fields to be used for the HTTP
	 *        Query-String.
	 */
	public Url( String aProtocol, String aHost, int aPort, String aPath, FormFields aQueryFields ) {
		setProtocol( aProtocol );
		_host = aHost;
		_port = aPort;
		setPath( aPath );
		_queryFields = aQueryFields;
	}

	/**
	 * Constructs an {@link Url} with the common attributes.
	 * 
	 * @param aProtocol The protocol {@link String} (e.g. "http://" or
	 *        "https://") to be used for the destination URL.
	 * @param aHost The host to which the destination URL is to point to.
	 * @param aPort The port to be used when connecting to the host.
	 * @param aPath The path on the host to which the base destination URL is to
	 *        point to.
	 * @param aFragment The fragment to be set.
	 * @param aQueryFields The Query-Fields to be used for the HTTP
	 *        Query-String.
	 */
	public Url( String aProtocol, String aHost, int aPort, String aPath, FormFields aQueryFields, String aFragment ) {
		setProtocol( aProtocol );
		_host = aHost;
		_port = aPort;
		setPath( aPath );
		_queryFields = aQueryFields;
		_fragment = aFragment;
	}

	/**
	 * Constructs an {@link Url} with the common attributes.
	 * 
	 * @param aScheme The {@link Scheme} (e.g. HTTP or HTTPS) to be used for the
	 *        destination URL.
	 * @param aHost The host to which the destination URL is to point to.
	 * @param aPath The path on the host to which the base destination URL is to
	 *        point to.
	 */
	public Url( Scheme aScheme, String aHost, String aPath ) {
		_scheme = aScheme;
		_host = aHost;
		setPath( aPath );
	}

	/**
	 * Constructs an {@link Url} with the common attributes.
	 * 
	 * @param aScheme The {@link Scheme} (e.g. HTTP or HTTPS) to be used for the
	 *        destination URL.
	 * @param aHost The host to which the destination URL is to point to.
	 * @param aPath The path on the host to which the base destination URL is to
	 *        point to.
	 * @param aQueryFields The Query-Fields to be used for the HTTP
	 *        Query-String.
	 */
	public Url( Scheme aScheme, String aHost, String aPath, FormFields aQueryFields ) {
		_scheme = aScheme;
		_host = aHost;
		setPath( aPath );
		_queryFields = aQueryFields;
	}

	/**
	 * Constructs an {@link Url} with the common attributes.
	 * 
	 * @param aScheme The {@link Scheme} (e.g. HTTP or HTTPS) to be used for the
	 *        destination URL.
	 * @param aHost The host to which the destination URL is to point to.
	 * @param aPath The path on the host to which the base destination URL is to
	 *        point to.
	 * @param aQueryFields The Query-Fields to be used for the HTTP
	 *        Query-String.
	 * @param aFragment The fragment to be set.
	 */
	public Url( Scheme aScheme, String aHost, String aPath, FormFields aQueryFields, String aFragment ) {
		_scheme = aScheme;
		_host = aHost;
		setPath( aPath );
		_queryFields = aQueryFields;
		_fragment = aFragment;
	}

	/**
	 * Constructs an {@link Url} with the common attributes.
	 * 
	 * @param aProtocol The protocol {@link String} (e.g. "http://" or
	 *        "https://") to be used for the destination URL.
	 * @param aHost The host to which the destination URL is to point to.
	 * @param aPath The path on the host to which the base destination URL is to
	 *        point to.
	 */
	public Url( String aProtocol, String aHost, String aPath ) {
		setProtocol( aProtocol );
		_host = aHost;
		setPath( aPath );
	}

	/**
	 * Constructs an {@link Url} with the common attributes.
	 * 
	 * @param aProtocol The protocol {@link String} (e.g. "http://" or
	 *        "https://") to be used for the destination URL.
	 * @param aHost The host to which the destination URL is to point to.
	 * @param aPath The path on the host to which the base destination URL is to
	 *        point to.
	 * @param aQueryFields The Query-Fields to be used for the HTTP
	 *        Query-String.
	 */
	public Url( String aProtocol, String aHost, String aPath, FormFields aQueryFields ) {
		setProtocol( aProtocol );
		_host = aHost;
		setPath( aPath );
		_queryFields = aQueryFields;
	}

	/**
	 * Constructs a new {@link Url} from the given {@link Url} instances by
	 * adding the other {@link Url}'s data to the first {@link Url}'s data. E.g.
	 * a path from the other {@link Url} is append to the first {@link Url}'s
	 * path, the query parameters are added or overwritten accordingly and so
	 * on.
	 * 
	 * @param aUrl The {@link Url} which is to be enriched.
	 * @param aOtherUrl The {@link Url} enriching the given {@link Url}.
	 */
	public Url( Url aUrl, Url aOtherUrl ) {
		this( aUrl != null ? aUrl : aOtherUrl );
		if ( aUrl != null ) {
			// Scheme |-->
			if ( aOtherUrl.getScheme() != null ) {
				setScheme( aOtherUrl.getScheme() );
			}
			else {
				if ( aOtherUrl.toProtocol() != null ) {
					setProtocol( aOtherUrl.toProtocol() );
				}
			}
			// Scheme <--|

			// User Info |-->
			if ( aOtherUrl.getIdentity() != null ) {
				_identity = aOtherUrl.getIdentity();
			}
			if ( aOtherUrl.getSecret() != null ) {
				_secret = aOtherUrl.getSecret();
			}
			// User Info <--|

			// Host |-->
			if ( aOtherUrl.getHost() != null ) {
				setHost( aOtherUrl.getHost() );
			}
			// Host <--|

			// Path |-->
			String theOtherPath = aOtherUrl.getPath();
			if ( theOtherPath != null ) {
				String thePath = getPath();
				if ( thePath == null ) {
					thePath = "";
				}
				thePath = Url.toTruncatePathSuffix( thePath );
				theOtherPath = Url.toTruncatePathPrefix( theOtherPath );
				setPath( thePath + Delimiter.PATH.getChar() + theOtherPath );
			}
			// Path <--|

			// Query fields |-->
			getQueryFields().putAll( aOtherUrl.getQueryFields() );
			// Query fields <--|

			// Fragment |-->
			if ( aOtherUrl.getFragment() != null ) {
				_fragment = aOtherUrl.getFragment();
			}
			// Fragment <--|
		}
	}

	/**
	 * Constructs an {@link Url} with the common attributes.
	 * 
	 * @param aProtocol The protocol {@link String} (e.g. "http://" or
	 *        "https://") to be used for the destination URL.
	 * @param aHost The host to which the destination URL is to point to.
	 * @param aPath The path on the host to which the base destination URL is to
	 *        point to.
	 * @param aQueryFields The Query-Fields to be used for the HTTP
	 *        Query-String.
	 * @param aFragment The fragment to be set.
	 */
	public Url( String aProtocol, String aHost, String aPath, FormFields aQueryFields, String aFragment ) {
		setProtocol( aProtocol );
		_host = aHost;
		setPath( aPath );
		_queryFields = aQueryFields;
		_fragment = aFragment;
	}

	/**
	 * Some {@link Url} algebra: Adds the provided path to the given {@link Url}
	 * by prepending it to the {@link Url}'s path.
	 * 
	 * @param aUrl The {@link Url} to which to add the path.
	 * @param aPaths The paths to be added to the given {@link Url}.
	 */
	public Url( Url aUrl, String... aPaths ) {
		this( aUrl );
		if ( _path != null && _path.length() != 0 ) {
			_path = Url.toTruncatePathSuffix( _path );
		}
		else {
			_path = "" + Delimiter.PATH.getChar();
		}
		String thePath = "";
		for ( String ePath : aPaths ) {
			if ( thePath.length() > 0 ) {
				thePath = thePath + Delimiter.PATH.getChar();
			}
			thePath = thePath + Url.toTruncatePath( ePath );
		}
		_path = _path + Delimiter.PATH.getChar() + thePath;
	}

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

	/**
	 * {@inheritDoc}
	 */
	@Override
	public String getFragment() {
		return _fragment;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public String getHost() {
		// try {
		// String theHost = IpAddress.toString( _ipAddress );
		// if ( theHost != null && theHost.length() > 0 ) {
		// return theHost;
		// }
		// }
		// catch ( IllegalArgumentException e ) {}
		return _host;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public String getIdentity() {
		return _identity;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public int[] getIpAddress() {
		return _ipAddress;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public String getPath() {
		return _path;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public int getPort() {
		return _port;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public FormFields getQueryFields() {
		if ( _queryFields == null ) {
			synchronized ( this ) {
				if ( _queryFields == null ) {
					_queryFields = new FormFields();
				}
			}
		}
		return _queryFields;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public Scheme getScheme() {
		return _scheme;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public String getSecret() {
		return _secret;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public String toProtocol() {
		return _scheme != null ? _scheme.toProtocol() : _protocol;
	}

	/**
	 * Returns the "host" depending on whether an IP-Address has been provided
	 * or a host name.
	 * 
	 * @return The determined host.
	 */
	public String toHost() {
		final String theHost = getHost();
		final String theCidrNotation = toCidrNotation();
		if ( theHost != null && theHost.length() > 0 && theCidrNotation != null && theCidrNotation.length() > 0 ) {
			throw new IllegalStateException( "Either the  or the  can be set, but not both of them: Host := <" + theHost + ">, IP-Address = <" + theCidrNotation + ">. Make sure only one ofd them is set!" );
		}
		return theHost != null ? theHost : theCidrNotation;
	}

	/**
	 * Creates the complete "HTTP" URL {@link String} from the {@link Url}
	 * instance's state.
	 * 
	 * @return The URL {@link String} for the given {@link Url}.
	 */
	public String toHttpUrl() {
		final StringBuilder theBuilder = new StringBuilder();

		// Scheme |-->
		final String theScheme = toProtocol();
		if ( theScheme != null && theScheme.length() != 0 ) {
			theBuilder.append( theScheme );
		}
		// Scheme <--|

		// AuthTypeCredentials |-->
		final String theIdentity = getIdentity();
		if ( theIdentity != null ) {
			theBuilder.append( theIdentity );
			if ( getSecret() != null ) {
				theBuilder.append( Delimiter.URL_CREDENTIALS.getChar() );
				theBuilder.append( getSecret() );
			}
			theBuilder.append( Delimiter.URL_USER_INFO.getChar() );
		}
		else {
			if ( getSecret() != null ) {
				throw new IllegalStateException( "The  property requires an  property not being null or of an empty length!" );
			}
		}
		// AuthTypeCredentials <--|

		// Host |-->
		final String theHost = toHost();
		if ( theHost != null && theHost.length() != 0 ) {
			theBuilder.append( theHost );
			// Port |-->
			final int thePort = getPort();
			if ( thePort != -1 ) {
				theBuilder.append( Delimiter.URL_PORT.getChar() );
				theBuilder.append( thePort );
			}
			// Port <--|
		}
		// Host <--|

		// Path |-->
		String thePath = getPath();
		if ( thePath != null ) {
			while ( thePath.length() > 0 && thePath.charAt( 0 ) == Delimiter.PATH.getChar() ) {
				thePath = thePath.substring( 1 );
			}
			if ( !theBuilder.toString().endsWith( "" + Delimiter.PATH.getChar() ) ) {
				theBuilder.append( Delimiter.PATH.getChar() );
			}
			theBuilder.append( thePath );
		}
		// Path <--|

		// Query fields |-->
		final FormFields theFields = getQueryFields();
		if ( theFields != null && theFields.size() > 0 ) {
			final String theQueryString = theFields.toUrlQueryString();
			if ( theQueryString != null && theQueryString.length() > 0 ) {
				theBuilder.append( theQueryString );
			}
		}
		// Query fields <--|

		// Fragment |-->
		final String theFragment = getFragment();
		if ( theFragment != null && theFragment.length() > 0 ) {
			theBuilder.append( Delimiter.URL_FRAGMENT.getChar() );
			theBuilder.append( theFragment );
		}
		// Fragment <--|

		return theBuilder.toString();
	}

	/**
	 * Creates the locator part from the {@link Url} instance's state, excluding
	 * the fragment or the query fields.
	 * 
	 * @return The locator for the given {@link Url}.
	 */
	public String toLocator() {
		final StringBuilder theBuilder = new StringBuilder();

		// Scheme |-->
		final String theScheme = toProtocol();
		if ( theScheme != null && theScheme.length() != 0 ) {
			theBuilder.append( theScheme );
		}
		// Scheme <--|

		// AuthTypeCredentials |-->
		final String theIdentity = getIdentity();
		if ( theIdentity != null ) {
			theBuilder.append( theIdentity );
			if ( getSecret() != null ) {
				theBuilder.append( Delimiter.URL_CREDENTIALS.getChar() );
				theBuilder.append( getSecret() );
			}
			theBuilder.append( Delimiter.URL_USER_INFO.getChar() );
		}
		else {
			if ( getSecret() != null ) {
				throw new IllegalStateException( "The  property requires an  property not being null or of an empty length!" );
			}
		}
		// AuthTypeCredentials <--|

		// Host |-->
		final String theHost = toHost();
		if ( theHost != null && theHost.length() != 0 ) {
			theBuilder.append( theHost );
			// Host <--|

			// Port |-->
			final int thePort = getPort();
			if ( thePort != -1 ) {
				theBuilder.append( Delimiter.URL_PORT.getChar() );
				theBuilder.append( thePort );
			}
			// Port <--|
		}

		// Path |-->
		String thePath = getPath();
		if ( thePath != null ) {
			while ( thePath.length() > 0 && thePath.charAt( 0 ) == Delimiter.PATH.getChar() ) {
				thePath = thePath.substring( 1 );
			}
			if ( !theBuilder.toString().endsWith( "" + Delimiter.PATH.getChar() ) ) {
				theBuilder.append( Delimiter.PATH.getChar() );
			}
			theBuilder.append( thePath );
		}
		// Path <--|

		return theBuilder.toString();
	}

	/**
	 * Constructs an {@link URL} instance from your {@link Url}'s state.
	 * 
	 * @return The according {@link URL} instance.
	 * 
	 * @throws MalformedURLException thrown in case the state of your
	 *         {@link Url} instance cannot be used to construct a valid
	 *         {@link URL}, you may be missing some properties.
	 */
	public URL toURL() throws MalformedURLException {
		return new URL( toHttpUrl() );
	}

	@Override
	public String toString() {
		return toHttpUrl();
	}

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

	/**
	 * Uses the given {@link String} to set the {@link Url}'s state.
	 * 
	 * @param aUrl The URL from which to determine the state.
	 * 
	 * @throws MalformedURLException in case the provided URL is considered
	 *         being malformed.
	 */
	protected void fromUrl( String aUrl ) throws MalformedURLException {
		try {
			String theUrl = aUrl;
			// |--> Scheme
			final Scheme theScheme = Scheme.fromScheme( theUrl );
			if ( theScheme != null ) {
				setScheme( theScheme );
				theUrl = theUrl.substring( theScheme.toProtocol().length() );
			}
			// Scheme <--|

			// User-Info |-->
			int index = theUrl.indexOf( Delimiter.URL_USER_INFO.getChar() );
			if ( index != -1 ) {
				final String theUserInfo = theUrl.substring( 0, index );
				if ( theUserInfo.length() > 0 ) {
					final int i = theUserInfo.indexOf( Delimiter.URL_CREDENTIALS.getChar() );
					if ( i == -1 ) {
						_identity = theUserInfo;
					}
					else {
						_identity = theUserInfo.substring( 0, i );
						_secret = theUserInfo.substring( i + 1 );
					}
				}
				theUrl = theUrl.substring( index + 1 );
			}
			// User-Info <--|

			// Host -->
			index = theUrl.indexOf( Delimiter.PATH.getChar() );
			if ( index != 0 ) { // we have a host, not a path!
				if ( index == -1 ) {
					index = theUrl.indexOf( Delimiter.URL_QUERY.getChar() );
					if ( index == -1 ) {
						index = theUrl.indexOf( Delimiter.URL_FRAGMENT.getChar() );
					}
				}
				if ( index == -1 ) {
					setHost( theUrl );
					theUrl = null; // done!
				}
				else {
					final String theHost = theUrl.substring( 0, index );
					if ( theHost != null && theHost.length() > 0 ) {
						final int i = theHost.indexOf( Delimiter.URL_PORT.getChar() );
						if ( i == -1 ) {
							setHost( theHost );
						}
						else {
							setHost( theHost.substring( 0, i ) );
							setPort( Integer.parseInt( theHost.substring( i + 1 ) ) );
						}
					}
					theUrl = theUrl.substring( index );
					// Host <--|
				}
			}
			// Path |-->
			if ( theUrl != null && theUrl.length() > 0 ) {
				index = theUrl.indexOf( Delimiter.URL_QUERY.getChar(), 1 );
				if ( index == -1 ) {
					index = theUrl.indexOf( Delimiter.URL_FRAGMENT.getChar(), 1 );
				}
				if ( index == -1 ) {
					setPath( theUrl );
				}
				else {
					final String thePath = theUrl.substring( 0, index );
					setPath( thePath );
					theUrl = theUrl.substring( index );
					// Path <--|
					if ( theUrl.length() > 0 ) {
						// Query Fields |-->
						if ( theUrl.charAt( 0 ) == Delimiter.URL_QUERY.getChar() ) {
							final String theQueryString;
							index = theUrl.indexOf( Delimiter.URL_FRAGMENT.getChar() );
							if ( index == -1 ) {
								theQueryString = theUrl;
							}
							else {
								theQueryString = theUrl.substring( 0, index );
							}
							final FormFields theQueryFields = new FormFields( theQueryString );
							_queryFields = theQueryFields;
						}
						// Query Fields <--|
						// Fragment |-->
						index = theUrl.indexOf( Delimiter.URL_FRAGMENT.getChar() );
						if ( index != -1 ) {
							final String theFragment = theUrl.substring( index + 1 );
							_fragment = theFragment;
						}
						// Fragment <--|
					}
				}
			}
		}
		catch ( Exception e ) {
			fromURL( new URL( aUrl ) );
		}
	}

	protected void fromURL( URL aUrl ) {
		// Scheme |-->
		Scheme theScheme = Scheme.fromName( aUrl.getProtocol() );
		if ( theScheme == null ) {
			theScheme = Scheme.fromProtocol( aUrl.getProtocol() );
		}
		setScheme( theScheme );
		// Scheme <--|

		// Host |-->
		setHost( aUrl.getHost() );
		// Host <--|

		// Port |-->
		setPort( aUrl.getPort() );
		// Port <--|

		// Identity |-->
		final String theUserInfo = aUrl.getUserInfo();
		if ( theUserInfo != null && theUserInfo.length() > 0 ) {
			final int index = theUserInfo.indexOf( Delimiter.URL_CREDENTIALS.getChar() );
			if ( index == -1 ) {
				_identity = theUserInfo;
			}
			else {
				_identity = theUserInfo.substring( 0, index );
				_secret = theUserInfo.substring( index + 1 );
			}
		}
		// Identity <--|

		// Path |-->
		final String thePath = aUrl.getPath();
		if ( thePath != null && thePath.length() > 0 ) {
			setPath( thePath );
		}
		// Path <--|

		// Query |-->
		if ( aUrl.getQuery() != null && aUrl.getQuery().length() > 0 ) {
			final FormFields theFormFields = new FormFields( aUrl.getQuery() );
			_queryFields = theFormFields;
		}
		// Query <--|

		// Fragment |-->
		if ( aUrl.getRef() != null && aUrl.getRef().length() > 0 ) {
			_fragment = aUrl.getRef();
		}
		// Fragment <--|
	}

	protected void setScheme( Scheme aScheme ) {
		_scheme = aScheme;
		_protocol = null;
	}

	protected void setProtocol( String aProtocol ) {
		final Scheme theScheme = Scheme.fromProtocol( aProtocol );
		if ( theScheme != null ) {
			_scheme = theScheme;
			_protocol = null;
		}
		else {
			_scheme = null;
			_protocol = aProtocol;
		}
	}

	protected void setHost( String aHost ) {
		try {
			final int[] theIpAddress = IpAddress.fromAnyCidrNotation( aHost );
			if ( theIpAddress != null && theIpAddress.length > 0 ) {
				_ipAddress = theIpAddress;
				_host = null;
			}
			else {
				_ipAddress = null;
				_host = aHost;
			}

		}
		catch ( IllegalArgumentException e ) {
			_host = aHost;
			_ipAddress = null;
		}
	}

	protected void setIpAddress( int[] aIpAddress ) {
		// Probe the IP-Address |-->
		IpAddress.toString( aIpAddress );
		// Probe the IP-Address <--|
		_ipAddress = aIpAddress;
		_host = null;

	}

	protected void setPort( int aPort ) {
		_port = aPort;
	}

	protected void setPath( String aPath ) {
		if ( aPath != null && aPath.length() != 0 && aPath.charAt( 0 ) != Delimiter.PATH.getChar() ) {
			aPath = Delimiter.PATH.getChar() + aPath;
		}
		_path = aPath;
	}

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

	/**
	 * Removes any leading path delimiters (as of {@link Delimiter#PATH}).
	 * 
	 * @param aPath The path to be processed.
	 * 
	 * @return The path without any leading path delimiters.
	 */
	protected static String toTruncatePathPrefix( String aPath ) {
		if ( aPath != null ) {
			while ( aPath.length() > 0 && aPath.charAt( 0 ) == Delimiter.PATH.getChar() ) {
				aPath = aPath.substring( 1 );
			}
		}
		return aPath;
	}

	/**
	 * Removes any trailing path delimiters (as of {@link Delimiter#PATH}).
	 * 
	 * @param aPath The path to be processed.
	 * 
	 * @return The path without any trailing path delimiters.
	 */
	protected static String toTruncatePathSuffix( String aPath ) {
		if ( aPath != null ) {
			while ( aPath.length() > 0 && aPath.charAt( aPath.length() - 1 ) == Delimiter.PATH.getChar() ) {
				aPath = aPath.substring( 0, aPath.length() - 1 );
			}
		}
		return aPath;
	}

	/**
	 * Removes any leading and trailing path delimiters (as of
	 * {@link Delimiter#PATH}).
	 * 
	 * @param aPath The path to be processed.
	 * 
	 * @return The path with neither any leading nor any trailing path
	 *         delimiters.
	 */
	protected static String toTruncatePath( String aPath ) {
		return toTruncatePathPrefix( toTruncatePathSuffix( aPath ) );
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy